<commit_before>package cmd

import (
	"fmt"

	"github.com/evandroflores/claimr/model"
)

// Messages is a map of centralized strings to be used on the project
var Messages = map[string]string{
	"not-implemented":                "No pancakes for you! 🥞",
	"direct-not-allowed":             "this look like a direct message. Containers are related to a channels",
	"admin-only":                     "Command available only for admins. ⛔",
	"command-not-found":              "Not sure what you are asking for. Type `@claimr help` for valid commands.",
	"same-name":                      "There is a container with the same name on this channel. Try a different one.",
	"added-to-channel":               "Container `%s` added to channel <#%s>.",
	"field-name-too-big":             fmt.Sprintf("try a smaller container name up to %d characters", model.MaxNameSize),
	"field-name-required":            "can not continue without a container name 🙄",
	"container-not-found-on-channel": "I couldn't find the container `%s` on <#%s>.",
	"container-in-use":               "Container `%s` is already in use, try another one.",
	"fail-to-update":                 "Fail to update the container.",
	"container-claimed":              "Got it. Container `%s` is all yours <@%s>.",
	"container-in-use-by-other":      "Hum Container `%s` is not being used by you.",
	"container-in-use-by-this":       "Can't remove. Container `%s` is in used by <@%s> since _%s_.",
	"container-free":                 "Got it. Container `%s` is now available",
	"fail-getting-containers":        "Fail to list containers.",
	"empty-containers-list":          "No containers to list.",
	"containers-list":                "Here is a list of containers for this channel:",
	"no-level-provided":              "No log level provided, keeping in `%s`",
	"same-log-level":                 "Same log level than actual. Nothing change.",
	"invalid-log-level":              "not a valid logrus Level: \"%s\"",
	"level-log-changed":              "Log level changed from `%s` to `%s`",
	"x-purged":                       "%d Container rows purged",
	"only-owner-can-remove":          "Only who created the container `%s` can remove it. Please check with <@%s>.",
	"container-removed":              "Container `%s` removed.",
	"container-created-by":           "Container `%s`.\nCreated by <@%s>.\n",
	"container-in-use-by-w-reason":   "In use by <@%s>%s since _%s_.",
	"shouldnt-mention-user":          "This message shouldn't contain user mentions",
	"shouldnt-mention-channel":       "This message shouldn't contain channel mentions",
}
<commit_msg>Preparing refresh-admins command<commit_after>package cmd

import (
	"fmt"

	"github.com/evandroflores/claimr/model"
)

// Messages is a map of centralized strings to be used on the project
var Messages = map[string]string{
	"not-implemented":                "No pancakes for you! 🥞",
	"direct-not-allowed":             "this look like a direct message. Containers are related to a channels",
	"admin-only":                     "Command available only for admins. ⛔",
	"command-not-found":              "Not sure what you are asking for. Type `@claimr help` for valid commands.",
	"same-name":                      "There is a container with the same name on this channel. Try a different one.",
	"added-to-channel":               "Container `%s` added to channel <#%s>.",
	"field-name-too-big":             fmt.Sprintf("try a smaller container name up to %d characters", model.MaxNameSize),
	"field-name-required":            "can not continue without a container name 🙄",
	"container-not-found-on-channel": "I couldn't find the container `%s` on <#%s>.",
	"container-in-use":               "Container `%s` is already in use, try another one.",
	"fail-to-update":                 "Fail to update the container.",
	"container-claimed":              "Got it. Container `%s` is all yours <@%s>.",
	"container-in-use-by-other":      "Hum Container `%s` is not being used by you.",
	"container-in-use-by-this":       "Can't remove. Container `%s` is in used by <@%s> since _%s_.",
	"container-free":                 "Got it. Container `%s` is now available",
	"fail-getting-containers":        "Fail to list containers.",
	"empty-containers-list":          "No containers to list.",
	"containers-list":                "Here is a list of containers for this channel:",
	"no-level-provided":              "No log level provided, keeping in `%s`",
	"same-log-level":                 "Same log level than actual. Nothing change.",
	"invalid-log-level":              "not a valid logrus Level: \"%s\"",
	"level-log-changed":              "Log level changed from `%s` to `%s`",
	"x-purged":                       "%d Container rows purged",
	"only-owner-can-remove":          "Only who created the container `%s` can remove it. Please check with <@%s>.",
	"container-removed":              "Container `%s` removed.",
	"container-created-by":           "Container `%s`.\nCreated by <@%s>.\n",
	"container-in-use-by-w-reason":   "In use by <@%s>%s since _%s_.",
	"shouldnt-mention-user":          "This message shouldn't contain user mentions",
	"shouldnt-mention-channel":       "This message shouldn't contain channel mentions",
	"x-admin-loaded":                 "%d admins loaded.",
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage:{-help info goes here}\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func error(s string) {
	fmt.Fprint(os.Stderr, s, "\n")
	os.Exit(1)
}

func main() {
	flag.Usage = usage
	flag.Parse()
	args := flag.Args()
}
<commit_msg>added //+build ignore<commit_after>//+build ignore

package main

import (
	"flag"
	"fmt"
	"os"
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage:{-help info goes here}\n")
	flag.PrintDefaults()
	os.Exit(2)
}

func error(s string) {
	fmt.Fprint(os.Stderr, s, "\n")
	os.Exit(1)
}

func main() {
	flag.Usage = usage
	flag.Parse()
	args := flag.Args()
}
<|endoftext|>


<commit_before>package cmds

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/qiniu/log"
)

var InstallFlag = cli.Command{
	Name:    "install",
	Aliases: []string{"i"},
	Usage:   fmt.Sprintf("Install binary from http://gobuild.io"),
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:  "debug",
			Usage: "Enable debug log",
		},
	},
	Action: InstallAction,
}

var GOBIN string

// Determine binary path
func init() {
	var paths []string
	gobin := os.Getenv("GOBIN")
	if gobin != "" {
		paths = filepath.SplitList(gobin)
	}
	if len(paths) > 0 {
		GOBIN = paths[0]
		return
	}
	paths = filepath.SplitList(os.Getenv("GOPATH"))
	if len(paths) > 0 {
		GOBIN = filepath.Join(paths[0], "bin")
		return
	}
	log.Fatal("Make sure you set env GOPATH or GOBIN")

	os.ExpandEnv("$HOME/.gopack/src")
}

func getInsPath(names ...string) string {
	base := os.ExpandEnv("$HOME/.gopack/")
	names = append([]string{base}, names...)
	target := filepath.Join(names...)
	os.MkdirAll(filepath.Dir(target), 0755)
	return target
}

// http://dn-gobuild5.qbox.me/gorelease/gorelease/gorelease/master/darwin-amd64/gorelease.zip
// Need to update
func downloadSource(name string) (dest string, err error) {
	parts := strings.Split(name, "/")
	if len(parts) != 2 {
		return "", fmt.Errorf("name: %s can only contains on /", name)
	}

	owner, repo := parts[0], parts[1]
	osarch := runtime.GOOS + "-" + runtime.GOARCH
	url := fmt.Sprintf("http://dn-gobuild5.qbox.me/gorelease/%s/%s/master/%s/%s.zip",
		owner, repo, osarch, repo)

	prompt("Downloading %v", url)
	log.Debug("download:", url)
	dest = getInsPath("src", fmt.Sprintf("%s.zip", repo))
	cmd := exec.Command("curl", "-fSL", url, "-o", dest)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	return
}

func deployPackage(pkgName, path string, binDir string) error {
	cmd := exec.Command("unzip", "-o", "-d", getInsPath("opt", pkgName), path)
	log.Debug("zip command:", cmd.Args)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return err
	}
	baseName := filepath.Base(pkgName)
	symlink := filepath.Join(GOBIN, baseName)

	prompt("Symlink %v", symlink)
	// for linux and darwin
	os.Remove(symlink)
	return os.Symlink(getInsPath("opt", pkgName, pkgName), symlink)
}

func prompt(format string, args ...interface{}) {
	format = "==> " + strings.TrimPrefix(format, "\n") + "\n"
	fmt.Printf(format, args...)
}

func InstallAction(c *cli.Context) {
	if c.Bool("debug") {
		log.SetOutputLevel(log.Ldebug)
	}
	if len(c.Args()) < 1 {
		log.Fatal("Need at lease one argument")
	}
	// log.Println(GOBIN)
	pkgName := c.Args().First()
	// TODO: use myname for now
	if len(strings.Split(pkgName, "/")) == 1 {
		pkgName = "codeskyblue/" + pkgName
	}

	prompt("Repository %v", pkgName)
	dest, err := downloadSource(pkgName)
	if err != nil {
		log.Fatal(err)
	}

	err = deployPackage(pkgName, dest, GOBIN)
	if err != nil {
		log.Fatal(err)
	}

	prompt("Program [%s] installed", pkgName)
}
<commit_msg>fix symlink<commit_after>package cmds

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/codegangsta/cli"
	"github.com/qiniu/log"
)

var InstallFlag = cli.Command{
	Name:    "install",
	Aliases: []string{"i"},
	Usage:   fmt.Sprintf("Install binary from http://gobuild.io"),
	Flags: []cli.Flag{
		cli.BoolFlag{
			Name:  "debug",
			Usage: "Enable debug log",
		},
	},
	Action: InstallAction,
}

var GOBIN string

// Determine binary path
func init() {
	var paths []string
	gobin := os.Getenv("GOBIN")
	if gobin != "" {
		paths = filepath.SplitList(gobin)
	}
	if len(paths) > 0 {
		GOBIN = paths[0]
		return
	}
	paths = filepath.SplitList(os.Getenv("GOPATH"))
	if len(paths) > 0 {
		GOBIN = filepath.Join(paths[0], "bin")
		return
	}
	log.Fatal("Make sure you set env GOPATH or GOBIN")

	os.ExpandEnv("$HOME/.gopack/src")
}

func getInsPath(names ...string) string {
	base := os.ExpandEnv("$HOME/.gopack/")
	names = append([]string{base}, names...)
	target := filepath.Join(names...)
	os.MkdirAll(filepath.Dir(target), 0755)
	return target
}

// http://dn-gobuild5.qbox.me/gorelease/gorelease/gorelease/master/darwin-amd64/gorelease.zip
// Need to update
func downloadSource(name string) (dest string, err error) {
	parts := strings.Split(name, "/")
	if len(parts) != 2 {
		return "", fmt.Errorf("name: %s can only contains on /", name)
	}

	owner, repo := parts[0], parts[1]
	osarch := runtime.GOOS + "-" + runtime.GOARCH
	url := fmt.Sprintf("http://dn-gobuild5.qbox.me/gorelease/%s/%s/master/%s/%s.zip",
		owner, repo, osarch, repo)

	prompt("Downloading %v", url)
	log.Debug("download:", url)
	dest = getInsPath("src", fmt.Sprintf("%s.zip", repo))
	cmd := exec.Command("curl", "-fSL", url, "-o", dest)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	return
}

func deployPackage(pkgName, path string, binDir string) error {
	cmd := exec.Command("unzip", "-o", "-d", getInsPath("opt", pkgName), path)
	log.Debug("zip command:", cmd.Args)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return err
	}
	baseName := filepath.Base(pkgName)
	symlink := filepath.Join(GOBIN, baseName)

	prompt("Symlink %v", symlink)
	// for linux and darwin
	os.Remove(symlink)

	// TODO: need to resolve multi binaries
	return os.Symlink(getInsPath("opt", pkgName, filepath.Base(pkgName)), symlink)
}

func prompt(format string, args ...interface{}) {
	format = "==> " + strings.TrimPrefix(format, "\n") + "\n"
	fmt.Printf(format, args...)
}

func InstallAction(c *cli.Context) {
	if c.Bool("debug") {
		log.SetOutputLevel(log.Ldebug)
	}
	if len(c.Args()) < 1 {
		log.Fatal("Need at lease one argument")
	}
	// log.Println(GOBIN)
	pkgName := c.Args().First()
	// TODO: use myname for now
	if len(strings.Split(pkgName, "/")) == 1 {
		pkgName = "codeskyblue/" + pkgName
	}

	prompt("Repository %v", pkgName)
	dest, err := downloadSource(pkgName)
	if err != nil {
		log.Fatal(err)
	}

	err = deployPackage(pkgName, dest, GOBIN)
	if err != nil {
		log.Fatal(err)
	}

	prompt("Program [%s] installed", pkgName)
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc.
//
// 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 sandbox

import (
	"fmt"
	"net"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"

	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/vishvananda/netlink"
	"gvisor.googlesource.com/gvisor/pkg/log"
	"gvisor.googlesource.com/gvisor/pkg/urpc"
	"gvisor.googlesource.com/gvisor/runsc/boot"
)

const (
	// Annotations used to indicate whether the container corresponds to a
	// pod or a container within a pod.
	crioContainerTypeAnnotation       = "io.kubernetes.cri-o.ContainerType"
	containerdContainerTypeAnnotation = "io.kubernetes.cri.container-type"
)

// setupNetwork configures the network stack to mimic the local network
// configuration. Docker uses network namespaces with vnets to configure the
// network for the container. The untrusted app expects to see the same network
// inside the sandbox. Routing and port mapping is handled directly by docker
// with most of network information not even available to the runtime.
//
// Netstack inside the sandbox speaks directly to the device using a raw socket.
// All IP addresses assigned to the NIC, are removed and passed on to netstack's
// device.
//
// If 'conf.Network' is NoNetwork, skips local configuration and creates a
// loopback interface only.
//
// Run the following container to test it:
//  docker run -di --runtime=runsc -p 8080:80 -v $PWD:/usr/local/apache2/htdocs/ httpd:2.4
func setupNetwork(conn *urpc.Client, pid int, spec *specs.Spec, conf *boot.Config) error {
	log.Infof("Setting up network")

	// HACK!
	//
	// When kubernetes starts a pod, it first creates a sandbox with an
	// application that just pauses forever.  Later, when a container is
	// added to the pod, kubernetes will create another sandbox with a
	// config that corresponds to the containerized application, and add it
	// to the same namespaces as the pause sandbox.
	//
	// Running a second sandbox currently breaks because the two sandboxes
	// have the same network namespace and configuration, and try to create
	// a tap device on the same host device which fails.
	//
	// Runsc will eventually need to detect that this container is meant to
	// be run in the same sandbox as the pausing application, and somehow
	// make that happen.
	//
	// For now the following HACK disables networking for the "pause"
	// sandbox, allowing the second sandbox to start up successfully.
	//
	// TODO: Remove this once multiple containers per sandbox
	// is properly supported.
	if spec.Annotations[crioContainerTypeAnnotation] == "sandbox" ||
		spec.Annotations[containerdContainerTypeAnnotation] == "sandbox" {
		log.Warningf("HACK: Disabling network")
		conf.Network = boot.NetworkNone
	}

	switch conf.Network {
	case boot.NetworkNone:
		log.Infof("Network is disabled, create loopback interface only")
		if err := createDefaultLoopbackInterface(conn); err != nil {
			return fmt.Errorf("error creating default loopback interface: %v", err)
		}
	case boot.NetworkSandbox:
		// Build the path to the net namespace of the sandbox process.
		// This is what we will copy.
		nsPath := filepath.Join("/proc", strconv.Itoa(pid), "ns/net")
		if err := createInterfacesAndRoutesFromNS(conn, nsPath); err != nil {
			return fmt.Errorf("error creating interfaces from net namespace %q: %v", nsPath, err)
		}
	case boot.NetworkHost:
		// Nothing to do here.
	default:
		return fmt.Errorf("Invalid network type: %d", conf.Network)
	}
	return nil
}

func createDefaultLoopbackInterface(conn *urpc.Client) error {
	link := boot.LoopbackLink{
		Name: "lo",
		Addresses: []net.IP{
			net.IP("\x7f\x00\x00\x01"),
			net.IPv6loopback,
		},
		Routes: []boot.Route{
			{
				Destination: net.IP("\x7f\x00\x00\x00"),
				Mask:        net.IPMask("\xff\x00\x00\x00"),
			},
			{
				Destination: net.IPv6loopback,
				Mask:        net.IPMask(strings.Repeat("\xff", 16)),
			},
		},
	}
	if err := conn.Call(boot.NetworkCreateLinksAndRoutes, &boot.CreateLinksAndRoutesArgs{
		LoopbackLinks: []boot.LoopbackLink{link},
	}, nil); err != nil {
		return fmt.Errorf("error creating loopback link and routes: %v", err)
	}
	return nil
}

func joinNetNS(nsPath string) (func(), error) {
	runtime.LockOSThread()
	restoreNS, err := applyNS(specs.LinuxNamespace{
		Type: specs.NetworkNamespace,
		Path: nsPath,
	})
	if err != nil {
		runtime.UnlockOSThread()
		return nil, fmt.Errorf("error joining net namespace %q: %v", nsPath, err)
	}
	return func() {
		restoreNS()
		runtime.UnlockOSThread()
	}, nil
}

// isRootNS determines whether we are running in the root net namespace.
//
// TODO: Find a better way to detect root network.
func isRootNS(ifaces []net.Interface) bool {
	for _, iface := range ifaces {
		if iface.Name == "docker0" {
			return true
		}
	}
	return false

}

// createInterfacesAndRoutesFromNS scrapes the interface and routes from the
// net namespace with the given path, creates them in the sandbox, and removes
// them from the host.
func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string) error {
	// Join the network namespace that we will be copying.
	restore, err := joinNetNS(nsPath)
	if err != nil {
		return err
	}
	defer restore()

	// Get all interfaces in the namespace.
	ifaces, err := net.Interfaces()
	if err != nil {
		return fmt.Errorf("error querying interfaces: %v", err)
	}

	if isRootNS(ifaces) {
		return fmt.Errorf("cannot run in with network enabled in root network namespace")
	}

	// Collect addresses and routes from the interfaces.
	var args boot.CreateLinksAndRoutesArgs
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			log.Infof("Skipping down interface: %+v", iface)
			continue
		}

		allAddrs, err := iface.Addrs()
		if err != nil {
			return fmt.Errorf("error fetching interface addresses for %q: %v", iface.Name, err)
		}

		// We build our own loopback devices.
		if iface.Flags&net.FlagLoopback != 0 {
			links, err := loopbackLinks(iface, allAddrs)
			if err != nil {
				return fmt.Errorf("error getting loopback routes and links for iface %q: %v", iface.Name, err)
			}
			args.LoopbackLinks = append(args.LoopbackLinks, links...)
			continue
		}

		// Keep only IPv4 addresses.
		var ip4addrs []*net.IPNet
		for _, ifaddr := range allAddrs {
			ipNet, ok := ifaddr.(*net.IPNet)
			if !ok {
				return fmt.Errorf("address is not IPNet: %+v", ifaddr)
			}
			if ipNet.IP.To4() == nil {
				log.Warningf("IPv6 is not supported, skipping: %v", ipNet)
				continue
			}
			ip4addrs = append(ip4addrs, ipNet)
		}
		if len(ip4addrs) == 0 {
			log.Warningf("No IPv4 address found for interface %q, skipping", iface.Name)
			continue
		}

		// Get the link for the interface.
		ifaceLink, err := netlink.LinkByName(iface.Name)
		if err != nil {
			return fmt.Errorf("error getting link for interface %q: %v", iface.Name, err)
		}

		// Create the socket.
		const protocol = 0x0300 // htons(ETH_P_ALL)
		fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, protocol)
		if err != nil {
			return fmt.Errorf("unable to create raw socket: %v", err)
		}
		deviceFile := os.NewFile(uintptr(fd), "raw-device-fd")

		// Bind to the appropriate device.
		ll := syscall.SockaddrLinklayer{
			Protocol: protocol,
			Ifindex:  ifaceLink.Attrs().Index,
			Hatype:   0, // No ARP type.
			Pkttype:  syscall.PACKET_OTHERHOST,
		}
		if err := syscall.Bind(fd, &ll); err != nil {
			return fmt.Errorf("unable to bind to %q: %v", iface.Name, err)
		}

		// Scrape the routes before removing the address, since that
		// will remove the routes as well.
		routes, def, err := routesForIface(iface)
		if err != nil {
			return fmt.Errorf("error getting routes for interface %q: %v", iface.Name, err)
		}
		if def != nil {
			if !args.DefaultGateway.Route.Empty() {
				return fmt.Errorf("more than one default route found, interface: %v, route: %v, default route: %+v", iface.Name, def, args.DefaultGateway)
			}
			args.DefaultGateway.Route = *def
			args.DefaultGateway.Name = iface.Name
		}

		link := boot.FDBasedLink{
			Name:   iface.Name,
			MTU:    iface.MTU,
			Routes: routes,
		}

		// Collect the addresses for the interface, enable forwarding,
		// and remove them from the host.
		for _, addr := range ip4addrs {
			link.Addresses = append(link.Addresses, addr.IP)

			// Steal IP address from NIC.
			if err := removeAddress(ifaceLink, addr.String()); err != nil {
				return fmt.Errorf("error removing address %v from device %q: %v", iface.Name, addr, err)
			}
		}

		args.FilePayload.Files = append(args.FilePayload.Files, deviceFile)
		args.FDBasedLinks = append(args.FDBasedLinks, link)
	}

	log.Debugf("Setting up network, config: %+v", args)
	if err := conn.Call(boot.NetworkCreateLinksAndRoutes, &args, nil); err != nil {
		return fmt.Errorf("error creating links and routes: %v", err)
	}
	return nil
}

// loopbackLinks collects the links for a loopback interface.
func loopbackLinks(iface net.Interface, addrs []net.Addr) ([]boot.LoopbackLink, error) {
	var links []boot.LoopbackLink
	for _, addr := range addrs {
		ipNet, ok := addr.(*net.IPNet)
		if !ok {
			return nil, fmt.Errorf("address is not IPNet: %+v", addr)
		}
		links = append(links, boot.LoopbackLink{
			Name:      iface.Name,
			Addresses: []net.IP{ipNet.IP},
			Routes: []boot.Route{{
				Destination: ipNet.IP.Mask(ipNet.Mask),
				Mask:        ipNet.Mask,
			}},
		})
	}
	return links, nil
}

// routesForIface iterates over all routes for the given interface and converts
// them to boot.Routes.
func routesForIface(iface net.Interface) ([]boot.Route, *boot.Route, error) {
	link, err := netlink.LinkByIndex(iface.Index)
	if err != nil {
		return nil, nil, err
	}
	rs, err := netlink.RouteList(link, netlink.FAMILY_ALL)
	if err != nil {
		return nil, nil, fmt.Errorf("error getting routes from %q: %v", iface.Name, err)
	}

	var def *boot.Route
	var routes []boot.Route
	for _, r := range rs {
		// Is it a default route?
		if r.Dst == nil {
			if r.Gw == nil {
				return nil, nil, fmt.Errorf("default route with no gateway %q: %+v", iface.Name, r)
			}
			if r.Gw.To4() == nil {
				log.Warningf("IPv6 is not supported, skipping default route: %v", r)
				continue
			}
			if def != nil {
				return nil, nil, fmt.Errorf("more than one default route found %q, def: %+v, route: %+v", iface.Name, def, r)
			}
			// Create a catch all route to the gateway.
			def = &boot.Route{
				Destination: net.IPv4zero,
				Mask:        net.IPMask(net.IPv4zero),
				Gateway:     r.Gw,
			}
			continue
		}
		if r.Dst.IP.To4() == nil {
			log.Warningf("IPv6 is not supported, skipping route: %v", r)
			continue
		}
		routes = append(routes, boot.Route{
			Destination: r.Dst.IP.Mask(r.Dst.Mask),
			Mask:        r.Dst.Mask,
			Gateway:     r.Gw,
		})
	}
	return routes, def, nil
}

// removeAddress removes IP address from network device. It's equivalent to:
//   ip addr del <ipAndMask> dev <name>
func removeAddress(source netlink.Link, ipAndMask string) error {
	addr, err := netlink.ParseAddr(ipAndMask)
	if err != nil {
		return err
	}
	return netlink.AddrDel(source, addr)
}
<commit_msg>Tiny reordering to network code<commit_after>// Copyright 2018 Google Inc.
//
// 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 sandbox

import (
	"fmt"
	"net"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"

	specs "github.com/opencontainers/runtime-spec/specs-go"
	"github.com/vishvananda/netlink"
	"gvisor.googlesource.com/gvisor/pkg/log"
	"gvisor.googlesource.com/gvisor/pkg/urpc"
	"gvisor.googlesource.com/gvisor/runsc/boot"
)

const (
	// Annotations used to indicate whether the container corresponds to a
	// pod or a container within a pod.
	crioContainerTypeAnnotation       = "io.kubernetes.cri-o.ContainerType"
	containerdContainerTypeAnnotation = "io.kubernetes.cri.container-type"
)

// setupNetwork configures the network stack to mimic the local network
// configuration. Docker uses network namespaces with vnets to configure the
// network for the container. The untrusted app expects to see the same network
// inside the sandbox. Routing and port mapping is handled directly by docker
// with most of network information not even available to the runtime.
//
// Netstack inside the sandbox speaks directly to the device using a raw socket.
// All IP addresses assigned to the NIC, are removed and passed on to netstack's
// device.
//
// If 'conf.Network' is NoNetwork, skips local configuration and creates a
// loopback interface only.
//
// Run the following container to test it:
//  docker run -di --runtime=runsc -p 8080:80 -v $PWD:/usr/local/apache2/htdocs/ httpd:2.4
func setupNetwork(conn *urpc.Client, pid int, spec *specs.Spec, conf *boot.Config) error {
	log.Infof("Setting up network")

	// HACK!
	//
	// When kubernetes starts a pod, it first creates a sandbox with an
	// application that just pauses forever.  Later, when a container is
	// added to the pod, kubernetes will create another sandbox with a
	// config that corresponds to the containerized application, and add it
	// to the same namespaces as the pause sandbox.
	//
	// Running a second sandbox currently breaks because the two sandboxes
	// have the same network namespace and configuration, and try to create
	// a tap device on the same host device which fails.
	//
	// Runsc will eventually need to detect that this container is meant to
	// be run in the same sandbox as the pausing application, and somehow
	// make that happen.
	//
	// For now the following HACK disables networking for the "pause"
	// sandbox, allowing the second sandbox to start up successfully.
	//
	// TODO: Remove this once multiple containers per sandbox
	// is properly supported.
	if spec.Annotations[crioContainerTypeAnnotation] == "sandbox" ||
		spec.Annotations[containerdContainerTypeAnnotation] == "sandbox" {
		log.Warningf("HACK: Disabling network")
		conf.Network = boot.NetworkNone
	}

	switch conf.Network {
	case boot.NetworkNone:
		log.Infof("Network is disabled, create loopback interface only")
		if err := createDefaultLoopbackInterface(conn); err != nil {
			return fmt.Errorf("error creating default loopback interface: %v", err)
		}
	case boot.NetworkSandbox:
		// Build the path to the net namespace of the sandbox process.
		// This is what we will copy.
		nsPath := filepath.Join("/proc", strconv.Itoa(pid), "ns/net")
		if err := createInterfacesAndRoutesFromNS(conn, nsPath); err != nil {
			return fmt.Errorf("error creating interfaces from net namespace %q: %v", nsPath, err)
		}
	case boot.NetworkHost:
		// Nothing to do here.
	default:
		return fmt.Errorf("Invalid network type: %d", conf.Network)
	}
	return nil
}

func createDefaultLoopbackInterface(conn *urpc.Client) error {
	link := boot.LoopbackLink{
		Name: "lo",
		Addresses: []net.IP{
			net.IP("\x7f\x00\x00\x01"),
			net.IPv6loopback,
		},
		Routes: []boot.Route{
			{
				Destination: net.IP("\x7f\x00\x00\x00"),
				Mask:        net.IPMask("\xff\x00\x00\x00"),
			},
			{
				Destination: net.IPv6loopback,
				Mask:        net.IPMask(strings.Repeat("\xff", 16)),
			},
		},
	}
	if err := conn.Call(boot.NetworkCreateLinksAndRoutes, &boot.CreateLinksAndRoutesArgs{
		LoopbackLinks: []boot.LoopbackLink{link},
	}, nil); err != nil {
		return fmt.Errorf("error creating loopback link and routes: %v", err)
	}
	return nil
}

func joinNetNS(nsPath string) (func(), error) {
	runtime.LockOSThread()
	restoreNS, err := applyNS(specs.LinuxNamespace{
		Type: specs.NetworkNamespace,
		Path: nsPath,
	})
	if err != nil {
		runtime.UnlockOSThread()
		return nil, fmt.Errorf("error joining net namespace %q: %v", nsPath, err)
	}
	return func() {
		restoreNS()
		runtime.UnlockOSThread()
	}, nil
}

// isRootNS determines whether we are running in the root net namespace.
//
// TODO: Find a better way to detect root network.
func isRootNS(ifaces []net.Interface) bool {
	for _, iface := range ifaces {
		if iface.Name == "docker0" {
			return true
		}
	}
	return false

}

// createInterfacesAndRoutesFromNS scrapes the interface and routes from the
// net namespace with the given path, creates them in the sandbox, and removes
// them from the host.
func createInterfacesAndRoutesFromNS(conn *urpc.Client, nsPath string) error {
	// Join the network namespace that we will be copying.
	restore, err := joinNetNS(nsPath)
	if err != nil {
		return err
	}
	defer restore()

	// Get all interfaces in the namespace.
	ifaces, err := net.Interfaces()
	if err != nil {
		return fmt.Errorf("error querying interfaces: %v", err)
	}

	if isRootNS(ifaces) {
		return fmt.Errorf("cannot run in with network enabled in root network namespace")
	}

	// Collect addresses and routes from the interfaces.
	var args boot.CreateLinksAndRoutesArgs
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			log.Infof("Skipping down interface: %+v", iface)
			continue
		}

		allAddrs, err := iface.Addrs()
		if err != nil {
			return fmt.Errorf("error fetching interface addresses for %q: %v", iface.Name, err)
		}

		// We build our own loopback devices.
		if iface.Flags&net.FlagLoopback != 0 {
			links, err := loopbackLinks(iface, allAddrs)
			if err != nil {
				return fmt.Errorf("error getting loopback routes and links for iface %q: %v", iface.Name, err)
			}
			args.LoopbackLinks = append(args.LoopbackLinks, links...)
			continue
		}

		// Keep only IPv4 addresses.
		var ip4addrs []*net.IPNet
		for _, ifaddr := range allAddrs {
			ipNet, ok := ifaddr.(*net.IPNet)
			if !ok {
				return fmt.Errorf("address is not IPNet: %+v", ifaddr)
			}
			if ipNet.IP.To4() == nil {
				log.Warningf("IPv6 is not supported, skipping: %v", ipNet)
				continue
			}
			ip4addrs = append(ip4addrs, ipNet)
		}
		if len(ip4addrs) == 0 {
			log.Warningf("No IPv4 address found for interface %q, skipping", iface.Name)
			continue
		}

		// Create the socket.
		const protocol = 0x0300 // htons(ETH_P_ALL)
		fd, err := syscall.Socket(syscall.AF_PACKET, syscall.SOCK_RAW, protocol)
		if err != nil {
			return fmt.Errorf("unable to create raw socket: %v", err)
		}
		deviceFile := os.NewFile(uintptr(fd), "raw-device-fd")

		// Bind to the appropriate device.
		ll := syscall.SockaddrLinklayer{
			Protocol: protocol,
			Ifindex:  iface.Index,
			Hatype:   0, // No ARP type.
			Pkttype:  syscall.PACKET_OTHERHOST,
		}
		if err := syscall.Bind(fd, &ll); err != nil {
			return fmt.Errorf("unable to bind to %q: %v", iface.Name, err)
		}

		// Scrape the routes before removing the address, since that
		// will remove the routes as well.
		routes, def, err := routesForIface(iface)
		if err != nil {
			return fmt.Errorf("error getting routes for interface %q: %v", iface.Name, err)
		}
		if def != nil {
			if !args.DefaultGateway.Route.Empty() {
				return fmt.Errorf("more than one default route found, interface: %v, route: %v, default route: %+v", iface.Name, def, args.DefaultGateway)
			}
			args.DefaultGateway.Route = *def
			args.DefaultGateway.Name = iface.Name
		}

		link := boot.FDBasedLink{
			Name:   iface.Name,
			MTU:    iface.MTU,
			Routes: routes,
		}

		// Get the link for the interface.
		ifaceLink, err := netlink.LinkByName(iface.Name)
		if err != nil {
			return fmt.Errorf("error getting link for interface %q: %v", iface.Name, err)
		}

		// Collect the addresses for the interface, enable forwarding,
		// and remove them from the host.
		for _, addr := range ip4addrs {
			link.Addresses = append(link.Addresses, addr.IP)

			// Steal IP address from NIC.
			if err := removeAddress(ifaceLink, addr.String()); err != nil {
				return fmt.Errorf("error removing address %v from device %q: %v", iface.Name, addr, err)
			}
		}

		args.FilePayload.Files = append(args.FilePayload.Files, deviceFile)
		args.FDBasedLinks = append(args.FDBasedLinks, link)
	}

	log.Debugf("Setting up network, config: %+v", args)
	if err := conn.Call(boot.NetworkCreateLinksAndRoutes, &args, nil); err != nil {
		return fmt.Errorf("error creating links and routes: %v", err)
	}
	return nil
}

// loopbackLinks collects the links for a loopback interface.
func loopbackLinks(iface net.Interface, addrs []net.Addr) ([]boot.LoopbackLink, error) {
	var links []boot.LoopbackLink
	for _, addr := range addrs {
		ipNet, ok := addr.(*net.IPNet)
		if !ok {
			return nil, fmt.Errorf("address is not IPNet: %+v", addr)
		}
		links = append(links, boot.LoopbackLink{
			Name:      iface.Name,
			Addresses: []net.IP{ipNet.IP},
			Routes: []boot.Route{{
				Destination: ipNet.IP.Mask(ipNet.Mask),
				Mask:        ipNet.Mask,
			}},
		})
	}
	return links, nil
}

// routesForIface iterates over all routes for the given interface and converts
// them to boot.Routes.
func routesForIface(iface net.Interface) ([]boot.Route, *boot.Route, error) {
	link, err := netlink.LinkByIndex(iface.Index)
	if err != nil {
		return nil, nil, err
	}
	rs, err := netlink.RouteList(link, netlink.FAMILY_ALL)
	if err != nil {
		return nil, nil, fmt.Errorf("error getting routes from %q: %v", iface.Name, err)
	}

	var def *boot.Route
	var routes []boot.Route
	for _, r := range rs {
		// Is it a default route?
		if r.Dst == nil {
			if r.Gw == nil {
				return nil, nil, fmt.Errorf("default route with no gateway %q: %+v", iface.Name, r)
			}
			if r.Gw.To4() == nil {
				log.Warningf("IPv6 is not supported, skipping default route: %v", r)
				continue
			}
			if def != nil {
				return nil, nil, fmt.Errorf("more than one default route found %q, def: %+v, route: %+v", iface.Name, def, r)
			}
			// Create a catch all route to the gateway.
			def = &boot.Route{
				Destination: net.IPv4zero,
				Mask:        net.IPMask(net.IPv4zero),
				Gateway:     r.Gw,
			}
			continue
		}
		if r.Dst.IP.To4() == nil {
			log.Warningf("IPv6 is not supported, skipping route: %v", r)
			continue
		}
		routes = append(routes, boot.Route{
			Destination: r.Dst.IP.Mask(r.Dst.Mask),
			Mask:        r.Dst.Mask,
			Gateway:     r.Gw,
		})
	}
	return routes, def, nil
}

// removeAddress removes IP address from network device. It's equivalent to:
//   ip addr del <ipAndMask> dev <name>
func removeAddress(source netlink.Link, ipAndMask string) error {
	addr, err := netlink.ParseAddr(ipAndMask)
	if err != nil {
		return err
	}
	return netlink.AddrDel(source, addr)
}
<|endoftext|>


<commit_before>// package name: riffle
package main

import (
	"C"

	"github.com/exis-io/core"
	"github.com/exis-io/core/shared"
)

// Required main method
func main() {}

//export CBID
func CBID() uint64 {
	return core.NewID()
}

// Note that with go 1.6 we can't pass pointers back up through the language boundrary.
// An "unsafe" implementation is going to fail, since the go GC is going to have a bad time.

// So yet another messaging bus. Make sure to destroy domains in the crust.

var domainIndex = make(map[uint64]core.Domain)

//export Free
func Free(pdomain uint64) {
	delete(domainIndex, pdomain)
}

//export NewDomain
func NewDomain(name *C.char) uint64 {
	i := CBID()
	d := core.NewDomain(C.GoString(name), nil)
	domainIndex[i] = d
	return i
}

//export Subdomain
func Subdomain(pdomain uint64, name *C.char) uint64 {
	d := get(pdomain)
	i := CBID()
	domainIndex[i] = d.Subdomain(C.GoString(name))
	return i
}

//export SetToken
func SetToken(pdomain uint64, token *C.char) {
    d := get(pdomain)
    d.GetApp().SetToken(C.GoString(token))
}

//export Receive
func Receive(dptr uint64) *C.char {
	// Used to be a byte slice, but 1.6 cgo checks will not allow that
	d := get(dptr)
	return C.CString(core.MantleMarshall(d.GetApp().CallbackListen()))
}

//export Join
func Join(pdomain uint64, cb uint64, eb uint64) {
	d := get(pdomain)

	if c, err := shared.Open(core.Fabric); err != nil {
		d.GetApp().CallbackSend(eb, err.Error())
	} else {
		if err := d.Join(c); err != nil {
			d.GetApp().CallbackSend(eb, err.Error())
		} else {
			d.GetApp().CallbackSend(cb)
		}
	}
}

//export Login
func Login(pdomain uint64) {
    d := get(pdomain)
    d.GetApp().Login()
}

//export Register
func Register(pdomain uint64) {
    d := get(pdomain)
}

//export Subscribe
func Subscribe(pdomain uint64, endpoint *C.char, cb uint64, eb uint64, hn uint64, types *C.char) {
	d := get(pdomain)
	go core.MantleSubscribe(d, C.GoString(endpoint), cb, eb, hn, core.MantleUnmarshal(C.GoString(types)))
}

//export Register
func Register(pdomain uint64, endpoint *C.char, cb uint64, eb uint64, hn uint64, types *C.char) {
	d := get(pdomain)
	go core.MantleRegister(d, C.GoString(endpoint), cb, eb, hn, core.MantleUnmarshal(C.GoString(types)))
}

//export Publish
func Publish(pdomain uint64, endpoint *C.char, cb uint64, eb uint64, args *C.char) {
	d := get(pdomain)
	go core.MantlePublish(d, C.GoString(endpoint), cb, eb, core.MantleUnmarshal(C.GoString(args)))
}

//export Call
func Call(pdomain uint64, endpoint *C.char, cb uint64, eb uint64, args *C.char) {
	d := get(pdomain)
	go core.MantleCall(d, C.GoString(endpoint), cb, eb, core.MantleUnmarshal(C.GoString(args)))
}

//export Yield
func Yield(pdomain uint64, request uint64, args *C.char) {
	d := get(pdomain)
	go d.GetApp().Yield(request, core.MantleUnmarshal(C.GoString(args)))
}

//export CallExpects
func CallExpects(pdomain uint64, cb uint64, types *C.char) {
	d := get(pdomain)
	go d.CallExpects(cb, core.MantleUnmarshal(C.GoString(types)))
}

//export Unsubscribe
func Unsubscribe(pdomain uint64, endpoint *C.char, cb uint64, eb uint64) {
	d := get(pdomain)
	go core.MantleUnsubscribe(d, C.GoString(endpoint), cb, eb)
}

//export Unregister
func Unregister(pdomain uint64, endpoint *C.char, cb uint64, eb uint64) {
	d := get(pdomain)
	go core.MantleUnregister(d, C.GoString(endpoint), cb, eb)
}

//export Leave
func Leave(pdomain uint64) {
	d := get(pdomain)
	go d.Leave()
}

func get(i uint64) core.Domain {
	// get the domain from the domain index
	if d, ok := domainIndex[i]; !ok {
		return nil
	} else {
		return d
	}
}

// Toying with model operations

func ModelApply() {

}

//export MantleSetLogLevelOff
func MantleSetLogLevelOff() { core.LogLevel = core.LogLevelOff }

//export MantleSetLogLevelApp
func MantleSetLogLevelApp() { core.LogLevel = core.LogLevelApp }

//export MantleSetLogLevelErr
func MantleSetLogLevelErr() { core.LogLevel = core.LogLevelErr }

//export MantleSetLogLevelWarn
func MantleSetLogLevelWarn() { core.LogLevel = core.LogLevelWarn }

//export MantleSetLogLevelInfo
func MantleSetLogLevelInfo() { core.LogLevel = core.LogLevelInfo }

//export MantleSetLogLevelDebug
func MantleSetLogLevelDebug() { core.LogLevel = core.LogLevelDebug }

//export MantleSetFabricDev
func MantleSetFabricDev() {
	core.Fabric = core.FabricDev
	core.Registrar = core.RegistrarDev
}

//export MantleSetFabricSandbox
func MantleSetFabricSandbox() { core.Fabric = core.FabricSandbox }

//export MantleSetFabricProduction
func MantleSetFabricProduction() {
	core.Fabric = core.FabricProduction
	core.Registrar = core.RegistrarProduction
}

//export MantleSetFabricLocal
func MantleSetFabricLocal() {
	core.Fabric = core.FabricLocal
	core.Registrar = core.RegistrarLocal
}

//export MantleSetFabric
func MantleSetFabric(url *C.char) { core.Fabric = C.GoString(url) }

//export MantleSetRegistrar
func MantleSetRegistrar(url *C.char) { core.Registrar = C.GoString(url) }

//export MantleApplication
func MantleApplication(s *C.char) { core.Application("%s", C.GoString(s)) }

//export MantleDebug
func MantleDebug(s *C.char) { core.Debug("%s", C.GoString(s)) }

//export MantleInfo
func MantleInfo(s *C.char) { core.Info("%s", C.GoString(s)) }

//export MantleWarn
func MantleWarn(s *C.char) { core.Warn("%s", C.GoString(s)) }

//export MantleError
func MantleError(s *C.char) { core.Error("%s", C.GoString(s)) }

//export MantleSetCuminStrict
func MantleSetCuminStrict() { core.CuminLevel = core.CuminStrict }

//export MantleSetCuminLoose
func MantleSetCuminLoose() { core.CuminLevel = core.CuminLoose }

//export MantleSetCuminOff
func MantleSetCuminOff() { core.CuminLevel = core.CuminOff }
<commit_msg>Removed troublesome functions in cMantle.<commit_after>// package name: riffle
package main

import (
	"C"

	"github.com/exis-io/core"
	"github.com/exis-io/core/shared"
)

// Required main method
func main() {}

//export CBID
func CBID() uint64 {
	return core.NewID()
}

// Note that with go 1.6 we can't pass pointers back up through the language boundrary.
// An "unsafe" implementation is going to fail, since the go GC is going to have a bad time.

// So yet another messaging bus. Make sure to destroy domains in the crust.

var domainIndex = make(map[uint64]core.Domain)

//export Free
func Free(pdomain uint64) {
	delete(domainIndex, pdomain)
}

//export NewDomain
func NewDomain(name *C.char) uint64 {
	i := CBID()
	d := core.NewDomain(C.GoString(name), nil)
	domainIndex[i] = d
	return i
}

//export Subdomain
func Subdomain(pdomain uint64, name *C.char) uint64 {
	d := get(pdomain)
	i := CBID()
	domainIndex[i] = d.Subdomain(C.GoString(name))
	return i
}

//export SetToken
func SetToken(pdomain uint64, token *C.char) {
    d := get(pdomain)
    d.GetApp().SetToken(C.GoString(token))
}

//export Receive
func Receive(dptr uint64) *C.char {
	// Used to be a byte slice, but 1.6 cgo checks will not allow that
	d := get(dptr)
	return C.CString(core.MantleMarshall(d.GetApp().CallbackListen()))
}

//export Join
func Join(pdomain uint64, cb uint64, eb uint64) {
	d := get(pdomain)

	if c, err := shared.Open(core.Fabric); err != nil {
		d.GetApp().CallbackSend(eb, err.Error())
	} else {
		if err := d.Join(c); err != nil {
			d.GetApp().CallbackSend(eb, err.Error())
		} else {
			d.GetApp().CallbackSend(cb)
		}
	}
}

//export Subscribe
func Subscribe(pdomain uint64, endpoint *C.char, cb uint64, eb uint64, hn uint64, types *C.char) {
	d := get(pdomain)
	go core.MantleSubscribe(d, C.GoString(endpoint), cb, eb, hn, core.MantleUnmarshal(C.GoString(types)))
}

//export Register
func Register(pdomain uint64, endpoint *C.char, cb uint64, eb uint64, hn uint64, types *C.char) {
	d := get(pdomain)
	go core.MantleRegister(d, C.GoString(endpoint), cb, eb, hn, core.MantleUnmarshal(C.GoString(types)))
}

//export Publish
func Publish(pdomain uint64, endpoint *C.char, cb uint64, eb uint64, args *C.char) {
	d := get(pdomain)
	go core.MantlePublish(d, C.GoString(endpoint), cb, eb, core.MantleUnmarshal(C.GoString(args)))
}

//export Call
func Call(pdomain uint64, endpoint *C.char, cb uint64, eb uint64, args *C.char) {
	d := get(pdomain)
	go core.MantleCall(d, C.GoString(endpoint), cb, eb, core.MantleUnmarshal(C.GoString(args)))
}

//export Yield
func Yield(pdomain uint64, request uint64, args *C.char) {
	d := get(pdomain)
	go d.GetApp().Yield(request, core.MantleUnmarshal(C.GoString(args)))
}

//export CallExpects
func CallExpects(pdomain uint64, cb uint64, types *C.char) {
	d := get(pdomain)
	go d.CallExpects(cb, core.MantleUnmarshal(C.GoString(types)))
}

//export Unsubscribe
func Unsubscribe(pdomain uint64, endpoint *C.char, cb uint64, eb uint64) {
	d := get(pdomain)
	go core.MantleUnsubscribe(d, C.GoString(endpoint), cb, eb)
}

//export Unregister
func Unregister(pdomain uint64, endpoint *C.char, cb uint64, eb uint64) {
	d := get(pdomain)
	go core.MantleUnregister(d, C.GoString(endpoint), cb, eb)
}

//export Leave
func Leave(pdomain uint64) {
	d := get(pdomain)
	go d.Leave()
}

func get(i uint64) core.Domain {
	// get the domain from the domain index
	if d, ok := domainIndex[i]; !ok {
		return nil
	} else {
		return d
	}
}

// Toying with model operations

func ModelApply() {

}

//export MantleSetLogLevelOff
func MantleSetLogLevelOff() { core.LogLevel = core.LogLevelOff }

//export MantleSetLogLevelApp
func MantleSetLogLevelApp() { core.LogLevel = core.LogLevelApp }

//export MantleSetLogLevelErr
func MantleSetLogLevelErr() { core.LogLevel = core.LogLevelErr }

//export MantleSetLogLevelWarn
func MantleSetLogLevelWarn() { core.LogLevel = core.LogLevelWarn }

//export MantleSetLogLevelInfo
func MantleSetLogLevelInfo() { core.LogLevel = core.LogLevelInfo }

//export MantleSetLogLevelDebug
func MantleSetLogLevelDebug() { core.LogLevel = core.LogLevelDebug }

//export MantleSetFabricDev
func MantleSetFabricDev() {
	core.Fabric = core.FabricDev
	core.Registrar = core.RegistrarDev
}

//export MantleSetFabricSandbox
func MantleSetFabricSandbox() { core.Fabric = core.FabricSandbox }

//export MantleSetFabricProduction
func MantleSetFabricProduction() {
	core.Fabric = core.FabricProduction
	core.Registrar = core.RegistrarProduction
}

//export MantleSetFabricLocal
func MantleSetFabricLocal() {
	core.Fabric = core.FabricLocal
	core.Registrar = core.RegistrarLocal
}

//export MantleSetFabric
func MantleSetFabric(url *C.char) { core.Fabric = C.GoString(url) }

//export MantleSetRegistrar
func MantleSetRegistrar(url *C.char) { core.Registrar = C.GoString(url) }

//export MantleApplication
func MantleApplication(s *C.char) { core.Application("%s", C.GoString(s)) }

//export MantleDebug
func MantleDebug(s *C.char) { core.Debug("%s", C.GoString(s)) }

//export MantleInfo
func MantleInfo(s *C.char) { core.Info("%s", C.GoString(s)) }

//export MantleWarn
func MantleWarn(s *C.char) { core.Warn("%s", C.GoString(s)) }

//export MantleError
func MantleError(s *C.char) { core.Error("%s", C.GoString(s)) }

//export MantleSetCuminStrict
func MantleSetCuminStrict() { core.CuminLevel = core.CuminStrict }

//export MantleSetCuminLoose
func MantleSetCuminLoose() { core.CuminLevel = core.CuminLoose }

//export MantleSetCuminOff
func MantleSetCuminOff() { core.CuminLevel = core.CuminOff }
<|endoftext|>


<commit_before>// package name: riffle
package main

import (
	"C"
	"unsafe"

	"github.com/exis-io/core"
	"github.com/exis-io/core/goRiffle"
)

// Required main method
func main() {}

// By default always connect to the production fabric at node.exis.io
var fabric string = core.FabricProduction

//export CBID
func CBID() uint64 {
	return core.NewID()
}

//export NewDomain
func NewDomain(name *C.char) unsafe.Pointer {
	d := core.NewDomain(C.GoString(name), nil)
	return unsafe.Pointer(&d)
}

//export Subdomain
func Subdomain(pdomain unsafe.Pointer, name *C.char) unsafe.Pointer {
	d := *(*core.Domain)(pdomain)
	n := d.Subdomain(C.GoString(name))
	return unsafe.Pointer(&n)
}

//export Receive
func Receive(pdomain unsafe.Pointer) []byte {
	d := *(*core.Domain)(pdomain)
	return []byte(core.MantleMarshall(d.GetApp().CallbackListen()))
}

//export Join
func Join(pdomain unsafe.Pointer, cb uint64, eb uint64) {
	d := *(*core.Domain)(pdomain)

	if c, err := goRiffle.Open(fabric); err != nil {
		d.GetApp().CallbackSend(eb, err.Error())
	} else {
		if err := d.Join(c); err != nil {
			d.GetApp().CallbackSend(eb, err.Error())
		} else {
			d.GetApp().CallbackSend(cb)
		}
	}
}

//export Subscribe
func Subscribe(pdomain unsafe.Pointer, cb uint64, endpoint *C.char) {
	d := *(*core.Domain)(pdomain)
	go func() {
		d.Subscribe(C.GoString(endpoint), cb, make([]interface{}, 0))
	}()
}

//export Register
func Register(pdomain unsafe.Pointer, cb uint64, endpoint *C.char) {
	d := *(*core.Domain)(pdomain)
	go func() {
		d.Register(C.GoString(endpoint), cb, make([]interface{}, 0))
	}()
}

//export Publish
func Publish(pdomain unsafe.Pointer, cb uint64, endpoint *C.char, args *C.char) {
	d := *(*core.Domain)(pdomain)
	a := C.GoString(args)
	s := core.MantleUnmarshal(a)
	core.Debug("String: %s, Unmarshalled: %s", a, s)
	go func() {
		d.Publish(C.GoString(endpoint), cb, s)
	}()
}

//export Call
func Call(pdomain unsafe.Pointer, cb uint64, endpoint *C.char, args *C.char) {
	d := *(*core.Domain)(pdomain)
	go func() {
		d.Call(C.GoString(endpoint), cb, core.MantleUnmarshal(C.GoString(args)))
	}()
}

//export Yield
func Yield(pdomain unsafe.Pointer, request uint64, args *C.char) {
	d := *(*core.Domain)(pdomain)
	go func() {
		d.GetApp().Yield(request, core.MantleUnmarshal(C.GoString(args)))
	}()
}

//export Unsubscribe
func Unsubscribe(pdomain unsafe.Pointer, endpoint *C.char) {
	d := *(*core.Domain)(pdomain)
	go func() {
		d.Unsubscribe(C.GoString(endpoint))
	}()
}

//export Unregister
func Unregister(pdomain unsafe.Pointer, endpoint *C.char) {
	d := *(*core.Domain)(pdomain)
	go func() {
		d.Unregister(C.GoString(endpoint))
	}()
}

//export Leave
func Leave(pdomain unsafe.Pointer) {
	d := *(*core.Domain)(pdomain)
	go func() {
		d.Leave()
	}()
}

//export SetLogLevelOff
func SetLogLevelOff() { core.LogLevel = core.LogLevelOff }

//export SetLogLevelApp
func SetLogLevelApp() { core.LogLevel = core.LogLevelApp }

//export SetLogLevelErr
func SetLogLevelErr() { core.LogLevel = core.LogLevelErr }

//export SetLogLevelWarn
func SetLogLevelWarn() { core.LogLevel = core.LogLevelWarn }

//export SetLogLevelInfo
func SetLogLevelInfo() { core.LogLevel = core.LogLevelInfo }

//export SetLogLevelDebug
func SetLogLevelDebug() { core.LogLevel = core.LogLevelDebug }

//export SetFabricDev
func SetFabricDev() { fabric = core.FabricDev }

//export SetFabricSandbox
func SetFabricSandbox() { fabric = core.FabricSandbox }

//export SetFabricProduction
func SetFabricProduction() { fabric = core.FabricProduction }

//export SetFabricLocal
func SetFabricLocal() { fabric = core.FabricLocal }

//export MantleSetFabric
func MantleSetFabric(url *C.char) { fabric = C.GoString(url) }

//export Application
func Application(s *C.char) { core.Application("%s", C.GoString(s)) }

//export Debug
func Debug(s *C.char) { core.Debug("%s", C.GoString(s)) }

//export Info
func Info(s *C.char) { core.Info("%s", C.GoString(s)) }

//export Warn
func Warn(s *C.char) { core.Warn("%s", C.GoString(s)) }

//export Error
func Error(s *C.char) { core.Error("%s", C.GoString(s)) }
<commit_msg>getting multiple file compilation with swift<commit_after>// package name: riffle
package main

import (
	"C"
	"unsafe"

	"github.com/exis-io/core"
	"github.com/exis-io/core/goRiffle"
)

// Required main method
func main() {}

// By default always connect to the production fabric at node.exis.io
var fabric string = core.FabricProduction

//export CBID
func CBID() uint64 {
	return core.NewID()
}

//export NewDomain
func NewDomain(name *C.char) unsafe.Pointer {
	d := core.NewDomain(C.GoString(name), nil)
	return unsafe.Pointer(&d)
}

//export Subdomain
func Subdomain(pdomain unsafe.Pointer, name *C.char) unsafe.Pointer {
	d := *(*core.Domain)(pdomain)
	n := d.Subdomain(C.GoString(name))
	return unsafe.Pointer(&n)
}

//export Receive
func Receive(pdomain unsafe.Pointer) []byte {
	d := *(*core.Domain)(pdomain)
	return []byte(core.MantleMarshall(d.GetApp().CallbackListen()))
}

//export Join
func Join(pdomain unsafe.Pointer, cb uint64, eb uint64) {
	d := *(*core.Domain)(pdomain)

	if c, err := goRiffle.Open(fabric); err != nil {
		d.GetApp().CallbackSend(eb, err.Error())
	} else {
		if err := d.Join(c); err != nil {
			d.GetApp().CallbackSend(eb, err.Error())
		} else {
			d.GetApp().CallbackSend(cb)
		}
	}
}

//export Subscribe
func Subscribe(pdomain unsafe.Pointer, endpoint *C.char, cb uint64, eb uint64, fn uint64, types string) {
	d := *(*core.Domain)(pdomain)
	// go core.MantleSubscribe(d, C.GoString(endpoint), cb, make([]interface{}, 0))
	go core.MantleSubscribe(d, C.GoString(endpoint), cb, eb, fn, core.MantleUnmarshal(types))
}

// //export Register
// func Register(pdomain unsafe.Pointer, cb uint64, endpoint *C.char) {
// 	d := *(*core.Domain)(pdomain)
// 	go core.MantleRegister(d, C.GoString(endpoint), cb, make([]interface{}, 0))
// }

// //export Publish
// func Publish(pdomain unsafe.Pointer, cb uint64, endpoint *C.char, args *C.char) {
// 	d := *(*core.Domain)(pdomain)
// 	a := C.GoString(args)
// 	s := core.MantleUnmarshal(a)
// 	// core.Debug("String: %s, Unmarshalled: %s", a, s)

// 	go core.MantlePublish(d, C.GoString(endpoint), cb, s)
// }

// //export Call
// func Call(pdomain unsafe.Pointer, cb uint64, endpoint *C.char, args *C.char) {
// 	d := *(*core.Domain)(pdomain)
// 	go core.MantleCall(d, C.GoString(endpoint), cb, core.MantleUnmarshal(C.GoString(args)))
// }

// //export Yield
// func Yield(pdomain unsafe.Pointer, request uint64, args *C.char) {
// 	d := *(*core.Domain)(pdomain)
// 	go d.GetApp().Yield(request, core.MantleUnmarshal(C.GoString(args)))
// }

// //export Unsubscribe
// func Unsubscribe(pdomain unsafe.Pointer, endpoint *C.char) {
// 	d := *(*core.Domain)(pdomain)
// 	go core.MantleUnsubscribe(d, C.GoString(endpoint))
// }

// //export Unregister
// func Unregister(pdomain unsafe.Pointer, endpoint *C.char) {
// 	d := *(*core.Domain)(pdomain)
// 	go core.MantleUnregister(d, C.GoString(endpoint))
// }

// //export Leave
// func Leave(pdomain unsafe.Pointer) {
// 	d := *(*core.Domain)(pdomain)
// 	go d.Leave()
// }

//export SetLogLevelOff
func SetLogLevelOff() { core.LogLevel = core.LogLevelOff }

//export SetLogLevelApp
func SetLogLevelApp() { core.LogLevel = core.LogLevelApp }

//export SetLogLevelErr
func SetLogLevelErr() { core.LogLevel = core.LogLevelErr }

//export SetLogLevelWarn
func SetLogLevelWarn() { core.LogLevel = core.LogLevelWarn }

//export SetLogLevelInfo
func SetLogLevelInfo() { core.LogLevel = core.LogLevelInfo }

//export SetLogLevelDebug
func SetLogLevelDebug() { core.LogLevel = core.LogLevelDebug }

//export SetFabricDev
func SetFabricDev() { fabric = core.FabricDev }

//export SetFabricSandbox
func SetFabricSandbox() { fabric = core.FabricSandbox }

//export SetFabricProduction
func SetFabricProduction() { fabric = core.FabricProduction }

//export SetFabricLocal
func SetFabricLocal() { fabric = core.FabricLocal }

//export MantleSetFabric
func MantleSetFabric(url *C.char) { fabric = C.GoString(url) }

//export Application
func Application(s *C.char) { core.Application("%s", C.GoString(s)) }

//export Debug
func Debug(s *C.char) { core.Debug("%s", C.GoString(s)) }

//export Info
func Info(s *C.char) { core.Info("%s", C.GoString(s)) }

//export Warn
func Warn(s *C.char) { core.Warn("%s", C.GoString(s)) }

//export Error
func Error(s *C.char) { core.Error("%s", C.GoString(s)) }
<|endoftext|>


<commit_before>package caddy

import (
	"fmt"
	"io"
	"log"
	"net"
	"sync"

	"github.com/mholt/caddy/caddy/letsencrypt"
	"github.com/mholt/caddy/caddy/parse"
	"github.com/mholt/caddy/caddy/setup"
	"github.com/mholt/caddy/middleware"
	"github.com/mholt/caddy/server"
)

const (
	// DefaultConfigFile is the name of the configuration file that is loaded
	// by default if no other file is specified.
	DefaultConfigFile = "Caddyfile"
)

// loadConfigs reads input (named filename) and parses it, returning the
// server configurations in the order they appeared in the input. As part
// of this, it activates Let's Encrypt for the configs that are produced.
// Thus, the returned configs are already optimally configured optimally
// for HTTPS.
func loadConfigs(filename string, input io.Reader) ([]server.Config, error) {
	var configs []server.Config

	// turn off timestamp for parsing
	flags := log.Flags()
	log.SetFlags(0)

	// Each server block represents similar hosts/addresses, since they
	// were grouped together in the Caddyfile.
	serverBlocks, err := parse.ServerBlocks(filename, input, true)
	if err != nil {
		return nil, err
	}
	if len(serverBlocks) == 0 {
		return []server.Config{NewDefault()}, nil
	}

	var lastDirectiveIndex int // we set up directives in two parts; this stores where we left off

	// Iterate each server block and make a config for each one,
	// executing the directives that were parsed in order up to the tls
	// directive; this is because we must activate Let's Encrypt.
	for i, sb := range serverBlocks {
		onces := makeOnces()
		storages := makeStorages()

		for j, addr := range sb.Addresses {
			config := server.Config{
				Host:       addr.Host,
				Port:       addr.Port,
				Root:       Root,
				Middleware: make(map[string][]middleware.Middleware),
				ConfigFile: filename,
				AppName:    AppName,
				AppVersion: AppVersion,
			}

			// It is crucial that directives are executed in the proper order.
			for k, dir := range directiveOrder {
				// Execute directive if it is in the server block
				if tokens, ok := sb.Tokens[dir.name]; ok {
					// Each setup function gets a controller, from which setup functions
					// get access to the config, tokens, and other state information useful
					// to set up its own host only.
					controller := &setup.Controller{
						Config:    &config,
						Dispenser: parse.NewDispenserTokens(filename, tokens),
						OncePerServerBlock: func(f func() error) error {
							var err error
							onces[dir.name].Do(func() {
								err = f()
							})
							return err
						},
						ServerBlockIndex:     i,
						ServerBlockHostIndex: j,
						ServerBlockHosts:     sb.HostList(),
						ServerBlockStorage:   storages[dir.name],
					}
					// execute setup function and append middleware handler, if any
					midware, err := dir.setup(controller)
					if err != nil {
						return nil, err
					}
					if midware != nil {
						// TODO: For now, we only support the default path scope /
						config.Middleware["/"] = append(config.Middleware["/"], midware)
					}
					storages[dir.name] = controller.ServerBlockStorage // persist for this server block
				}

				// Stop after TLS setup, since we need to activate Let's Encrypt before continuing;
				// it makes some changes to the configs that middlewares might want to know about.
				if dir.name == "tls" {
					lastDirectiveIndex = k
					break
				}
			}

			configs = append(configs, config)
		}
	}

	// Now we have all the configs, but they have only been set up to the
	// point of tls. We need to activate Let's Encrypt before setting up
	// the rest of the middlewares so they have correct information regarding
	// TLS configuration, if necessary. (this call is append-only, so our
	// iterations below shouldn't be affected)
	configs, err = letsencrypt.Activate(configs)
	if err != nil {
		return nil, err
	}

	// Finish setting up the rest of the directives, now that TLS is
	// optimally configured. These loops are similar to above except
	// we don't iterate all the directives from the beginning and we
	// don't create new configs.
	configIndex := -1
	for i, sb := range serverBlocks {
		onces := makeOnces()
		storages := makeStorages()

		for j := range sb.Addresses {
			configIndex++

			for k := lastDirectiveIndex + 1; k < len(directiveOrder); k++ {
				dir := directiveOrder[k]

				if tokens, ok := sb.Tokens[dir.name]; ok {
					controller := &setup.Controller{
						Config:    &configs[configIndex],
						Dispenser: parse.NewDispenserTokens(filename, tokens),
						OncePerServerBlock: func(f func() error) error {
							var err error
							onces[dir.name].Do(func() {
								err = f()
							})
							return err
						},
						ServerBlockIndex:     i,
						ServerBlockHostIndex: j,
						ServerBlockHosts:     sb.HostList(),
						ServerBlockStorage:   storages[dir.name],
					}
					midware, err := dir.setup(controller)
					if err != nil {
						return nil, err
					}
					if midware != nil {
						// TODO: For now, we only support the default path scope /
						configs[configIndex].Middleware["/"] = append(configs[configIndex].Middleware["/"], midware)
					}
					storages[dir.name] = controller.ServerBlockStorage // persist for this server block
				}
			}
		}
	}

	// restore logging settings
	log.SetFlags(flags)

	return configs, nil
}

// makeOnces makes a map of directive name to sync.Once
// instance. This is intended to be called once per server
// block when setting up configs so that Setup functions
// for each directive can perform a task just once per
// server block, even if there are multiple hosts on the block.
//
// We need one Once per directive, otherwise the first
// directive to use it would exclude other directives from
// using it at all, which would be a bug.
func makeOnces() map[string]*sync.Once {
	onces := make(map[string]*sync.Once)
	for _, dir := range directiveOrder {
		onces[dir.name] = new(sync.Once)
	}
	return onces
}

// makeStorages makes a map of directive name to interface{}
// so that directives' setup functions can persist state
// between different hosts on the same server block during the
// setup phase.
func makeStorages() map[string]interface{} {
	storages := make(map[string]interface{})
	for _, dir := range directiveOrder {
		storages[dir.name] = nil
	}
	return storages
}

// arrangeBindings groups configurations by their bind address. For example,
// a server that should listen on localhost and another on 127.0.0.1 will
// be grouped into the same address: 127.0.0.1. It will return an error
// if an address is malformed or a TLS listener is configured on the
// same address as a plaintext HTTP listener. The return value is a map of
// bind address to list of configs that would become VirtualHosts on that
// server. Use the keys of the returned map to create listeners, and use
// the associated values to set up the virtualhosts.
func arrangeBindings(allConfigs []server.Config) (Group, error) {
	var groupings Group

	// Group configs by bind address
	for _, conf := range allConfigs {
		// use default port if none is specified
		if conf.Port == "" {
			conf.Port = Port
		}

		bindAddr, warnErr, fatalErr := resolveAddr(conf)
		if fatalErr != nil {
			return groupings, fatalErr
		}
		if warnErr != nil {
			log.Println("[Warning]", warnErr)
		}

		// Make sure to compare the string representation of the address,
		// not the pointer, since a new *TCPAddr is created each time.
		var existing bool
		for i := 0; i < len(groupings); i++ {
			if groupings[i].BindAddr.String() == bindAddr.String() {
				groupings[i].Configs = append(groupings[i].Configs, conf)
				existing = true
				break
			}
		}
		if !existing {
			groupings = append(groupings, BindingMapping{
				BindAddr: bindAddr,
				Configs:  []server.Config{conf},
			})
		}
	}

	// Don't allow HTTP and HTTPS to be served on the same address
	for _, group := range groupings {
		isTLS := group.Configs[0].TLS.Enabled
		for _, config := range group.Configs {
			if config.TLS.Enabled != isTLS {
				thisConfigProto, otherConfigProto := "HTTP", "HTTP"
				if config.TLS.Enabled {
					thisConfigProto = "HTTPS"
				}
				if group.Configs[0].TLS.Enabled {
					otherConfigProto = "HTTPS"
				}
				return groupings, fmt.Errorf("configuration error: Cannot multiplex %s (%s) and %s (%s) on same address",
					group.Configs[0].Address(), otherConfigProto, config.Address(), thisConfigProto)
			}
		}
	}

	return groupings, nil
}

// resolveAddr determines the address (host and port) that a config will
// bind to. The returned address, resolvAddr, should be used to bind the
// listener or group the config with other configs using the same address.
// The first error, if not nil, is just a warning and should be reported
// but execution may continue. The second error, if not nil, is a real
// problem and the server should not be started.
//
// This function handles edge cases gracefully. If a port name like
// "http" or "https" is unknown to the system, this function will
// change them to 80 or 443 respectively. If a hostname fails to
// resolve, that host can still be served but will be listening on
// the wildcard host instead. This function takes care of this for you.
func resolveAddr(conf server.Config) (resolvAddr *net.TCPAddr, warnErr error, fatalErr error) {
	bindHost := conf.BindHost

	// TODO: Do we even need the port? Maybe we just need to look up the host.
	resolvAddr, warnErr = net.ResolveTCPAddr("tcp", net.JoinHostPort(bindHost, conf.Port))
	if warnErr != nil {
		// Most likely the host lookup failed or the port is unknown
		tryPort := conf.Port

		switch errVal := warnErr.(type) {
		case *net.AddrError:
			if errVal.Err == "unknown port" {
				// some odd Linux machines don't support these port names; see issue #136
				switch conf.Port {
				case "http":
					tryPort = "80"
				case "https":
					tryPort = "443"
				}
			}
			resolvAddr, fatalErr = net.ResolveTCPAddr("tcp", net.JoinHostPort(bindHost, tryPort))
			if fatalErr != nil {
				return
			}
		default:
			// the hostname probably couldn't be resolved, just bind to wildcard then
			resolvAddr, fatalErr = net.ResolveTCPAddr("tcp", net.JoinHostPort("0.0.0.0", tryPort))
			if fatalErr != nil {
				return
			}
		}

		return
	}

	return
}

// validDirective returns true if d is a valid
// directive; false otherwise.
func validDirective(d string) bool {
	for _, dir := range directiveOrder {
		if dir.name == d {
			return true
		}
	}
	return false
}

// NewDefault makes a default configuration, which
// is empty except for root, host, and port,
// which are essentials for serving the cwd.
func NewDefault() server.Config {
	return server.Config{
		Root: Root,
		Host: Host,
		Port: Port,
	}
}

// DefaultInput returns the default Caddyfile input
// to use when it is otherwise empty or missing.
func DefaultInput() CaddyfileInput {
	return CaddyfileInput{
		Contents: []byte(fmt.Sprintf("%s:%s\nroot %s", Host, Port, Root)),
	}
}

// These defaults are configurable through the command line
var (
	// Site root
	Root = DefaultRoot

	// Site host
	Host = DefaultHost

	// Site port
	Port = DefaultPort
)

// BindingMapping maps a network address to configurations
// that will bind to it. The order of the configs is important.
type BindingMapping struct {
	BindAddr *net.TCPAddr
	Configs  []server.Config
}

// Group maps network addresses to their configurations.
// Preserving the order of the groupings is important
// (related to graceful shutdown and restart)
// so this is a slice, not a literal map.
type Group []BindingMapping
<commit_msg>Fixed behavior with empty Caddyfile<commit_after>package caddy

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"net"
	"sync"

	"github.com/mholt/caddy/caddy/letsencrypt"
	"github.com/mholt/caddy/caddy/parse"
	"github.com/mholt/caddy/caddy/setup"
	"github.com/mholt/caddy/middleware"
	"github.com/mholt/caddy/server"
)

const (
	// DefaultConfigFile is the name of the configuration file that is loaded
	// by default if no other file is specified.
	DefaultConfigFile = "Caddyfile"
)

// loadConfigs reads input (named filename) and parses it, returning the
// server configurations in the order they appeared in the input. As part
// of this, it activates Let's Encrypt for the configs that are produced.
// Thus, the returned configs are already optimally configured optimally
// for HTTPS.
func loadConfigs(filename string, input io.Reader) ([]server.Config, error) {
	var configs []server.Config

	// turn off timestamp for parsing
	flags := log.Flags()
	log.SetFlags(0)

	// Each server block represents similar hosts/addresses, since they
	// were grouped together in the Caddyfile.
	serverBlocks, err := parse.ServerBlocks(filename, input, true)
	if err != nil {
		return nil, err
	}
	if len(serverBlocks) == 0 {
		newInput := DefaultInput()
		serverBlocks, err = parse.ServerBlocks(newInput.Path(), bytes.NewReader(newInput.Body()), true)
		if err != nil {
			return nil, err
		}
	}

	var lastDirectiveIndex int // we set up directives in two parts; this stores where we left off

	// Iterate each server block and make a config for each one,
	// executing the directives that were parsed in order up to the tls
	// directive; this is because we must activate Let's Encrypt.
	for i, sb := range serverBlocks {
		onces := makeOnces()
		storages := makeStorages()

		for j, addr := range sb.Addresses {
			config := server.Config{
				Host:       addr.Host,
				Port:       addr.Port,
				Root:       Root,
				Middleware: make(map[string][]middleware.Middleware),
				ConfigFile: filename,
				AppName:    AppName,
				AppVersion: AppVersion,
			}

			// It is crucial that directives are executed in the proper order.
			for k, dir := range directiveOrder {
				// Execute directive if it is in the server block
				if tokens, ok := sb.Tokens[dir.name]; ok {
					// Each setup function gets a controller, from which setup functions
					// get access to the config, tokens, and other state information useful
					// to set up its own host only.
					controller := &setup.Controller{
						Config:    &config,
						Dispenser: parse.NewDispenserTokens(filename, tokens),
						OncePerServerBlock: func(f func() error) error {
							var err error
							onces[dir.name].Do(func() {
								err = f()
							})
							return err
						},
						ServerBlockIndex:     i,
						ServerBlockHostIndex: j,
						ServerBlockHosts:     sb.HostList(),
						ServerBlockStorage:   storages[dir.name],
					}
					// execute setup function and append middleware handler, if any
					midware, err := dir.setup(controller)
					if err != nil {
						return nil, err
					}
					if midware != nil {
						// TODO: For now, we only support the default path scope /
						config.Middleware["/"] = append(config.Middleware["/"], midware)
					}
					storages[dir.name] = controller.ServerBlockStorage // persist for this server block
				}

				// Stop after TLS setup, since we need to activate Let's Encrypt before continuing;
				// it makes some changes to the configs that middlewares might want to know about.
				if dir.name == "tls" {
					lastDirectiveIndex = k
					break
				}
			}

			configs = append(configs, config)
		}
	}

	// Now we have all the configs, but they have only been set up to the
	// point of tls. We need to activate Let's Encrypt before setting up
	// the rest of the middlewares so they have correct information regarding
	// TLS configuration, if necessary. (this call is append-only, so our
	// iterations below shouldn't be affected)
	configs, err = letsencrypt.Activate(configs)
	if err != nil {
		return nil, err
	}

	// Finish setting up the rest of the directives, now that TLS is
	// optimally configured. These loops are similar to above except
	// we don't iterate all the directives from the beginning and we
	// don't create new configs.
	configIndex := -1
	for i, sb := range serverBlocks {
		onces := makeOnces()
		storages := makeStorages()

		for j := range sb.Addresses {
			configIndex++

			for k := lastDirectiveIndex + 1; k < len(directiveOrder); k++ {
				dir := directiveOrder[k]

				if tokens, ok := sb.Tokens[dir.name]; ok {
					controller := &setup.Controller{
						Config:    &configs[configIndex],
						Dispenser: parse.NewDispenserTokens(filename, tokens),
						OncePerServerBlock: func(f func() error) error {
							var err error
							onces[dir.name].Do(func() {
								err = f()
							})
							return err
						},
						ServerBlockIndex:     i,
						ServerBlockHostIndex: j,
						ServerBlockHosts:     sb.HostList(),
						ServerBlockStorage:   storages[dir.name],
					}
					midware, err := dir.setup(controller)
					if err != nil {
						return nil, err
					}
					if midware != nil {
						// TODO: For now, we only support the default path scope /
						configs[configIndex].Middleware["/"] = append(configs[configIndex].Middleware["/"], midware)
					}
					storages[dir.name] = controller.ServerBlockStorage // persist for this server block
				}
			}
		}
	}

	// restore logging settings
	log.SetFlags(flags)

	return configs, nil
}

// makeOnces makes a map of directive name to sync.Once
// instance. This is intended to be called once per server
// block when setting up configs so that Setup functions
// for each directive can perform a task just once per
// server block, even if there are multiple hosts on the block.
//
// We need one Once per directive, otherwise the first
// directive to use it would exclude other directives from
// using it at all, which would be a bug.
func makeOnces() map[string]*sync.Once {
	onces := make(map[string]*sync.Once)
	for _, dir := range directiveOrder {
		onces[dir.name] = new(sync.Once)
	}
	return onces
}

// makeStorages makes a map of directive name to interface{}
// so that directives' setup functions can persist state
// between different hosts on the same server block during the
// setup phase.
func makeStorages() map[string]interface{} {
	storages := make(map[string]interface{})
	for _, dir := range directiveOrder {
		storages[dir.name] = nil
	}
	return storages
}

// arrangeBindings groups configurations by their bind address. For example,
// a server that should listen on localhost and another on 127.0.0.1 will
// be grouped into the same address: 127.0.0.1. It will return an error
// if an address is malformed or a TLS listener is configured on the
// same address as a plaintext HTTP listener. The return value is a map of
// bind address to list of configs that would become VirtualHosts on that
// server. Use the keys of the returned map to create listeners, and use
// the associated values to set up the virtualhosts.
func arrangeBindings(allConfigs []server.Config) (Group, error) {
	var groupings Group

	// Group configs by bind address
	for _, conf := range allConfigs {
		// use default port if none is specified
		if conf.Port == "" {
			conf.Port = Port
		}

		bindAddr, warnErr, fatalErr := resolveAddr(conf)
		if fatalErr != nil {
			return groupings, fatalErr
		}
		if warnErr != nil {
			log.Println("[Warning]", warnErr)
		}

		// Make sure to compare the string representation of the address,
		// not the pointer, since a new *TCPAddr is created each time.
		var existing bool
		for i := 0; i < len(groupings); i++ {
			if groupings[i].BindAddr.String() == bindAddr.String() {
				groupings[i].Configs = append(groupings[i].Configs, conf)
				existing = true
				break
			}
		}
		if !existing {
			groupings = append(groupings, BindingMapping{
				BindAddr: bindAddr,
				Configs:  []server.Config{conf},
			})
		}
	}

	// Don't allow HTTP and HTTPS to be served on the same address
	for _, group := range groupings {
		isTLS := group.Configs[0].TLS.Enabled
		for _, config := range group.Configs {
			if config.TLS.Enabled != isTLS {
				thisConfigProto, otherConfigProto := "HTTP", "HTTP"
				if config.TLS.Enabled {
					thisConfigProto = "HTTPS"
				}
				if group.Configs[0].TLS.Enabled {
					otherConfigProto = "HTTPS"
				}
				return groupings, fmt.Errorf("configuration error: Cannot multiplex %s (%s) and %s (%s) on same address",
					group.Configs[0].Address(), otherConfigProto, config.Address(), thisConfigProto)
			}
		}
	}

	return groupings, nil
}

// resolveAddr determines the address (host and port) that a config will
// bind to. The returned address, resolvAddr, should be used to bind the
// listener or group the config with other configs using the same address.
// The first error, if not nil, is just a warning and should be reported
// but execution may continue. The second error, if not nil, is a real
// problem and the server should not be started.
//
// This function handles edge cases gracefully. If a port name like
// "http" or "https" is unknown to the system, this function will
// change them to 80 or 443 respectively. If a hostname fails to
// resolve, that host can still be served but will be listening on
// the wildcard host instead. This function takes care of this for you.
func resolveAddr(conf server.Config) (resolvAddr *net.TCPAddr, warnErr error, fatalErr error) {
	bindHost := conf.BindHost

	// TODO: Do we even need the port? Maybe we just need to look up the host.
	resolvAddr, warnErr = net.ResolveTCPAddr("tcp", net.JoinHostPort(bindHost, conf.Port))
	if warnErr != nil {
		// Most likely the host lookup failed or the port is unknown
		tryPort := conf.Port

		switch errVal := warnErr.(type) {
		case *net.AddrError:
			if errVal.Err == "unknown port" {
				// some odd Linux machines don't support these port names; see issue #136
				switch conf.Port {
				case "http":
					tryPort = "80"
				case "https":
					tryPort = "443"
				}
			}
			resolvAddr, fatalErr = net.ResolveTCPAddr("tcp", net.JoinHostPort(bindHost, tryPort))
			if fatalErr != nil {
				return
			}
		default:
			// the hostname probably couldn't be resolved, just bind to wildcard then
			resolvAddr, fatalErr = net.ResolveTCPAddr("tcp", net.JoinHostPort("0.0.0.0", tryPort))
			if fatalErr != nil {
				return
			}
		}

		return
	}

	return
}

// validDirective returns true if d is a valid
// directive; false otherwise.
func validDirective(d string) bool {
	for _, dir := range directiveOrder {
		if dir.name == d {
			return true
		}
	}
	return false
}

// NewDefault makes a default configuration, which
// is empty except for root, host, and port,
// which are essentials for serving the cwd.
func NewDefault() server.Config {
	return server.Config{
		Root: Root,
		Host: Host,
		Port: Port,
	}
}

// DefaultInput returns the default Caddyfile input
// to use when it is otherwise empty or missing.
func DefaultInput() CaddyfileInput {
	return CaddyfileInput{
		Contents: []byte(fmt.Sprintf("%s:%s\nroot %s", Host, Port, Root)),
	}
}

// These defaults are configurable through the command line
var (
	// Site root
	Root = DefaultRoot

	// Site host
	Host = DefaultHost

	// Site port
	Port = DefaultPort
)

// BindingMapping maps a network address to configurations
// that will bind to it. The order of the configs is important.
type BindingMapping struct {
	BindAddr *net.TCPAddr
	Configs  []server.Config
}

// Group maps network addresses to their configurations.
// Preserving the order of the groupings is important
// (related to graceful shutdown and restart)
// so this is a slice, not a literal map.
type Group []BindingMapping
<|endoftext|>


<commit_before>// cfswrap is a wrapper for the linux mount command
//   to run the cfs fuse client
//
//

package main

import (
	"flag"
	"fmt"
	"os"
	"syscall"
)

// USER and GROUP ID for the root user
const (
	UID  = 0
	GUID = 0
)

func main() {

	var clargs []string
	// Parsing command line arguments
	flag.Parse()

	// Set the path to cfs
	path := "/root/go/bin/cfs"
	cfscmd := "mount"

	// Working with command line arguments to pass them thru to cfs
	clargs = append([]string{path}, cfscmd)
	clargs = append([]string{path}, flag.Args()...)

	// The Credential fields are used to set UID, GID and attitional GIDS of the process
	// You need to run the program as  root to do this
	var cred = &syscall.Credential{Uid: UID, Gid: GUID, Groups: []uint32{}}
	// the Noctty flag is used to detach the process from parent tty
	var sysproc = &syscall.SysProcAttr{Credential: cred, Noctty: true}
	var attr = os.ProcAttr{
		Dir: ".",
		Env: os.Environ(),
		Files: []*os.File{
			os.Stdin,
			os.Stdout,
			os.Stderr,
		},
		Sys: sysproc,
	}
	process, err := os.StartProcess(path, clargs, &attr)
	if err == nil {
		// It is not clear from docs, but Realease actually detaches the process
		err = process.Release()
		if err != nil {
			fmt.Println(err.Error())
		}

	} else {
		fmt.Println(err.Error())
	}
}
<commit_msg>Removed use of flag<commit_after>// cfswrap is a wrapper for the linux mount command
//   to run the cfs fuse client
//
//

package main

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

// USER and GROUP ID for the root user
const (
	UID  = 0
	GUID = 0
)

func main() {

	var clargs []string
	// Parsing command line arguments

	// Set the path to cfs
	path := "/root/go/bin/cfs"
	cfscmd := "mount"

	// Working with command line arguments to pass them thru to cfs
	clargs = append([]string{path}, cfscmd)
	clargs = append([]string{path}, os.Args...)

	fmt.Println(clargs)

	// The Credential fields are used to set UID, GID and attitional GIDS of the process
	// You need to run the program as  root to do this
	var cred = &syscall.Credential{Uid: UID, Gid: GUID, Groups: []uint32{}}
	// the Noctty flag is used to detach the process from parent tty
	var sysproc = &syscall.SysProcAttr{Credential: cred, Noctty: true}
	var attr = os.ProcAttr{
		Dir: ".",
		Env: os.Environ(),
		Files: []*os.File{
			os.Stdin,
			os.Stdout,
			os.Stderr,
		},
		Sys: sysproc,
	}
	process, err := os.StartProcess(path, clargs, &attr)
	if err == nil {
		// It is not clear from docs, but Realease actually detaches the process
		err = process.Release()
		if err != nil {
			fmt.Println(err.Error())
		}

	} else {
		fmt.Println(err.Error())
	}
}
<|endoftext|>


<commit_before>package cli

import (
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/errors"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/elpinal/coco3/config"
	"github.com/elpinal/coco3/eval"
	"github.com/elpinal/coco3/gate"
	"github.com/elpinal/coco3/parser"

	"github.com/elpinal/coco3/extra"
	eparser "github.com/elpinal/coco3/extra/parser"

	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3"
)

type CLI struct {
	In  io.Reader
	Out io.Writer
	Err io.Writer

	*config.Config

	db *sqlx.DB

	execute1 func([]byte) (action, error)
}

func (c *CLI) Run(args []string) int {
	f := flag.NewFlagSet("coco3", flag.ContinueOnError)
	f.SetOutput(c.Err)
	f.Usage = func() {
		c.Err.Write([]byte("coco3 is a shell.\n"))
		c.Err.Write([]byte("Usage:\n"))
		f.PrintDefaults()
	}

	if c.Config == nil {
		c.Config = &config.Config{}
	}
	c.Config.Init()
	flagC := f.String("c", "", "take first argument as a command to execute")
	flagE := f.Bool("extra", c.Config.Extra, "switch to extra mode")
	if err := f.Parse(args); err != nil {
		return 2
	}
	return c.run(f.Args(), flagC, flagE)
}

func (c *CLI) run(args []string, flagC *string, flagE *bool) int {
	// Aliases, only available for non-extra mode.
	for _, alias := range c.Config.Alias {
		eval.DefAlias(alias[0], alias[1])
	}

	// Prepare environment.
	for k, v := range c.Config.Env {
		err := os.Setenv(k, v)
		if err != nil {
			c.errorln(err)
			return 1
		}
	}

	setpath(c.Config.Paths)

	if *flagE {
		// If -extra flag is on, enable extra mode on any command executions.
		c.execute1 = c.executeExtra
	} else {
		c.execute1 = c.execute
	}

	if len(c.Config.StartUpCommand) > 0 {
		a, err := c.execute1(c.Config.StartUpCommand)
		if err != nil {
			c.printExecError(err)
			return 1
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
	}

	// The -c flag.
	if *flagC != "" {
		return c.fromArg(*flagC)
	}

	// Execute files.
	if len(args) > 0 {
		return c.executeFiles(args)
	}

	// Interactive mode.
	return c.runInteractiveMode()
}

func (c *CLI) fromArg(program string) int {
	a, err := c.execute1([]byte(program))
	if err != nil {
		c.printExecError(err)
		return 1
	}
	if e, ok := a.(exit); ok {
		return e.code
	}
	return 0
}

func (c *CLI) executeFiles(args []string) int {
	a, err := c.runFiles(args)
	if err != nil {
		c.printExecError(err)
		return 1
	}
	if e, ok := a.(exit); ok {
		return e.code
	}
	return 0
}

func (c *CLI) runInteractiveMode() int {
	// Inherit history.
	histRunes, err := c.getHistory(c.Config.HistFile)
	if err != nil {
		c.errorln(err)
		return 1
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	g := gate.NewContext(ctx, c.Config, c.In, c.Out, c.Err, histRunes)
	for {
		a, err := c.interact(g)
		if err != nil {
			c.printExecError(err)
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
	}
	return 0
}

func (c *CLI) errorf(s string, a ...interface{}) {
	fmt.Fprintf(c.Err, s, a...)
}

func (c *CLI) errorln(s ...interface{}) {
	fmt.Fprintln(c.Err, s...)
}

func (c *CLI) errorp(s ...interface{}) {
	fmt.Fprint(c.Err, s...)
}

func (c *CLI) getHistory(filename string) ([][]rune, error) {
	db, err := sqlx.Connect("sqlite3", filename)
	if err != nil {
		return nil, errors.Wrap(err, "connecting history file")
	}
	_, err = db.Exec(schema)
	if err != nil {
		return nil, errors.Wrap(err, "initializing history file")
	}
	var history []string
	err = db.Select(&history, "select line from command_info")
	if err != nil {
		return nil, errors.Wrap(err, "restoring history")
	}
	// TODO: Is this way proper?
	c.db = db
	return sanitizeHistory(history), nil
}

func (c *CLI) printExecError(err error) {
	if pe, ok := err.(*eparser.ParseError); ok {
		c.errorln(pe.Verbose())
	} else {
		c.errorln(err)
	}
}

// setpath sets the PATH environment variable.
func setpath(args []string) {
	if len(args) == 0 {
		return
	}
	paths := filepath.SplitList(os.Getenv("PATH"))
	var newPaths []string
	for _, path := range paths {
		if contains(args, path) {
			continue
		}
		newPaths = append(newPaths, path)
	}
	newPaths = append(args, newPaths...)
	os.Setenv("PATH", strings.Join(newPaths, string(filepath.ListSeparator)))
}

func contains(xs []string, s string) bool {
	for _, x := range xs {
		if x == s {
			return true
		}
	}
	return false
}

func sanitizeHistory(history []string) [][]rune {
	histRunes := make([][]rune, 0, len(history))
	for _, line := range history {
		if line == "" {
			continue
		}
		l := len(histRunes)
		s := []rune(line)
		if l > 0 && compareRunes(histRunes[l-1], s) {
			continue
		}
		histRunes = append(histRunes, s)
	}
	return histRunes
}

func compareRunes(r1, r2 []rune) bool {
	if len(r1) != len(r2) {
		return false
	}
	for i, r := range r1 {
		if r2[i] != r {
			return false
		}
	}
	return true
}

func (c *CLI) interact(g gate.Gate) (action, error) {
	r, end, err := c.read(g)
	if err != nil {
		return nil, err
	}
	if end {
		return exitSuccess, nil
	}
	ch := c.writeHistory(r)
	a, err := c.execute1([]byte(string(r)))
	if err != nil {
		return a, err
	}
	return a, <-ch
}

func (c *CLI) read(g gate.Gate) ([]rune, bool, error) {
	defer c.Out.Write([]byte{'\n'})
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		return nil, false, err
	}
	defer func() {
		if err := terminal.Restore(0, oldState); err != nil {
			c.errorln(err)
		}
	}()
	r, end, err := g.Read()
	if err != nil {
		return nil, false, err
	}
	return r, end, nil
}

func (c *CLI) writeHistory(r []rune) <-chan error {
	startTime := time.Now()
	ch := make(chan error)
	go func() {
		_, err := c.db.Exec("insert into command_info (time, line) values ($1, $2)", startTime, string(r))
		if err != nil {
			ch <- errors.Wrap(err, "saving history")
			return
		}
		ch <- nil
	}()
	return ch
}

const schema = `
create table if not exists command_info (
    time datetime,
    line text
)`

func (c *CLI) execute(b []byte) (action, error) {
	f, err := parser.ParseSrc(b)
	if err != nil {
		return nil, err
	}
	e := eval.New(c.In, c.Out, c.Err, c.db)
	err = e.Eval(f.Lines)
	select {
	case code := <-e.ExitCh:
		return exit{code}, nil
	default:
	}
	return nil, err
}

func (c *CLI) executeExtra(b []byte) (action, error) {
	cmd, err := eparser.Parse(b)
	if err != nil {
		return nil, err
	}
	e := extra.New(extra.Option{DB: c.db})
	err = e.Eval(cmd)
	if err == nil {
		return nil, nil
	}
	if pe, ok := err.(*eparser.ParseError); ok {
		pe.Src = string(b)
	}
	return nil, err
}

func (c *CLI) runFiles(files []string) (action, error) {
	for _, file := range files {
		b, err := ioutil.ReadFile(file)
		if err != nil {
			return nil, err
		}
		a, err := c.execute1(b)
		if err != nil {
			return nil, err
		}
		if a != nil {
			return a, nil
		}
	}
	return nil, nil
}

type action interface {
	act()
}

type exit struct {
	code int
}

func (e exit) act() {}

var exitSuccess = exit{0}
<commit_msg>Use switch instead of two if-expressions<commit_after>package cli

import (
	"context"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/pkg/errors"

	"golang.org/x/crypto/ssh/terminal"

	"github.com/elpinal/coco3/config"
	"github.com/elpinal/coco3/eval"
	"github.com/elpinal/coco3/gate"
	"github.com/elpinal/coco3/parser"

	"github.com/elpinal/coco3/extra"
	eparser "github.com/elpinal/coco3/extra/parser"

	"github.com/jmoiron/sqlx"
	_ "github.com/mattn/go-sqlite3"
)

type CLI struct {
	In  io.Reader
	Out io.Writer
	Err io.Writer

	*config.Config

	db *sqlx.DB

	execute1 func([]byte) (action, error)
}

func (c *CLI) Run(args []string) int {
	f := flag.NewFlagSet("coco3", flag.ContinueOnError)
	f.SetOutput(c.Err)
	f.Usage = func() {
		c.Err.Write([]byte("coco3 is a shell.\n"))
		c.Err.Write([]byte("Usage:\n"))
		f.PrintDefaults()
	}

	if c.Config == nil {
		c.Config = &config.Config{}
	}
	c.Config.Init()
	flagC := f.String("c", "", "take first argument as a command to execute")
	flagE := f.Bool("extra", c.Config.Extra, "switch to extra mode")
	if err := f.Parse(args); err != nil {
		return 2
	}
	return c.run(f.Args(), flagC, flagE)
}

func (c *CLI) run(args []string, flagC *string, flagE *bool) int {
	// Aliases, only available for non-extra mode.
	for _, alias := range c.Config.Alias {
		eval.DefAlias(alias[0], alias[1])
	}

	// Prepare environment.
	for k, v := range c.Config.Env {
		err := os.Setenv(k, v)
		if err != nil {
			c.errorln(err)
			return 1
		}
	}

	setpath(c.Config.Paths)

	if *flagE {
		// If -extra flag is on, enable extra mode on any command executions.
		c.execute1 = c.executeExtra
	} else {
		c.execute1 = c.execute
	}

	if len(c.Config.StartUpCommand) > 0 {
		a, err := c.execute1(c.Config.StartUpCommand)
		if err != nil {
			c.printExecError(err)
			return 1
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
	}

	switch {
	case *flagC != "":
		// The -c flag.
		return c.fromArg(*flagC)
	case len(args) > 0:
		// Execute files.
		return c.executeFiles(args)
	default:
		// Interactive mode.
		return c.runInteractiveMode()
	}
}

func (c *CLI) fromArg(program string) int {
	a, err := c.execute1([]byte(program))
	if err != nil {
		c.printExecError(err)
		return 1
	}
	if e, ok := a.(exit); ok {
		return e.code
	}
	return 0
}

func (c *CLI) executeFiles(args []string) int {
	a, err := c.runFiles(args)
	if err != nil {
		c.printExecError(err)
		return 1
	}
	if e, ok := a.(exit); ok {
		return e.code
	}
	return 0
}

func (c *CLI) runInteractiveMode() int {
	// Inherit history.
	histRunes, err := c.getHistory(c.Config.HistFile)
	if err != nil {
		c.errorln(err)
		return 1
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	g := gate.NewContext(ctx, c.Config, c.In, c.Out, c.Err, histRunes)
	for {
		a, err := c.interact(g)
		if err != nil {
			c.printExecError(err)
		}
		if e, ok := a.(exit); ok {
			return e.code
		}
	}
	return 0
}

func (c *CLI) errorf(s string, a ...interface{}) {
	fmt.Fprintf(c.Err, s, a...)
}

func (c *CLI) errorln(s ...interface{}) {
	fmt.Fprintln(c.Err, s...)
}

func (c *CLI) errorp(s ...interface{}) {
	fmt.Fprint(c.Err, s...)
}

func (c *CLI) getHistory(filename string) ([][]rune, error) {
	db, err := sqlx.Connect("sqlite3", filename)
	if err != nil {
		return nil, errors.Wrap(err, "connecting history file")
	}
	_, err = db.Exec(schema)
	if err != nil {
		return nil, errors.Wrap(err, "initializing history file")
	}
	var history []string
	err = db.Select(&history, "select line from command_info")
	if err != nil {
		return nil, errors.Wrap(err, "restoring history")
	}
	// TODO: Is this way proper?
	c.db = db
	return sanitizeHistory(history), nil
}

func (c *CLI) printExecError(err error) {
	if pe, ok := err.(*eparser.ParseError); ok {
		c.errorln(pe.Verbose())
	} else {
		c.errorln(err)
	}
}

// setpath sets the PATH environment variable.
func setpath(args []string) {
	if len(args) == 0 {
		return
	}
	paths := filepath.SplitList(os.Getenv("PATH"))
	var newPaths []string
	for _, path := range paths {
		if contains(args, path) {
			continue
		}
		newPaths = append(newPaths, path)
	}
	newPaths = append(args, newPaths...)
	os.Setenv("PATH", strings.Join(newPaths, string(filepath.ListSeparator)))
}

func contains(xs []string, s string) bool {
	for _, x := range xs {
		if x == s {
			return true
		}
	}
	return false
}

func sanitizeHistory(history []string) [][]rune {
	histRunes := make([][]rune, 0, len(history))
	for _, line := range history {
		if line == "" {
			continue
		}
		l := len(histRunes)
		s := []rune(line)
		if l > 0 && compareRunes(histRunes[l-1], s) {
			continue
		}
		histRunes = append(histRunes, s)
	}
	return histRunes
}

func compareRunes(r1, r2 []rune) bool {
	if len(r1) != len(r2) {
		return false
	}
	for i, r := range r1 {
		if r2[i] != r {
			return false
		}
	}
	return true
}

func (c *CLI) interact(g gate.Gate) (action, error) {
	r, end, err := c.read(g)
	if err != nil {
		return nil, err
	}
	if end {
		return exitSuccess, nil
	}
	ch := c.writeHistory(r)
	a, err := c.execute1([]byte(string(r)))
	if err != nil {
		return a, err
	}
	return a, <-ch
}

func (c *CLI) read(g gate.Gate) ([]rune, bool, error) {
	defer c.Out.Write([]byte{'\n'})
	oldState, err := terminal.MakeRaw(0)
	if err != nil {
		return nil, false, err
	}
	defer func() {
		if err := terminal.Restore(0, oldState); err != nil {
			c.errorln(err)
		}
	}()
	r, end, err := g.Read()
	if err != nil {
		return nil, false, err
	}
	return r, end, nil
}

func (c *CLI) writeHistory(r []rune) <-chan error {
	startTime := time.Now()
	ch := make(chan error)
	go func() {
		_, err := c.db.Exec("insert into command_info (time, line) values ($1, $2)", startTime, string(r))
		if err != nil {
			ch <- errors.Wrap(err, "saving history")
			return
		}
		ch <- nil
	}()
	return ch
}

const schema = `
create table if not exists command_info (
    time datetime,
    line text
)`

func (c *CLI) execute(b []byte) (action, error) {
	f, err := parser.ParseSrc(b)
	if err != nil {
		return nil, err
	}
	e := eval.New(c.In, c.Out, c.Err, c.db)
	err = e.Eval(f.Lines)
	select {
	case code := <-e.ExitCh:
		return exit{code}, nil
	default:
	}
	return nil, err
}

func (c *CLI) executeExtra(b []byte) (action, error) {
	cmd, err := eparser.Parse(b)
	if err != nil {
		return nil, err
	}
	e := extra.New(extra.Option{DB: c.db})
	err = e.Eval(cmd)
	if err == nil {
		return nil, nil
	}
	if pe, ok := err.(*eparser.ParseError); ok {
		pe.Src = string(b)
	}
	return nil, err
}

func (c *CLI) runFiles(files []string) (action, error) {
	for _, file := range files {
		b, err := ioutil.ReadFile(file)
		if err != nil {
			return nil, err
		}
		a, err := c.execute1(b)
		if err != nil {
			return nil, err
		}
		if a != nil {
			return a, nil
		}
	}
	return nil, nil
}

type action interface {
	act()
}

type exit struct {
	code int
}

func (e exit) act() {}

var exitSuccess = exit{0}
<|endoftext|>