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

//go:generate stringer -type=Verbosity
//go:generate stringer -type=ComponentType

package resp

import (
	"encoding/hex"
	"encoding/json"
	"strings"
	"time"

	"golang.org/x/net/context"

	"github.com/luci/luci-go/common/logging"
	"github.com/luci/luci-go/milo/common"
	"github.com/luci/luci-go/milo/common/model"
)

// MiloBuild denotes a full renderable Milo build page.
type MiloBuild struct {
	// Summary is a top level summary of the page.
	Summary BuildComponent

	// SourceStamp gives information about how the build came about.
	SourceStamp *SourceStamp

	// Components is a detailed list of components and subcomponents of the page.
	// This is most often used for steps (buildbot/luci) or deps (luci).
	Components []*BuildComponent

	// PropertyGroup is a list of input and output property of this page.
	// This is used for build and emitted properties (buildbot) and quest
	// information (luci).  This is also grouped by the "type" of property
	// so different categories of properties can be separated and sorted.
	//
	// This is not a map so code that constructs MiloBuild can control the
	// order of property groups, for example show important properties
	// first.
	PropertyGroup []*PropertyGroup

	// Blame is a list of people and commits that is likely to be in relation to
	// the thing displayed on this page.
	Blame []*Commit
}

// SourceStamp is the combination of pointing to a single commit, with information
// about where that commit came from (eg. the repository).
type SourceStamp struct {
	Commit
	// Source is the trigger source.  In buildbot, this would be the "Reason".
	// This has no meaning in SwarmBucket and DM yet.
	Source string
}

// Property specifies k/v pair representing some
// sort of property, such as buildbot property, quest property, etc.
type Property struct {
	Key   string
	Value string
}

// PropertyGroup is a cluster of similar properties.  In buildbot land this would be the "source".
// This is a way to segregate different types of properties such as Quest properties,
// swarming properties, emitted properties, revision properties, etc.
type PropertyGroup struct {
	GroupName string
	Property  []*Property
}

func (p PropertyGroup) Len() int { return len(p.Property) }
func (p PropertyGroup) Swap(i, j int) {
	p.Property[i], p.Property[j] = p.Property[j], p.Property[i]
}
func (p PropertyGroup) Less(i, j int) bool { return p.Property[i].Key < p.Property[j].Key }

// Commit represents a single commit to a repository, rendered as part of a blamelist.
type Commit struct {
	// Who made the commit?
	AuthorName string
	// Email of the committer.
	AuthorEmail string
	// Time of the commit.
	CommitTime time.Time
	// Full URL of the main source repository.
	Repo string
	// Branch of the repo.
	Branch string
	// Requested revision of the commit or base commit.
	RequestRevision *Link
	// Revision of the commit or base commit.
	Revision *Link
	// The commit message.
	Description string
	// Rietveld or Gerrit URL if the commit is a patch.
	Changelist *Link
	// Browsable URL of the commit.
	CommitURL string
	// List of changed filenames.
	File []string
}

// Title is the first line of the commit message (Description).
func (c *Commit) Title() string {
	switch lines := strings.SplitN(c.Description, "\n", 2); len(lines) {
	case 0:
		return ""
	case 1:
		return c.Description
	default:
		return lines[0]
	}
}

// BuildProgress is a way to show progress.  Percent should always be specified.
type BuildProgress struct {
	// The total number of entries. Shows up as a tooltip.  Leave at 0 to
	// disable the tooltip.
	total uint64

	// The number of entries completed.  Shows up as <progress> / <total>.
	progress uint64

	// A number between 0 to 100 denoting the percentage completed.
	percent uint32
}

// ComponentType is the type of build component.
type ComponentType int

const (
	// Recipe corresponds to a full recipe run.  Dependencies are recipes.
	Recipe ComponentType = iota

	// Step is a single step of a recipe.
	Step

	// Summary denotes that this does not pretain to any particular step.
	Summary
)

// MarshalJSON renders enums into String rather than an int when marshalling.
func (c ComponentType) MarshalJSON() ([]byte, error) {
	return json.Marshal(c.String())
}

// LogoBanner is a banner of logos that define the OS and devices that a
// component is associated with.
type LogoBanner struct {
	OS     []Logo
	Device []Logo
}

// Verbosity can be tagged onto a BuildComponent to indicate whether it should
// be hidden or annuciated.
type Verbosity int

const (
	// Normal items are displayed as usual.  This is the default.
	Normal Verbosity = iota

	// Hidden items are by default not displayed.
	Hidden

	// Interesting items are a signal that they should be annuciated, or
	// pre-fetched.
	Interesting
)

// BuildComponent represents a single Step, subsetup, attempt, or recipe.
type BuildComponent struct {
	// The parent of this component.  For buildbot and swarmbucket builds, this
	// refers to the builder.  For DM, this refers to whatever triggered the Quest.
	ParentLabel *Link `json:",omitempty"`

	// The main label for the component.
	Label string

	// Status of the build.
	Status model.Status

	// Banner is a banner of logos that define the OS and devices this
	// component is associated with.
	Banner *LogoBanner `json:",omitempty"`

	// Bot is the machine or execution instance that this component ran on.
	Bot *Link `json:",omitempty"`

	// Recipe is a link to the recipe this component is based on.
	Recipe *Link `json:",omitempty"`

	// Source is a link to the external (buildbot, swarming, dm, etc) data
	// source that this component relates to.
	Source *Link `json:",omitempty"`

	// Links to show adjacent to the main label.
	MainLink LinkSet `json:",omitempty"`

	// Links to show right below the main label. Top-level slice are rows of
	// links, second level shows up as
	SubLink []LinkSet `json:",omitempty"`

	// Designates the progress of the current component. Set null for no progress.
	Progress *BuildProgress `json:",omitempty"`

	// When did this step start.
	Started time.Time

	// When did this step finish.
	Finished time.Time

	// The time it took for this step to finish. If unfinished, this is the
	// current elapsed duration.
	Duration time.Duration

	// The type of component.  This manifests itself as a little label on the
	// top left corner of the component.
	// This is either "RECIPE" or "STEP".  An attempt is considered a recipe.
	Type ComponentType

	// Specifies if this is a top level or a dependency.  Manifests itself as an
	// indentation level.  Valid options are 0 and 1.  Anything more than 1 is
	// automatically truncated to 1.
	LevelsDeep uint32

	// Verbosity indicates how important this step is.
	Verbosity Verbosity

	// Arbitrary text to display below links.  One line per entry,
	// newlines are stripped.
	Text []string
}

// Navigation is the top bar of the page, used for navigating out of the page.
type Navigation struct {
	// Title of the site, is generally consistent throughout the whole site.
	SiteTitle *Link

	// Title of the page, usually talks about what's currently here
	PageTitle *Link

	// List of clickable thing to navigate down the hierarchy.
	Breadcrumbs []*Link

	// List of (smaller) clickable things to display on the right side.
	Right []*Link
}

// LinkSet is an ordered collection of Link objects that will be rendered on the
// same line.
type LinkSet []*Link

// Link denotes a single labeled link.
//
// JSON tags here are for test expectations.
type Link struct {
	model.Link

	// An icon for the link.  Not compatible with label.  Rendered as
	Img string `json:",omitempty"`

	// Alt text for the image, only supported with img.
	Alt string `json:",omitempty"`

	// Alias, if true, means that this link is an [alias link].
	Alias bool `json:",omitempty"`
}

// NewLink does just about what you'd expect.
func NewLink(label, url string) *Link {
	return &Link{Link: model.Link{Label: label, URL: url}}
}

func (comp *BuildComponent) toModelSummary() model.Summary {
	text := comp.Text
	// Max length for a datastore text field is 1500 char, but for summary purposes
	// we don't really need anything much longer than 256 char or so.
	const maxTextLen = 256
	sliceIdx := 0
	// Make sure we don't cut off characters inbetween runes.
	for idx := range text {
		if idx > maxTextLen {
			break
		}
		sliceIdx = idx
	}
	text = text[:sliceIdx]
	return model.Summary{
		Status: comp.Status,
		Start:  comp.Started,
		End:    comp.Finished,
		Text:   text,
	}
}

// SummarizeTo summarizes the data into a given model.BuildSummary.
func (rb *MiloBuild) SummarizeTo(c context.Context, bs *model.BuildSummary) error {
	bs.Summary = rb.Summary.toModelSummary()
	if rb.Summary.Status == model.Running {
		// Assume the last step is the current step.
		if len(rb.Components) > 0 {
			cs := rb.Components[len(rb.Components)-1]
			bs.CurrentStep = cs.toModelSummary()
		}
	}
	if rb.SourceStamp != nil {
		// TODO(hinoka, iannucci): This should be full manifests, but lets just use
		// single revisions for now. HACKS!
		if rb.SourceStamp.Revision != nil {
			revisionBytes, err := hex.DecodeString(rb.SourceStamp.Revision.Label)
			if err != nil {
				logging.WithError(err).Warningf(c, "bad revision (not hex-decodable)")
			} else {
				bs.Manifests = append(bs.Manifests, model.ManifestLink{
					Name: "REVISION",
					ID:   []byte(rb.SourceStamp.Revision.Label),
				})
				consoles, err := common.GetAllConsoles(c, bs.BuilderID)
				if err != nil {
					return err
				}
				for _, con := range consoles {
					// HACK(iannucci): Until we have real manifest support, console
					// definitions will specify their manifest as "REVISION", and we'll do
					// lookups with null URL fields.
					bs.AddManifestKey(
						con.GetProjectName(), con.ID, "REVISION", "", revisionBytes)
				}
			}
		}
		if rb.SourceStamp.Changelist != nil {
			bs.Patches = append(bs.Patches, model.PatchInfo{
				Link:        rb.SourceStamp.Changelist.Link,
				AuthorEmail: rb.SourceStamp.AuthorEmail,
			})
		}
	}
	return nil
}
<commit_msg>Milo: Actually shorten step text<commit_after>// Copyright 2015 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:generate stringer -type=Verbosity
//go:generate stringer -type=ComponentType

package resp

import (
	"encoding/hex"
	"encoding/json"
	"strings"
	"time"

	"golang.org/x/net/context"

	"github.com/luci/luci-go/common/logging"
	"github.com/luci/luci-go/milo/common"
	"github.com/luci/luci-go/milo/common/model"
)

// MiloBuild denotes a full renderable Milo build page.
type MiloBuild struct {
	// Summary is a top level summary of the page.
	Summary BuildComponent

	// SourceStamp gives information about how the build came about.
	SourceStamp *SourceStamp

	// Components is a detailed list of components and subcomponents of the page.
	// This is most often used for steps (buildbot/luci) or deps (luci).
	Components []*BuildComponent

	// PropertyGroup is a list of input and output property of this page.
	// This is used for build and emitted properties (buildbot) and quest
	// information (luci).  This is also grouped by the "type" of property
	// so different categories of properties can be separated and sorted.
	//
	// This is not a map so code that constructs MiloBuild can control the
	// order of property groups, for example show important properties
	// first.
	PropertyGroup []*PropertyGroup

	// Blame is a list of people and commits that is likely to be in relation to
	// the thing displayed on this page.
	Blame []*Commit
}

// SourceStamp is the combination of pointing to a single commit, with information
// about where that commit came from (eg. the repository).
type SourceStamp struct {
	Commit
	// Source is the trigger source.  In buildbot, this would be the "Reason".
	// This has no meaning in SwarmBucket and DM yet.
	Source string
}

// Property specifies k/v pair representing some
// sort of property, such as buildbot property, quest property, etc.
type Property struct {
	Key   string
	Value string
}

// PropertyGroup is a cluster of similar properties.  In buildbot land this would be the "source".
// This is a way to segregate different types of properties such as Quest properties,
// swarming properties, emitted properties, revision properties, etc.
type PropertyGroup struct {
	GroupName string
	Property  []*Property
}

func (p PropertyGroup) Len() int { return len(p.Property) }
func (p PropertyGroup) Swap(i, j int) {
	p.Property[i], p.Property[j] = p.Property[j], p.Property[i]
}
func (p PropertyGroup) Less(i, j int) bool { return p.Property[i].Key < p.Property[j].Key }

// Commit represents a single commit to a repository, rendered as part of a blamelist.
type Commit struct {
	// Who made the commit?
	AuthorName string
	// Email of the committer.
	AuthorEmail string
	// Time of the commit.
	CommitTime time.Time
	// Full URL of the main source repository.
	Repo string
	// Branch of the repo.
	Branch string
	// Requested revision of the commit or base commit.
	RequestRevision *Link
	// Revision of the commit or base commit.
	Revision *Link
	// The commit message.
	Description string
	// Rietveld or Gerrit URL if the commit is a patch.
	Changelist *Link
	// Browsable URL of the commit.
	CommitURL string
	// List of changed filenames.
	File []string
}

// Title is the first line of the commit message (Description).
func (c *Commit) Title() string {
	switch lines := strings.SplitN(c.Description, "\n", 2); len(lines) {
	case 0:
		return ""
	case 1:
		return c.Description
	default:
		return lines[0]
	}
}

// BuildProgress is a way to show progress.  Percent should always be specified.
type BuildProgress struct {
	// The total number of entries. Shows up as a tooltip.  Leave at 0 to
	// disable the tooltip.
	total uint64

	// The number of entries completed.  Shows up as <progress> / <total>.
	progress uint64

	// A number between 0 to 100 denoting the percentage completed.
	percent uint32
}

// ComponentType is the type of build component.
type ComponentType int

const (
	// Recipe corresponds to a full recipe run.  Dependencies are recipes.
	Recipe ComponentType = iota

	// Step is a single step of a recipe.
	Step

	// Summary denotes that this does not pretain to any particular step.
	Summary
)

// MarshalJSON renders enums into String rather than an int when marshalling.
func (c ComponentType) MarshalJSON() ([]byte, error) {
	return json.Marshal(c.String())
}

// LogoBanner is a banner of logos that define the OS and devices that a
// component is associated with.
type LogoBanner struct {
	OS     []Logo
	Device []Logo
}

// Verbosity can be tagged onto a BuildComponent to indicate whether it should
// be hidden or annuciated.
type Verbosity int

const (
	// Normal items are displayed as usual.  This is the default.
	Normal Verbosity = iota

	// Hidden items are by default not displayed.
	Hidden

	// Interesting items are a signal that they should be annuciated, or
	// pre-fetched.
	Interesting
)

// BuildComponent represents a single Step, subsetup, attempt, or recipe.
type BuildComponent struct {
	// The parent of this component.  For buildbot and swarmbucket builds, this
	// refers to the builder.  For DM, this refers to whatever triggered the Quest.
	ParentLabel *Link `json:",omitempty"`

	// The main label for the component.
	Label string

	// Status of the build.
	Status model.Status

	// Banner is a banner of logos that define the OS and devices this
	// component is associated with.
	Banner *LogoBanner `json:",omitempty"`

	// Bot is the machine or execution instance that this component ran on.
	Bot *Link `json:",omitempty"`

	// Recipe is a link to the recipe this component is based on.
	Recipe *Link `json:",omitempty"`

	// Source is a link to the external (buildbot, swarming, dm, etc) data
	// source that this component relates to.
	Source *Link `json:",omitempty"`

	// Links to show adjacent to the main label.
	MainLink LinkSet `json:",omitempty"`

	// Links to show right below the main label. Top-level slice are rows of
	// links, second level shows up as
	SubLink []LinkSet `json:",omitempty"`

	// Designates the progress of the current component. Set null for no progress.
	Progress *BuildProgress `json:",omitempty"`

	// When did this step start.
	Started time.Time

	// When did this step finish.
	Finished time.Time

	// The time it took for this step to finish. If unfinished, this is the
	// current elapsed duration.
	Duration time.Duration

	// The type of component.  This manifests itself as a little label on the
	// top left corner of the component.
	// This is either "RECIPE" or "STEP".  An attempt is considered a recipe.
	Type ComponentType

	// Specifies if this is a top level or a dependency.  Manifests itself as an
	// indentation level.  Valid options are 0 and 1.  Anything more than 1 is
	// automatically truncated to 1.
	LevelsDeep uint32

	// Verbosity indicates how important this step is.
	Verbosity Verbosity

	// Arbitrary text to display below links.  One line per entry,
	// newlines are stripped.
	Text []string
}

// Navigation is the top bar of the page, used for navigating out of the page.
type Navigation struct {
	// Title of the site, is generally consistent throughout the whole site.
	SiteTitle *Link

	// Title of the page, usually talks about what's currently here
	PageTitle *Link

	// List of clickable thing to navigate down the hierarchy.
	Breadcrumbs []*Link

	// List of (smaller) clickable things to display on the right side.
	Right []*Link
}

// LinkSet is an ordered collection of Link objects that will be rendered on the
// same line.
type LinkSet []*Link

// Link denotes a single labeled link.
//
// JSON tags here are for test expectations.
type Link struct {
	model.Link

	// An icon for the link.  Not compatible with label.  Rendered as
	Img string `json:",omitempty"`

	// Alt text for the image, only supported with img.
	Alt string `json:",omitempty"`

	// Alias, if true, means that this link is an [alias link].
	Alias bool `json:",omitempty"`
}

// NewLink does just about what you'd expect.
func NewLink(label, url string) *Link {
	return &Link{Link: model.Link{Label: label, URL: url}}
}

func (comp *BuildComponent) toModelSummary() model.Summary {
	text := strings.Join(comp.Text, "\n")
	// Max length for a datastore text field is 1500 char, but for summary purposes
	// we don't really need anything much longer than 256 char or so.
	const maxTextLen = 256
	sliceIdx := 0
	// Make sure we don't cut off characters inbetween runes.
	for idx := range text {
		if idx > maxTextLen {
			break
		}
		sliceIdx = idx
	}
	text = text[:sliceIdx]
	return model.Summary{
		Status: comp.Status,
		Start:  comp.Started,
		End:    comp.Finished,
		Text:   strings.Split(text, "\n"),
	}
}

// SummarizeTo summarizes the data into a given model.BuildSummary.
func (rb *MiloBuild) SummarizeTo(c context.Context, bs *model.BuildSummary) error {
	bs.Summary = rb.Summary.toModelSummary()
	if rb.Summary.Status == model.Running {
		// Assume the last step is the current step.
		if len(rb.Components) > 0 {
			cs := rb.Components[len(rb.Components)-1]
			bs.CurrentStep = cs.toModelSummary()
		}
	}
	if rb.SourceStamp != nil {
		// TODO(hinoka, iannucci): This should be full manifests, but lets just use
		// single revisions for now. HACKS!
		if rb.SourceStamp.Revision != nil {
			revisionBytes, err := hex.DecodeString(rb.SourceStamp.Revision.Label)
			if err != nil {
				logging.WithError(err).Warningf(c, "bad revision (not hex-decodable)")
			} else {
				bs.Manifests = append(bs.Manifests, model.ManifestLink{
					Name: "REVISION",
					ID:   []byte(rb.SourceStamp.Revision.Label),
				})
				consoles, err := common.GetAllConsoles(c, bs.BuilderID)
				if err != nil {
					return err
				}
				for _, con := range consoles {
					// HACK(iannucci): Until we have real manifest support, console
					// definitions will specify their manifest as "REVISION", and we'll do
					// lookups with null URL fields.
					bs.AddManifestKey(
						con.GetProjectName(), con.ID, "REVISION", "", revisionBytes)
				}
			}
		}
		if rb.SourceStamp.Changelist != nil {
			bs.Patches = append(bs.Patches, model.PatchInfo{
				Link:        rb.SourceStamp.Changelist.Link,
				AuthorEmail: rb.SourceStamp.AuthorEmail,
			})
		}
	}
	return nil
}
<|endoftext|>


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

package money

import (
	"net/http"
	"net/http/httptest"
	"strings"
)

// Money's version of an http.Handler to decorate
type Handler interface {
	ServeHTTP(http.ResponseWriter, *http.Request)
}

// HandlerFunc is a function type that implements the Handler interface
type HandlerFunc func(http.ResponseWriter, *http.Request)

func (f HandlerFunc) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	f(rw, req)
}

// A Decorator wraps a Handler with extra behaviour
type Decorator func(Handler) Handler

// Used when decorating an http.HandlerFunc()
// spanName: will set the span-name
func AddToHandler(spanName string) Decorator {
	return func(f Handler) Handler {
		return HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
			if req.Header.Get(HEADER) == "" {
				f.ServeHTTP(rw, req)
			} else {
				rec := httptest.NewRecorder()
				defer Finish(rw, req, rec)

				rw, req, rec = Start(rw, req, rec, spanName)

				f.ServeHTTP(rec, req)
			}
		})
	}
}

// Decorate decorates a Handler h with all the given Decorators, in order.
func Decorate(h Handler, ds ...(func(Handler) Handler)) Handler {
	decorated := h
	for _, decorate := range ds {
		decorated = decorate(decorated)
	}
	return decorated
}

// Removes the Money header from the http.Header
func DelAllMNYHeaders(header http.Header) http.Header {
	for h := range header {
		if strings.EqualFold(h, HEADER) {
			header.Del(h)
			return header
		}
	}

	return header
}

// Adds Money values to the http.Header
func AddAllMNYHeaders(header http.Header, allMNY []*Money) http.Header {
	for x := range allMNY {
		header.Add(HEADER, allMNY[x].ToString())
	}

	return header
}

// Updates the Money Header values for a *http.Request
func UpdateMNYHeaderReq(req *http.Request, allMNY []*Money) *http.Request {
	header := DelAllMNYHeaders(req.Header)
	header = AddAllMNYHeaders(header, allMNY)
	req.Header = header

	return req
}

// Updates the Money Header values for a http.ResponseWriter
func UpdateMNYHeaderRW(rw http.ResponseWriter, allMNY []*Money) http.ResponseWriter {
	header := DelAllMNYHeaders(rw.Header())
	header = AddAllMNYHeaders(header, allMNY)
	if mnyheader, ok := header[http.CanonicalHeaderKey(HEADER)]; ok {
		for i := 0; i < len(mnyheader); i++ {
			if i == 0 {
				rw.Header().Set(HEADER, mnyheader[i])
			} else {
				rw.Header().Add(HEADER, mnyheader[i])
			}
		}
	}

	return rw
}

// Updates the Money Header values for a httptest.ResponseRecorder
func UpdateMNYHeaderRec(rec *httptest.ResponseRecorder, allMNY []*Money) *httptest.ResponseRecorder {
	header := DelAllMNYHeaders(rec.HeaderMap)
	header = AddAllMNYHeaders(header, allMNY)
	if mnyheader, ok := header[http.CanonicalHeaderKey(HEADER)]; ok {
		for i := 0; i < len(mnyheader); i++ {
			if i == 0 {
				rec.HeaderMap.Set(HEADER, mnyheader[i])
			} else {
				rec.HeaderMap.Add(HEADER, mnyheader[i])
			}
		}
	}

	return rec
}

func copyOfMNY(MNYs []*Money) []Money {
	var mnys []Money
	for i := 0; i < len(MNYs); i++ {
		mnys = append(mnys, *MNYs[i])
	}

	return mnys
}

// Finds the Money value for this instance
func GetCurrentHeader(MNYs []*Money) Money {
	mnys := copyOfMNY(MNYs)

	pID := mnys[0].parentId
	for len(mnys) > 1 {
		found := false
		for i := 0; i < len(mnys); i++ {
			if mnys[i].spanId == pID {
				pID = mnys[i].parentId
				found = true
				mnys = append(mnys[:i], mnys[i+1:]...)

				break
			}
		}

		if !found {
			mnys = append(mnys[:0], mnys[1:]...)
			pID = mnys[0].parentId
		}
	}

	return mnys[0]
}

// Decorator start of the money trace process
func Start(rw http.ResponseWriter, req *http.Request, rec *httptest.ResponseRecorder, spanName string) (http.ResponseWriter, *http.Request, *httptest.ResponseRecorder) {
	var allMNY []*Money
	for _, m := range req.Header[ http.CanonicalHeaderKey(HEADER) ] {
		allMNY = append(allMNY, StringToObject(m))
	}

	pMNY := GetCurrentHeader(allMNY)
	cMNY := NewChild(pMNY.ToString(), spanName)
	allMNY = append(allMNY, cMNY)

	req = UpdateMNYHeaderReq(req, allMNY)
	rw = UpdateMNYHeaderRW(rw, allMNY)
	rec = UpdateMNYHeaderRec(rec, allMNY)

	return rw, req, rec
}

// Decorator finish of the money trace process
func Finish(rw http.ResponseWriter, req *http.Request, rec *httptest.ResponseRecorder) (http.ResponseWriter, *http.Request) {
	for k, v := range rec.HeaderMap {
		for i := range v {
			if i == 0 {
				rw.Header().Set(k, v[i])
			} else {
				rw.Header().Add(k, v[i])
			}
		}
	}

	var allMNY []*Money
	for _, v := range rw.Header()[ http.CanonicalHeaderKey(HEADER) ] {
		allMNY = append(allMNY, StringToObject(v))
	}

	mny := GetCurrentHeader(allMNY)

	if rec.Code >= 400 {
		mny.AddResults(rec.Code, false)
	} else {
		mny.AddResults(rec.Code, true)
	}

	// update current mny object
	for x := range allMNY {
		if allMNY[x].spanId == mny.spanId {
			allMNY[x] = &mny
			break
		}
	}

	req = UpdateMNYHeaderReq(req, allMNY)
	rw = UpdateMNYHeaderRW(rw, allMNY)

	rw.WriteHeader(mny.errorCode)
	rw.Write([]byte(rec.Body.String()))

	return rw, req
}

// Begin will obtain the money headers from the request,
// Build a Money object array from those.
// Create a new child money object and add it to the array.
// return the Money array, and the new child Money object
func Begin(req *http.Request, spanName string) ([]*Money, *Money) {
	var allMNY []*Money
	for _, m := range req.Header[ http.CanonicalHeaderKey(HEADER) ] {
		allMNY = append(allMNY, StringToObject(m))
	}

	pMNY := GetCurrentHeader(allMNY)
	cMNY := NewChild(pMNY.ToString(), spanName)
	allMNY = append(allMNY, cMNY)

	return allMNY, cMNY
}

// AddResponseMoney takes the additional money objects from the response then
// adds them into the money object array
func AddResponseMoney(allMNY []*Money, resp *http.Response) []*Money {
	var respMNY []*Money
	for _, m := range resp.Header[ http.CanonicalHeaderKey(HEADER) ] {
		respMNY = append(respMNY, StringToObject(m))
	}

	for _, rm := range respMNY {
		found := false

		for _, am := range allMNY {
			if rm.spanId == am.spanId {
				found = true
			}
		}

		if !found {
			allMNY = append(allMNY, rm)
		}
	}

	return allMNY
}

// End adds the results to the current/child span
// returns a http.ResponseWriter with the updated Money array
func End(rw http.ResponseWriter, allMNY []*Money, cMNY *Money, statusCode int, spanSuccess bool) http.ResponseWriter {
	cMNY.AddResults(statusCode, spanSuccess)

	// update current mny object
	for x := range allMNY {
		if allMNY[x].spanId == cMNY.spanId {
			allMNY[x] = cMNY
			break
		}
	}

	rw = UpdateMNYHeaderRW(rw, allMNY)

	return rw
}

/*
AT BEGINING
get the request money headers
add a new money span (current)

IN MIDDLE
if any request is made sending money headers update money array with response money headers

AT END
addResults to current span
update the responseWriter money header
*/<commit_msg>removing some commented out lines<commit_after>// Copyright 2016 Comcast Cable Communications Management, LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// End Copyright

package money

import (
	"net/http"
	"net/http/httptest"
	"strings"
)

// Money's version of an http.Handler to decorate
type Handler interface {
	ServeHTTP(http.ResponseWriter, *http.Request)
}

// HandlerFunc is a function type that implements the Handler interface
type HandlerFunc func(http.ResponseWriter, *http.Request)

func (f HandlerFunc) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	f(rw, req)
}

// A Decorator wraps a Handler with extra behaviour
type Decorator func(Handler) Handler

// Used when decorating an http.HandlerFunc()
// spanName: will set the span-name
func AddToHandler(spanName string) Decorator {
	return func(f Handler) Handler {
		return HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
			if req.Header.Get(HEADER) == "" {
				f.ServeHTTP(rw, req)
			} else {
				rec := httptest.NewRecorder()
				defer Finish(rw, req, rec)

				rw, req, rec = Start(rw, req, rec, spanName)

				f.ServeHTTP(rec, req)
			}
		})
	}
}

// Decorate decorates a Handler h with all the given Decorators, in order.
func Decorate(h Handler, ds ...(func(Handler) Handler)) Handler {
	decorated := h
	for _, decorate := range ds {
		decorated = decorate(decorated)
	}
	return decorated
}

// Removes the Money header from the http.Header
func DelAllMNYHeaders(header http.Header) http.Header {
	for h := range header {
		if strings.EqualFold(h, HEADER) {
			header.Del(h)
			return header
		}
	}

	return header
}

// Adds Money values to the http.Header
func AddAllMNYHeaders(header http.Header, allMNY []*Money) http.Header {
	for x := range allMNY {
		header.Add(HEADER, allMNY[x].ToString())
	}

	return header
}

// Updates the Money Header values for a *http.Request
func UpdateMNYHeaderReq(req *http.Request, allMNY []*Money) *http.Request {
	header := DelAllMNYHeaders(req.Header)
	header = AddAllMNYHeaders(header, allMNY)
	req.Header = header

	return req
}

// Updates the Money Header values for a http.ResponseWriter
func UpdateMNYHeaderRW(rw http.ResponseWriter, allMNY []*Money) http.ResponseWriter {
	header := DelAllMNYHeaders(rw.Header())
	header = AddAllMNYHeaders(header, allMNY)
	if mnyheader, ok := header[http.CanonicalHeaderKey(HEADER)]; ok {
		for i := 0; i < len(mnyheader); i++ {
			if i == 0 {
				rw.Header().Set(HEADER, mnyheader[i])
			} else {
				rw.Header().Add(HEADER, mnyheader[i])
			}
		}
	}

	return rw
}

// Updates the Money Header values for a httptest.ResponseRecorder
func UpdateMNYHeaderRec(rec *httptest.ResponseRecorder, allMNY []*Money) *httptest.ResponseRecorder {
	header := DelAllMNYHeaders(rec.HeaderMap)
	header = AddAllMNYHeaders(header, allMNY)
	if mnyheader, ok := header[http.CanonicalHeaderKey(HEADER)]; ok {
		for i := 0; i < len(mnyheader); i++ {
			if i == 0 {
				rec.HeaderMap.Set(HEADER, mnyheader[i])
			} else {
				rec.HeaderMap.Add(HEADER, mnyheader[i])
			}
		}
	}

	return rec
}

func copyOfMNY(MNYs []*Money) []Money {
	var mnys []Money
	for i := 0; i < len(MNYs); i++ {
		mnys = append(mnys, *MNYs[i])
	}

	return mnys
}

// Finds the Money value for this instance
func GetCurrentHeader(MNYs []*Money) Money {
	mnys := copyOfMNY(MNYs)

	pID := mnys[0].parentId
	for len(mnys) > 1 {
		found := false
		for i := 0; i < len(mnys); i++ {
			if mnys[i].spanId == pID {
				pID = mnys[i].parentId
				found = true
				mnys = append(mnys[:i], mnys[i+1:]...)

				break
			}
		}

		if !found {
			mnys = append(mnys[:0], mnys[1:]...)
			pID = mnys[0].parentId
		}
	}

	return mnys[0]
}

// Decorator start of the money trace process
func Start(rw http.ResponseWriter, req *http.Request, rec *httptest.ResponseRecorder, spanName string) (http.ResponseWriter, *http.Request, *httptest.ResponseRecorder) {
	var allMNY []*Money
	for _, m := range req.Header[ http.CanonicalHeaderKey(HEADER) ] {
		allMNY = append(allMNY, StringToObject(m))
	}

	pMNY := GetCurrentHeader(allMNY)
	cMNY := NewChild(pMNY.ToString(), spanName)
	allMNY = append(allMNY, cMNY)

	req = UpdateMNYHeaderReq(req, allMNY)
	rw = UpdateMNYHeaderRW(rw, allMNY)
	rec = UpdateMNYHeaderRec(rec, allMNY)

	return rw, req, rec
}

// Decorator finish of the money trace process
func Finish(rw http.ResponseWriter, req *http.Request, rec *httptest.ResponseRecorder) (http.ResponseWriter, *http.Request) {
	for k, v := range rec.HeaderMap {
		for i := range v {
			if i == 0 {
				rw.Header().Set(k, v[i])
			} else {
				rw.Header().Add(k, v[i])
			}
		}
	}

	var allMNY []*Money
	for _, v := range rw.Header()[ http.CanonicalHeaderKey(HEADER) ] {
		allMNY = append(allMNY, StringToObject(v))
	}

	mny := GetCurrentHeader(allMNY)

	if rec.Code >= 400 {
		mny.AddResults(rec.Code, false)
	} else {
		mny.AddResults(rec.Code, true)
	}

	// update current mny object
	for x := range allMNY {
		if allMNY[x].spanId == mny.spanId {
			allMNY[x] = &mny
			break
		}
	}

	req = UpdateMNYHeaderReq(req, allMNY)
	rw = UpdateMNYHeaderRW(rw, allMNY)

	rw.WriteHeader(mny.errorCode)
	rw.Write([]byte(rec.Body.String()))

	return rw, req
}

// Begin will obtain the money headers from the request,
// Build a Money object array from those.
// Create a new child money object and add it to the array.
// return the Money array, and the new child Money object
func Begin(req *http.Request, spanName string) ([]*Money, *Money) {
	var allMNY []*Money
	for _, m := range req.Header[ http.CanonicalHeaderKey(HEADER) ] {
		allMNY = append(allMNY, StringToObject(m))
	}

	pMNY := GetCurrentHeader(allMNY)
	cMNY := NewChild(pMNY.ToString(), spanName)
	allMNY = append(allMNY, cMNY)

	return allMNY, cMNY
}

// AddResponseMoney takes the additional money objects from the response then
// adds them into the money object array
func AddResponseMoney(allMNY []*Money, resp *http.Response) []*Money {
	var respMNY []*Money
	for _, m := range resp.Header[ http.CanonicalHeaderKey(HEADER) ] {
		respMNY = append(respMNY, StringToObject(m))
	}

	for _, rm := range respMNY {
		found := false

		for _, am := range allMNY {
			if rm.spanId == am.spanId {
				found = true
			}
		}

		if !found {
			allMNY = append(allMNY, rm)
		}
	}

	return allMNY
}

// End adds the results to the current/child span
// returns a http.ResponseWriter with the updated Money array
func End(rw http.ResponseWriter, allMNY []*Money, cMNY *Money, statusCode int, spanSuccess bool) http.ResponseWriter {
	cMNY.AddResults(statusCode, spanSuccess)

	// update current mny object
	for x := range allMNY {
		if allMNY[x].spanId == cMNY.spanId {
			allMNY[x] = cMNY
			break
		}
	}

	rw = UpdateMNYHeaderRW(rw, allMNY)

	return rw
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/bitly/go-simplejson"
	"github.com/ninjasphere/go-ninja"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/devices"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-sonos"
	"github.com/ninjasphere/go-sonos/upnp"
)

const (
	defaultInstanceID = 0
	defaultSpeed      = "1"
)

type sonosPlayer struct {
	*sonos.Sonos
	log    *logger.Logger
	player *devices.MediaPlayerDevice
}

func (sp *sonosPlayer) applyPlayPause(playing bool) error {

	sp.log.Infof("applyPlayPause called, playing: %t", playing)

	vol, err := sp.GetVolume(defaultInstanceID, upnp.Channel_Master)

	if err != nil {
		return err
	}

	sp.log.Infof("volue is %d", vol)

	if playing {
		err := sp.Play(defaultInstanceID, defaultSpeed)

		if err != nil {
			return err
		}

		return sp.player.UpdateControlState(channels.MediaControlEventPaused)

	}

	err = sp.Pause(defaultInstanceID)

	if err != nil {
		return err
	}

	return sp.player.UpdateControlState(channels.MediaControlEventPlaying)
}

func (sp *sonosPlayer) applyStop() error {
	sp.log.Infof("applyStop called")

	err := sp.Stop(defaultInstanceID)

	if err != nil {
		return err
	}

	return sp.player.UpdateControlState(channels.MediaControlEventStopped)
}

func (sp *sonosPlayer) applyPlaylistJump(delta int) error {
	sp.log.Infof("applyPlaylistJump called, delta : %d", delta)
	if delta < 0 {
		return sp.Previous(defaultInstanceID)
	}
	return sp.Next(defaultInstanceID)
}

func (sp *sonosPlayer) applyVolume(volume float64) error {
	sp.log.Infof("applyVolume called, volume %f", volume)

	vol := uint16(volume * 100)

	err := sp.SetVolume(defaultInstanceID, upnp.Channel_Master, vol)

	if err != nil {
		return err
	}

	return sp.player.UpdateVolumeState(volume)
}

func (sp *sonosPlayer) applyMuted(muted bool) error {
	err := sp.SetMute(defaultInstanceID, upnp.Channel_Master, muted)

	if err != nil {
		return err
	}

	return sp.player.UpdateMutedState(muted)
}

func (sp *sonosPlayer) bindMethods() {

	sp.player.ApplyPlayPause = sp.applyPlayPause
	sp.player.ApplyStop = sp.applyStop
	sp.player.ApplyPlaylistJump = sp.applyPlaylistJump
	sp.player.ApplyVolume = sp.applyVolume
	sp.player.ApplyMuted = sp.applyMuted

	sp.player.EnableControlChannel([]string{
		"playing",
		"paused",
		"stopped",
		"idle",
	})

	sp.player.EnableVolumeChannel()
}

func (sp *sonosPlayer) updateState() error {

	muted, err := sp.GetMute(defaultInstanceID, upnp.Channel_Master)

	if err != nil {
		return err
	}

	nlog.Infof("UpdateMutedState %t", muted)
	if sp.player.UpdateMutedState(muted); err != nil {
		return err
	}

	vol, err := sp.GetVolume(defaultInstanceID, upnp.Channel_Master)

	if err != nil {
		return err
	}

	var volume float64

	if vol > 0 {
		volume = float64(vol) / 100
	} else {
		volume = float64(0)

	}

	nlog.Infof("UpdateVolumeState %d  %f", vol, volume)
	return sp.player.UpdateVolumeState(volume)
}

func NewPlayer(bus *ninja.DriverBus, sonosUnit *sonos.Sonos) (*sonosPlayer, error) {

	group, _ := sonosUnit.GetZoneGroupAttributes()

	id := group.CurrentZoneGroupID
	name := group.CurrentZoneGroupName

	nlog.Infof("Making media player with ID: %s Label: %s", id, name)

	sigs, _ := simplejson.NewJson([]byte(`{
			"ninja:manufacturer": "Sonos",
			"ninja:productName": "Sonos Player",
			"ninja:productType": "MediaPlayer",
			"ninja:thingType": "MediaPlayer"
	}`))

	deviceBus, err := bus.AnnounceDevice(id, "media-player", name, sigs)
	if err != nil {
		nlog.FatalError(err, "Failed to create media player device bus")
	}

	player, err := devices.CreateMediaPlayerDevice(name, deviceBus)

	if err != nil {
		nlog.FatalError(err, "Failed to create media player device")
	}

	sp := &sonosPlayer{sonosUnit, logger.GetLogger("sonosPlayer"), player}

	sp.bindMethods()

	err = sp.updateState()

	if err != nil {
		nlog.FatalError(err, "Failed to create media player device bus")
	}

	return sp, nil
}
<commit_msg>Removed unnecessary volume check.<commit_after>package main

import (
	"github.com/bitly/go-simplejson"
	"github.com/ninjasphere/go-ninja"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/devices"
	"github.com/ninjasphere/go-ninja/logger"
	"github.com/ninjasphere/go-sonos"
	"github.com/ninjasphere/go-sonos/upnp"
)

const (
	defaultInstanceID = 0
	defaultSpeed      = "1"
)

type sonosPlayer struct {
	*sonos.Sonos
	log    *logger.Logger
	player *devices.MediaPlayerDevice
}

func (sp *sonosPlayer) applyPlayPause(playing bool) error {

	sp.log.Infof("applyPlayPause called, playing: %t", playing)

	if playing {
		err := sp.Play(defaultInstanceID, defaultSpeed)

		if err != nil {
			return err
		}

		return sp.player.UpdateControlState(channels.MediaControlEventPaused)

	}

	err = sp.Pause(defaultInstanceID)

	if err != nil {
		return err
	}

	return sp.player.UpdateControlState(channels.MediaControlEventPlaying)
}

func (sp *sonosPlayer) applyStop() error {
	sp.log.Infof("applyStop called")

	err := sp.Stop(defaultInstanceID)

	if err != nil {
		return err
	}

	return sp.player.UpdateControlState(channels.MediaControlEventStopped)
}

func (sp *sonosPlayer) applyPlaylistJump(delta int) error {
	sp.log.Infof("applyPlaylistJump called, delta : %d", delta)
	if delta < 0 {
		return sp.Previous(defaultInstanceID)
	}
	return sp.Next(defaultInstanceID)
}

func (sp *sonosPlayer) applyVolume(volume float64) error {
	sp.log.Infof("applyVolume called, volume %f", volume)

	vol := uint16(volume * 100)

	err := sp.SetVolume(defaultInstanceID, upnp.Channel_Master, vol)

	if err != nil {
		return err
	}

	return sp.player.UpdateVolumeState(volume)
}

func (sp *sonosPlayer) applyMuted(muted bool) error {
	err := sp.SetMute(defaultInstanceID, upnp.Channel_Master, muted)

	if err != nil {
		return err
	}

	return sp.player.UpdateMutedState(muted)
}

func (sp *sonosPlayer) bindMethods() {

	sp.player.ApplyPlayPause = sp.applyPlayPause
	sp.player.ApplyStop = sp.applyStop
	sp.player.ApplyPlaylistJump = sp.applyPlaylistJump
	sp.player.ApplyVolume = sp.applyVolume
	sp.player.ApplyMuted = sp.applyMuted

	sp.player.EnableControlChannel([]string{
		"playing",
		"paused",
		"stopped",
		"idle",
	})

	sp.player.EnableVolumeChannel()
}

func (sp *sonosPlayer) updateState() error {

	muted, err := sp.GetMute(defaultInstanceID, upnp.Channel_Master)

	if err != nil {
		return err
	}

	nlog.Infof("UpdateMutedState %t", muted)
	if sp.player.UpdateMutedState(muted); err != nil {
		return err
	}

	vol, err := sp.GetVolume(defaultInstanceID, upnp.Channel_Master)

	if err != nil {
		return err
	}

	var volume float64

	if vol > 0 {
		volume = float64(vol) / 100
	} else {
		volume = float64(0)

	}

	nlog.Infof("UpdateVolumeState %d  %f", vol, volume)
	return sp.player.UpdateVolumeState(volume)
}

func NewPlayer(bus *ninja.DriverBus, sonosUnit *sonos.Sonos) (*sonosPlayer, error) {

	group, _ := sonosUnit.GetZoneGroupAttributes()

	id := group.CurrentZoneGroupID
	name := group.CurrentZoneGroupName

	nlog.Infof("Making media player with ID: %s Label: %s", id, name)

	sigs, _ := simplejson.NewJson([]byte(`{
			"ninja:manufacturer": "Sonos",
			"ninja:productName": "Sonos Player",
			"ninja:productType": "MediaPlayer",
			"ninja:thingType": "MediaPlayer"
	}`))

	deviceBus, err := bus.AnnounceDevice(id, "media-player", name, sigs)
	if err != nil {
		nlog.FatalError(err, "Failed to create media player device bus")
	}

	player, err := devices.CreateMediaPlayerDevice(name, deviceBus)

	if err != nil {
		nlog.FatalError(err, "Failed to create media player device")
	}

	sp := &sonosPlayer{sonosUnit, logger.GetLogger("sonosPlayer"), player}

	sp.bindMethods()

	err = sp.updateState()

	if err != nil {
		nlog.FatalError(err, "Failed to create media player device bus")
	}

	return sp, nil
}
<|endoftext|>


<commit_before>// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// The driver should be used via the database/sql package:
//
//  import "database/sql"
//  import _ "github.com/go-sql-driver/mysql"
//
//  db, err := sql.Open("mysql", "user:password@/dbname")
//
// See https://github.com/go-sql-driver/mysql#usage for details
package mysql

import (
	"database/sql"
	"database/sql/driver"
	"net"
)

// This struct is exported to make the driver directly accessible.
// In general the driver is used via the database/sql package.
type MySQLDriver struct{}

// DialFunc is a function which can be used to establish the network connection.
// Custom dial functions must be registered with RegisterDial
type DialFunc func(addr string) (net.Conn, error)

var dials map[string]DialFunc

// RegisterDial registers a custom dial function. It can then be used by the
// network address mynet(addr), where mynet is the registered new network.
// addr is passed as a parameter to the dial function.
func RegisterDial(net string, dial DialFunc) {
	if dials == nil {
		dials = make(map[string]DialFunc)
	}
	dials[net] = dial
}

// Open new Connection.
// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how
// the DSN string is formated
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
	var err error

	// New mysqlConn
	mc := &mysqlConn{
		maxPacketAllowed: maxPacketSize,
		maxWriteSize:     maxPacketSize - 1,
	}
	mc.cfg, err = parseDSN(dsn)
	if err != nil {
		return nil, err
	}

	// Connect to Server
	if dial, ok := dials[mc.cfg.net]; ok {
		mc.netConn, err = dial(mc.cfg.addr)
	} else {
		nd := net.Dialer{Timeout: mc.cfg.timeout}
		mc.netConn, err = nd.Dial(mc.cfg.net, mc.cfg.addr)
	}
	if err != nil {
		return nil, err
	}

	// Enable TCP Keepalives on TCP connections
	if tc, ok := mc.netConn.(*net.TCPConn); ok {
		if err := tc.SetKeepAlive(true); err != nil {
			// Don't send COM_QUIT before handshake.
			mc.netConn.Close()
			mc.netConn = nil
			return nil, err
		}
	}

	mc.buf = newBuffer(mc.netConn)

	// Reading Handshake Initialization Packet
	cipher, err := mc.readInitPacket()
	if err != nil {
		mc.cleanup()
		return nil, err
	}

	// Send Client Authentication Packet
	if err = mc.writeAuthPacket(cipher); err != nil {
		mc.cleanup()
		return nil, err
	}

	// Handle response to auth packet, switch methods if possible
	if err = handleAuthResult(mc, cipher); err != nil {
		// Authentication failed and MySQL has already closed the connection
		// (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
		// Do not send COM_QUIT, just cleanup and return the error.
		mc.cleanup()
		return nil, err
	}

	// Get max allowed packet size
	maxap, err := mc.getSystemVar("max_allowed_packet")
	if err != nil {
		mc.Close()
		return nil, err
	}
	mc.maxPacketAllowed = stringToInt(maxap) - 1
	if mc.maxPacketAllowed < maxPacketSize {
		mc.maxWriteSize = mc.maxPacketAllowed
	}

	// Handle DSN Params
	err = mc.handleParams()
	if err != nil {
		mc.Close()
		return nil, err
	}

	return mc, nil
}

func handleAuthResult(mc *mysqlConn, cipher []byte) (err error) {
	// Read Result Packet
	err = mc.readResultOK()
	if err == nil {
		return // auth successful
	}

	if mc.cfg == nil {
		return // auth failed and retry not possible
	}

	// Retry auth if configured to do so.
	if mc.cfg.allowOldPasswords && err == ErrOldPassword {
		// Retry with old authentication method. Note: there are edge cases
		// where this should work but doesn't; this is currently "wontfix":
		// https://github.com/go-sql-driver/mysql/issues/184
		if err = mc.writeOldAuthPacket(cipher); err != nil {
			return
		}
		err = mc.readResultOK()
	} else if mc.cfg.allowCleartextPasswords && err == ErrCleartextPassword {
		// Retry with clear text password for
		// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
		// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
		if err = mc.writeClearAuthPacket(); err != nil {
			return
		}
		err = mc.readResultOK()
	}
	return
}

func init() {
	sql.Register("mysql", &MySQLDriver{})
}
<commit_msg>Use explicit return in handleAuthResult.<commit_after>// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.

// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// The driver should be used via the database/sql package:
//
//  import "database/sql"
//  import _ "github.com/go-sql-driver/mysql"
//
//  db, err := sql.Open("mysql", "user:password@/dbname")
//
// See https://github.com/go-sql-driver/mysql#usage for details
package mysql

import (
	"database/sql"
	"database/sql/driver"
	"net"
)

// This struct is exported to make the driver directly accessible.
// In general the driver is used via the database/sql package.
type MySQLDriver struct{}

// DialFunc is a function which can be used to establish the network connection.
// Custom dial functions must be registered with RegisterDial
type DialFunc func(addr string) (net.Conn, error)

var dials map[string]DialFunc

// RegisterDial registers a custom dial function. It can then be used by the
// network address mynet(addr), where mynet is the registered new network.
// addr is passed as a parameter to the dial function.
func RegisterDial(net string, dial DialFunc) {
	if dials == nil {
		dials = make(map[string]DialFunc)
	}
	dials[net] = dial
}

// Open new Connection.
// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how
// the DSN string is formated
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
	var err error

	// New mysqlConn
	mc := &mysqlConn{
		maxPacketAllowed: maxPacketSize,
		maxWriteSize:     maxPacketSize - 1,
	}
	mc.cfg, err = parseDSN(dsn)
	if err != nil {
		return nil, err
	}

	// Connect to Server
	if dial, ok := dials[mc.cfg.net]; ok {
		mc.netConn, err = dial(mc.cfg.addr)
	} else {
		nd := net.Dialer{Timeout: mc.cfg.timeout}
		mc.netConn, err = nd.Dial(mc.cfg.net, mc.cfg.addr)
	}
	if err != nil {
		return nil, err
	}

	// Enable TCP Keepalives on TCP connections
	if tc, ok := mc.netConn.(*net.TCPConn); ok {
		if err := tc.SetKeepAlive(true); err != nil {
			// Don't send COM_QUIT before handshake.
			mc.netConn.Close()
			mc.netConn = nil
			return nil, err
		}
	}

	mc.buf = newBuffer(mc.netConn)

	// Reading Handshake Initialization Packet
	cipher, err := mc.readInitPacket()
	if err != nil {
		mc.cleanup()
		return nil, err
	}

	// Send Client Authentication Packet
	if err = mc.writeAuthPacket(cipher); err != nil {
		mc.cleanup()
		return nil, err
	}

	// Handle response to auth packet, switch methods if possible
	if err = handleAuthResult(mc, cipher); err != nil {
		// Authentication failed and MySQL has already closed the connection
		// (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
		// Do not send COM_QUIT, just cleanup and return the error.
		mc.cleanup()
		return nil, err
	}

	// Get max allowed packet size
	maxap, err := mc.getSystemVar("max_allowed_packet")
	if err != nil {
		mc.Close()
		return nil, err
	}
	mc.maxPacketAllowed = stringToInt(maxap) - 1
	if mc.maxPacketAllowed < maxPacketSize {
		mc.maxWriteSize = mc.maxPacketAllowed
	}

	// Handle DSN Params
	err = mc.handleParams()
	if err != nil {
		mc.Close()
		return nil, err
	}

	return mc, nil
}

func handleAuthResult(mc *mysqlConn, cipher []byte) error {
	// Read Result Packet
	err := mc.readResultOK()
	if err == nil {
		return nil // auth successful
	}

	if mc.cfg == nil {
		return err // auth failed and retry not possible
	}

	// Retry auth if configured to do so.
	if mc.cfg.allowOldPasswords && err == ErrOldPassword {
		// Retry with old authentication method. Note: there are edge cases
		// where this should work but doesn't; this is currently "wontfix":
		// https://github.com/go-sql-driver/mysql/issues/184
		if err = mc.writeOldAuthPacket(cipher); err != nil {
			return err
		}
		err = mc.readResultOK()
	} else if mc.cfg.allowCleartextPasswords && err == ErrCleartextPassword {
		// Retry with clear text password for
		// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
		// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
		if err = mc.writeClearAuthPacket(); err != nil {
			return err
		}
		err = mc.readResultOK()
	}
	return err
}

func init() {
	sql.Register("mysql", &MySQLDriver{})
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

// +build cgo

package runtime_test

import (
	"runtime"
	"strings"
	"testing"
)

func TestCgoCrashHandler(t *testing.T) {
	testCrashHandler(t, true)
}

func TestCgoSignalDeadlock(t *testing.T) {
	if testing.Short() && runtime.GOOS == "windows" {
		t.Skip("Skipping in short mode") // takes up to 64 seconds
	}
	got := executeTest(t, cgoSignalDeadlockSource, nil)
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got %q", want, got)
	}
}

func TestCgoTraceback(t *testing.T) {
	got := executeTest(t, cgoTracebackSource, nil)
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got %q", want, got)
	}
}

func TestCgoExternalThreadPanic(t *testing.T) {
	got := executeTest(t, cgoExternalThreadPanicSource, nil, "main.c", cgoExternalThreadPanicC)
	want := "panic: BOOM"
	if !strings.Contains(got, want) {
		t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
	}
}

const cgoSignalDeadlockSource = `
package main

import "C"

import (
	"fmt"
	"runtime"
	"time"
)

func main() {
	runtime.GOMAXPROCS(100)
	ping := make(chan bool)
	go func() {
		for i := 0; ; i++ {
			runtime.Gosched()
			select {
			case done := <-ping:
				if done {
					ping <- true
					return
				}
				ping <- true
			default:
			}
			func() {
				defer func() {
					recover()
				}()
				var s *string
				*s = ""
			}()
		}
	}()
	time.Sleep(time.Millisecond)
	for i := 0; i < 64; i++ {
		go func() {
			runtime.LockOSThread()
			select {}
		}()
		go func() {
			runtime.LockOSThread()
			select {}
		}()
		time.Sleep(time.Millisecond)
		ping <- false
		select {
		case <-ping:
		case <-time.After(time.Second):
			fmt.Printf("HANG\n")
			return
		}
	}
	ping <- true
	select {
	case <-ping:
	case <-time.After(time.Second):
		fmt.Printf("HANG\n")
		return
	}
	fmt.Printf("OK\n")
}
`

const cgoTracebackSource = `
package main

/* void foo(void) {} */
import "C"

import (
	"fmt"
	"runtime"
)

func main() {
	C.foo()
	buf := make([]byte, 1)
	runtime.Stack(buf, true)
	fmt.Printf("OK\n")
}
`

const cgoExternalThreadPanicSource = `
package main

// void start(void);
import "C"

func main() {
	C.start()
	select {}
}

//export gopanic
func gopanic() {
	panic("BOOM")
}
`

const cgoExternalThreadPanicC = `
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

void gopanic(void);

static void*
die(void* x)
{
	gopanic();
	return 0;
}

void
start(void)
{
	pthread_t t;
	if(pthread_create(&t, 0, die, 0) != 0)
		printf("pthread_create failed\n");
}
`
<commit_msg>runtime: fix windows build<commit_after>// Copyright 2012 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.

// +build cgo

package runtime_test

import (
	"runtime"
	"strings"
	"testing"
)

func TestCgoCrashHandler(t *testing.T) {
	testCrashHandler(t, true)
}

func TestCgoSignalDeadlock(t *testing.T) {
	if testing.Short() && runtime.GOOS == "windows" {
		t.Skip("Skipping in short mode") // takes up to 64 seconds
	}
	got := executeTest(t, cgoSignalDeadlockSource, nil)
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got %q", want, got)
	}
}

func TestCgoTraceback(t *testing.T) {
	got := executeTest(t, cgoTracebackSource, nil)
	want := "OK\n"
	if got != want {
		t.Fatalf("expected %q, but got %q", want, got)
	}
}

func TestCgoExternalThreadPanic(t *testing.T) {
	if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
		t.Skipf("no pthreads on %s", runtime.GOOS)
	}
	got := executeTest(t, cgoExternalThreadPanicSource, nil, "main.c", cgoExternalThreadPanicC)
	want := "panic: BOOM"
	if !strings.Contains(got, want) {
		t.Fatalf("want failure containing %q. output:\n%s\n", want, got)
	}
}

const cgoSignalDeadlockSource = `
package main

import "C"

import (
	"fmt"
	"runtime"
	"time"
)

func main() {
	runtime.GOMAXPROCS(100)
	ping := make(chan bool)
	go func() {
		for i := 0; ; i++ {
			runtime.Gosched()
			select {
			case done := <-ping:
				if done {
					ping <- true
					return
				}
				ping <- true
			default:
			}
			func() {
				defer func() {
					recover()
				}()
				var s *string
				*s = ""
			}()
		}
	}()
	time.Sleep(time.Millisecond)
	for i := 0; i < 64; i++ {
		go func() {
			runtime.LockOSThread()
			select {}
		}()
		go func() {
			runtime.LockOSThread()
			select {}
		}()
		time.Sleep(time.Millisecond)
		ping <- false
		select {
		case <-ping:
		case <-time.After(time.Second):
			fmt.Printf("HANG\n")
			return
		}
	}
	ping <- true
	select {
	case <-ping:
	case <-time.After(time.Second):
		fmt.Printf("HANG\n")
		return
	}
	fmt.Printf("OK\n")
}
`

const cgoTracebackSource = `
package main

/* void foo(void) {} */
import "C"

import (
	"fmt"
	"runtime"
)

func main() {
	C.foo()
	buf := make([]byte, 1)
	runtime.Stack(buf, true)
	fmt.Printf("OK\n")
}
`

const cgoExternalThreadPanicSource = `
package main

// void start(void);
import "C"

func main() {
	C.start()
	select {}
}

//export gopanic
func gopanic() {
	panic("BOOM")
}
`

const cgoExternalThreadPanicC = `
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

void gopanic(void);

static void*
die(void* x)
{
	gopanic();
	return 0;
}

void
start(void)
{
	pthread_t t;
	if(pthread_create(&t, 0, die, 0) != 0)
		printf("pthread_create failed\n");
}
`
<|endoftext|>


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

//+build !nacl,!windows,!js

package runtime_test

import (
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
	"testing"
	"time"
)

// Issue #27250. Spurious wakeups to pthread_cond_timedwait_relative_np
// shouldn't cause semasleep to retry with the same timeout which would
// cause indefinite spinning.
func TestSpuriousWakeupsNeverHangSemasleep(t *testing.T) {
	testenv.MustHaveGoBuild(t)
	tempDir, err := ioutil.TempDir("", "issue-27250")
	if err != nil {
		t.Fatalf("Failed to create the temp directory: %v", err)
	}
	defer os.RemoveAll(tempDir)

	repro := `
    package main

    import "time"

    func main() {
        <-time.After(1 * time.Second)
    }
    `
	mainPath := filepath.Join(tempDir, "main.go")
	if err := ioutil.WriteFile(mainPath, []byte(repro), 0644); err != nil {
		t.Fatalf("Failed to create temp file for repro.go: %v", err)
	}
	binaryPath := filepath.Join(tempDir, "binary")

	// Build the binary so that we can send the signal to its PID.
	out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", binaryPath, mainPath).CombinedOutput()
	if err != nil {
		t.Fatalf("Failed to compile the binary: err: %v\nOutput: %s\n", err, out)
	}
	if err := os.Chmod(binaryPath, 0755); err != nil {
		t.Fatalf("Failed to chmod binary: %v", err)
	}

	// Now run the binary.
	cmd := exec.Command(binaryPath)
	if err := cmd.Start(); err != nil {
		t.Fatalf("Failed to start command: %v", err)
	}
	doneCh := make(chan error, 1)
	go func() {
		doneCh <- cmd.Wait()
	}()

	// With the repro running, we can continuously send to it
	// a non-terminal signal such as SIGIO, to spuriously
	// wakeup pthread_cond_timedwait_relative_np.
	unfixedTimer := time.NewTimer(2 * time.Second)
	for {
		select {
		case <-time.After(200 * time.Millisecond):
			// Send the pesky signal that toggles spinning
			// indefinitely if #27520 is not fixed.
			cmd.Process.Signal(syscall.SIGIO)

		case <-unfixedTimer.C:
			t.Error("Program failed to return on time and has to be killed, issue #27520 still exists")
			cmd.Process.Signal(syscall.SIGKILL)
			return

		case err := <-doneCh:
			if err != nil {
				t.Fatalf("The program returned but unfortunately with an error: %v", err)
			}
			return
		}
	}
}
<commit_msg>runtime: don't build semasleep_test on Plan 9<commit_after>// Copyright 2018 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.

//+build !nacl,!plan9,!windows,!js

package runtime_test

import (
	"internal/testenv"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"syscall"
	"testing"
	"time"
)

// Issue #27250. Spurious wakeups to pthread_cond_timedwait_relative_np
// shouldn't cause semasleep to retry with the same timeout which would
// cause indefinite spinning.
func TestSpuriousWakeupsNeverHangSemasleep(t *testing.T) {
	testenv.MustHaveGoBuild(t)
	tempDir, err := ioutil.TempDir("", "issue-27250")
	if err != nil {
		t.Fatalf("Failed to create the temp directory: %v", err)
	}
	defer os.RemoveAll(tempDir)

	repro := `
    package main

    import "time"

    func main() {
        <-time.After(1 * time.Second)
    }
    `
	mainPath := filepath.Join(tempDir, "main.go")
	if err := ioutil.WriteFile(mainPath, []byte(repro), 0644); err != nil {
		t.Fatalf("Failed to create temp file for repro.go: %v", err)
	}
	binaryPath := filepath.Join(tempDir, "binary")

	// Build the binary so that we can send the signal to its PID.
	out, err := exec.Command(testenv.GoToolPath(t), "build", "-o", binaryPath, mainPath).CombinedOutput()
	if err != nil {
		t.Fatalf("Failed to compile the binary: err: %v\nOutput: %s\n", err, out)
	}
	if err := os.Chmod(binaryPath, 0755); err != nil {
		t.Fatalf("Failed to chmod binary: %v", err)
	}

	// Now run the binary.
	cmd := exec.Command(binaryPath)
	if err := cmd.Start(); err != nil {
		t.Fatalf("Failed to start command: %v", err)
	}
	doneCh := make(chan error, 1)
	go func() {
		doneCh <- cmd.Wait()
	}()

	// With the repro running, we can continuously send to it
	// a non-terminal signal such as SIGIO, to spuriously
	// wakeup pthread_cond_timedwait_relative_np.
	unfixedTimer := time.NewTimer(2 * time.Second)
	for {
		select {
		case <-time.After(200 * time.Millisecond):
			// Send the pesky signal that toggles spinning
			// indefinitely if #27520 is not fixed.
			cmd.Process.Signal(syscall.SIGIO)

		case <-unfixedTimer.C:
			t.Error("Program failed to return on time and has to be killed, issue #27520 still exists")
			cmd.Process.Signal(syscall.SIGKILL)
			return

		case err := <-doneCh:
			if err != nil {
				t.Fatalf("The program returned but unfortunately with an error: %v", err)
			}
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/jessevdk/go-flags"
	"github.com/oschwald/geoip2-golang"
	"html/template"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
)

var agentExp = regexp.MustCompile("^(?i)(curl|wget|fetch\\slibfetch)\\/.*$")

type Client struct {
	IP     net.IP
	JSON   string
	Header http.Header
	Cmd
}

type Cmd struct {
	Name string
	Args string
}

type Ifconfig struct {
	DB *geoip2.Reader
}

func (c *Cmd) String() string {
	return c.Name + " " + c.Args
}

func isCLI(userAgent string) bool {
	return agentExp.MatchString(userAgent)
}

func parseRealIP(req *http.Request) net.IP {
	var host string
	realIP := req.Header.Get("X-Real-IP")
	if realIP != "" {
		host = realIP
	} else {
		host, _, _ = net.SplitHostPort(req.RemoteAddr)
	}
	return net.ParseIP(host)
}

func pathToKey(path string) string {
	trimmed := strings.TrimSuffix(strings.TrimPrefix(path, "/"), ".json")
	return strings.ToLower(trimmed)
}

func isJSON(req *http.Request) bool {
	return strings.HasSuffix(req.URL.Path, ".json") ||
		strings.Contains(req.Header.Get("Accept"), "application/json")
}

func (i *Ifconfig) LookupCountry(ip net.IP) (string, error) {
	if i.DB == nil {
		return "", nil
	}
	record, err := i.DB.Country(ip)
	if err != nil {
		return "", err
	}
	country, exists := record.Country.Names["en"]
	if !exists {
		country, exists = record.RegisteredCountry.Names["en"]
		if !exists {
			return "", fmt.Errorf(
				"could not determine country for IP: %s", ip)
		}
	}
	return country, nil
}

func (i *Ifconfig) JSON(req *http.Request, key string) (string, error) {
	var header http.Header
	if key == "all" {
		header = req.Header
	} else {
		header = http.Header{
			key: []string{req.Header.Get(key)},
		}
	}
	b, err := json.MarshalIndent(header, "", "  ")
	if err != nil {
		return "", err
	}
	return string(b), nil
}

func (i *Ifconfig) Plain(req *http.Request, key string, ip net.IP) string {
	if key == "" || key == "ip" {
		return fmt.Sprintf("%s\n", ip)
	}
	return fmt.Sprintf("%s\n", req.Header.Get(key))
}

func lookupCmd(values url.Values) Cmd {
	cmd, exists := values["cmd"]
	if !exists || len(cmd) == 0 {
		return Cmd{Name: "curl"}
	}
	switch cmd[0] {
	case "curl":
		return Cmd{Name: "curl"}
	case "fetch":
		return Cmd{Name: "fetch", Args: "-qo -"}
	case "wget":
		return Cmd{Name: "wget", Args: "-qO -"}
	}
	return Cmd{Name: "curl"}
}

func (i *Ifconfig) handler(w http.ResponseWriter, req *http.Request) {
	if req.Method != "GET" {
		http.Error(w, "Invalid request method", 405)
		return
	}
	ip := parseRealIP(req)
	key := pathToKey(req.URL.Path)
	cmd := lookupCmd(req.URL.Query())
	country, err := i.LookupCountry(ip)
	if err != nil {
		log.Print(err)
	}
	req.Header["X-Ip-Country"] = []string{country}
	if isJSON(req) {
		out, err := i.JSON(req, key)
		if err != nil {
			log.Print(err)
			http.Error(w, "Failed to marshal JSON", 500)
			return
		}
		io.WriteString(w, out)
	} else if isCLI(req.UserAgent()) {
		io.WriteString(w, i.Plain(req, key, ip))
	} else {
		funcMap := template.FuncMap{
			"ToLower": strings.ToLower,
		}
		t, _ := template.
			New("index.html").
			Funcs(funcMap).
			ParseFiles("index.html")
		b, err := json.MarshalIndent(req.Header, "", "  ")
		if err != nil {
			log.Print(err)
			http.Error(w, "Failed to marshal JSON", 500)
			return
		}
		client := &Client{
			IP:     ip,
			JSON:   string(b),
			Header: req.Header,
			Cmd:    cmd,
		}
		t.Execute(w, client)
	}
}

func Create(path string) (*Ifconfig, error) {
	if path == "" {
		log.Print("Path to GeoIP database not given. Country lookup will be disabled")
		return &Ifconfig{}, nil
	}
	db, err := geoip2.Open(path)
	if err != nil {
		return nil, err
	}
	return &Ifconfig{DB: db}, nil
}

func main() {
	var opts struct {
		DBPath string `short:"f" long:"file" description:"Path to GeoIP database" value-name:"FILE" default:""`
		Listen string `short:"l" long:"listen" description:"Listening address" value-name:"ADDR" default:":8080"`
	}
	_, err := flags.ParseArgs(&opts, os.Args)
	if err != nil {
		os.Exit(1)
	}
	i, err := Create(opts.DBPath)
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/", i.handler)
	log.Printf("Listening on %s", opts.Listen)
	if err := http.ListenAndServe(opts.Listen, nil); err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<commit_msg>Logs country if possible<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/jessevdk/go-flags"
	"github.com/oschwald/geoip2-golang"
	"html/template"
	"io"
	"log"
	"net"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strings"
)

var agentExp = regexp.MustCompile("^(?i)(curl|wget|fetch\\slibfetch)\\/.*$")

type Client struct {
	IP     net.IP
	JSON   string
	Header http.Header
	Cmd
}

type Cmd struct {
	Name string
	Args string
}

type Ifconfig struct {
	DB *geoip2.Reader
}

func (c *Cmd) String() string {
	return c.Name + " " + c.Args
}

func isCLI(userAgent string) bool {
	return agentExp.MatchString(userAgent)
}

func parseRealIP(req *http.Request) net.IP {
	var host string
	realIP := req.Header.Get("X-Real-IP")
	if realIP != "" {
		host = realIP
	} else {
		host, _, _ = net.SplitHostPort(req.RemoteAddr)
	}
	return net.ParseIP(host)
}

func pathToKey(path string) string {
	trimmed := strings.TrimSuffix(strings.TrimPrefix(path, "/"), ".json")
	return strings.ToLower(trimmed)
}

func isJSON(req *http.Request) bool {
	return strings.HasSuffix(req.URL.Path, ".json") ||
		strings.Contains(req.Header.Get("Accept"), "application/json")
}

func (i *Ifconfig) LookupCountry(ip net.IP) (string, error) {
	if i.DB == nil {
		return "", nil
	}
	record, err := i.DB.Country(ip)
	if err != nil {
		return "", err
	}
	country, exists := record.Country.Names["en"]
	if !exists {
		country, exists = record.RegisteredCountry.Names["en"]
		if !exists {
			return "", fmt.Errorf(
				"could not determine country for IP: %s", ip)
		}
	} else {
		log.Printf("Access from %s (%s)\n",country,ip)
	}
	return country, nil
}

func (i *Ifconfig) JSON(req *http.Request, key string) (string, error) {
	var header http.Header
	if key == "all" {
		header = req.Header
	} else {
		header = http.Header{
			key: []string{req.Header.Get(key)},
		}
	}
	b, err := json.MarshalIndent(header, "", "  ")
	if err != nil {
		return "", err
	}
	return string(b), nil
}

func (i *Ifconfig) Plain(req *http.Request, key string, ip net.IP) string {
	if key == "" || key == "ip" {
		return fmt.Sprintf("%s\n", ip)
	}
	return fmt.Sprintf("%s\n", req.Header.Get(key))
}

func lookupCmd(values url.Values) Cmd {
	cmd, exists := values["cmd"]
	if !exists || len(cmd) == 0 {
		return Cmd{Name: "curl"}
	}
	switch cmd[0] {
	case "curl":
		return Cmd{Name: "curl"}
	case "fetch":
		return Cmd{Name: "fetch", Args: "-qo -"}
	case "wget":
		return Cmd{Name: "wget", Args: "-qO -"}
	}
	return Cmd{Name: "curl"}
}

func (i *Ifconfig) handler(w http.ResponseWriter, req *http.Request) {
	if req.Method != "GET" {
		http.Error(w, "Invalid request method", 405)
		return
	}
	ip := parseRealIP(req)
	key := pathToKey(req.URL.Path)
	cmd := lookupCmd(req.URL.Query())
	country, err := i.LookupCountry(ip)
	if err != nil {
		log.Print(err)
	}
	req.Header["X-Ip-Country"] = []string{country}
	if isJSON(req) {
		out, err := i.JSON(req, key)
		if err != nil {
			log.Print(err)
			http.Error(w, "Failed to marshal JSON", 500)
			return
		}
		io.WriteString(w, out)
	} else if isCLI(req.UserAgent()) {
		io.WriteString(w, i.Plain(req, key, ip))
	} else {
		funcMap := template.FuncMap{
			"ToLower": strings.ToLower,
		}
		t, _ := template.
			New("index.html").
			Funcs(funcMap).
			ParseFiles("index.html")
		b, err := json.MarshalIndent(req.Header, "", "  ")
		if err != nil {
			log.Print(err)
			http.Error(w, "Failed to marshal JSON", 500)
			return
		}
		client := &Client{
			IP:     ip,
			JSON:   string(b),
			Header: req.Header,
			Cmd:    cmd,
		}
		t.Execute(w, client)
	}
}

func Create(path string) (*Ifconfig, error) {
	if path == "" {
		log.Print("Path to GeoIP database not given. Country lookup will be disabled")
		return &Ifconfig{}, nil
	}
	db, err := geoip2.Open(path)
	if err != nil {
		return nil, err
	}
	return &Ifconfig{DB: db}, nil
}

func main() {
	var opts struct {
		DBPath string `short:"f" long:"file" description:"Path to GeoIP database" value-name:"FILE" default:""`
		Listen string `short:"l" long:"listen" description:"Listening address" value-name:"ADDR" default:":8080"`
	}
	_, err := flags.ParseArgs(&opts, os.Args)
	if err != nil {
		os.Exit(1)
	}
	i, err := Create(opts.DBPath)
	if err != nil {
		log.Fatal(err)
	}

	http.HandleFunc("/", i.handler)
	log.Printf("Listening on %s", opts.Listen)
	if err := http.ListenAndServe(opts.Listen, nil); err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"github.com/cheggaaa/pb"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"log"
	"math"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"strings"
)

var subdivisions *int
var tolerance *int

var scratchDir string

type pictable [][][]uint64

type imageInfo struct {
	Data     pictable
	Bounds   image.Rectangle
	Filesize int64
}

func MkPictable(dx int, dy int) pictable {
	pic := make([][][]uint64, dx) /* type declaration */
	for i := range pic {
		pic[i] = make([][]uint64, dy) /* again the type? */
		for j := range pic[i] {
			pic[i][j] = []uint64{0, 0, 0}
		}
	}
	return pic
}

func absdiff(a uint64, b uint64) uint64 {
	return uint64(math.Abs(float64(a) - float64(b)))
}

func init() {
	subdivisions = flag.Int("subdivisions", 10, "Slices per axis")
	tolerance = flag.Int("tolerance", 100, "Color delta tolerance, higher = more tolerant")
	flag.Parse()

	if flag.NArg() < 1 {
		fmt.Println("usage: imgdedup [options] [<directories>/files]")
		flag.PrintDefaults()
		os.Exit(1)
	}

	image.RegisterFormat("png", "png", png.Decode, png.DecodeConfig)
	image.RegisterFormat("jpeg", "jpeg", jpeg.Decode, jpeg.DecodeConfig)
	image.RegisterFormat("gif", "gif", gif.Decode, gif.DecodeConfig)
}

func init() {
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	scratchDir = path.Join(usr.HomeDir, ".imgdedup")

	if _, err := os.Stat(scratchDir); err != nil {
		if os.IsNotExist(err) {
			os.Mkdir(scratchDir, 0700)
		} else {
			log.Fatal(err)
		}
	}
}

func scanImg(file *os.File) (*imageInfo, error) {
	m, _, err := image.Decode(file)
	if err != nil {
		return nil, err
	}
	bounds := m.Bounds()

	avgdata := MkPictable(*subdivisions, *subdivisions)

	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			rX := int64(math.Floor((float64(x) / float64(bounds.Max.X)) * float64(*subdivisions)))
			rY := int64(math.Floor((float64(y) / float64(bounds.Max.Y)) * float64(*subdivisions)))

			r, g, b, _ := m.At(x, y).RGBA()
			avgdata[rX][rY][0] += uint64((float32(r) / 65535) * 255)
			avgdata[rX][rY][1] += uint64((float32(g) / 65535) * 255)
			avgdata[rX][rY][2] += uint64((float32(b) / 65535) * 255)
		}
	}

	divisor := uint64((bounds.Max.X / *subdivisions) * (bounds.Max.Y / *subdivisions))
	if divisor == 0 {
		return nil, fmt.Errorf("Image dimensions %d x %d invalid", bounds.Max.X, bounds.Max.Y)
	}

	for rX := 0; rX < *subdivisions; rX++ {
		for rY := 0; rY < *subdivisions; rY++ {
			avgdata[rX][rY][0] = avgdata[rX][rY][0] / divisor
			avgdata[rX][rY][1] = avgdata[rX][rY][1] / divisor
			avgdata[rX][rY][2] = avgdata[rX][rY][2] / divisor
		}
	}

	fi, err := file.Stat()
	if err != nil {
		log.Fatal(err)
	}

	return &imageInfo{Data: avgdata, Bounds: bounds, Filesize: fi.Size()}, nil
}

func main() {

	imgdata := make(map[string]*imageInfo)

	fileList := getFiles(flag.Args())

	bar := pb.StartNew(len(fileList))
	bar.Output = os.Stderr

	for _, imgpath := range fileList {
		bar.Increment()

		file, err := os.Open(imgpath)
		if err != nil {
			log.Fatal(err)
		}

		fExt := strings.ToLower(filepath.Ext(imgpath))
		if fExt == ".png" || fExt == ".jpg" || fExt == ".jpeg" || fExt == ".gif" {

			fi, err := file.Stat()
			if err != nil {
				log.Fatal(err)
			}

			h := md5.New()

			cacheUnit := imgpath + "|" + string(*subdivisions) + "|" + string(fi.Size()) + string(fi.ModTime().Unix())

			io.WriteString(h, cacheUnit)
			cachename := path.Join(scratchDir, fmt.Sprintf("%x", h.Sum(nil))+".tmp")

			var imginfo *imageInfo

			imginfo, err = loadCache(cachename)

			if err != nil {

				imginfo, err = scanImg(file)
				if err != nil {
					log.Print(imgpath, " - ", err)
					continue
				}

				storeCache(cachename, imginfo)
			}

			imgdata[imgpath] = imginfo

			file.Close()

		} else {
			file.Close()
		}
	}

	bar.Finish()

	fileLength := len(fileList)

	for i := 0; i < fileLength-1; i++ {
		for j := i + 1; j < fileLength-1; j++ {

			filename1 := fileList[i]
			filename2 := fileList[j]

			imgdata1, ok1 := imgdata[filename1]
			imgdata2, ok2 := imgdata[filename2]

			if ok1 && ok2 {

				avgdata1 := imgdata1.Data
				avgdata2 := imgdata2.Data

				if filename1 == filename2 {
					continue
				}

				var xdiff uint64 = 0

				for rX := 0; rX < *subdivisions; rX++ {
					for rY := 0; rY < *subdivisions; rY++ {
						aa := avgdata1[rX][rY]
						bb := avgdata2[rX][rY]

						xdiff += absdiff(absdiff(absdiff(aa[0], bb[0]), absdiff(aa[1], bb[1])), absdiff(aa[2], bb[2]))
					}
				}

				if xdiff < uint64(*tolerance) {
					fmt.Println(filename1, filename2)
					fmt.Println(xdiff)
				}

			}

		}
	}

}

func getFiles(paths []string) []string {
	var fileList []string

	for _, imgpath := range paths {

		file, err := os.Open(imgpath)
		if err != nil {
			log.Fatal(err)
		}

		fi, err := file.Stat()
		if err != nil {
			log.Fatal(err)
		}

		switch mode := fi.Mode(); {
		case mode.IsDir():
			// fmt.Println("directory")
			filepath.Walk(imgpath, func(path string, f os.FileInfo, err error) error {

				submode := f.Mode()
				if submode.IsRegular() {
					fpath, _ := filepath.Abs(path)
					fileList = append(fileList, fpath)
				}

				return nil
			})
		case mode.IsRegular():
			// fmt.Println("file")
			fpath, _ := filepath.Abs(imgpath)
			fileList = append(fileList, fpath)
		}

		file.Close()

	}

	return fileList
}
<commit_msg>Very slight cleanup<commit_after>package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"github.com/cheggaaa/pb"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"io"
	"log"
	"math"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"strings"
)

var scratchDir string

type pictable [][][]uint64

type imageInfo struct {
	Data     pictable
	Bounds   image.Rectangle
	Filesize int64
}

func MkPictable(dx int, dy int) pictable {
	pic := make([][][]uint64, dx) /* type declaration */
	for i := range pic {
		pic[i] = make([][]uint64, dy) /* again the type? */
		for j := range pic[i] {
			pic[i][j] = []uint64{0, 0, 0}
		}
	}
	return pic
}

func absdiff(a uint64, b uint64) uint64 {
	return uint64(math.Abs(float64(a) - float64(b)))
}

var (
	subdivisions *int = flag.Int("subdivisions", 10, "Slices per axis")
	tolerance    *int = flag.Int("tolerance", 100, "Color delta tolerance, higher = more tolerant")
)

func init() {

	flag.Parse()

	if flag.NArg() < 1 {
		fmt.Println("usage: imgdedup [options] [<directories>/files]")
		flag.PrintDefaults()
		os.Exit(1)
	}

	image.RegisterFormat("png", "png", png.Decode, png.DecodeConfig)
	image.RegisterFormat("jpeg", "jpeg", jpeg.Decode, jpeg.DecodeConfig)
	image.RegisterFormat("gif", "gif", gif.Decode, gif.DecodeConfig)
}

func init() {
	usr, err := user.Current()
	if err != nil {
		log.Fatal(err)
	}
	scratchDir = path.Join(usr.HomeDir, ".imgdedup")

	if _, err := os.Stat(scratchDir); err != nil {
		if os.IsNotExist(err) {
			os.Mkdir(scratchDir, 0700)
		} else {
			log.Fatal(err)
		}
	}
}

func scanImg(file *os.File) (*imageInfo, error) {
	m, _, err := image.Decode(file)
	if err != nil {
		return nil, err
	}
	bounds := m.Bounds()

	avgdata := MkPictable(*subdivisions, *subdivisions)

	for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
		for x := bounds.Min.X; x < bounds.Max.X; x++ {
			rX := int64(math.Floor((float64(x) / float64(bounds.Max.X)) * float64(*subdivisions)))
			rY := int64(math.Floor((float64(y) / float64(bounds.Max.Y)) * float64(*subdivisions)))

			r, g, b, _ := m.At(x, y).RGBA()
			avgdata[rX][rY][0] += uint64((float32(r) / 65535) * 255)
			avgdata[rX][rY][1] += uint64((float32(g) / 65535) * 255)
			avgdata[rX][rY][2] += uint64((float32(b) / 65535) * 255)
		}
	}

	divisor := uint64((bounds.Max.X / *subdivisions) * (bounds.Max.Y / *subdivisions))
	if divisor == 0 {
		return nil, fmt.Errorf("Image dimensions %d x %d invalid", bounds.Max.X, bounds.Max.Y)
	}

	for rX := 0; rX < *subdivisions; rX++ {
		for rY := 0; rY < *subdivisions; rY++ {
			avgdata[rX][rY][0] = avgdata[rX][rY][0] / divisor
			avgdata[rX][rY][1] = avgdata[rX][rY][1] / divisor
			avgdata[rX][rY][2] = avgdata[rX][rY][2] / divisor
		}
	}

	fi, err := file.Stat()
	if err != nil {
		log.Fatal(err)
	}

	return &imageInfo{Data: avgdata, Bounds: bounds, Filesize: fi.Size()}, nil
}

func main() {

	imgdata := make(map[string]*imageInfo)

	fileList := getFiles(flag.Args())

	bar := pb.StartNew(len(fileList))
	bar.Output = os.Stderr

	for _, imgpath := range fileList {
		bar.Increment()

		file, err := os.Open(imgpath)
		if err != nil {
			log.Fatal(err)
		}

		fExt := strings.ToLower(filepath.Ext(imgpath))
		if fExt == ".png" || fExt == ".jpg" || fExt == ".jpeg" || fExt == ".gif" {

			fi, err := file.Stat()
			if err != nil {
				log.Fatal(err)
			}

			h := md5.New()

			cacheUnit := imgpath + "|" + string(*subdivisions) + "|" + string(fi.Size()) + string(fi.ModTime().Unix())

			io.WriteString(h, cacheUnit)
			cachename := path.Join(scratchDir, fmt.Sprintf("%x", h.Sum(nil))+".tmp")

			var imginfo *imageInfo

			imginfo, err = loadCache(cachename)

			if err != nil {

				imginfo, err = scanImg(file)
				if err != nil {
					log.Print(imgpath, " - ", err)
					continue
				}

				storeCache(cachename, imginfo)
			}

			imgdata[imgpath] = imginfo

			file.Close()

		} else {
			file.Close()
		}
	}

	bar.Finish()

	fileLength := len(fileList)

	for i := 0; i < fileLength-1; i++ {
		for j := i + 1; j < fileLength-1; j++ {

			filename1 := fileList[i]
			filename2 := fileList[j]

			imgdata1, ok1 := imgdata[filename1]
			imgdata2, ok2 := imgdata[filename2]

			if ok1 && ok2 {

				avgdata1 := imgdata1.Data
				avgdata2 := imgdata2.Data

				if filename1 == filename2 {
					continue
				}

				var xdiff uint64 = 0

				for rX := 0; rX < *subdivisions; rX++ {
					for rY := 0; rY < *subdivisions; rY++ {
						aa := avgdata1[rX][rY]
						bb := avgdata2[rX][rY]

						xdiff += absdiff(absdiff(absdiff(aa[0], bb[0]), absdiff(aa[1], bb[1])), absdiff(aa[2], bb[2]))
					}
				}

				if xdiff < uint64(*tolerance) {
					fmt.Println(filename1, filename2)
					fmt.Println(xdiff)
				}

			}

		}
	}

}

func getFiles(paths []string) []string {
	var fileList []string

	for _, imgpath := range paths {

		file, err := os.Open(imgpath)
		if err != nil {
			log.Fatal(err)
		}

		fi, err := file.Stat()
		if err != nil {
			log.Fatal(err)
		}

		switch mode := fi.Mode(); {
		case mode.IsDir():
			// fmt.Println("directory")
			filepath.Walk(imgpath, func(path string, f os.FileInfo, err error) error {

				submode := f.Mode()
				if submode.IsRegular() {
					fpath, _ := filepath.Abs(path)
					fileList = append(fileList, fpath)
				}

				return nil
			})
		case mode.IsRegular():
			// fmt.Println("file")
			fpath, _ := filepath.Abs(imgpath)
			fileList = append(fileList, fpath)
		}

		file.Close()

	}

	return fileList
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/olekukonko/tablewriter"
	"github.com/urfave/cli"
	"github.com/valyala/gorpc"
	"os"
	"strings"
	"sync"
	"time"
	"fmt"
)

var (
	// LutraVersion should match the one in lutractl/main.go
	LutraVersion = "0.1"

	// StartupServices Should only be used for the FIRST startup
	// StartupServices is the in-memory map list of processes started on a full-start boot
	StartupServices = make(map[ServiceName][]ServiceName)
	StartupTargets = make([]ServiceName, 0)

	// LoadedServices is used for any other actions, start, stop, etc.
	LoadedServices   = make(map[ServiceName]*Service)
	LoadedServicesMu = sync.RWMutex{}

	// NetFs design the list of known network file systems to be avoided mounted at boot
	NetFs = []string{"nfs", "nfs4", "smbfs", "cifs", "codafs", "ncpfs", "shfs", "fuse", "fuseblk", "glusterfs", "davfs", "fuse.glusterfs"}
	// VirtFs design the list of known virtual file systems to avoid unmounting at shutdown
	VirtFs = []string{"proc", "sysfs", "tmpfs", "devtmpfs", "devpts"}

	// GoRPCServer for client
	GoRPCServer = &gorpc.Server{}
	// GoRPCStarted or not
	GoRPCStarted = false

	//ShuttingDown is used to break various check loops like in getty
	ShuttingDown bool

	lsFnameSerialized = "/run/lutrainit.reexec.ls.bin"
	glFnameSerialized = "/run/lutrainit.reexec.gl.bin"

	// Theses two last should only filled by LDFLAGS, see Makefile

	// LutraBuildTime is the time of the build
	LutraBuildTime string
	// LutraBuildGitHash is the git sha1 of the commit based on
	LutraBuildGitHash string
)

func main() {
	app := cli.NewApp()
	app.Name = "lutrainit"
	app.Usage = "lutra init daemon"
	app.Version = LutraVersion
	app.Commands = []cli.Command{
		CmdServicesTree,
		CmdServicesList,
		CmdSysinit,
	}
	app.Flags = append(app.Flags, []cli.Flag{}...)

	// No argument will start the system init processing
	if len(os.Args) <= 1 {
		os.Args = []string{"lutrainit", "sysinit"}
	}

	app.Run(os.Args)
}

var CmdServicesTree = cli.Command{
	Name:        "services-tree",
	Usage:       "List the services tree",
	Description: "List the services tree",
	Action:      dumpServicesTree,
	Flags: []cli.Flag{
		cli.StringFlag{Name: "confdir", Value: "/etc/lutrainit", Usage: "Lutrainit config directory"},
	},
}

func dumpServicesTree(ctx *cli.Context) error {
	err := setupLogging(false)
	if err != nil {
		println("[lutra] Error: This is going bad, could not setup logging", err.Error())
		// we have no choice
		// PANIC PANIC PANIC
		os.Exit(-1)
	}

	var baseDir string

	if !ctx.IsSet("confdir") {
		baseDir = "/etc/lutrainit"
	} else {
		baseDir = ctx.String("confdir")
	}
	if err = ReloadConfig(false, baseDir, false); err != nil {
		return err
	}

	time.Sleep(500 * time.Microsecond)

	// Sort the services
	SortServicesForBoot()

	// Print the tree
	for idx, target := range StartupTargets {
		fmt.Printf("+ [%d] %s\n", idx, target)
		for idx, service := range StartupServices[target] {
			fmt.Printf(" - [%d] %s\n", idx, service)
		}
	}

	return nil
}

var CmdServicesList = cli.Command{
	Name:        "services-list",
	Usage:       "List the services list",
	Description: "List the services",
	Action:      dumpServicesList,
	Flags: []cli.Flag{
		cli.StringFlag{Name: "confdir", Value: "/etc/lutrainit", Usage: "Lutrainit config directory"},
	},
}

func dumpServicesList(ctx *cli.Context) error {
	err := setupLogging(false)
	if err != nil {
		println("[lutra] Error: This is going bad, could not setup logging", err.Error())
		// we have no choice
		// PANIC PANIC PANIC
		os.Exit(-1)
	}

	var baseDir string

	if !ctx.IsSet("confdir") {
		baseDir = "/etc/lutrainit"
	} else {
		baseDir = ctx.String("confdir")
	}
	ReloadConfig(false, baseDir, false)

	time.Sleep(500 * time.Microsecond)

	data := [][]string{}
	for _, service := range LoadedServices {
		data = append(data, []string{
			string(service.Name),
			service.Type,
			strings.Join(service.Requires, ","),
			strings.Join(service.After, ","),
			strings.Join(service.Before, ","),
		})
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"name", "type", "requires", "after", "before"})

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

	return nil
}
<commit_msg>dump service will only list services, also show target<commit_after>package main

import (
	"github.com/olekukonko/tablewriter"
	"github.com/urfave/cli"
	"github.com/valyala/gorpc"
	"os"
	"strings"
	"sync"
	"time"
	"fmt"
)

var (
	// LutraVersion should match the one in lutractl/main.go
	LutraVersion = "0.1"

	// StartupServices Should only be used for the FIRST startup
	// StartupServices is the in-memory map list of processes started on a full-start boot
	StartupServices = make(map[ServiceName][]ServiceName)
	StartupTargets = make([]ServiceName, 0)

	// LoadedServices is used for any other actions, start, stop, etc.
	LoadedServices   = make(map[ServiceName]*Service)
	LoadedServicesMu = sync.RWMutex{}

	// NetFs design the list of known network file systems to be avoided mounted at boot
	NetFs = []string{"nfs", "nfs4", "smbfs", "cifs", "codafs", "ncpfs", "shfs", "fuse", "fuseblk", "glusterfs", "davfs", "fuse.glusterfs"}
	// VirtFs design the list of known virtual file systems to avoid unmounting at shutdown
	VirtFs = []string{"proc", "sysfs", "tmpfs", "devtmpfs", "devpts"}

	// GoRPCServer for client
	GoRPCServer = &gorpc.Server{}
	// GoRPCStarted or not
	GoRPCStarted = false

	//ShuttingDown is used to break various check loops like in getty
	ShuttingDown bool

	lsFnameSerialized = "/run/lutrainit.reexec.ls.bin"
	glFnameSerialized = "/run/lutrainit.reexec.gl.bin"

	// Theses two last should only filled by LDFLAGS, see Makefile

	// LutraBuildTime is the time of the build
	LutraBuildTime string
	// LutraBuildGitHash is the git sha1 of the commit based on
	LutraBuildGitHash string
)

func main() {
	app := cli.NewApp()
	app.Name = "lutrainit"
	app.Usage = "lutra init daemon"
	app.Version = LutraVersion
	app.Commands = []cli.Command{
		CmdServicesTree,
		CmdServicesList,
		CmdSysinit,
	}
	app.Flags = append(app.Flags, []cli.Flag{}...)

	// No argument will start the system init processing
	if len(os.Args) <= 1 {
		os.Args = []string{"lutrainit", "sysinit"}
	}

	app.Run(os.Args)
}

var CmdServicesTree = cli.Command{
	Name:        "services-tree",
	Usage:       "List the services tree",
	Description: "List the services tree",
	Action:      dumpServicesTree,
	Flags: []cli.Flag{
		cli.StringFlag{Name: "confdir", Value: "/etc/lutrainit", Usage: "Lutrainit config directory"},
	},
}

func dumpServicesTree(ctx *cli.Context) error {
	err := setupLogging(false)
	if err != nil {
		println("[lutra] Error: This is going bad, could not setup logging", err.Error())
		// we have no choice
		// PANIC PANIC PANIC
		os.Exit(-1)
	}

	var baseDir string

	if !ctx.IsSet("confdir") {
		baseDir = "/etc/lutrainit"
	} else {
		baseDir = ctx.String("confdir")
	}
	if err = ReloadConfig(false, baseDir, false); err != nil {
		return err
	}

	time.Sleep(500 * time.Microsecond)

	// Sort the services
	SortServicesForBoot()

	// Print the tree
	for idx, target := range StartupTargets {
		fmt.Printf("+ [%d] %s\n", idx, target)
		for idx, service := range StartupServices[target] {
			fmt.Printf(" - [%d] %s\n", idx, service)
		}
	}

	return nil
}

var CmdServicesList = cli.Command{
	Name:        "services-list",
	Usage:       "List the services list",
	Description: "List the services",
	Action:      dumpServicesList,
	Flags: []cli.Flag{
		cli.StringFlag{Name: "confdir", Value: "/etc/lutrainit", Usage: "Lutrainit config directory"},
	},
}

func dumpServicesList(ctx *cli.Context) error {
	err := setupLogging(false)
	if err != nil {
		println("[lutra] Error: This is going bad, could not setup logging", err.Error())
		// we have no choice
		// PANIC PANIC PANIC
		os.Exit(-1)
	}

	var baseDir string

	if !ctx.IsSet("confdir") {
		baseDir = "/etc/lutrainit"
	} else {
		baseDir = ctx.String("confdir")
	}
	ReloadConfig(false, baseDir, false)

	time.Sleep(500 * time.Microsecond)

	data := [][]string{}
	for _, service := range LoadedServices {
		if !service.IsService() {
			continue
		}
		data = append(data, []string{
			string(service.Name),
			string(service.WantedBy),
			service.Type,
			strings.Join(service.Requires, ","),
			strings.Join(service.After, ","),
			strings.Join(service.Before, ","),
		})
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"name", "target", "type", "requires", "after", "before"})

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Ted Elwartowski <xelwarto.pub@gmail.com>

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

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

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

package main

import (
	"flag"
  "fmt"
	"os"
  "io/ioutil"
  "encoding/json"
  "text/template"
)

var version = string("v0.2.0")

var disVersion bool
var tmplFile string
var jsonFile string
var jsonString string
var jsonMap interface{}
var jsonData map[string]interface{}
var envParam string

func init() {
	flag.BoolVar(&disVersion, "version", false, "Display version")
  flag.StringVar(&tmplFile, "tmpl", "", "Template File")
  flag.StringVar(&jsonFile, "file", "", "JSON Data File")
	flag.StringVar(&jsonString, "json", "", "JSON Input String")
	flag.StringVar(&envParam, "env", "", "Environment Parameter")
  flag.Parse()
}

func main() {
	if disVersion {
		fmt.Fprintf(os.Stdout, "Version: %v\n", version)
		os.Exit(1)
	}

	var err error
	data := []byte(jsonString)

	if jsonFile != "" {
		data, err = ioutil.ReadFile(jsonFile)
	  if err != nil {
	    fmt.Fprintf(os.Stderr, "Error reading JSON file: %v\n", err)
	    os.Exit(1)
	  }
	}

	if data != nil {
		err = json.Unmarshal(data, &jsonMap)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error parsing JSON data: %v\n", err)
			os.Exit(1)
		}
		jsonData = jsonMap.(map[string]interface{})

		if envParam != "" {
			jsonData = jsonData[envParam].(map[string]interface{})
		}
	} else {
		fmt.Fprintf(os.Stderr, "Error reading JSON data: %v\n", err)
		os.Exit(1)
	}

  tmpl, err := template.ParseFiles(tmplFile)
  if err != nil {
    fmt.Fprintf(os.Stderr, "Error reading template file: %v\n", err)
    os.Exit(1)
  }
  err = tmpl.Execute(os.Stdout, jsonData)
  if err != nil {
    fmt.Fprintf(os.Stderr, "Error executing template file: %v\n", err)
    os.Exit(1)
  }

	os.Exit(0)
}
<commit_msg>Added custom function: ENV<commit_after>/*
Copyright 2016 Ted Elwartowski <xelwarto.pub@gmail.com>

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

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

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

package main

import (
	"flag"
  "fmt"
	"os"
  "io/ioutil"
  "encoding/json"
  "text/template"
	"path/filepath"
)

var version = string("v0.2.0")

var disVersion bool
var tmplFile string
var jsonFile string
var jsonString string
var jsonMap interface{}
var jsonData map[string]interface{}
var envParam string

func init() {
	flag.BoolVar(&disVersion, "version", false, "Display version")
  flag.StringVar(&tmplFile, "tmpl", "", "Template File")
  flag.StringVar(&jsonFile, "file", "", "JSON Data File")
	flag.StringVar(&jsonString, "json", "", "JSON Input String")
	flag.StringVar(&envParam, "env", "", "Environment Parameter")
  flag.Parse()
}

func main() {
	if disVersion {
		fmt.Fprintf(os.Stdout, "Version: %v\n", version)
		os.Exit(1)
	}

	var err error
	data := []byte(jsonString)

	funcMap := template.FuncMap {
		"ENV": func (e string) string {
      return os.Getenv(e)
    },
	}

	if jsonFile != "" {
		data, err = ioutil.ReadFile(jsonFile)
	  if err != nil {
	    fmt.Fprintf(os.Stderr, "Error reading JSON file: %v\n", err)
	    os.Exit(1)
	  }
	}

	if data != nil {
		err = json.Unmarshal(data, &jsonMap)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error parsing JSON data: %v\n", err)
			os.Exit(1)
		}
		jsonData = jsonMap.(map[string]interface{})

		if envParam != "" {
			jsonData = jsonData[envParam].(map[string]interface{})
		}
	} else {
		fmt.Fprintf(os.Stderr, "Error reading JSON data: %v\n", err)
		os.Exit(1)
	}

	tmplName := filepath.Base(tmplFile)
  tmpl, err := template.New(tmplName).Funcs(funcMap).ParseFiles(tmplFile)
  if err != nil {
    fmt.Fprintf(os.Stderr, "Error reading template file: %v\n", err)
    os.Exit(1)
  }

  err = tmpl.Execute(os.Stdout, jsonData)
  if err != nil {
    fmt.Fprintf(os.Stderr, "Error executing template file: %v\n", err)
    os.Exit(1)
  }

	os.Exit(0)
}
<|endoftext|>