<commit_before>package upload

import (
	"time"

	"github.com/emicklei/go-restful"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/schema"
	"github.com/materials-commons/mcstore/pkg/ws/rest"
	"github.com/materials-commons/mcstore/server/mcstored/service/data"
	"github.com/materials-commons/mcstore/server/mcstored/service/uploads"
)

// An uploadResource handles all upload requests.
type uploadResource struct {
	log           *app.Logger
	idService     uploads.IDService
	uploadService uploads.UploadService
	dirService    data.DirService
}

// UploadEntry is a client side representation of an upload.
type UploadEntry struct {
	FileName    string    `json:"filename"`
	DirectoryID string    `json:"directory_id"`
	ProjectID   string    `json:"project_id"`
	Size        int64     `json:"size"`
	Host        string    `json:"host"`
	Checksum    string    `json:"checksum"`
	Birthtime   time.Time `json:"birthtime"`
}

// NewResources creates a new upload resource
func NewResource(uploadService uploads.UploadService, idService uploads.IDService, dirService data.DirService) rest.Service {
	return &uploadResource{
		log:           app.NewLog("resource", "upload"),
		idService:     idService,
		uploadService: uploadService,
		dirService:    dirService,
	}
}

// WebService creates an instance of the upload web service.
func (r *uploadResource) WebService() *restful.WebService {
	ws := new(restful.WebService)

	ws.Path("/upload").Produces(restful.MIME_JSON).Consumes(restful.MIME_JSON)
	ws.Route(ws.POST("").To(rest.RouteHandler(r.createUploadRequest)).
		Doc("Creates a new upload request").
		Reads(CreateRequest{}).
		Writes(CreateResponse{}))
	ws.Route(ws.POST("/chunk").To(rest.RouteHandler1(r.uploadFileChunk)).
		Consumes("multipart/form-data").
		Doc("Upload a file chunk"))
	ws.Route(ws.DELETE("{id}").To(rest.RouteHandler1(r.deleteUploadRequest)).
		Doc("Deletes an existing upload request").
		Param(ws.PathParameter("id", "upload request to delete").DataType("string")))
	ws.Route(ws.GET("{project}").To(rest.RouteHandler(r.listProjectUploadRequests)).
		Param(ws.PathParameter("project", "project id").DataType("string")).
		Doc("Lists upload requests for project").
		Writes([]UploadEntry{}))

	return ws
}

// CreateRequest describes the JSON request a client will send
// to create a new upload request.
type CreateRequest struct {
	ProjectID     string `json:"project_id"`
	DirectoryID   string `json:"directory_id"`
	DirectoryPath string `json:"directory_path"`
	FileName      string `json:"filename"`
	FileSize      int64  `json:"filesize"`
	FileMTime     string `json:"filemtime"`
}

// uploadCreateResponse is the format of JSON sent back containing
// the upload request ID.
type CreateResponse struct {
	RequestID string `json:"request_id"`
}

// createUploadRequest services requests to create a new upload id. It validates
// the given request, and ensures that the returned upload id is unique. Upload
// requests are persisted until deleted or a successful upload occurs.
func (r *uploadResource) createUploadRequest(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	cr, err := r.makeIDRequest(request, user.ID)
	if err != nil {
		return nil, err
	}

	upload, err := r.idService.ID(cr)
	if err != nil {
		return nil, err
	}

	resp := CreateResponse{
		RequestID: upload.ID,
	}
	return &resp, nil
}

// makeIDRequest fills out an id request to send to the idService. It handles request parameter errors.
func (r *uploadResource) makeIDRequest(request *restful.Request, userID string) (uploads.IDRequest, error) {
	var req CreateRequest
	var cr uploads.IDRequest

	if err := request.ReadEntity(&req); err != nil {
		return cr, err
	}

	fileMTime, err := time.Parse(time.RFC1123, req.FileMTime)
	if err != nil {
		return cr, err
	}

	directoryID, err := r.getDirectoryID(req)
	if err != nil {
		return cr, err
	}

	cr = uploads.IDRequest{
		User:        userID,
		DirectoryID: directoryID,
		ProjectID:   req.ProjectID,
		FileName:    req.FileName,
		FileSize:    req.FileSize,
		FileMTime:   fileMTime,
		Host:        request.Request.RemoteAddr,
		Birthtime:   time.Now(),
	}

	return cr, nil

}

// getDirectoryID returns the directoryID. A user can pass either a directoryID
// or a directory path. If a directory path is passed in, then the method will
// get the directoryID associated with that path in the project. If the path
// doesn't exist it will create it.
func (r *uploadResource) getDirectoryID(req CreateRequest) (directoryID string, err error) {
	switch {
	case req.DirectoryID == "" && req.DirectoryPath == "":
		return "", app.ErrInvalid
	case req.DirectoryID != "":
		return req.DirectoryID, nil
	default:
		dir, err := r.dirService.CreateDir(req.ProjectID, req.DirectoryPath)
		if err != nil {
			return "", err
		}
		return dir.ID, nil
	}
}

// uploadFileChunk uploads a new file chunk.
func (r *uploadResource) uploadFileChunk(request *restful.Request, response *restful.Response, user schema.User) error {
	flowRequest, err := form2FlowRequest(request)
	if err != nil {
		r.log.Errorf("Error converting form to flow.Request: %s", err)
		return err
	}

	req := uploads.UploadRequest{
		Request: flowRequest,
	}
	return r.uploadService.Upload(&req)
}

// deleteUploadRequest will delete an existing upload request. It validates that
// the requesting user has access to delete the request.
func (r *uploadResource) deleteUploadRequest(request *restful.Request, response *restful.Response, user schema.User) error {
	uploadID := request.PathParameter("id")
	return r.idService.Delete(uploadID, user.ID)
}

// listProjectUploadRequests returns the upload requests for the project if the requestor
// has access to the project.
func (r *uploadResource) listProjectUploadRequests(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	projectID := request.PathParameter("project")
	entries, err := r.idService.ListForProject(projectID, user.ID)
	if err != nil {
		return nil, err
	}
	return uploads2uploadEntries(entries), nil
}

// uploads2uploadEntries converts schema.Upload array into an array of UploadEntry.
func uploads2uploadEntries(projectUploads []schema.Upload) []UploadEntry {
	entries := make([]UploadEntry, len(projectUploads))
	for i, entry := range projectUploads {
		entries[i] = UploadEntry{
			FileName:    entry.File.Name,
			DirectoryID: entry.DirectoryID,
			ProjectID:   entry.ProjectID,
			Size:        entry.File.Size,
			Host:        entry.Host,
			Checksum:    entry.File.Checksum,
			Birthtime:   entry.Birthtime,
		}
	}
	return entries
}
<commit_msg>Add debug log entries.<commit_after>package upload

import (
	"time"

	"github.com/emicklei/go-restful"
	"github.com/materials-commons/mcstore/pkg/app"
	"github.com/materials-commons/mcstore/pkg/db/schema"
	"github.com/materials-commons/mcstore/pkg/ws/rest"
	"github.com/materials-commons/mcstore/server/mcstored/service/data"
	"github.com/materials-commons/mcstore/server/mcstored/service/uploads"
)

// An uploadResource handles all upload requests.
type uploadResource struct {
	log           *app.Logger
	idService     uploads.IDService
	uploadService uploads.UploadService
	dirService    data.DirService
}

// UploadEntry is a client side representation of an upload.
type UploadEntry struct {
	FileName    string    `json:"filename"`
	DirectoryID string    `json:"directory_id"`
	ProjectID   string    `json:"project_id"`
	Size        int64     `json:"size"`
	Host        string    `json:"host"`
	Checksum    string    `json:"checksum"`
	Birthtime   time.Time `json:"birthtime"`
}

// NewResources creates a new upload resource
func NewResource(uploadService uploads.UploadService, idService uploads.IDService, dirService data.DirService) rest.Service {
	return &uploadResource{
		log:           app.NewLog("resource", "upload"),
		idService:     idService,
		uploadService: uploadService,
		dirService:    dirService,
	}
}

// WebService creates an instance of the upload web service.
func (r *uploadResource) WebService() *restful.WebService {
	ws := new(restful.WebService)

	ws.Path("/upload").Produces(restful.MIME_JSON).Consumes(restful.MIME_JSON)
	ws.Route(ws.POST("").To(rest.RouteHandler(r.createUploadRequest)).
		Doc("Creates a new upload request").
		Reads(CreateRequest{}).
		Writes(CreateResponse{}))
	ws.Route(ws.POST("/chunk").To(rest.RouteHandler1(r.uploadFileChunk)).
		Consumes("multipart/form-data").
		Doc("Upload a file chunk"))
	ws.Route(ws.DELETE("{id}").To(rest.RouteHandler1(r.deleteUploadRequest)).
		Doc("Deletes an existing upload request").
		Param(ws.PathParameter("id", "upload request to delete").DataType("string")))
	ws.Route(ws.GET("{project}").To(rest.RouteHandler(r.listProjectUploadRequests)).
		Param(ws.PathParameter("project", "project id").DataType("string")).
		Doc("Lists upload requests for project").
		Writes([]UploadEntry{}))

	return ws
}

// CreateRequest describes the JSON request a client will send
// to create a new upload request.
type CreateRequest struct {
	ProjectID     string `json:"project_id"`
	DirectoryID   string `json:"directory_id"`
	DirectoryPath string `json:"directory_path"`
	FileName      string `json:"filename"`
	FileSize      int64  `json:"filesize"`
	FileMTime     string `json:"filemtime"`
}

// uploadCreateResponse is the format of JSON sent back containing
// the upload request ID.
type CreateResponse struct {
	RequestID string `json:"request_id"`
}

// createUploadRequest services requests to create a new upload id. It validates
// the given request, and ensures that the returned upload id is unique. Upload
// requests are persisted until deleted or a successful upload occurs.
func (r *uploadResource) createUploadRequest(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	cr, err := r.makeIDRequest(request, user.ID)
	if err != nil {
		return nil, err
	}

	upload, err := r.idService.ID(cr)
	if err != nil {
		return nil, err
	}

	resp := CreateResponse{
		RequestID: upload.ID,
	}
	return &resp, nil
}

// makeIDRequest fills out an id request to send to the idService. It handles request parameter errors.
func (r *uploadResource) makeIDRequest(request *restful.Request, userID string) (uploads.IDRequest, error) {
	var req CreateRequest
	var cr uploads.IDRequest

	if err := request.ReadEntity(&req); err != nil {
		app.Log.Debugf("makeIDRequest ReadEntity failed: %s", err)
		return cr, err
	}

	app.Log.Debugf("%#v", req)

	fileMTime, err := time.Parse(time.RFC1123, req.FileMTime)
	if err != nil {
		app.Log.Debugf("makeIDRequest time.Parse failed on %s: %s", req.FileMTime, err)
		return cr, err
	}

	directoryID, err := r.getDirectoryID(req)
	if err != nil {
		app.Log.Debugf("makeIDRequest getDirectoryID failed: %s", err)
		return cr, err
	}

	cr = uploads.IDRequest{
		User:        userID,
		DirectoryID: directoryID,
		ProjectID:   req.ProjectID,
		FileName:    req.FileName,
		FileSize:    req.FileSize,
		FileMTime:   fileMTime,
		Host:        request.Request.RemoteAddr,
		Birthtime:   time.Now(),
	}

	return cr, nil

}

// getDirectoryID returns the directoryID. A user can pass either a directoryID
// or a directory path. If a directory path is passed in, then the method will
// get the directoryID associated with that path in the project. If the path
// doesn't exist it will create it.
func (r *uploadResource) getDirectoryID(req CreateRequest) (directoryID string, err error) {
	switch {
	case req.DirectoryID == "" && req.DirectoryPath == "":
		app.Log.Debugf("No directoryID or directoryPath specified")
		return "", app.ErrInvalid
	case req.DirectoryID != "":
		return req.DirectoryID, nil
	default:
		dir, err := r.dirService.CreateDir(req.ProjectID, req.DirectoryPath)
		if err != nil {
			app.Log.Debugf("CreateDir %s %s failed: %s", req.ProjectID, req.DirectoryPath, err)
			return "", err
		}
		return dir.ID, nil
	}
}

// uploadFileChunk uploads a new file chunk.
func (r *uploadResource) uploadFileChunk(request *restful.Request, response *restful.Response, user schema.User) error {
	flowRequest, err := form2FlowRequest(request)
	if err != nil {
		r.log.Errorf("Error converting form to flow.Request: %s", err)
		return err
	}

	req := uploads.UploadRequest{
		Request: flowRequest,
	}
	return r.uploadService.Upload(&req)
}

// deleteUploadRequest will delete an existing upload request. It validates that
// the requesting user has access to delete the request.
func (r *uploadResource) deleteUploadRequest(request *restful.Request, response *restful.Response, user schema.User) error {
	uploadID := request.PathParameter("id")
	return r.idService.Delete(uploadID, user.ID)
}

// listProjectUploadRequests returns the upload requests for the project if the requestor
// has access to the project.
func (r *uploadResource) listProjectUploadRequests(request *restful.Request, response *restful.Response, user schema.User) (interface{}, error) {
	projectID := request.PathParameter("project")
	entries, err := r.idService.ListForProject(projectID, user.ID)
	if err != nil {
		return nil, err
	}
	return uploads2uploadEntries(entries), nil
}

// uploads2uploadEntries converts schema.Upload array into an array of UploadEntry.
func uploads2uploadEntries(projectUploads []schema.Upload) []UploadEntry {
	entries := make([]UploadEntry, len(projectUploads))
	for i, entry := range projectUploads {
		entries[i] = UploadEntry{
			FileName:    entry.File.Name,
			DirectoryID: entry.DirectoryID,
			ProjectID:   entry.ProjectID,
			Size:        entry.File.Size,
			Host:        entry.Host,
			Checksum:    entry.File.Checksum,
			Birthtime:   entry.Birthtime,
		}
	}
	return entries
}
<|endoftext|>


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

package main

import (
	"fmt"
	"os"
	"testing"
	"time"

	. "github.com/jacobsa/ogletest"
	"github.com/jgeewax/cli"
)

func TestFlags(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type FlagsTest struct {
}

func init() { RegisterTestSuite(&FlagsTest{}) }

func parseArgs(args []string) (flags *flagStorage) {
	// Create a CLI app, and abuse it to snoop on the flags.
	app := newApp()
	app.Action = func(appCtx *cli.Context) {
		flags = populateFlags(appCtx)
	}

	// Simulate argv.
	fullArgs := append([]string{"some_app"}, args...)

	err := app.Run(fullArgs)
	AssertEq(nil, err)

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *FlagsTest) Defaults() {
	f := parseArgs([]string{})

	// File system
	ExpectEq(0, len(f.MountOptions), "Options: %v", f.MountOptions)
	ExpectEq(os.FileMode(0755), f.DirMode)
	ExpectEq(os.FileMode(0644), f.FileMode)
	ExpectEq(-1, f.Uid)
	ExpectEq(-1, f.Gid)
	ExpectFalse(f.ImplicitDirs)

	// GCS
	ExpectEq("", f.KeyFile)
	ExpectEq(-1, f.EgressBandwidthLimitBytesPerSecond)
	ExpectEq(5, f.OpRateLimitHz)

	// Tuning
	ExpectEq(time.Minute, f.StatCacheTTL)
	ExpectEq(time.Minute, f.TypeCacheTTL)
	ExpectEq(1<<24, f.GCSChunkSize)
	ExpectEq("", f.TempDir)
	ExpectEq(1<<31, f.TempDirLimit)

	// Debugging
	ExpectFalse(f.DebugFuse)
	ExpectFalse(f.DebugGCS)
	ExpectFalse(f.DebugHTTP)
	ExpectFalse(f.DebugInvariants)
}

func (t *FlagsTest) Bools() {
	names := []string{
		"implicit-dirs",
		"debug_fuse",
		"debug_gcs",
		"debug_http",
		"debug_invariants",
	}

	var args []string
	var f *flagStorage

	// --foo form
	args = nil
	for _, n := range names {
		args = append(args, fmt.Sprintf("-%s", n))
	}

	f = parseArgs(args)
	ExpectTrue(f.ImplicitDirs)
	ExpectTrue(f.DebugFuse)
	ExpectTrue(f.DebugGCS)
	ExpectTrue(f.DebugHTTP)
	ExpectTrue(f.DebugInvariants)

	// --foo=false form
	args = nil
	for _, n := range names {
		args = append(args, fmt.Sprintf("-%s=false", n))
	}

	f = parseArgs(args)
	ExpectFalse(f.ImplicitDirs)
	ExpectFalse(f.DebugFuse)
	ExpectFalse(f.DebugGCS)
	ExpectFalse(f.DebugHTTP)
	ExpectFalse(f.DebugInvariants)

	// --foo=true form
	args = nil
	for _, n := range names {
		args = append(args, fmt.Sprintf("-%s=true", n))
	}

	f = parseArgs(args)
	ExpectTrue(f.ImplicitDirs)
	ExpectTrue(f.DebugFuse)
	ExpectTrue(f.DebugGCS)
	ExpectTrue(f.DebugHTTP)
	ExpectTrue(f.DebugInvariants)
}

func (t *FlagsTest) Integers() {
	AssertTrue(false, "TODO")
}

func (t *FlagsTest) Strings() {
	AssertTrue(false, "TODO")
}

func (t *FlagsTest) Durations() {
	AssertTrue(false, "TODO")
}

func (t *FlagsTest) Maps() {
	AssertTrue(false, "TODO")
}
<commit_msg>FlagsTest.Numbers<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"os"
	"testing"
	"time"

	. "github.com/jacobsa/ogletest"
	"github.com/jgeewax/cli"
)

func TestFlags(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type FlagsTest struct {
}

func init() { RegisterTestSuite(&FlagsTest{}) }

func parseArgs(args []string) (flags *flagStorage) {
	// Create a CLI app, and abuse it to snoop on the flags.
	app := newApp()
	app.Action = func(appCtx *cli.Context) {
		flags = populateFlags(appCtx)
	}

	// Simulate argv.
	fullArgs := append([]string{"some_app"}, args...)

	err := app.Run(fullArgs)
	AssertEq(nil, err)

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *FlagsTest) Defaults() {
	f := parseArgs([]string{})

	// File system
	ExpectEq(0, len(f.MountOptions), "Options: %v", f.MountOptions)
	ExpectEq(os.FileMode(0755), f.DirMode)
	ExpectEq(os.FileMode(0644), f.FileMode)
	ExpectEq(-1, f.Uid)
	ExpectEq(-1, f.Gid)
	ExpectFalse(f.ImplicitDirs)

	// GCS
	ExpectEq("", f.KeyFile)
	ExpectEq(-1, f.EgressBandwidthLimitBytesPerSecond)
	ExpectEq(5, f.OpRateLimitHz)

	// Tuning
	ExpectEq(time.Minute, f.StatCacheTTL)
	ExpectEq(time.Minute, f.TypeCacheTTL)
	ExpectEq(1<<24, f.GCSChunkSize)
	ExpectEq("", f.TempDir)
	ExpectEq(1<<31, f.TempDirLimit)

	// Debugging
	ExpectFalse(f.DebugFuse)
	ExpectFalse(f.DebugGCS)
	ExpectFalse(f.DebugHTTP)
	ExpectFalse(f.DebugInvariants)
}

func (t *FlagsTest) Bools() {
	names := []string{
		"implicit-dirs",
		"debug_fuse",
		"debug_gcs",
		"debug_http",
		"debug_invariants",
	}

	var args []string
	var f *flagStorage

	// --foo form
	args = nil
	for _, n := range names {
		args = append(args, fmt.Sprintf("-%s", n))
	}

	f = parseArgs(args)
	ExpectTrue(f.ImplicitDirs)
	ExpectTrue(f.DebugFuse)
	ExpectTrue(f.DebugGCS)
	ExpectTrue(f.DebugHTTP)
	ExpectTrue(f.DebugInvariants)

	// --foo=false form
	args = nil
	for _, n := range names {
		args = append(args, fmt.Sprintf("-%s=false", n))
	}

	f = parseArgs(args)
	ExpectFalse(f.ImplicitDirs)
	ExpectFalse(f.DebugFuse)
	ExpectFalse(f.DebugGCS)
	ExpectFalse(f.DebugHTTP)
	ExpectFalse(f.DebugInvariants)

	// --foo=true form
	args = nil
	for _, n := range names {
		args = append(args, fmt.Sprintf("-%s=true", n))
	}

	f = parseArgs(args)
	ExpectTrue(f.ImplicitDirs)
	ExpectTrue(f.DebugFuse)
	ExpectTrue(f.DebugGCS)
	ExpectTrue(f.DebugHTTP)
	ExpectTrue(f.DebugInvariants)
}

func (t *FlagsTest) Numbers() {
	args := []string{
		"--dir-mode=0711",
		"--file-mode", "0611",
		"--uid=17",
		"--gid=19",
		"--limit-bytes-per-sec=123.4",
		"--limit-ops-per-sec=56.78",
		"--gcs-chunk-size=1000",
		"--temp-dir-bytes=2000",
	}

	f := parseArgs(args)
	ExpectEq(os.FileMode(0711), f.DirMode)
	ExpectEq(os.FileMode(0611), f.FileMode)
	ExpectEq(17, f.Uid)
	ExpectEq(19, f.Gid)
	ExpectEq(123.4, f.EgressBandwidthLimitBytesPerSecond)
	ExpectEq(56.78, f.OpRateLimitHz)
	ExpectEq(1000, f.GCSChunkSize)
	ExpectEq(2000, f.TempDirLimit)
}

func (t *FlagsTest) Strings() {
	AssertTrue(false, "TODO")
}

func (t *FlagsTest) Durations() {
	AssertTrue(false, "TODO")
}

func (t *FlagsTest) Maps() {
	AssertTrue(false, "TODO")
}
<|endoftext|>


<commit_before>package fleet

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

const (
	unitsPath = "/units"
)

type Unit struct {
	Name         string        `json:"name,omitempty"`
	Options      []*UnitOption `json:"options,omitempty"`
	DesiredState string        `json:"desiredState,omitempty"`
	CurrentState string        `json:"currentState,omitempty"`
	MachineID    string        `json:"machineID,omitempty"`
}

type UnitOption struct {
	Section string `json:"section,omitempty"`
	Name    string `json:"name,omitempty"`
	Value   string `json:"value,omitempty"`
}

type unitResponse struct {
	Units []Unit `json:"units,omitempty"`
}

func (c *Client) Units() ([]Unit, error) {
	var unitsRes unitResponse

	req, err := http.NewRequest("GET", c.URL+basePath+unitsPath, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	err = json.NewDecoder(res.Body).Decode(&unitsRes)
	if err != nil {
		return nil, err
	}

	return unitsRes.Units, nil
}

// createOrUpdateUnit creates or updates an unit
func (c *Client) createOrUpdateUnit(u Unit) error {
	switch u.DesiredState {
	case "inactive":
		break
	case "loaded":
		break
	case "launched":
		break
	default:
		return errors.New("Invalid desired state")
	}

	j, err := json.Marshal(u)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("PUT", c.URL+basePath+unitsPath+"/"+u.Name, bytes.NewReader(j))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	switch res.StatusCode {
	case 201:
		// Created successfully
		return nil
	case 204:
		// Modified successfully
		return nil
	case 400:
		// Attempting to create/modify an Unit with an invalid entity
		return errors.New("400 Bad Request")
	case 409:
		// Attempting to create an entity without options
		return errors.New("409 Conflict")
	default:
		message := fmt.Sprintf("%d Faild to create/update an unit", res.StatusCode)
		return errors.New(message)
	}

	return nil
}

func (c *Client) deleteUnit(name string) error {
	req, err := http.NewRequest("DELETE", c.URL+basePath+unitsPath+"/"+name, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	switch res.StatusCode {
	case 204:
		// Deleted successfully
		return nil
	case 404:
		// The indicated Unit does not exist
		return errors.New("400 Bad Request")
	default:
		message := fmt.Sprintf("%d Faild to delete an unit", res.StatusCode)
		return errors.New(message)
	}

	return nil
}

func (c *Client) Submit(name string, opts []*UnitOption, targetState string) error {
	unit := Unit{
		Name:         name,
		Options:      opts,
		DesiredState: targetState,
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Load(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "loaded",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Start(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "launched",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Stop(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "loaded",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Unload(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "inactive",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Destroy(name string) error {
	return c.deleteUnit(name)
}
<commit_msg>Fix wrong error message<commit_after>package fleet

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

const (
	unitsPath = "/units"
)

type Unit struct {
	Name         string        `json:"name,omitempty"`
	Options      []*UnitOption `json:"options,omitempty"`
	DesiredState string        `json:"desiredState,omitempty"`
	CurrentState string        `json:"currentState,omitempty"`
	MachineID    string        `json:"machineID,omitempty"`
}

type UnitOption struct {
	Section string `json:"section,omitempty"`
	Name    string `json:"name,omitempty"`
	Value   string `json:"value,omitempty"`
}

type unitResponse struct {
	Units []Unit `json:"units,omitempty"`
}

func (c *Client) Units() ([]Unit, error) {
	var unitsRes unitResponse

	req, err := http.NewRequest("GET", c.URL+basePath+unitsPath, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	err = json.NewDecoder(res.Body).Decode(&unitsRes)
	if err != nil {
		return nil, err
	}

	return unitsRes.Units, nil
}

// createOrUpdateUnit creates or updates an unit
func (c *Client) createOrUpdateUnit(u Unit) error {
	switch u.DesiredState {
	case "inactive":
		break
	case "loaded":
		break
	case "launched":
		break
	default:
		return errors.New("Invalid desired state")
	}

	j, err := json.Marshal(u)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("PUT", c.URL+basePath+unitsPath+"/"+u.Name, bytes.NewReader(j))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	switch res.StatusCode {
	case 201:
		// Created successfully
		return nil
	case 204:
		// Modified successfully
		return nil
	case 400:
		// Attempting to create/modify an Unit with an invalid entity
		return errors.New("400 Bad Request")
	case 409:
		// Attempting to create an entity without options
		return errors.New("409 Conflict")
	default:
		message := fmt.Sprintf("%d Faild to create/update an unit", res.StatusCode)
		return errors.New(message)
	}

	return nil
}

func (c *Client) deleteUnit(name string) error {
	req, err := http.NewRequest("DELETE", c.URL+basePath+unitsPath+"/"+name, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	switch res.StatusCode {
	case 204:
		// Deleted successfully
		return nil
	case 404:
		// The indicated Unit does not exist
		return errors.New("404 Not Found")
	default:
		message := fmt.Sprintf("%d Faild to delete an unit", res.StatusCode)
		return errors.New(message)
	}

	return nil
}

func (c *Client) Submit(name string, opts []*UnitOption, targetState string) error {
	unit := Unit{
		Name:         name,
		Options:      opts,
		DesiredState: targetState,
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Load(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "loaded",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Start(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "launched",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Stop(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "loaded",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Unload(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "inactive",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Destroy(name string) error {
	return c.deleteUnit(name)
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	var s []int
	show(s)

	s = cAdd(s, 1, 2, 3)
	show(s)
}

func cAdd(s []int, v ...int) []int {
	return append(s, v...)
}

func show(s []int) {
	fmt.Printf("%v\nlen: %v cap: %v\n%p\n\n",
		s,
		len(s),
		cap(s),
		s)
}
<commit_msg>aloc: add pAdd<commit_after>package main

import "fmt"

func main() {
	var s []int
	show(s)

	s = cAdd(s, 1, 2, 3)
	pAdd(&s, 11, 22, 33)
	show(s)
}

func cAdd(s []int, v ...int) []int {
	return append(s, v...)
}

func pAdd(s *[]int, v ...int) {
	*s = append(*s, v...)
}

func show(s []int) {
	fmt.Printf("%v\nlen: %v cap: %v\n%p\n\n",
		s,
		len(s),
		cap(s),
		s)
}
<|endoftext|>


<commit_before>package gottp

import (
	"gopkg.in/simversity/gotracer.v1"
	"sync"
)

var workerRunning bool

var worker func(chan bool, chan bool)

var errChan = make(chan bool)

var exitChan = make(chan bool)

var wg = new(sync.WaitGroup)

func spawner() {
	go workerWrapper()

	s := <-errChan
	if s {
		go spawner()
	}
}

func workerWrapper() {

	wg.Add(1)

	defer wg.Done()
	defer gotracer.Tracer{Dummy: true}.Notify(func() string {
		errChan <- true
		return "Exception in worker"
	})

	worker(errChan, exitChan)
	errChan <- false
}

func RunWorker(wk func(chan bool, chan bool)) {
	worker = wk
	workerRunning = true
	go spawner()
}

func StopWorker() {
	if workerRunning {
		exitChan <- true
		wg.Wait()
	}
}
<commit_msg>Checking if worker is already running or not.<commit_after>package gottp

import (
	"gopkg.in/simversity/gotracer.v1"
	"sync"
)

var workerRunning bool

var worker func(chan bool, chan bool)

var errChan = make(chan bool)

var exitChan = make(chan bool)

var wg = new(sync.WaitGroup)

func spawner() {
	go workerWrapper()

	s := <-errChan
	if s {
		go spawner()
	}
}

func workerWrapper() {

	wg.Add(1)

	defer wg.Done()
	defer gotracer.Tracer{Dummy: true}.Notify(func() string {
		errChan <- true
		return "Exception in worker"
	})

	worker(errChan, exitChan)
	errChan <- false
}

func RunWorker(wk func(chan bool, chan bool)) {
	if workerRunning {
		panic("Worker already running.")
	}
	worker = wk
	workerRunning = true
	go spawner()
}

func StopWorker() {
	if workerRunning {
		exitChan <- true
		wg.Wait()
		workerRunning = false
	}
}
<|endoftext|>


<commit_before>package front

import (
	"bytes"
	"io/ioutil"
	"testing"
)

func TestMatter(t *testing.T) {
	bodyData, err := ioutil.ReadFile("testdata/front/body.md")
	if err != nil {
		t.Error(err)
	}
	m := NewMatter()
	m.Handle("+++", JSONHandler)
	b, err := ioutil.ReadFile("testdata/front/json.md")
	if err != nil {
		t.Error(err)
	}
	front, body, err := m.parse(bytes.NewReader(b))
	if err != nil {
		t.Error(err)
	}
	if body != string(bodyData) {
		t.Errorf("expected %s got %s", string(bodyData), body)
	}
	if _, ok := front["title"]; !ok {
		t.Error("expected front matter to contain title got nil instead")
	}
}
<commit_msg>fixed typo<commit_after>package front

import (
	"bytes"
	"io/ioutil"
	"testing"
)

func TestMatter(t *testing.T) {
	bodyData, err := ioutil.ReadFile("testdata/front/body.md")
	if err != nil {
		t.Error(err)
	}
	m := NewMatter()
	m.Handle("+++", JSONHandler)
	b, err := ioutil.ReadFile("testdata/front/json.md")
	if err != nil {
		t.Error(err)
	}
	front, body, err := m.Parse(bytes.NewReader(b))
	if err != nil {
		t.Error(err)
	}
	if body != string(bodyData) {
		t.Errorf("expected %s got %s", string(bodyData), body)
	}
	if _, ok := front["title"]; !ok {
		t.Error("expected front matter to contain title got nil instead")
	}
}
<|endoftext|>


<commit_before>package common

import (
	"fmt"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/HouzuoGuo/laitos/lalog"
	"github.com/HouzuoGuo/laitos/misc"
)

const (
	// ServerRateLimitIntervalSec is the interval at which client rate limit counter operates, i.e. maximum N clients per interval of X.
	ServerRateLimitIntervalSec = 1
	/*
		ServerDefaultIOTimeoutSec is the default IO timeout applied to all client connections. The IO timeout prevents a
		potentially malfunctioning server application from hanging at a lingering client.
		Server application should always override the default IO timeout by setting a new timeout in connection handler.
	*/
	ServerDefaultIOTimeoutSec = 10 * 60
)

// TCPApp defines routines for a TCP server application to accept, process, and interact with client connections.
type TCPApp interface {
	// GetTCPStatsCollector returns the stats collector that counts and times client connections for the TCP application.
	GetTCPStatsCollector() *misc.Stats
	// HandleTCPConnection converses with the TCP client. The client connection is closed by server upon returning from the implementation.
	HandleTCPConnection(lalog.Logger, string, *net.TCPConn)
}

// TCPServer implements common routines for a TCP server that interacts with unlimited number of clients while applying a rate limit.
type TCPServer struct {
	// ListenAddr is the IP address to listen on. Use 0.0.0.0 to listen on all network interfaces.
	ListenAddr string
	// ListenPort is the port number to listen on.
	ListenPort int
	// AppName is a human readable name that identifies the server application in log entries.
	AppName string
	// App is the concrete implementation of TCP server application.
	App TCPApp
	/*
		LimitPerSec is the maximum number of actions and connections acceptable from a single IP at a time.
		Once the limit is reached, new connections from the IP will be closed right away, and existing conversations are
		terminated.
	*/
	LimitPerSec int

	mutex     *sync.Mutex
	logger    lalog.Logger
	rateLimit *misc.RateLimit
	listener  net.Listener
}

// NewTCPServer constructs a new TCP server and initialises its internal structures.
func NewTCPServer(listenAddr string, listenPort int, appName string, app TCPApp, limitPerSec int) (srv *TCPServer) {
	srv = &TCPServer{
		ListenAddr:  listenAddr,
		ListenPort:  listenPort,
		AppName:     appName,
		App:         app,
		LimitPerSec: limitPerSec,
	}
	srv.Initialise()
	return
}

// Initialise initialises the internal structures of the TCP server, preparing it for accepting clients.
func (srv *TCPServer) Initialise() {
	srv.mutex = new(sync.Mutex)
	srv.logger = lalog.Logger{
		ComponentName: srv.AppName,
		ComponentID:   []lalog.LoggerIDField{{Key: "Addr", Value: srv.ListenAddr}, {Key: "TCPPort", Value: srv.ListenPort}},
	}
	srv.rateLimit = &misc.RateLimit{Logger: srv.logger, UnitSecs: 1, MaxCount: srv.LimitPerSec}
	srv.rateLimit.Initialise()
}

/*
StartAndBlock starts TCP listener to process client connections and blocks until the server is told to stop.
Call this function after having initialised the TCP server.
*/
func (srv *TCPServer) StartAndBlock() error {
	defer srv.Stop()
	srv.mutex.Lock()
	if srv.listener != nil {
		srv.mutex.Unlock()
		return fmt.Errorf("TCPServer.StartAndBlock(%s): listener on port %d must not be started a second time", srv.AppName, srv.ListenPort)
	}
	srv.logger.Info("StartAndBlock", "", nil, "starting TCP listener")
	var err error
	listener, err := net.Listen("tcp", net.JoinHostPort(srv.ListenAddr, strconv.Itoa(srv.ListenPort)))
	if err != nil {
		srv.mutex.Unlock()
		return fmt.Errorf("TCPServer.StartAndBlock(%s): failed to listen on port %d - %v", srv.AppName, srv.ListenPort, err)
	}
	srv.listener = listener
	srv.mutex.Unlock()
	for {
		if misc.EmergencyLockDown {
			srv.logger.Warning("StartAndBlock", srv.AppName, misc.ErrEmergencyLockDown, "")
			return misc.ErrEmergencyLockDown
		}
		client, err := srv.listener.Accept()
		if err != nil {
			if strings.Contains(err.Error(), "closed") {
				return nil
			}
			return fmt.Errorf("TCPServer.StartAndBlock(%s): failed to accept new connection - %v", srv.AppName, err)
		}
		// Check client IP against rate limit
		tcpClient := client.(*net.TCPConn)
		clientIP := tcpClient.RemoteAddr().(*net.TCPAddr).IP.String()
		if !srv.rateLimit.Add(clientIP, true) {
			srv.logger.MaybeMinorError(tcpClient.Close())
			continue
		}
		go srv.handleConnection(clientIP, tcpClient)
	}
}

// AddAndCheckRateLimit may be optionally invoked by TCP application in the middle of an ongoing conversation to check whether conversation is going on too fast.
func (srv *TCPServer) AddAndCheckRateLimit(clientIP string) bool {
	return srv.rateLimit.Add(clientIP, true)
}

// handleConnection is launched in an independent goroutine by StartAndBlock to interact with a connected client.
func (srv *TCPServer) handleConnection(clientIP string, client *net.TCPConn) {
	// Put processing duration into statistics
	beginTimeNano := time.Now().UnixNano()
	defer func() {
		srv.logger.MaybeMinorError(client.Close())
		srv.App.GetTCPStatsCollector().Trigger(float64(time.Now().UnixNano() - beginTimeNano))
	}()
	srv.logger.Info("handleConnection", clientIP, nil, "connection is accepted")
	// Turn on keep-alive for OS to detect and remove dead clients
	if err := client.SetKeepAlive(true); err != nil {
		srv.logger.Warning("handleConnection", clientIP, err, "failed to turn on keep alive")
	}
	if err := client.SetKeepAlivePeriod(ServerDefaultIOTimeoutSec / 3); err != nil {
		srv.logger.Warning("handleConnection", clientIP, err, "failed to turn on keep alive")
	}
	// Apply the default IO timeout to prevent a potentially malfunctioning connection handler from hanging
	if err := client.SetReadDeadline(time.Now().Add(ServerDefaultIOTimeoutSec * time.Second)); err != nil {
		srv.logger.Warning("handleConnection", clientIP, err, "failed to set default read deadline, terminating the connection.")
		return
	}
	if err := client.SetWriteDeadline(time.Now().Add(ServerDefaultIOTimeoutSec * time.Second)); err != nil {
		srv.logger.Warning("handleConnection", clientIP, err, "failed to set default write deadline, terminating the connection.")
		return
	}
	srv.App.HandleTCPConnection(srv.logger, clientIP, client)
}

// Stop the TCP server from accepting new connections. Ongoing connections will continue nonetheless.
func (srv *TCPServer) Stop() {
	srv.mutex.Lock()
	defer srv.mutex.Unlock()
	if srv.listener != nil {
		if err := srv.listener.Close(); err != nil {
			srv.logger.Warning("Stop", srv.AppName, err, "failed to stop TCP server listener")
		}
		srv.listener = nil
	}
	srv.logger.Info("Stop", srv.AppName, nil, "TCP server has shut down successfully")
}
<commit_msg>fix a rare nil pointer dereference in TCPServer.StartAndBlock()<commit_after>package common

import (
	"fmt"
	"net"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/HouzuoGuo/laitos/lalog"
	"github.com/HouzuoGuo/laitos/misc"
)

const (
	// ServerRateLimitIntervalSec is the interval at which client rate limit counter operates, i.e. maximum N clients per interval of X.
	ServerRateLimitIntervalSec = 1
	/*
		ServerDefaultIOTimeoutSec is the default IO timeout applied to all client connections. The IO timeout prevents a
		potentially malfunctioning server application from hanging at a lingering client.
		Server application should always override the default IO timeout by setting a new timeout in connection handler.
	*/
	ServerDefaultIOTimeoutSec = 10 * 60
)

// TCPApp defines routines for a TCP server application to accept, process, and interact with client connections.
type TCPApp interface {
	// GetTCPStatsCollector returns the stats collector that counts and times client connections for the TCP application.
	GetTCPStatsCollector() *misc.Stats
	// HandleTCPConnection converses with the TCP client. The client connection is closed by server upon returning from the implementation.
	HandleTCPConnection(lalog.Logger, string, *net.TCPConn)
}

// TCPServer implements common routines for a TCP server that interacts with unlimited number of clients while applying a rate limit.
type TCPServer struct {
	// ListenAddr is the IP address to listen on. Use 0.0.0.0 to listen on all network interfaces.
	ListenAddr string
	// ListenPort is the port number to listen on.
	ListenPort int
	// AppName is a human readable name that identifies the server application in log entries.
	AppName string
	// App is the concrete implementation of TCP server application.
	App TCPApp
	/*
		LimitPerSec is the maximum number of actions and connections acceptable from a single IP at a time.
		Once the limit is reached, new connections from the IP will be closed right away, and existing conversations are
		terminated.
	*/
	LimitPerSec int

	mutex     *sync.Mutex
	logger    lalog.Logger
	rateLimit *misc.RateLimit
	listener  net.Listener
}

// NewTCPServer constructs a new TCP server and initialises its internal structures.
func NewTCPServer(listenAddr string, listenPort int, appName string, app TCPApp, limitPerSec int) (srv *TCPServer) {
	srv = &TCPServer{
		ListenAddr:  listenAddr,
		ListenPort:  listenPort,
		AppName:     appName,
		App:         app,
		LimitPerSec: limitPerSec,
	}
	srv.Initialise()
	return
}

// Initialise initialises the internal structures of the TCP server, preparing it for accepting clients.
func (srv *TCPServer) Initialise() {
	srv.mutex = new(sync.Mutex)
	srv.logger = lalog.Logger{
		ComponentName: srv.AppName,
		ComponentID:   []lalog.LoggerIDField{{Key: "Addr", Value: srv.ListenAddr}, {Key: "TCPPort", Value: srv.ListenPort}},
	}
	srv.rateLimit = &misc.RateLimit{Logger: srv.logger, UnitSecs: 1, MaxCount: srv.LimitPerSec}
	srv.rateLimit.Initialise()
}

/*
StartAndBlock starts TCP listener to process client connections and blocks until the server is told to stop.
Call this function after having initialised the TCP server.
*/
func (srv *TCPServer) StartAndBlock() error {
	defer srv.Stop()
	srv.mutex.Lock()
	if srv.listener != nil {
		srv.mutex.Unlock()
		return fmt.Errorf("TCPServer.StartAndBlock(%s): listener on port %d must not be started a second time", srv.AppName, srv.ListenPort)
	}
	srv.logger.Info("StartAndBlock", "", nil, "starting TCP listener")
	var err error
	listener, err := net.Listen("tcp", net.JoinHostPort(srv.ListenAddr, strconv.Itoa(srv.ListenPort)))
	if err != nil {
		srv.mutex.Unlock()
		return fmt.Errorf("TCPServer.StartAndBlock(%s): failed to listen on port %d - %v", srv.AppName, srv.ListenPort, err)
	}
	srv.listener = listener
	srv.mutex.Unlock()
	for {
		if misc.EmergencyLockDown {
			srv.logger.Warning("StartAndBlock", srv.AppName, misc.ErrEmergencyLockDown, "")
			return misc.ErrEmergencyLockDown
		}
		client, err := listener.Accept()
		if err != nil {
			if strings.Contains(err.Error(), "closed") {
				return nil
			}
			return fmt.Errorf("TCPServer.StartAndBlock(%s): failed to accept new connection - %v", srv.AppName, err)
		}
		// Check client IP against rate limit
		tcpClient := client.(*net.TCPConn)
		clientIP := tcpClient.RemoteAddr().(*net.TCPAddr).IP.String()
		if !srv.rateLimit.Add(clientIP, true) {
			srv.logger.MaybeMinorError(tcpClient.Close())
			continue
		}
		go srv.handleConnection(clientIP, tcpClient)
	}
}

// AddAndCheckRateLimit may be optionally invoked by TCP application in the middle of an ongoing conversation to check whether conversation is going on too fast.
func (srv *TCPServer) AddAndCheckRateLimit(clientIP string) bool {
	return srv.rateLimit.Add(clientIP, true)
}

// handleConnection is launched in an independent goroutine by StartAndBlock to interact with a connected client.
func (srv *TCPServer) handleConnection(clientIP string, client *net.TCPConn) {
	// Put processing duration into statistics
	beginTimeNano := time.Now().UnixNano()
	defer func() {
		srv.logger.MaybeMinorError(client.Close())
		srv.App.GetTCPStatsCollector().Trigger(float64(time.Now().UnixNano() - beginTimeNano))
	}()
	srv.logger.Info("handleConnection", clientIP, nil, "connection is accepted")
	// Turn on keep-alive for OS to detect and remove dead clients
	if err := client.SetKeepAlive(true); err != nil {
		srv.logger.Warning("handleConnection", clientIP, err, "failed to turn on keep alive")
	}
	if err := client.SetKeepAlivePeriod(ServerDefaultIOTimeoutSec / 3); err != nil {
		srv.logger.Warning("handleConnection", clientIP, err, "failed to turn on keep alive")
	}
	// Apply the default IO timeout to prevent a potentially malfunctioning connection handler from hanging
	if err := client.SetReadDeadline(time.Now().Add(ServerDefaultIOTimeoutSec * time.Second)); err != nil {
		srv.logger.Warning("handleConnection", clientIP, err, "failed to set default read deadline, terminating the connection.")
		return
	}
	if err := client.SetWriteDeadline(time.Now().Add(ServerDefaultIOTimeoutSec * time.Second)); err != nil {
		srv.logger.Warning("handleConnection", clientIP, err, "failed to set default write deadline, terminating the connection.")
		return
	}
	srv.App.HandleTCPConnection(srv.logger, clientIP, client)
}

// Stop the TCP server from accepting new connections. Ongoing connections will continue nonetheless.
func (srv *TCPServer) Stop() {
	srv.mutex.Lock()
	defer srv.mutex.Unlock()
	if srv.listener != nil {
		if err := srv.listener.Close(); err != nil {
			srv.logger.Warning("Stop", srv.AppName, err, "failed to stop TCP server listener")
		}
		srv.listener = nil
	}
	srv.logger.Info("Stop", srv.AppName, nil, "TCP server has shut down successfully")
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"time"

	"github.com/github/git-lfs/config"
	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/progress"
	"github.com/rubyist/tracerx"
	"github.com/spf13/cobra"
)

var (
	fetchCmd = &cobra.Command{
		Use: "fetch",
		Run: fetchCommand,
	}
	fetchIncludeArg string
	fetchExcludeArg string
	fetchRecentArg  bool
	fetchAllArg     bool
	fetchPruneArg   bool
)

func fetchCommand(cmd *cobra.Command, args []string) {
	requireInRepo()

	var refs []*git.Ref

	if len(args) > 0 {
		// Remote is first arg
		if err := git.ValidateRemote(args[0]); err != nil {
			Exit("Invalid remote name %q", args[0])
		}
		config.Config.CurrentRemote = args[0]
	} else {
		// Actively find the default remote, don't just assume origin
		defaultRemote, err := git.DefaultRemote()
		if err != nil {
			Exit("No default remote")
		}
		config.Config.CurrentRemote = defaultRemote
	}

	if len(args) > 1 {
		resolvedrefs, err := git.ResolveRefs(args[1:])
		if err != nil {
			Panic(err, "Invalid ref argument: %v", args[1:])
		}
		refs = resolvedrefs
	} else {
		ref, err := git.CurrentRef()
		if err != nil {
			Panic(err, "Could not fetch")
		}
		refs = []*git.Ref{ref}
	}

	success := true
	if fetchAllArg {
		if fetchRecentArg || len(args) > 1 {
			Exit("Cannot combine --all with ref arguments or --recent")
		}
		if fetchIncludeArg != "" || fetchExcludeArg != "" {
			Exit("Cannot combine --all with --include or --exclude")
		}
		if len(config.Config.FetchIncludePaths()) > 0 || len(config.Config.FetchExcludePaths()) > 0 {
			Print("Ignoring global include / exclude paths to fulfil --all")
		}
		success = fetchAll()

	} else { // !all
		includePaths, excludePaths := determineIncludeExcludePaths(config.Config, fetchIncludeArg, fetchExcludeArg)

		// Fetch refs sequentially per arg order; duplicates in later refs will be ignored
		for _, ref := range refs {
			Print("Fetching %v", ref.Name)
			s := fetchRef(ref.Sha, includePaths, excludePaths)
			success = success && s
		}

		if fetchRecentArg || config.Config.FetchPruneConfig().FetchRecentAlways {
			s := fetchRecent(refs, includePaths, excludePaths)
			success = success && s
		}
	}

	if fetchPruneArg {
		verify := config.Config.FetchPruneConfig().PruneVerifyRemoteAlways
		// no dry-run or verbose options in fetch, assume false
		prune(verify, false, false)
	}

	if !success {
		Exit("Warning: errors occurred")
	}
}

func init() {
	fetchCmd.Flags().StringVarP(&fetchIncludeArg, "include", "I", "", "Include a list of paths")
	fetchCmd.Flags().StringVarP(&fetchExcludeArg, "exclude", "X", "", "Exclude a list of paths")
	fetchCmd.Flags().BoolVarP(&fetchRecentArg, "recent", "r", false, "Fetch recent refs & commits")
	fetchCmd.Flags().BoolVarP(&fetchAllArg, "all", "a", false, "Fetch all LFS files ever referenced")
	fetchCmd.Flags().BoolVarP(&fetchPruneArg, "prune", "p", false, "After fetching, prune old data")
	RootCmd.AddCommand(fetchCmd)
}

func pointersToFetchForRef(ref string) ([]*lfs.WrappedPointer, error) {
	// Use SkipDeletedBlobs to avoid fetching ALL previous versions of modified files
	opts := lfs.NewScanRefsOptions()
	opts.ScanMode = lfs.ScanRefsMode
	opts.SkipDeletedBlobs = true
	return lfs.ScanRefs(ref, "", opts)
}

func fetchRefToChan(ref string, include, exclude []string) chan *lfs.WrappedPointer {
	c := make(chan *lfs.WrappedPointer)
	pointers, err := pointersToFetchForRef(ref)
	if err != nil {
		Panic(err, "Could not scan for Git LFS files")
	}

	go fetchAndReportToChan(pointers, include, exclude, c)

	return c
}

// Fetch all binaries for a given ref (that we don't have already)
func fetchRef(ref string, include, exclude []string) bool {
	pointers, err := pointersToFetchForRef(ref)
	if err != nil {
		Panic(err, "Could not scan for Git LFS files")
	}
	return fetchPointers(pointers, include, exclude)
}

// Fetch all previous versions of objects from since to ref (not including final state at ref)
// So this will fetch all the '-' sides of the diff from since to ref
func fetchPreviousVersions(ref string, since time.Time, include, exclude []string) bool {
	pointers, err := lfs.ScanPreviousVersions(ref, since)
	if err != nil {
		Panic(err, "Could not scan for Git LFS previous versions")
	}
	return fetchPointers(pointers, include, exclude)
}

// Fetch recent objects based on config
func fetchRecent(alreadyFetchedRefs []*git.Ref, include, exclude []string) bool {
	fetchconf := config.Config.FetchPruneConfig()

	if fetchconf.FetchRecentRefsDays == 0 && fetchconf.FetchRecentCommitsDays == 0 {
		return true
	}

	ok := true
	// Make a list of what unique commits we've already fetched for to avoid duplicating work
	uniqueRefShas := make(map[string]string, len(alreadyFetchedRefs))
	for _, ref := range alreadyFetchedRefs {
		uniqueRefShas[ref.Sha] = ref.Name
	}
	// First find any other recent refs
	if fetchconf.FetchRecentRefsDays > 0 {
		Print("Fetching recent branches within %v days", fetchconf.FetchRecentRefsDays)
		refsSince := time.Now().AddDate(0, 0, -fetchconf.FetchRecentRefsDays)
		refs, err := git.RecentBranches(refsSince, fetchconf.FetchRecentRefsIncludeRemotes, config.Config.CurrentRemote)
		if err != nil {
			Panic(err, "Could not scan for recent refs")
		}
		for _, ref := range refs {
			// Don't fetch for the same SHA twice
			if prevRefName, ok := uniqueRefShas[ref.Sha]; ok {
				if ref.Name != prevRefName {
					tracerx.Printf("Skipping fetch for %v, already fetched via %v", ref.Name, prevRefName)
				}
			} else {
				uniqueRefShas[ref.Sha] = ref.Name
				Print("Fetching %v", ref.Name)
				k := fetchRef(ref.Sha, include, exclude)
				ok = ok && k
			}
		}
	}
	// For every unique commit we've fetched, check recent commits too
	if fetchconf.FetchRecentCommitsDays > 0 {
		for commit, refName := range uniqueRefShas {
			// We measure from the last commit at the ref
			summ, err := git.GetCommitSummary(commit)
			if err != nil {
				Error("Couldn't scan commits at %v: %v", refName, err)
				continue
			}
			Print("Fetching changes within %v days of %v", fetchconf.FetchRecentCommitsDays, refName)
			commitsSince := summ.CommitDate.AddDate(0, 0, -fetchconf.FetchRecentCommitsDays)
			k := fetchPreviousVersions(commit, commitsSince, include, exclude)
			ok = ok && k
		}

	}
	return ok
}

func fetchAll() bool {
	pointers := scanAll()
	Print("Fetching objects...")
	return fetchPointers(pointers, nil, nil)
}

func scanAll() []*lfs.WrappedPointer {
	// converts to `git rev-list --all`
	// We only pick up objects in real commits and not the reflog
	opts := lfs.NewScanRefsOptions()
	opts.ScanMode = lfs.ScanAllMode
	opts.SkipDeletedBlobs = false

	// This could be a long process so use the chan version & report progress
	Print("Scanning for all objects ever referenced...")
	spinner := progress.NewSpinner()
	var numObjs int64
	pointerchan, err := lfs.ScanRefsToChan("", "", opts)
	if err != nil {
		Panic(err, "Could not scan for Git LFS files")
	}

	pointers := make([]*lfs.WrappedPointer, 0)

	for p := range pointerchan.Results {
		numObjs++
		spinner.Print(OutputWriter, fmt.Sprintf("%d objects found", numObjs))
		pointers = append(pointers, p)
	}
	err = pointerchan.Wait()
	if err != nil {
		Panic(err, "Could not scan for Git LFS files")
	}

	spinner.Finish(OutputWriter, fmt.Sprintf("%d objects found", numObjs))
	return pointers
}

func fetchPointers(pointers []*lfs.WrappedPointer, include, exclude []string) bool {
	return fetchAndReportToChan(pointers, include, exclude, nil)
}

// Fetch and report completion of each OID to a channel (optional, pass nil to skip)
// Returns true if all completed with no errors, false if errors were written to stderr/log
func fetchAndReportToChan(pointers []*lfs.WrappedPointer, include, exclude []string, out chan<- *lfs.WrappedPointer) bool {
	totalSize := int64(0)
	for _, p := range pointers {
		totalSize += p.Size
	}
	q := lfs.NewDownloadQueue(len(pointers), totalSize, false)

	if out != nil {
		dlwatch := q.Watch()

		go func() {
			// fetch only reports single OID, but OID *might* be referenced by multiple
			// WrappedPointers if same content is at multiple paths, so map oid->slice
			oidToPointers := make(map[string][]*lfs.WrappedPointer, len(pointers))
			for _, pointer := range pointers {
				plist := oidToPointers[pointer.Oid]
				oidToPointers[pointer.Oid] = append(plist, pointer)
			}

			for oid := range dlwatch {
				plist, ok := oidToPointers[oid]
				if !ok {
					continue
				}
				for _, p := range plist {
					out <- p
				}
			}
			close(out)
		}()
	}

	for _, p := range pointers {
		// Only add to download queue if local file is not the right size already
		// This avoids previous case of over-reporting a requirement for files we already have
		// which would only be skipped by PointerSmudgeObject later
		passFilter := lfs.FilenamePassesIncludeExcludeFilter(p.Name, include, exclude)

		lfs.LinkOrCopyFromReference(p.Oid, p.Size)

		if !lfs.ObjectExistsOfSize(p.Oid, p.Size) && passFilter {
			tracerx.Printf("fetch %v [%v]", p.Name, p.Oid)
			q.Add(lfs.NewDownloadable(p))
		} else {
			if !passFilter {
				tracerx.Printf("Skipping %v [%v], include/exclude filters applied", p.Name, p.Oid)
			} else {
				tracerx.Printf("Skipping %v [%v], already exists", p.Name, p.Oid)
			}

			// If we already have it, or it won't be fetched
			// report it to chan immediately to support pull/checkout
			if out != nil {
				out <- p
			}

		}
	}

	processQueue := time.Now()
	q.Wait()
	tracerx.PerformanceSince("process queue", processQueue)

	ok := true
	for _, err := range q.Errors() {
		ok = false
		ExitWithError(err)
	}
	return ok
}
<commit_msg>Improve reporting of entirely skipped files in progress<commit_after>package commands

import (
	"fmt"
	"time"

	"github.com/github/git-lfs/config"
	"github.com/github/git-lfs/git"
	"github.com/github/git-lfs/lfs"
	"github.com/github/git-lfs/progress"
	"github.com/rubyist/tracerx"
	"github.com/spf13/cobra"
)

var (
	fetchCmd = &cobra.Command{
		Use: "fetch",
		Run: fetchCommand,
	}
	fetchIncludeArg string
	fetchExcludeArg string
	fetchRecentArg  bool
	fetchAllArg     bool
	fetchPruneArg   bool
)

func fetchCommand(cmd *cobra.Command, args []string) {
	requireInRepo()

	var refs []*git.Ref

	if len(args) > 0 {
		// Remote is first arg
		if err := git.ValidateRemote(args[0]); err != nil {
			Exit("Invalid remote name %q", args[0])
		}
		config.Config.CurrentRemote = args[0]
	} else {
		// Actively find the default remote, don't just assume origin
		defaultRemote, err := git.DefaultRemote()
		if err != nil {
			Exit("No default remote")
		}
		config.Config.CurrentRemote = defaultRemote
	}

	if len(args) > 1 {
		resolvedrefs, err := git.ResolveRefs(args[1:])
		if err != nil {
			Panic(err, "Invalid ref argument: %v", args[1:])
		}
		refs = resolvedrefs
	} else {
		ref, err := git.CurrentRef()
		if err != nil {
			Panic(err, "Could not fetch")
		}
		refs = []*git.Ref{ref}
	}

	success := true
	if fetchAllArg {
		if fetchRecentArg || len(args) > 1 {
			Exit("Cannot combine --all with ref arguments or --recent")
		}
		if fetchIncludeArg != "" || fetchExcludeArg != "" {
			Exit("Cannot combine --all with --include or --exclude")
		}
		if len(config.Config.FetchIncludePaths()) > 0 || len(config.Config.FetchExcludePaths()) > 0 {
			Print("Ignoring global include / exclude paths to fulfil --all")
		}
		success = fetchAll()

	} else { // !all
		includePaths, excludePaths := determineIncludeExcludePaths(config.Config, fetchIncludeArg, fetchExcludeArg)

		// Fetch refs sequentially per arg order; duplicates in later refs will be ignored
		for _, ref := range refs {
			Print("Fetching %v", ref.Name)
			s := fetchRef(ref.Sha, includePaths, excludePaths)
			success = success && s
		}

		if fetchRecentArg || config.Config.FetchPruneConfig().FetchRecentAlways {
			s := fetchRecent(refs, includePaths, excludePaths)
			success = success && s
		}
	}

	if fetchPruneArg {
		verify := config.Config.FetchPruneConfig().PruneVerifyRemoteAlways
		// no dry-run or verbose options in fetch, assume false
		prune(verify, false, false)
	}

	if !success {
		Exit("Warning: errors occurred")
	}
}

func init() {
	fetchCmd.Flags().StringVarP(&fetchIncludeArg, "include", "I", "", "Include a list of paths")
	fetchCmd.Flags().StringVarP(&fetchExcludeArg, "exclude", "X", "", "Exclude a list of paths")
	fetchCmd.Flags().BoolVarP(&fetchRecentArg, "recent", "r", false, "Fetch recent refs & commits")
	fetchCmd.Flags().BoolVarP(&fetchAllArg, "all", "a", false, "Fetch all LFS files ever referenced")
	fetchCmd.Flags().BoolVarP(&fetchPruneArg, "prune", "p", false, "After fetching, prune old data")
	RootCmd.AddCommand(fetchCmd)
}

func pointersToFetchForRef(ref string) ([]*lfs.WrappedPointer, error) {
	// Use SkipDeletedBlobs to avoid fetching ALL previous versions of modified files
	opts := lfs.NewScanRefsOptions()
	opts.ScanMode = lfs.ScanRefsMode
	opts.SkipDeletedBlobs = true
	return lfs.ScanRefs(ref, "", opts)
}

func fetchRefToChan(ref string, include, exclude []string) chan *lfs.WrappedPointer {
	c := make(chan *lfs.WrappedPointer)
	pointers, err := pointersToFetchForRef(ref)
	if err != nil {
		Panic(err, "Could not scan for Git LFS files")
	}

	go fetchAndReportToChan(pointers, include, exclude, c)

	return c
}

// Fetch all binaries for a given ref (that we don't have already)
func fetchRef(ref string, include, exclude []string) bool {
	pointers, err := pointersToFetchForRef(ref)
	if err != nil {
		Panic(err, "Could not scan for Git LFS files")
	}
	return fetchPointers(pointers, include, exclude)
}

// Fetch all previous versions of objects from since to ref (not including final state at ref)
// So this will fetch all the '-' sides of the diff from since to ref
func fetchPreviousVersions(ref string, since time.Time, include, exclude []string) bool {
	pointers, err := lfs.ScanPreviousVersions(ref, since)
	if err != nil {
		Panic(err, "Could not scan for Git LFS previous versions")
	}
	return fetchPointers(pointers, include, exclude)
}

// Fetch recent objects based on config
func fetchRecent(alreadyFetchedRefs []*git.Ref, include, exclude []string) bool {
	fetchconf := config.Config.FetchPruneConfig()

	if fetchconf.FetchRecentRefsDays == 0 && fetchconf.FetchRecentCommitsDays == 0 {
		return true
	}

	ok := true
	// Make a list of what unique commits we've already fetched for to avoid duplicating work
	uniqueRefShas := make(map[string]string, len(alreadyFetchedRefs))
	for _, ref := range alreadyFetchedRefs {
		uniqueRefShas[ref.Sha] = ref.Name
	}
	// First find any other recent refs
	if fetchconf.FetchRecentRefsDays > 0 {
		Print("Fetching recent branches within %v days", fetchconf.FetchRecentRefsDays)
		refsSince := time.Now().AddDate(0, 0, -fetchconf.FetchRecentRefsDays)
		refs, err := git.RecentBranches(refsSince, fetchconf.FetchRecentRefsIncludeRemotes, config.Config.CurrentRemote)
		if err != nil {
			Panic(err, "Could not scan for recent refs")
		}
		for _, ref := range refs {
			// Don't fetch for the same SHA twice
			if prevRefName, ok := uniqueRefShas[ref.Sha]; ok {
				if ref.Name != prevRefName {
					tracerx.Printf("Skipping fetch for %v, already fetched via %v", ref.Name, prevRefName)
				}
			} else {
				uniqueRefShas[ref.Sha] = ref.Name
				Print("Fetching %v", ref.Name)
				k := fetchRef(ref.Sha, include, exclude)
				ok = ok && k
			}
		}
	}
	// For every unique commit we've fetched, check recent commits too
	if fetchconf.FetchRecentCommitsDays > 0 {
		for commit, refName := range uniqueRefShas {
			// We measure from the last commit at the ref
			summ, err := git.GetCommitSummary(commit)
			if err != nil {
				Error("Couldn't scan commits at %v: %v", refName, err)
				continue
			}
			Print("Fetching changes within %v days of %v", fetchconf.FetchRecentCommitsDays, refName)
			commitsSince := summ.CommitDate.AddDate(0, 0, -fetchconf.FetchRecentCommitsDays)
			k := fetchPreviousVersions(commit, commitsSince, include, exclude)
			ok = ok && k
		}

	}
	return ok
}

func fetchAll() bool {
	pointers := scanAll()
	Print("Fetching objects...")
	return fetchPointers(pointers, nil, nil)
}

func scanAll() []*lfs.WrappedPointer {
	// converts to `git rev-list --all`
	// We only pick up objects in real commits and not the reflog
	opts := lfs.NewScanRefsOptions()
	opts.ScanMode = lfs.ScanAllMode
	opts.SkipDeletedBlobs = false

	// This could be a long process so use the chan version & report progress
	Print("Scanning for all objects ever referenced...")
	spinner := progress.NewSpinner()
	var numObjs int64
	pointerchan, err := lfs.ScanRefsToChan("", "", opts)
	if err != nil {
		Panic(err, "Could not scan for Git LFS files")
	}

	pointers := make([]*lfs.WrappedPointer, 0)

	for p := range pointerchan.Results {
		numObjs++
		spinner.Print(OutputWriter, fmt.Sprintf("%d objects found", numObjs))
		pointers = append(pointers, p)
	}
	err = pointerchan.Wait()
	if err != nil {
		Panic(err, "Could not scan for Git LFS files")
	}

	spinner.Finish(OutputWriter, fmt.Sprintf("%d objects found", numObjs))
	return pointers
}

func fetchPointers(pointers []*lfs.WrappedPointer, include, exclude []string) bool {
	return fetchAndReportToChan(pointers, include, exclude, nil)
}

// Fetch and report completion of each OID to a channel (optional, pass nil to skip)
// Returns true if all completed with no errors, false if errors were written to stderr/log
func fetchAndReportToChan(pointers []*lfs.WrappedPointer, include, exclude []string, out chan<- *lfs.WrappedPointer) bool {
	totalSize := int64(0)
	for _, p := range pointers {
		totalSize += p.Size
	}
	q := lfs.NewDownloadQueue(len(pointers), totalSize, false)

	if out != nil {
		dlwatch := q.Watch()

		go func() {
			// fetch only reports single OID, but OID *might* be referenced by multiple
			// WrappedPointers if same content is at multiple paths, so map oid->slice
			oidToPointers := make(map[string][]*lfs.WrappedPointer, len(pointers))
			for _, pointer := range pointers {
				plist := oidToPointers[pointer.Oid]
				oidToPointers[pointer.Oid] = append(plist, pointer)
			}

			for oid := range dlwatch {
				plist, ok := oidToPointers[oid]
				if !ok {
					continue
				}
				for _, p := range plist {
					out <- p
				}
			}
			close(out)
		}()
	}

	for _, p := range pointers {
		// Only add to download queue if local file is not the right size already
		// This avoids previous case of over-reporting a requirement for files we already have
		// which would only be skipped by PointerSmudgeObject later
		passFilter := lfs.FilenamePassesIncludeExcludeFilter(p.Name, include, exclude)

		lfs.LinkOrCopyFromReference(p.Oid, p.Size)

		if !lfs.ObjectExistsOfSize(p.Oid, p.Size) && passFilter {
			tracerx.Printf("fetch %v [%v]", p.Name, p.Oid)
			q.Add(lfs.NewDownloadable(p))
		} else {
			// Ensure progress matches
			q.Skip(p.Size)
			if !passFilter {
				tracerx.Printf("Skipping %v [%v], include/exclude filters applied", p.Name, p.Oid)
			} else {
				tracerx.Printf("Skipping %v [%v], already exists", p.Name, p.Oid)
			}

			// If we already have it, or it won't be fetched
			// report it to chan immediately to support pull/checkout
			if out != nil {
				out <- p
			}

		}
	}

	processQueue := time.Now()
	q.Wait()
	tracerx.PerformanceSince("process queue", processQueue)

	ok := true
	for _, err := range q.Errors() {
		ok = false
		ExitWithError(err)
	}
	return ok
}
<|endoftext|>


<commit_before>package main

/**
 * CPU Information
 */

import (
	"fmt"
	"time"

	linuxproc "github.com/c9s/goprocinfo/linux"
	ui "github.com/gizak/termui"
)

////////////////////////////////////////////
// Widget: CPU
////////////////////////////////////////////

type CPUWidget struct {
	widget *ui.LineChart

	numProcessors      int
	lastStat           linuxproc.CPUStat
	curStat            linuxproc.CPUStat
	cpuPercent         float64
	loadLast1Min       []float64
	loadLast5Min       []float64
	timestamps         []string
	mostRecent1MinLoad float64
	mostRecent5MinLoad float64
}

func NewCPUWidget() *CPUWidget {
	// Create base element
	e := ui.NewLineChart()
	e.Height = 20
	e.Border = true
	e.PaddingTop = 1
	e.LineColor = ui.ColorBlue | ui.AttrBold
	e.AxesColor = ui.ColorYellow

	// Create widget
	w := &CPUWidget{
		widget:       e,
		cpuPercent:   0,
		loadLast1Min: make([]float64, 0),
		loadLast5Min: make([]float64, 0),
		timestamps:   make([]string, 0),
	}

	w.update()
	w.resize()

	return w
}

func (w *CPUWidget) getGridWidget() ui.GridBufferer {
	return w.widget
}

func (w *CPUWidget) update() {
	w.loadProcessorStats()

	loadPercent := float64(w.mostRecent5MinLoad) / float64(w.numProcessors)

	cpuColorString := percentToAttributeString(int(100.0*w.cpuPercent), 0, 100, true)

	loadColor := percentToAttribute(int(100.0*loadPercent), 0, 100, true)
	loadColorString := percentToAttributeString(int(100.0*loadPercent), 0, 100, true)

	w.widget.BorderLabel = fmt.Sprintf("[CPU: %0.2f%%](%s)[───](fg-white)[5m Load: %0.2f](%s)", w.cpuPercent*100, cpuColorString, w.mostRecent5MinLoad, loadColorString)
	w.widget.Data = w.loadLast1Min
	w.widget.DataLabels = w.timestamps

	// Adjust graph axes color by Load value (never bold)
	w.widget.AxesColor = loadColor

}

func (w *CPUWidget) resize() {
	// Update
}

func (w *CPUWidget) loadProcessorStats() {
	// Read /proc/stat for the overall CPU percentage
	stats, statErr := linuxproc.ReadStat("/proc/stat")

	if statErr == nil {
		// Save last two stats records
		w.lastStat = w.curStat
		w.curStat = stats.CPUStatAll
		w.numProcessors = len(stats.CPUStats)

		// Calculate usage percentage
		// from: https://stackoverflow.com/a/23376195

		prevIdle := w.lastStat.Idle + w.lastStat.IOWait
		curIdle := w.curStat.Idle + w.curStat.IOWait

		prevNonIdle := w.lastStat.User + w.lastStat.Nice + w.lastStat.System + w.lastStat.IRQ + w.lastStat.SoftIRQ + w.lastStat.Steal
		curNonIdle := w.curStat.User + w.curStat.Nice + w.curStat.System + w.curStat.IRQ + w.curStat.SoftIRQ + w.curStat.Steal

		prevTotal := prevIdle + prevNonIdle
		curTotal := curIdle + curNonIdle

		//  differentiate: actual value minus the previous one
		totald := curTotal - prevTotal
		idled := curIdle - prevIdle

		w.cpuPercent = (float64(totald - idled)) / float64(totald)
	}

	// Read load average
	loadavg, loadErr := linuxproc.ReadLoadAvg("/proc/loadavg")

	if loadErr == nil {
		w.mostRecent1MinLoad = loadavg.Last1Min
		w.mostRecent5MinLoad = loadavg.Last5Min
		now := time.Now()
		ts := fmt.Sprintf("%02d:%02d", now.Minute(), now.Second())

		// Record, keep a fixed number around
		if len(w.loadLast1Min) > (w.widget.Width * 2) {
			w.loadLast1Min = append(w.loadLast1Min[1:], loadavg.Last1Min)
		} else {
			w.loadLast1Min = append(w.loadLast1Min, loadavg.Last1Min)
		}

		if len(w.loadLast5Min) > (w.widget.Width * 2) {
			w.loadLast5Min = append(w.loadLast5Min[1:], loadavg.Last5Min)
		} else {
			w.loadLast5Min = append(w.loadLast5Min, loadavg.Last5Min)
		}

		if len(w.timestamps) > (w.widget.Width * 2) {
			w.timestamps = append(w.timestamps[1:], ts)
		} else {
			w.timestamps = append(w.timestamps, ts)
		}
	}
}
<commit_msg>Map to hour:minute instead of minute:second for better history.<commit_after>package main

/**
 * CPU Information
 */

import (
	"fmt"
	"time"

	linuxproc "github.com/c9s/goprocinfo/linux"
	ui "github.com/gizak/termui"
)

////////////////////////////////////////////
// Widget: CPU
////////////////////////////////////////////

type CPUWidget struct {
	widget *ui.LineChart

	numProcessors      int
	lastStat           linuxproc.CPUStat
	curStat            linuxproc.CPUStat
	cpuPercent         float64
	loadLast1Min       []float64
	loadLast5Min       []float64
	timestamps         []string
	mostRecent1MinLoad float64
	mostRecent5MinLoad float64
}

func NewCPUWidget() *CPUWidget {
	// Create base element
	e := ui.NewLineChart()
	e.Height = 20
	e.Border = true
	e.PaddingTop = 1
	e.LineColor = ui.ColorBlue | ui.AttrBold
	e.AxesColor = ui.ColorYellow

	// Create widget
	w := &CPUWidget{
		widget:       e,
		cpuPercent:   0,
		loadLast1Min: make([]float64, 0),
		loadLast5Min: make([]float64, 0),
		timestamps:   make([]string, 0),
	}

	w.update()
	w.resize()

	return w
}

func (w *CPUWidget) getGridWidget() ui.GridBufferer {
	return w.widget
}

func (w *CPUWidget) update() {
	w.loadProcessorStats()

	loadPercent := float64(w.mostRecent5MinLoad) / float64(w.numProcessors)

	cpuColorString := percentToAttributeString(int(100.0*w.cpuPercent), 0, 100, true)

	loadColor := percentToAttribute(int(100.0*loadPercent), 0, 100, true)
	loadColorString := percentToAttributeString(int(100.0*loadPercent), 0, 100, true)

	w.widget.BorderLabel = fmt.Sprintf("[CPU: %0.2f%%](%s)[───](fg-white)[5m Load: %0.2f](%s)", w.cpuPercent*100, cpuColorString, w.mostRecent5MinLoad, loadColorString)
	w.widget.Data = w.loadLast1Min
	w.widget.DataLabels = w.timestamps

	// Adjust graph axes color by Load value (never bold)
	w.widget.AxesColor = loadColor

}

func (w *CPUWidget) resize() {
	// Update
}

func (w *CPUWidget) loadProcessorStats() {
	// Read /proc/stat for the overall CPU percentage
	stats, statErr := linuxproc.ReadStat("/proc/stat")

	if statErr == nil {
		// Save last two stats records
		w.lastStat = w.curStat
		w.curStat = stats.CPUStatAll
		w.numProcessors = len(stats.CPUStats)

		// Calculate usage percentage
		// from: https://stackoverflow.com/a/23376195

		prevIdle := w.lastStat.Idle + w.lastStat.IOWait
		curIdle := w.curStat.Idle + w.curStat.IOWait

		prevNonIdle := w.lastStat.User + w.lastStat.Nice + w.lastStat.System + w.lastStat.IRQ + w.lastStat.SoftIRQ + w.lastStat.Steal
		curNonIdle := w.curStat.User + w.curStat.Nice + w.curStat.System + w.curStat.IRQ + w.curStat.SoftIRQ + w.curStat.Steal

		prevTotal := prevIdle + prevNonIdle
		curTotal := curIdle + curNonIdle

		//  differentiate: actual value minus the previous one
		totald := curTotal - prevTotal
		idled := curIdle - prevIdle

		w.cpuPercent = (float64(totald - idled)) / float64(totald)
	}

	// Read load average
	loadavg, loadErr := linuxproc.ReadLoadAvg("/proc/loadavg")

	if loadErr == nil {
		w.mostRecent1MinLoad = loadavg.Last1Min
		w.mostRecent5MinLoad = loadavg.Last5Min
		now := time.Now()
		ts := fmt.Sprintf("%02d:%02d", now.Hour(), now.Minute())

		// Record, keep a fixed number around
		if len(w.loadLast1Min) > (w.widget.Width * 2) {
			w.loadLast1Min = append(w.loadLast1Min[1:], loadavg.Last1Min)
		} else {
			w.loadLast1Min = append(w.loadLast1Min, loadavg.Last1Min)
		}

		if len(w.loadLast5Min) > (w.widget.Width * 2) {
			w.loadLast5Min = append(w.loadLast5Min[1:], loadavg.Last5Min)
		} else {
			w.loadLast5Min = append(w.loadLast5Min, loadavg.Last5Min)
		}

		if len(w.timestamps) > (w.widget.Width * 2) {
			w.timestamps = append(w.timestamps[1:], ts)
		} else {
			w.timestamps = append(w.timestamps, ts)
		}
	}
}
<|endoftext|>


<commit_before>package gotop

import (
	// "fmt"
	"strconv"
	"strings"
	"time"
)

var (
	totalCPUFile = "/proc/stat"
	cpuInfoFile  = "/proc/cpuinfo"
)

type CPUInfo struct {
	AverageUtilization float64
	CPUUtilization     []float64
}

func TotalCPU(done <-chan struct{}, delay time.Duration) (<-chan CPUInfo, <-chan error) {
	result := make(chan CPUInfo, 1)
	errc := make(chan error)
	var err error
	cleanup := func() {
		errc <- err
		close(errc)
		close(result)
	}
	go func() {
		defer cleanup()
		numberOfCpus := numberOfCpus()
		cpuInfo := CPUInfo{AverageUtilization: 0.0, CPUUtilization: make([]float64, numberOfCpus)}
		var cur, prev []string
		cur, err = readCPUFile()
		if err != nil {
			return
		}
		for {
			prev = cur
			time.Sleep(delay)
			cur, err = readCPUFile()
			if err != nil {
				return
			}
			cpuInfo.AverageUtilization = getStats(cur[0], prev[0])
			for i := 1; i <= numberOfCpus; i++ {
				cpuInfo.CPUUtilization[i-1] = getStats(cur[i], prev[i])
			}
			select {
			case result <- cpuInfo:
			case <-done:
				return
			}
		}
	}()
	return result, errc
}

func numberOfCpus() int {
	numberOfCpus := 0
	cpuStats, err := readCPUFile()
	if err != nil {
		return 0
	}
	for i := 0; i < len(cpuStats); i++ {
		if strings.Index(cpuStats[i], "cpu") == 0 {
			numberOfCpus = numberOfCpus + 1
		} else {
			break
		}
	}
	// The first line of the file is the average of all CPUs
	return numberOfCpus - 1
}

func getStats(current, previous string) float64 {
	// start :=
	prev, cur := strings.Split(previous[5:], " "), strings.Split(current[5:], " ")
	activeTime, idleTime := 0.0, 0.0
	for i := 0; i < len(cur); i++ {
		time1, _ := strconv.ParseFloat(prev[i], 32)
		time2, _ := strconv.ParseFloat(cur[i], 32)
		if i != 3 {
			activeTime = activeTime + time2 - time1
		} else {
			// Idle time is the fourth column
			idleTime = +time2 - time1
		}
	}
	activePercentage := 100 * activeTime / (activeTime + idleTime)
	// Return value is truncated to 2 places after decimal
	return float64(int(100*activePercentage)) / 100
}

func readCPUFile() ([]string, error) {
	snapshot, err := readFile(totalCPUFile)
	if err != nil {
		return make([]string, 0), err
	}
	return strings.Split(snapshot, "\n"), nil
}

func cpuModel() string {
	info, _ := readFile(cpuInfoFile)
	for _, line := range strings.Split(info, "\n") {
		splitline := strings.Split(line, ":")
		field := strings.Trim(splitline[0], " \t")
		if strings.EqualFold(field, "model name") {
			return strings.Trim(splitline[1], " \t")
		}
	}
	return ""
}
<commit_msg>Simplify TotalCPU() by extracting out getCPUInfo()<commit_after>package gotop

import (
	// "fmt"
	"strconv"
	"strings"
	"time"
)

var (
	totalCPUFile = "/proc/stat"
	cpuInfoFile  = "/proc/cpuinfo"
)

// Average utilization is the average of the elements of CPUUtilization.
// Each element of CPUUtilization corresponds to a CPU core.
type CPUInfo struct {
	AverageUtilization float64
	CPUUtilization     []float64
}

//
func TotalCPU(done <-chan struct{}, delay time.Duration) (<-chan CPUInfo, <-chan error) {
	result := make(chan CPUInfo, 1)
	errc := make(chan error)
	var err error
	cleanup := func() {
		errc <- err
		close(errc)
		close(result)
	}
	go func() {
		defer cleanup()
		var cur, prev []string
		for {
			prev = cur
			cur, err = readCPUFile()
			if err != nil {
				return
			}
			cpuInfo := getCPUInfo(prev, cur)
			select {
			case result <- cpuInfo:
			case <-done:
				return
			}
			time.Sleep(delay)
		}
	}()
	return result, errc
}

func getCPUInfo(prev, cur []string) CPUInfo {
	numberOfCpus := numberOfCpus()
	cpuInfo := CPUInfo{AverageUtilization: 0.0, CPUUtilization: make([]float64, numberOfCpus)}

	// This is the first time the query is happening
	if len(prev) == 0 {
		return cpuInfo
	}

	cpuInfo.AverageUtilization = getStats(cur[0], prev[0])
	for i := 1; i <= numberOfCpus; i++ {
		cpuInfo.CPUUtilization[i-1] = getStats(cur[i], prev[i])
	}
	return cpuInfo
}

func getStats(current, previous string) float64 {
	// Should replace hardcoded value 5. If cpu levels are absurdly high, its because of this
	prev, cur := strings.Split(previous[5:], " "), strings.Split(current[5:], " ")
	activeTime, idleTime := 0.0, 0.0
	for i := 0; i < len(cur); i++ {
		time1, _ := strconv.ParseFloat(prev[i], 32)
		time2, _ := strconv.ParseFloat(cur[i], 32)
		if i != 3 {
			activeTime = activeTime + time2 - time1
		} else {
			// Idle time is the fourth column
			idleTime = +time2 - time1
		}
	}
	activePercentage := 100 * activeTime / (activeTime + idleTime)
	// Return value is truncated to 2 places after decimal
	return float64(int(100*activePercentage)) / 100
}

func numberOfCpus() int {
	numberOfCpus := 0
	cpuStats, err := readCPUFile()
	if err != nil {
		return 0
	}
	for i := 0; i < len(cpuStats); i++ {
		if strings.Index(cpuStats[i], "cpu") == 0 {
			numberOfCpus = numberOfCpus + 1
		} else {
			break
		}
	}
	// The first line of the file is the average of all CPUs
	return numberOfCpus - 1
}

func readCPUFile() ([]string, error) {
	snapshot, err := readFile(totalCPUFile)
	if err != nil {
		return make([]string, 0), err
	}
	return strings.Split(snapshot, "\n"), nil
}

func cpuModel() string {
	info, _ := readFile(cpuInfoFile)
	for _, line := range strings.Split(info, "\n") {
		splitline := strings.Split(line, ":")
		field := strings.Trim(splitline[0], " \t")
		if strings.EqualFold(field, "model name") {
			return strings.Trim(splitline[1], " \t")
		}
	}
	return ""
}
<|endoftext|>


<commit_before>// Package i2c enables gophers i2c speaking ability.
package i2c

import (
	"fmt"
	"os"
	"reflect"
	"sync"
	"syscall"
	"time"
	"unsafe"
)

const (
	delay = 20

	slaveCmd = 0x0703
	rdrwCmd  = 0x0707

	I2C_M_RD = 0x0001
)

type Bus interface {
	ReadByte(addr byte) (value byte, err error)
	WriteByte(addr, value byte) error
	WriteBytes(addr byte, value []byte) error

	ReadFromReg(addr, reg byte, value []byte) (err error)
	ReadByteFromReg(addr, reg byte) (value byte, err error)
	ReadInt(addr, reg byte) (value int, err error)

	WriteToReg(addr, reg, value byte) error
}

type i2c_msg struct {
	addr  uint16
	flags uint16
	len   uint16
	buf   uintptr
}

type i2c_rdwr_ioctl_data struct {
	msgs uintptr
	nmsg uint32
}

var busMap map[byte]*bus
var busMapLock sync.Mutex
var Default Bus

type bus struct {
	file *os.File
	addr byte
	mu   sync.Mutex
}

func init() {
	busMap = make(map[byte]*bus)
	var err error
	Default, err = NewBus(1)
	if err != nil {
		panic(err)
	}
}

// NewBus creates a new I2C bus interface. The l variable
// controls which bus we connect to.
//
// For the newer RaspberryPi, the number is 1 (earlier model uses 0.)
func NewBus(l byte) (Bus, error) {
	busMapLock.Lock()
	defer busMapLock.Unlock()

	var b *bus

	if b = busMap[l]; b == nil {
		b = new(bus)
		var err error
		if b.file, err = os.OpenFile(fmt.Sprintf("/dev/i2c-%v", l), os.O_RDWR, os.ModeExclusive); err != nil {
			return nil, err
		}
		busMap[l] = b
	}

	return b, nil
}

func (b *bus) setAddress(addr byte) (err error) {
	if addr != b.addr {
		if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), slaveCmd, uintptr(addr)); errno != 0 {
			err = syscall.Errno(errno)
			return
		}

		b.addr = addr
	}

	return
}

// Read a byte from the given address.
func (b *bus) ReadByte(addr byte) (value byte, err error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err = b.setAddress(addr); err != nil {
		return
	}

	bytes := make([]byte, 1)
	n, err := b.file.Read(bytes)

	if n != 1 {
		err = fmt.Errorf("i2c: Unexpected number (%v) of bytes read", n)
	}

	value = bytes[0]

	return
}

// Write a byte to the given address.
func (b *bus) WriteByte(addr, value byte) (err error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err = b.setAddress(addr); err != nil {
		return
	}

	n, err := b.file.Write([]byte{value})

	if n != 1 {
		err = fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteByte", n)
	}

	return
}

// Write a bunch of bytes ot the given address.
func (b *bus) WriteBytes(addr byte, value []byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.setAddress(addr); err != nil {
		return err
	}

	for i := range value {
		n, err := b.file.Write([]byte{value[i]})

		if n != 1 {
			return fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteBytes", n)
		}
		if err != nil {
			return err
		}

		time.Sleep(delay * time.Millisecond)
	}

	return nil
}

// Read a bunch of bytes (len(value)) from the given address and register.
func (b *bus) ReadFromReg(addr, reg byte, value []byte) (err error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err = b.setAddress(addr); err != nil {
		return
	}

	hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&value))

	var messages [2]i2c_msg
	messages[0].addr = uint16(addr)
	messages[0].flags = 0
	messages[0].len = 1
	messages[0].buf = uintptr(unsafe.Pointer(&reg))

	messages[1].addr = uint16(addr)
	messages[1].flags = I2C_M_RD
	messages[1].len = uint16(len(value))
	messages[1].buf = uintptr(unsafe.Pointer(hdrp.Data))

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&messages))
	packets.nmsg = 2

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		return syscall.Errno(errno)
	}

	return nil
}

// Read a byte from the given address and register.
func (b *bus) ReadByteFromReg(addr, reg byte) (value byte, err error) {
	buf := make([]byte, 1)
	if err = b.ReadFromReg(addr, reg, buf); err != nil {
		return
	}
	value = buf[0]
	return
}

// Read a int from the given address and register.
func (b *bus) ReadInt(addr, reg byte) (value int, err error) {
	var buf = make([]byte, 2)
	if err = b.ReadFromReg(addr, reg, buf); err != nil {
		return
	}
	value = int((int(buf[0]) << 8) | int(buf[1]))
	return
}

// Write a byte to the given address and register.
func (b *bus) WriteToReg(addr, reg, value byte) (err error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err = b.setAddress(addr); err != nil {
		return
	}

	var outbuf [2]byte
	var messages i2c_msg
	messages.addr = uint16(addr)
	messages.flags = 0
	messages.len = uint16(len(outbuf))
	messages.buf = uintptr(unsafe.Pointer(&outbuf))

	outbuf[0] = reg
	outbuf[1] = value

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&messages))
	packets.nmsg = 1

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		err = syscall.Errno(errno)
		return
	}

	return
}

// Read a byte from the given address.
func ReadByte(addr byte) (value byte, err error) {
	return Default.ReadByte(addr)
}

// Write a byte to the given address.
func WriteByte(addr, value byte) (err error) {
	return Default.WriteByte(addr, value)
}

// Write a bunch of bytes ot the given address.
func WriteBytes(addr byte, value []byte) error {
	return Default.WriteBytes(addr, value)
}

// Read a bunch of bytes (len(value)) from the given address and register.
func ReadFromReg(addr, reg byte, value []byte) (err error) {
	return Default.ReadFromReg(addr, reg, value)
}

// Read a byte from the given address and register.
func ReadByteFromReg(addr, reg byte) (value byte, err error) {
	return Default.ReadByteFromReg(addr, reg)
}

// Read a int from the given address and register.
func ReadInt(addr, reg byte) (value int, err error) {
	return Default.ReadInt(addr, reg)
}

// Write a byte to the given address and register.
func WriteToReg(addr, reg, value byte) (err error) {
	return Default.WriteToReg(addr, reg, value)
}
<commit_msg>improve doc<commit_after>// Package i2c enables gophers i2c speaking ability.
package i2c

import (
	"fmt"
	"os"
	"reflect"
	"sync"
	"syscall"
	"time"
	"unsafe"
)

const (
	delay = 20

	slaveCmd = 0x0703 // Cmd to set slave address
	rdrwCmd  = 0x0707 // Cmd to read/write data together

	rd = 0x0001
)

type Bus interface {
	// Read a byte from the given address.
	ReadByte(addr byte) (value byte, err error)
	// Write a byte to the given address.
	WriteByte(addr, value byte) error
	// Write a bunch of bytes ot the given address.
	WriteBytes(addr byte, value []byte) error

	// Read a bunch of bytes (len(value)) from the given address and register.
	ReadFromReg(addr, reg byte, value []byte) (err error)
	// Read a byte from the given address and register.
	ReadByteFromReg(addr, reg byte) (value byte, err error)
	// Read a int from the given address and register.
	ReadInt(addr, reg byte) (value int, err error)

	// Write a byte to the given address and register.
	WriteToReg(addr, reg, value byte) error
}

type i2c_msg struct {
	addr  uint16
	flags uint16
	len   uint16
	buf   uintptr
}

type i2c_rdwr_ioctl_data struct {
	msgs uintptr
	nmsg uint32
}

var busMap map[byte]*bus
var busMapLock sync.Mutex

// Default instance of the i2c bus
var Default Bus

type bus struct {
	file *os.File
	addr byte
	mu   sync.Mutex
}

func init() {
	busMap = make(map[byte]*bus)
	var err error
	Default, err = NewBus(1)
	if err != nil {
		panic(err)
	}
}

// NewBus creates a new I2C bus interface. The l variable
// controls which bus we connect to.
//
// For the newer RaspberryPi, the number is 1 (earlier model uses 0.)
func NewBus(l byte) (Bus, error) {
	busMapLock.Lock()
	defer busMapLock.Unlock()

	var b *bus

	if b = busMap[l]; b == nil {
		b = new(bus)
		var err error
		if b.file, err = os.OpenFile(fmt.Sprintf("/dev/i2c-%v", l), os.O_RDWR, os.ModeExclusive); err != nil {
			return nil, err
		}
		busMap[l] = b
	}

	return b, nil
}

func (b *bus) setAddress(addr byte) (err error) {
	if addr != b.addr {
		if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), slaveCmd, uintptr(addr)); errno != 0 {
			err = syscall.Errno(errno)
			return
		}

		b.addr = addr
	}

	return
}

// Read a byte from the given address.
func (b *bus) ReadByte(addr byte) (value byte, err error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err = b.setAddress(addr); err != nil {
		return
	}

	bytes := make([]byte, 1)
	n, err := b.file.Read(bytes)

	if n != 1 {
		err = fmt.Errorf("i2c: Unexpected number (%v) of bytes read", n)
	}

	value = bytes[0]

	return
}

// Write a byte to the given address.
func (b *bus) WriteByte(addr, value byte) (err error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err = b.setAddress(addr); err != nil {
		return
	}

	n, err := b.file.Write([]byte{value})

	if n != 1 {
		err = fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteByte", n)
	}

	return
}

// Write a bunch of bytes ot the given address.
func (b *bus) WriteBytes(addr byte, value []byte) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err := b.setAddress(addr); err != nil {
		return err
	}

	for i := range value {
		n, err := b.file.Write([]byte{value[i]})

		if n != 1 {
			return fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteBytes", n)
		}
		if err != nil {
			return err
		}

		time.Sleep(delay * time.Millisecond)
	}

	return nil
}

// Read a bunch of bytes (len(value)) from the given address and register.
func (b *bus) ReadFromReg(addr, reg byte, value []byte) (err error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err = b.setAddress(addr); err != nil {
		return
	}

	hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&value))

	var messages [2]i2c_msg
	messages[0].addr = uint16(addr)
	messages[0].flags = 0
	messages[0].len = 1
	messages[0].buf = uintptr(unsafe.Pointer(&reg))

	messages[1].addr = uint16(addr)
	messages[1].flags = rd
	messages[1].len = uint16(len(value))
	messages[1].buf = uintptr(unsafe.Pointer(hdrp.Data))

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&messages))
	packets.nmsg = 2

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		return syscall.Errno(errno)
	}

	return nil
}

// Read a byte from the given address and register.
func (b *bus) ReadByteFromReg(addr, reg byte) (value byte, err error) {
	buf := make([]byte, 1)
	if err = b.ReadFromReg(addr, reg, buf); err != nil {
		return
	}
	value = buf[0]
	return
}

// Read a int from the given address and register.
func (b *bus) ReadInt(addr, reg byte) (value int, err error) {
	var buf = make([]byte, 2)
	if err = b.ReadFromReg(addr, reg, buf); err != nil {
		return
	}
	value = int((int(buf[0]) << 8) | int(buf[1]))
	return
}

// Write a byte to the given address and register.
func (b *bus) WriteToReg(addr, reg, value byte) (err error) {
	b.mu.Lock()
	defer b.mu.Unlock()

	if err = b.setAddress(addr); err != nil {
		return
	}

	var outbuf [2]byte
	var messages i2c_msg
	messages.addr = uint16(addr)
	messages.flags = 0
	messages.len = uint16(len(outbuf))
	messages.buf = uintptr(unsafe.Pointer(&outbuf))

	outbuf[0] = reg
	outbuf[1] = value

	var packets i2c_rdwr_ioctl_data

	packets.msgs = uintptr(unsafe.Pointer(&messages))
	packets.nmsg = 1

	if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 {
		err = syscall.Errno(errno)
		return
	}

	return
}

// Read a byte from the given address.
func ReadByte(addr byte) (value byte, err error) {
	return Default.ReadByte(addr)
}

// Write a byte to the given address.
func WriteByte(addr, value byte) (err error) {
	return Default.WriteByte(addr, value)
}

// Write a bunch of bytes ot the given address.
func WriteBytes(addr byte, value []byte) error {
	return Default.WriteBytes(addr, value)
}

// Read a bunch of bytes (len(value)) from the given address and register.
func ReadFromReg(addr, reg byte, value []byte) (err error) {
	return Default.ReadFromReg(addr, reg, value)
}

// Read a byte from the given address and register.
func ReadByteFromReg(addr, reg byte) (value byte, err error) {
	return Default.ReadByteFromReg(addr, reg)
}

// Read a int from the given address and register.
func ReadInt(addr, reg byte) (value int, err error) {
	return Default.ReadInt(addr, reg)
}

// Write a byte to the given address and register.
func WriteToReg(addr, reg, value byte) (err error) {
	return Default.WriteToReg(addr, reg, value)
}
<|endoftext|>