<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	conf "github.com/dns-gh/flagsconfig"
	"github.com/dns-gh/tojson"
)

type nasaClient struct {
	apiKey      string
	firstOffset int
	offset      int
	poll        time.Duration
	path        string
	body        string // orbiting body to watch
	debug       bool
}

func (n *nasaClient) hasDefaultKey() bool {
	return n.apiKey == nasaAPIDefaultKey
}

func makeNasaClient(config *conf.Config) *nasaClient {
	apiKey := os.Getenv("NASA_API_KEY")
	if len(apiKey) == 0 {
		apiKey = nasaAPIDefaultKey
	}
	return &nasaClient{
		apiKey:      apiKey,
		firstOffset: parseInt(config.Get(firstOffsetFlag)),
		offset:      parseInt(config.Get(offsetFlag)),
		poll:        parseDuration(config.Get(pollFrequencyFlag)),
		path:        config.Get(nasaPathFlag),
		debug:       parseBool(config.Get(debugFlag)),
	}
}

type links struct {
	Next string `json:"next"`
	Prev string `json:"prev"`
	Self string `json:"self"`
}

type diameter struct {
	EstimatedDiameterMin float64 `json:"estimated_diameter_min"`
	EstimatedDiameterMax float64 `json:"estimated_diameter_max"`
}

type estimatedDiameter struct {
	Kilometers diameter `json:"kilometers"`
	Meters     diameter `json:"meters"`
	Miles      diameter `json:"miles"`
	Feet       diameter `json:"feet"`
}

type relativeVelocity struct {
	KilometersPerSecond string `json:"kilometers_per_second"`
	KilometersPerHour   string `json:"kilometers_per_hour"`
	MilesPerHour        string `json:"miles_per_hour"`
}

type missDistance struct {
	Astronomical string `json:"astronomical"`
	Lunar        string `json:"lunar"`
	Kilometers   string `json:"kilometers"`
	Miles        string `json:"miles"`
}

type closeApprochInfo struct {
	CloseApproachDate      string           `json:"close_approach_date"`
	EpochDateCloseApproach int64            `json:"epoch_date_close_approach"`
	RelativeVelocity       relativeVelocity `json:"relative_velocity"`
	MissDistance           missDistance     `json:"miss_distance"`
	OrbitingBody           string           `json:"orbiting_body"`
}

type object struct {
	Links                          links              `json:"links"`
	NeoReferenceID                 string             `json:"neo_reference_id"`
	Name                           string             `json:"name"`
	NasaJplURL                     string             `json:"nasa_jpl_url"`
	AbsoluteMagnitudeH             float64            `json:"absolute_magnitude_h"`
	EstimatedDiameter              estimatedDiameter  `json:"estimated_diameter"`
	IsPotentiallyHazardousAsteroid bool               `json:"is_potentially_hazardous_asteroid"`
	CloseApproachData              []closeApprochInfo `json:"close_approach_data"`
}

// SpaceRocks (asteroids) represents all asteroids data available between two dates.
// The information is stored in the NearEarthObjects map.
// [Generated with the help of https://mholt.github.io/json-to-go/]
type SpaceRocks struct {
	Links        links `json:"links"`
	ElementCount int   `json:"element_count"`
	// the key of the NearEarthObjects map represents a date with the following format YYYY-MM-DD
	NearEarthObjects map[string][]object `json:"near_earth_objects"`
}

func (n *nasaClient) load() ([]object, error) {
	objects := &[]object{}
	if _, err := os.Stat(n.path); os.IsNotExist(err) {
		tojson.Save(n.path, objects)
	}
	err := tojson.Load(n.path, objects)
	if err != nil {
		return nil, err
	}
	return *objects, nil
}

func merge(previous, current []object) ([]object, []object) {
	merged := []object{}
	diff := []object{}
	added := map[string]struct{}{}
	for _, v := range previous {
		added[v.NeoReferenceID] = struct{}{}
		merged = append(merged, v)
	}
	for _, v := range current {
		if _, ok := added[v.NeoReferenceID]; ok {
			continue
		}
		added[v.NeoReferenceID] = struct{}{}
		merged = append(merged, v)
		diff = append(diff, v)
	}
	return merged, diff
}

func (n *nasaClient) update(current []object) ([]object, error) {
	previous, err := n.load()
	if err != nil {
		return nil, err
	}
	merged, diff := merge(previous, current)
	tojson.Save(n.path, merged)
	return diff, nil
}

func (n *nasaClient) fetchRocks(days int) (*SpaceRocks, error) {
	if days > 7 {
		return nil, fmt.Errorf(fetchMaxSizeError)
	} else if days < -7 {
		return nil, fmt.Errorf(fetchMaxSizeError)
	}
	now := time.Now()
	start := ""
	end := ""
	if days >= 0 {
		start = now.Format(nasaTimeFormat)
		end = now.AddDate(0, 0, days).Format(nasaTimeFormat)
	} else {
		start = now.AddDate(0, 0, days).Format(nasaTimeFormat)
		end = now.Format(nasaTimeFormat)
	}
	url := nasaAsteroidsAPIGet +
		n.apiKey +
		"&start_date=" + start +
		"&end_date=" + end
	resp, err := http.Get(url)
	if err != nil {
		log.Fatalln(err.Error())
	}
	defer resp.Body.Close()
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if strings.Contains(string(bytes), "OVER_RATE_LIMIT") {
		return nil, fmt.Errorf("http get rate limit reached, wait of use a proper key instead of the default one")
	}

	spacerocks := &SpaceRocks{}
	json.Unmarshal(bytes, spacerocks)
	return spacerocks, nil
}

func (n *nasaClient) getDangerousRocks(offset int) ([]object, error) {
	rocks, err := n.fetchRocks(offset)
	if err != nil {
		return nil, err
	}
	dangerous := map[int64]object{}
	keys := []int64{}
	for _, v := range rocks.NearEarthObjects {
		if len(v) != 0 {
			for _, object := range v {
				if object.IsPotentiallyHazardousAsteroid {
					if len(object.CloseApproachData) != 0 &&
						object.CloseApproachData[0].OrbitingBody == n.body {
						t := parseTime(object.CloseApproachData[0].CloseApproachDate, nasaTimeFormat)
						timestamp := t.UnixNano()
						dangerous[timestamp] = object
						keys = append(keys, timestamp)
					}
				}
			}
		}
	}
	quickSort(keys)
	objects := []object{}
	for _, key := range keys {
		objects = append(objects, dangerous[key])
	}
	return objects, nil
}

func (n *nasaClient) sleep() {
	if !n.debug {
		sleep(maxRandTimeSleepBetweenTweets)
	}
}

func (n *nasaClient) fetch(offset int) ([]string, error) {
	log.Println("[nasa] checking nasa rocks...")
	current, err := n.getDangerousRocks(offset)
	if err != nil {
		return nil, err
	}
	log.Println("[nasa] found", len(current), "potential rocks to tweet")
	// TODO only merge and save asteroids once they are tweeted ?
	diff, err := n.update(current)
	if err != nil {
		return nil, err
	}
	formatedDiff := []string{}
	for _, object := range diff {
		sleep(maxRandTimeSleepBetweenTweets)
		closeData := object.CloseApproachData[0]
		approachDate := parseTime(closeData.CloseApproachDate, nasaTimeFormat)
		// extract lisible name
		name := object.Name
		parts := strings.SplitN(object.Name, " ", 2)
		if len(parts) == 2 {
			name = parts[1]
		}
		// extract lisible speed
		speed := closeData.RelativeVelocity.KilometersPerSecond
		parts = strings.Split(speed, ".")
		if len(parts) == 2 && len(parts[1]) > 2 {
			speed = parts[0] + "." + parts[1][0:1]
		}
		// extract lisible month
		month := approachDate.Month().String()
		if len(month) >= 3 {
			month = month[0:3]
		}
		// build status message
		statusMsg := fmt.Sprintf("A #%s #asteroid %s, Ø ~%.2f km and ~%s km/s is coming close to #%s on %s. %02d (details here %s)",
			getRandomElement(asteroidsQualificativeAdjective),
			name,
			(object.EstimatedDiameter.Kilometers.EstimatedDiameterMin+object.EstimatedDiameter.Kilometers.EstimatedDiameterMax)/2,
			speed,
			n.body,
			month,
			approachDate.Day(),
			object.NasaJplURL)
		formatedDiff = append(formatedDiff, statusMsg)
	}
	return formatedDiff, nil
}
<commit_msg>[srb] fix nasa body watch bug<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"time"

	conf "github.com/dns-gh/flagsconfig"
	"github.com/dns-gh/tojson"
)

type nasaClient struct {
	apiKey      string
	firstOffset int
	offset      int
	poll        time.Duration
	path        string
	body        string // orbiting body to watch
	debug       bool
}

func (n *nasaClient) hasDefaultKey() bool {
	return n.apiKey == nasaAPIDefaultKey
}

func makeNasaClient(config *conf.Config) *nasaClient {
	apiKey := os.Getenv("NASA_API_KEY")
	if len(apiKey) == 0 {
		apiKey = nasaAPIDefaultKey
	}
	return &nasaClient{
		apiKey:      apiKey,
		firstOffset: parseInt(config.Get(firstOffsetFlag)),
		offset:      parseInt(config.Get(offsetFlag)),
		poll:        parseDuration(config.Get(pollFrequencyFlag)),
		path:        config.Get(nasaPathFlag),
		body:        config.Get(bodyFlag),
		debug:       parseBool(config.Get(debugFlag)),
	}
}

type links struct {
	Next string `json:"next"`
	Prev string `json:"prev"`
	Self string `json:"self"`
}

type diameter struct {
	EstimatedDiameterMin float64 `json:"estimated_diameter_min"`
	EstimatedDiameterMax float64 `json:"estimated_diameter_max"`
}

type estimatedDiameter struct {
	Kilometers diameter `json:"kilometers"`
	Meters     diameter `json:"meters"`
	Miles      diameter `json:"miles"`
	Feet       diameter `json:"feet"`
}

type relativeVelocity struct {
	KilometersPerSecond string `json:"kilometers_per_second"`
	KilometersPerHour   string `json:"kilometers_per_hour"`
	MilesPerHour        string `json:"miles_per_hour"`
}

type missDistance struct {
	Astronomical string `json:"astronomical"`
	Lunar        string `json:"lunar"`
	Kilometers   string `json:"kilometers"`
	Miles        string `json:"miles"`
}

type closeApprochInfo struct {
	CloseApproachDate      string           `json:"close_approach_date"`
	EpochDateCloseApproach int64            `json:"epoch_date_close_approach"`
	RelativeVelocity       relativeVelocity `json:"relative_velocity"`
	MissDistance           missDistance     `json:"miss_distance"`
	OrbitingBody           string           `json:"orbiting_body"`
}

type object struct {
	Links                          links              `json:"links"`
	NeoReferenceID                 string             `json:"neo_reference_id"`
	Name                           string             `json:"name"`
	NasaJplURL                     string             `json:"nasa_jpl_url"`
	AbsoluteMagnitudeH             float64            `json:"absolute_magnitude_h"`
	EstimatedDiameter              estimatedDiameter  `json:"estimated_diameter"`
	IsPotentiallyHazardousAsteroid bool               `json:"is_potentially_hazardous_asteroid"`
	CloseApproachData              []closeApprochInfo `json:"close_approach_data"`
}

// SpaceRocks (asteroids) represents all asteroids data available between two dates.
// The information is stored in the NearEarthObjects map.
// [Generated with the help of https://mholt.github.io/json-to-go/]
type SpaceRocks struct {
	Links        links `json:"links"`
	ElementCount int   `json:"element_count"`
	// the key of the NearEarthObjects map represents a date with the following format YYYY-MM-DD
	NearEarthObjects map[string][]object `json:"near_earth_objects"`
}

func (n *nasaClient) load() ([]object, error) {
	objects := &[]object{}
	if _, err := os.Stat(n.path); os.IsNotExist(err) {
		tojson.Save(n.path, objects)
	}
	err := tojson.Load(n.path, objects)
	if err != nil {
		return nil, err
	}
	return *objects, nil
}

func merge(previous, current []object) ([]object, []object) {
	merged := []object{}
	diff := []object{}
	added := map[string]struct{}{}
	for _, v := range previous {
		added[v.NeoReferenceID] = struct{}{}
		merged = append(merged, v)
	}
	for _, v := range current {
		if _, ok := added[v.NeoReferenceID]; ok {
			continue
		}
		added[v.NeoReferenceID] = struct{}{}
		merged = append(merged, v)
		diff = append(diff, v)
	}
	return merged, diff
}

func (n *nasaClient) update(current []object) ([]object, error) {
	previous, err := n.load()
	if err != nil {
		return nil, err
	}
	merged, diff := merge(previous, current)
	tojson.Save(n.path, merged)
	return diff, nil
}

func (n *nasaClient) fetchRocks(days int) (*SpaceRocks, error) {
	if days > 7 {
		return nil, fmt.Errorf(fetchMaxSizeError)
	} else if days < -7 {
		return nil, fmt.Errorf(fetchMaxSizeError)
	}
	now := time.Now()
	start := ""
	end := ""
	if days >= 0 {
		start = now.Format(nasaTimeFormat)
		end = now.AddDate(0, 0, days).Format(nasaTimeFormat)
	} else {
		start = now.AddDate(0, 0, days).Format(nasaTimeFormat)
		end = now.Format(nasaTimeFormat)
	}
	url := nasaAsteroidsAPIGet +
		n.apiKey +
		"&start_date=" + start +
		"&end_date=" + end
	resp, err := http.Get(url)
	if err != nil {
		log.Fatalln(err.Error())
	}
	defer resp.Body.Close()
	bytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	if strings.Contains(string(bytes), "OVER_RATE_LIMIT") {
		return nil, fmt.Errorf("http get rate limit reached, wait of use a proper key instead of the default one")
	}

	spacerocks := &SpaceRocks{}
	json.Unmarshal(bytes, spacerocks)
	return spacerocks, nil
}

func (n *nasaClient) getDangerousRocks(offset int) ([]object, error) {
	rocks, err := n.fetchRocks(offset)
	if err != nil {
		return nil, err
	}
	dangerous := map[int64]object{}
	keys := []int64{}
	for _, v := range rocks.NearEarthObjects {
		if len(v) != 0 {
			for _, object := range v {
				if object.IsPotentiallyHazardousAsteroid {
					if len(object.CloseApproachData) != 0 &&
						object.CloseApproachData[0].OrbitingBody == n.body {
						t := parseTime(object.CloseApproachData[0].CloseApproachDate, nasaTimeFormat)
						timestamp := t.UnixNano()
						dangerous[timestamp] = object
						keys = append(keys, timestamp)
					}
				}
			}
		}
	}
	quickSort(keys)
	objects := []object{}
	for _, key := range keys {
		objects = append(objects, dangerous[key])
	}
	return objects, nil
}

func (n *nasaClient) sleep() {
	if !n.debug {
		sleep(maxRandTimeSleepBetweenTweets)
	}
}

func (n *nasaClient) fetch(offset int) ([]string, error) {
	log.Println("[nasa] checking nasa rocks...")
	current, err := n.getDangerousRocks(offset)
	if err != nil {
		return nil, err
	}
	log.Println("[nasa] found", len(current), "potential rocks to tweet")
	// TODO only merge and save asteroids once they are tweeted ?
	diff, err := n.update(current)
	if err != nil {
		return nil, err
	}
	formatedDiff := []string{}
	for _, object := range diff {
		sleep(maxRandTimeSleepBetweenTweets)
		closeData := object.CloseApproachData[0]
		approachDate := parseTime(closeData.CloseApproachDate, nasaTimeFormat)
		// extract lisible name
		name := object.Name
		parts := strings.SplitN(object.Name, " ", 2)
		if len(parts) == 2 {
			name = parts[1]
		}
		// extract lisible speed
		speed := closeData.RelativeVelocity.KilometersPerSecond
		parts = strings.Split(speed, ".")
		if len(parts) == 2 && len(parts[1]) > 2 {
			speed = parts[0] + "." + parts[1][0:1]
		}
		// extract lisible month
		month := approachDate.Month().String()
		if len(month) >= 3 {
			month = month[0:3]
		}
		// build status message
		statusMsg := fmt.Sprintf("A #%s #asteroid %s, Ø ~%.2f km and ~%s km/s is coming close to #%s on %s. %02d (details here %s)",
			getRandomElement(asteroidsQualificativeAdjective),
			name,
			(object.EstimatedDiameter.Kilometers.EstimatedDiameterMin+object.EstimatedDiameter.Kilometers.EstimatedDiameterMax)/2,
			speed,
			n.body,
			month,
			approachDate.Day(),
			object.NasaJplURL)
		formatedDiff = append(formatedDiff, statusMsg)
	}
	return formatedDiff, nil
}
<|endoftext|>


<commit_before>package vm

import (
	"sync"
	"sync/atomic"
)

type thunkState int32

const (
	illegal thunkState = iota
	normal
	locked
	app
)

type Thunk struct {
	result   Object
	function *Thunk
	// `args` is represented as a slice but not a List to let users optimize
	// their functions. If you want to define functions with arguments fully
	// lazy, just create a function which takes only a thunk of a List as a
	// argument.
	args      []*Thunk
	state     thunkState
	blackHole sync.WaitGroup
}

func Normal(o Object) *Thunk {
	checkObject("Normal's argument", o)

	return &Thunk{result: o, state: normal}
}

func App(f *Thunk, args ...*Thunk) *Thunk {
	t := &Thunk{function: f, args: args, state: app}
	t.blackHole.Add(1)
	return t
}

func (t *Thunk) Eval() Object { // return WHNF
	if t.lock() {
		children := make([]*Thunk, 0, 1) // TODO: best capacity?

		for {
			o := t.function.Eval()
			f, ok := o.(callable)

			if !ok {
				t.result = notCallableError(o)
				break
			}

			t.result = f.call(t.args...)
			child, ok := t.result.(*Thunk)

			if !ok {
				break
			}

			t.function, t.args, ok = child.delegateEval()

			if !ok {
				t.result = child.Eval()
				break
			}

			children = append(children, child)
		}

		checkObject("Thunk.result", t.result)

		for _, child := range children {
			child.result = t.result
			child.finalize()
		}

		t.finalize()
	} else {
		t.blackHole.Wait()
	}

	checkObject("Thunk.result", t.result)

	return t.result
}

func (t *Thunk) lock() bool {
	return t.compareAndSwapState(app, locked)
}

func (t *Thunk) delegateEval() (*Thunk, []*Thunk, bool) {
	if t.lock() {
		return t.function, t.args, true
	}

	return nil, nil, false
}

func (t *Thunk) finalize() {
	t.function = nil
	t.args = nil
	t.storeState(normal)
	t.blackHole.Done()
}

func (t *Thunk) compareAndSwapState(old, new thunkState) bool {
	return atomic.CompareAndSwapInt32((*int32)(&t.state), int32(old), int32(new))
}

func (t *Thunk) storeState(new thunkState) {
	atomic.StoreInt32((*int32)(&t.state), int32(new))
}

func checkObject(s string, o Object) {
	if _, ok := o.(*Thunk); ok {
		panic(s + " is *Thunk.")
	}
}
<commit_msg>Comment on unnecessary thunk states<commit_after>package vm

import (
	"sync"
	"sync/atomic"
)

type thunkState int32

const (
	// States of illegal and locked are unnecessary and provided only for
	// debuggability.
	illegal thunkState = iota
	normal
	locked
	app
)

type Thunk struct {
	result   Object
	function *Thunk
	// `args` is represented as a slice but not a List to let users optimize
	// their functions. If you want to define functions with arguments fully
	// lazy, just create a function which takes only a thunk of a List as a
	// argument.
	args      []*Thunk
	state     thunkState
	blackHole sync.WaitGroup
}

func Normal(o Object) *Thunk {
	checkObject("Normal's argument", o)

	return &Thunk{result: o, state: normal}
}

func App(f *Thunk, args ...*Thunk) *Thunk {
	t := &Thunk{function: f, args: args, state: app}
	t.blackHole.Add(1)
	return t
}

func (t *Thunk) Eval() Object { // return WHNF
	if t.lock() {
		children := make([]*Thunk, 0, 1) // TODO: best capacity?

		for {
			o := t.function.Eval()
			f, ok := o.(callable)

			if !ok {
				t.result = notCallableError(o)
				break
			}

			t.result = f.call(t.args...)
			child, ok := t.result.(*Thunk)

			if !ok {
				break
			}

			t.function, t.args, ok = child.delegateEval()

			if !ok {
				t.result = child.Eval()
				break
			}

			children = append(children, child)
		}

		checkObject("Thunk.result", t.result)

		for _, child := range children {
			child.result = t.result
			child.finalize()
		}

		t.finalize()
	} else {
		t.blackHole.Wait()
	}

	checkObject("Thunk.result", t.result)

	return t.result
}

func (t *Thunk) lock() bool {
	return t.compareAndSwapState(app, locked)
}

func (t *Thunk) delegateEval() (*Thunk, []*Thunk, bool) {
	if t.lock() {
		return t.function, t.args, true
	}

	return nil, nil, false
}

func (t *Thunk) finalize() {
	t.function = nil
	t.args = nil
	t.storeState(normal)
	t.blackHole.Done()
}

func (t *Thunk) compareAndSwapState(old, new thunkState) bool {
	return atomic.CompareAndSwapInt32((*int32)(&t.state), int32(old), int32(new))
}

func (t *Thunk) storeState(new thunkState) {
	atomic.StoreInt32((*int32)(&t.state), int32(new))
}

func checkObject(s string, o Object) {
	if _, ok := o.(*Thunk); ok {
		panic(s + " is *Thunk.")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/toomore/gogrs/tradingdays"
)

// Log is show viwer log.
func Log(req *http.Request) {
	log.Println(req.URL, req.RemoteAddr, req.UserAgent(), req.Form, req.Referer())
}

// Home is home page.
func Home(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Write([]byte("查詢台灣股市是否開市
"))
	w.Write([]byte(fmt.Sprintf("範例
", time.Now().Unix())))
	w.Write([]byte("[Docs] [github]
"))

	Log(req)
}

type tradeJSON struct {
	Date time.Time `json:"date"`
	Open bool      `json:"open"`
}

type errorJSON struct {
	Error string `json:"error"`
}

// TradeOpen is "./open" page.
func TradeOpen(w http.ResponseWriter, req *http.Request) {
	var jsonStr []byte

	if data, err := strconv.ParseInt(req.FormValue("q"), 10, 64); err != nil {
		jsonStr, _ = json.Marshal(&errorJSON{Error: "Wrong date format"})
	} else {
		if csvcachetime.InCache(defaultcachetime) != true {
			tradingdays.DownloadCSV(true)
			csvcachetime.Set()
			log.Println("DownloadCSV.")
		}

		date := time.Unix(data, 0)
		jsonStr, _ = json.Marshal(&tradeJSON{
			Date: date.UTC(),
			Open: tradingdays.IsOpen(date.Year(), date.Month(), date.Day(), date.Location())})
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(jsonStr)
	Log(req)
}

type cachetime struct {
	timestamp int64
}

func (c *cachetime) Set() {
	c.timestamp = time.Now().Unix()
}

func (c *cachetime) InCache(seconds int64) bool {
	result := (time.Now().Unix() - c.timestamp) <= seconds
	if result != true {
		c.timestamp = 0
	}
	return result
}

var csvcachetime cachetime

var httpport string
var defaultcachetime int64

func init() {
	tradingdays.DownloadCSV(true)
	log.Println("Init DownloadCSV.")
	csvcachetime.Set()
	flag.StringVar(&httpport, "http", ":59123", "HTTP service address (e.g., ':59123')")
	flag.Int64Var(&defaultcachetime, "csvcachetime", 21600, "CSV cache time.")
}

func main() {
	flag.Parse()
	log.Println("http:", httpport, "csvcachetime:", defaultcachetime)
	http.HandleFunc("/", Home)
	http.HandleFunc("/open", TradeOpen)
	log.Fatal(http.ListenAndServe(httpport, nil))
}
<commit_msg>Tiny changed in userIP.<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"strconv"
	"time"

	"github.com/toomore/gogrs/tradingdays"
)

// Log is show viwer log.
func Log(req *http.Request) {
	var userIP string
	if userIP = req.Header.Get("X-FORWARDED-FOR"); userIP == "" {
		userIP = req.RemoteAddr
	}
	log.Println(req.URL, userIP, req.UserAgent(), req.Form, req.Referer())
}

// Home is home page.
func Home(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.Write([]byte("查詢台灣股市是否開市
"))
	w.Write([]byte(fmt.Sprintf("範例
", time.Now().Unix())))
	w.Write([]byte("[Docs] [github]
"))

	Log(req)
}

type tradeJSON struct {
	Date time.Time `json:"date"`
	Open bool      `json:"open"`
}

type errorJSON struct {
	Error string `json:"error"`
}

// TradeOpen is "./open" page.
func TradeOpen(w http.ResponseWriter, req *http.Request) {
	var jsonStr []byte

	if data, err := strconv.ParseInt(req.FormValue("q"), 10, 64); err != nil {
		jsonStr, _ = json.Marshal(&errorJSON{Error: "Wrong date format"})
	} else {
		if csvcachetime.InCache(defaultcachetime) != true {
			tradingdays.DownloadCSV(true)
			csvcachetime.Set()
			log.Println("DownloadCSV.")
		}

		date := time.Unix(data, 0)
		jsonStr, _ = json.Marshal(&tradeJSON{
			Date: date.UTC(),
			Open: tradingdays.IsOpen(date.Year(), date.Month(), date.Day(), date.Location())})
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(jsonStr)
	Log(req)
}

type cachetime struct {
	timestamp int64
}

func (c *cachetime) Set() {
	c.timestamp = time.Now().Unix()
}

func (c *cachetime) InCache(seconds int64) bool {
	result := (time.Now().Unix() - c.timestamp) <= seconds
	if result != true {
		c.timestamp = 0
	}
	return result
}

var csvcachetime cachetime

var httpport string
var defaultcachetime int64

func init() {
	tradingdays.DownloadCSV(true)
	log.Println("Init DownloadCSV.")
	csvcachetime.Set()
	flag.StringVar(&httpport, "http", ":59123", "HTTP service address (e.g., ':59123')")
	flag.Int64Var(&defaultcachetime, "csvcachetime", 21600, "CSV cache time.")
}

func main() {
	flag.Parse()
	log.Println("http:", httpport, "csvcachetime:", defaultcachetime)
	http.HandleFunc("/", Home)
	http.HandleFunc("/open", TradeOpen)
	log.Fatal(http.ListenAndServe(httpport, nil))
}
<|endoftext|>


<commit_before>package opts

import (
	"encoding/csv"
	"fmt"
	"os"
	"strconv"
	"strings"

	mounttypes "github.com/docker/docker/api/types/mount"
	"github.com/docker/go-units"
)

// MountOpt is a Value type for parsing mounts
type MountOpt struct {
	values []mounttypes.Mount
}

// Set a new mount value
func (m *MountOpt) Set(value string) error {
	csvReader := csv.NewReader(strings.NewReader(value))
	fields, err := csvReader.Read()
	if err != nil {
		return err
	}

	mount := mounttypes.Mount{}

	volumeOptions := func() *mounttypes.VolumeOptions {
		if mount.VolumeOptions == nil {
			mount.VolumeOptions = &mounttypes.VolumeOptions{
				Labels: make(map[string]string),
			}
		}
		if mount.VolumeOptions.DriverConfig == nil {
			mount.VolumeOptions.DriverConfig = &mounttypes.Driver{}
		}
		return mount.VolumeOptions
	}

	bindOptions := func() *mounttypes.BindOptions {
		if mount.BindOptions == nil {
			mount.BindOptions = new(mounttypes.BindOptions)
		}
		return mount.BindOptions
	}

	tmpfsOptions := func() *mounttypes.TmpfsOptions {
		if mount.TmpfsOptions == nil {
			mount.TmpfsOptions = new(mounttypes.TmpfsOptions)
		}
		return mount.TmpfsOptions
	}

	setValueOnMap := func(target map[string]string, value string) {
		parts := strings.SplitN(value, "=", 2)
		if len(parts) == 1 {
			target[value] = ""
		} else {
			target[parts[0]] = parts[1]
		}
	}

	mount.Type = mounttypes.TypeVolume // default to volume mounts
	// Set writable as the default
	for _, field := range fields {
		parts := strings.SplitN(field, "=", 2)
		key := strings.ToLower(parts[0])

		if len(parts) == 1 {
			switch key {
			case "readonly", "ro":
				mount.ReadOnly = true
				continue
			case "volume-nocopy":
				volumeOptions().NoCopy = true
				continue
			}
		}

		if len(parts) != 2 {
			return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
		}

		value := parts[1]
		switch key {
		case "type":
			mount.Type = mounttypes.Type(strings.ToLower(value))
		case "source", "src":
			mount.Source = value
		case "target", "dst", "destination":
			mount.Target = value
		case "readonly", "ro":
			mount.ReadOnly, err = strconv.ParseBool(value)
			if err != nil {
				return fmt.Errorf("invalid value for %s: %s", key, value)
			}
		case "bind-propagation":
			bindOptions().Propagation = mounttypes.Propagation(strings.ToLower(value))
		case "volume-nocopy":
			volumeOptions().NoCopy, err = strconv.ParseBool(value)
			if err != nil {
				return fmt.Errorf("invalid value for populate: %s", value)
			}
		case "volume-label":
			setValueOnMap(volumeOptions().Labels, value)
		case "volume-driver":
			volumeOptions().DriverConfig.Name = value
		case "volume-opt":
			if volumeOptions().DriverConfig.Options == nil {
				volumeOptions().DriverConfig.Options = make(map[string]string)
			}
			setValueOnMap(volumeOptions().DriverConfig.Options, value)
		case "tmpfs-size":
			sizeBytes, err := units.RAMInBytes(value)
			if err != nil {
				return fmt.Errorf("invalid value for %s: %s", key, value)
			}
			tmpfsOptions().SizeBytes = sizeBytes
		case "tmpfs-mode":
			ui64, err := strconv.ParseUint(value, 8, 32)
			if err != nil {
				return fmt.Errorf("invalid value for %s: %s", key, value)
			}
			tmpfsOptions().Mode = os.FileMode(ui64)
		default:
			return fmt.Errorf("unexpected key '%s' in '%s'", key, field)
		}
	}

	if mount.Type == "" {
		return fmt.Errorf("type is required")
	}

	if mount.Target == "" {
		return fmt.Errorf("target is required")
	}

	if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume {
		return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mount.Type)
	}
	if mount.BindOptions != nil && mount.Type != mounttypes.TypeBind {
		return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type)
	}
	if mount.TmpfsOptions != nil && mount.Type != mounttypes.TypeTmpfs {
		return fmt.Errorf("cannot mix 'tmpfs-*' options with mount type '%s'", mount.Type)
	}

	m.values = append(m.values, mount)
	return nil
}

// Type returns the type of this option
func (m *MountOpt) Type() string {
	return "mount"
}

// String returns a string repr of this option
func (m *MountOpt) String() string {
	mounts := []string{}
	for _, mount := range m.values {
		repr := fmt.Sprintf("%s %s %s", mount.Type, mount.Source, mount.Target)
		mounts = append(mounts, repr)
	}
	return strings.Join(mounts, ", ")
}

// Value returns the mounts
func (m *MountOpt) Value() []mounttypes.Mount {
	return m.values
}
<commit_msg>Add 'consistent', 'cached', and 'delegated' mode flags<commit_after>package opts

import (
	"encoding/csv"
	"fmt"
	"os"
	"strconv"
	"strings"

	mounttypes "github.com/docker/docker/api/types/mount"
	"github.com/docker/go-units"
)

// MountOpt is a Value type for parsing mounts
type MountOpt struct {
	values []mounttypes.Mount
}

// Set a new mount value
func (m *MountOpt) Set(value string) error {
	csvReader := csv.NewReader(strings.NewReader(value))
	fields, err := csvReader.Read()
	if err != nil {
		return err
	}

	mount := mounttypes.Mount{}

	volumeOptions := func() *mounttypes.VolumeOptions {
		if mount.VolumeOptions == nil {
			mount.VolumeOptions = &mounttypes.VolumeOptions{
				Labels: make(map[string]string),
			}
		}
		if mount.VolumeOptions.DriverConfig == nil {
			mount.VolumeOptions.DriverConfig = &mounttypes.Driver{}
		}
		return mount.VolumeOptions
	}

	bindOptions := func() *mounttypes.BindOptions {
		if mount.BindOptions == nil {
			mount.BindOptions = new(mounttypes.BindOptions)
		}
		return mount.BindOptions
	}

	tmpfsOptions := func() *mounttypes.TmpfsOptions {
		if mount.TmpfsOptions == nil {
			mount.TmpfsOptions = new(mounttypes.TmpfsOptions)
		}
		return mount.TmpfsOptions
	}

	setValueOnMap := func(target map[string]string, value string) {
		parts := strings.SplitN(value, "=", 2)
		if len(parts) == 1 {
			target[value] = ""
		} else {
			target[parts[0]] = parts[1]
		}
	}

	mount.Type = mounttypes.TypeVolume // default to volume mounts
	// Set writable as the default
	for _, field := range fields {
		parts := strings.SplitN(field, "=", 2)
		key := strings.ToLower(parts[0])

		if len(parts) == 1 {
			switch key {
			case "readonly", "ro":
				mount.ReadOnly = true
				continue
			case "volume-nocopy":
				volumeOptions().NoCopy = true
				continue
			}
		}

		if len(parts) != 2 {
			return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
		}

		value := parts[1]
		switch key {
		case "type":
			mount.Type = mounttypes.Type(strings.ToLower(value))
		case "source", "src":
			mount.Source = value
		case "target", "dst", "destination":
			mount.Target = value
		case "readonly", "ro":
			mount.ReadOnly, err = strconv.ParseBool(value)
			if err != nil {
				return fmt.Errorf("invalid value for %s: %s", key, value)
			}
		case "consistency":
			mount.Consistency = mounttypes.Consistency(strings.ToLower(value))
		case "bind-propagation":
			bindOptions().Propagation = mounttypes.Propagation(strings.ToLower(value))
		case "volume-nocopy":
			volumeOptions().NoCopy, err = strconv.ParseBool(value)
			if err != nil {
				return fmt.Errorf("invalid value for populate: %s", value)
			}
		case "volume-label":
			setValueOnMap(volumeOptions().Labels, value)
		case "volume-driver":
			volumeOptions().DriverConfig.Name = value
		case "volume-opt":
			if volumeOptions().DriverConfig.Options == nil {
				volumeOptions().DriverConfig.Options = make(map[string]string)
			}
			setValueOnMap(volumeOptions().DriverConfig.Options, value)
		case "tmpfs-size":
			sizeBytes, err := units.RAMInBytes(value)
			if err != nil {
				return fmt.Errorf("invalid value for %s: %s", key, value)
			}
			tmpfsOptions().SizeBytes = sizeBytes
		case "tmpfs-mode":
			ui64, err := strconv.ParseUint(value, 8, 32)
			if err != nil {
				return fmt.Errorf("invalid value for %s: %s", key, value)
			}
			tmpfsOptions().Mode = os.FileMode(ui64)
		default:
			return fmt.Errorf("unexpected key '%s' in '%s'", key, field)
		}
	}

	if mount.Type == "" {
		return fmt.Errorf("type is required")
	}

	if mount.Target == "" {
		return fmt.Errorf("target is required")
	}

	if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume {
		return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mount.Type)
	}
	if mount.BindOptions != nil && mount.Type != mounttypes.TypeBind {
		return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type)
	}
	if mount.TmpfsOptions != nil && mount.Type != mounttypes.TypeTmpfs {
		return fmt.Errorf("cannot mix 'tmpfs-*' options with mount type '%s'", mount.Type)
	}

	m.values = append(m.values, mount)
	return nil
}

// Type returns the type of this option
func (m *MountOpt) Type() string {
	return "mount"
}

// String returns a string repr of this option
func (m *MountOpt) String() string {
	mounts := []string{}
	for _, mount := range m.values {
		repr := fmt.Sprintf("%s %s %s", mount.Type, mount.Source, mount.Target)
		mounts = append(mounts, repr)
	}
	return strings.Join(mounts, ", ")
}

// Value returns the mounts
func (m *MountOpt) Value() []mounttypes.Mount {
	return m.values
}
<|endoftext|>


<commit_before>package coinbasepro

import (
	"errors"
	"testing"
)

func TestCreateLimitOrders(t *testing.T) {
	client := NewTestClient()

	order := Order{
		Price:     "1.00000000",
		Size:      "1000.00000000",
		Side:      "buy",
		ProductID: "BTC-USD",
	}

	savedOrder, err := client.CreateOrder(&order)
	if err != nil {
		t.Error(err)
	}

	if savedOrder.ID == "" {
		t.Error(errors.New("No create id found"))
	}

	props := []string{"Price", "Size", "Side", "ProductID"}
	_, err = CompareProperties(order, savedOrder, props)
	if err != nil {
		t.Error(err)
	}

	if err := client.CancelOrder(savedOrder.ID); err != nil {
		t.Error(err)
	}
}

func TestCreateMarketOrders(t *testing.T) {
	client := NewTestClient()

	order := Order{
		Funds:     "10.00",
		Size:      "1000.00000000",
		Side:      "buy",
		Type:      "market",
		ProductID: "BTC-USD",
	}

	savedOrder, err := client.CreateOrder(&order)
	if err != nil {
		t.Error(err)
	}

	if savedOrder.ID == "" {
		t.Error(errors.New("No create id found"))
	}

	props := []string{"Price", "Size", "Side", "ProductID"}
	_, err = CompareProperties(order, savedOrder, props)
	if err != nil {
		t.Error(err)
	}
}

func TestCancelOrder(t *testing.T) {
	client := NewTestClient()

	order := Order{
		Price:     "1.00",
		Size:      "1000.00",
		Side:      "buy",
		ProductID: "BTC-USD",
	}

	savedOrder, err := client.CreateOrder(&order)
	if err != nil {
		t.Error(err)
	}

	if err := client.CancelOrder(savedOrder.ID); err != nil {
		t.Error(err)
		t.Error(err)
	}
}

func TestGetOrder(t *testing.T) {
	client := NewTestClient()

	order := Order{
		Price:     "1.00",
		Size:      "1.00",
		Side:      "buy",
		ProductID: "BTC-USD",
	}

	savedOrder, err := client.CreateOrder(&order)
	if err != nil {
		t.Error(err)
	}

	getOrder, err := client.GetOrder(savedOrder.ID)
	if err != nil {
		t.Error(err)
	}

	if getOrder.ID != savedOrder.ID {
		t.Error(errors.New("Order ids do not match"))
	}

	if err := client.CancelOrder(savedOrder.ID); err != nil {
		t.Error(err)
	}
}

func TestListOrders(t *testing.T) {
	client := NewTestClient()
	cursor := client.ListOrders()
	var orders []Order

	for cursor.HasMore {
		if err := cursor.NextPage(&orders); err != nil {
			t.Error(err)
		}

		for _, o := range orders {
			if StructHasZeroValues(o) {
				t.Error(errors.New("Zero value"))
			}
		}
	}

	cursor = client.ListOrders(ListOrdersParams{Status: "open", ProductID: "LTC-EUR"})
	for cursor.HasMore {
		if err := cursor.NextPage(&orders); err != nil {
			t.Error(err)
		}

		for _, o := range orders {
			if StructHasZeroValues(o) {
				t.Error(errors.New("Zero value"))
			}
		}
	}
}

func TestCancelAllOrders(t *testing.T) {
	client := NewTestClient()

	for _, pair := range []string{"BTC-USD"} {
		for i := 0; i < 2; i++ {
			order := Order{Price: "100000.00", Size: "1.00", Side: "sell", ProductID: pair}

			if _, err := client.CreateOrder(&order); err != nil {
				t.Error(err)
			}
		}

		orderIDs, err := client.CancelAllOrders(CancelAllOrdersParams{ProductID: pair})
		if err != nil {
			t.Error(err)
		}

		if len(orderIDs) != 2 {
			t.Error("Did not cancel all orders")
		}
	}
}
<commit_msg>Fixed broken test (#98)<commit_after>package coinbasepro

import (
	"errors"
	"testing"
)

func TestCreateLimitOrders(t *testing.T) {
	client := NewTestClient()

	order := Order{
		Price:     "1.00000000",
		Size:      "1000.00000000",
		Side:      "buy",
		ProductID: "BTC-USD",
	}

	savedOrder, err := client.CreateOrder(&order)
	if err != nil {
		t.Error(err)
	}

	if savedOrder.ID == "" {
		t.Error(errors.New("No create id found"))
	}

	props := []string{"Price", "Size", "Side", "ProductID"}
	_, err = CompareProperties(order, savedOrder, props)
	if err != nil {
		t.Error(err)
	}

	if err := client.CancelOrder(savedOrder.ID); err != nil {
		t.Error(err)
	}
}

func TestCreateMarketOrders(t *testing.T) {
	client := NewTestClient()

	order := Order{
		Funds:     "10.00",
		Size:      "1000.00000000",
		Side:      "buy",
		Type:      "market",
		ProductID: "BTC-USD",
	}

	savedOrder, err := client.CreateOrder(&order)
	if err != nil {
		t.Error(err)
	}

	if savedOrder.ID == "" {
		t.Error(errors.New("No create id found"))
	}

	props := []string{"Price", "Size", "Side", "ProductID"}
	_, err = CompareProperties(order, savedOrder, props)
	if err != nil {
		t.Error(err)
	}
}

func TestCancelOrder(t *testing.T) {
	client := NewTestClient()

	order := Order{
		Price:     "1.00",
		Size:      "1000.00",
		Side:      "buy",
		ProductID: "BTC-USD",
	}

	savedOrder, err := client.CreateOrder(&order)
	if err != nil {
		t.Error(err)
	}

	if err := client.CancelOrder(savedOrder.ID); err != nil {
		t.Error(err)
		t.Error(err)
	}
}

func TestGetOrder(t *testing.T) {
	client := NewTestClient()

	order := Order{
		Price:     "1.00",
		Size:      "1.00",
		Side:      "buy",
		ProductID: "BTC-USD",
	}

	savedOrder, err := client.CreateOrder(&order)
	if err != nil {
		t.Error(err)
	}

	getOrder, err := client.GetOrder(savedOrder.ID)
	if err != nil {
		t.Error(err)
	}

	if getOrder.ID != savedOrder.ID {
		t.Error(errors.New("Order ids do not match"))
	}

	if err := client.CancelOrder(savedOrder.ID); err != nil {
		t.Error(err)
	}
}

func TestListOrders(t *testing.T) {
	client := NewTestClient()
	cursor := client.ListOrders()
	var orders []Order

	for cursor.HasMore {
		if err := cursor.NextPage(&orders); err != nil {
			t.Error(err)
		}

		for _, o := range orders {
			if StructHasZeroValues(o) {
				t.Error(errors.New("Zero value"))
			}
		}
	}

	cursor = client.ListOrders(ListOrdersParams{Status: "open", ProductID: "BTC-EUR"})
	for cursor.HasMore {
		if err := cursor.NextPage(&orders); err != nil {
			t.Error(err)
		}

		for _, o := range orders {
			if StructHasZeroValues(o) {
				t.Error(errors.New("Zero value"))
			}
		}
	}
}

func TestCancelAllOrders(t *testing.T) {
	client := NewTestClient()

	for _, pair := range []string{"BTC-USD"} {
		for i := 0; i < 2; i++ {
			order := Order{Price: "100000.00", Size: "1.00", Side: "sell", ProductID: pair}

			if _, err := client.CreateOrder(&order); err != nil {
				t.Error(err)
			}
		}

		orderIDs, err := client.CancelAllOrders(CancelAllOrdersParams{ProductID: pair})
		if err != nil {
			t.Error(err)
		}

		if len(orderIDs) != 2 {
			t.Error("Did not cancel all orders")
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"strings"
)

const (
	defQueryType  = "ANY"
	defQueryClass = "IN"
	defProtocol   = "UDP"
)

var (
	eDNS0 = false
)

// checkQueryParam checks against possible list of parameters
func checkQueryParam(arg string, list map[string]bool) bool {
	_, ok := list[strings.ToUpper(arg)]
	return ok
}

// init injects our "dns" related commands/options.
func init() {
	// Fill-in the various commands
	cliCommands = append(cliCommands, cli.Command{
		Name:        "dns",
		Usage:       "send dns queries",
		Description: "send DNS queries about an host/IP/domain\n   use: <Q> [<TYPE> [<CLASS>]]",
		Aliases: []string{
			"dig",
			"drill",
		},
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:        "E, edns0",
				Usage:       "use EDNS0",
				Destination: &eDNS0,
			},
			cli.BoolFlag{
				Name:        "D, disable-dnssec",
				Usage:       "Do not try to validate DNSSEC RR",
				Destination: &fDisableDNSSEC,
			},
			cli.BoolFlag{
				Name:        "C, disable-dnssec-checks",
				Usage:       "Do not try to validate DNSSEC Check by probes",
				Destination: &fBitCD,
			},
			cli.StringFlag{
				Name:        "p, protocol",
				Usage:       "Select UDP or TCP",
				Destination: &fProtocol,
			},
		},
		Action: cmdDNS,
	})
}

func prepareDNS(proto, qa, qc, qt string, do, cd bool) (req *atlas.MeasurementRequest) {
	opts := map[string]string{
		"Type":          "dns",
		"Description":   fmt.Sprintf("DNS - %s", qa),
		"Protocol":      proto,
		"Target":        qa,
		"QueryArgument": qa,
		"QueryClass":    qc,
		"QueryType":     qt,
		"SetDOBit":      boolToString(do),
		"SetCDBit":      boolToString(cd),
	}

	if eDNS0 {
		opts["UDPPayloadSize"] = "4096"
		opts["Protocol"] = "UDP"
	} else {
		opts["UDPPayloadSize"] = "512"
	}

	// Check global parameters
	opts = checkGlobalFlags(opts)

	req = client.NewMeasurement()

	if wantAF == WantBoth {

		opts["AF"] = "4"
		req.AddDefinition(opts)

		opts["AF"] = "6"
		req.AddDefinition(opts)
	} else {
		opts["AF"] = wantAF
		req.AddDefinition(opts)
	}

	if fVerbose {
		displayOptions(opts)
	}

	return
}

func cmdDNS(c *cli.Context) error {
	var (
		bitDO  = true
		bitCD  = false
		qtype  = defQueryType
		qclass = defQueryClass
		proto  = defProtocol

		addr string
	)

	args := c.Args()
	if args == nil || len(args) == 0 {
		log.Fatal("Error: you must specify at least a name")
	}

	if len(args) == 1 {
		addr = args[0]
	} else if len(args) == 2 {
		addr = args[0]
		qtype = strings.ToUpper(args[1])
	} else if len(args) == 3 {
		addr = args[0]
		qtype = strings.ToUpper(args[1])
		qclass = strings.ToUpper(args[2])
	}

	if fProtocol != "" {
		log.Printf("Use %s", fProtocol)
		proto = fProtocol
	}

	if fDisableDNSSEC {
		bitDO = false
	}

	if fBitCD {
		bitCD = true
	}

	req := prepareDNS(proto, addr, qclass, qtype, bitDO, bitCD)

	if fDebug {
		log.Printf("req=%#v", req)
	}
	m, err := client.DNS(req)
	if err != nil {
		fmt.Printf("err: %v", err)
		os.Exit(1)
	}

	//str := res.Result.Display()
	displayMeasurementID(*m)

	return nil
}
<commit_msg>Describe the format.<commit_after>package main

import (
	"fmt"
	"github.com/keltia/ripe-atlas"
	"github.com/urfave/cli"
	"log"
	"os"
	"strings"
)

const (
	defQueryType  = "ANY"
	defQueryClass = "IN"
	defProtocol   = "UDP"
)

var (
	eDNS0 = false
)

// checkQueryParam checks against possible list of parameters
func checkQueryParam(arg string, list map[string]bool) bool {
	_, ok := list[strings.ToUpper(arg)]
	return ok
}

// init injects our "dns" related commands/options.
func init() {
	// Fill-in the various commands
	cliCommands = append(cliCommands, cli.Command{
		Name:        "dns",
		Usage:       "send dns queries",
		Description: "send DNS queries about an host/IP/domain\n   use: <Q> [<TYPE> [<CLASS>]]",
		Aliases: []string{
			"dig",
			"drill",
		},
		Flags: []cli.Flag{
			cli.BoolFlag{
				Name:        "E, edns0",
				Usage:       "use EDNS0",
				Destination: &eDNS0,
			},
			cli.BoolFlag{
				Name:        "D, disable-dnssec",
				Usage:       "Do not try to validate DNSSEC RR",
				Destination: &fDisableDNSSEC,
			},
			cli.BoolFlag{
				Name:        "C, disable-dnssec-checks",
				Usage:       "Do not try to validate DNSSEC Check by probes",
				Destination: &fBitCD,
			},
			cli.StringFlag{
				Name:        "p, protocol",
				Usage:       "Select UDP or TCP",
				Destination: &fProtocol,
			},
		},
		Action: cmdDNS,
	})
}

func prepareDNS(proto, qa, qc, qt string, do, cd bool) (req *atlas.MeasurementRequest) {
	opts := map[string]string{
		"Type":          "dns",
		"Description":   fmt.Sprintf("DNS - %s", qa),
		"Protocol":      proto,
		"Target":        qa,
		"QueryArgument": qa,
		"QueryClass":    qc,
		"QueryType":     qt,
		"SetDOBit":      boolToString(do),
		"SetCDBit":      boolToString(cd),
	}

	if eDNS0 {
		opts["UDPPayloadSize"] = "4096"
		opts["Protocol"] = "UDP"
	} else {
		opts["UDPPayloadSize"] = "512"
	}

	// Check global parameters
	opts = checkGlobalFlags(opts)

	req = client.NewMeasurement()

	if wantAF == WantBoth {

		opts["AF"] = "4"
		req.AddDefinition(opts)

		opts["AF"] = "6"
		req.AddDefinition(opts)
	} else {
		opts["AF"] = wantAF
		req.AddDefinition(opts)
	}

	if fVerbose {
		displayOptions(opts)
	}

	return
}

func cmdDNS(c *cli.Context) error {
	var (
		bitDO  = true
		bitCD  = false
		qtype  = defQueryType
		qclass = defQueryClass
		proto  = defProtocol

		addr string
	)

	args := c.Args()
	if args == nil || len(args) == 0 {
		log.Fatal("Error: you must specify at least a name")
	}

	// Format is <query_data> [<query_type> [<query_class>]]
	if len(args) == 1 {
		addr = args[0]
	} else if len(args) == 2 {
		addr = args[0]
		qtype = strings.ToUpper(args[1])
	} else if len(args) == 3 {
		addr = args[0]
		qtype = strings.ToUpper(args[1])
		qclass = strings.ToUpper(args[2])
	}

	if fProtocol != "" {
		log.Printf("Use %s", fProtocol)
		proto = fProtocol
	}

	if fDisableDNSSEC {
		bitDO = false
	}

	if fBitCD {
		bitCD = true
	}

	req := prepareDNS(proto, addr, qclass, qtype, bitDO, bitCD)

	if fDebug {
		log.Printf("req=%#v", req)
	}
	m, err := client.DNS(req)
	if err != nil {
		fmt.Printf("err: %v", err)
		os.Exit(1)
	}

	//str := res.Result.Display()
	displayMeasurementID(*m)

	return nil
}
<|endoftext|>


<commit_before>package commands

import (
	"os"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	apiv1 "k8s.io/api/core/v1"

	"github.com/Shopify/kubeaudit"
	"github.com/Shopify/kubeaudit/auditors/all"
	"github.com/Shopify/kubeaudit/config"
	"github.com/Shopify/kubeaudit/internal/k8s"
)

var rootConfig rootFlags

type rootFlags struct {
	format      string
	kubeConfig  string
	manifest    string
	namespace   string
	minSeverity string
	exitCode    int
}

// RootCmd defines the shell command usage for kubeaudit.
var RootCmd = &cobra.Command{
	Use:   "kubeaudit",
	Short: "A Kubernetes security auditor",
	Long: `Kubeaudit audits Kubernetes clusters for common security controls.

kubeaudit has three modes:
  1. Manifest mode: If a Kubernetes manifest file is provided using the -f/--manifest flag, kubeaudit will audit the manifest file. Kubeaudit also supports autofixing in manifest mode using the 'autofix' command. This will fix the manifest in-place. The fixed manifest can be written to a different file using the -o/--out flag.
  2. Cluster mode: If kubeaudit detects it is running in a cluster, it will audit the other resources in the cluster.
  3. Local mode: kubeaudit will try to connect to a cluster using the local kubeconfig file ($HOME/.kube/config). A different kubeconfig location can be specified using the -c/--kubeconfig flag
`,
}

// Execute is a wrapper for the RootCmd.Execute method which will exit the program if there is an error.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		log.Fatal(err)
	}
}

func init() {
	RootCmd.PersistentFlags().StringVarP(&rootConfig.kubeConfig, "kubeconfig", "c", "", "Path to local Kubernetes config file. Only used in local mode (default is $HOME/.kube/config)")
	RootCmd.PersistentFlags().StringVarP(&rootConfig.minSeverity, "minseverity", "m", "info", "Set the lowest severity level to report (one of \"error\", \"warning\", \"info\")")
	RootCmd.PersistentFlags().StringVarP(&rootConfig.format, "format", "p", "pretty", "The output format to use (one of \"pretty\", \"logrus\", \"json\")")
	RootCmd.PersistentFlags().StringVarP(&rootConfig.namespace, "namespace", "n", apiv1.NamespaceAll, "Only audit resources in the specified namespace. Not currently supported in manifest mode.")
	RootCmd.PersistentFlags().StringVarP(&rootConfig.manifest, "manifest", "f", "", "Path to the yaml configuration to audit. Only used in manifest mode.")
	RootCmd.PersistentFlags().IntVarP(&rootConfig.exitCode, "exitcode", "e", 2, "Exit code to use if there are results with severity of \"error\". Conventionally, 0 is used for success and all non-zero codes for an error.")
}

// KubeauditLogLevels represents an enum for the supported log levels.
var KubeauditLogLevels = map[string]kubeaudit.SeverityLevel{
	"error":   kubeaudit.Error,
	"warn":    kubeaudit.Warn,
	"warning": kubeaudit.Warn,
	"info":    kubeaudit.Info,
}

func runAudit(auditable ...kubeaudit.Auditable) func(cmd *cobra.Command, args []string) {
	return func(cmd *cobra.Command, args []string) {
		report := getReport(auditable...)

		printOptions := []kubeaudit.PrintOption{
			kubeaudit.WithMinSeverity(KubeauditLogLevels[strings.ToLower(rootConfig.minSeverity)]),
		}

		switch rootConfig.format {
		case "json":
			printOptions = append(printOptions, kubeaudit.WithFormatter(&log.JSONFormatter{}))
		case "logrus":
			printOptions = append(printOptions, kubeaudit.WithFormatter(&log.TextFormatter{}))
		}

		report.PrintResults(printOptions...)

		if report.HasErrors() {
			os.Exit(rootConfig.exitCode)
		}
	}
}

func getReport(auditors ...kubeaudit.Auditable) *kubeaudit.Report {
	auditor := initKubeaudit(auditors...)

	if rootConfig.manifest != "" {
		manifest, err := os.Open(rootConfig.manifest)
		if err != nil {
			log.WithError(err).Fatal("Error opening manifest file")
		}
		report, err := auditor.AuditManifest(manifest)
		if err != nil {
			log.WithError(err).Fatal("Error auditing manifest")
		}
		return report
	}

	if k8s.IsRunningInCluster(k8s.DefaultClient) {
		report, err := auditor.AuditCluster(k8s.ClientOptions{Namespace: rootConfig.namespace})
		if err != nil {
			log.WithError(err).Fatal("Error auditing cluster")
		}
		return report
	}

	report, err := auditor.AuditLocal(rootConfig.kubeConfig, k8s.ClientOptions{Namespace: rootConfig.namespace})
	if err != nil {
		log.WithError(err).Fatal("Error auditing cluster in local mode")
	}
	return report
}

func initKubeaudit(auditable ...kubeaudit.Auditable) *kubeaudit.Kubeaudit {
	if len(auditable) == 0 {
		allAuditors, err := all.Auditors(config.KubeauditConfig{})
		if err != nil {
			log.WithError(err).Fatal("Error initializing auditors")
		}
		auditable = allAuditors
	}

	auditor, err := kubeaudit.New(auditable)
	if err != nil {
		log.WithError(err).Fatal("Error creating auditor")
	}

	return auditor
}
<commit_msg>Run Auditlocal, even when kubeaudit detected to run in a cluster. (#304)<commit_after>package commands

import (
	"os"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	apiv1 "k8s.io/api/core/v1"

	"github.com/Shopify/kubeaudit"
	"github.com/Shopify/kubeaudit/auditors/all"
	"github.com/Shopify/kubeaudit/config"
	"github.com/Shopify/kubeaudit/internal/k8s"
)

var rootConfig rootFlags

type rootFlags struct {
	format      string
	kubeConfig  string
	manifest    string
	namespace   string
	minSeverity string
	exitCode    int
}

// RootCmd defines the shell command usage for kubeaudit.
var RootCmd = &cobra.Command{
	Use:   "kubeaudit",
	Short: "A Kubernetes security auditor",
	Long: `Kubeaudit audits Kubernetes clusters for common security controls.

kubeaudit has three modes:
  1. Manifest mode: If a Kubernetes manifest file is provided using the -f/--manifest flag, kubeaudit will audit the manifest file. Kubeaudit also supports autofixing in manifest mode using the 'autofix' command. This will fix the manifest in-place. The fixed manifest can be written to a different file using the -o/--out flag.
  2. Cluster mode: If kubeaudit detects it is running in a cluster, it will audit the other resources in the cluster.
  3. Local mode: kubeaudit will try to connect to a cluster using the local kubeconfig file ($HOME/.kube/config). A different kubeconfig location can be specified using the -c/--kubeconfig flag
`,
}

// Execute is a wrapper for the RootCmd.Execute method which will exit the program if there is an error.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		log.Fatal(err)
	}
}

func init() {
	RootCmd.PersistentFlags().StringVarP(&rootConfig.kubeConfig, "kubeconfig", "c", "", "Path to local Kubernetes config file. Only used in local mode (default is $HOME/.kube/config)")
	RootCmd.PersistentFlags().StringVarP(&rootConfig.minSeverity, "minseverity", "m", "info", "Set the lowest severity level to report (one of \"error\", \"warning\", \"info\")")
	RootCmd.PersistentFlags().StringVarP(&rootConfig.format, "format", "p", "pretty", "The output format to use (one of \"pretty\", \"logrus\", \"json\")")
	RootCmd.PersistentFlags().StringVarP(&rootConfig.namespace, "namespace", "n", apiv1.NamespaceAll, "Only audit resources in the specified namespace. Not currently supported in manifest mode.")
	RootCmd.PersistentFlags().StringVarP(&rootConfig.manifest, "manifest", "f", "", "Path to the yaml configuration to audit. Only used in manifest mode.")
	RootCmd.PersistentFlags().IntVarP(&rootConfig.exitCode, "exitcode", "e", 2, "Exit code to use if there are results with severity of \"error\". Conventionally, 0 is used for success and all non-zero codes for an error.")
}

// KubeauditLogLevels represents an enum for the supported log levels.
var KubeauditLogLevels = map[string]kubeaudit.SeverityLevel{
	"error":   kubeaudit.Error,
	"warn":    kubeaudit.Warn,
	"warning": kubeaudit.Warn,
	"info":    kubeaudit.Info,
}

func runAudit(auditable ...kubeaudit.Auditable) func(cmd *cobra.Command, args []string) {
	return func(cmd *cobra.Command, args []string) {
		report := getReport(auditable...)

		printOptions := []kubeaudit.PrintOption{
			kubeaudit.WithMinSeverity(KubeauditLogLevels[strings.ToLower(rootConfig.minSeverity)]),
		}

		switch rootConfig.format {
		case "json":
			printOptions = append(printOptions, kubeaudit.WithFormatter(&log.JSONFormatter{}))
		case "logrus":
			printOptions = append(printOptions, kubeaudit.WithFormatter(&log.TextFormatter{}))
		}

		report.PrintResults(printOptions...)

		if report.HasErrors() {
			os.Exit(rootConfig.exitCode)
		}
	}
}

func getReport(auditors ...kubeaudit.Auditable) *kubeaudit.Report {
	auditor := initKubeaudit(auditors...)

	if rootConfig.manifest != "" {
		manifest, err := os.Open(rootConfig.manifest)
		if err != nil {
			log.WithError(err).Fatal("Error opening manifest file")
		}
		report, err := auditor.AuditManifest(manifest)
		if err != nil {
			log.WithError(err).Fatal("Error auditing manifest")
		}
		return report
	}

	if ( k8s.IsRunningInCluster(k8s.DefaultClient) && rootConfig.kubeConfig == "" ) {
		report, err := auditor.AuditCluster(k8s.ClientOptions{Namespace: rootConfig.namespace})
		if err != nil {
			log.WithError(err).Fatal("Error auditing cluster")
		}
		return report
	}

	report, err := auditor.AuditLocal(rootConfig.kubeConfig, k8s.ClientOptions{Namespace: rootConfig.namespace})
	if err != nil {
		log.WithError(err).Fatal("Error auditing cluster in local mode")
	}
	return report
}

func initKubeaudit(auditable ...kubeaudit.Auditable) *kubeaudit.Kubeaudit {
	if len(auditable) == 0 {
		allAuditors, err := all.Auditors(config.KubeauditConfig{})
		if err != nil {
			log.WithError(err).Fatal("Error initializing auditors")
		}
		auditable = allAuditors
	}

	auditor, err := kubeaudit.New(auditable)
	if err != nil {
		log.WithError(err).Fatal("Error creating auditor")
	}

	return auditor
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"
	"time"

	"golang.org/x/oauth2"

	"github.com/codegangsta/cli"
	"github.com/remind101/empire"
	"github.com/remind101/empire/server"
	"github.com/remind101/empire/server/auth"
	"github.com/remind101/empire/server/auth/github"
)

func runServer(c *cli.Context) {
	port := c.String(FlagPort)

	if c.Bool(FlagAutoMigrate) {
		runMigrate(c)
	}

	e, err := newEmpire(c)
	if err != nil {
		log.Fatal(err)
	}

	s := newServer(c, e)
	log.Printf("Starting on port %s", port)
	log.Fatal(http.ListenAndServe(":"+port, s))
}

func newServer(c *cli.Context, e *empire.Empire) http.Handler {
	var opts server.Options
	opts.Authenticator = newAuthenticator(c, e)
	opts.GitHub.Webhooks.Secret = c.String(FlagGithubWebhooksSecret)
	opts.GitHub.Deployments.Environment = c.String(FlagGithubDeploymentsEnvironment)
	opts.GitHub.Deployments.ImageTemplate = c.String(FlagGithubDeploymentsImageTemplate)
	opts.GitHub.Deployments.TugboatURL = c.String(FlagGithubDeploymentsTugboatURL)

	return server.New(e, opts)
}

func newAuthenticator(c *cli.Context, e *empire.Empire) auth.Authenticator {
	// an authenticator authenticating requests with a users empire acccess
	// token.
	authenticators := []auth.Authenticator{
		auth.NewAccessTokenAuthenticator(e),
	}

	var client *github.Client
	// If a GitHub client id is provided, we'll use GitHub as an
	// authentication backend. Otherwise, we'll just use a static username
	// and password backend.
	if c.String(FlagGithubClient) == "" {
		// Fake authentication password where the user is "fake" and
		// password is blank.
		authenticators = append(authenticators, auth.StaticAuthenticator("fake", "", "", &empire.User{
			Name: "fake",
		}))
	} else {
		client = github.NewClient(&oauth2.Config{
			ClientID:     c.String(FlagGithubClient),
			ClientSecret: c.String(FlagGithubClientSecret),
			Scopes:       []string{"repo_deployment", "read:org"},
		})
		client.URL = c.String(FlagGithubApiURL)

		// an authenticator for authenticating requests with a users github
		// credentials.
		authenticators = append(authenticators, github.NewAuthenticator(client))
	}

	// try access token before falling back to github.
	authenticator := auth.MultiAuthenticator(authenticators...)

	// After the user is authenticated, check their GitHub Organization membership.
	if org := c.String(FlagGithubOrg); org != "" {
		authorizer := github.NewOrganizationAuthorizer(client)
		authorizer.Organization = org

		return auth.WithAuthorization(
			authenticator,
			// Cache the organization check for 30 minutes since
			// it's pretty slow.
			auth.CacheAuthorization(authorizer, 30*time.Minute),
		)
	}

	return authenticator
}
<commit_msg>Log configuration on boot.<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"golang.org/x/oauth2"

	"github.com/codegangsta/cli"
	"github.com/remind101/empire"
	"github.com/remind101/empire/server"
	"github.com/remind101/empire/server/auth"
	"github.com/remind101/empire/server/auth/github"
)

func runServer(c *cli.Context) {
	port := c.String(FlagPort)

	if c.Bool(FlagAutoMigrate) {
		runMigrate(c)
	}

	e, err := newEmpire(c)
	if err != nil {
		log.Fatal(err)
	}

	s := newServer(c, e)
	log.Printf("Starting on port %s", port)
	log.Fatal(http.ListenAndServe(":"+port, s))
}

func newServer(c *cli.Context, e *empire.Empire) http.Handler {
	var opts server.Options
	opts.Authenticator = newAuthenticator(c, e)
	opts.GitHub.Webhooks.Secret = c.String(FlagGithubWebhooksSecret)
	opts.GitHub.Deployments.Environment = c.String(FlagGithubDeploymentsEnvironment)
	opts.GitHub.Deployments.ImageTemplate = c.String(FlagGithubDeploymentsImageTemplate)
	opts.GitHub.Deployments.TugboatURL = c.String(FlagGithubDeploymentsTugboatURL)

	return server.New(e, opts)
}

func newAuthenticator(c *cli.Context, e *empire.Empire) auth.Authenticator {
	// an authenticator authenticating requests with a users empire acccess
	// token.
	authenticators := []auth.Authenticator{
		auth.NewAccessTokenAuthenticator(e),
	}

	var client *github.Client
	// If a GitHub client id is provided, we'll use GitHub as an
	// authentication backend. Otherwise, we'll just use a static username
	// and password backend.
	if c.String(FlagGithubClient) == "" {
		log.Println("Using static authentication backend")
		// Fake authentication password where the user is "fake" and
		// password is blank.
		authenticators = append(authenticators, auth.StaticAuthenticator("fake", "", "", &empire.User{
			Name: "fake",
		}))
	} else {
		config := &oauth2.Config{
			ClientID:     c.String(FlagGithubClient),
			ClientSecret: c.String(FlagGithubClientSecret),
			Scopes:       []string{"repo_deployment", "read:org"},
		}

		client = github.NewClient(config)
		client.URL = c.String(FlagGithubApiURL)

		log.Println("Using GitHub authentication backend with the following configuration:")
		log.Println(fmt.Sprintf("  ClientID: %v", config.ClientID))
		log.Println(fmt.Sprintf("  ClientSecret: ****"))
		log.Println(fmt.Sprintf("  Scopes: %v", config.Scopes))
		log.Println(fmt.Sprintf("  GitHubAPI: %v", client.URL))

		// an authenticator for authenticating requests with a users github
		// credentials.
		authenticators = append(authenticators, github.NewAuthenticator(client))
	}

	// try access token before falling back to github.
	authenticator := auth.MultiAuthenticator(authenticators...)

	// After the user is authenticated, check their GitHub Organization membership.
	if org := c.String(FlagGithubOrg); org != "" {
		authorizer := github.NewOrganizationAuthorizer(client)
		authorizer.Organization = org

		log.Println("Adding GitHub Organization authorizer with the following configuration:")
		log.Println(fmt.Sprintf("  Organization: %v ", org))

		return auth.WithAuthorization(
			authenticator,
			// Cache the organization check for 30 minutes since
			// it's pretty slow.
			auth.CacheAuthorization(authorizer, 30*time.Minute),
		)
	}

	return authenticator
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or 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 (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"os"

	"github.com/google/gapid/core/app"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/device"
)

type devicesVerb struct{ DevicesFlags }

// deviceObj wraps the device info in a JSON-Marshable type
type deviceObj struct {
	DeviceID string
	Instance *device.Instance
}

func init() {
	verb := &devicesVerb{}
	app.AddVerb(&app.Verb{
		Name:      "devices",
		ShortHelp: "Lists the devices available",
		Action:    verb,
	})
}

func (verb *devicesVerb) Run(ctx context.Context, flags flag.FlagSet) error {
	client, err := getGapis(ctx, verb.Gapis, GapirFlags{})
	if err != nil {
		return log.Err(ctx, err, "Failed to connect to the GAPIS server")
	}
	defer client.Close()

	devices, err := client.GetDevices(ctx)
	if err != nil {
		return log.Err(ctx, err, "Failed to get device list")
	}

	deviceObjs := []deviceObj{}
	for _, p := range devices {
		o, err := client.Get(ctx, p.Path(), nil)
		if err != nil {
			log.Err(ctx, err, "Couldn't resolve device")
			continue
		}
		d := o.(*device.Instance)
		if verb.OS != device.UnknownOS && verb.OS != d.GetConfiguration().GetOS().GetKind() {
			continue
		}
		devObj := deviceObj{
			DeviceID: fmt.Sprintf("%v", p.ID.ID()),
			Instance: d,
		}
		deviceObjs = append(deviceObjs, devObj)
	}

	jsonBytes, err := json.MarshalIndent(deviceObjs, "", "  ")
	if err != nil {
		return log.Err(ctx, err, "Failed to marshal devices to JSON")
	}
	fmt.Fprintln(os.Stdout, string(jsonBytes))

	return nil
}
<commit_msg>Properly log error in gapit devices (#3737)<commit_after>// Copyright (C) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or 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 (
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"os"

	"github.com/google/gapid/core/app"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/device"
)

type devicesVerb struct{ DevicesFlags }

// deviceObj wraps the device info in a JSON-Marshable type
type deviceObj struct {
	DeviceID string
	Instance *device.Instance
}

func init() {
	verb := &devicesVerb{}
	app.AddVerb(&app.Verb{
		Name:      "devices",
		ShortHelp: "Lists the devices available",
		Action:    verb,
	})
}

func (verb *devicesVerb) Run(ctx context.Context, flags flag.FlagSet) error {
	client, err := getGapis(ctx, verb.Gapis, GapirFlags{})
	if err != nil {
		return log.Err(ctx, err, "Failed to connect to the GAPIS server")
	}
	defer client.Close()

	devices, err := client.GetDevices(ctx)
	if err != nil {
		return log.Err(ctx, err, "Failed to get device list")
	}

	deviceObjs := []deviceObj{}
	for _, p := range devices {
		o, err := client.Get(ctx, p.Path(), nil)
		if err != nil {
			log.E(ctx, "Couldn't resolve device: %v", err)
			continue
		}
		d := o.(*device.Instance)
		if verb.OS != device.UnknownOS && verb.OS != d.GetConfiguration().GetOS().GetKind() {
			continue
		}
		devObj := deviceObj{
			DeviceID: fmt.Sprintf("%v", p.ID.ID()),
			Instance: d,
		}
		deviceObjs = append(deviceObjs, devObj)
	}

	jsonBytes, err := json.MarshalIndent(deviceObjs, "", "  ")
	if err != nil {
		return log.Err(ctx, err, "Failed to marshal devices to JSON")
	}
	fmt.Fprintln(os.Stdout, string(jsonBytes))

	return nil
}
<|endoftext|>


<commit_before>/**
external ACL helper for squid.

Configure with:
  external_acl_type ext ttl=10 concurrency=2 %PROTO %SRC %METHOD %URI /usr/local/bin/proxyacl -db=/var/spool/squid3/proxyacl.sqlite -log=/var/log/squid3/proxyacl.log
  acl ext_acl external ext
  http_access allow ext_acl

Copyright 2016 Google Inc.

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

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

Unless required by applicable law or 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 (
	"bufio"
	"database/sql"
	"flag"
	"fmt"
	"log"
	"net"
	"net/url"
	"os"
	"regexp"
	"strings"
	"syscall"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

var (
	dbFile   = flag.String("db", "", "sqlite database.")
	logFile  = flag.String("log", "", "Logfile. Default to stderr.")
	verbose  = flag.Int("v", 1, "Verbosity level.")
	blockLog = flag.String("block_log", "", "Block log.")

	db *sql.DB
)

type action string

const (
	actionNone   action = "none"
	actionBlock  action = "block"
	actionIgnore action = "ignore"
	actionAllow  action = "allow"

	actionDefault = actionBlock

	aclMatch   = "OK"
	aclNoMatch = "ERR"
)

type Config struct {
	// Map from source to rules.
	Sources map[string][]string
	Rules   map[string]Rule
}

type Rule interface {
	Check(proto, src, method, uri string) (bool, error)
	Action() action
}

type DomainRule struct {
	value  string
	action action
}

func (d *DomainRule) Action() action { return d.action }
func (d *DomainRule) Check(proto, src, method, uri string) (bool, error) {
	if proto != "HTTP" {
		return false, nil
	}
	if d.value == "" {
		return false, nil
	}
	p, err := url.Parse(uri)
	if err != nil {
		return false, err
	}

	// Exact hostname.
	if p.Host == d.value {
		return true, nil
	}

	// Domain suffix.
	if d.value[0] == '.' && ("."+p.Host == d.value || strings.HasSuffix(p.Host, d.value)) {
		return true, nil
	}
	return false, nil
}

type RegexRule struct {
	action action
	re     *regexp.Regexp
}

func (d *RegexRule) Action() action { return d.action }
func (d *RegexRule) Check(proto, src, method, uri string) (bool, error) {
	if proto != "HTTP" {
		return false, nil
	}
	if d.re.MatchString(uri) {
		return true, nil
	}
	return false, nil
}

type ExactRule struct {
	value  string
	action action
}

func (d *ExactRule) Action() action { return d.action }
func (d *ExactRule) Check(proto, src, method, uri string) (bool, error) {
	if proto != "HTTP" {
		return false, nil
	}
	return d.value == uri, nil
}

type HTTPSDomainRule struct {
	value  string
	action action
}

func (d *HTTPSDomainRule) Action() action { return d.action }
func (d *HTTPSDomainRule) Check(proto, src, method, uri string) (bool, error) {
	if proto != "NONE" {
		return false, nil
	}
	if method != "CONNECT" {
		return false, nil
	}
	dhost, dport, err := net.SplitHostPort(d.value)
	if err != nil {
		dhost, dport = d.value, "443"
	}
	if dhost == "" {
		return false, nil
	}
	host, port, err := net.SplitHostPort(uri)
	if err != nil {
		return false, fmt.Errorf("failed to parse HTTPS host:port %q: %v", uri, err)
	}
	if port != dport {
		return false, nil
	}
	// Exact hostname.
	if host == dhost {
		return true, nil
	}

	// Domain suffix.
	if dhost[0] == '.' && ("."+host == dhost || strings.HasSuffix(host, dhost)) {
		return true, nil
	}
	return false, nil
}

// decide returns 'match found', 'action to take', error
func decide(cfg *Config, proto, src, method, uri string) (bool, action, error) {
	// Special case this because net/url can't parse these.
	if strings.HasPrefix(uri, "cache_object://") {
		return true, actionIgnore, nil
	}

	source := net.ParseIP(src)
	if source == nil {
		return false, actionNone, fmt.Errorf("source is not a valid address: %q", src)
	}
	for a, rules := range cfg.Sources {
		_, net, err := net.ParseCIDR(a)
		if err != nil {
			log.Printf("Not a valid source: %q: %v", a, err)
			continue
		}
		if !net.Contains(source) {
			continue
		}
		for _, ruleName := range rules {
			rule := cfg.Rules[ruleName]
			t, err := rule.Check(proto, src, method, uri)
			if err != nil {
				log.Printf("Failed to evaluate rule %q: %v", ruleName, err)
			} else if t {
				return true, rule.Action(), nil
			}
		}
	}
	return false, actionDefault, nil
}

func mainLoop() {
	cfg, err := loadConfig()
	if err != nil {
		log.Fatal(err)
	}
	// TODO: multithread this.
	scanner := bufio.NewScanner(os.Stdin)
	lastLoad := time.Now()
	for scanner.Scan() {
		if time.Since(lastLoad) > time.Second {
			cfg2, err := loadConfig()
			if err != nil {
				log.Printf("Failed to reload database")
			} else {
				cfg = cfg2
			}
			lastLoad = time.Now()
		}

		s := strings.Split(scanner.Text(), " ")
		if *verbose > 1 {
			log.Printf("Got %q", s)
		}
		token := s[0]
		proto := s[1]
		src := s[2]
		method := s[3]
		uri := s[4]
		urip, err := url.QueryUnescape(uri)
		reply := aclNoMatch
		if err != nil {
			log.Printf("URI escape error on %q: %v", s, err)
		} else {
			_, act, err := decide(cfg, proto, src, method, urip)
			if err != nil {
				log.Printf("Decision error on %q: %v", s, err)
			}
			switch act {
			case actionBlock, actionNone:
				if *verbose > 0 && reply != aclMatch {
					log.Printf("No match(%s): %q", act, s)
				}
				if err := logBlock(proto, src, method, urip); err != nil {
					log.Printf("Logging block: %v", err)
				}
			case actionIgnore:
			case actionAllow:
				reply = aclMatch
			}
		}
		if *verbose > 1 {
			log.Printf("Replied: %s %s", token, reply)
		}
		fmt.Printf("%s %s\n", token, reply)
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
}

func logBlock(proto, src, method, urip string) error {
	f, err := os.OpenFile(*blockLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {
		return err
	}
	fmt.Fprintf(f, "%f 0 %s %s %d %s %s - HIER/- foo/bar\n", float64(time.Now().UnixNano())/1e9, src, "DENIED", 0, method, urip)
	if err := f.Sync(); err != nil {
		log.Printf("Failed to sync blockfile: %v", err)
	}
	if err := syscall.Flock(int(f.Fd()), syscall.LOCK_UN); err != nil {
		return err
	}
	return nil
}

func loadConfig() (*Config, error) {
	cfg := &Config{
		Sources: make(map[string][]string),
		Rules:   make(map[string]Rule),
	}
	if err := func() error {
		rows, err := db.Query(`
SELECT sources.source, rules.rule_id
FROM sources
JOIN members ON sources.source_id=members.source_id
JOIN groups ON members.group_id=groups.group_id
JOIN groupaccess ON groups.group_id=groupaccess.group_id
JOIN acls ON groupaccess.acl_id=acls.acl_id
JOIN aclrules ON acls.acl_id=aclrules.acl_id
JOIN rules ON aclrules.rule_id=rules.rule_id`)
		if err != nil {
			return err
		}
		defer rows.Close()
		for rows.Next() {
			var source, rule string
			if err := rows.Scan(&source, &rule); err != nil {
				return err
			}
			cfg.Sources[source] = append(cfg.Sources[source], rule)
		}
		return rows.Err()
	}(); err != nil {
		return nil, err
	}

	if err := func() error {
		rows, err := db.Query(`
SELECT rule_id, type, value, action
FROM rules
`)
		if err != nil {
			return err
		}
		defer rows.Close()
		for rows.Next() {
			var rule, typ, val, act string
			if err := rows.Scan(&rule, &typ, &val, &act); err != nil {
				return err
			}
			var r Rule
			switch typ {
			case "https-domain":
				r = &HTTPSDomainRule{value: val, action: action(act)}
			case "domain":
				r = &DomainRule{value: val, action: action(act)}
			case "exact":
				r = &ExactRule{value: val, action: action(act)}
			case "regex":
				x, err := regexp.Compile(val)
				if err != nil {
					return fmt.Errorf("compiling regex %q: %v", val, err)
				}
				r = &RegexRule{re: x, action: action(act)}
			default:
				return fmt.Errorf("unknown rule type %q", typ)
			}
			cfg.Rules[rule] = r
		}
		return rows.Err()
	}(); err != nil {
		return nil, err
	}
	return cfg, nil
}

func openDB() {
	var err error
	db, err = sql.Open("sqlite3", *dbFile)
	if err != nil {
		log.Fatalf("Failed to open database %q: %v", *dbFile, err)
	}
	if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil {
		log.Fatalf("Failed to turn on foreign keys")
	}
}

func main() {
	flag.Parse()
	log.SetFlags(log.LstdFlags | log.LUTC)
	if flag.NArg() > 0 {
		log.Fatalf("Extra args on cmdline: %q", flag.Args())
	}
	if *logFile != "" {
		f, err := os.OpenFile(*logFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
		if err != nil {
			log.Fatalf("Opening log file %q: %v", *logFile, err)
		}
		defer f.Close()
		log.SetOutput(f)
	}
	openDB()
	defer db.Close()
	log.Printf("Running...")
	mainLoop()
}
<commit_msg>Remove repetition in rule action code<commit_after>/**
external ACL helper for squid.

Configure with:
  external_acl_type ext ttl=10 concurrency=2 %PROTO %SRC %METHOD %URI /usr/local/bin/proxyacl -db=/var/spool/squid3/proxyacl.sqlite -log=/var/log/squid3/proxyacl.log
  acl ext_acl external ext
  http_access allow ext_acl

Copyright 2016 Google Inc.

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

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

Unless required by applicable law or 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 (
	"bufio"
	"database/sql"
	"flag"
	"fmt"
	"log"
	"net"
	"net/url"
	"os"
	"regexp"
	"strings"
	"syscall"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

var (
	dbFile   = flag.String("db", "", "sqlite database.")
	logFile  = flag.String("log", "", "Logfile. Default to stderr.")
	verbose  = flag.Int("v", 1, "Verbosity level.")
	blockLog = flag.String("block_log", "", "Block log.")

	db *sql.DB
)

type action string

const (
	actionNone   action = "none"
	actionBlock  action = "block"
	actionIgnore action = "ignore"
	actionAllow  action = "allow"

	actionDefault = actionBlock

	aclMatch   = "OK"
	aclNoMatch = "ERR"
)

type Config struct {
	// Map from source to rules.
	Sources map[string][]string
	Rules   map[string]RuleAction
}

type Rule interface {
	Check(proto, src, method, uri string) (bool, error)
}

type RuleAction struct {
	rule   Rule
	action action
}

type DomainRule struct {
	value string
}

func (d *DomainRule) Check(proto, src, method, uri string) (bool, error) {
	if proto != "HTTP" {
		return false, nil
	}
	if d.value == "" {
		return false, nil
	}
	p, err := url.Parse(uri)
	if err != nil {
		return false, err
	}

	// Exact hostname.
	if p.Host == d.value {
		return true, nil
	}

	// Domain suffix.
	if d.value[0] == '.' && ("."+p.Host == d.value || strings.HasSuffix(p.Host, d.value)) {
		return true, nil
	}
	return false, nil
}

type RegexRule struct {
	re *regexp.Regexp
}

func (d *RegexRule) Check(proto, src, method, uri string) (bool, error) {
	if proto != "HTTP" {
		return false, nil
	}
	if d.re.MatchString(uri) {
		return true, nil
	}
	return false, nil
}

type ExactRule struct {
	value string
}

func (d *ExactRule) Check(proto, src, method, uri string) (bool, error) {
	if proto != "HTTP" {
		return false, nil
	}
	return d.value == uri, nil
}

type HTTPSDomainRule struct {
	value string
}

func (d *HTTPSDomainRule) Check(proto, src, method, uri string) (bool, error) {
	if proto != "NONE" {
		return false, nil
	}
	if method != "CONNECT" {
		return false, nil
	}
	dhost, dport, err := net.SplitHostPort(d.value)
	if err != nil {
		dhost, dport = d.value, "443"
	}
	if dhost == "" {
		return false, nil
	}
	host, port, err := net.SplitHostPort(uri)
	if err != nil {
		return false, fmt.Errorf("failed to parse HTTPS host:port %q: %v", uri, err)
	}
	if port != dport {
		return false, nil
	}
	// Exact hostname.
	if host == dhost {
		return true, nil
	}

	// Domain suffix.
	if dhost[0] == '.' && ("."+host == dhost || strings.HasSuffix(host, dhost)) {
		return true, nil
	}
	return false, nil
}

// decide returns 'match found', 'action to take', error
func decide(cfg *Config, proto, src, method, uri string) (bool, action, error) {
	// Special case this because net/url can't parse these.
	if strings.HasPrefix(uri, "cache_object://") {
		return true, actionIgnore, nil
	}

	source := net.ParseIP(src)
	if source == nil {
		return false, actionNone, fmt.Errorf("source is not a valid address: %q", src)
	}
	for a, rules := range cfg.Sources {
		_, net, err := net.ParseCIDR(a)
		if err != nil {
			log.Printf("Not a valid source: %q: %v", a, err)
			continue
		}
		if !net.Contains(source) {
			continue
		}
		for _, ruleName := range rules {
			rule := cfg.Rules[ruleName]
			t, err := rule.rule.Check(proto, src, method, uri)
			if err != nil {
				log.Printf("Failed to evaluate rule %q: %v", ruleName, err)
			} else if t {
				return true, rule.action, nil
			}
		}
	}
	return false, actionDefault, nil
}

func mainLoop() {
	cfg, err := loadConfig()
	if err != nil {
		log.Fatal(err)
	}
	// TODO: multithread this.
	scanner := bufio.NewScanner(os.Stdin)
	lastLoad := time.Now()
	for scanner.Scan() {
		if time.Since(lastLoad) > time.Second {
			cfg2, err := loadConfig()
			if err != nil {
				log.Printf("Failed to reload database")
			} else {
				cfg = cfg2
			}
			lastLoad = time.Now()
		}

		s := strings.Split(scanner.Text(), " ")
		if *verbose > 1 {
			log.Printf("Got %q", s)
		}
		token := s[0]
		proto := s[1]
		src := s[2]
		method := s[3]
		uri := s[4]
		urip, err := url.QueryUnescape(uri)
		reply := aclNoMatch
		if err != nil {
			log.Printf("URI escape error on %q: %v", s, err)
		} else {
			_, act, err := decide(cfg, proto, src, method, urip)
			if err != nil {
				log.Printf("Decision error on %q: %v", s, err)
			}
			switch act {
			case actionBlock, actionNone:
				if *verbose > 0 && reply != aclMatch {
					log.Printf("No match(%s): %q", act, s)
				}
				if err := logBlock(proto, src, method, urip); err != nil {
					log.Printf("Logging block: %v", err)
				}
			case actionIgnore:
			case actionAllow:
				reply = aclMatch
			}
		}
		if *verbose > 1 {
			log.Printf("Replied: %s %s", token, reply)
		}
		fmt.Printf("%s %s\n", token, reply)
	}
	if err := scanner.Err(); err != nil {
		log.Fatal(err)
	}
}

func logBlock(proto, src, method, urip string) error {
	f, err := os.OpenFile(*blockLog, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {
		return err
	}
	fmt.Fprintf(f, "%f 0 %s %s %d %s %s - HIER/- foo/bar\n", float64(time.Now().UnixNano())/1e9, src, "DENIED", 0, method, urip)
	if err := f.Sync(); err != nil {
		log.Printf("Failed to sync blockfile: %v", err)
	}
	if err := syscall.Flock(int(f.Fd()), syscall.LOCK_UN); err != nil {
		return err
	}
	return nil
}

func loadConfig() (*Config, error) {
	cfg := &Config{
		Sources: make(map[string][]string),
		Rules:   make(map[string]RuleAction),
	}
	if err := func() error {
		rows, err := db.Query(`
SELECT sources.source, rules.rule_id
FROM sources
JOIN members ON sources.source_id=members.source_id
JOIN groups ON members.group_id=groups.group_id
JOIN groupaccess ON groups.group_id=groupaccess.group_id
JOIN acls ON groupaccess.acl_id=acls.acl_id
JOIN aclrules ON acls.acl_id=aclrules.acl_id
JOIN rules ON aclrules.rule_id=rules.rule_id`)
		if err != nil {
			return err
		}
		defer rows.Close()
		for rows.Next() {
			var source, rule string
			if err := rows.Scan(&source, &rule); err != nil {
				return err
			}
			cfg.Sources[source] = append(cfg.Sources[source], rule)
		}
		return rows.Err()
	}(); err != nil {
		return nil, err
	}

	if err := func() error {
		rows, err := db.Query(`
SELECT rule_id, type, value, action
FROM rules
`)
		if err != nil {
			return err
		}
		defer rows.Close()
		for rows.Next() {
			var rule, typ, val, act string
			if err := rows.Scan(&rule, &typ, &val, &act); err != nil {
				return err
			}
			r := RuleAction{action: action(act)}
			switch typ {
			case "https-domain":
				r.rule = &HTTPSDomainRule{value: val}
			case "domain":
				r.rule = &DomainRule{value: val}
			case "exact":
				r.rule = &ExactRule{value: val}
			case "regex":
				x, err := regexp.Compile(val)
				if err != nil {
					return fmt.Errorf("compiling regex %q: %v", val, err)
				}
				r.rule = &RegexRule{re: x}
			default:
				return fmt.Errorf("unknown rule type %q", typ)
			}
			cfg.Rules[rule] = r
		}
		return rows.Err()
	}(); err != nil {
		return nil, err
	}
	return cfg, nil
}

func openDB() {
	var err error
	db, err = sql.Open("sqlite3", *dbFile)
	if err != nil {
		log.Fatalf("Failed to open database %q: %v", *dbFile, err)
	}
	if _, err := db.Exec("PRAGMA foreign_keys = ON"); err != nil {
		log.Fatalf("Failed to turn on foreign keys")
	}
}

func main() {
	flag.Parse()
	log.SetFlags(log.LstdFlags | log.LUTC)
	if flag.NArg() > 0 {
		log.Fatalf("Extra args on cmdline: %q", flag.Args())
	}
	if *logFile != "" {
		f, err := os.OpenFile(*logFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
		if err != nil {
			log.Fatalf("Opening log file %q: %v", *logFile, err)
		}
		defer f.Close()
		log.SetOutput(f)
	}
	openDB()
	defer db.Close()
	log.Printf("Running...")
	mainLoop()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/hex"
	"flag"
	"fmt"
	"image/color"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/LiuPai/himawari"
)

var (
	level = flag.Int("level", 4,
		"Image quality and size choose one of [4, 8, 16, 20]")
	cache = flag.String("cache", os.TempDir(),
		"Path to the cache file directory")
	defaultImageFile = fmt.Sprintf("%s/himawari.png", os.TempDir())
	output           = flag.String("output", defaultImageFile,
		"The link of current himawari image")
	daemon = flag.Bool("daemon", false,
		"Run himawari as daemon")
	coastline = flag.Bool("coastline", false,
		"Draw coast line")
	colorStr = flag.String("color", "ff0000ff",
		"Coastline color RGBA hex string")
	tick = flag.Uint("tick", 300,
		"Duration to check himawari latest timestamp in seconds")
	pidFile = flag.String("pid", "",
		"Himawari unix like system pid file")
	latestTimestamp    *time.Time
	coastlineImageFile string
)

func checkLatestImage() (err error) {
	latestTimestamp, err = himawari.LatestTimestamp()
	if err != nil {
		return err
	}

	imageFile, err := himawari.FetchImage(*level,
		latestTimestamp,
		*cache)
	if err != nil {
		return err
	}
	if coastlineImageFile != "" {
		imageFile, err = himawari.MergeCoastline(imageFile)
		if err != nil {
			return err
		}
	}
	_ = os.Remove(*output)
	err = os.Symlink(imageFile, *output)
	if err != nil {
		return err
	}
	return nil
}

func main() {
	flag.Parse()
	switch *level {
	case 4, 8, 16, 20:
	default:
		log.Fatalf("unsupport level value: %d", *level)
	}
	// fetch coastline
	if *coastline {
		var (
			err  error
			c    color.Color
			data []byte
		)
		if *colorStr != "" && *colorStr != "ff0000ff" {
			data, err = hex.DecodeString(*colorStr)
			if err != nil {
				log.Fatal(err)
			}
			if len(data) != 4 {
				log.Fatal("invalide color format")
			}
			c = color.RGBA{
				R: data[0],
				G: data[1],
				B: data[2],
				A: data[3],
			}
		}
		coastlineImageFile, err = himawari.FetchCoastline(*level, c, *cache)
		if err != nil {
			log.Fatal(err)
		}
	}

	// oneshot fetch current himawari image
	if !*daemon {
		err := checkLatestImage()
		if err != nil {
			log.Fatal(err)
		}
		return
	}

	// store pid file
	if *pidFile != "" {
		err := ioutil.WriteFile(*pidFile,
			[]byte(fmt.Sprintf("%d", os.Getpid())),
			0644)
		if err != nil {
			log.Fatalf("failed to write pid file %s, err: %v",
				*pidFile, err)
		}
	}

	// daemon ticker
	ticker := time.NewTicker(time.Second * time.Duration(*tick))

	err := checkLatestImage()
	if err != nil {
		log.Print(err)
	}
	// main loop
	for _ = range ticker.C {
		timestamp, err := himawari.LatestTimestamp()
		if err != nil {
			log.Print(err)
			continue
		}
		// check latest timestamp
		if timestamp.Unix() != latestTimestamp.Unix() {
			latestTimestamp = timestamp
			imageFile, err := himawari.FetchImage(*level,
				latestTimestamp,
				*cache)
			if err != nil {
				log.Print(err)
				continue
			}
			_ = os.Remove(*output)
			err = os.Symlink(imageFile, *output)
			if err != nil {
				log.Print(err)
				continue
			}
		}
	}
}
<commit_msg>Bug fix: coastline for daemon not work<commit_after>package main

import (
	"encoding/hex"
	"flag"
	"fmt"
	"image/color"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/LiuPai/himawari"
)

var (
	level = flag.Int("level", 4,
		"Image quality and size choose one of [4, 8, 16, 20]")
	cache = flag.String("cache", os.TempDir(),
		"Path to the cache file directory")
	defaultImageFile = fmt.Sprintf("%s/himawari.png", os.TempDir())
	output           = flag.String("output", defaultImageFile,
		"The link of current himawari image")
	daemon = flag.Bool("daemon", false,
		"Run himawari as daemon")
	coastline = flag.Bool("coastline", false,
		"Draw coast line")
	colorStr = flag.String("color", "ff0000ff",
		"Coastline color RGBA hex string")
	tick = flag.Uint("tick", 300,
		"Duration to check himawari latest timestamp in seconds")
	pidFile = flag.String("pid", "",
		"Himawari unix like system pid file")
	latestTimestamp    *time.Time
	coastlineImageFile string
)

func checkLatestImage() (err error) {
	latestTimestamp, err = himawari.LatestTimestamp()
	if err != nil {
		return err
	}

	imageFile, err := himawari.FetchImage(*level,
		latestTimestamp,
		*cache)
	if err != nil {
		return err
	}
	if coastlineImageFile != "" {
		imageFile, err = himawari.MergeCoastline(imageFile)
		if err != nil {
			return err
		}
	}
	_ = os.Remove(*output)
	err = os.Symlink(imageFile, *output)
	if err != nil {
		return err
	}
	return nil
}

func main() {
	flag.Parse()
	switch *level {
	case 4, 8, 16, 20:
	default:
		log.Fatalf("unsupport level value: %d", *level)
	}
	// fetch coastline
	if *coastline {
		var (
			err  error
			c    color.Color
			data []byte
		)
		if *colorStr != "" && *colorStr != "ff0000ff" {
			data, err = hex.DecodeString(*colorStr)
			if err != nil {
				log.Fatal(err)
			}
			if len(data) != 4 {
				log.Fatal("invalide color format")
			}
			c = color.RGBA{
				R: data[0],
				G: data[1],
				B: data[2],
				A: data[3],
			}
		}
		coastlineImageFile, err = himawari.FetchCoastline(*level, c, *cache)
		if err != nil {
			log.Fatal(err)
		}
	}

	// oneshot fetch current himawari image
	if !*daemon {
		err := checkLatestImage()
		if err != nil {
			log.Fatal(err)
		}
		return
	}

	// store pid file
	if *pidFile != "" {
		err := ioutil.WriteFile(*pidFile,
			[]byte(fmt.Sprintf("%d", os.Getpid())),
			0644)
		if err != nil {
			log.Fatalf("failed to write pid file %s, err: %v",
				*pidFile, err)
		}
	}

	// daemon ticker
	ticker := time.NewTicker(time.Second * time.Duration(*tick))

	err := checkLatestImage()
	if err != nil {
		log.Print(err)
	}
	// main loop
	for _ = range ticker.C {
		timestamp, err := himawari.LatestTimestamp()
		if err != nil {
			log.Print(err)
			continue
		}
		// check latest timestamp
		if timestamp.Unix() != latestTimestamp.Unix() {
			latestTimestamp = timestamp
			imageFile, err := himawari.FetchImage(*level,
				latestTimestamp,
				*cache)
			if err != nil {
				log.Print(err)
				continue
			}
			if coastlineImageFile != "" {
				imageFile, err = himawari.MergeCoastline(imageFile)
				if err != nil {
					log.Print(err)
					continue
				}
			}
			_ = os.Remove(*output)
			err = os.Symlink(imageFile, *output)
			if err != nil {
				log.Print(err)
				continue
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"log/syslog"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	"github.com/immortal/immortal"
)

var version string

func main() {
	parser := &immortal.Parse{
		UserLookup: user.Lookup,
	}

	// flag set
	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	fs.Usage = parser.Usage(fs)

	cfg, err := immortal.ParseArgs(parser, fs)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	// if -v print version
	if (fs.Lookup("v")).Value.(flag.Getter).Get().(bool) {
		fmt.Printf("%s\n", version)
		os.Exit(0)
	}

	// log to syslog
	logger, err := syslog.New(syslog.LOG_NOTICE|syslog.LOG_DAEMON, "immortal")
	if err == nil {
		log.SetOutput(logger)
		log.SetFlags(0)
	} else {
		defer logger.Close()
	}

	// check for dependencies to be up and running otherwise don't start
	if len(cfg.Require) > 0 {
		halt := false
		ctl := &immortal.Controller{}
		for _, r := range cfg.Require {
			socket := filepath.Join(immortal.GetSdir(), r, "immortal.sock")
			if status, err := ctl.GetStatus(socket); err != nil {
				halt = true
			} else if status.Up == "" {
				halt = true
			}
		}
		if halt {
			log.Fatalf("required services are not UP: %s", strings.Join(cfg.Require, ", "))
		}
	}

	// fork
	if os.Getppid() > 1 {
		if pid, err := immortal.Fork(); err != nil {
			log.Fatalf("Error while forking: %s", err)
		} else {
			if pid > 0 {
				os.Exit(0)
			}
		}
	}

	// create daemon
	daemon, err := immortal.New(cfg)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("%c  %d", immortal.Logo(), os.Getpid())

	// listen on socket
	if err := daemon.Listen(); err != nil {
		log.Fatal(err)
	}

	immortal.Supervise(daemon)
}
<commit_msg>when using require log.fatal the services that are down<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"log/syslog"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	"github.com/immortal/immortal"
)

var version string

func main() {
	parser := &immortal.Parse{
		UserLookup: user.Lookup,
	}

	// flag set
	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
	fs.Usage = parser.Usage(fs)

	cfg, err := immortal.ParseArgs(parser, fs)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	// if -v print version
	if (fs.Lookup("v")).Value.(flag.Getter).Get().(bool) {
		fmt.Printf("%s\n", version)
		os.Exit(0)
	}

	// log to syslog
	logger, err := syslog.New(syslog.LOG_NOTICE|syslog.LOG_DAEMON, "immortal")
	if err == nil {
		log.SetOutput(logger)
		log.SetFlags(0)
	} else {
		defer logger.Close()
	}

	// check for required services to be UP otherwise don't start
	if len(cfg.Require) > 0 {
		down := []string{}
		ctl := &immortal.Controller{}
		for _, r := range cfg.Require {
			socket := filepath.Join(immortal.GetSdir(), r, "immortal.sock")
			if status, err := ctl.GetStatus(socket); err != nil {
				down = append(down, r)
			} else if status.Up == "" {
				down = append(down, r)
			}
		}
		if len(down) > 0 {
			log.Fatalf("required services are not UP: %s", strings.Join(down, ", "))
		}
	}

	// fork
	if os.Getppid() > 1 {
		if pid, err := immortal.Fork(); err != nil {
			log.Fatalf("Error while forking: %s", err)
		} else {
			if pid > 0 {
				os.Exit(0)
			}
		}
	}

	// create daemon
	daemon, err := immortal.New(cfg)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf("%c  %d", immortal.Logo(), os.Getpid())

	// listen on socket
	if err := daemon.Listen(); err != nil {
		log.Fatal(err)
	}

	immortal.Supervise(daemon)
}
<|endoftext|>