<commit_before>package caches

import (
	"fmt"
	"github.com/lpabon/godbc"
	"strconv"
)

type Cache struct {
	cacheobjids              map[string]string
	cachemap                 map[string]int
	cachesize                uint64
	readhits, writehits      int
	reads, writes            int
	deletions, deletionhits  int
	evictions, invalidations int
	insertions               int
	writethrough             bool
}

func cacheCreateObjKey(obj string) func() string {
	counter := 0
	return func() string {
		godbc.Require(counter >= 0)

		counter += 1
		return strconv.Itoa(counter)
	}
}

func NewCache(cachesize uint64, writethrough bool) *Cache {

	godbc.Require(cachesize > 0)

	cache := &Cache{}
	cache.cachesize = cachesize
	cache.writethrough = writethrough
	cache.cacheobjids = make(map[string]string)
	cache.cachemap = make(map[string]int)

	godbc.Ensure(cache.cacheobjids != nil)
	godbc.Ensure(cache.cachemap != nil)
	godbc.Ensure(cache.cachesize > 0)

	return cache
}

func (c *Cache) getObjKey(obj string) string {
	if val, ok := c.cacheobjids[obj]; ok {
		return val
	} else {
		newid := cacheCreateObjKey(obj)
		c.cacheobjids[obj] = newid()
		return c.cacheobjids[obj]
	}
}

func (c *Cache) Copy() *Cache {
	cachecopy := &Cache{}
	*cachecopy = *c

	return cachecopy
}

func (c *Cache) Invalidate(chunkkey string) {
	if _, ok := c.cachemap[chunkkey]; ok {
		c.writehits++
		c.invalidations++
		delete(c.cachemap, chunkkey)
	}
}

func (c *Cache) Evict() {
	c.evictions++

	// BIG ASSUMPTION! I have no idea
	// if Go keeps track of the iteration
	// through a map
	for {
		for key, val := range c.cachemap {
			if val == 1 {

				// Clock Algorithm: We looked at it
				// and set to zero for next time
				c.cachemap[key] = 0
			} else {
				delete(c.cachemap, key)
				return
			}
		}
	}
}

func (c *Cache) Insert(chunkkey string) {
	c.insertions++

	if uint64(len(c.cachemap)) > c.cachesize {
		c.Evict()
	}

	c.cachemap[chunkkey] = 1
}

func (c *Cache) Write(obj string, chunk string) {
	c.writes++

	key := c.getObjKey(obj) + chunk

	// Invalidate
	c.Invalidate(key)

	// We would do back end IO here

	// Insert
	if c.writethrough {
		c.Insert(key)
	}
}

func (c *Cache) Read(obj, chunk string) {
	c.reads++

	key := c.getObjKey(obj) + chunk

	if _, ok := c.cachemap[key]; ok {
		// Read Hit
		c.readhits++

		// Clock Algorithm: Set that we looked
		// at it
		c.cachemap[key] = 1
	} else {
		// Read miss
		// We would do IO here
		c.Insert(key)
	}
}

func (c *Cache) Delete(obj string) {
	c.deletions++

	if _, ok := c.cacheobjids[obj]; ok {
		c.deletionhits++
		delete(c.cacheobjids, obj)
	}
}

func (c *Cache) ReadHitRate() float64 {
	if c.reads == 0 {
		return 0.0
	} else {
		return float64(c.readhits) / float64(c.reads)
	}
}

func (c *Cache) WriteHitRate() float64 {
	if c.writes == 0 {
		return 0.0
	} else {
		return float64(c.writehits) / float64(c.writes)
	}

}

func (c *Cache) ReadHitRateDelta(prev *Cache) float64 {
	reads := c.reads - prev.reads
	readhits := c.readhits - prev.readhits
	if reads == 0 {
		return 0.0
	} else {
		return float64(readhits) / float64(reads)
	}
}

func (c *Cache) WriteHitRateDelta(prev *Cache) float64 {
	writes := c.writes - prev.writes
	writehits := c.writehits - prev.writehits
	if writes == 0 {
		return 0.0
	} else {
		return float64(writehits) / float64(writes)
	}

}

func (c *Cache) String() string {
	return fmt.Sprintf(
		"Cache Information:\n"+
			"Read Hit Rate: %v\n"+
			"Write Hit Rate: %v\n"+
			"Read hits: %d\n"+
			"Write hits: %d\n"+
			"Delete hits: %d\n"+
			"Reads: %d\n"+
			"Writes: %d\n"+
			"Deletions: %d\n"+
			"Insertions: %d\n"+
			"Evictions: %d\n"+
			"Invalidations: %d\n",
		c.ReadHitRate(),
		c.WriteHitRate(),
		c.readhits,
		c.writehits,
		c.deletionhits,
		c.reads,
		c.writes,
		c.deletions,
		c.insertions,
		c.evictions,
		c.invalidations)
}

func (c *Cache) Dump() string {
	return fmt.Sprintf(
		"%v,"+ // Read Hit Rate 1
			"%v,"+ // Write Hit Rate 2
			"%d,"+ // Read Hits 3
			"%d,"+ // Write Hits 4
			"%d,"+ // Deletion Hits 5
			"%d,"+ // Reads 6
			"%d,"+ // Writes 7
			"%d,"+ // Deletions 8
			"%d,"+ // Insertions 9
			"%d,"+ // Evictions 10
			"%d\n", // Invalidations 11
		c.ReadHitRate(),
		c.WriteHitRate(),
		c.readhits,
		c.writehits,
		c.deletionhits,
		c.reads,
		c.writes,
		c.deletions,
		c.insertions,
		c.evictions,
		c.invalidations)

}

func (c *Cache) DumpDelta(prev *Cache) string {
	return fmt.Sprintf(
		"%v,"+ // Read Hit Rate 1
			"%v,"+ // Write Hit Rate 2
			"%d,"+ // Read Hits 3
			"%d,"+ // Write Hits 4
			"%d,"+ // Deletion Hits 5
			"%d,"+ // Reads 6
			"%d,"+ // Writes 7
			"%d,"+ // Deletions 8
			"%d,"+ // Insertions 9
			"%d,"+ // Evictions 10
			"%d\n", // Invalidations 11
		c.ReadHitRateDelta(prev),
		c.WriteHitRateDelta(prev),
		c.readhits-prev.readhits,
		c.writehits-prev.writehits,
		c.deletionhits-prev.deletionhits,
		c.reads-prev.reads,
		c.writes-prev.writes,
		c.deletions-prev.deletions,
		c.insertions-prev.insertions,
		c.evictions-prev.evictions,
		c.invalidations-prev.invalidations)

}
<commit_msg>Added cache utilization<commit_after>package caches

import (
	"fmt"
	"github.com/lpabon/godbc"
	"strconv"
)

type Cache struct {
	cacheobjids              map[string]string
	cachemap                 map[string]int
	cachesize                uint64
	readhits, writehits      int
	reads, writes            int
	deletions, deletionhits  int
	evictions, invalidations int
	insertions               int
	writethrough             bool
}

func cacheCreateObjKey(obj string) func() string {
	counter := 0
	return func() string {
		godbc.Require(counter >= 0)

		counter += 1
		return strconv.Itoa(counter)
	}
}

func NewCache(cachesize uint64, writethrough bool) *Cache {

	godbc.Require(cachesize > 0)

	cache := &Cache{}
	cache.cachesize = cachesize
	cache.writethrough = writethrough
	cache.cacheobjids = make(map[string]string)
	cache.cachemap = make(map[string]int)

	godbc.Ensure(cache.cacheobjids != nil)
	godbc.Ensure(cache.cachemap != nil)
	godbc.Ensure(cache.cachesize > 0)

	return cache
}

func (c *Cache) getObjKey(obj string) string {
	if val, ok := c.cacheobjids[obj]; ok {
		return val
	} else {
		newid := cacheCreateObjKey(obj)
		c.cacheobjids[obj] = newid()
		return c.cacheobjids[obj]
	}
}

func (c *Cache) Copy() *Cache {
	cachecopy := &Cache{}
	*cachecopy = *c

	return cachecopy
}

func (c *Cache) Invalidate(chunkkey string) {
	if _, ok := c.cachemap[chunkkey]; ok {
		c.writehits++
		c.invalidations++
		delete(c.cachemap, chunkkey)
	}
}

func (c *Cache) Evict() {
	c.evictions++

	// BIG ASSUMPTION! I have no idea
	// if Go keeps track of the iteration
	// through a map
	for {
		for key, val := range c.cachemap {
			if val == 1 {

				// Clock Algorithm: We looked at it
				// and set to zero for next time
				c.cachemap[key] = 0
			} else {
				delete(c.cachemap, key)
				return
			}
		}
	}
}

func (c *Cache) Insert(chunkkey string) {
	c.insertions++

	if uint64(len(c.cachemap)) > c.cachesize {
		c.Evict()
	}

	c.cachemap[chunkkey] = 1
}

func (c *Cache) Write(obj string, chunk string) {
	c.writes++

	key := c.getObjKey(obj) + chunk

	// Invalidate
	c.Invalidate(key)

	// We would do back end IO here

	// Insert
	if c.writethrough {
		c.Insert(key)
	}
}

func (c *Cache) Read(obj, chunk string) {
	c.reads++

	key := c.getObjKey(obj) + chunk

	if _, ok := c.cachemap[key]; ok {
		// Read Hit
		c.readhits++

		// Clock Algorithm: Set that we looked
		// at it
		c.cachemap[key] = 1
	} else {
		// Read miss
		// We would do IO here
		c.Insert(key)
	}
}

func (c *Cache) Delete(obj string) {
	c.deletions++

	if _, ok := c.cacheobjids[obj]; ok {
		c.deletionhits++
		delete(c.cacheobjids, obj)
	}
}

func (c *Cache) ReadHitRate() float64 {
	if c.reads == 0 {
		return 0.0
	} else {
		return float64(c.readhits) / float64(c.reads)
	}
}

func (c *Cache) WriteHitRate() float64 {
	if c.writes == 0 {
		return 0.0
	} else {
		return float64(c.writehits) / float64(c.writes)
	}

}

func (c *Cache) ReadHitRateDelta(prev *Cache) float64 {
	reads := c.reads - prev.reads
	readhits := c.readhits - prev.readhits
	if reads == 0 {
		return 0.0
	} else {
		return float64(readhits) / float64(reads)
	}
}

func (c *Cache) WriteHitRateDelta(prev *Cache) float64 {
	writes := c.writes - prev.writes
	writehits := c.writehits - prev.writehits
	if writes == 0 {
		return 0.0
	} else {
		return float64(writehits) / float64(writes)
	}

}

func (c *Cache) String() string {
	return fmt.Sprintf(
		"== Cache Information ==\n"+
			"Cache Utilization: %v\n"+
			"Read Hit Rate: %v\n"+
			"Write Hit Rate: %v\n"+
			"Read hits: %d\n"+
			"Write hits: %d\n"+
			"Delete hits: %d\n"+
			"Reads: %d\n"+
			"Writes: %d\n"+
			"Deletions: %d\n"+
			"Insertions: %d\n"+
			"Evictions: %d\n"+
			"Invalidations: %d\n",
		float64(len(c.cachemap))/float64(c.cachesize),
		c.ReadHitRate(),
		c.WriteHitRate(),
		c.readhits,
		c.writehits,
		c.deletionhits,
		c.reads,
		c.writes,
		c.deletions,
		c.insertions,
		c.evictions,
		c.invalidations)
}

func (c *Cache) Dump() string {
	return fmt.Sprintf(
		"%v,"+ // Read Hit Rate 1
			"%v,"+ // Write Hit Rate 2
			"%d,"+ // Read Hits 3
			"%d,"+ // Write Hits 4
			"%d,"+ // Deletion Hits 5
			"%d,"+ // Reads 6
			"%d,"+ // Writes 7
			"%d,"+ // Deletions 8
			"%d,"+ // Insertions 9
			"%d,"+ // Evictions 10
			"%d\n", // Invalidations 11
		c.ReadHitRate(),
		c.WriteHitRate(),
		c.readhits,
		c.writehits,
		c.deletionhits,
		c.reads,
		c.writes,
		c.deletions,
		c.insertions,
		c.evictions,
		c.invalidations)

}

func (c *Cache) DumpDelta(prev *Cache) string {
	return fmt.Sprintf(
		"%v,"+ // Read Hit Rate 1
			"%v,"+ // Write Hit Rate 2
			"%d,"+ // Read Hits 3
			"%d,"+ // Write Hits 4
			"%d,"+ // Deletion Hits 5
			"%d,"+ // Reads 6
			"%d,"+ // Writes 7
			"%d,"+ // Deletions 8
			"%d,"+ // Insertions 9
			"%d,"+ // Evictions 10
			"%d\n", // Invalidations 11
		c.ReadHitRateDelta(prev),
		c.WriteHitRateDelta(prev),
		c.readhits-prev.readhits,
		c.writehits-prev.writehits,
		c.deletionhits-prev.deletionhits,
		c.reads-prev.reads,
		c.writes-prev.writes,
		c.deletions-prev.deletions,
		c.insertions-prev.insertions,
		c.evictions-prev.evictions,
		c.invalidations-prev.invalidations)

}
<|endoftext|>


<commit_before>/*
    Copyright (c) 2016 VMware, 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 utils

import (
	"fmt"

	"github.com/vmware/harbor/job/config"
	"github.com/vmware/harbor/utils/log"
	"os"
	"path/filepath"
)

// NewLogger create a logger for a speicified job
func NewLogger(jobID int64) *log.Logger {
	logFile := GetJobLogPath(jobID)
	f, err := os.OpenFile(logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		log.Errorf("Failed to open log file %s, the log of job %d will be printed to standard output", logFile, jobID)
		f = os.Stdout
	}
	return log.New(f, log.NewTextFormatter(), log.InfoLevel)
}

// GetJobLogPath returns the absolute path in which the job log file is located.
func GetJobLogPath(jobID int64) string {
	fn := fmt.Sprintf("job_%d.log", jobID)
	return filepath.Join(config.LogDir(), fn)
}
<commit_msg>Introduce hierachy of directories to store job log file<commit_after>/*
   Copyright (c) 2016 VMware, 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 utils

import (
	"fmt"

	"github.com/vmware/harbor/job/config"
	"github.com/vmware/harbor/utils/log"
	"os"
	"path/filepath"
	"strconv"
)

// NewLogger create a logger for a speicified job
func NewLogger(jobID int64) *log.Logger {
	logFile := GetJobLogPath(jobID)
	d := filepath.Dir(logFile)
	if _, err := os.Stat(d); os.IsNotExist(err) {
		err := os.MkdirAll(d, 0660)
		if err != nil {
			log.Errorf("Failed to create directory for log file %s, the error: %v", logFile, err)
		}
	}
	f, err := os.OpenFile(logFile, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
	if err != nil {
		log.Errorf("Failed to open log file %s, the log of job %d will be printed to standard output, the error: %v", logFile, jobID, err)
		f = os.Stdout
	}
	return log.New(f, log.NewTextFormatter(), log.InfoLevel)
}

// GetJobLogPath returns the absolute path in which the job log file is located.
func GetJobLogPath(jobID int64) string {
	f := fmt.Sprintf("job_%d.log", jobID)
	k := jobID / 1000
	p := ""
	var d string
	for k > 0 {
		d = strconv.FormatInt(k%1000, 10)
		k = k / 1000
		if k > 0 && len(d) == 1 {
			d = "00" + d
		}
		if k > 0 && len(d) == 2 {
			d = "0" + d
		}

		p = filepath.Join(d, p)
	}
	p = filepath.Join(config.LogDir(), p, f)
	return p
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"sync"
	"time"

	"golang.org/x/net/context"

	pubsub "google.golang.org/api/pubsub/v1"
)

type (
	Puller interface {
		Pull(subscription string, pullrequest *pubsub.PullRequest) (*pubsub.PullResponse, error)
		Acknowledge(subscription, ackId string) (*pubsub.Empty, error)
		ModifyAckDeadline(subscription string, ackIds []string, ackDeadlineSeconds int64) (*pubsub.Empty, error)
	}

	pubsubPuller struct {
		subscriptionsService *pubsub.ProjectsSubscriptionsService
	}
)

func (pp *pubsubPuller) Pull(subscription string, pullrequest *pubsub.PullRequest) (*pubsub.PullResponse, error) {
	return pp.subscriptionsService.Pull(subscription, pullrequest).Do()
}

func (pp *pubsubPuller) Acknowledge(subscription, ackId string) (*pubsub.Empty, error) {
	ackRequest := &pubsub.AcknowledgeRequest{
		AckIds: []string{ackId},
	}
	return pp.subscriptionsService.Acknowledge(subscription, ackRequest).Do()
}

func (pp *pubsubPuller) ModifyAckDeadline(subscription string, ackIds []string, ackDeadlineSeconds int64) (*pubsub.Empty, error) {
	req := &pubsub.ModifyAckDeadlineRequest{
		AckDeadlineSeconds: ackDeadlineSeconds,
		AckIds:             ackIds,
	}
	return pp.subscriptionsService.ModifyAckDeadline(subscription, req).Do()
}

type (
	JobSustainerConfig struct {
		Delay    float64 `json:"delay,omitempty"`
		Interval float64 `json:"interval,omitempty"`
	}

	JobConfig struct {
		Subscription string              `json:"subscription,omitempty"`
		PullInterval int                 `json:"pull_interval,omitempty"`
		Sustainer    *JobSustainerConfig `json:"sustainer,omitempty"`
	}

	JobSubStatus uint8

	JobSubscription struct {
		config *JobConfig
		puller Puller
		status JobSubStatus
		mux    sync.Mutex
	}
)

const (
	initial JobSubStatus = iota
	running
	done
	acked
)

func (s *JobSubscription) listen(ctx context.Context, f func(msg *pubsub.ReceivedMessage) error) error {
	for {
		err := s.process(ctx, f)
		if err != nil {
			return err
		}
		time.Sleep(time.Duration(s.config.PullInterval) * time.Second)
	}
	return nil
}

func (s *JobSubscription) process(ctx context.Context, f func(msg *pubsub.ReceivedMessage) error) error {
	msg, err := s.waitForMessage(ctx)
	if err != nil {
		return err
	}
	if msg == nil {
		return nil
	}

	s.status = running

	go s.sendMADPeriodically(msg.AckId)

	err = f(msg)
	s.status = done

	if err != nil {
		return err
	}

	s.mux.Lock()
	defer s.mux.Unlock()

	_, err = s.puller.Acknowledge(s.config.Subscription, msg.AckId)
	if err != nil {
		log.Fatalf("Failed to acknowledge for message: %v cause of %v\n", msg, err)
		return err
	}

	s.status = acked

	return nil
}

func (s *JobSubscription) running() bool {
	return s.status == running
}

func (s *JobSubscription) sendMADPeriodically(ackId string) error {
	for {
		nextLimit := time.Now().Add(time.Duration(s.config.Sustainer.Interval) * time.Second)
		err := s.waitAndSendMAD(nextLimit, ackId)
		if err != nil {
			return err
		}
		if !s.running() {
			return nil
		}
	}
	// return nil
}

func (s *JobSubscription) waitAndSendMAD(nextLimit time.Time, ackId string) error {
	ticker := time.NewTicker(100 * time.Millisecond)
	for now := range ticker.C {
		if !s.running() {
			ticker.Stop()
			return nil
		}
		if now.After(nextLimit) {
			ticker.Stop()
		}
	}

	s.mux.Lock()
	defer s.mux.Unlock()

	// Don't send MAD after sending ACK
	if s.status == acked {
		return nil
	}

	_, err := s.puller.ModifyAckDeadline(s.config.Subscription, []string{ackId}, int64(s.config.Sustainer.Delay))
	if err != nil {
		log.Fatalf("Failed modifyAckDeadline %v, %v, %v cause of %v\n", s.config.Subscription, ackId, s.config.Sustainer.Delay, err)
	}
	return nil
}

func (s *JobSubscription) waitForMessage(ctx context.Context) (*pubsub.ReceivedMessage, error) {
	pullRequest := &pubsub.PullRequest{
		ReturnImmediately: false,
		MaxMessages:       1,
	}
	res, err := s.puller.Pull(s.config.Subscription, pullRequest)
	if err != nil {
		log.Printf("Failed to pull %v cause of %v\n", s.config.Subscription, err)
		return nil, err
	}
	if len(res.ReceivedMessages) == 0 {
		return nil, nil
	}
	return res.ReceivedMessages[0], nil
}
<commit_msg>:recycle: Move waitForMessage to the bottom of `process`<commit_after>package main

import (
	"log"
	"sync"
	"time"

	"golang.org/x/net/context"

	pubsub "google.golang.org/api/pubsub/v1"
)

type (
	Puller interface {
		Pull(subscription string, pullrequest *pubsub.PullRequest) (*pubsub.PullResponse, error)
		Acknowledge(subscription, ackId string) (*pubsub.Empty, error)
		ModifyAckDeadline(subscription string, ackIds []string, ackDeadlineSeconds int64) (*pubsub.Empty, error)
	}

	pubsubPuller struct {
		subscriptionsService *pubsub.ProjectsSubscriptionsService
	}
)

func (pp *pubsubPuller) Pull(subscription string, pullrequest *pubsub.PullRequest) (*pubsub.PullResponse, error) {
	return pp.subscriptionsService.Pull(subscription, pullrequest).Do()
}

func (pp *pubsubPuller) Acknowledge(subscription, ackId string) (*pubsub.Empty, error) {
	ackRequest := &pubsub.AcknowledgeRequest{
		AckIds: []string{ackId},
	}
	return pp.subscriptionsService.Acknowledge(subscription, ackRequest).Do()
}

func (pp *pubsubPuller) ModifyAckDeadline(subscription string, ackIds []string, ackDeadlineSeconds int64) (*pubsub.Empty, error) {
	req := &pubsub.ModifyAckDeadlineRequest{
		AckDeadlineSeconds: ackDeadlineSeconds,
		AckIds:             ackIds,
	}
	return pp.subscriptionsService.ModifyAckDeadline(subscription, req).Do()
}

type (
	JobSustainerConfig struct {
		Delay    float64 `json:"delay,omitempty"`
		Interval float64 `json:"interval,omitempty"`
	}

	JobConfig struct {
		Subscription string              `json:"subscription,omitempty"`
		PullInterval int                 `json:"pull_interval,omitempty"`
		Sustainer    *JobSustainerConfig `json:"sustainer,omitempty"`
	}

	JobSubStatus uint8

	JobSubscription struct {
		config *JobConfig
		puller Puller
		status JobSubStatus
		mux    sync.Mutex
	}
)

const (
	initial JobSubStatus = iota
	running
	done
	acked
)

func (s *JobSubscription) listen(ctx context.Context, f func(msg *pubsub.ReceivedMessage) error) error {
	for {
		err := s.process(ctx, f)
		if err != nil {
			return err
		}
		time.Sleep(time.Duration(s.config.PullInterval) * time.Second)
	}
	return nil
}

func (s *JobSubscription) process(ctx context.Context, f func(msg *pubsub.ReceivedMessage) error) error {
	msg, err := s.waitForMessage(ctx)
	if err != nil {
		return err
	}
	if msg == nil {
		return nil
	}

	s.status = running

	go s.sendMADPeriodically(msg.AckId)

	err = f(msg)
	s.status = done

	if err != nil {
		return err
	}

	s.mux.Lock()
	defer s.mux.Unlock()

	_, err = s.puller.Acknowledge(s.config.Subscription, msg.AckId)
	if err != nil {
		log.Fatalf("Failed to acknowledge for message: %v cause of %v\n", msg, err)
		return err
	}

	s.status = acked

	return nil
}

func (s *JobSubscription) waitForMessage(ctx context.Context) (*pubsub.ReceivedMessage, error) {
	pullRequest := &pubsub.PullRequest{
		ReturnImmediately: false,
		MaxMessages:       1,
	}
	res, err := s.puller.Pull(s.config.Subscription, pullRequest)
	if err != nil {
		log.Printf("Failed to pull %v cause of %v\n", s.config.Subscription, err)
		return nil, err
	}
	if len(res.ReceivedMessages) == 0 {
		return nil, nil
	}
	return res.ReceivedMessages[0], nil
}

func (s *JobSubscription) running() bool {
	return s.status == running
}

func (s *JobSubscription) sendMADPeriodically(ackId string) error {
	for {
		nextLimit := time.Now().Add(time.Duration(s.config.Sustainer.Interval) * time.Second)
		err := s.waitAndSendMAD(nextLimit, ackId)
		if err != nil {
			return err
		}
		if !s.running() {
			return nil
		}
	}
	// return nil
}

func (s *JobSubscription) waitAndSendMAD(nextLimit time.Time, ackId string) error {
	ticker := time.NewTicker(100 * time.Millisecond)
	for now := range ticker.C {
		if !s.running() {
			ticker.Stop()
			return nil
		}
		if now.After(nextLimit) {
			ticker.Stop()
		}
	}

	s.mux.Lock()
	defer s.mux.Unlock()

	// Don't send MAD after sending ACK
	if s.status == acked {
		return nil
	}

	_, err := s.puller.ModifyAckDeadline(s.config.Subscription, []string{ackId}, int64(s.config.Sustainer.Delay))
	if err != nil {
		log.Fatalf("Failed modifyAckDeadline %v, %v, %v cause of %v\n", s.config.Subscription, ackId, s.config.Sustainer.Delay, err)
	}
	return nil
}
<|endoftext|>


<commit_before>package busetabot

import (
	"context"
	"encoding/json"
	"fmt"
	"sync"

	"github.com/pkg/errors"
	"github.com/yi-jiayu/telegram-bot-api"
	"google.golang.org/appengine"
	"google.golang.org/appengine/log"

	"github.com/yi-jiayu/bus-eta-bot/v4/telegram"
)

var callbackQueryHandlers = map[string]CallbackQueryHandler{
	"refresh":  RefreshCallbackHandler,
	"resend":   NewEtaHandler,
	"eta":      EtaCallbackHandler,
	"eta_demo": EtaDemoCallbackHandler,
	"new_eta":  NewEtaHandler,
	"addf":     ToggleFavouritesHandler,
	"togf":     ToggleFavouritesHandler,
}

// CallbackQueryHandler is a handler for callback queries
type CallbackQueryHandler func(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response)

func answerCallbackQuery(bot *BusEtaBot, c tgbotapi.Chattable, answer tgbotapi.CallbackConfig) error {
	errs := make([]error, 0)

	var wg sync.WaitGroup
	wg.Add(2)

	errChan := make(chan error, 2)
	go func() {
		for err := range errChan {
			errs = append(errs, err)
		}
	}()

	go func() {
		defer wg.Done()

		_, err := bot.Telegram.Send(c)
		if err != nil {
			err = errors.Wrap(err, fmt.Sprintf("%#v", c))
			errChan <- err
		}
	}()

	go func() {
		defer wg.Done()

		_, err := bot.Telegram.AnswerCallbackQuery(answer)
		if err != nil {
			err = errors.Wrap(err, fmt.Sprintf("%#v", answer))
			errChan <- err
		}
	}()

	wg.Wait()
	close(errChan)

	switch len(errs) {
	case 1:
		return errs[0]
	case 2:
		return fmt.Errorf("%v\n%v", errs[0], errs[1])
	default:
		return nil
	}
}

func updateETAMessage(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, code string, services []string, responses chan<- Response) {
	text, err := ETAMessageText(bot.BusStops, bot.Datamall, SummaryETAFormatter{}, bot.NowFunc(), code, services)
	if err != nil {
		responses <- notOk(err)
		return
	}
	editMessageTextRequest := telegram.EditMessageTextRequest{
		Text:      text,
		ParseMode: "markdown",
	}
	if cbq.InlineMessageID != "" {
		editMessageTextRequest.InlineMessageID = cbq.InlineMessageID
		editMessageTextRequest.ReplyMarkup = NewETAMessageReplyMarkup(code, services, true)
	} else {
		editMessageTextRequest.ChatID = cbq.Message.Chat.ID
		editMessageTextRequest.MessageID = cbq.Message.MessageID
		editMessageTextRequest.ReplyMarkup = NewETAMessageReplyMarkup(code, services, false)
	}
	responses <- ok(editMessageTextRequest)
	answerCallbackQueryRequest := telegram.AnswerCallbackQueryRequest{
		CallbackQueryID: cbq.ID,
		Text:            "ETAs updated!",
	}
	responses <- ok(answerCallbackQueryRequest)
}

func sendETAMessage(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, code string, services []string, responses chan<- Response) {
	text, err := ETAMessageText(bot.BusStops, bot.Datamall, SummaryETAFormatter{}, bot.NowFunc(), code, services)
	if err != nil {
		responses <- notOk(err)
		return
	}
	markup := NewETAMessageReplyMarkup(code, services, false)
	sendMessageRequest := telegram.SendMessageRequest{
		Text:        text,
		ChatID:      cbq.Message.Chat.ID,
		ParseMode:   "markdown",
		ReplyMarkup: markup,
	}
	responses <- ok(sendMessageRequest)
	answerCallbackQueryRequest := telegram.AnswerCallbackQueryRequest{
		CallbackQueryID: cbq.ID,
		Text:            "ETAs sent!",
	}
	responses <- ok(answerCallbackQueryRequest)
}

// RefreshCallbackHandler handles the callback for the Refresh button on an eta message.
func RefreshCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
	defer close(responses)

	var data CallbackData
	err := json.Unmarshal([]byte(cbq.Data), &data)
	if err != nil {
		responses <- notOk(errors.Wrap(err, "error unmarshalling callback data"))
		return
	}
	updateETAMessage(ctx, bot, cbq, data.BusStopID, data.ServiceNos, responses)
	return
}

// EtaCallbackHandler handles callback queries from eta messages from old versions of the bot for
// backwards-compatibility.
func EtaCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
	defer close(responses)

	var data CallbackData
	err := json.Unmarshal([]byte(cbq.Data), &data)
	if err != nil {
		responses <- notOk(errors.Wrap(err, "error unmarshalling callback data"))
		return
	}
	var code string
	var services []string
	if data.Argstr != "" {
		code, services, _ = InferEtaQuery(data.Argstr)
	} else {
		code = data.BusStopID
		services = data.ServiceNos
	}
	updateETAMessage(ctx, bot, cbq, code, services, responses)
	return
}

// EtaDemoCallbackHandler handles an eta_demo callback from a start command.
func EtaDemoCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
	go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionEtaDemoCallback, cbq.Message.Chat.Type)

	sendETAMessage(ctx, bot, cbq, "96049", nil, responses)
	close(responses)
}

// NewEtaHandler sends etas for a bus stop when a user taps "Get etas" on a bus stop location returned from a
// location query
func NewEtaHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
	go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionEtaFromLocationCallback, "")

	var data CallbackData
	err := json.Unmarshal([]byte(cbq.Data), &data)
	if err != nil {
		responses <- notOk(err)
		return
	}
	code, services := data.BusStopID, data.ServiceNos
	sendETAMessage(ctx, bot, cbq, code, services, responses)
	close(responses)
}

func stringInSlice(a string, list []string) (bool, int) {
	for i, b := range list {
		if b == a {
			return true, i
		}
	}
	return false, 0
}

// ToggleFavouritesHandler handles the toggle favourite callback button on etas
func ToggleFavouritesHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
	defer close(responses)

	var data CallbackData
	err := json.Unmarshal([]byte(cbq.Data), &data)
	if err != nil {
		responses <- notOk(errors.Wrap(err, fmt.Sprintf("error unmarshalling callback data: %s", cbq.Data)))
		return
	}
	userID := cbq.From.ID
	favourites, err := bot.Users.GetUserFavourites(ctx, userID)
	if err != nil {
		responses <- notOk(errors.Wrap(err, "could not retrieve user favourites"))
		return
	}
	var action string
	// if the entry is already in the favourites, we remove it
	if exists, pos := stringInSlice(data.Argstr, favourites); exists {
		// remove item from slice
		copy(favourites[pos:], favourites[pos+1:])
		favourites[len(favourites)-1] = ""
		favourites = favourites[:len(favourites)-1]

		action = "removed from"
	} else {
		favourites = append(favourites, data.Argstr)
		action = "added to"
	}
	err = bot.Users.SetUserFavourites(ctx, userID, favourites)
	if err != nil {
		responses <- notOk(errors.Wrap(err, "error updating user favourites"))
	}
	sendMessageRequest := telegram.SendMessageRequest{
		ChatID:    cbq.Message.Chat.ID,
		Text:      fmt.Sprintf("ETA query `%s` %s favourites!", data.Argstr, action),
		ParseMode: "markdown",
	}
	if len(favourites) > 0 {
		var keyboard [][]telegram.KeyboardButton
		for _, fav := range favourites {
			button := telegram.KeyboardButton{
				Text: fav,
			}
			row := []telegram.KeyboardButton{button}
			keyboard = append(keyboard, row)
		}
		sendMessageRequest.ReplyMarkup = telegram.ReplyKeyboardMarkup{
			Keyboard:       keyboard,
			ResizeKeyboard: true,
		}
	} else {
		sendMessageRequest.ReplyMarkup = telegram.ReplyKeyboardRemove{}
	}
	responses <- ok(sendMessageRequest)
	answerCallbackQueryRequest := telegram.AnswerCallbackQueryRequest{
		CallbackQueryID: cbq.ID,
	}
	responses <- ok(answerCallbackQueryRequest)

	if action == "removed from" {
		go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionRemoveFavouriteCalback, cbq.Message.Chat.Type)
	} else {
		go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionAddFavouriteCalback, cbq.Message.Chat.Type)
	}
}

// callbackErrorHandler is for informing the user about an error while processing a callback query.
func callbackErrorHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, err error) {
	log.Errorf(ctx, "%+v", err)

	text := fmt.Sprintf("Oh no! Something went wrong. \n\nRequest ID: `%s`", appengine.RequestID(ctx))
	answer := tgbotapi.NewCallbackWithAlert(cbq.ID, text)

	_, err = bot.Telegram.AnswerCallbackQuery(answer)
	if err != nil {
		err := errors.Wrap(err, fmt.Sprintf("%#v", answer))
		log.Errorf(ctx, "%+v", err)
	}
}
<commit_msg>callbacks: Remove unused code<commit_after>package busetabot

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/pkg/errors"
	"github.com/yi-jiayu/telegram-bot-api"
	"google.golang.org/appengine"
	"google.golang.org/appengine/log"

	"github.com/yi-jiayu/bus-eta-bot/v4/telegram"
)

var callbackQueryHandlers = map[string]CallbackQueryHandler{
	"refresh":  RefreshCallbackHandler,
	"resend":   NewEtaHandler,
	"eta":      EtaCallbackHandler,
	"eta_demo": EtaDemoCallbackHandler,
	"new_eta":  NewEtaHandler,
	"addf":     ToggleFavouritesHandler,
	"togf":     ToggleFavouritesHandler,
}

// CallbackQueryHandler is a handler for callback queries
type CallbackQueryHandler func(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response)

func updateETAMessage(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, code string, services []string, responses chan<- Response) {
	text, err := ETAMessageText(bot.BusStops, bot.Datamall, SummaryETAFormatter{}, bot.NowFunc(), code, services)
	if err != nil {
		responses <- notOk(err)
		return
	}
	editMessageTextRequest := telegram.EditMessageTextRequest{
		Text:      text,
		ParseMode: "markdown",
	}
	if cbq.InlineMessageID != "" {
		editMessageTextRequest.InlineMessageID = cbq.InlineMessageID
		editMessageTextRequest.ReplyMarkup = NewETAMessageReplyMarkup(code, services, true)
	} else {
		editMessageTextRequest.ChatID = cbq.Message.Chat.ID
		editMessageTextRequest.MessageID = cbq.Message.MessageID
		editMessageTextRequest.ReplyMarkup = NewETAMessageReplyMarkup(code, services, false)
	}
	responses <- ok(editMessageTextRequest)
	answerCallbackQueryRequest := telegram.AnswerCallbackQueryRequest{
		CallbackQueryID: cbq.ID,
		Text:            "ETAs updated!",
	}
	responses <- ok(answerCallbackQueryRequest)
}

func sendETAMessage(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, code string, services []string, responses chan<- Response) {
	text, err := ETAMessageText(bot.BusStops, bot.Datamall, SummaryETAFormatter{}, bot.NowFunc(), code, services)
	if err != nil {
		responses <- notOk(err)
		return
	}
	markup := NewETAMessageReplyMarkup(code, services, false)
	sendMessageRequest := telegram.SendMessageRequest{
		Text:        text,
		ChatID:      cbq.Message.Chat.ID,
		ParseMode:   "markdown",
		ReplyMarkup: markup,
	}
	responses <- ok(sendMessageRequest)
	answerCallbackQueryRequest := telegram.AnswerCallbackQueryRequest{
		CallbackQueryID: cbq.ID,
		Text:            "ETAs sent!",
	}
	responses <- ok(answerCallbackQueryRequest)
}

// RefreshCallbackHandler handles the callback for the Refresh button on an eta message.
func RefreshCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
	defer close(responses)

	var data CallbackData
	err := json.Unmarshal([]byte(cbq.Data), &data)
	if err != nil {
		responses <- notOk(errors.Wrap(err, "error unmarshalling callback data"))
		return
	}
	updateETAMessage(ctx, bot, cbq, data.BusStopID, data.ServiceNos, responses)
	return
}

// EtaCallbackHandler handles callback queries from eta messages from old versions of the bot for
// backwards-compatibility.
func EtaCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
	defer close(responses)

	var data CallbackData
	err := json.Unmarshal([]byte(cbq.Data), &data)
	if err != nil {
		responses <- notOk(errors.Wrap(err, "error unmarshalling callback data"))
		return
	}
	var code string
	var services []string
	if data.Argstr != "" {
		code, services, _ = InferEtaQuery(data.Argstr)
	} else {
		code = data.BusStopID
		services = data.ServiceNos
	}
	updateETAMessage(ctx, bot, cbq, code, services, responses)
	return
}

// EtaDemoCallbackHandler handles an eta_demo callback from a start command.
func EtaDemoCallbackHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
	go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionEtaDemoCallback, cbq.Message.Chat.Type)

	sendETAMessage(ctx, bot, cbq, "96049", nil, responses)
	close(responses)
}

// NewEtaHandler sends etas for a bus stop when a user taps "Get etas" on a bus stop location returned from a
// location query
func NewEtaHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
	go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionEtaFromLocationCallback, "")

	var data CallbackData
	err := json.Unmarshal([]byte(cbq.Data), &data)
	if err != nil {
		responses <- notOk(err)
		return
	}
	code, services := data.BusStopID, data.ServiceNos
	sendETAMessage(ctx, bot, cbq, code, services, responses)
	close(responses)
}

func stringInSlice(a string, list []string) (bool, int) {
	for i, b := range list {
		if b == a {
			return true, i
		}
	}
	return false, 0
}

// ToggleFavouritesHandler handles the toggle favourite callback button on etas
func ToggleFavouritesHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, responses chan<- Response) {
	defer close(responses)

	var data CallbackData
	err := json.Unmarshal([]byte(cbq.Data), &data)
	if err != nil {
		responses <- notOk(errors.Wrap(err, fmt.Sprintf("error unmarshalling callback data: %s", cbq.Data)))
		return
	}
	userID := cbq.From.ID
	favourites, err := bot.Users.GetUserFavourites(ctx, userID)
	if err != nil {
		responses <- notOk(errors.Wrap(err, "could not retrieve user favourites"))
		return
	}
	var action string
	// if the entry is already in the favourites, we remove it
	if exists, pos := stringInSlice(data.Argstr, favourites); exists {
		// remove item from slice
		copy(favourites[pos:], favourites[pos+1:])
		favourites[len(favourites)-1] = ""
		favourites = favourites[:len(favourites)-1]

		action = "removed from"
	} else {
		favourites = append(favourites, data.Argstr)
		action = "added to"
	}
	err = bot.Users.SetUserFavourites(ctx, userID, favourites)
	if err != nil {
		responses <- notOk(errors.Wrap(err, "error updating user favourites"))
	}
	sendMessageRequest := telegram.SendMessageRequest{
		ChatID:    cbq.Message.Chat.ID,
		Text:      fmt.Sprintf("ETA query `%s` %s favourites!", data.Argstr, action),
		ParseMode: "markdown",
	}
	if len(favourites) > 0 {
		var keyboard [][]telegram.KeyboardButton
		for _, fav := range favourites {
			button := telegram.KeyboardButton{
				Text: fav,
			}
			row := []telegram.KeyboardButton{button}
			keyboard = append(keyboard, row)
		}
		sendMessageRequest.ReplyMarkup = telegram.ReplyKeyboardMarkup{
			Keyboard:       keyboard,
			ResizeKeyboard: true,
		}
	} else {
		sendMessageRequest.ReplyMarkup = telegram.ReplyKeyboardRemove{}
	}
	responses <- ok(sendMessageRequest)
	answerCallbackQueryRequest := telegram.AnswerCallbackQueryRequest{
		CallbackQueryID: cbq.ID,
	}
	responses <- ok(answerCallbackQueryRequest)

	if action == "removed from" {
		go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionRemoveFavouriteCalback, cbq.Message.Chat.Type)
	} else {
		go bot.LogEvent(ctx, cbq.From, CategoryCallback, ActionAddFavouriteCalback, cbq.Message.Chat.Type)
	}
}

// callbackErrorHandler is for informing the user about an error while processing a callback query.
func callbackErrorHandler(ctx context.Context, bot *BusEtaBot, cbq *tgbotapi.CallbackQuery, err error) {
	log.Errorf(ctx, "%+v", err)

	text := fmt.Sprintf("Oh no! Something went wrong. \n\nRequest ID: `%s`", appengine.RequestID(ctx))
	answer := tgbotapi.NewCallbackWithAlert(cbq.ID, text)

	_, err = bot.Telegram.AnswerCallbackQuery(answer)
	if err != nil {
		err := errors.Wrap(err, fmt.Sprintf("%#v", answer))
		log.Errorf(ctx, "%+v", err)
	}
}
<|endoftext|>


<commit_before>package bst

import (
	"bytes"
	"strconv"
	"strings"

	"github.com/catorpilor/leetcode/utils"
)

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */

type Codec struct {
}

func Constructor() *Codec {
	return &Codec{}
}

// Serializes a tree to a single string. we use the level order traversal here. time complexity O(N), space complexity O(N)
func (this *Codec) serialize(root *utils.TreeNode) string {
	if root == nil {
		return ""
	}
	// just a level order traversal
	var vals []*utils.TreeNode
	vals = append(vals, root)
	var sb bytes.Buffer
	for len(vals) > 0 {
		top := vals[0]
		vals = vals[1:]
		sb.WriteString(strconv.Itoa(top.Val))
		if top.Left != nil {
			vals = append(vals, top.Left)
		}
		if top.Right != nil {
			vals = append(vals, top.Right)
		}
		if len(vals) > 0 {
			sb.WriteString(" ")
		}
	}
	return sb.String()
}

// deserialize your encoded data to tree.
func (this *Codec) deserialize(data string) *utils.TreeNode {
	// construct a bst from level order traversal
	// split data by space
	sd := strings.Fields(data)
	var root *utils.TreeNode
	for i := range sd {
		val, _ := strconv.Atoi(string(sd[i]))
		root = levelOrder(root, val)
	}
	return root
}

func levelOrder(root *utils.TreeNode, val int) *utils.TreeNode {
	if root == nil {
		root = &utils.TreeNode{Val: val}
		return root
	}
	if val <= root.Val {
		root.Left = levelOrder(root.Left, val)
	} else {
		root.Right = levelOrder(root.Right, val)
	}
	return root

}

/**
 * Your Codec object will be instantiated and called as such:
 * ser := Constructor()
 * deser := Constructor()
 * tree := ser.serialize(root)
 * ans := deser.deserialize(tree)
 * return ans
 */
<commit_msg>add a more general way to serialize and deserialize bst<commit_after>package bst

import (
	"bytes"
	"math"
	"strconv"
	"strings"

	"github.com/catorpilor/leetcode/utils"
)

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */

type Codec struct {
}

func Constructor() *Codec {
	return &Codec{}
}

// Serializes a tree to a single string. we use the level order traversal here. time complexity O(N), space complexity O(N)
func (this *Codec) serialize(root *utils.TreeNode) string {
	if root == nil {
		return ""
	}
	// just a level order traversal
	var vals []*utils.TreeNode
	vals = append(vals, root)
	var sb bytes.Buffer
	for len(vals) > 0 {
		top := vals[0]
		vals = vals[1:]
		sb.WriteString(strconv.Itoa(top.Val))
		if top.Left != nil {
			vals = append(vals, top.Left)
		}
		if top.Right != nil {
			vals = append(vals, top.Right)
		}
		if len(vals) > 0 {
			sb.WriteString(" ")
		}
	}
	return sb.String()
}

// deserialize your encoded data to tree.
func (this *Codec) deserialize(data string) *utils.TreeNode {
	// construct a bst from level order traversal
	// split data by space
	sd := strings.Fields(data)
	var root *utils.TreeNode
	for i := range sd {
		val, _ := strconv.Atoi(string(sd[i]))
		root = levelOrder(root, val)
	}
	return root
}

func levelOrder(root *utils.TreeNode, val int) *utils.TreeNode {
	if root == nil {
		root = &utils.TreeNode{Val: val}
		return root
	}
	if val <= root.Val {
		root.Left = levelOrder(root.Left, val)
	} else {
		root.Right = levelOrder(root.Right, val)
	}
	return root

}

func (this *Codec) serializeUseDFS(root *utils.TreeNode) string {
	if root == nil {
		return ""
	}
	var sb bytes.Buffer
	helper(&sb, root)
	return sb.String()
}

func helper(sb *bytes.Buffer, root *utils.TreeNode) {
	if root == nil {
		return
	}
	sb.WriteString(strconv.Itoa(root.Val))
	sb.WriteString(",")
	helper(sb, root.Left)
	helper(sb, root.Right)
}

func (this *Codec) deserializeUseDFS(data string) *utils.TreeNode {
	if len(data) < 1 {
		return nil
	}
	sd := strings.FieldsFunc(data, func(r rune) bool {
		return r == ','
	})
	return insert(sd, math.MinInt32, math.MaxInt32)
}

func insert(sd []string, min, max int) *utils.TreeNode {
	if len(sd) == 0 {
		return nil
	}
	h := sd[0]
	ih, _ := strconv.Atoi(h)
	if ih < min || ih > max {
		return nil
	}
	sd = sd[1:]
	root := &utils.TreeNode{Val: ih}
	root.Left = insert(sd, min, ih)
	root.Right = insert(sd, ih, max)
	return root
}

/**
 * Your Codec object will be instantiated and called as such:
 * ser := Constructor()
 * deser := Constructor()
 * tree := ser.serialize(root)
 * ans := deser.deserialize(tree)
 * return ans
 */
<|endoftext|>


<commit_before>package callstat

import (
	"fmt"
	"time"
)

// Call represents information about a function call.
type Call struct {
	// Description is a textual description of the call.
	Description string
	// Start is the time at which the call started.
	Start time.Time
	// End is the time at which the call ended or timed out.
	End time.Time
	// Err is the error returned from unsuccessful calls.
	Err error
	// Inner is the set of internal subcalls that were made as part of the call.
	Inner []Call
}

// Duration is the total time it took to make the call.
func (c *Call) Duration() time.Duration {
	return c.End.Sub(c.Start)
}

// Begin sets the call description and records the current time as the start of
// the call.
func (c *Call) Begin(description string) {
	c.Description = description
	c.Start = time.Now()
}

// Complete sets the call error state and records the current time as the end of
// the call.
//
// If the call was not previously started, Complete assumes that the start and
// end times of the call are equal.
func (c *Call) Complete(err error) {
	c.End = time.Now()
	c.Err = err
	if c.Start.IsZero() {
		c.Start = c.End
	}
}

// Add adds the given call as inner call.
func (c *Call) Add(inner *Call) {
	c.Inner = append(c.Inner, *inner)
}

// String returns a string representation of the call data.
func (c Call) String() string {
	desc := c.Description
	if desc == "" {
		desc = "Call"
	}
	seconds := fmt.Sprintf("%.4fs", c.Duration().Seconds())
	if len(c.Inner) > 0 {
		return fmt.Sprintf("(%s %s %v)", desc, seconds, c.Inner)
	}
	return fmt.Sprintf("(%s %s)", desc, seconds)
}
<commit_msg>Updated callstat formatting<commit_after>package callstat

import (
	"fmt"
	"time"
)

// Call represents information about a function call.
type Call struct {
	// Description is a textual description of the call.
	Description string
	// Start is the time at which the call started.
	Start time.Time
	// End is the time at which the call ended or timed out.
	End time.Time
	// Err is the error returned from unsuccessful calls.
	Err error
	// Inner is the set of internal subcalls that were made as part of the call.
	Inner []Call
}

// Duration is the total time it took to make the call.
func (c *Call) Duration() time.Duration {
	return c.End.Sub(c.Start)
}

// Begin sets the call description and records the current time as the start of
// the call.
func (c *Call) Begin(description string) {
	c.Description = description
	c.Start = time.Now()
}

// Complete sets the call error state and records the current time as the end of
// the call.
//
// If the call was not previously started, Complete assumes that the start and
// end times of the call are equal.
func (c *Call) Complete(err error) {
	c.End = time.Now()
	c.Err = err
	if c.Start.IsZero() {
		c.Start = c.End
	}
}

// Add adds the given call as inner call.
func (c *Call) Add(inner *Call) {
	c.Inner = append(c.Inner, *inner)
}

// String returns a string representation of the call data.
func (c Call) String() string {
	desc := c.Description
	if desc == "" {
		desc = "Call"
	}

	seconds := fmt.Sprintf("%.4fs", c.Duration().Seconds())

	if len(c.Inner) == 0 {
		return fmt.Sprintf("%s %s", desc, seconds)
	}

	var inner []byte
	for i := 0; i < len(c.Inner); i++ {
		if i > 0 {
			inner = append(inner, ", "...)
		}
		inner = append(inner, c.Inner[i].String()...)
	}

	return fmt.Sprintf("%s %s (%s)", desc, seconds, inner)
}
<|endoftext|>


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

package fmt_test

import (
	. "fmt"
	"io"
	"os"
	"reflect"
	"strings"
	"testing"
)

type ScanTest struct {
	text string
	in   interface{}
	out  interface{}
}

var boolVal bool
var intVal int
var int8Val int8
var int16Val int16
var int32Val int32
var int64Val int64
var uintVal uint
var uint8Val uint8
var uint16Val uint16
var uint32Val uint32
var uint64Val uint64
var floatVal float
var float32Val float32
var float64Val float64
var stringVal string
var complexVal complex
var complex64Val complex64
var complex128Val complex128

var scanTests = []ScanTest{
	ScanTest{"T\n", &boolVal, true},
	ScanTest{"21\n", &intVal, 21},
	ScanTest{"22\n", &int8Val, int8(22)},
	ScanTest{"23\n", &int16Val, int16(23)},
	ScanTest{"24\n", &int32Val, int32(24)},
	ScanTest{"25\n", &int64Val, int64(25)},
	ScanTest{"127\n", &int8Val, int8(127)},
	ScanTest{"-21\n", &intVal, -21},
	ScanTest{"-22\n", &int8Val, int8(-22)},
	ScanTest{"-23\n", &int16Val, int16(-23)},
	ScanTest{"-24\n", &int32Val, int32(-24)},
	ScanTest{"-25\n", &int64Val, int64(-25)},
	ScanTest{"-128\n", &int8Val, int8(-128)},
	ScanTest{"+21\n", &intVal, +21},
	ScanTest{"+22\n", &int8Val, int8(+22)},
	ScanTest{"+23\n", &int16Val, int16(+23)},
	ScanTest{"+24\n", &int32Val, int32(+24)},
	ScanTest{"+25\n", &int64Val, int64(+25)},
	ScanTest{"+127\n", &int8Val, int8(+127)},
	ScanTest{"26\n", &uintVal, uint(26)},
	ScanTest{"27\n", &uint8Val, uint8(27)},
	ScanTest{"28\n", &uint16Val, uint16(28)},
	ScanTest{"29\n", &uint32Val, uint32(29)},
	ScanTest{"30\n", &uint64Val, uint64(30)},
	ScanTest{"255\n", &uint8Val, uint8(255)},
	ScanTest{"32767\n", &int16Val, int16(32767)},
	ScanTest{"2.3\n", &floatVal, 2.3},
	ScanTest{"2.3e1\n", &float32Val, float32(2.3e1)},
	ScanTest{"2.3e2\n", &float64Val, float64(2.3e2)},
	ScanTest{"2.35\n", &stringVal, "2.35"},
	ScanTest{"(3.4e1-2i)\n", &complexVal, 3.4e1 - 2i},
	ScanTest{"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)},
	ScanTest{"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)},
}

var overflowTests = []ScanTest{
	ScanTest{"128", &int8Val, 0},
	ScanTest{"32768", &int16Val, 0},
	ScanTest{"-129", &int8Val, 0},
	ScanTest{"-32769", &int16Val, 0},
	ScanTest{"256", &uint8Val, 0},
	ScanTest{"65536", &uint16Val, 0},
	ScanTest{"1e100", &float32Val, 0},
	ScanTest{"1e500", &float64Val, 0},
	ScanTest{"(1e100+0i)", &complexVal, 0},
	ScanTest{"(1+1e100i)", &complex64Val, 0},
	ScanTest{"(1-1e500i)", &complex128Val, 0},
}

func testScan(t *testing.T, scan func(r io.Reader, a ...interface{}) (int, os.Error)) {
	for _, test := range scanTests {
		r := strings.NewReader(test.text)
		n, err := scan(r, test.in)
		if err != nil {
			t.Errorf("got error scanning %q: %s", test.text, err)
			continue
		}
		if n != 1 {
			t.Errorf("count error on entry %q: got %d", test.text, n)
			continue
		}
		// The incoming value may be a pointer
		v := reflect.NewValue(test.in)
		if p, ok := v.(*reflect.PtrValue); ok {
			v = p.Elem()
		}
		val := v.Interface()
		if !reflect.DeepEqual(val, test.out) {
			t.Errorf("scanning %q: expected %v got %v, type %T", test.text, test.out, val, val)
		}
	}
}

func TestScan(t *testing.T) {
	testScan(t, Scan)
}

func TestScanln(t *testing.T) {
	testScan(t, Scanln)
}

func TestScanOverflow(t *testing.T) {
	for _, test := range overflowTests {
		r := strings.NewReader(test.text)
		_, err := Scan(r, test.in)
		if err == nil {
			t.Errorf("expected overflow scanning %q", test.text)
			continue
		}
		if strings.Index(err.String(), "overflow") < 0 && strings.Index(err.String(), "too large") < 0 {
			t.Errorf("expected overflow error scanning %q: %s", test.text, err)
		}
	}
}

func TestScanMultiple(t *testing.T) {
	text := "1 2 3 x"
	r := strings.NewReader(text)
	var a, b, c, d int
	n, err := Scan(r, &a, &b, &c, &d)
	if n != 3 {
		t.Errorf("count error: expected 3: got %d", n)
	}
	if err == nil {
		t.Errorf("expected error scanning ", text)
	}
}

func TestScanNotPointer(t *testing.T) {
	r := strings.NewReader("1")
	var a int
	_, err := Scan(r, a)
	if err == nil {
		t.Error("expected error scanning non-pointer")
	} else if strings.Index(err.String(), "pointer") < 0 {
		t.Errorf("expected pointer error scanning non-pointer, got: %s", err)
	}
}

func TestScanlnNoNewline(t *testing.T) {
	r := strings.NewReader("1 x\n")
	var a int
	_, err := Scanln(r, &a)
	if err == nil {
		t.Error("expected error scanning string missing newline")
	} else if strings.Index(err.String(), "newline") < 0 {
		t.Errorf("expected newline error scanning string missing newline, got: %s", err)
	}
}

func TestScanlnWithMiddleNewline(t *testing.T) {
	r := strings.NewReader("123\n456\n")
	var a, b int
	_, err := Scanln(r, &a, &b)
	if err == nil {
		t.Error("expected error scanning string with extra newline")
	} else if strings.Index(err.String(), "newline") < 0 {
		t.Errorf("expected newline error scanning string with extra newline, got: %s", err)
	}
}
<commit_msg>fmt: fix 386 build. error strings differ for overflow on 386.<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fmt_test

import (
	. "fmt"
	"io"
	"os"
	"reflect"
	"strings"
	"testing"
)

type ScanTest struct {
	text string
	in   interface{}
	out  interface{}
}

var boolVal bool
var intVal int
var int8Val int8
var int16Val int16
var int32Val int32
var int64Val int64
var uintVal uint
var uint8Val uint8
var uint16Val uint16
var uint32Val uint32
var uint64Val uint64
var floatVal float
var float32Val float32
var float64Val float64
var stringVal string
var complexVal complex
var complex64Val complex64
var complex128Val complex128

var scanTests = []ScanTest{
	ScanTest{"T\n", &boolVal, true},
	ScanTest{"21\n", &intVal, 21},
	ScanTest{"22\n", &int8Val, int8(22)},
	ScanTest{"23\n", &int16Val, int16(23)},
	ScanTest{"24\n", &int32Val, int32(24)},
	ScanTest{"25\n", &int64Val, int64(25)},
	ScanTest{"127\n", &int8Val, int8(127)},
	ScanTest{"-21\n", &intVal, -21},
	ScanTest{"-22\n", &int8Val, int8(-22)},
	ScanTest{"-23\n", &int16Val, int16(-23)},
	ScanTest{"-24\n", &int32Val, int32(-24)},
	ScanTest{"-25\n", &int64Val, int64(-25)},
	ScanTest{"-128\n", &int8Val, int8(-128)},
	ScanTest{"+21\n", &intVal, +21},
	ScanTest{"+22\n", &int8Val, int8(+22)},
	ScanTest{"+23\n", &int16Val, int16(+23)},
	ScanTest{"+24\n", &int32Val, int32(+24)},
	ScanTest{"+25\n", &int64Val, int64(+25)},
	ScanTest{"+127\n", &int8Val, int8(+127)},
	ScanTest{"26\n", &uintVal, uint(26)},
	ScanTest{"27\n", &uint8Val, uint8(27)},
	ScanTest{"28\n", &uint16Val, uint16(28)},
	ScanTest{"29\n", &uint32Val, uint32(29)},
	ScanTest{"30\n", &uint64Val, uint64(30)},
	ScanTest{"255\n", &uint8Val, uint8(255)},
	ScanTest{"32767\n", &int16Val, int16(32767)},
	ScanTest{"2.3\n", &floatVal, 2.3},
	ScanTest{"2.3e1\n", &float32Val, float32(2.3e1)},
	ScanTest{"2.3e2\n", &float64Val, float64(2.3e2)},
	ScanTest{"2.35\n", &stringVal, "2.35"},
	ScanTest{"(3.4e1-2i)\n", &complexVal, 3.4e1 - 2i},
	ScanTest{"-3.45e1-3i\n", &complex64Val, complex64(-3.45e1 - 3i)},
	ScanTest{"-.45e1-1e2i\n", &complex128Val, complex128(-.45e1 - 100i)},
}

var overflowTests = []ScanTest{
	ScanTest{"128", &int8Val, 0},
	ScanTest{"32768", &int16Val, 0},
	ScanTest{"-129", &int8Val, 0},
	ScanTest{"-32769", &int16Val, 0},
	ScanTest{"256", &uint8Val, 0},
	ScanTest{"65536", &uint16Val, 0},
	ScanTest{"1e100", &float32Val, 0},
	ScanTest{"1e500", &float64Val, 0},
	ScanTest{"(1e100+0i)", &complexVal, 0},
	ScanTest{"(1+1e100i)", &complex64Val, 0},
	ScanTest{"(1-1e500i)", &complex128Val, 0},
}

func testScan(t *testing.T, scan func(r io.Reader, a ...interface{}) (int, os.Error)) {
	for _, test := range scanTests {
		r := strings.NewReader(test.text)
		n, err := scan(r, test.in)
		if err != nil {
			t.Errorf("got error scanning %q: %s", test.text, err)
			continue
		}
		if n != 1 {
			t.Errorf("count error on entry %q: got %d", test.text, n)
			continue
		}
		// The incoming value may be a pointer
		v := reflect.NewValue(test.in)
		if p, ok := v.(*reflect.PtrValue); ok {
			v = p.Elem()
		}
		val := v.Interface()
		if !reflect.DeepEqual(val, test.out) {
			t.Errorf("scanning %q: expected %v got %v, type %T", test.text, test.out, val, val)
		}
	}
}

func TestScan(t *testing.T) {
	testScan(t, Scan)
}

func TestScanln(t *testing.T) {
	testScan(t, Scanln)
}

func TestScanOverflow(t *testing.T) {
	// different machines and different types report errors with different strings.
	re := testing.MustCompile("overflow|too large|out of range|not representable")
	for _, test := range overflowTests {
		r := strings.NewReader(test.text)
		_, err := Scan(r, test.in)
		if err == nil {
			t.Errorf("expected overflow scanning %q", test.text)
			continue
		}
		if !re.MatchString(err.String()) {
			t.Errorf("expected overflow error scanning %q: %s", test.text, err)
		}
	}
}

func TestScanMultiple(t *testing.T) {
	text := "1 2 3 x"
	r := strings.NewReader(text)
	var a, b, c, d int
	n, err := Scan(r, &a, &b, &c, &d)
	if n != 3 {
		t.Errorf("count error: expected 3: got %d", n)
	}
	if err == nil {
		t.Errorf("expected error scanning ", text)
	}
}

func TestScanNotPointer(t *testing.T) {
	r := strings.NewReader("1")
	var a int
	_, err := Scan(r, a)
	if err == nil {
		t.Error("expected error scanning non-pointer")
	} else if strings.Index(err.String(), "pointer") < 0 {
		t.Errorf("expected pointer error scanning non-pointer, got: %s", err)
	}
}

func TestScanlnNoNewline(t *testing.T) {
	r := strings.NewReader("1 x\n")
	var a int
	_, err := Scanln(r, &a)
	if err == nil {
		t.Error("expected error scanning string missing newline")
	} else if strings.Index(err.String(), "newline") < 0 {
		t.Errorf("expected newline error scanning string missing newline, got: %s", err)
	}
}

func TestScanlnWithMiddleNewline(t *testing.T) {
	r := strings.NewReader("123\n456\n")
	var a, b int
	_, err := Scanln(r, &a, &b)
	if err == nil {
		t.Error("expected error scanning string with extra newline")
	} else if strings.Index(err.String(), "newline") < 0 {
		t.Errorf("expected newline error scanning string with extra newline, got: %s", err)
	}
}
<|endoftext|>


<commit_before>/*
 * pam.go - Utility functions for interfacing with the PAM libraries.
 *
 * Copyright 2017 Google Inc.
 * Author: Joe Richey (joerichey@google.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 pam

/*
#cgo LDFLAGS: -lpam
#include "pam.h"

#include <pwd.h>
#include <stdlib.h>
#include <security/pam_modules.h>
*/
import "C"
import (
	"errors"
	"fmt"
	"log"
	"unsafe"

	"github.com/google/fscrypt/security"
)

// Handle wraps the C pam_handle_t type. This is used from within modules.
type Handle struct {
	handle *C.pam_handle_t
	status C.int
	privs  *security.Privileges
	// UID of the user being authenticated
	UID int
	// GID of the user being authenticated
	GID int
}

// NewHandle creates a Handle from a raw pointer.
func NewHandle(pamh unsafe.Pointer) (*Handle, error) {
	h := &Handle{
		handle: (*C.pam_handle_t)(pamh),
		status: C.PAM_SUCCESS,
	}

	var pamUsername *C.char
	h.status = C.pam_get_user(h.handle, &pamUsername, nil)
	if err := h.err(); err != nil {
		return nil, err
	}

	pwnam := C.getpwnam(pamUsername)
	if pwnam == nil {
		return nil, fmt.Errorf("unknown user %q", C.GoString(pamUsername))
	}
	h.UID = int(pwnam.pw_uid)
	h.GID = int(pwnam.pw_gid)
	return h, nil
}

func (h *Handle) setData(name string, data unsafe.Pointer, cleanup C.CleanupFunc) error {
	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))
	h.status = C.pam_set_data(h.handle, cName, data, cleanup)
	return h.err()
}

func (h *Handle) getData(name string) (unsafe.Pointer, error) {
	var data unsafe.Pointer
	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))
	h.status = C.pam_get_data(h.handle, cName, &data)
	return data, h.err()
}

// ClearData remotes the PAM data with the specified name.
func (h *Handle) ClearData(name string) error {
	return h.setData(name, unsafe.Pointer(C.CString("")), C.CleanupFunc(C.freeData))
}

// SetSecret sets a copy of the C string secret into the PAM data with the
// specified name. This copy will be held in locked memory until this PAM data
// is cleared.
func (h *Handle) SetSecret(name string, secret unsafe.Pointer) error {
	return h.setData(name, C.copyIntoSecret(secret), C.CleanupFunc(C.freeSecret))
}

// GetSecret returns a pointer to the C string PAM data with the specified name.
// This a pointer directory to the data, so it shouldn't be modified. It should
// have been previously set with SetSecret().
func (h *Handle) GetSecret(name string) (unsafe.Pointer, error) {
	return h.getData(name)
}

// SetString sets a string value for the PAM data with the specified name.
func (h *Handle) SetString(name string, s string) error {
	return h.setData(name, unsafe.Pointer(C.CString(s)), C.CleanupFunc(C.freeData))
}

// GetString gets a string value for the PAM data with the specified name. It
// should have been previously set with SetString().
func (h *Handle) GetString(name string) (string, error) {
	data, err := h.getData(name)
	if err != nil {
		return "", err
	}
	return C.GoString((*C.char)(data)), nil
}

// GetItem retrieves a PAM information item. This a pointer directory to the
// data, so it shouldn't be modified.
func (h *Handle) GetItem(i Item) (unsafe.Pointer, error) {
	var data unsafe.Pointer
	h.status = C.pam_get_item(h.handle, C.int(i), &data)
	return data, h.err()
}

// DropThreadPrivileges sets the effective privileges to that of the PAM user
func (h *Handle) DropThreadPrivileges() error {
	var err error
	h.privs, err = security.DropThreadPrivileges(h.UID, h.GID)
	return err
}

// RaiseThreadPrivileges restores the original privileges that were running the
// PAM module (this is usually root). As this error is often ignored in a defer
// statement, any error is also logged.
func (h *Handle) RaiseThreadPrivileges() error {
	err := security.RaiseThreadPrivileges(h.privs)
	if err != nil {
		log.Print(err)
	}
	return err
}

func (h *Handle) err() error {
	if h.status == C.PAM_SUCCESS {
		return nil
	}
	s := C.GoString(C.pam_strerror(h.handle, C.int(h.status)))
	return errors.New(s)
}

// Transaction represents a wrapped pam_handle_t type created with pam_start
// form an application.
type Transaction Handle

// Start initializes a pam Transaction. End() should be called after the
// Transaction is no longer needed.
func Start(service, username string) (*Transaction, error) {
	cService := C.CString(service)
	defer C.free(unsafe.Pointer(cService))
	cUsername := C.CString(username)
	defer C.free(unsafe.Pointer(cUsername))

	t := &Transaction{
		handle: nil,
		status: C.PAM_SUCCESS,
	}
	t.status = C.pam_start(
		cService,
		cUsername,
		C.goConv,
		&t.handle)
	return t, (*Handle)(t).err()
}

// End finalizes a pam Transaction with pam_end().
func (t *Transaction) End() {
	C.pam_end(t.handle, t.status)
}

// Authenticate returns a boolean indicating if the user authenticated correctly
// or not. If the authentication check did not complete, an error is returned.
func (t *Transaction) Authenticate(quiet bool) (bool, error) {
	var flags C.int = C.PAM_DISALLOW_NULL_AUTHTOK
	if quiet {
		flags |= C.PAM_SILENT
	}
	t.status = C.pam_authenticate(t.handle, flags)
	if t.status == C.PAM_AUTH_ERR {
		return false, nil
	}
	return true, (*Handle)(t).err()
}
<commit_msg>pam: Handle holds data for calling and PAM users<commit_after>/*
 * pam.go - Utility functions for interfacing with the PAM libraries.
 *
 * Copyright 2017 Google Inc.
 * Author: Joe Richey (joerichey@google.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 pam

/*
#cgo LDFLAGS: -lpam
#include "pam.h"

#include <pwd.h>
#include <stdlib.h>
#include <security/pam_modules.h>
*/
import "C"
import (
	"errors"
	"log"
	"os/user"
	"unsafe"

	"github.com/google/fscrypt/security"
	"github.com/google/fscrypt/util"
)

// Handle wraps the C pam_handle_t type. This is used from within modules.
type Handle struct {
	handle *C.pam_handle_t
	status C.int
	// OrigUser is the user who invoked the PAM module (usually root)
	OrigUser *user.User
	// PamUser is the user who the PAM module is for
	PamUser *user.User
}

// NewHandle creates a Handle from a raw pointer.
func NewHandle(pamh unsafe.Pointer) (*Handle, error) {
	var err error
	h := &Handle{
		handle: (*C.pam_handle_t)(pamh),
		status: C.PAM_SUCCESS,
	}

	var pamUsername *C.char
	h.status = C.pam_get_user(h.handle, &pamUsername, nil)
	if err := h.err(); err != nil {
		return nil, err
	}

	if h.PamUser, err = user.Lookup(C.GoString(pamUsername)); err != nil {
		return nil, err
	}
	if h.OrigUser, err = util.EffectiveUser(); err != nil {
		return nil, err
	}
	return h, nil
}

func (h *Handle) setData(name string, data unsafe.Pointer, cleanup C.CleanupFunc) error {
	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))
	h.status = C.pam_set_data(h.handle, cName, data, cleanup)
	return h.err()
}

func (h *Handle) getData(name string) (unsafe.Pointer, error) {
	var data unsafe.Pointer
	cName := C.CString(name)
	defer C.free(unsafe.Pointer(cName))
	h.status = C.pam_get_data(h.handle, cName, &data)
	return data, h.err()
}

// ClearData remotes the PAM data with the specified name.
func (h *Handle) ClearData(name string) error {
	return h.setData(name, unsafe.Pointer(C.CString("")), C.CleanupFunc(C.freeData))
}

// SetSecret sets a copy of the C string secret into the PAM data with the
// specified name. This copy will be held in locked memory until this PAM data
// is cleared.
func (h *Handle) SetSecret(name string, secret unsafe.Pointer) error {
	return h.setData(name, C.copyIntoSecret(secret), C.CleanupFunc(C.freeSecret))
}

// GetSecret returns a pointer to the C string PAM data with the specified name.
// This a pointer directory to the data, so it shouldn't be modified. It should
// have been previously set with SetSecret().
func (h *Handle) GetSecret(name string) (unsafe.Pointer, error) {
	return h.getData(name)
}

// SetString sets a string value for the PAM data with the specified name.
func (h *Handle) SetString(name string, s string) error {
	return h.setData(name, unsafe.Pointer(C.CString(s)), C.CleanupFunc(C.freeData))
}

// GetString gets a string value for the PAM data with the specified name. It
// should have been previously set with SetString().
func (h *Handle) GetString(name string) (string, error) {
	data, err := h.getData(name)
	if err != nil {
		return "", err
	}
	return C.GoString((*C.char)(data)), nil
}

// GetItem retrieves a PAM information item. This a pointer directory to the
// data, so it shouldn't be modified.
func (h *Handle) GetItem(i Item) (unsafe.Pointer, error) {
	var data unsafe.Pointer
	h.status = C.pam_get_item(h.handle, C.int(i), &data)
	return data, h.err()
}

// StartAsPamUser sets the effective privileges to that of the PAM user, and
// configures the PAM user's keyrings to be properly linked.
func (h *Handle) StartAsPamUser() error {
	if err := security.KeyringsSetup(h.PamUser, h.OrigUser); err != nil {
		return err
	}
	return security.SetThreadPrivileges(h.PamUser, false)
}

// StopAsPamUser restores the original privileges that were running the
// PAM module (this is usually root). As this error is often ignored in a defer
// statement, any error is also logged.
func (h *Handle) StopAsPamUser() error {
	err := security.SetThreadPrivileges(h.OrigUser, false)
	if err != nil {
		log.Print(err)
	}
	return err
}

func (h *Handle) err() error {
	if h.status == C.PAM_SUCCESS {
		return nil
	}
	s := C.GoString(C.pam_strerror(h.handle, C.int(h.status)))
	return errors.New(s)
}

// Transaction represents a wrapped pam_handle_t type created with pam_start
// form an application.
type Transaction Handle

// Start initializes a pam Transaction. End() should be called after the
// Transaction is no longer needed.
func Start(service, username string) (*Transaction, error) {
	cService := C.CString(service)
	defer C.free(unsafe.Pointer(cService))
	cUsername := C.CString(username)
	defer C.free(unsafe.Pointer(cUsername))

	t := &Transaction{
		handle: nil,
		status: C.PAM_SUCCESS,
	}
	t.status = C.pam_start(
		cService,
		cUsername,
		C.goConv,
		&t.handle)
	return t, (*Handle)(t).err()
}

// End finalizes a pam Transaction with pam_end().
func (t *Transaction) End() {
	C.pam_end(t.handle, t.status)
}

// Authenticate returns a boolean indicating if the user authenticated correctly
// or not. If the authentication check did not complete, an error is returned.
func (t *Transaction) Authenticate(quiet bool) (bool, error) {
	var flags C.int = C.PAM_DISALLOW_NULL_AUTHTOK
	if quiet {
		flags |= C.PAM_SILENT
	}
	t.status = C.pam_authenticate(t.handle, flags)
	if t.status == C.PAM_AUTH_ERR {
		return false, nil
	}
	return true, (*Handle)(t).err()
}
<|endoftext|>


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

// Parse "zoneinfo" time zone file.
// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo,
// and ftp://munnari.oz.au/pub/oldtz/

package time

import (
	"io/ioutil"
	"once"
	"os"
)

const (
	headerSize = 4 + 16 + 4*7
	zoneDir    = "/usr/share/zoneinfo/"
)

// Simple I/O interface to binary blob of data.
type data struct {
	p     []byte
	error bool
}

func (d *data) read(n int) []byte {
	if len(d.p) < n {
		d.p = nil
		d.error = true
		return nil
	}
	p := d.p[0:n]
	d.p = d.p[n:]
	return p
}

func (d *data) big4() (n uint32, ok bool) {
	p := d.read(4)
	if len(p) < 4 {
		d.error = true
		return 0, false
	}
	return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true
}

func (d *data) byte() (n byte, ok bool) {
	p := d.read(1)
	if len(p) < 1 {
		d.error = true
		return 0, false
	}
	return p[0], true
}

// Make a string by stopping at the first NUL
func byteString(p []byte) string {
	for i := 0; i < len(p); i++ {
		if p[i] == 0 {
			return string(p[0:i])
		}
	}
	return string(p)
}

// Parsed representation
type zone struct {
	utcoff int
	isdst  bool
	name   string
}

type zonetime struct {
	time         int32 // transition time, in seconds since 1970 GMT
	zone         *zone // the zone that goes into effect at that time
	isstd, isutc bool  // ignored - no idea what these mean
}

func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
	d := data{bytes, false}

	// 4-byte magic "TZif"
	if magic := d.read(4); string(magic) != "TZif" {
		return nil, false
	}

	// 1-byte version, then 15 bytes of padding
	var p []byte
	if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
		return nil, false
	}

	// six big-endian 32-bit integers:
	//	number of UTC/local indicators
	//	number of standard/wall indicators
	//	number of leap seconds
	//	number of transition times
	//	number of local time zones
	//	number of characters of time zone abbrev strings
	const (
		NUTCLocal = iota
		NStdWall
		NLeap
		NTime
		NZone
		NChar
	)
	var n [6]int
	for i := 0; i < 6; i++ {
		nn, ok := d.big4()
		if !ok {
			return nil, false
		}
		n[i] = int(nn)
	}

	// Transition times.
	txtimes := data{d.read(n[NTime] * 4), false}

	// Time zone indices for transition times.
	txzones := d.read(n[NTime])

	// Zone info structures
	zonedata := data{d.read(n[NZone] * 6), false}

	// Time zone abbreviations.
	abbrev := d.read(n[NChar])

	// Leap-second time pairs
	d.read(n[NLeap] * 8)

	// Whether tx times associated with local time types
	// are specified as standard time or wall time.
	isstd := d.read(n[NStdWall])

	// Whether tx times associated with local time types
	// are specified as UTC or local time.
	isutc := d.read(n[NUTCLocal])

	if d.error { // ran out of data
		return nil, false
	}

	// If version == 2, the entire file repeats, this time using
	// 8-byte ints for txtimes and leap seconds.
	// We won't need those until 2106.

	// Now we can build up a useful data structure.
	// First the zone information.
	//	utcoff[4] isdst[1] nameindex[1]
	z := make([]zone, n[NZone])
	for i := 0; i < len(z); i++ {
		var ok bool
		var n uint32
		if n, ok = zonedata.big4(); !ok {
			return nil, false
		}
		z[i].utcoff = int(n)
		var b byte
		if b, ok = zonedata.byte(); !ok {
			return nil, false
		}
		z[i].isdst = b != 0
		if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
			return nil, false
		}
		z[i].name = byteString(abbrev[b:])
	}

	// Now the transition time info.
	zt = make([]zonetime, n[NTime])
	for i := 0; i < len(zt); i++ {
		var ok bool
		var n uint32
		if n, ok = txtimes.big4(); !ok {
			return nil, false
		}
		zt[i].time = int32(n)
		if int(txzones[i]) >= len(z) {
			return nil, false
		}
		zt[i].zone = &z[txzones[i]]
		if i < len(isstd) {
			zt[i].isstd = isstd[i] != 0
		}
		if i < len(isutc) {
			zt[i].isutc = isutc[i] != 0
		}
	}
	return zt, true
}

func readinfofile(name string) ([]zonetime, bool) {
	buf, err := ioutil.ReadFile(name)
	if err != nil {
		return nil, false
	}
	return parseinfo(buf)
}

var zones []zonetime
var onceSetupZone sync.Once

func setupZone() {
	// consult $TZ to find the time zone to use.
	// no $TZ means use the system default /etc/localtime.
	// $TZ="" means use UTC.
	// $TZ="foo" means use /usr/share/zoneinfo/foo.

	tz, err := os.Getenverror("TZ")
	switch {
	case err == os.ENOENV:
		zones, _ = readinfofile("/etc/localtime")
	case len(tz) > 0:
		zones, _ = readinfofile(zoneDir + tz)
	case len(tz) == 0:
		// do nothing: use UTC
	}
}

// Look up the correct time zone (daylight savings or not) for the given unix time, in the current location.
func lookupTimezone(sec int64) (zone string, offset int) {
	onceSetupZone.Do(setupZone)
	if len(zones) == 0 {
		return "UTC", 0
	}

	// Binary search for entry with largest time <= sec
	tz := zones
	for len(tz) > 1 {
		m := len(tz) / 2
		if sec < int64(tz[m].time) {
			tz = tz[0:m]
		} else {
			tz = tz[m:]
		}
	}
	z := tz[0].zone
	return z.name, z.utcoff
}
<commit_msg>Remove relic "once" import from time/zoneinfo.go<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Parse "zoneinfo" time zone file.
// This is a fairly standard file format used on OS X, Linux, BSD, Sun, and others.
// See tzfile(5), http://en.wikipedia.org/wiki/Zoneinfo,
// and ftp://munnari.oz.au/pub/oldtz/

package time

import (
	"io/ioutil"
	"os"
)

const (
	headerSize = 4 + 16 + 4*7
	zoneDir    = "/usr/share/zoneinfo/"
)

// Simple I/O interface to binary blob of data.
type data struct {
	p     []byte
	error bool
}

func (d *data) read(n int) []byte {
	if len(d.p) < n {
		d.p = nil
		d.error = true
		return nil
	}
	p := d.p[0:n]
	d.p = d.p[n:]
	return p
}

func (d *data) big4() (n uint32, ok bool) {
	p := d.read(4)
	if len(p) < 4 {
		d.error = true
		return 0, false
	}
	return uint32(p[0])<<24 | uint32(p[1])<<16 | uint32(p[2])<<8 | uint32(p[3]), true
}

func (d *data) byte() (n byte, ok bool) {
	p := d.read(1)
	if len(p) < 1 {
		d.error = true
		return 0, false
	}
	return p[0], true
}

// Make a string by stopping at the first NUL
func byteString(p []byte) string {
	for i := 0; i < len(p); i++ {
		if p[i] == 0 {
			return string(p[0:i])
		}
	}
	return string(p)
}

// Parsed representation
type zone struct {
	utcoff int
	isdst  bool
	name   string
}

type zonetime struct {
	time         int32 // transition time, in seconds since 1970 GMT
	zone         *zone // the zone that goes into effect at that time
	isstd, isutc bool  // ignored - no idea what these mean
}

func parseinfo(bytes []byte) (zt []zonetime, ok bool) {
	d := data{bytes, false}

	// 4-byte magic "TZif"
	if magic := d.read(4); string(magic) != "TZif" {
		return nil, false
	}

	// 1-byte version, then 15 bytes of padding
	var p []byte
	if p = d.read(16); len(p) != 16 || p[0] != 0 && p[0] != '2' {
		return nil, false
	}

	// six big-endian 32-bit integers:
	//	number of UTC/local indicators
	//	number of standard/wall indicators
	//	number of leap seconds
	//	number of transition times
	//	number of local time zones
	//	number of characters of time zone abbrev strings
	const (
		NUTCLocal = iota
		NStdWall
		NLeap
		NTime
		NZone
		NChar
	)
	var n [6]int
	for i := 0; i < 6; i++ {
		nn, ok := d.big4()
		if !ok {
			return nil, false
		}
		n[i] = int(nn)
	}

	// Transition times.
	txtimes := data{d.read(n[NTime] * 4), false}

	// Time zone indices for transition times.
	txzones := d.read(n[NTime])

	// Zone info structures
	zonedata := data{d.read(n[NZone] * 6), false}

	// Time zone abbreviations.
	abbrev := d.read(n[NChar])

	// Leap-second time pairs
	d.read(n[NLeap] * 8)

	// Whether tx times associated with local time types
	// are specified as standard time or wall time.
	isstd := d.read(n[NStdWall])

	// Whether tx times associated with local time types
	// are specified as UTC or local time.
	isutc := d.read(n[NUTCLocal])

	if d.error { // ran out of data
		return nil, false
	}

	// If version == 2, the entire file repeats, this time using
	// 8-byte ints for txtimes and leap seconds.
	// We won't need those until 2106.

	// Now we can build up a useful data structure.
	// First the zone information.
	//	utcoff[4] isdst[1] nameindex[1]
	z := make([]zone, n[NZone])
	for i := 0; i < len(z); i++ {
		var ok bool
		var n uint32
		if n, ok = zonedata.big4(); !ok {
			return nil, false
		}
		z[i].utcoff = int(n)
		var b byte
		if b, ok = zonedata.byte(); !ok {
			return nil, false
		}
		z[i].isdst = b != 0
		if b, ok = zonedata.byte(); !ok || int(b) >= len(abbrev) {
			return nil, false
		}
		z[i].name = byteString(abbrev[b:])
	}

	// Now the transition time info.
	zt = make([]zonetime, n[NTime])
	for i := 0; i < len(zt); i++ {
		var ok bool
		var n uint32
		if n, ok = txtimes.big4(); !ok {
			return nil, false
		}
		zt[i].time = int32(n)
		if int(txzones[i]) >= len(z) {
			return nil, false
		}
		zt[i].zone = &z[txzones[i]]
		if i < len(isstd) {
			zt[i].isstd = isstd[i] != 0
		}
		if i < len(isutc) {
			zt[i].isutc = isutc[i] != 0
		}
	}
	return zt, true
}

func readinfofile(name string) ([]zonetime, bool) {
	buf, err := ioutil.ReadFile(name)
	if err != nil {
		return nil, false
	}
	return parseinfo(buf)
}

var zones []zonetime
var onceSetupZone sync.Once

func setupZone() {
	// consult $TZ to find the time zone to use.
	// no $TZ means use the system default /etc/localtime.
	// $TZ="" means use UTC.
	// $TZ="foo" means use /usr/share/zoneinfo/foo.

	tz, err := os.Getenverror("TZ")
	switch {
	case err == os.ENOENV:
		zones, _ = readinfofile("/etc/localtime")
	case len(tz) > 0:
		zones, _ = readinfofile(zoneDir + tz)
	case len(tz) == 0:
		// do nothing: use UTC
	}
}

// Look up the correct time zone (daylight savings or not) for the given unix time, in the current location.
func lookupTimezone(sec int64) (zone string, offset int) {
	onceSetupZone.Do(setupZone)
	if len(zones) == 0 {
		return "UTC", 0
	}

	// Binary search for entry with largest time <= sec
	tz := zones
	for len(tz) > 1 {
		m := len(tz) / 2
		if sec < int64(tz[m].time) {
			tz = tz[0:m]
		} else {
			tz = tz[m:]
		}
	}
	z := tz[0].zone
	return z.name, z.utcoff
}
<|endoftext|>


<commit_before>package caspercloud

import (
	"bufio"
	"encoding/json"
	"log"
	"os"
	"os/exec"
	"strings"
)

type CasperCmd struct {
	proxyServer string
	id          string
	tmpl        string
	message     chan map[string]interface{}
	input       chan map[string]string
	isKill      bool
	isFinish    bool
	args        map[string]string
	status      int
}

func NewCasperCmd(id, tmpl, proxyServer string) *CasperCmd {
	ret := &CasperCmd{
		proxyServer: proxyServer,
		id:          id,
		tmpl:        tmpl,
		message:     make(chan map[string]interface{}, 1),
		input:       make(chan map[string]string, 1),
		args:        make(map[string]string),
		status:      kCommandStatusIdle,
	}
	go ret.run()
	return ret
}

func (self *CasperCmd) GetId() string {
	return self.id
}

func (self *CasperCmd) GetStatus() int {
	return self.status
}

func (self *CasperCmd) SetInputArgs(input map[string]string) {
	if self.Finished() {
		log.Println("start another casperjs")
		go self.run()
	}
	log.Println("insert input:", input)
	self.input <- input
}

func (self *CasperCmd) GetMessage() map[string]interface{} {
	return <-self.message
}

func (self *CasperCmd) readInputArgs(key string) string {
	args := <-self.input
	log.Println("read args:", args)
	for k, v := range args {
		self.args[k] = v
	}
	if val, ok := self.args[key]; ok {
		return val
	}

	message := make(map[string]interface{})
	message["id"] = self.GetArgsValue("id")
	message["need_args"] = key
	message[kJobStatus] = kJobOndoing
	self.message <- message

	return ""
}

func (self *CasperCmd) GetArgsValue(key string) string {
	if val, ok := self.args[key]; ok {
		log.Println("successfully get args value", val)
		return val
	}
	for {
		val := self.readInputArgs(key)
		if len(val) != 0 {
			log.Println("successfully get args value", val)
			return val
		}
	}
	return ""
}

func (self *CasperCmd) getArgsList(args string) []string {
	segs := strings.Split(args, "/")
	if len(segs) < 2 {
		return nil
	}
	return segs[1:]
}

func (self *CasperCmd) Finished() bool {
	return self.isKill || self.isFinish
}

func (self *CasperCmd) run() {
	self.isFinish = false
	self.isKill = false

	path := "./" + self.tmpl + "/" + self.id
	os.RemoveAll(path)
	if err := os.MkdirAll(path, 0755); err != nil {
		log.Fatalln("can not create", path, err)
	}

	cookieFile, err := os.Create(path + "/cookie.txt")
	defer cookieFile.Close()
	var cmd *exec.Cmd
	if len(self.proxyServer) == 0 {
		cmd = exec.Command("casperjs", self.tmpl+".js", "--web-security=no", "--cookies-file="+path+"/cookie.txt", "--context="+path)
	} else {
		cmd = exec.Command("casperjs", self.tmpl+".js", "--web-security=no", "--cookies-file="+path+"/cookie.txt", "--proxy="+self.proxyServer, "--proxy-type=http", "--context="+path)
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Panicln("can not get stdout pipe:", err)
	}
	bufout := bufio.NewReader(stdout)

	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Panicln("can not get stdin pipe:", err)
	}

	bufin := bufio.NewWriter(stdin)
	if err := cmd.Start(); err != nil {
		log.Panicln("can not start cmd:", err)
	}

	log.Println("begin read line from capser")
	for {
		line, err := bufout.ReadString('\n')
		if err != nil {
			log.Println(err)
			cmd.Process.Wait()
			cmd.Process.Kill()
			break
		}
		log.Println(line)

		if strings.HasPrefix(line, "CMD GET ARGS") {
			for _, v := range self.getArgsList(line) {
				key := strings.TrimRight(v, "\n")
				bufin.WriteString(self.GetArgsValue(key))
				delete(self.args, key)
				bufin.WriteRune('\n')
				bufin.Flush()
			}
			continue
		}

		if strings.HasPrefix(line, "CMD INFO RANDCODE") {
			message := make(map[string]interface{})
			message["id"] = self.GetArgsValue("id")
			result := strings.TrimPrefix(line, "CMD INFO RANDCODE")
			result = strings.Trim(result, " \n")
			message["result"] = result
			message[kJobStatus] = kJobOndoing
			log.Println("send result:", message)
			self.message <- message
			continue
		}

		if strings.HasPrefix(line, "CMD INFO CONTENT") {
			message := make(map[string]interface{})
			message["id"] = self.GetArgsValue("id")
			result := strings.TrimPrefix(line, "CMD INFO CONTENT")
			result = strings.Trim(result, " \n")
			message["result"] = result
			var out map[string]interface{}
			err := json.Unmarshal([]byte(result), &out)
			if err == nil {
				message["json"] = out
			}
			message[kJobStatus] = kJobFinished
			log.Println("send result:", message)
			self.message <- message
			self.status = kCommandStatusIdle
		}

		if strings.HasPrefix(line, "CMD EXIT") {
			message := make(map[string]interface{})
			message["id"] = self.GetArgsValue("id")
			message[kJobStatus] = kJobFailed
			log.Println("send result:", message)
			self.message <- message
			self.status = kCommandStatusIdle
			cmd.Process.Wait()
			cmd.Process.Kill()
		}
	}
	self.isFinish = true
}
<commit_msg>sleep 1 minute after success<commit_after>package caspercloud

import (
	"bufio"
	"encoding/json"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"
)

type CasperCmd struct {
	proxyServer string
	id          string
	tmpl        string
	message     chan map[string]interface{}
	input       chan map[string]string
	isKill      bool
	isFinish    bool
	args        map[string]string
	status      int
}

func NewCasperCmd(id, tmpl, proxyServer string) *CasperCmd {
	ret := &CasperCmd{
		proxyServer: proxyServer,
		id:          id,
		tmpl:        tmpl,
		message:     make(chan map[string]interface{}, 1),
		input:       make(chan map[string]string, 1),
		args:        make(map[string]string),
		status:      kCommandStatusIdle,
	}
	go ret.run()
	return ret
}

func (self *CasperCmd) GetId() string {
	return self.id
}

func (self *CasperCmd) GetStatus() int {
	return self.status
}

func (self *CasperCmd) SetInputArgs(input map[string]string) {
	if self.Finished() {
		log.Println("start another casperjs")
		go self.run()
	}
	log.Println("insert input:", input)
	self.input <- input
}

func (self *CasperCmd) GetMessage() map[string]interface{} {
	return <-self.message
}

func (self *CasperCmd) readInputArgs(key string) string {
	args := <-self.input
	log.Println("read args:", args)
	for k, v := range args {
		self.args[k] = v
	}
	if val, ok := self.args[key]; ok {
		return val
	}

	message := make(map[string]interface{})
	message["id"] = self.GetArgsValue("id")
	message["need_args"] = key
	message[kJobStatus] = kJobOndoing
	self.message <- message

	return ""
}

func (self *CasperCmd) GetArgsValue(key string) string {
	if val, ok := self.args[key]; ok {
		log.Println("successfully get args value", val)
		return val
	}
	for {
		val := self.readInputArgs(key)
		if len(val) != 0 {
			log.Println("successfully get args value", val)
			return val
		}
	}
	return ""
}

func (self *CasperCmd) getArgsList(args string) []string {
	segs := strings.Split(args, "/")
	if len(segs) < 2 {
		return nil
	}
	return segs[1:]
}

func (self *CasperCmd) Finished() bool {
	return self.isKill || self.isFinish
}

func (self *CasperCmd) run() {
	self.isFinish = false
	self.isKill = false

	path := "./" + self.tmpl + "/" + self.id
	os.RemoveAll(path)
	if err := os.MkdirAll(path, 0755); err != nil {
		log.Fatalln("can not create", path, err)
	}

	cookieFile, err := os.Create(path + "/cookie.txt")
	defer cookieFile.Close()
	var cmd *exec.Cmd
	if len(self.proxyServer) == 0 {
		cmd = exec.Command("casperjs", self.tmpl+".js", "--web-security=no", "--cookies-file="+path+"/cookie.txt", "--context="+path)
	} else {
		cmd = exec.Command("casperjs", self.tmpl+".js", "--web-security=no", "--cookies-file="+path+"/cookie.txt", "--proxy="+self.proxyServer, "--proxy-type=http", "--context="+path)
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		log.Panicln("can not get stdout pipe:", err)
	}
	bufout := bufio.NewReader(stdout)

	stdin, err := cmd.StdinPipe()
	if err != nil {
		log.Panicln("can not get stdin pipe:", err)
	}

	bufin := bufio.NewWriter(stdin)
	if err := cmd.Start(); err != nil {
		log.Panicln("can not start cmd:", err)
	}

	log.Println("begin read line from capser")
	for {
		line, err := bufout.ReadString('\n')
		if err != nil {
			log.Println(err)
			cmd.Process.Wait()
			cmd.Process.Kill()
			break
		}
		log.Println(line)

		if strings.HasPrefix(line, "CMD GET ARGS") {
			for _, v := range self.getArgsList(line) {
				key := strings.TrimRight(v, "\n")
				bufin.WriteString(self.GetArgsValue(key))
				delete(self.args, key)
				bufin.WriteRune('\n')
				bufin.Flush()
			}
			continue
		}

		if strings.HasPrefix(line, "CMD INFO RANDCODE") {
			message := make(map[string]interface{})
			message["id"] = self.GetArgsValue("id")
			result := strings.TrimPrefix(line, "CMD INFO RANDCODE")
			result = strings.Trim(result, " \n")
			message["result"] = result
			message[kJobStatus] = kJobOndoing
			log.Println("send result:", message)
			self.message <- message
			continue
		}

		if strings.HasPrefix(line, "CMD INFO CONTENT") {
			message := make(map[string]interface{})
			message["id"] = self.GetArgsValue("id")
			result := strings.TrimPrefix(line, "CMD INFO CONTENT")
			result = strings.Trim(result, " \n")
			message["result"] = result
			var out map[string]interface{}
			err := json.Unmarshal([]byte(result), &out)
			if err == nil {
				message["json"] = out
			}
			message[kJobStatus] = kJobFinished
			log.Println("send result:", message)
			self.message <- message
			time.Sleep(time.Minute)
			self.status = kCommandStatusIdle
		}

		if strings.HasPrefix(line, "CMD EXIT") {
			message := make(map[string]interface{})
			message["id"] = self.GetArgsValue("id")
			message[kJobStatus] = kJobFailed
			log.Println("send result:", message)
			self.message <- message
			self.status = kCommandStatusIdle
			cmd.Process.Wait()
			cmd.Process.Kill()
		}
	}
	self.isFinish = true
}
<|endoftext|>