<commit_before>// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package proc

import (
	"bytes"
	"fmt"
	"sort"
	"strconv"

	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/context"
	"gvisor.dev/gvisor/pkg/refs"
	"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
	"gvisor.dev/gvisor/pkg/sentry/kernel"
	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
	"gvisor.dev/gvisor/pkg/sentry/vfs"
	"gvisor.dev/gvisor/pkg/syserror"
)

type fdDir struct {
	inoGen InoGenerator
	task   *kernel.Task

	// When produceSymlinks is set, dirents produces for the FDs are reported
	// as symlink. Otherwise, they are reported as regular files.
	produceSymlink bool
}

func (i *fdDir) lookup(name string) (*vfs.FileDescription, kernel.FDFlags, error) {
	fd, err := strconv.ParseUint(name, 10, 64)
	if err != nil {
		return nil, kernel.FDFlags{}, syserror.ENOENT
	}

	var (
		file  *vfs.FileDescription
		flags kernel.FDFlags
	)
	i.task.WithMuLocked(func(t *kernel.Task) {
		if fdTable := t.FDTable(); fdTable != nil {
			file, flags = fdTable.GetVFS2(int32(fd))
		}
	})
	if file == nil {
		return nil, kernel.FDFlags{}, syserror.ENOENT
	}
	return file, flags, nil
}

// IterDirents implements kernfs.inodeDynamicLookup.
func (i *fdDir) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback, absOffset, relOffset int64) (int64, error) {
	var fds []int32
	i.task.WithMuLocked(func(t *kernel.Task) {
		if fdTable := t.FDTable(); fdTable != nil {
			fds = fdTable.GetFDs()
		}
	})

	offset := absOffset + relOffset
	typ := uint8(linux.DT_REG)
	if i.produceSymlink {
		typ = linux.DT_LNK
	}

	// Find the appropriate starting point.
	idx := sort.Search(len(fds), func(i int) bool { return fds[i] >= int32(relOffset) })
	if idx >= len(fds) {
		return offset, nil
	}
	for _, fd := range fds[idx:] {
		dirent := vfs.Dirent{
			Name:    strconv.FormatUint(uint64(fd), 10),
			Type:    typ,
			Ino:     i.inoGen.NextIno(),
			NextOff: offset + 1,
		}
		if err := cb.Handle(dirent); err != nil {
			return offset, err
		}
		offset++
	}
	return offset, nil
}

// fdDirInode represents the inode for /proc/[pid]/fd directory.
//
// +stateify savable
type fdDirInode struct {
	kernfs.InodeNotSymlink
	kernfs.InodeDirectoryNoNewChildren
	kernfs.InodeAttrs
	kernfs.OrderedChildren
	kernfs.AlwaysValid
	fdDir
}

var _ kernfs.Inode = (*fdDirInode)(nil)

func newFDDirInode(task *kernel.Task, inoGen InoGenerator) *kernfs.Dentry {
	inode := &fdDirInode{
		fdDir: fdDir{
			inoGen:         inoGen,
			task:           task,
			produceSymlink: true,
		},
	}
	inode.InodeAttrs.Init(task.Credentials(), inoGen.NextIno(), linux.ModeDirectory|0555)

	dentry := &kernfs.Dentry{}
	dentry.Init(inode)
	inode.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})

	return dentry
}

// Lookup implements kernfs.inodeDynamicLookup.
func (i *fdDirInode) Lookup(ctx context.Context, name string) (*vfs.Dentry, error) {
	file, _, err := i.lookup(name)
	if err != nil {
		return nil, err
	}
	taskDentry := newFDSymlink(i.task.Credentials(), file, i.inoGen.NextIno())
	return taskDentry.VFSDentry(), nil
}

// Open implements kernfs.Inode.
func (i *fdDirInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
	fd := &kernfs.GenericDirectoryFD{}
	fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
	return fd.VFSFileDescription(), nil
}

// CheckPermissions implements kernfs.Inode.
//
// This is to match Linux, which uses a special permission handler to guarantee
// that a process can still access /proc/self/fd after it has executed
// setuid. See fs/proc/fd.c:proc_fd_permission.
func (i *fdDirInode) CheckPermissions(ctx context.Context, creds *auth.Credentials, ats vfs.AccessTypes) error {
	err := i.InodeAttrs.CheckPermissions(ctx, creds, ats)
	if err == nil {
		// Access granted, no extra check needed.
		return nil
	}
	if t := kernel.TaskFromContext(ctx); t != nil {
		// Allow access if the task trying to access it is in the thread group
		// corresponding to this directory.
		if i.task.ThreadGroup() == t.ThreadGroup() {
			// Access granted (overridden).
			return nil
		}
	}
	return err
}

// fdSymlink is an symlink for the /proc/[pid]/fd/[fd] file.
//
// +stateify savable
type fdSymlink struct {
	refs.AtomicRefCount
	kernfs.InodeAttrs
	kernfs.InodeSymlink

	file *vfs.FileDescription
}

var _ kernfs.Inode = (*fdSymlink)(nil)

func newFDSymlink(creds *auth.Credentials, file *vfs.FileDescription, ino uint64) *kernfs.Dentry {
	file.IncRef()
	inode := &fdSymlink{file: file}
	inode.Init(creds, ino, linux.ModeSymlink|0777)

	d := &kernfs.Dentry{}
	d.Init(inode)
	return d
}

func (s *fdSymlink) Readlink(ctx context.Context) (string, error) {
	root := vfs.RootFromContext(ctx)
	defer root.DecRef()

	vfsObj := s.file.VirtualDentry().Mount().Filesystem().VirtualFilesystem()
	return vfsObj.PathnameWithDeleted(ctx, root, s.file.VirtualDentry())
}

func (s *fdSymlink) Getlink(ctx context.Context) (vfs.VirtualDentry, string, error) {
	vd := s.file.VirtualDentry()
	vd.IncRef()
	return vd, "", nil
}

func (s *fdSymlink) DecRef() {
	s.AtomicRefCount.DecRefWithDestructor(func() {
		s.Destroy()
	})
}

func (s *fdSymlink) Destroy() {
	s.file.DecRef()
}

// fdInfoDirInode represents the inode for /proc/[pid]/fdinfo directory.
//
// +stateify savable
type fdInfoDirInode struct {
	kernfs.InodeNotSymlink
	kernfs.InodeDirectoryNoNewChildren
	kernfs.InodeAttrs
	kernfs.OrderedChildren
	kernfs.AlwaysValid
	fdDir
}

var _ kernfs.Inode = (*fdInfoDirInode)(nil)

func newFDInfoDirInode(task *kernel.Task, inoGen InoGenerator) *kernfs.Dentry {
	inode := &fdInfoDirInode{
		fdDir: fdDir{
			inoGen: inoGen,
			task:   task,
		},
	}
	inode.InodeAttrs.Init(task.Credentials(), inoGen.NextIno(), linux.ModeDirectory|0555)

	dentry := &kernfs.Dentry{}
	dentry.Init(inode)
	inode.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})

	return dentry
}

// Lookup implements kernfs.inodeDynamicLookup.
func (i *fdInfoDirInode) Lookup(ctx context.Context, name string) (*vfs.Dentry, error) {
	file, flags, err := i.lookup(name)
	if err != nil {
		return nil, err
	}

	data := &fdInfoData{file: file, flags: flags}
	dentry := newTaskOwnedFile(i.task, i.inoGen.NextIno(), 0444, data)
	return dentry.VFSDentry(), nil
}

// Open implements kernfs.Inode.
func (i *fdInfoDirInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
	fd := &kernfs.GenericDirectoryFD{}
	fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
	return fd.VFSFileDescription(), nil
}

// fdInfoData implements vfs.DynamicBytesSource for /proc/[pid]/fdinfo/[fd].
//
// +stateify savable
type fdInfoData struct {
	kernfs.DynamicBytesFile
	refs.AtomicRefCount

	file  *vfs.FileDescription
	flags kernel.FDFlags
}

var _ dynamicInode = (*fdInfoData)(nil)

func (d *fdInfoData) DecRef() {
	d.AtomicRefCount.DecRefWithDestructor(d.destroy)
}

func (d *fdInfoData) destroy() {
	d.file.DecRef()
}

// Generate implements vfs.DynamicBytesSource.Generate.
func (d *fdInfoData) Generate(ctx context.Context, buf *bytes.Buffer) error {
	// TODO(b/121266871): Include pos, locks, and other data. For now we only
	// have flags.
	// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
	flags := uint(d.file.StatusFlags()) | d.flags.ToLinuxFileFlags()
	fmt.Fprintf(buf, "flags:\t0%o\n", flags)
	return nil
}
<commit_msg>Do not hold FileDescription references in VFS2 procfs inodes.<commit_after>// Copyright 2020 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package proc

import (
	"bytes"
	"fmt"
	"sort"
	"strconv"

	"gvisor.dev/gvisor/pkg/abi/linux"
	"gvisor.dev/gvisor/pkg/context"
	"gvisor.dev/gvisor/pkg/refs"
	"gvisor.dev/gvisor/pkg/sentry/fsimpl/kernfs"
	"gvisor.dev/gvisor/pkg/sentry/kernel"
	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
	"gvisor.dev/gvisor/pkg/sentry/vfs"
	"gvisor.dev/gvisor/pkg/syserror"
)

func getTaskFD(t *kernel.Task, fd int32) (*vfs.FileDescription, kernel.FDFlags) {
	var (
		file  *vfs.FileDescription
		flags kernel.FDFlags
	)
	t.WithMuLocked(func(t *kernel.Task) {
		if fdt := t.FDTable(); fdt != nil {
			file, flags = fdt.GetVFS2(fd)
		}
	})
	return file, flags
}

func taskFDExists(t *kernel.Task, fd int32) bool {
	file, _ := getTaskFD(t, fd)
	if file == nil {
		return false
	}
	file.DecRef()
	return true
}

type fdDir struct {
	inoGen InoGenerator
	task   *kernel.Task

	// When produceSymlinks is set, dirents produces for the FDs are reported
	// as symlink. Otherwise, they are reported as regular files.
	produceSymlink bool
}

// IterDirents implements kernfs.inodeDynamicLookup.
func (i *fdDir) IterDirents(ctx context.Context, cb vfs.IterDirentsCallback, absOffset, relOffset int64) (int64, error) {
	var fds []int32
	i.task.WithMuLocked(func(t *kernel.Task) {
		if fdTable := t.FDTable(); fdTable != nil {
			fds = fdTable.GetFDs()
		}
	})

	offset := absOffset + relOffset
	typ := uint8(linux.DT_REG)
	if i.produceSymlink {
		typ = linux.DT_LNK
	}

	// Find the appropriate starting point.
	idx := sort.Search(len(fds), func(i int) bool { return fds[i] >= int32(relOffset) })
	if idx >= len(fds) {
		return offset, nil
	}
	for _, fd := range fds[idx:] {
		dirent := vfs.Dirent{
			Name:    strconv.FormatUint(uint64(fd), 10),
			Type:    typ,
			Ino:     i.inoGen.NextIno(),
			NextOff: offset + 1,
		}
		if err := cb.Handle(dirent); err != nil {
			return offset, err
		}
		offset++
	}
	return offset, nil
}

// fdDirInode represents the inode for /proc/[pid]/fd directory.
//
// +stateify savable
type fdDirInode struct {
	kernfs.InodeNotSymlink
	kernfs.InodeDirectoryNoNewChildren
	kernfs.InodeAttrs
	kernfs.OrderedChildren
	kernfs.AlwaysValid
	fdDir
}

var _ kernfs.Inode = (*fdDirInode)(nil)

func newFDDirInode(task *kernel.Task, inoGen InoGenerator) *kernfs.Dentry {
	inode := &fdDirInode{
		fdDir: fdDir{
			inoGen:         inoGen,
			task:           task,
			produceSymlink: true,
		},
	}
	inode.InodeAttrs.Init(task.Credentials(), inoGen.NextIno(), linux.ModeDirectory|0555)

	dentry := &kernfs.Dentry{}
	dentry.Init(inode)
	inode.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})

	return dentry
}

// Lookup implements kernfs.inodeDynamicLookup.
func (i *fdDirInode) Lookup(ctx context.Context, name string) (*vfs.Dentry, error) {
	fdInt, err := strconv.ParseInt(name, 10, 32)
	if err != nil {
		return nil, syserror.ENOENT
	}
	fd := int32(fdInt)
	if !taskFDExists(i.task, fd) {
		return nil, syserror.ENOENT
	}
	taskDentry := newFDSymlink(i.task, fd, i.inoGen.NextIno())
	return taskDentry.VFSDentry(), nil
}

// Open implements kernfs.Inode.
func (i *fdDirInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
	fd := &kernfs.GenericDirectoryFD{}
	fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
	return fd.VFSFileDescription(), nil
}

// CheckPermissions implements kernfs.Inode.
//
// This is to match Linux, which uses a special permission handler to guarantee
// that a process can still access /proc/self/fd after it has executed
// setuid. See fs/proc/fd.c:proc_fd_permission.
func (i *fdDirInode) CheckPermissions(ctx context.Context, creds *auth.Credentials, ats vfs.AccessTypes) error {
	err := i.InodeAttrs.CheckPermissions(ctx, creds, ats)
	if err == nil {
		// Access granted, no extra check needed.
		return nil
	}
	if t := kernel.TaskFromContext(ctx); t != nil {
		// Allow access if the task trying to access it is in the thread group
		// corresponding to this directory.
		if i.task.ThreadGroup() == t.ThreadGroup() {
			// Access granted (overridden).
			return nil
		}
	}
	return err
}

// fdSymlink is an symlink for the /proc/[pid]/fd/[fd] file.
//
// +stateify savable
type fdSymlink struct {
	kernfs.InodeAttrs
	kernfs.InodeNoopRefCount
	kernfs.InodeSymlink

	task *kernel.Task
	fd   int32
}

var _ kernfs.Inode = (*fdSymlink)(nil)

func newFDSymlink(task *kernel.Task, fd int32, ino uint64) *kernfs.Dentry {
	inode := &fdSymlink{
		task: task,
		fd:   fd,
	}
	inode.Init(task.Credentials(), ino, linux.ModeSymlink|0777)

	d := &kernfs.Dentry{}
	d.Init(inode)
	return d
}

func (s *fdSymlink) Readlink(ctx context.Context) (string, error) {
	file, _ := getTaskFD(s.task, s.fd)
	if file == nil {
		return "", syserror.ENOENT
	}
	defer file.DecRef()
	root := vfs.RootFromContext(ctx)
	defer root.DecRef()
	return s.task.Kernel().VFS().PathnameWithDeleted(ctx, root, file.VirtualDentry())
}

func (s *fdSymlink) Getlink(ctx context.Context) (vfs.VirtualDentry, string, error) {
	file, _ := getTaskFD(s.task, s.fd)
	if file == nil {
		return vfs.VirtualDentry{}, "", syserror.ENOENT
	}
	defer file.DecRef()
	vd := file.VirtualDentry()
	vd.IncRef()
	return vd, "", nil
}

// fdInfoDirInode represents the inode for /proc/[pid]/fdinfo directory.
//
// +stateify savable
type fdInfoDirInode struct {
	kernfs.InodeNotSymlink
	kernfs.InodeDirectoryNoNewChildren
	kernfs.InodeAttrs
	kernfs.OrderedChildren
	kernfs.AlwaysValid
	fdDir
}

var _ kernfs.Inode = (*fdInfoDirInode)(nil)

func newFDInfoDirInode(task *kernel.Task, inoGen InoGenerator) *kernfs.Dentry {
	inode := &fdInfoDirInode{
		fdDir: fdDir{
			inoGen: inoGen,
			task:   task,
		},
	}
	inode.InodeAttrs.Init(task.Credentials(), inoGen.NextIno(), linux.ModeDirectory|0555)

	dentry := &kernfs.Dentry{}
	dentry.Init(inode)
	inode.OrderedChildren.Init(kernfs.OrderedChildrenOptions{})

	return dentry
}

// Lookup implements kernfs.inodeDynamicLookup.
func (i *fdInfoDirInode) Lookup(ctx context.Context, name string) (*vfs.Dentry, error) {
	fdInt, err := strconv.ParseInt(name, 10, 32)
	if err != nil {
		return nil, syserror.ENOENT
	}
	fd := int32(fdInt)
	if !taskFDExists(i.task, fd) {
		return nil, syserror.ENOENT
	}
	data := &fdInfoData{
		task: i.task,
		fd:   fd,
	}
	dentry := newTaskOwnedFile(i.task, i.inoGen.NextIno(), 0444, data)
	return dentry.VFSDentry(), nil
}

// Open implements kernfs.Inode.
func (i *fdInfoDirInode) Open(rp *vfs.ResolvingPath, vfsd *vfs.Dentry, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
	fd := &kernfs.GenericDirectoryFD{}
	fd.Init(rp.Mount(), vfsd, &i.OrderedChildren, &opts)
	return fd.VFSFileDescription(), nil
}

// fdInfoData implements vfs.DynamicBytesSource for /proc/[pid]/fdinfo/[fd].
//
// +stateify savable
type fdInfoData struct {
	kernfs.DynamicBytesFile
	refs.AtomicRefCount

	task *kernel.Task
	fd   int32
}

var _ dynamicInode = (*fdInfoData)(nil)

// Generate implements vfs.DynamicBytesSource.Generate.
func (d *fdInfoData) Generate(ctx context.Context, buf *bytes.Buffer) error {
	file, descriptorFlags := getTaskFD(d.task, d.fd)
	if file == nil {
		return syserror.ENOENT
	}
	defer file.DecRef()
	// TODO(b/121266871): Include pos, locks, and other data. For now we only
	// have flags.
	// See https://www.kernel.org/doc/Documentation/filesystems/proc.txt
	flags := uint(file.StatusFlags()) | descriptorFlags.ToLinuxFileFlags()
	fmt.Fprintf(buf, "flags:\t0%o\n", flags)
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Skaffold Authors

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

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

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

package kubectl

import (
	"context"
	"io"
	"os/exec"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha2"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// CLI holds parameters to run kubectl.
type CLI struct {
	Namespace   string
	KubeContext string
	Flags       v1alpha2.KubectlFlags

	previousApply ManifestList
}

// Detete runs `kubectl delete` on a list of manifests.
func (c *CLI) Detete(ctx context.Context, out io.Writer, manifests ManifestList) error {
	if err := c.Run(ctx, manifests.Reader(), out, "delete", c.Flags.Delete, "--ignore-not-found=true", "-f", "-"); err != nil {
		return errors.Wrap(err, "kubectl delete")
	}

	return nil
}

// Apply runs `kubectl apply` on a list of manifests.
func (c *CLI) Apply(ctx context.Context, out io.Writer, manifests ManifestList) (ManifestList, error) {
	// Only redeploy modified or new manifests
	// TODO(dgageot): should we delete a manifest that was deployed and is not anymore?
	updated := c.previousApply.Diff(manifests)
	logrus.Debugln(len(manifests), "manifests to deploy.", len(manifests), "are updated or new")
	c.previousApply = manifests
	if len(updated) == 0 {
		return nil, nil
	}

	if err := c.Run(ctx, manifests.Reader(), out, "apply", c.Flags.Apply, "-f", "-"); err != nil {
		return nil, errors.Wrap(err, "kubectl apply")
	}

	return updated, nil
}

// Run shells out kubectl CLI.
func (c *CLI) Run(ctx context.Context, in io.Reader, out io.Writer, command string, commandFlags []string, arg ...string) error {
	args := []string{"--context", c.KubeContext}
	if c.Namespace != "" {
		args = append(args, "--namespace", c.Namespace)
	}
	args = append(args, c.Flags.Global...)
	args = append(args, command)
	args = append(args, commandFlags...)
	args = append(args, arg...)

	cmd := exec.CommandContext(ctx, "kubectl", args...)
	cmd.Stdin = in
	cmd.Stdout = out
	cmd.Stderr = out

	return util.RunCmd(cmd)
}
<commit_msg>Delete and redeploy object upon error 'field is immutable' As discussed in #891, when running skaffold dev certain immutable Kubernetes objects (like Jobs) can't be redeployed. A 'field is immutable' error is returned when this happens.<commit_after>/*
Copyright 2018 The Skaffold Authors

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

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

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

package kubectl

import (
	"bufio"
	"bytes"
	"context"
	"io"
	"os/exec"
	"strings"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/v1alpha2"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

// CLI holds parameters to run kubectl.
type CLI struct {
	Namespace   string
	KubeContext string
	Flags       v1alpha2.KubectlFlags

	previousApply ManifestList
}

// Detete runs `kubectl delete` on a list of manifests.
func (c *CLI) Detete(ctx context.Context, out io.Writer, manifests ManifestList) error {
	if err := c.Run(ctx, manifests.Reader(), out, "delete", c.Flags.Delete, "--ignore-not-found=true", "-f", "-"); err != nil {
		return errors.Wrap(err, "kubectl delete")
	}

	return nil
}

// Apply runs `kubectl apply` on a list of manifests.
func (c *CLI) Apply(ctx context.Context, out io.Writer, manifests ManifestList) (ManifestList, error) {
	// Only redeploy modified or new manifests
	// TODO(dgageot): should we delete a manifest that was deployed and is not anymore?
	updated := c.previousApply.Diff(manifests)
	logrus.Debugln(len(manifests), "manifests to deploy.", len(manifests), "are updated or new")
	c.previousApply = manifests
	if len(updated) == 0 {
		return nil, nil
	}

	buf := bytes.NewBuffer([]byte{})
	writer := bufio.NewWriter(buf)
	if err := c.Run(ctx, manifests.Reader(), writer, "apply", c.Flags.Apply, "-f", "-"); err != nil {
		if !strings.Contains(string(buf.Bytes()), "field is immutable") {
			return nil, err
		}
		// If the output contains the string 'field is immutable', we want to delete the object and recreate it
		// See Issue #891 for more information
		if err := c.Detete(ctx, out, manifests); err != nil {
			return nil, errors.Wrap(err, "deleting manifest")
		}
		if err := c.Run(ctx, manifests.Reader(), out, "apply", c.Flags.Apply, "-f", "-"); err != nil {
			return nil, errors.Wrap(err, "kubectl apply after deletion")
		}
	} else {
		// Write output to out
		if _, err := out.Write(buf.Bytes()); err != nil {
			return nil, errors.Wrap(err, "writing to out")
		}
	}

	return updated, nil
}

// Run shells out kubectl CLI.
func (c *CLI) Run(ctx context.Context, in io.Reader, out io.Writer, command string, commandFlags []string, arg ...string) error {
	args := []string{"--context", c.KubeContext}
	if c.Namespace != "" {
		args = append(args, "--namespace", c.Namespace)
	}
	args = append(args, c.Flags.Global...)
	args = append(args, command)
	args = append(args, commandFlags...)
	args = append(args, arg...)

	cmd := exec.CommandContext(ctx, "kubectl", args...)
	cmd.Stdin = in
	cmd.Stdout = out
	cmd.Stderr = out

	return util.RunCmd(cmd)
}
<|endoftext|>


<commit_before>package policy

import (
	"os"
	"strconv"
	"sync"

	sb "github.com/open-lambda/open-lambda/worker/sandbox"
)

type ForkServer struct {
	Sandbox  sb.ContainerSandbox
	Pid      string
	SockPath string
	Packages map[string]bool
	Hits     float64
	Parent   *ForkServer
	Children int
	Runners  bool
	Size     float64
	Mutex    *sync.Mutex
}

func (fs *ForkServer) Hit() {
	curr := fs
	for curr != nil {
		curr.Hits += 1.0
		curr = curr.Parent
	}

	return
}

func (fs *ForkServer) Kill() error {
	if fs.Parent == nil {
		panic("attempted to kill the root")
	}

	pid, err := strconv.Atoi(fs.Pid)
	if err != nil {
		return err
	}

	proc, err := os.FindProcess(pid)
	if err != nil {
		return err
	}

	proc.Kill()

	fs.Sandbox.Stop()
	fs.Sandbox.Remove()
	fs.Parent.Children -= 1

	return nil
}
<commit_msg>kill cache containers in background<commit_after>package policy

import (
	"os"
	"strconv"
	"sync"

	sb "github.com/open-lambda/open-lambda/worker/sandbox"
)

type ForkServer struct {
	Sandbox  sb.ContainerSandbox
	Pid      string
	SockPath string
	Packages map[string]bool
	Hits     float64
	Parent   *ForkServer
	Children int
	Runners  bool
	Size     float64
	Mutex    *sync.Mutex
}

func (fs *ForkServer) Hit() {
	curr := fs
	for curr != nil {
		curr.Hits += 1.0
		curr = curr.Parent
	}

	return
}

func (fs *ForkServer) Kill() error {
	if fs.Parent == nil {
		panic("attempted to kill the root")
	}

	pid, err := strconv.Atoi(fs.Pid)
	if err != nil {
		return err
	}

	proc, err := os.FindProcess(pid)
	if err != nil {
		return err
	}

	proc.Kill()

	go func() {
		fs.Sandbox.Stop()
		fs.Sandbox.Remove()
	}()
	fs.Parent.Children -= 1

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/engine"
	"github.com/keybase/client/go/install"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
	jsonw "github.com/keybase/go-jsonw"
)

type ConfigHandler struct {
	libkb.Contextified
	xp     rpc.Transporter
	svc    *Service
	connID libkb.ConnectionID
}

var _ keybase1.ConfigInterface = (*ConfigHandler)(nil)

func NewConfigHandler(xp rpc.Transporter, i libkb.ConnectionID, g *libkb.GlobalContext, svc *Service) *ConfigHandler {
	return &ConfigHandler{
		Contextified: libkb.NewContextified(g),
		xp:           xp,
		svc:          svc,
		connID:       i,
	}
}

func (h ConfigHandler) GetCurrentStatus(ctx context.Context, sessionID int) (res keybase1.GetCurrentStatusRes, err error) {
	var cs libkb.CurrentStatus
	if cs, err = libkb.GetCurrentStatus(ctx, h.G()); err == nil {
		res = cs.Export()
	}
	return
}

func (h ConfigHandler) GetValue(_ context.Context, path string) (ret keybase1.ConfigValue, err error) {
	var i interface{}
	i, err = h.G().Env.GetConfig().GetInterfaceAtPath(path)
	if err != nil {
		return ret, err
	}
	if i == nil {
		ret.IsNull = true
	} else {
		switch v := i.(type) {
		case int:
			ret.I = &v
		case string:
			ret.S = &v
		case bool:
			ret.B = &v
		case float64:
			tmp := int(v)
			ret.I = &tmp
		default:
			var b []byte
			b, err = json.Marshal(v)
			if err == nil {
				tmp := string(b)
				ret.O = &tmp
			}
		}
	}
	return ret, err
}

func (h ConfigHandler) SetValue(_ context.Context, arg keybase1.SetValueArg) (err error) {
	w := h.G().Env.GetConfigWriter()
	if arg.Path == "users" {
		err = fmt.Errorf("The field 'users' cannot be edited for fear of config corruption")
		return err
	}
	switch {
	case arg.Value.IsNull:
		err = w.SetNullAtPath(arg.Path)
	case arg.Value.S != nil:
		err = w.SetStringAtPath(arg.Path, *arg.Value.S)
	case arg.Value.I != nil:
		err = w.SetIntAtPath(arg.Path, *arg.Value.I)
	case arg.Value.B != nil:
		err = w.SetBoolAtPath(arg.Path, *arg.Value.B)
	case arg.Value.O != nil:
		var jw *jsonw.Wrapper
		jw, err = jsonw.Unmarshal([]byte(*arg.Value.O))
		if err == nil {
			err = w.SetWrapperAtPath(arg.Path, jw)
		}
	default:
		err = fmt.Errorf("Bad type for setting a value")
	}
	if err == nil {
		h.G().ConfigReload()
	}
	return err
}

func (h ConfigHandler) ClearValue(_ context.Context, path string) error {
	h.G().Env.GetConfigWriter().DeleteAtPath(path)
	h.G().ConfigReload()
	return nil
}

func (h ConfigHandler) GetExtendedStatus(ctx context.Context, sessionID int) (res keybase1.ExtendedStatus, err error) {
	return libkb.GetExtendedStatus(libkb.NewMetaContext(ctx, h.G()))
}

func (h ConfigHandler) GetClientStatus(ctx context.Context, sessionID int) (res []keybase1.ClientStatus, err error) {
	return libkb.GetClientStatus(libkb.NewMetaContext(ctx, h.G())), nil
}

func (h ConfigHandler) GetAllProvisionedUsernames(ctx context.Context, sessionID int) (res keybase1.AllProvisionedUsernames, err error) {
	defaultUsername, all, err := libkb.GetAllProvisionedUsernames(libkb.NewMetaContext(ctx, h.G()))
	if err != nil {
		return res, err
	}

	// If the default is missing, fill it in from the first provisioned.
	if defaultUsername.IsNil() && len(all) > 0 {
		defaultUsername = all[0]
	}
	hasProvisionedUser := !defaultUsername.IsNil()

	// Callers expect ProvisionedUsernames to contain the DefaultUsername, so
	// we ensure it is here as a final sanity check before returning.
	hasDefaultUsername := false
	provisionedUsernames := []string{}
	for _, username := range all {
		provisionedUsernames = append(provisionedUsernames, username.String())
		hasDefaultUsername = hasDefaultUsername || username.Eq(defaultUsername)
	}

	if !hasDefaultUsername && hasProvisionedUser {
		provisionedUsernames = append(provisionedUsernames, defaultUsername.String())
	}

	return keybase1.AllProvisionedUsernames{
		DefaultUsername:      defaultUsername.String(),
		ProvisionedUsernames: provisionedUsernames,
		HasProvisionedUser:   hasProvisionedUser,
	}, nil
}

func (h ConfigHandler) GetConfig(_ context.Context, sessionID int) (keybase1.Config, error) {
	var c keybase1.Config

	c.ServerURI = h.G().Env.GetServerURI()
	c.RunMode = string(h.G().Env.GetRunMode())
	var err error
	c.SocketFile, err = h.G().Env.GetSocketBindFile()
	if err != nil {
		return c, err
	}

	gpg := h.G().GetGpgClient()
	canExec, err := gpg.CanExec()
	if err == nil {
		c.GpgExists = canExec
		c.GpgPath = gpg.Path()
	}

	c.Version = libkb.VersionString()
	c.VersionShort = libkb.Version

	var v []string
	libkb.VersionMessage(func(s string) {
		v = append(v, s)
	})
	c.VersionFull = strings.Join(v, "\n")

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		c.Path = dir
	} else {
		h.G().Log.Warning("Failed to get service path: %s", err)
	}

	realpath, err := libkb.CurrentBinaryRealpath()
	if err == nil {
		c.BinaryRealpath = realpath
	} else {
		h.G().Log.Warning("Failed to get service realpath: %s", err)
	}

	c.ConfigPath = h.G().Env.GetConfigFilename()
	c.Label = h.G().Env.GetLabel()
	if h.svc != nil {
		if h.svc.ForkType == keybase1.ForkType_AUTO {
			c.IsAutoForked = true
		}
		c.ForkType = h.svc.ForkType
	}

	return c, nil
}

func (h ConfigHandler) SetUserConfig(ctx context.Context, arg keybase1.SetUserConfigArg) (err error) {
	eng := engine.NewUserConfigEngine(h.G(), &engine.UserConfigEngineArg{
		Key:   arg.Key,
		Value: arg.Value,
	})
	m := libkb.NewMetaContext(ctx, h.G())
	err = engine.RunEngine2(m, eng)
	if err != nil {
		return err
	}
	return nil
}

func (h ConfigHandler) SetPath(_ context.Context, arg keybase1.SetPathArg) error {
	h.G().Log.Debug("SetPath calling mergeIntoPath(%s)", arg.Path)
	return mergeIntoPath(h.G(), arg.Path)
}

func mergeIntoPath(g *libkb.GlobalContext, p2 string) error {

	svcPath := os.Getenv("PATH")
	g.Log.Debug("mergeIntoPath: service path = %s", svcPath)
	g.Log.Debug("mergeIntoPath: merge path   = %s", p2)

	pathenv := filepath.SplitList(svcPath)
	pathset := make(map[string]bool)
	for _, p := range pathenv {
		pathset[p] = true
	}

	var clientAdditions []string
	for _, dir := range filepath.SplitList(p2) {
		if _, ok := pathset[dir]; ok {
			continue
		}
		clientAdditions = append(clientAdditions, dir)
	}

	pathenv = append(pathenv, clientAdditions...)
	combined := strings.Join(pathenv, string(os.PathListSeparator))

	if combined == svcPath {
		g.Log.Debug("No path changes needed")
		return nil
	}

	g.Log.Debug("mergeIntoPath: merged path = %s", combined)
	os.Setenv("PATH", combined)
	return nil
}

func (h ConfigHandler) HelloIAm(_ context.Context, arg keybase1.ClientDetails) error {
	tmp := fmt.Sprintf("%v", arg.Argv)
	re := regexp.MustCompile(`\b(chat|encrypt|git|accept-invite|wallet\s+send|wallet\s+import|passphrase\s+check)\b`)
	if mtch := re.FindString(tmp); len(mtch) > 0 {
		arg.Argv = []string{arg.Argv[0], mtch, "(redacted)"}
	}
	h.G().Log.Debug("HelloIAm: %d - %v", h.connID, arg)
	return h.G().ConnectionManager.Label(h.connID, arg)
}

func (h ConfigHandler) CheckAPIServerOutOfDateWarning(_ context.Context) (keybase1.OutOfDateInfo, error) {
	return h.G().GetOutOfDateInfo(), nil
}

func (h ConfigHandler) GetUpdateInfo(ctx context.Context) (keybase1.UpdateInfo, error) {
	outOfDateInfo := h.G().GetOutOfDateInfo()
	if len(outOfDateInfo.UpgradeTo) != 0 {
		// This is from the API server. Consider client critically out of date
		// if we are asked to upgrade by the API server.
		return keybase1.UpdateInfo{
			Status:  keybase1.UpdateInfoStatus_CRITICALLY_OUT_OF_DATE,
			Message: outOfDateInfo.CustomMessage,
		}, nil
	}
	needUpdate, err := install.GetNeedUpdate() // This is from the updater.
	if err != nil {
		h.G().Log.Errorf("Error calling updater: %s", err)
		return keybase1.UpdateInfo{
			Status: keybase1.UpdateInfoStatus_UP_TO_DATE,
		}, err
	}
	if needUpdate {
		return keybase1.UpdateInfo{
			Status: keybase1.UpdateInfoStatus_NEED_UPDATE,
		}, nil
	}
	return keybase1.UpdateInfo{
		Status: keybase1.UpdateInfoStatus_UP_TO_DATE,
	}, nil
}

func (h ConfigHandler) StartUpdateIfNeeded(ctx context.Context) error {
	return install.StartUpdateIfNeeded(ctx, h.G().Log)
}

func (h ConfigHandler) WaitForClient(_ context.Context, arg keybase1.WaitForClientArg) (bool, error) {
	return h.G().ConnectionManager.WaitForClientType(arg.ClientType, arg.Timeout.Duration()), nil
}

func (h ConfigHandler) GetBootstrapStatus(ctx context.Context, sessionID int) (keybase1.BootstrapStatus, error) {
	eng := engine.NewBootstrap(h.G())
	m := libkb.NewMetaContext(ctx, h.G())
	if err := engine.RunEngine2(m, eng); err != nil {
		return keybase1.BootstrapStatus{}, err
	}

	return eng.Status(), nil
}

func (h ConfigHandler) GetRememberPassphrase(ctx context.Context, sessionID int) (bool, error) {
	return h.G().Env.RememberPassphrase(), nil
}

func (h ConfigHandler) SetRememberPassphrase(ctx context.Context, arg keybase1.SetRememberPassphraseArg) error {
	m := libkb.NewMetaContext(ctx, h.G())
	remember, err := h.GetRememberPassphrase(ctx, arg.SessionID)
	if err != nil {
		return err
	}
	if remember == arg.Remember {
		m.Debug("SetRememberPassphrase: no change necessary (remember = %v)", remember)
		return nil
	}

	// set the config variable
	w := h.G().Env.GetConfigWriter()
	if err := w.SetRememberPassphrase(arg.Remember); err != nil {
		return err
	}
	h.G().ConfigReload()

	// replace the secret store
	if err := h.G().ReplaceSecretStore(ctx); err != nil {
		m.Debug("error replacing secret store for SetRememberPassphrase(%v): %s", arg.Remember, err)
		return err
	}

	m.Debug("SetRememberPassphrase(%v) success", arg.Remember)

	return nil
}

type rawGetPkgCheck struct {
	Status libkb.AppStatus      `json:"status"`
	Res    keybase1.UpdateInfo2 `json:"res"`
}

func (r *rawGetPkgCheck) GetAppStatus() *libkb.AppStatus {
	return &r.Status
}

func (h ConfigHandler) GetUpdateInfo2(ctx context.Context, arg keybase1.GetUpdateInfo2Arg) (res keybase1.UpdateInfo2, err error) {
	m := libkb.NewMetaContext(ctx, h.G())

	var version string
	var platform string

	if arg.Platform != nil {
		platform = *arg.Platform
	} else {
		platform = libkb.GetPlatformString()
	}
	if arg.Version != nil {
		version = *arg.Version
	} else {
		version = libkb.VersionString()
	}

	apiArg := libkb.NewAPIArg("pkg/check")
	apiArg.Args = libkb.HTTPArgs{
		"version":  libkb.S{Val: version},
		"platform": libkb.S{Val: platform},
	}
	var raw rawGetPkgCheck
	if err = m.G().API.GetDecode(m, apiArg, &raw); err != nil {
		return res, err
	}
	return raw.Res, nil
}
<commit_msg>retry pkg/check api call (#17391)<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package service

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"golang.org/x/net/context"

	"github.com/keybase/client/go/engine"
	"github.com/keybase/client/go/install"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
	jsonw "github.com/keybase/go-jsonw"
)

type ConfigHandler struct {
	libkb.Contextified
	xp     rpc.Transporter
	svc    *Service
	connID libkb.ConnectionID
}

var _ keybase1.ConfigInterface = (*ConfigHandler)(nil)

func NewConfigHandler(xp rpc.Transporter, i libkb.ConnectionID, g *libkb.GlobalContext, svc *Service) *ConfigHandler {
	return &ConfigHandler{
		Contextified: libkb.NewContextified(g),
		xp:           xp,
		svc:          svc,
		connID:       i,
	}
}

func (h ConfigHandler) GetCurrentStatus(ctx context.Context, sessionID int) (res keybase1.GetCurrentStatusRes, err error) {
	var cs libkb.CurrentStatus
	if cs, err = libkb.GetCurrentStatus(ctx, h.G()); err == nil {
		res = cs.Export()
	}
	return
}

func (h ConfigHandler) GetValue(_ context.Context, path string) (ret keybase1.ConfigValue, err error) {
	var i interface{}
	i, err = h.G().Env.GetConfig().GetInterfaceAtPath(path)
	if err != nil {
		return ret, err
	}
	if i == nil {
		ret.IsNull = true
	} else {
		switch v := i.(type) {
		case int:
			ret.I = &v
		case string:
			ret.S = &v
		case bool:
			ret.B = &v
		case float64:
			tmp := int(v)
			ret.I = &tmp
		default:
			var b []byte
			b, err = json.Marshal(v)
			if err == nil {
				tmp := string(b)
				ret.O = &tmp
			}
		}
	}
	return ret, err
}

func (h ConfigHandler) SetValue(_ context.Context, arg keybase1.SetValueArg) (err error) {
	w := h.G().Env.GetConfigWriter()
	if arg.Path == "users" {
		err = fmt.Errorf("The field 'users' cannot be edited for fear of config corruption")
		return err
	}
	switch {
	case arg.Value.IsNull:
		err = w.SetNullAtPath(arg.Path)
	case arg.Value.S != nil:
		err = w.SetStringAtPath(arg.Path, *arg.Value.S)
	case arg.Value.I != nil:
		err = w.SetIntAtPath(arg.Path, *arg.Value.I)
	case arg.Value.B != nil:
		err = w.SetBoolAtPath(arg.Path, *arg.Value.B)
	case arg.Value.O != nil:
		var jw *jsonw.Wrapper
		jw, err = jsonw.Unmarshal([]byte(*arg.Value.O))
		if err == nil {
			err = w.SetWrapperAtPath(arg.Path, jw)
		}
	default:
		err = fmt.Errorf("Bad type for setting a value")
	}
	if err == nil {
		h.G().ConfigReload()
	}
	return err
}

func (h ConfigHandler) ClearValue(_ context.Context, path string) error {
	h.G().Env.GetConfigWriter().DeleteAtPath(path)
	h.G().ConfigReload()
	return nil
}

func (h ConfigHandler) GetExtendedStatus(ctx context.Context, sessionID int) (res keybase1.ExtendedStatus, err error) {
	return libkb.GetExtendedStatus(libkb.NewMetaContext(ctx, h.G()))
}

func (h ConfigHandler) GetClientStatus(ctx context.Context, sessionID int) (res []keybase1.ClientStatus, err error) {
	return libkb.GetClientStatus(libkb.NewMetaContext(ctx, h.G())), nil
}

func (h ConfigHandler) GetAllProvisionedUsernames(ctx context.Context, sessionID int) (res keybase1.AllProvisionedUsernames, err error) {
	defaultUsername, all, err := libkb.GetAllProvisionedUsernames(libkb.NewMetaContext(ctx, h.G()))
	if err != nil {
		return res, err
	}

	// If the default is missing, fill it in from the first provisioned.
	if defaultUsername.IsNil() && len(all) > 0 {
		defaultUsername = all[0]
	}
	hasProvisionedUser := !defaultUsername.IsNil()

	// Callers expect ProvisionedUsernames to contain the DefaultUsername, so
	// we ensure it is here as a final sanity check before returning.
	hasDefaultUsername := false
	provisionedUsernames := []string{}
	for _, username := range all {
		provisionedUsernames = append(provisionedUsernames, username.String())
		hasDefaultUsername = hasDefaultUsername || username.Eq(defaultUsername)
	}

	if !hasDefaultUsername && hasProvisionedUser {
		provisionedUsernames = append(provisionedUsernames, defaultUsername.String())
	}

	return keybase1.AllProvisionedUsernames{
		DefaultUsername:      defaultUsername.String(),
		ProvisionedUsernames: provisionedUsernames,
		HasProvisionedUser:   hasProvisionedUser,
	}, nil
}

func (h ConfigHandler) GetConfig(_ context.Context, sessionID int) (keybase1.Config, error) {
	var c keybase1.Config

	c.ServerURI = h.G().Env.GetServerURI()
	c.RunMode = string(h.G().Env.GetRunMode())
	var err error
	c.SocketFile, err = h.G().Env.GetSocketBindFile()
	if err != nil {
		return c, err
	}

	gpg := h.G().GetGpgClient()
	canExec, err := gpg.CanExec()
	if err == nil {
		c.GpgExists = canExec
		c.GpgPath = gpg.Path()
	}

	c.Version = libkb.VersionString()
	c.VersionShort = libkb.Version

	var v []string
	libkb.VersionMessage(func(s string) {
		v = append(v, s)
	})
	c.VersionFull = strings.Join(v, "\n")

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		c.Path = dir
	} else {
		h.G().Log.Warning("Failed to get service path: %s", err)
	}

	realpath, err := libkb.CurrentBinaryRealpath()
	if err == nil {
		c.BinaryRealpath = realpath
	} else {
		h.G().Log.Warning("Failed to get service realpath: %s", err)
	}

	c.ConfigPath = h.G().Env.GetConfigFilename()
	c.Label = h.G().Env.GetLabel()
	if h.svc != nil {
		if h.svc.ForkType == keybase1.ForkType_AUTO {
			c.IsAutoForked = true
		}
		c.ForkType = h.svc.ForkType
	}

	return c, nil
}

func (h ConfigHandler) SetUserConfig(ctx context.Context, arg keybase1.SetUserConfigArg) (err error) {
	eng := engine.NewUserConfigEngine(h.G(), &engine.UserConfigEngineArg{
		Key:   arg.Key,
		Value: arg.Value,
	})
	m := libkb.NewMetaContext(ctx, h.G())
	err = engine.RunEngine2(m, eng)
	if err != nil {
		return err
	}
	return nil
}

func (h ConfigHandler) SetPath(_ context.Context, arg keybase1.SetPathArg) error {
	h.G().Log.Debug("SetPath calling mergeIntoPath(%s)", arg.Path)
	return mergeIntoPath(h.G(), arg.Path)
}

func mergeIntoPath(g *libkb.GlobalContext, p2 string) error {

	svcPath := os.Getenv("PATH")
	g.Log.Debug("mergeIntoPath: service path = %s", svcPath)
	g.Log.Debug("mergeIntoPath: merge path   = %s", p2)

	pathenv := filepath.SplitList(svcPath)
	pathset := make(map[string]bool)
	for _, p := range pathenv {
		pathset[p] = true
	}

	var clientAdditions []string
	for _, dir := range filepath.SplitList(p2) {
		if _, ok := pathset[dir]; ok {
			continue
		}
		clientAdditions = append(clientAdditions, dir)
	}

	pathenv = append(pathenv, clientAdditions...)
	combined := strings.Join(pathenv, string(os.PathListSeparator))

	if combined == svcPath {
		g.Log.Debug("No path changes needed")
		return nil
	}

	g.Log.Debug("mergeIntoPath: merged path = %s", combined)
	os.Setenv("PATH", combined)
	return nil
}

func (h ConfigHandler) HelloIAm(_ context.Context, arg keybase1.ClientDetails) error {
	tmp := fmt.Sprintf("%v", arg.Argv)
	re := regexp.MustCompile(`\b(chat|encrypt|git|accept-invite|wallet\s+send|wallet\s+import|passphrase\s+check)\b`)
	if mtch := re.FindString(tmp); len(mtch) > 0 {
		arg.Argv = []string{arg.Argv[0], mtch, "(redacted)"}
	}
	h.G().Log.Debug("HelloIAm: %d - %v", h.connID, arg)
	return h.G().ConnectionManager.Label(h.connID, arg)
}

func (h ConfigHandler) CheckAPIServerOutOfDateWarning(_ context.Context) (keybase1.OutOfDateInfo, error) {
	return h.G().GetOutOfDateInfo(), nil
}

func (h ConfigHandler) GetUpdateInfo(ctx context.Context) (keybase1.UpdateInfo, error) {
	outOfDateInfo := h.G().GetOutOfDateInfo()
	if len(outOfDateInfo.UpgradeTo) != 0 {
		// This is from the API server. Consider client critically out of date
		// if we are asked to upgrade by the API server.
		return keybase1.UpdateInfo{
			Status:  keybase1.UpdateInfoStatus_CRITICALLY_OUT_OF_DATE,
			Message: outOfDateInfo.CustomMessage,
		}, nil
	}
	needUpdate, err := install.GetNeedUpdate() // This is from the updater.
	if err != nil {
		h.G().Log.Errorf("Error calling updater: %s", err)
		return keybase1.UpdateInfo{
			Status: keybase1.UpdateInfoStatus_UP_TO_DATE,
		}, err
	}
	if needUpdate {
		return keybase1.UpdateInfo{
			Status: keybase1.UpdateInfoStatus_NEED_UPDATE,
		}, nil
	}
	return keybase1.UpdateInfo{
		Status: keybase1.UpdateInfoStatus_UP_TO_DATE,
	}, nil
}

func (h ConfigHandler) StartUpdateIfNeeded(ctx context.Context) error {
	return install.StartUpdateIfNeeded(ctx, h.G().Log)
}

func (h ConfigHandler) WaitForClient(_ context.Context, arg keybase1.WaitForClientArg) (bool, error) {
	return h.G().ConnectionManager.WaitForClientType(arg.ClientType, arg.Timeout.Duration()), nil
}

func (h ConfigHandler) GetBootstrapStatus(ctx context.Context, sessionID int) (keybase1.BootstrapStatus, error) {
	eng := engine.NewBootstrap(h.G())
	m := libkb.NewMetaContext(ctx, h.G())
	if err := engine.RunEngine2(m, eng); err != nil {
		return keybase1.BootstrapStatus{}, err
	}

	return eng.Status(), nil
}

func (h ConfigHandler) GetRememberPassphrase(ctx context.Context, sessionID int) (bool, error) {
	return h.G().Env.RememberPassphrase(), nil
}

func (h ConfigHandler) SetRememberPassphrase(ctx context.Context, arg keybase1.SetRememberPassphraseArg) error {
	m := libkb.NewMetaContext(ctx, h.G())
	remember, err := h.GetRememberPassphrase(ctx, arg.SessionID)
	if err != nil {
		return err
	}
	if remember == arg.Remember {
		m.Debug("SetRememberPassphrase: no change necessary (remember = %v)", remember)
		return nil
	}

	// set the config variable
	w := h.G().Env.GetConfigWriter()
	if err := w.SetRememberPassphrase(arg.Remember); err != nil {
		return err
	}
	h.G().ConfigReload()

	// replace the secret store
	if err := h.G().ReplaceSecretStore(ctx); err != nil {
		m.Debug("error replacing secret store for SetRememberPassphrase(%v): %s", arg.Remember, err)
		return err
	}

	m.Debug("SetRememberPassphrase(%v) success", arg.Remember)

	return nil
}

type rawGetPkgCheck struct {
	Status libkb.AppStatus      `json:"status"`
	Res    keybase1.UpdateInfo2 `json:"res"`
}

func (r *rawGetPkgCheck) GetAppStatus() *libkb.AppStatus {
	return &r.Status
}

func (h ConfigHandler) GetUpdateInfo2(ctx context.Context, arg keybase1.GetUpdateInfo2Arg) (res keybase1.UpdateInfo2, err error) {
	m := libkb.NewMetaContext(ctx, h.G())

	var version string
	var platform string

	if arg.Platform != nil {
		platform = *arg.Platform
	} else {
		platform = libkb.GetPlatformString()
	}
	if arg.Version != nil {
		version = *arg.Version
	} else {
		version = libkb.VersionString()
	}

	apiArg := libkb.APIArg{
		Endpoint: "pkg/check",
		Args: libkb.HTTPArgs{
			"version":  libkb.S{Val: version},
			"platform": libkb.S{Val: platform},
		},
		RetryCount: 3,
	}
	var raw rawGetPkgCheck
	if err = m.G().API.GetDecode(m, apiArg, &raw); err != nil {
		return res, err
	}
	return raw.Res, nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Matt Martz <matt@sivel.net>
// All Rights Reserved.
//
//    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 main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path"
)

type Key struct {
	Key string
}

func usage() {
	fmt.Printf(`usage: %s --repo=owner/name string
    --repo REPO           Repository slug (:owner/:name)
    string                String to encrypt
`, path.Base(os.Args[0]))
}

func main() {
	flag.Usage = usage
	var repo string
	flag.StringVar(&repo, "repo", "", "Repository slug (:owner/:name)")
	flag.Parse()
	if repo == "" {
		fmt.Println("ERROR: No --repo provided\n")
		usage()
		os.Exit(2)
	}
	keyurl := fmt.Sprintf("https://api.travis-ci.org/repos/%s/key", repo)

	stringToEncrypt := flag.Arg(0)
	if stringToEncrypt == "" {
		fmt.Println("ERROR: No string to encrypt\n")
		usage()
		os.Exit(2)
	}

	resp, err := http.Get(keyurl)
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	var key Key
	json.Unmarshal(body, &key)

	block, _ := pem.Decode([]byte(key.Key))
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		fmt.Printf("Failed to parse RSA public key: %s\n", err)
		os.Exit(2)
	}

	rsaPub, ok := pub.(*rsa.PublicKey)
	if !ok {
		fmt.Println("Value returned from ParsePKIXPublicKey was not an RSA public key")
		os.Exit(2)
	}

	encrypted, err := rsa.EncryptPKCS1v15(rand.Reader, rsaPub, []byte(stringToEncrypt))
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	encoded := base64.StdEncoding.EncodeToString(encrypted)
	fmt.Printf("secure: \"%s\"\n", encoded)
}
<commit_msg>Add version output and improve help<commit_after>// Copyright 2014 Matt Martz <matt@sivel.net>
// All Rights Reserved.
//
//    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 main

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/base64"
	"encoding/json"
	"encoding/pem"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path"
)

var version string = "1.0.0"

type Key struct {
	Key string
}

func usage() {
	fmt.Printf(`usage: %s --repo=owner/name string
    -h, --help            Show this help message and exit
    --version             Show program's version number and exit
    --repo REPO           Repository slug (:owner/:name)
    string                String to encrypt
`, path.Base(os.Args[0]))
}

func printVersion() {
	fmt.Println(version)
}

func main() {
	flag.Usage = usage
	var repo string
	var ver bool
	flag.StringVar(&repo, "repo", "", "Repository slug (:owner/:name)")
	flag.BoolVar(&ver, "version", true, "Show program's version number and exit")
	flag.Parse()
	if ver {
		printVersion()
		os.Exit(0)
	}
	if repo == "" {
		fmt.Println("ERROR: No --repo provided\n")
		usage()
		os.Exit(2)
	}
	keyurl := fmt.Sprintf("https://api.travis-ci.org/repos/%s/key", repo)

	stringToEncrypt := flag.Arg(0)
	if stringToEncrypt == "" {
		fmt.Println("ERROR: No string to encrypt\n")
		usage()
		os.Exit(2)
	}

	resp, err := http.Get(keyurl)
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	var key Key
	json.Unmarshal(body, &key)

	block, _ := pem.Decode([]byte(key.Key))
	pub, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		fmt.Printf("Failed to parse RSA public key: %s\n", err)
		os.Exit(2)
	}

	rsaPub, ok := pub.(*rsa.PublicKey)
	if !ok {
		fmt.Println("Value returned from ParsePKIXPublicKey was not an RSA public key")
		os.Exit(2)
	}

	encrypted, err := rsa.EncryptPKCS1v15(rand.Reader, rsaPub, []byte(stringToEncrypt))
	if err != nil {
		fmt.Println(err)
		os.Exit(2)
	}

	encoded := base64.StdEncoding.EncodeToString(encrypted)
	fmt.Printf("secure: \"%s\"\n", encoded)
}
<|endoftext|>


<commit_before>package tricorder

import (
	"bytes"
	"encoding/json"
	"github.com/Symantec/tricorder/go/tricorder/messages"
	"net/http"
)

var (
	jsonUrl = "/metricsapi"
)

func jsonAsMetric(m *metric, s *session) *messages.Metric {
	return &messages.Metric{
		Path:        m.AbsPath(),
		Description: m.Description,
		Unit:        m.Unit(),
		Value:       m.AsJsonValue(s)}
}

type jsonMetricsCollector messages.MetricList

func (c *jsonMetricsCollector) Collect(m *metric, s *session) (err error) {
	*c = append(*c, jsonAsMetric(m, s))
	return nil
}

func jsonSetUpHeaders(h http.Header) {
	h.Set("Content-Type", "text/plain")
	h.Set("X-Tricorder-Media-Type", "tricorder.v1")
}

func jsonHandlerFunc(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	jsonSetUpHeaders(w.Header())
	path := r.URL.Path
	var content []byte
	var err error
	if r.Form.Get("singleton") != "" {
		m := root.GetMetric(path)
		if m == nil {
			httpError(w, http.StatusNotFound)
			return
		}
		content, err = json.Marshal(jsonAsMetric(m, nil))
	} else {
		var collector jsonMetricsCollector
		root.GetAllMetricsByPath(path, &collector, nil)
		content, err = json.Marshal(collector)
	}
	if err != nil {
		handleError(w, err)
		return
	}
	var buffer bytes.Buffer
	json.Indent(&buffer, content, "", "\t")
	buffer.WriteTo(w)
}

func initJsonHandlers() {
	http.Handle(jsonUrl+"/", http.StripPrefix(jsonUrl, gzipHandler{http.HandlerFunc(jsonHandlerFunc)}))
}
<commit_msg>Use application/json as content-type for json.<commit_after>package tricorder

import (
	"bytes"
	"encoding/json"
	"github.com/Symantec/tricorder/go/tricorder/messages"
	"net/http"
)

var (
	jsonUrl = "/metricsapi"
)

func jsonAsMetric(m *metric, s *session) *messages.Metric {
	return &messages.Metric{
		Path:        m.AbsPath(),
		Description: m.Description,
		Unit:        m.Unit(),
		Value:       m.AsJsonValue(s)}
}

type jsonMetricsCollector messages.MetricList

func (c *jsonMetricsCollector) Collect(m *metric, s *session) (err error) {
	*c = append(*c, jsonAsMetric(m, s))
	return nil
}

func jsonSetUpHeaders(h http.Header) {
	h.Set("Content-Type", "application/json")
	h.Set("X-Tricorder-Media-Type", "tricorder.v1")
}

func jsonHandlerFunc(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	jsonSetUpHeaders(w.Header())
	path := r.URL.Path
	var content []byte
	var err error
	if r.Form.Get("singleton") != "" {
		m := root.GetMetric(path)
		if m == nil {
			httpError(w, http.StatusNotFound)
			return
		}
		content, err = json.Marshal(jsonAsMetric(m, nil))
	} else {
		var collector jsonMetricsCollector
		root.GetAllMetricsByPath(path, &collector, nil)
		content, err = json.Marshal(collector)
	}
	if err != nil {
		handleError(w, err)
		return
	}
	var buffer bytes.Buffer
	json.Indent(&buffer, content, "", "\t")
	buffer.WriteTo(w)
}

func initJsonHandlers() {
	http.Handle(jsonUrl+"/", http.StripPrefix(jsonUrl, gzipHandler{http.HandlerFunc(jsonHandlerFunc)}))
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 agreedto 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 vttls

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
)

// Updated list of acceptable cipher suits to address
// Fixed upstream in https://github.com/golang/go/issues/13385
// This removed CBC mode ciphers that are suseptiable to Lucky13 style attacks
func newTLSConfig() *tls.Config {
	return &tls.Config{
		// MySQL Community edition has some problems with TLS1.2
		// TODO: Validate this will not break servers using mysql community edition < 5.7.10
		// MinVersion: tls.VersionTLS12,

		// Default ordering taken from
		// go 1.11 crypto/tls/cipher_suites.go
		CipherSuites: []uint16{
			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_RSA_WITH_AES_256_CBC_SHA,
		},
	}
}

// ClientConfig returns the TLS config to use for a client to
// connect to a server with the provided parameters.
func ClientConfig(cert, key, ca, name string) (*tls.Config, error) {
	config := newTLSConfig()

	// Load the client-side cert & key if any.
	if cert != "" && key != "" {
		crt, err := tls.LoadX509KeyPair(cert, key)
		if err != nil {
			return nil, fmt.Errorf("failed to load cert/key: %v", err)
		}
		config.Certificates = []tls.Certificate{crt}
	}

	// Load the server CA if any.
	if ca != "" {
		b, err := ioutil.ReadFile(ca)
		if err != nil {
			return nil, fmt.Errorf("failed to read ca file: %v", err)
		}
		cp := x509.NewCertPool()
		if !cp.AppendCertsFromPEM(b) {
			return nil, fmt.Errorf("failed to append certificates")
		}
		config.RootCAs = cp
	}

	// Set the server name if any.
	if name != "" {
		config.ServerName = name
	}

	return config, nil
}

// ServerConfig returns the TLS config to use for a server to
// accept client connections.
func ServerConfig(cert, key, ca string) (*tls.Config, error) {
	config := newTLSConfig()

	// Load the server cert and key.
	crt, err := tls.LoadX509KeyPair(cert, key)
	if err != nil {
		return nil, fmt.Errorf("failed to load cert/key: %v", err)
	}
	config.Certificates = []tls.Certificate{crt}

	// if specified, load ca to validate client,
	// and enforce clients present valid certs.
	if ca != "" {
		b, err := ioutil.ReadFile(ca)
		if err != nil {
			return nil, fmt.Errorf("Failed to read ca file: %v", err)
		}
		cp := x509.NewCertPool()
		if !cp.AppendCertsFromPEM(b) {
			return nil, fmt.Errorf("Failed to append certificates")
		}
		config.ClientCAs = cp
		config.ClientAuth = tls.RequireAndVerifyClientCert
	}

	return config, nil
}
<commit_msg>staticcheck: vttls package<commit_after>/*
Copyright 2017 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 agreedto 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 vttls

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
)

// Updated list of acceptable cipher suits to address
// Fixed upstream in https://github.com/golang/go/issues/13385
// This removed CBC mode ciphers that are suseptiable to Lucky13 style attacks
func newTLSConfig() *tls.Config {
	return &tls.Config{
		// MySQL Community edition has some problems with TLS1.2
		// TODO: Validate this will not break servers using mysql community edition < 5.7.10
		// MinVersion: tls.VersionTLS12,

		// Default ordering taken from
		// go 1.11 crypto/tls/cipher_suites.go
		CipherSuites: []uint16{
			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
			tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
			tls.TLS_RSA_WITH_AES_128_CBC_SHA,
			tls.TLS_RSA_WITH_AES_256_CBC_SHA,
		},
	}
}

// ClientConfig returns the TLS config to use for a client to
// connect to a server with the provided parameters.
func ClientConfig(cert, key, ca, name string) (*tls.Config, error) {
	config := newTLSConfig()

	// Load the client-side cert & key if any.
	if cert != "" && key != "" {
		crt, err := tls.LoadX509KeyPair(cert, key)
		if err != nil {
			return nil, fmt.Errorf("failed to load cert/key: %v", err)
		}
		config.Certificates = []tls.Certificate{crt}
	}

	// Load the server CA if any.
	if ca != "" {
		b, err := ioutil.ReadFile(ca)
		if err != nil {
			return nil, fmt.Errorf("failed to read ca file: %v", err)
		}
		cp := x509.NewCertPool()
		if !cp.AppendCertsFromPEM(b) {
			return nil, fmt.Errorf("failed to append certificates")
		}
		config.RootCAs = cp
	}

	// Set the server name if any.
	if name != "" {
		config.ServerName = name
	}

	return config, nil
}

// ServerConfig returns the TLS config to use for a server to
// accept client connections.
func ServerConfig(cert, key, ca string) (*tls.Config, error) {
	config := newTLSConfig()

	// Load the server cert and key.
	crt, err := tls.LoadX509KeyPair(cert, key)
	if err != nil {
		return nil, fmt.Errorf("failed to load cert/key: %v", err)
	}
	config.Certificates = []tls.Certificate{crt}

	// if specified, load ca to validate client,
	// and enforce clients present valid certs.
	if ca != "" {
		b, err := ioutil.ReadFile(ca)
		if err != nil {
			return nil, fmt.Errorf("failed to read ca file: %v", err)
		}
		cp := x509.NewCertPool()
		if !cp.AppendCertsFromPEM(b) {
			return nil, fmt.Errorf("failed to append certificates")
		}
		config.ClientCAs = cp
		config.ClientAuth = tls.RequireAndVerifyClientCert
	}

	return config, nil
}
<|endoftext|>


<commit_before>package serializer

import (
	"encoding/binary"
	"fmt"
	"github.com/PuerkitoBio/lune/types"
	"io"
	"unsafe"
)

const (
	LUNE_MAJOR_VERSION      = 5
	LUNE_MINOR_VERSION      = 2
	_VERSION           byte = LUNE_MAJOR_VERSION*16 + LUNE_MINOR_VERSION
	_FORMAT            byte = 0
	_HEADER_SZ              = 4
	_TAIL_SZ                = 6
)

var (
	_HEADER = [...]byte{0x1B, 0x4C, 0x75, 0x61}
	_TAIL   = [...]byte{0x19, 0x93, '\r', '\n', 0x1a, '\n'}
)

type gHeader struct {
	Signature  [_HEADER_SZ]byte
	Version    byte
	Format     byte
	Endianness byte
	IntSz      byte
	SizeTSz    byte
	InstrSz    byte
	NumberSz   byte
	IntFlag    byte
	Tail       [_TAIL_SZ]byte
}

func (h *gHeader) MajorVersion() byte {
	return h.Version >> 4
}

func (h *gHeader) MinorVersion() byte {
	return h.Version & 0x0F
}

func NewHeader() *gHeader {
	var i int32   // Force 4 bytes even on 64bit systems? Validate on 64bit Linux
	var ui uint64 // TODO : Force 8 bytes, uint gives only 4, inconsistent with Lua on 64-bit platforms
	var instr types.Instruction

	// Create a standard header based on the current architecture
	return &gHeader{
		Signature:  _HEADER,
		Version:    _VERSION,
		Format:     _FORMAT,
		Endianness: 1, // TODO : For now, force little-endian
		IntSz:      byte(unsafe.Sizeof(i)),
		SizeTSz:    byte(unsafe.Sizeof(ui)), // TODO : Is this consistent with what Lua gives on this platform?
		InstrSz:    byte(unsafe.Sizeof(instr)),
		NumberSz:   8, // TODO : Sizeof(the custom Number size)
		IntFlag:    0, // TODO : Support non-floating point compilation?
		Tail:       _TAIL,
	}
}

type prototype struct {
	meta   *funcMeta
	code   []types.Instruction
	ks     []types.Value
	protos []*prototype
}

type funcMeta struct {
	LineDefined     uint32
	LastLineDefined uint32
	NumParams       byte
	IsVarArg        byte
	MaxStackSize    byte
}

func readString(r io.Reader) (string, error) {
	var sz uint64
	var s string

	err := binary.Read(r, binary.LittleEndian, &sz)
	if err != nil {
		return "", err
	}
	if sz > 0 {
		fmt.Println("sz= ", sz)
		ch := make([]byte, sz)
		err = binary.Read(r, binary.LittleEndian, ch)
		if err != nil {
			return "", err
		}
		// Remove 0x00
		s = string(ch[:len(ch)-1])
	}
	return s, nil
}

func readConstants(r io.Reader, p *prototype) error {
	var n uint32
	var i uint32

	err := binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return err
	}
	fmt.Printf("Number of constants: %d\n", n)

	for i = 0; i < n; i++ {
		// Read the constant's type, 1 byte
		var t byte
		err = binary.Read(r, binary.LittleEndian, &t)
		if err != nil {
			return err
		}
		switch types.ValType(t) {
		case types.TNIL:
			var v types.Value = nil
			p.ks = append(p.ks, v)
		case types.TBOOL:
			var v types.Value
			err = binary.Read(r, binary.LittleEndian, &t)
			if err != nil {
				return err
			}
			if t == 0 {
				v = false
			} else if t == 1 {
				v = true
			} else {
				return fmt.Errorf("invalid value for boolean: %d", t)
			}
			p.ks = append(p.ks, v)
		case types.TNUMBER:
			// TODO : A number is a double in Lua, will a read in a float64 work?
			var f float64
			var v types.Value
			err = binary.Read(r, binary.LittleEndian, &f)
			if err != nil {
				return err
			}
			v = f
			p.ks = append(p.ks, v)
		case types.TSTRING:
			var v types.Value
			v, err = readString(r)
			if err != nil {
				return err
			}
			p.ks = append(p.ks, v)
		default:
			return fmt.Errorf("unexpected constant type: %d", t)
		}
	}
	return nil
}

func readCode(r io.Reader, p *prototype) error {
	var n uint32
	var i uint32

	err := binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return err
	}
	fmt.Printf("Number of instructions: %d\n", n)
	for i = 0; i < n; i++ {
		var instr types.Instruction
		err = binary.Read(r, binary.LittleEndian, &instr)
		if err != nil {
			return err
		}
		p.code = append(p.code, instr)
	}
	return nil
}

func readFunction(r io.Reader) (*prototype, error) {
	var fm funcMeta
	var p prototype
	var n uint32
	var i uint32

	// Meta-data about the function
	err := binary.Read(r, binary.LittleEndian, &fm)
	if err != nil {
		return nil, err
	}
	p.meta = &fm
	fmt.Printf("Function meta: %+v\n", fm)

	// Function's instructions
	err = readCode(r, &p)
	if err != nil {
		return nil, err
	}

	// Function's constants
	err = readConstants(r, &p)
	if err != nil {
		return nil, err
	}

	// Inner function's functions (prototypes)
	err = binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return nil, err
	}
	for i = 0; i < n; i++ {
		var subP *prototype
		subP, err = readFunction(r)
		if err != nil {
			return nil, err
		}
		p.protos = append(p.protos, subP)
	}

	return &p, nil
}

func readHeader(r io.Reader) (*gHeader, error) {
	var h gHeader

	err := binary.Read(r, binary.LittleEndian, &h)
	if err != nil {
		return nil, err
	}

	// Validate header
	stdH := NewHeader()
	fmt.Printf("h: %v\n", h)
	fmt.Printf("stdH: %v\n", *stdH)

	// As a whole
	if h == *stdH {
		return &h, nil
	} else if h.Signature != stdH.Signature {
		return nil, fmt.Errorf("is not a precompiled chunk")
	} else if h.Version != stdH.Version {
		return nil, fmt.Errorf("version mismatch, got %d.%d, expected %d.%d", h.MajorVersion(), h.MinorVersion(), stdH.MajorVersion(), stdH.MinorVersion())
	}

	return nil, fmt.Errorf("incompatible")
}

func Load(r io.Reader) error {
	// First up, the Header (12 bytes) + LUAC_TAIL to "catch conversion errors", as described in Lua
	h, err := readHeader(r)
	if err != nil {
		return err
	}
	fmt.Printf("Header: %+v\n", h)

	// Then, the function header (a prototype)
	p, err := readFunction(r)
	if err != nil {
		return err
	}
	fmt.Printf("Prototype: %+v\n", p)

	for _, i := range p.code {
		fmt.Println(i)
	}
	/*
		s, err := readString(r)
		if err != nil {
			return err
		}
		fmt.Printf("String: %s\n", s)
	*/
	return nil
}
<commit_msg>reads whole ex1.lua binary chunk, prints it out<commit_after>package serializer

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"github.com/PuerkitoBio/lune/types"
	"io"
	"unsafe"
)

const (
	LUNE_MAJOR_VERSION      = 5
	LUNE_MINOR_VERSION      = 2
	_VERSION           byte = LUNE_MAJOR_VERSION*16 + LUNE_MINOR_VERSION
	_FORMAT            byte = 0
	_HEADER_SZ              = 4
	_TAIL_SZ                = 6
)

var (
	_HEADER = [...]byte{0x1B, 0x4C, 0x75, 0x61}
	_TAIL   = [...]byte{0x19, 0x93, '\r', '\n', 0x1a, '\n'}
)

type gHeader struct {
	Signature  [_HEADER_SZ]byte
	Version    byte
	Format     byte
	Endianness byte
	IntSz      byte
	SizeTSz    byte
	InstrSz    byte
	NumberSz   byte
	IntFlag    byte
	Tail       [_TAIL_SZ]byte
}

func (h *gHeader) MajorVersion() byte {
	return h.Version >> 4
}

func (h *gHeader) MinorVersion() byte {
	return h.Version & 0x0F
}

func NewHeader() *gHeader {
	var i int32   // Force 4 bytes even on 64bit systems? Validate on 64bit Linux
	var ui uint64 // TODO : Force 8 bytes, uint gives only 4, inconsistent with Lua on 64-bit platforms
	var instr types.Instruction

	// Create a standard header based on the current architecture
	return &gHeader{
		Signature:  _HEADER,
		Version:    _VERSION,
		Format:     _FORMAT,
		Endianness: 1, // TODO : For now, force little-endian
		IntSz:      byte(unsafe.Sizeof(i)),
		SizeTSz:    byte(unsafe.Sizeof(ui)), // TODO : Is this consistent with what Lua gives on this platform?
		InstrSz:    byte(unsafe.Sizeof(instr)),
		NumberSz:   8, // TODO : Sizeof(the custom Number size)
		IntFlag:    0, // TODO : Support non-floating point compilation?
		Tail:       _TAIL,
	}
}

type prototype struct {
	meta     *funcMeta
	code     []types.Instruction
	ks       []types.Value
	protos   []*prototype
	upvalues []*upvalue

	// Debug info, unavailable in release build
	source   string
	lineInfo []int32
	locVars  []*locVar
}

func (p *prototype) String() string {
	var buf bytes.Buffer

	buf.WriteString(fmt.Sprintf("%+v\n", p.meta))
	buf.WriteString(fmt.Sprintln("Instructions (", len(p.code), ") :"))
	for _, c := range p.code {
		buf.WriteString(fmt.Sprintln(c))
	}
	buf.WriteString(fmt.Sprintln("Constants (", len(p.ks), ") :"))
	buf.WriteString(fmt.Sprintln(p.ks))
	buf.WriteString(fmt.Sprintln("Functions (", len(p.protos), ") :"))
	for _, f := range p.protos {
		buf.WriteString(fmt.Sprintln(f))
	}
	buf.WriteString(fmt.Sprintln("Upvalues (", len(p.upvalues), ") :"))
	for _, u := range p.upvalues {
		buf.WriteString(fmt.Sprintf("%+v\n", u))
	}
	buf.WriteString("\nDebug information:\n\n")
	buf.WriteString("Source: " + p.source + "\n")
	buf.WriteString(fmt.Sprintln("Line info (", len(p.lineInfo), ") :"))
	buf.WriteString(fmt.Sprintln(p.lineInfo))
	buf.WriteString(fmt.Sprintln("Local variables (", len(p.locVars), ") :"))
	for _, lv := range p.locVars {
		buf.WriteString(fmt.Sprintf("%+v\n", lv))
	}

	return buf.String()
}

type funcMeta struct {
	LineDefined     uint32
	LastLineDefined uint32
	NumParams       byte
	IsVarArg        byte
	MaxStackSize    byte
}

type upvalue struct {
	name    string
	instack byte
	idx     byte
}

type locVar struct {
	name    string
	startpc int
	endpc   int
}

func readString(r io.Reader) (string, error) {
	var sz uint64
	var s string

	err := binary.Read(r, binary.LittleEndian, &sz)
	if err != nil {
		return "", err
	}
	if sz > 0 {
		ch := make([]byte, sz)
		err = binary.Read(r, binary.LittleEndian, ch)
		if err != nil {
			return "", err
		}
		// Remove 0x00
		s = string(ch[:len(ch)-1])
	}
	return s, nil
}

func readDebug(r io.Reader, p *prototype) error {
	var n uint32
	var i uint32
	var err error

	// Source file name
	p.source, err = readString(r)
	if err != nil {
		return err
	}

	// Line numbers
	err = binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return err
	}
	for i = 0; i < n; i++ {
		var li int32
		err = binary.Read(r, binary.LittleEndian, &li)
		if err != nil {
			return err
		}
		p.lineInfo = append(p.lineInfo, li)
	}

	// Local variables
	err = binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return err
	}
	for i = 0; i < n; i++ {
		var lv locVar
		lv.name, err = readString(r)
		if err != nil {
			return err
		}
		err = binary.Read(r, binary.LittleEndian, &lv.startpc)
		if err != nil {
			return err
		}
		err = binary.Read(r, binary.LittleEndian, &lv.endpc)
		if err != nil {
			return err
		}
		p.locVars = append(p.locVars, &lv)
	}

	// Upvalue names
	err = binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return err
	}
	for i = 0; i < n; i++ {
		p.upvalues[i].name, err = readString(r)
		if err != nil {
			return err
		}
	}

	return nil
}

func readUpvalues(r io.Reader, p *prototype) error {
	var n uint32
	var i uint32

	err := binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return err
	}
	for i = 0; i < n; i++ {
		var ba [2]byte
		err = binary.Read(r, binary.LittleEndian, &ba)
		if err != nil {
			return err
		}
		p.upvalues = append(p.upvalues, &upvalue{"", ba[0], ba[1]})
	}

	return nil
}

func readConstants(r io.Reader, p *prototype) error {
	var n uint32
	var i uint32

	err := binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return err
	}

	for i = 0; i < n; i++ {
		// Read the constant's type, 1 byte
		var t byte
		err = binary.Read(r, binary.LittleEndian, &t)
		if err != nil {
			return err
		}
		switch types.ValType(t) {
		case types.TNIL:
			var v types.Value = nil
			p.ks = append(p.ks, v)
		case types.TBOOL:
			var v types.Value
			err = binary.Read(r, binary.LittleEndian, &t)
			if err != nil {
				return err
			}
			if t == 0 {
				v = false
			} else if t == 1 {
				v = true
			} else {
				return fmt.Errorf("invalid value for boolean: %d", t)
			}
			p.ks = append(p.ks, v)
		case types.TNUMBER:
			// TODO : A number is a double in Lua, will a read in a float64 work?
			var f float64
			var v types.Value
			err = binary.Read(r, binary.LittleEndian, &f)
			if err != nil {
				return err
			}
			v = f
			p.ks = append(p.ks, v)
		case types.TSTRING:
			var v types.Value
			v, err = readString(r)
			if err != nil {
				return err
			}
			p.ks = append(p.ks, v)
		default:
			return fmt.Errorf("unexpected constant type: %d", t)
		}
	}
	return nil
}

func readCode(r io.Reader, p *prototype) error {
	var n uint32
	var i uint32

	err := binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return err
	}
	for i = 0; i < n; i++ {
		var instr types.Instruction
		err = binary.Read(r, binary.LittleEndian, &instr)
		if err != nil {
			return err
		}
		p.code = append(p.code, instr)
	}
	return nil
}

func readFunction(r io.Reader) (*prototype, error) {
	var fm funcMeta
	var p prototype
	var n uint32
	var i uint32

	// Meta-data about the function
	err := binary.Read(r, binary.LittleEndian, &fm)
	if err != nil {
		return nil, err
	}
	p.meta = &fm

	// Function's instructions
	err = readCode(r, &p)
	if err != nil {
		return nil, err
	}

	// Function's constants
	err = readConstants(r, &p)
	if err != nil {
		return nil, err
	}

	// Inner function's functions (prototypes)
	err = binary.Read(r, binary.LittleEndian, &n)
	if err != nil {
		return nil, err
	}
	for i = 0; i < n; i++ {
		var subP *prototype
		subP, err = readFunction(r)
		if err != nil {
			return nil, err
		}
		p.protos = append(p.protos, subP)
	}

	// Upvalues
	err = readUpvalues(r, &p)
	if err != nil {
		return nil, err
	}

	// Debug
	err = readDebug(r, &p)
	if err != nil {
		return nil, err
	}

	return &p, nil
}

func readHeader(r io.Reader) (*gHeader, error) {
	var h gHeader

	err := binary.Read(r, binary.LittleEndian, &h)
	if err != nil {
		return nil, err
	}

	// Validate header
	stdH := NewHeader()

	// As a whole
	if h == *stdH {
		return &h, nil
	} else if h.Signature != stdH.Signature {
		return nil, fmt.Errorf("is not a precompiled chunk")
	} else if h.Version != stdH.Version {
		return nil, fmt.Errorf("version mismatch, got %d.%d, expected %d.%d", h.MajorVersion(), h.MinorVersion(), stdH.MajorVersion(), stdH.MinorVersion())
	}

	return nil, fmt.Errorf("incompatible")
}

func Load(r io.Reader) error {
	// First up, the Header (12 bytes) + LUAC_TAIL to "catch conversion errors", as described in Lua
	h, err := readHeader(r)
	if err != nil {
		return err
	}
	fmt.Printf("Header: %+v\n", h)

	// Then, the function header (a prototype)
	p, err := readFunction(r)
	if err != nil {
		return err
	}
	fmt.Println(p)

	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"errors"
	"sync"

	"github.com/moov-io/ach"
)

// Repository is the Service storage mechanism abstraction
type Repository interface {
	StoreFile(file *ach.File) error
	FindFile(id string) (*ach.File, error)
	FindAllFiles() []*ach.File
	DeleteFile(id string) error
	StoreBatch(fileID string, batch ach.Batcher) error
	FindBatch(fileID string, batchID string) (ach.Batcher, error)
	FindAllBatches(fileID string) []ach.Batcher
	DeleteBatch(fileID string, batchID string) error
}

type repositoryInMemory struct {
	mtx   sync.RWMutex
	files map[string]*ach.File
}

// NewRepositoryInMemory is an in memory ach storage repository for files
func NewRepositoryInMemory() Repository {
	f := map[string]*ach.File{}
	return &repositoryInMemory{
		files: f,
	}
}
func (r *repositoryInMemory) StoreFile(f *ach.File) error {
	if f == nil {
		return errors.New("nil ACH file provided")
	}

	r.mtx.Lock()
	defer r.mtx.Unlock()
	if _, ok := r.files[f.ID]; ok {
		return ErrAlreadyExists
	}
	r.files[f.ID] = f
	return nil
}

// FindFile retrieves a ach.File based on the supplied ID
func (r *repositoryInMemory) FindFile(id string) (*ach.File, error) {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	if val, ok := r.files[id]; ok {
		return val, nil
	}
	return nil, ErrNotFound
}

// FindAllFiles returns all files that have been saved in memory
func (r *repositoryInMemory) FindAllFiles() []*ach.File {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	files := make([]*ach.File, 0, len(r.files))
	for i := range r.files {
		files = append(files, r.files[i])
	}
	return files
}

func (r *repositoryInMemory) DeleteFile(id string) error {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	delete(r.files, id)
	return nil
}

// TODO(adam): was copying ach.Batcher causing issues?
func (r *repositoryInMemory) StoreBatch(fileID string, batch ach.Batcher) error {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	// Ensure the file does not already exist
	if _, ok := r.files[fileID]; !ok {
		return ErrNotFound
	}
	// ensure the batch does not already exist
	for _, val := range r.files[fileID].Batches {
		if val.ID() == batch.ID() {
			return ErrAlreadyExists
		}
	}
	// Add the batch to the file
	r.files[fileID].AddBatch(batch)
	return nil
}

// FindBatch retrieves a ach.Batcher based on the supplied ID
func (r *repositoryInMemory) FindBatch(fileID string, batchID string) (ach.Batcher, error) {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	for _, val := range r.files[fileID].Batches {
		if val.ID() == batchID {
			return val, nil
		}
	}
	return nil, ErrNotFound
}

// FindAllBatches
func (r *repositoryInMemory) FindAllBatches(fileID string) []ach.Batcher {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	batches := make([]ach.Batcher, 0, len(r.files[fileID].Batches))
	batches = append(batches, r.files[fileID].Batches...)
	return batches
}

func (r *repositoryInMemory) DeleteBatch(fileID string, batchID string) error {
	r.mtx.RLock()
	defer r.mtx.RUnlock()

	for i := len(r.files[fileID].Batches) - 1; i >= 0; i-- {
		if r.files[fileID].Batches[i].ID() == batchID {
			r.files[fileID].Batches = append(r.files[fileID].Batches[:i], r.files[fileID].Batches[i+1:]...)
			//fmt.Println(r.files[fileID].Batches)
			return nil
		}
	}
	return ErrNotFound
}
<commit_msg>server: write lock on deletes<commit_after>package server

import (
	"errors"
	"sync"

	"github.com/moov-io/ach"
)

// Repository is the Service storage mechanism abstraction
type Repository interface {
	StoreFile(file *ach.File) error
	FindFile(id string) (*ach.File, error)
	FindAllFiles() []*ach.File
	DeleteFile(id string) error
	StoreBatch(fileID string, batch ach.Batcher) error
	FindBatch(fileID string, batchID string) (ach.Batcher, error)
	FindAllBatches(fileID string) []ach.Batcher
	DeleteBatch(fileID string, batchID string) error
}

type repositoryInMemory struct {
	mtx   sync.RWMutex
	files map[string]*ach.File
}

// NewRepositoryInMemory is an in memory ach storage repository for files
func NewRepositoryInMemory() Repository {
	f := map[string]*ach.File{}
	return &repositoryInMemory{
		files: f,
	}
}
func (r *repositoryInMemory) StoreFile(f *ach.File) error {
	if f == nil {
		return errors.New("nil ACH file provided")
	}

	r.mtx.Lock()
	defer r.mtx.Unlock()
	if _, ok := r.files[f.ID]; ok {
		return ErrAlreadyExists
	}
	r.files[f.ID] = f
	return nil
}

// FindFile retrieves a ach.File based on the supplied ID
func (r *repositoryInMemory) FindFile(id string) (*ach.File, error) {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	if val, ok := r.files[id]; ok {
		return val, nil
	}
	return nil, ErrNotFound
}

// FindAllFiles returns all files that have been saved in memory
func (r *repositoryInMemory) FindAllFiles() []*ach.File {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	files := make([]*ach.File, 0, len(r.files))
	for i := range r.files {
		files = append(files, r.files[i])
	}
	return files
}

func (r *repositoryInMemory) DeleteFile(id string) error {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	delete(r.files, id)
	return nil
}

// TODO(adam): was copying ach.Batcher causing issues?
func (r *repositoryInMemory) StoreBatch(fileID string, batch ach.Batcher) error {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	// Ensure the file does not already exist
	if _, ok := r.files[fileID]; !ok {
		return ErrNotFound
	}
	// ensure the batch does not already exist
	for _, val := range r.files[fileID].Batches {
		if val.ID() == batch.ID() {
			return ErrAlreadyExists
		}
	}
	// Add the batch to the file
	r.files[fileID].AddBatch(batch)
	return nil
}

// FindBatch retrieves a ach.Batcher based on the supplied ID
func (r *repositoryInMemory) FindBatch(fileID string, batchID string) (ach.Batcher, error) {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	for _, val := range r.files[fileID].Batches {
		if val.ID() == batchID {
			return val, nil
		}
	}
	return nil, ErrNotFound
}

// FindAllBatches
func (r *repositoryInMemory) FindAllBatches(fileID string) []ach.Batcher {
	r.mtx.RLock()
	defer r.mtx.RUnlock()
	batches := make([]ach.Batcher, 0, len(r.files[fileID].Batches))
	batches = append(batches, r.files[fileID].Batches...)
	return batches
}

func (r *repositoryInMemory) DeleteBatch(fileID string, batchID string) error {
	r.mtx.Lock()
	defer r.mtx.Unlock()

	for i := len(r.files[fileID].Batches) - 1; i >= 0; i-- {
		if r.files[fileID].Batches[i].ID() == batchID {
			r.files[fileID].Batches = append(r.files[fileID].Batches[:i], r.files[fileID].Batches[i+1:]...)
			//fmt.Println(r.files[fileID].Batches)
			return nil
		}
	}
	return ErrNotFound
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/metainfo"

	"github.com/jpillora/cloud-torrent/engine"
)

func (s *Server) api(r *http.Request) error {
	defer r.Body.Close()
	if r.Method != "POST" {
		return fmt.Errorf("Invalid request method (expecting POST)")
	}

	action := strings.TrimPrefix(r.URL.Path, "/api/")

	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return fmt.Errorf("Failed to download request body")
	}

	//convert url into torrent bytes
	if action == "url" {
		url := string(data)
		remote, err := http.Get(url)
		if err != nil {
			return fmt.Errorf("Invalid remote torrent URL: %s (%s)", err, url)
		}
		//TODO enforce max body size (32k?)
		data, err = ioutil.ReadAll(remote.Body)
		if err != nil {
			return fmt.Errorf("Failed to download remote torrent: %s", err)
		}
		action = "torrentfile"
	}

	//convert torrent bytes into magnet
	if action == "torrentfile" {
		reader := bytes.NewBuffer(data)
		info, err := metainfo.Load(reader)
		if err != nil {
			return err
		}
		spec := torrent.TorrentSpecFromMetaInfo(info)
		if err := s.engine.NewTorrent(spec); err != nil {
			return fmt.Errorf("Torrent error: %s", err)
		}
		return nil
	}

	//update after action completes
	defer s.state.Push()

	//interface with engine
	switch action {
	case "configure":
		c := engine.Config{}
		if err := json.Unmarshal(data, &c); err != nil {
			return err
		}
		if err := s.reconfigure(c); err != nil {
			return err
		}
	case "magnet":
		uri := string(data)
		if err := s.engine.NewMagnet(uri); err != nil {
			return fmt.Errorf("Magnet error: %s", err)
		}
	case "torrent":
		cmd := strings.SplitN(string(data), ":", 2)
		if len(cmd) != 2 {
			return fmt.Errorf("Invalid request")
		}
		state := cmd[0]
		infohash := cmd[1]
		if state == "start" {
			if err := s.engine.StartTorrent(infohash); err != nil {
				return err
			}
		} else if state == "stop" {
			if err := s.engine.StopTorrent(infohash); err != nil {
				return err
			}
		} else if state == "delete" {
			if err := s.engine.DeleteTorrent(infohash); err != nil {
				return err
			}
		} else {
			return fmt.Errorf("Invalid state: %s", state)
		}
	case "file":
		cmd := strings.SplitN(string(data), ":", 3)
		if len(cmd) != 3 {
			return fmt.Errorf("Invalid request")
		}
		state := cmd[0]
		infohash := cmd[1]
		filepath := cmd[2]
		if state == "start" {
			if err := s.engine.StartFile(infohash, filepath); err != nil {
				return err
			}
		} else if state == "stop" {
			if err := s.engine.StopFile(infohash, filepath); err != nil {
				return err
			}
		} else {
			return fmt.Errorf("Invalid state: %s", state)
		}
	default:
		return fmt.Errorf("Invalid action: %s", action)
	}
	return nil
}
<commit_msg>Update server_api.go<commit_after>package server

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/anacrolix/torrent"
	"github.com/anacrolix/torrent/metainfo"

	"github.com/Thenast/Harryhs/engine"
)

func (s *Server) api(r *http.Request) error {
	defer r.Body.Close()
	if r.Method != "POST" {
		return fmt.Errorf("Invalid request method (expecting POST)")
	}

	action := strings.TrimPrefix(r.URL.Path, "/api/")

	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return fmt.Errorf("Failed to download request body")
	}

	//convert url into torrent bytes
	if action == "url" {
		url := string(data)
		remote, err := http.Get(url)
		if err != nil {
			return fmt.Errorf("Invalid remote torrent URL: %s (%s)", err, url)
		}
		//TODO enforce max body size (32k?)
		data, err = ioutil.ReadAll(remote.Body)
		if err != nil {
			return fmt.Errorf("Failed to download remote torrent: %s", err)
		}
		action = "torrentfile"
	}

	//convert torrent bytes into magnet
	if action == "torrentfile" {
		reader := bytes.NewBuffer(data)
		info, err := metainfo.Load(reader)
		if err != nil {
			return err
		}
		spec := torrent.TorrentSpecFromMetaInfo(info)
		if err := s.engine.NewTorrent(spec); err != nil {
			return fmt.Errorf("Torrent error: %s", err)
		}
		return nil
	}

	//update after action completes
	defer s.state.Push()

	//interface with engine
	switch action {
	case "configure":
		c := engine.Config{}
		if err := json.Unmarshal(data, &c); err != nil {
			return err
		}
		if err := s.reconfigure(c); err != nil {
			return err
		}
	case "magnet":
		uri := string(data)
		if err := s.engine.NewMagnet(uri); err != nil {
			return fmt.Errorf("Magnet error: %s", err)
		}
	case "torrent":
		cmd := strings.SplitN(string(data), ":", 2)
		if len(cmd) != 2 {
			return fmt.Errorf("Invalid request")
		}
		state := cmd[0]
		infohash := cmd[1]
		if state == "start" {
			if err := s.engine.StartTorrent(infohash); err != nil {
				return err
			}
		} else if state == "stop" {
			if err := s.engine.StopTorrent(infohash); err != nil {
				return err
			}
		} else if state == "delete" {
			if err := s.engine.DeleteTorrent(infohash); err != nil {
				return err
			}
		} else {
			return fmt.Errorf("Invalid state: %s", state)
		}
	case "file":
		cmd := strings.SplitN(string(data), ":", 3)
		if len(cmd) != 3 {
			return fmt.Errorf("Invalid request")
		}
		state := cmd[0]
		infohash := cmd[1]
		filepath := cmd[2]
		if state == "start" {
			if err := s.engine.StartFile(infohash, filepath); err != nil {
				return err
			}
		} else if state == "stop" {
			if err := s.engine.StopFile(infohash, filepath); err != nil {
				return err
			}
		} else {
			return fmt.Errorf("Invalid state: %s", state)
		}
	default:
		return fmt.Errorf("Invalid action: %s", action)
	}
	return nil
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/antonmedv/expr/file"
	"github.com/emicklei/melrose/core"
	"github.com/emicklei/melrose/dsl"
	"github.com/emicklei/melrose/notify"
)

func (l *LanguageServer) statementHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		notify.Console.Warnf("HTTP method not allowed:%s", r.Method)
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	query := r.URL.Query()
	l.context.Environment().Store(core.WorkingDirectory, filepath.Dir(query.Get("file")))

	debug := query.Get("debug") == "true" || core.IsDebug()
	if debug {
		notify.Debugf("service.http: %s", r.URL.String())
	}
	// get line
	line := 1
	lineString := query.Get("line")
	if len(lineString) > 0 {
		if i, err := strconv.Atoi(lineString); err == nil {
			line = i
		}
		l.context.Environment().Store(core.EditorLineStart, line)
	}
	// get expression source
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	source := string(data)

	// get and store line end
	breaks := strings.Count(source, "\n")
	if breaks > 0 {
		l.context.Environment().Store(core.EditorLineEnd, line+breaks)
	} else {
		l.context.Environment().Store(core.EditorLineEnd, line)
	}

	if debug {
		notify.Debugf("http.request.body %s", source)
	}
	defer r.Body.Close()
	if query.Get("action") == "kill" {
		// kill the play and any loop
		dsl.StopAllPlayables(l.context)
		l.context.Device().Reset()
		return
	}
	returnValue, err := l.evaluator.EvaluateProgram(source)
	var response evaluationResult
	if err != nil {
		// evaluation failed.
		w.WriteHeader(http.StatusBadRequest)
		response = resultFrom(query.Get("file"), line, err)
	} else {
		// evaluation was ok.

		if query.Get("action") == "inspect" {
			// check for function
			if reflect.TypeOf(returnValue).Kind() == reflect.Func {
				if fn, ok := l.evaluator.LookupFunction(string(data)); ok {
					fmt.Fprintf(notify.Console.StandardOut, "%s: %s\n", fn.Title, fn.Description)
				}
			} else {
				core.PrintValue(l.context, returnValue)
			}
		}

		// check if play was requested and is playable
		if query.Get("action") == "play" {
			// first check Playable
			if pl, ok := returnValue.(core.Playable); ok {
				notify.Infof("play(%s)", displayString(l.context, pl))
				_ = pl.Play(l.context, time.Now())
			} else {
				// any sequenceable is playable
				if s, ok := returnValue.(core.Sequenceable); ok {
					notify.Infof("play(%s)", displayString(l.context, s))
					l.context.Device().Play(
						core.NoCondition,
						s,
						l.context.Control().BPM(),
						time.Now())
				}
			}
		}
		// deprectated TODO
		if query.Get("action") == "begin" {
			if p, ok := returnValue.(core.Playable); ok {
				notify.Infof("begin(%s)", displayString(l.context, p))
				p.Play(l.context, time.Now())
			}
		}
		// deprectated end TODO
		if query.Get("action") == "end" || query.Get("action") == "stop" {
			if p, ok := returnValue.(core.Stoppable); ok {
				notify.Infof("stop(%s)", displayString(l.context, p))
				p.Stop(l.context)
			}
		}

		response = resultFrom(query.Get("file"), line, returnValue)
	}
	w.Header().Set("content-type", "application/json")
	enc := json.NewEncoder(w)
	enc.SetIndent("", "\t")
	err = enc.Encode(response)
	if err != nil {
		notify.NewErrorf("error:%v\n", err)
		return
	}
	if response.IsError {
		notify.Print(notify.NewError(response.Object.(error)))
	} else {
		core.PrintValue(l.context, response.Object)
	}
	if debug {
		// doit again
		buf := bytes.Buffer{}
		enc := json.NewEncoder(&buf)
		enc.SetIndent("", "\t")
		err = enc.Encode(response)
		notify.Debugf("http.response: %s error=%v", buf.String(), err)
	}
}

func displayString(ctx core.Context, v interface{}) string {
	name := ctx.Variables().NameFor(v)
	if len(name) == 0 {
		name = core.Storex(v)
	}
	return name
}

type evaluationResult struct {
	Type         string      `json:"type"`
	IsError      bool        `json:"is-error"`
	IsStoppeable bool        `json:"stoppable"`
	Message      string      `json:"message"`
	Filename     string      `json:"file"`
	Line         int         `json:"line"`
	Column       int         `json:"column"`
	Object       interface{} `json:"object"`
}

func resultFrom(filename string, line int, val interface{}) evaluationResult {
	t := fmt.Sprintf("%T", val)
	_, isStoppable := val.(core.Stoppable)
	if err, ok := val.(error); ok {
		// patch Location of error
		if fe, ok := err.(*file.Error); ok {
			fe.Location.Line = fe.Location.Line - 1 + line
		}
		return evaluationResult{
			Type:         t,
			IsError:      true,
			IsStoppeable: isStoppable,
			Filename:     filename,
			Message:      err.Error(),
			Line:         line,
			Object:       val,
		}
	}
	// no error
	var msg string
	if stor, ok := val.(core.Storable); ok {
		msg = stor.Storex()
	} else {
		msg = fmt.Sprintf("%v", val)
	}
	// no Object if ok
	return evaluationResult{
		Type:         t,
		IsError:      false,
		IsStoppeable: isStoppable,
		Filename:     filename,
		Line:         line,
		Message:      msg}
}
<commit_msg>stoppable need no variable anymore 2<commit_after>package server

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/antonmedv/expr/file"
	"github.com/emicklei/melrose/core"
	"github.com/emicklei/melrose/dsl"
	"github.com/emicklei/melrose/notify"
)

func (l *LanguageServer) statementHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		notify.Console.Warnf("HTTP method not allowed:%s", r.Method)
		w.WriteHeader(http.StatusMethodNotAllowed)
		return
	}
	query := r.URL.Query()
	l.context.Environment().Store(core.WorkingDirectory, filepath.Dir(query.Get("file")))

	debug := query.Get("debug") == "true" || core.IsDebug()
	if debug {
		notify.Debugf("service.http: %s", r.URL.String())
	}
	// get line
	line := 1
	lineString := query.Get("line")
	if len(lineString) > 0 {
		if i, err := strconv.Atoi(lineString); err == nil {
			line = i
		}
		l.context.Environment().Store(core.EditorLineEnd, line)
	}
	// get expression source
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	source := string(data)

	// get and store line end
	breaks := strings.Count(source, "\n")
	if breaks > 0 {
		l.context.Environment().Store(core.EditorLineStart, line-breaks)
	} else {
		l.context.Environment().Store(core.EditorLineStart, line)
	}

	if debug {
		notify.Debugf("http.request.body %s", source)
	}
	defer r.Body.Close()
	if query.Get("action") == "kill" {
		// kill the play and any loop
		dsl.StopAllPlayables(l.context)
		l.context.Device().Reset()
		return
	}
	returnValue, err := l.evaluator.EvaluateProgram(source)
	var response evaluationResult
	if err != nil {
		// evaluation failed.
		w.WriteHeader(http.StatusBadRequest)
		response = resultFrom(query.Get("file"), line, err)
	} else {
		// evaluation was ok.

		if query.Get("action") == "inspect" {
			// check for function
			if reflect.TypeOf(returnValue).Kind() == reflect.Func {
				if fn, ok := l.evaluator.LookupFunction(string(data)); ok {
					fmt.Fprintf(notify.Console.StandardOut, "%s: %s\n", fn.Title, fn.Description)
				}
			} else {
				core.PrintValue(l.context, returnValue)
			}
		}

		// check if play was requested and is playable
		if query.Get("action") == "play" {
			// first check Playable
			if pl, ok := returnValue.(core.Playable); ok {
				notify.Infof("play(%s)", displayString(l.context, pl))
				_ = pl.Play(l.context, time.Now())
			} else {
				// any sequenceable is playable
				if s, ok := returnValue.(core.Sequenceable); ok {
					notify.Infof("play(%s)", displayString(l.context, s))
					l.context.Device().Play(
						core.NoCondition,
						s,
						l.context.Control().BPM(),
						time.Now())
				}
			}
		}
		// deprectated TODO
		if query.Get("action") == "begin" {
			if p, ok := returnValue.(core.Playable); ok {
				notify.Infof("begin(%s)", displayString(l.context, p))
				p.Play(l.context, time.Now())
			}
		}
		// deprectated end TODO
		if query.Get("action") == "end" || query.Get("action") == "stop" {
			if p, ok := returnValue.(core.Stoppable); ok {
				notify.Infof("stop(%s)", displayString(l.context, p))
				p.Stop(l.context)
			}
		}

		response = resultFrom(query.Get("file"), line, returnValue)
	}
	w.Header().Set("content-type", "application/json")
	enc := json.NewEncoder(w)
	enc.SetIndent("", "\t")
	err = enc.Encode(response)
	if err != nil {
		notify.NewErrorf("error:%v\n", err)
		return
	}
	if response.IsError {
		notify.Print(notify.NewError(response.Object.(error)))
	} else {
		core.PrintValue(l.context, response.Object)
	}
	if debug {
		// doit again
		buf := bytes.Buffer{}
		enc := json.NewEncoder(&buf)
		enc.SetIndent("", "\t")
		err = enc.Encode(response)
		notify.Debugf("http.response: %s error=%v", buf.String(), err)
	}
}

func displayString(ctx core.Context, v interface{}) string {
	name := ctx.Variables().NameFor(v)
	if len(name) == 0 {
		name = core.Storex(v)
	}
	return name
}

type evaluationResult struct {
	Type         string      `json:"type"`
	IsError      bool        `json:"is-error"`
	IsStoppeable bool        `json:"stoppable"`
	Message      string      `json:"message"`
	Filename     string      `json:"file"`
	Line         int         `json:"line"`
	Column       int         `json:"column"`
	Object       interface{} `json:"object"`
}

func resultFrom(filename string, line int, val interface{}) evaluationResult {
	t := fmt.Sprintf("%T", val)
	_, isStoppable := val.(core.Stoppable)
	if err, ok := val.(error); ok {
		// patch Location of error
		if fe, ok := err.(*file.Error); ok {
			fe.Location.Line = fe.Location.Line - 1 + line
		}
		return evaluationResult{
			Type:         t,
			IsError:      true,
			IsStoppeable: isStoppable,
			Filename:     filename,
			Message:      err.Error(),
			Line:         line,
			Object:       val,
		}
	}
	// no error
	var msg string
	if stor, ok := val.(core.Storable); ok {
		msg = stor.Storex()
	} else {
		msg = fmt.Sprintf("%v", val)
	}
	// no Object if ok
	return evaluationResult{
		Type:         t,
		IsError:      false,
		IsStoppeable: isStoppable,
		Filename:     filename,
		Line:         line,
		Message:      msg}
}
<|endoftext|>