<commit_before>package main

import (
	_ "time"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"runtime"
	"errors"
	"html/template"
	"os/exec"
)

var templates = template.Must(template.ParseFiles("index.html"))

const MESSAGE_QUEUE_SIZE = 10

const STATUS_FAILURE = "{\"status\":\"failure\"}"

var authKey = []byte("somesecretauth")
var store sessions.Store

var pool *Pool
var clients map[int64]*Client

var db *sql.DB

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	otherid int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      []byte
	client1 *Client
	client2 *Client
	Question *Question
}

type Question struct {
	Id 	int64			`json:"id"`
	Title string		`json:"title"`
	Body string			`json:"body"`
	Difficulty int 		`json:"diff"`
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		for c1.id == c2.id {
			c2 = <- p.in
		}

		fmt.Println("match found for ", c1.id, " and ", c2.id)

		b := make([]byte, 32)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 32 {
			return
		}

		row := db.QueryRow("SELECT * FROM questions ORDER BY RAND()")
		q := new(Question)
		err = row.Scan(&q.Id, &q.Title, &q.Body, &q.Difficulty)
		if err !=  nil {
			fmt.Println(err)
		}

		room := &Room{b, c1, c2, q}

		c1.otherid, c2.otherid = c2.id, c1.id
		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	if userid == nil {
		return 0, errors.New("no cookie set")
	}
	return userid.(int64), nil
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, _ = sql.Open("mysql", "root:pass@/suitup")
	defer db.Close()

	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/", mainHandle)

	http.HandleFunc("/login", login)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)

	http.HandleFunc("/question/new", newQuestion)
	http.HandleFunc("/question/submit", testCode)

	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)

	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("/home/suitup/hackmit/assets/"))))
	http.ListenAndServe(":8080", nil)
}

func mainHandle(w http.ResponseWriter, r *http.Request) {
	templates.ExecuteTemplate(w, "index.html", nil)
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("join ", uid)

	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string, MESSAGE_QUEUE_SIZE),
		retChan: retChan,
	}

	clients[uid] = client
	pool.in <- client

	chatroom := <- retChan

	fmt.Println("joinChatRoom-chatroom.id: ", chatroom.id)

	qjs, err := json.Marshal(chatroom.Question)

	fmt.Println("qjs: ", string(qjs))

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":\"", asciify(chatroom.id), "\",\"question\":", string(qjs), "}")
}

func asciify(ba []byte) string {
	ret := make([]byte, len(ba))
	for i, b := range ba {
		ret[i] = (b % 26) + 97
	}
	return string(ret)
}

func testCode(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	code := r.FormValue("submission")
	_ = r.FormValue("cvid")
	qid := "1"
	app := "/home/suitup/hackmit/secure.sh"
	// out1, err1 := exec.Command("/usr/bin/secure", qid, code).Output()
	// if err1 != nil {
 //    	fmt.Println(err1)
	// }
	// fmt.Printf("The date is %s\n", out1)
	out, err := exec.Command(app, qid, code).Output()
    if err != nil {
    	fmt.Println("LOL", err)
    	return
    }
    fmt.Println(string(out))
    fmt.Fprint(w, "{\"status\":\"success\", \"data\": \"", string(out), "\"}" )
    return
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	client := clients[uid]

	if client != nil {
		fmt.Println("leaving")
		delete(clients, client.otherid)
		delete(clients, uid)
	}

	fmt.Println("leave ", uid)

	fmt.Fprint(w, "{\"status\":\"success\"}")
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("send ", uid)

	r.ParseForm()
	message := r.FormValue("s")

	client := clients[uid]

	if client != nil {
		client.out <- message
		fmt.Fprint(w, "{\"status\":\"success\"}")
	} else {
		fmt.Fprint(w, STATUS_FAILURE)
	}
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("check ", uid)

	client := clients[uid]

	if client != nil {
		select {
		case message, ok := <- client.in:
			if ok {
				fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"", message, "\"}")
			} else {
				delete(clients, uid)
				fmt.Fprint(w, "{\"status\":\"failure\"}")
			}
		default:
			fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"\"}")
		}

	} else {
		fmt.Fprint(w, "{\"status\":\"failure\",\"s\":\"\"}")
	}
}

type User struct {
    Id            	int64      	`json:"id"`
    FacebookId  	string		`json:fbid"`
    Username 		string		`json:username"`
    Email 			string		`json:email"`
    Level 			int64		`json:lvl"`
    Score 			int64		`json:score"`
}

func newQuestion(w http.ResponseWriter, r *http.Request) {
	row := db.QueryRow("SELECT * FROM questions ORDER BY RAND()")
	q := new(Question)
	err := row.Scan(&q.Id, &q.Title, &q.Body, &q.Difficulty)

	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	b, err := json.Marshal(q)
	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	fmt.Fprint(w, string(b))
}

func login(w http.ResponseWriter, r *http.Request) {
	inputToken := r.FormValue("access_token")
	if len(inputToken) != 0 {
		uid := GetMe(inputToken)

		// row := db.QueryRow("SELECT id FROM users")
		row := db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
		user := new(User)
		err := row.Scan(&user.Id)

		if err != nil {
			_, err = db.Exec("INSERT INTO users (facebook_id, username, email, level, points) VALUES (?, ?, ?, 0, 0)", uid, "", "")
			if err != nil {
				fmt.Fprint(w, STATUS_FAILURE)
				return
			} else {
				row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
				err = row.Scan(&user.Id)
				if err != nil {
					fmt.Fprint(w, STATUS_FAILURE)
					return
				}
			}

		}

		session, _ := store.Get(r, "session")
		session.Values["userid"] = user.Id
		session.Save(r, w)

		fmt.Fprint(w, "{\"status\":\"success\"}")
	}
}

func readHttpBody(response *http.Response) string {
	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

func getUncachedResponse(uri string) (*http.Response, error) {
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if (err != nil) {
	}
	return nil, err

}

func GetMe(token string) string {
	response, err := getUncachedResponse("https://graph.facebook.com/me?access_token="+token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<commit_msg>adding changes to bash call<commit_after>package main

import (
	_ "time"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"github.com/gorilla/sessions"
	"io"
	"net/http"
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
	"runtime"
	"errors"
	"html/template"
	"os/exec"
)

var templates = template.Must(template.ParseFiles("index.html"))

const MESSAGE_QUEUE_SIZE = 10

const STATUS_FAILURE = "{\"status\":\"failure\"}"

var authKey = []byte("somesecretauth")
var store sessions.Store

var pool *Pool
var clients map[int64]*Client

var db *sql.DB

type Pool struct {
	in  chan *Client
	out chan *Room
}

type Client struct {
	id      int64
	otherid int64
	in      chan string
	out     chan string
	retChan chan *Room
}

type Room struct {
	id      []byte
	client1 *Client
	client2 *Client
	Question *Question
}

type Question struct {
	Id 	int64			`json:"id"`
	Title string		`json:"title"`
	Body string			`json:"body"`
	Difficulty int 		`json:"diff"`
}

func (p *Pool) Pair() {
	for {
		c1, c2 := <-p.in, <-p.in

		for c1.id == c2.id {
			c2 = <- p.in
		}

		fmt.Println("match found for ", c1.id, " and ", c2.id)

		b := make([]byte, 32)
		n, err := io.ReadFull(rand.Reader, b)
		if err != nil || n != 32 {
			return
		}

		row := db.QueryRow("SELECT * FROM questions ORDER BY RAND()")
		q := new(Question)
		err = row.Scan(&q.Id, &q.Title, &q.Body, &q.Difficulty)
		if err !=  nil {
			fmt.Println(err)
		}

		room := &Room{b, c1, c2, q}

		c1.otherid, c2.otherid = c2.id, c1.id
		c1.in, c2.in = c2.out, c1.out

		c1.retChan <- room
		c2.retChan <- room
	}
}

func newPool() *Pool {
	pool := &Pool{
		in:  make(chan *Client),
		out: make(chan *Room),
	}

	go pool.Pair()

	return pool
}

func UIDFromSession(w http.ResponseWriter, r *http.Request) (int64, error) {
	session, _ := store.Get(r, "session")
	userid := session.Values["userid"]

	if userid == nil {
		return 0, errors.New("no cookie set")
	}
	return userid.(int64), nil
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	db, _ = sql.Open("mysql", "root:pass@/suitup")
	defer db.Close()

	store = sessions.NewCookieStore(authKey)

	pool = newPool()
	clients = make(map[int64]*Client)

	http.HandleFunc("/", mainHandle)

	http.HandleFunc("/login", login)

	http.HandleFunc("/message/check", checkMessage)
	http.HandleFunc("/message/send", sendMessage)

	http.HandleFunc("/question/new", newQuestion)
	http.HandleFunc("/question/submit", testCode)

	http.HandleFunc("/chatroom/join", joinChatRoom)
	http.HandleFunc("/chatroom/leave", leaveChatRoom)

	http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("/home/suitup/hackmit/assets/"))))
	http.ListenAndServe(":8080", nil)
}

func mainHandle(w http.ResponseWriter, r *http.Request) {
	templates.ExecuteTemplate(w, "index.html", nil)
}

func joinChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("join ", uid)

	retChan := make(chan *Room)
	client := &Client{
		id:      uid,
		in:      nil,
		out:     make(chan string, MESSAGE_QUEUE_SIZE),
		retChan: retChan,
	}

	clients[uid] = client
	pool.in <- client

	chatroom := <- retChan

	fmt.Println("joinChatRoom-chatroom.id: ", chatroom.id)

	qjs, err := json.Marshal(chatroom.Question)

	fmt.Println("qjs: ", string(qjs))

	fmt.Fprint(w, "{\"status\":\"success\",\"crid\":\"", asciify(chatroom.id), "\",\"question\":", string(qjs), "}")
}

func asciify(ba []byte) string {
	ret := make([]byte, len(ba))
	for i, b := range ba {
		ret[i] = (b % 26) + 97
	}
	return string(ret)
}

func testCode(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	code := r.FormValue("submission")
	_ = r.FormValue("cvid")
	qid := "1"
	app := "secure.sh"
	// out1, err1 := exec.Command("/usr/bin/secure", qid, code).Output()
	// if err1 != nil {
 //    	fmt.Println(err1)
	// }
	// fmt.Printf("The date is %s\n", out1)
	out, err := exec.Command(app, qid, code).Output()
    if err != nil {
    	fmt.Println("LOL", err)
    	return
    }
    fmt.Println(string(out))
    fmt.Fprint(w, "{\"status\":\"success\", \"data\": \"", string(out), "\"}" )
    return
}

func leaveChatRoom(w http.ResponseWriter, r *http.Request) {
	uid, _ := UIDFromSession(w, r)
	client := clients[uid]

	if client != nil {
		fmt.Println("leaving")
		delete(clients, client.otherid)
		delete(clients, uid)
	}

	fmt.Println("leave ", uid)

	fmt.Fprint(w, "{\"status\":\"success\"}")
}

func sendMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("send ", uid)

	r.ParseForm()
	message := r.FormValue("s")

	client := clients[uid]

	if client != nil {
		client.out <- message
		fmt.Fprint(w, "{\"status\":\"success\"}")
	} else {
		fmt.Fprint(w, STATUS_FAILURE)
	}
}

func checkMessage(w http.ResponseWriter, r *http.Request) {
	uid, err := UIDFromSession(w, r)
	handleError(err)

	fmt.Println("check ", uid)

	client := clients[uid]

	if client != nil {
		select {
		case message, ok := <- client.in:
			if ok {
				fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"", message, "\"}")
			} else {
				delete(clients, uid)
				fmt.Fprint(w, "{\"status\":\"failure\"}")
			}
		default:
			fmt.Fprint(w, "{\"status\":\"success\",\"s\":\"\"}")
		}

	} else {
		fmt.Fprint(w, "{\"status\":\"failure\",\"s\":\"\"}")
	}
}

type User struct {
    Id            	int64      	`json:"id"`
    FacebookId  	string		`json:fbid"`
    Username 		string		`json:username"`
    Email 			string		`json:email"`
    Level 			int64		`json:lvl"`
    Score 			int64		`json:score"`
}

func newQuestion(w http.ResponseWriter, r *http.Request) {
	row := db.QueryRow("SELECT * FROM questions ORDER BY RAND()")
	q := new(Question)
	err := row.Scan(&q.Id, &q.Title, &q.Body, &q.Difficulty)

	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	b, err := json.Marshal(q)
	if err != nil {
		fmt.Println(err)
		fmt.Fprint(w, STATUS_FAILURE)
	}

	fmt.Fprint(w, string(b))
}

func login(w http.ResponseWriter, r *http.Request) {
	inputToken := r.FormValue("access_token")
	if len(inputToken) != 0 {
		uid := GetMe(inputToken)

		// row := db.QueryRow("SELECT id FROM users")
		row := db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
		user := new(User)
		err := row.Scan(&user.Id)

		if err != nil {
			_, err = db.Exec("INSERT INTO users (facebook_id, username, email, level, points) VALUES (?, ?, ?, 0, 0)", uid, "", "")
			if err != nil {
				fmt.Fprint(w, STATUS_FAILURE)
				return
			} else {
				row = db.QueryRow("SELECT id FROM users WHERE facebook_id=?", string(uid))
				err = row.Scan(&user.Id)
				if err != nil {
					fmt.Fprint(w, STATUS_FAILURE)
					return
				}
			}

		}

		session, _ := store.Get(r, "session")
		session.Values["userid"] = user.Id
		session.Save(r, w)

		fmt.Fprint(w, "{\"status\":\"success\"}")
	}
}

func readHttpBody(response *http.Response) string {
	bodyBuffer := make([]byte, 1000)
	var str string

	count, err := response.Body.Read(bodyBuffer)

	for ; count > 0; count, err = response.Body.Read(bodyBuffer) {

		if err != nil {

		}

		str += string(bodyBuffer[:count])
	}

	return str

}

func getUncachedResponse(uri string) (*http.Response, error) {
	request, err := http.NewRequest("GET", uri, nil)

	if err == nil {
		request.Header.Add("Cache-Control", "no-cache")

		client := new(http.Client)

		return client.Do(request)
	}

	if (err != nil) {
	}
	return nil, err

}

func GetMe(token string) string {
	response, err := getUncachedResponse("https://graph.facebook.com/me?access_token="+token)

	if err == nil {

		var jsonBlob interface{}

		responseBody := readHttpBody(response)

		if responseBody != "" {
			err = json.Unmarshal([]byte(responseBody), &jsonBlob)

			if err == nil {
				jsonObj := jsonBlob.(map[string]interface{})
				return jsonObj["id"].(string)
			}
		}
		return err.Error()
	}

	return err.Error()
}

func handleError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}
<|endoftext|>


<commit_before>/*
Package jsend implements JSend* specification.

Use by wrapping existing ResponseWriter

You can wrap your ResponseWriter:

	w := jsend.Wrap(w)

Returning object is also implements http.ResponseWriter. You can "Write" your json blob to it.

	w.Write(`{"foo": "bar"}`)

	// body:
	{
		"status": "success",
		"data": {
			"foo": "bar"
		}
	}

Status field in response body is derived from http status code. Status is "fail"
if code is 4XX, "error" if code is 5XX and "success" otherwise.

Fail:
	w.WriteHeader(400)
	w.Write(`{"foo": "invalid"}`)

	// body:
	{
		"status": "fail",
		"data": {
			"foo": "invalid"
		}
	}

Error:
	w.WriteHeader(500)
	w.Write(`we are closed`)

	// body:
	{
		"status": "error",
		"message": "we are closed"
	}

Note: Write method does not json-encode your data.

Use status functions

You can use Success, Fail and Error functions directly to write json responses with those statuses.

Success:
	jsend.Success(w, data, 200) // w is ResponseWriter

	// body:
	{
		"status": "success",
		"data": // your json encoded data
	}

Fail:
	jsend.Fail(w, data, 400)

	// body:
	{
		"status": "fail",
		"data": // your json encoded data
	}

Error:
	jsend.Error(w, "something bad happened", 500)

	{
		"status": "error",
		"message": "something bad happened"
	}

Success, Fail and Error functions write given statusCode to response. Also
"Content-Type" header is set to "application/json" if it is not set already.

* See http://labs.omniti.com/labs/jsend for details.
*/
package jsend

import (
	"encoding/json"
	"errors"
	"net/http"
	"sync"
)

// JSend status codes
const (
	StatusSuccess = "success"
	StatusError   = "error"
	StatusFail    = "fail"
)

// Error types
var (
	ErrInvalidRawJSON = errors.New("jsend: given data is not a valid json.RawMessage")
	ErrJSONEncode     = errors.New("jsend: could not json encode given data")
	ErrWrittenAlready = errors.New("jsend: written already")
)

// Success json encodes and writes data to specified response with "success" status.
func Success(w http.ResponseWriter, data interface{}, code int) (int, error) {
	return write(w, StatusSuccess, code, data, "")
}

// Error writes error string to specified response with "error" status.
func Error(w http.ResponseWriter, error string, code int) (int, error) {
	return write(w, StatusError, code, nil, error)
}

// Fail json encodes and writes data to specified response with "fail" status.
func Fail(w http.ResponseWriter, data interface{}, code int) (int, error) {
	return write(w, StatusFail, code, data, "")
}

type jsonResponse struct {
	Status  string          `json:"status"`
	Data    json.RawMessage `json:"data,omitempty"`
	Message string          `json:"message,omitempty"`
}

func writeJSONResponse(w http.ResponseWriter, response *jsonResponse) (int, error) {
	resJSON, err := json.Marshal(response)
	if err != nil {
		return 0, ErrInvalidRawJSON
	}

	return w.Write(resJSON)
}

func write(w http.ResponseWriter, status string, statusCode int, data interface{}, error string) (int, error) {
	res := &jsonResponse{Status: status}
	if data != nil {
		dataJSON, err := json.Marshal(data)
		if err != nil {
			return 0, ErrJSONEncode
		}

		res.Data = dataJSON
	}

	if error != "" {
		res.Message = error
	}

	if w.Header().Get("Content-Type") == "" {
		w.Header().Set("Content-Type", "application/json")
	}

	w.WriteHeader(statusCode)

	return writeJSONResponse(w, res)
}

// Wrap wraps given http.ResponseWriter and returns a response object which
// implements http.ResponseWriter interface.
func Wrap(rw http.ResponseWriter) http.ResponseWriter {
	if rw.Header().Get("Content-Type") == "" {
		rw.Header().Set("Content-Type", "application/json")
	}

	return &response{rw: rw}
}

// A response wraps a http.ResponseWriter.
type response struct {
	rw      http.ResponseWriter
	code    int
	written bool
	sync.Mutex
}

func (r *response) Header() http.Header {
	return r.rw.Header()
}

func (r *response) WriteHeader(code int) {
	r.code = code
	r.rw.WriteHeader(code)
}

func (r *response) Write(data []byte) (int, error) {
	r.Lock()
	defer r.Unlock()

	if r.written {
		return 0, ErrWrittenAlready
	}
	r.written = true

	st := getStatus(r.code)
	jr := &jsonResponse{Status: st}
	switch st {
	case StatusError:
		jr.Message = string(data)
	case StatusFail:
		jr.Data = data
	default:
		jr.Data = data
	}

	return writeJSONResponse(r.rw, jr)
}

func getStatus(code int) string {
	switch {
	case code >= 500:
		return StatusError
	case code >= 400 && code < 500:
		return StatusFail
	}

	return StatusSuccess
}
<commit_msg>Fix omit-empty problems<commit_after>/*
Package jsend implements JSend* specification.

Use by wrapping existing ResponseWriter

You can wrap your ResponseWriter:

	w := jsend.Wrap(w)

Returning object is also implements http.ResponseWriter. You can "Write" your json blob to it.

	w.Write(`{"foo": "bar"}`)

	// body:
	{
		"status": "success",
		"data": {
			"foo": "bar"
		}
	}

Status field in response body is derived from http status code. Status is "fail"
if code is 4XX, "error" if code is 5XX and "success" otherwise.

Fail:
	w.WriteHeader(400)
	w.Write(`{"foo": "invalid"}`)

	// body:
	{
		"status": "fail",
		"data": {
			"foo": "invalid"
		}
	}

Error:
	w.WriteHeader(500)
	w.Write(`we are closed`)

	// body:
	{
		"status": "error",
		"message": "we are closed"
	}

Note: Write method does not json-encode your data.

Use status functions

You can use Success, Fail and Error functions directly to write json responses with those statuses.

Success:
	jsend.Success(w, data, 200) // w is ResponseWriter

	// body:
	{
		"status": "success",
		"data": // your json encoded data
	}

Fail:
	jsend.Fail(w, data, 400)

	// body:
	{
		"status": "fail",
		"data": // your json encoded data
	}

Error:
	jsend.Error(w, "something bad happened", 500)

	{
		"status": "error",
		"message": "something bad happened"
	}

Success, Fail and Error functions write given statusCode to response. Also
"Content-Type" header is set to "application/json" if it is not set already.

* See http://labs.omniti.com/labs/jsend for details.
*/
package jsend

import (
	"encoding/json"
	"errors"
	"net/http"
	"sync"
)

// JSend status codes
const (
	StatusSuccess = "success"
	StatusError   = "error"
	StatusFail    = "fail"
)

// Error types
var (
	ErrInvalidRawJSON = errors.New("jsend: given data is not a valid json.RawMessage")
	ErrJSONEncode     = errors.New("jsend: could not json encode given data")
	ErrWrittenAlready = errors.New("jsend: written already")
)

// Success json encodes and writes data to specified response with "success" status.
func Success(w http.ResponseWriter, data interface{}, code int) (int, error) {
	return write(w, StatusSuccess, code, data, "")
}

// Error writes error string to specified response with "error" status.
func Error(w http.ResponseWriter, error string, code int) (int, error) {
	return write(w, StatusError, code, nil, error)
}

// Fail json encodes and writes data to specified response with "fail" status.
func Fail(w http.ResponseWriter, data interface{}, code int) (int, error) {
	return write(w, StatusFail, code, data, "")
}

type jsonResponse struct {
	Status  string
	Data    json.RawMessage
	Message string
}

func (j *jsonResponse) MarshalJSON() ([]byte, error) {
	if j.Status == StatusError {
		return json.Marshal(map[string]interface{}{
			"status":  j.Status,
			"message": j.Message,
		})
	}

	var data *json.RawMessage
	if len(j.Data) > 0 {
		data = &j.Data
	}

	return json.Marshal(map[string]interface{}{
		"status": j.Status,
		"data":   data,
	})
}

func writeJSONResponse(w http.ResponseWriter, response *jsonResponse) (int, error) {
	resJSON, err := json.Marshal(response)
	if err != nil {
		return 0, ErrInvalidRawJSON
	}

	return w.Write(resJSON)
}

func write(w http.ResponseWriter, status string, statusCode int, data interface{}, error string) (int, error) {
	res := &jsonResponse{Status: status}
	if data != nil {
		dataJSON, err := json.Marshal(data)
		if err != nil {
			return 0, ErrJSONEncode
		}

		res.Data = dataJSON
	}

	if error != "" {
		res.Message = error
	}

	if w.Header().Get("Content-Type") == "" {
		w.Header().Set("Content-Type", "application/json")
	}

	w.WriteHeader(statusCode)

	return writeJSONResponse(w, res)
}

// Wrap wraps given http.ResponseWriter and returns a response object which
// implements http.ResponseWriter interface.
func Wrap(rw http.ResponseWriter) http.ResponseWriter {
	if rw.Header().Get("Content-Type") == "" {
		rw.Header().Set("Content-Type", "application/json")
	}

	return &response{rw: rw}
}

// A response wraps a http.ResponseWriter.
type response struct {
	rw      http.ResponseWriter
	code    int
	written bool
	sync.Mutex
}

func (r *response) Header() http.Header {
	return r.rw.Header()
}

func (r *response) WriteHeader(code int) {
	r.code = code
	r.rw.WriteHeader(code)
}

func (r *response) Write(data []byte) (int, error) {
	r.Lock()
	defer r.Unlock()

	if r.written {
		return 0, ErrWrittenAlready
	}
	r.written = true

	st := getStatus(r.code)
	jr := &jsonResponse{Status: st}
	switch st {
	case StatusError:
		jr.Message = string(data)
	case StatusFail:
		jr.Data = data
	default:
		jr.Data = data
	}

	return writeJSONResponse(r.rw, jr)
}

func getStatus(code int) string {
	switch {
	case code >= 500:
		return StatusError
	case code >= 400 && code < 500:
		return StatusFail
	}

	return StatusSuccess
}
<|endoftext|>


<commit_before>package slave

import (
	"github.com/KIT-MAMID/mamid/msp"
	"os/exec"
	"time"
)

type ProcessManager struct {
	killChan         chan msp.PortNumber
	command          string
	dataDir          string
	runningProcesses map[msp.PortNumber]*exec.Cmd
}

func NewProcessManager(command string, dataDir string) *ProcessManager {
	return &ProcessManager{
		killChan:         make(chan msp.PortNumber),
		command:          command,
		dataDir:          dataDir,
		runningProcesses: make(map[msp.PortNumber]*exec.Cmd),
	}
}

func (p *ProcessManager) Run() {
	go func() {
		for {
			port := <-p.killChan
			delete(p.runningProcesses, port)
		}
	}()
}

func (p *ProcessManager) HasProcess(port msp.PortNumber) bool {
	_, exists := p.runningProcesses[port]
	return exists
}

func (p *ProcessManager) SpawnProcess(m msp.Mongod) (err error) {

	if err = p.createDirSkeleton(m); err != nil {
		return
	}

	cmd := exec.Command(p.command, p.buildMongodCommandLine(m)...)
	if err := cmd.Start(); err != nil {
		return err
	}

	go func() {
		cmd.Process.Wait()
		p.killChan <- m.Port
	}()

	p.runningProcesses[m.Port] = cmd
	return nil

}

func (p *ProcessManager) RunningProcesses() []msp.PortNumber {
	ports := make([]msp.PortNumber, 0, len(p.runningProcesses))
	for port := range p.runningProcesses {
		ports = append(ports, port)
	}
	return ports
}

func (p *ProcessManager) KillProcess(port msp.PortNumber) error {
	if cmd, exists := p.runningProcesses[port]; exists {
		return cmd.Process.Kill()
	}
	return nil
}

func (p *ProcessManager) KillAfterTimeout(port msp.PortNumber, timeout time.Duration) error {
	if cmd, exists := p.runningProcesses[port]; exists {
		done := make(chan struct{})
		go func() {
			cmd.Process.Wait()
			close(done)
		}()

		select {
		case <-done:
			return nil
		case <-time.After(timeout):
			return cmd.Process.Kill()
		}
	}
	return nil
}

// killProcess is destructive. Even when there was an error (already killed, stuck state, permissions lost), we do not care. The error is purely informational that _something_ went wrong.
// This function is to be used for complete clean restart/shutdown only.
func (p *ProcessManager) KillProcesses() error {
	var err error = nil
	for _, cmd := range p.runningProcesses {
		curErr := cmd.Process.Kill()
		if err == nil {
			err = curErr
		}
	}
	p.runningProcesses = make(map[msp.PortNumber]*exec.Cmd)
	return err
}
<commit_msg>UPD: slave/procmgr: remove unused KillAfterTimeout + add log messages for process spawn + wait<commit_after>package slave

import (
	"github.com/KIT-MAMID/mamid/msp"
	"os/exec"
)

type ProcessManager struct {
	killChan         chan msp.PortNumber
	command          string
	dataDir          string
	runningProcesses map[msp.PortNumber]*exec.Cmd
}

func NewProcessManager(command string, dataDir string) *ProcessManager {
	return &ProcessManager{
		killChan:         make(chan msp.PortNumber),
		command:          command,
		dataDir:          dataDir,
		runningProcesses: make(map[msp.PortNumber]*exec.Cmd),
	}
}

func (p *ProcessManager) Run() {
	go func() {
		for {
			port := <-p.killChan
			delete(p.runningProcesses, port)
		}
	}()
}

func (p *ProcessManager) HasProcess(port msp.PortNumber) bool {
	_, exists := p.runningProcesses[port]
	return exists
}

func (p *ProcessManager) SpawnProcess(m msp.Mongod) (err error) {

	if err = p.createDirSkeleton(m); err != nil {
		return
	}

	args := p.buildMongodCommandLine(m)
	log.Debugf("spwaning Mongod with arguments: %v", args)
	cmd := exec.Command(p.command, args...)
	if err := cmd.Start(); err != nil {
		return err
	}

	go func() {
		processState, err := cmd.Process.Wait()
		if err != nil {
			log.Errorf("error waiting for process `%v`: %s", cmd, err)
		} else if !processState.Success() {
			log.Errorf("Mongod exited unsuccessfully: %v", processState)
		}
		p.killChan <- m.Port
	}()

	p.runningProcesses[m.Port] = cmd
	return nil

}

func (p *ProcessManager) RunningProcesses() []msp.PortNumber {
	ports := make([]msp.PortNumber, 0, len(p.runningProcesses))
	for port := range p.runningProcesses {
		ports = append(ports, port)
	}
	return ports
}

func (p *ProcessManager) KillProcess(port msp.PortNumber) error {
	if cmd, exists := p.runningProcesses[port]; exists {
		return cmd.Process.Kill()
	}
	return nil
}

// killProcess is destructive. Even when there was an error (already killed, stuck state, permissions lost), we do not care. The error is purely informational that _something_ went wrong.
// This function is to be used for complete clean restart/shutdown only.
func (p *ProcessManager) KillProcesses() error {
	var err error = nil
	for _, cmd := range p.runningProcesses {
		curErr := cmd.Process.Kill()
		if err == nil {
			err = curErr
		}
	}
	p.runningProcesses = make(map[msp.PortNumber]*exec.Cmd)
	return err
}
<|endoftext|>


<commit_before>// Package junos provides automation for Junos (Juniper Networks) devices.
package junos

import (
	"encoding/xml"
	"errors"
	"fmt"
	"github.com/Juniper/go-netconf/netconf"
	"io/ioutil"
	"log"
	"strings"
)

// Junos holds the connection information to our Junos device.
type Junos struct {
	*netconf.Session
}

// CommandXML parses our operational command responses.
type commandXML struct {
	Config string `xml:",innerxml"`
}

// commitError parses any errors during the commit process.
type commitError struct {
	Path    string `xml:"error-path"`
	Element string `xml:"error-info>bad-element"`
	Message string `xml:"error-message"`
}

// commitResults stores our errors if we have any.
type commitResults struct {
	XMLName xml.Name      `xml:"commit-results"`
	Errors  []commitError `xml:"rpc-error"`
}

// rollbackXML parses our rollback diff configuration.
type rollbackXML struct {
	XMLName xml.Name `xml:"rollback-information"`
	Config  string   `xml:"configuration-information>configuration-output"`
}

// Close disconnects our session to the device.
func (j *Junos) Close() {
	j.Transport.Close()
}

// Commit commits the configuration.
func (j *Junos) Commit() error {
	errs := &commitResults{}
	reply, err := j.Exec(rpcCommand["commit"])
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// CommitAt commits the configuration at the specified <time>.
func (j *Junos) CommitAt(time string) error {
	errs := &commitResults{}
	command := fmt.Sprintf(rpcCommand["commit-at"], time)
	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// CommitCheck checks the configuration for syntax errors.
func (j *Junos) CommitCheck() error {
	errs := &commitResults{}
	reply, err := j.Exec(rpcCommand["commit-check"])
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// CommitConfirm rolls back the configuration after <delay> minutes.
func (j *Junos) CommitConfirm(delay int) error {
	errs := &commitResults{}
	command := fmt.Sprintf(rpcCommand["commit-confirm"], delay)
	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// Command runs any operational mode command, such as "show" or "request."
// Format is either "text" or "xml".
func (j *Junos) Command(cmd, format string) (string, error) {
	c := &commandXML{}
	var command string

	switch format {
	case "xml":
		command = fmt.Sprintf(rpcCommand["command-xml"], cmd)
	default:
		command = fmt.Sprintf(rpcCommand["command"], cmd)
	}
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &c)
	if err != nil {
		return "", err
	}

	if c.Config == "" {
		return "No output available.", nil
	}

	return c.Config, nil
}

// LoadConfig loads a given configuration file locally or from
// an FTP or HTTP server. Format is either "set" "text" or "xml."
func (j *Junos) LoadConfig(path, format string, commit bool) error {
	var command string
	switch format {
	case "set":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-set"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-set"], string(data))
		}
	case "text":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-text"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-text"], string(data))
		}
	case "xml":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-xml"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["load-config-local-xml"], string(data))
		}
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if commit {
		err = j.Commit()
		if err != nil {
			return err
		}
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// Lock locks the candidate configuration.
func (j *Junos) Lock() error {
	reply, err := j.Exec(rpcCommand["lock"])
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// NewSession establishes a new connection to a Junos device that we will use
// to run our commands against.
func NewSession(host, user, password string) *Junos {
	s, err := netconf.DialSSH(host, netconf.SSHConfigPassword(user, password))
	if err != nil {
		log.Fatal(err)
	}

	return &Junos{
		s,
	}
}

// RollbackConfig loads and commits the configuration of a given rollback or rescue state.
func (j *Junos) RollbackConfig(option interface{}) error {
	var command string
	switch option.(type) {
	case int:
		command = fmt.Sprintf(rpcCommand["rollback-config"], option)
	case string:
		if option == "rescue" {
			command = fmt.Sprintf(rpcCommand["rescue-config"])
		}
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	err = j.Commit()
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// RollbackDiff compares the current active configuration to a given rollback configuration.
func (j *Junos) RollbackDiff(compare int) (string, error) {
	rb := &rollbackXML{}
	command := fmt.Sprintf(rpcCommand["get-rollback-information-compare"], compare)
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), rb)
	if err != nil {
		return "", err
	}

	return rb.Config, nil
}

// Unlock unlocks the candidate configuration.
func (j *Junos) Unlock() error {
	resp, err := j.Exec(rpcCommand["unlock"])
	if err != nil {
		return err
	}

	if resp.Ok == false {
		for _, m := range resp.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}
<commit_msg>Renamed rpcCommand key lookups<commit_after>// Package junos provides automation for Junos (Juniper Networks) devices.
package junos

import (
	"encoding/xml"
	"errors"
	"fmt"
	"github.com/Juniper/go-netconf/netconf"
	"io/ioutil"
	"log"
	"strings"
)

// Junos holds the connection information to our Junos device.
type Junos struct {
	*netconf.Session
}

// CommandXML parses our operational command responses.
type commandXML struct {
	Config string `xml:",innerxml"`
}

// commitError parses any errors during the commit process.
type commitError struct {
	Path    string `xml:"error-path"`
	Element string `xml:"error-info>bad-element"`
	Message string `xml:"error-message"`
}

// commitResults stores our errors if we have any.
type commitResults struct {
	XMLName xml.Name      `xml:"commit-results"`
	Errors  []commitError `xml:"rpc-error"`
}

// rollbackXML parses our rollback diff configuration.
type rollbackXML struct {
	XMLName xml.Name `xml:"rollback-information"`
	Config  string   `xml:"configuration-information>configuration-output"`
}

// Close disconnects our session to the device.
func (j *Junos) Close() {
	j.Transport.Close()
}

// Commit commits the configuration.
func (j *Junos) Commit() error {
	errs := &commitResults{}
	reply, err := j.Exec(rpcCommand["commit"])
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// CommitAt commits the configuration at the specified <time>.
func (j *Junos) CommitAt(time string) error {
	errs := &commitResults{}
	command := fmt.Sprintf(rpcCommand["commit-at"], time)
	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// CommitCheck checks the configuration for syntax errors.
func (j *Junos) CommitCheck() error {
	errs := &commitResults{}
	reply, err := j.Exec(rpcCommand["commit-check"])
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// CommitConfirm rolls back the configuration after <delay> minutes.
func (j *Junos) CommitConfirm(delay int) error {
	errs := &commitResults{}
	command := fmt.Sprintf(rpcCommand["commit-confirm"], delay)
	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &errs)
	if err != nil {
		return err
	}

	if errs.Errors != nil {
		for _, m := range errs.Errors {
			message := fmt.Sprintf("[%s]\n    %s\nError: %s", strings.Trim(m.Path, "[\r\n]"), strings.Trim(m.Element, "[\r\n]"), strings.Trim(m.Message, "[\r\n]"))
			return errors.New(message)
		}
	}

	return nil
}

// Command runs any operational mode command, such as "show" or "request."
// Format is either "text" or "xml".
func (j *Junos) Command(cmd, format string) (string, error) {
	c := &commandXML{}
	var command string

	switch format {
	case "xml":
		command = fmt.Sprintf(rpcCommand["command-xml"], cmd)
	default:
		command = fmt.Sprintf(rpcCommand["command"], cmd)
	}
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), &c)
	if err != nil {
		return "", err
	}

	if c.Config == "" {
		return "No output available.", nil
	}

	return c.Config, nil
}

// LoadConfig loads a given configuration file locally or from
// an FTP or HTTP server. Format is either "set" "text" or "xml."
func (j *Junos) LoadConfig(path, format string, commit bool) error {
	var command string
	switch format {
	case "set":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-set"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["config-file-set"], string(data))
		}
	case "text":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-text"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["config-file-text"], string(data))
		}
	case "xml":
		if strings.Contains(path, "tp://") {
			command = fmt.Sprintf(rpcCommand["load-config-url-xml"], path)
		} else {
			data, err := ioutil.ReadFile(path)
			if err != nil {
				return err
			}

			command = fmt.Sprintf(rpcCommand["config-file-xml"], string(data))
		}
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	if commit {
		err = j.Commit()
		if err != nil {
			return err
		}
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// Lock locks the candidate configuration.
func (j *Junos) Lock() error {
	reply, err := j.Exec(rpcCommand["lock"])
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// NewSession establishes a new connection to a Junos device that we will use
// to run our commands against.
func NewSession(host, user, password string) *Junos {
	s, err := netconf.DialSSH(host, netconf.SSHConfigPassword(user, password))
	if err != nil {
		log.Fatal(err)
	}

	return &Junos{
		s,
	}
}

// RollbackConfig loads and commits the configuration of a given rollback or rescue state.
func (j *Junos) RollbackConfig(option interface{}) error {
	var command string
	switch option.(type) {
	case int:
		command = fmt.Sprintf(rpcCommand["rollback-config"], option)
	case string:
		if option == "rescue" {
			command = fmt.Sprintf(rpcCommand["rescue-config"])
		}
	}

	reply, err := j.Exec(command)
	if err != nil {
		return err
	}

	err = j.Commit()
	if err != nil {
		return err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}

// RollbackDiff compares the current active configuration to a given rollback configuration.
func (j *Junos) RollbackDiff(compare int) (string, error) {
	rb := &rollbackXML{}
	command := fmt.Sprintf(rpcCommand["get-rollback-information-compare"], compare)
	reply, err := j.Exec(command)
	if err != nil {
		return "", err
	}

	if reply.Errors != nil {
		for _, m := range reply.Errors {
			return "", errors.New(m.Message)
		}
	}

	err = xml.Unmarshal([]byte(reply.Data), rb)
	if err != nil {
		return "", err
	}

	return rb.Config, nil
}

// Unlock unlocks the candidate configuration.
func (j *Junos) Unlock() error {
	resp, err := j.Exec(rpcCommand["unlock"])
	if err != nil {
		return err
	}

	if resp.Ok == false {
		for _, m := range resp.Errors {
			return errors.New(m.Message)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package peer

import (
	"image"
	"image/color"
	"image/draw"
	"log"
	"sort"
	"sync"
	"time"

	"github.com/golang/freetype/truetype"
	"github.com/pankona/gomo-simra/simra/config"
	"github.com/pankona/gomo-simra/simra/simlog"
	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/goregular"
	"golang.org/x/image/math/fixed"
	"golang.org/x/mobile/app"
	"golang.org/x/mobile/asset"
	"golang.org/x/mobile/exp/app/debug"
	"golang.org/x/mobile/exp/f32"
	"golang.org/x/mobile/exp/gl/glutil"
	"golang.org/x/mobile/exp/sprite"
	"golang.org/x/mobile/exp/sprite/clock"
	"golang.org/x/mobile/exp/sprite/glsprite"
	"golang.org/x/mobile/gl"
)

// GLer interface represents interface of GL
type GLer interface {
	// Initialize initializes GLPeer.
	// This function must be called in advance of using GLPeer
	Initialize(glc *GLContext)
	// LoadTexture return texture that is loaded by the information of arguments.
	// Loaded texture can assign using AddSprite function.
	LoadTexture(assetName string, rect image.Rectangle) sprite.SubTex
	// MakeTextureByText create and return texture by specified text
	// Loaded texture can assign using AddSprite function.
	// TODO: font parameterize
	MakeTextureByText(text string, fontsize float64, fontcolor color.RGBA, rect image.Rectangle) sprite.SubTex
	// Finalize finalizes GLPeer.
	// This is called at termination of application.
	Finalize()
	// Update updates screen.
	// This is called 60 times per 1 sec.
	Update(sc SpriteContainerer)
	// Reset resets current gl context.
	// All sprites are also cleaned.
	// This is called at changing of scene, and
	// this function is for clean previous scene.
	Reset()
	// NewTexture returns a new Texture instance
	NewTexture(s sprite.SubTex) *Texture
	// ReleaseTexture releases specified texture
	ReleaseTexture(t *Texture)
	// NewNode returns new node
	NewNode(fn arrangerFunc) *ZNode
	// AppendNode adds specified node as a child
	AppendNode(n *ZNode)
	// RemoveNode removes specified node
	RemoveNode(n *ZNode)
	// SetSubTex registers subtexture to specified node
	SetSubTex(n *ZNode, subTex *sprite.SubTex)
	// ZIndexDirty updates dirty flag with specified argument
	ZIndexDirty()
}

// GLPeer represents gl context.
// Singleton.
type GLPeer struct {
	glc         *GLContext
	startTime   time.Time
	images      *glutil.Images
	fps         *debug.FPS
	eng         sprite.Engine
	znodes      ZNodes
	mu          sync.Mutex
	zindexDirty bool
}

// ZNode represents node with zindex
type ZNode struct {
	Node   *sprite.Node
	ZIndex int
}

// ZNodes represents array of ZNode
type ZNodes []*ZNode

func (zns ZNodes) Len() int {
	return len(zns)
}

func (zns ZNodes) Less(i, j int) bool {
	return zns[i].ZIndex < zns[j].ZIndex
}

func (zns ZNodes) Swap(i, j int) {
	zns[i], zns[j] = zns[j], zns[i]
}

// NewGLPeer returns a instance of GLPeer
func NewGLPeer() GLer {
	return &GLPeer{}
}

// GLContext is a wrapper of gl.Context
type GLContext struct {
	glcontext gl.Context
	publish   func() app.PublishResult
}

// Initialize initializes GLPeer.
// This function must be called in advance of using GLPeer
func (glpeer *GLPeer) Initialize(glc *GLContext) {
	simlog.FuncIn()

	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.glc = glc
	glpeer.startTime = time.Now()

	glctx := glc.glcontext

	// transparency of png
	glctx.Enable(gl.BLEND)
	glctx.BlendEquation(gl.FUNC_ADD)
	glctx.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	glpeer.images = glutil.NewImages(glctx)
	glpeer.fps = debug.NewFPS(glpeer.images)
	glpeer.initEng()

	simlog.FuncOut()
}

func (glpeer *GLPeer) initEng() {
	if glpeer.eng != nil {
		glpeer.eng.Release()
	}
	glpeer.eng = glsprite.Engine(glpeer.images)
	glpeer.znodes = make([]*ZNode, 0)
}

type arrangerFunc func(e sprite.Engine, n *sprite.Node, t clock.Time)

func (a arrangerFunc) Arrange(e sprite.Engine, n *sprite.Node, t clock.Time) { a(e, n, t) }

// NewNode returns new node
func (glpeer *GLPeer) NewNode(fn arrangerFunc) *ZNode {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	n := &sprite.Node{Arranger: fn}
	glpeer.eng.Register(n)
	return &ZNode{Node: n}
}

// AppendNode adds specified node as a child
func (glpeer *GLPeer) AppendNode(zn *ZNode) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.znodes = append(glpeer.znodes, zn)
}

// RemoveNode removes specified node
func (glpeer *GLPeer) RemoveNode(n *ZNode) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	znodes := make([]*ZNode, len(glpeer.znodes)-1)
	for i, zn := range glpeer.znodes {
		if n != zn {
			znodes[i] = n
		}
	}
	glpeer.znodes = znodes
}

// LoadTexture return texture that is loaded by the information of arguments.
// Loaded texture can assign using AddSprite function.
func (glpeer *GLPeer) LoadTexture(assetName string, rect image.Rectangle) sprite.SubTex {
	simlog.FuncIn()

	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	a, err := asset.Open(assetName)
	if err != nil {
		simlog.Error(err)
	}
	defer func() {
		closeErr := a.Close()
		if closeErr != nil {
			simlog.Error(closeErr)
		}
	}()

	img, _, err := image.Decode(a)
	if err != nil {
		simlog.Error(err)
	}
	t, err := glpeer.eng.LoadTexture(img)
	if err != nil {
		simlog.Error(err)
	}

	simlog.FuncOut()
	return sprite.SubTex{T: t, R: rect}
}

// MakeTextureByText create and return texture by specified text
// Loaded texture can assign using AddSprite function.
// TODO: font parameterize
func (glpeer *GLPeer) MakeTextureByText(text string, fontsize float64, fontcolor color.RGBA, rect image.Rectangle) sprite.SubTex {
	simlog.FuncIn()

	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	dpi := float64(72)
	width := rect.Dx()
	height := rect.Dy()
	img := image.NewRGBA(image.Rect(0, 0, width, height))

	fg, bg := image.NewUniform(fontcolor), image.Transparent
	draw.Draw(img, img.Bounds(), bg, image.Point{}, draw.Src)

	// Draw the text.
	h := font.HintingNone

	gofont, _ := truetype.Parse(goregular.TTF)

	d := &font.Drawer{
		Dst: img,
		Src: fg,
		Face: truetype.NewFace(gofont, &truetype.Options{
			Size:    fontsize,
			DPI:     dpi,
			Hinting: h,
		}),
	}

	textWidth := d.MeasureString(text)

	d.Dot = fixed.Point26_6{
		X: fixed.I(width/2) - textWidth/2,
		Y: fixed.I(int(fontsize * dpi / 72)),
	}
	d.DrawString(text)

	t, err := glpeer.eng.LoadTexture(img)
	if err != nil {
		log.Fatal(err)
	}

	simlog.FuncOut()
	return sprite.SubTex{T: t, R: rect}
}

// Finalize finalizes GLPeer.
// This is called at termination of application.
func (glpeer *GLPeer) Finalize() {
	simlog.FuncIn()

	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	glpeer.eng.Release()
	glpeer.fps.Release()
	glpeer.images.Release()
	glpeer.glc.glcontext = nil

	simlog.FuncOut()
}

// Update updates screen.
// This is called 60 times per 1 sec.
func (glpeer *GLPeer) Update(sc SpriteContainerer) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	glctx := glpeer.glc.glcontext
	if glctx == nil {
		return
	}
	glctx.ClearColor(0, 0, 0, 1) // black background
	glctx.Clear(gl.COLOR_BUFFER_BIT)
	now := clock.Time(time.Since(glpeer.startTime) * 60 / time.Second)

	glpeer.apply(sc)

	if glpeer.zindexDirty {
		sort.Sort(glpeer.znodes)
		glpeer.zindexDirty = false
	}
	for _, zn := range glpeer.znodes {
		glpeer.eng.Render(zn.Node, now, screensize.sz)
	}
	if config.DEBUG {
		glpeer.fps.Draw(screensize.sz)
	}

	// app.Publish() calls glctx.Flush,
	// it must be called within this mutex locking.
	glpeer.glc.publish()
}

// ZIndexDirty enables dirty flag. It indicates sorting of znodes is necessary
// because at least one of their zindex has been updated.
func (glpeer *GLPeer) ZIndexDirty() {
	glpeer.zindexDirty = true
}

// Reset resets current gl context.
// All sprites are also cleaned.
// This is called at changing of scene, and
// this function is for clean previous scene.
func (glpeer *GLPeer) Reset() {
	simlog.FuncIn()

	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.initEng()

	simlog.FuncOut()
}

// SetSubTex registers subtexture to specified node
func (glpeer *GLPeer) SetSubTex(zn *ZNode, subTex *sprite.SubTex) {
	glpeer.eng.SetSubTex(zn.Node, *subTex)
}

func (glpeer *GLPeer) apply(sc SpriteContainerer) {
	snpairs := sc.GetSpriteNodePairs()
	snpairs.Range(func(k, v interface{}) bool {
		sn := v.(*spriteNodePair)
		if sn.sprite == nil || !sn.inuse {
			return true
		}
		s := sn.sprite

		affine := &f32.Affine{
			{1, 0, 0},
			{0, 1, 0},
		}
		affine.Translate(affine,
			s.X*screensize.scale-s.W/2*screensize.scale+screensize.marginWidth/2,
			(screensize.height-s.Y)*screensize.scale-s.H/2*screensize.scale+screensize.marginHeight/2)
		if s.R != 0 {
			affine.Translate(affine,
				0.5*s.W*screensize.scale,
				0.5*s.H*screensize.scale)
			affine.Rotate(affine, s.R)
			affine.Translate(affine,
				-0.5*s.W*screensize.scale,
				-0.5*s.H*screensize.scale)
		}
		affine.Scale(affine,
			s.W*screensize.scale,
			s.H*screensize.scale)
		glpeer.eng.SetTransform(sn.znode.Node, *affine)
		return true
	})
}

// Texture represents a texture object that contains subTex
type Texture struct {
	subTex sprite.SubTex
}

// NewTexture returns a new Texture instance
func (glpeer *GLPeer) NewTexture(s sprite.SubTex) *Texture {
	return &Texture{
		subTex: s,
	}
}

// ReleaseTexture releases specified texture
func (glpeer *GLPeer) ReleaseTexture(t *Texture) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	t.subTex.T.Release()
}
<commit_msg>[#66] let bigger zindex goes far side<commit_after>package peer

import (
	"image"
	"image/color"
	"image/draw"
	"log"
	"sort"
	"sync"
	"time"

	"github.com/golang/freetype/truetype"
	"github.com/pankona/gomo-simra/simra/config"
	"github.com/pankona/gomo-simra/simra/simlog"
	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/goregular"
	"golang.org/x/image/math/fixed"
	"golang.org/x/mobile/app"
	"golang.org/x/mobile/asset"
	"golang.org/x/mobile/exp/app/debug"
	"golang.org/x/mobile/exp/f32"
	"golang.org/x/mobile/exp/gl/glutil"
	"golang.org/x/mobile/exp/sprite"
	"golang.org/x/mobile/exp/sprite/clock"
	"golang.org/x/mobile/exp/sprite/glsprite"
	"golang.org/x/mobile/gl"
)

// GLer interface represents interface of GL
type GLer interface {
	// Initialize initializes GLPeer.
	// This function must be called in advance of using GLPeer
	Initialize(glc *GLContext)
	// LoadTexture return texture that is loaded by the information of arguments.
	// Loaded texture can assign using AddSprite function.
	LoadTexture(assetName string, rect image.Rectangle) sprite.SubTex
	// MakeTextureByText create and return texture by specified text
	// Loaded texture can assign using AddSprite function.
	// TODO: font parameterize
	MakeTextureByText(text string, fontsize float64, fontcolor color.RGBA, rect image.Rectangle) sprite.SubTex
	// Finalize finalizes GLPeer.
	// This is called at termination of application.
	Finalize()
	// Update updates screen.
	// This is called 60 times per 1 sec.
	Update(sc SpriteContainerer)
	// Reset resets current gl context.
	// All sprites are also cleaned.
	// This is called at changing of scene, and
	// this function is for clean previous scene.
	Reset()
	// NewTexture returns a new Texture instance
	NewTexture(s sprite.SubTex) *Texture
	// ReleaseTexture releases specified texture
	ReleaseTexture(t *Texture)
	// NewNode returns new node
	NewNode(fn arrangerFunc) *ZNode
	// AppendNode adds specified node as a child
	AppendNode(n *ZNode)
	// RemoveNode removes specified node
	RemoveNode(n *ZNode)
	// SetSubTex registers subtexture to specified node
	SetSubTex(n *ZNode, subTex *sprite.SubTex)
	// ZIndexDirty updates dirty flag with specified argument
	ZIndexDirty()
}

// GLPeer represents gl context.
// Singleton.
type GLPeer struct {
	glc         *GLContext
	startTime   time.Time
	images      *glutil.Images
	fps         *debug.FPS
	eng         sprite.Engine
	znodes      ZNodes
	mu          sync.Mutex
	zindexDirty bool
}

// ZNode represents node with zindex
type ZNode struct {
	Node   *sprite.Node
	ZIndex int
}

// ZNodes represents array of ZNode
type ZNodes []*ZNode

func (zns ZNodes) Len() int {
	return len(zns)
}

func (zns ZNodes) Less(i, j int) bool {
	// bigger zindex goes far side
	return zns[i].ZIndex > zns[j].ZIndex
}

func (zns ZNodes) Swap(i, j int) {
	zns[i], zns[j] = zns[j], zns[i]
}

// NewGLPeer returns a instance of GLPeer
func NewGLPeer() GLer {
	return &GLPeer{}
}

// GLContext is a wrapper of gl.Context
type GLContext struct {
	glcontext gl.Context
	publish   func() app.PublishResult
}

// Initialize initializes GLPeer.
// This function must be called in advance of using GLPeer
func (glpeer *GLPeer) Initialize(glc *GLContext) {
	simlog.FuncIn()

	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.glc = glc
	glpeer.startTime = time.Now()

	glctx := glc.glcontext

	// transparency of png
	glctx.Enable(gl.BLEND)
	glctx.BlendEquation(gl.FUNC_ADD)
	glctx.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
	glpeer.images = glutil.NewImages(glctx)
	glpeer.fps = debug.NewFPS(glpeer.images)
	glpeer.initEng()

	simlog.FuncOut()
}

func (glpeer *GLPeer) initEng() {
	if glpeer.eng != nil {
		glpeer.eng.Release()
	}
	glpeer.eng = glsprite.Engine(glpeer.images)
	glpeer.znodes = make([]*ZNode, 0)
}

type arrangerFunc func(e sprite.Engine, n *sprite.Node, t clock.Time)

func (a arrangerFunc) Arrange(e sprite.Engine, n *sprite.Node, t clock.Time) { a(e, n, t) }

// NewNode returns new node
func (glpeer *GLPeer) NewNode(fn arrangerFunc) *ZNode {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	n := &sprite.Node{Arranger: fn}
	glpeer.eng.Register(n)
	return &ZNode{Node: n}
}

// AppendNode adds specified node as a child
func (glpeer *GLPeer) AppendNode(zn *ZNode) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.znodes = append(glpeer.znodes, zn)
}

// RemoveNode removes specified node
func (glpeer *GLPeer) RemoveNode(n *ZNode) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	znodes := make([]*ZNode, len(glpeer.znodes)-1)
	for i, zn := range glpeer.znodes {
		if n != zn {
			znodes[i] = n
		}
	}
	glpeer.znodes = znodes
}

// LoadTexture return texture that is loaded by the information of arguments.
// Loaded texture can assign using AddSprite function.
func (glpeer *GLPeer) LoadTexture(assetName string, rect image.Rectangle) sprite.SubTex {
	simlog.FuncIn()

	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	a, err := asset.Open(assetName)
	if err != nil {
		simlog.Error(err)
	}
	defer func() {
		closeErr := a.Close()
		if closeErr != nil {
			simlog.Error(closeErr)
		}
	}()

	img, _, err := image.Decode(a)
	if err != nil {
		simlog.Error(err)
	}
	t, err := glpeer.eng.LoadTexture(img)
	if err != nil {
		simlog.Error(err)
	}

	simlog.FuncOut()
	return sprite.SubTex{T: t, R: rect}
}

// MakeTextureByText create and return texture by specified text
// Loaded texture can assign using AddSprite function.
// TODO: font parameterize
func (glpeer *GLPeer) MakeTextureByText(text string, fontsize float64, fontcolor color.RGBA, rect image.Rectangle) sprite.SubTex {
	simlog.FuncIn()

	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	dpi := float64(72)
	width := rect.Dx()
	height := rect.Dy()
	img := image.NewRGBA(image.Rect(0, 0, width, height))

	fg, bg := image.NewUniform(fontcolor), image.Transparent
	draw.Draw(img, img.Bounds(), bg, image.Point{}, draw.Src)

	// Draw the text.
	h := font.HintingNone

	gofont, _ := truetype.Parse(goregular.TTF)

	d := &font.Drawer{
		Dst: img,
		Src: fg,
		Face: truetype.NewFace(gofont, &truetype.Options{
			Size:    fontsize,
			DPI:     dpi,
			Hinting: h,
		}),
	}

	textWidth := d.MeasureString(text)

	d.Dot = fixed.Point26_6{
		X: fixed.I(width/2) - textWidth/2,
		Y: fixed.I(int(fontsize * dpi / 72)),
	}
	d.DrawString(text)

	t, err := glpeer.eng.LoadTexture(img)
	if err != nil {
		log.Fatal(err)
	}

	simlog.FuncOut()
	return sprite.SubTex{T: t, R: rect}
}

// Finalize finalizes GLPeer.
// This is called at termination of application.
func (glpeer *GLPeer) Finalize() {
	simlog.FuncIn()

	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	glpeer.eng.Release()
	glpeer.fps.Release()
	glpeer.images.Release()
	glpeer.glc.glcontext = nil

	simlog.FuncOut()
}

// Update updates screen.
// This is called 60 times per 1 sec.
func (glpeer *GLPeer) Update(sc SpriteContainerer) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()

	glctx := glpeer.glc.glcontext
	if glctx == nil {
		return
	}
	glctx.ClearColor(0, 0, 0, 1) // black background
	glctx.Clear(gl.COLOR_BUFFER_BIT)
	now := clock.Time(time.Since(glpeer.startTime) * 60 / time.Second)

	glpeer.apply(sc)

	if glpeer.zindexDirty {
		sort.Sort(glpeer.znodes)
		glpeer.zindexDirty = false
		simlog.Debug("nodes sorted by zindex!")
	}
	for _, zn := range glpeer.znodes {
		glpeer.eng.Render(zn.Node, now, screensize.sz)
	}
	if config.DEBUG {
		glpeer.fps.Draw(screensize.sz)
	}

	// app.Publish() calls glctx.Flush,
	// it must be called within this mutex locking.
	glpeer.glc.publish()
}

// ZIndexDirty enables dirty flag. It indicates sorting of znodes is necessary
// because at least one of their zindex has been updated.
func (glpeer *GLPeer) ZIndexDirty() {
	glpeer.zindexDirty = true
}

// Reset resets current gl context.
// All sprites are also cleaned.
// This is called at changing of scene, and
// this function is for clean previous scene.
func (glpeer *GLPeer) Reset() {
	simlog.FuncIn()

	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	glpeer.initEng()

	simlog.FuncOut()
}

// SetSubTex registers subtexture to specified node
func (glpeer *GLPeer) SetSubTex(zn *ZNode, subTex *sprite.SubTex) {
	glpeer.eng.SetSubTex(zn.Node, *subTex)
}

func (glpeer *GLPeer) apply(sc SpriteContainerer) {
	snpairs := sc.GetSpriteNodePairs()
	snpairs.Range(func(k, v interface{}) bool {
		sn := v.(*spriteNodePair)
		if sn.sprite == nil || !sn.inuse {
			return true
		}
		s := sn.sprite

		affine := &f32.Affine{
			{1, 0, 0},
			{0, 1, 0},
		}
		affine.Translate(affine,
			s.X*screensize.scale-s.W/2*screensize.scale+screensize.marginWidth/2,
			(screensize.height-s.Y)*screensize.scale-s.H/2*screensize.scale+screensize.marginHeight/2)
		if s.R != 0 {
			affine.Translate(affine,
				0.5*s.W*screensize.scale,
				0.5*s.H*screensize.scale)
			affine.Rotate(affine, s.R)
			affine.Translate(affine,
				-0.5*s.W*screensize.scale,
				-0.5*s.H*screensize.scale)
		}
		affine.Scale(affine,
			s.W*screensize.scale,
			s.H*screensize.scale)
		glpeer.eng.SetTransform(sn.znode.Node, *affine)
		return true
	})
}

// Texture represents a texture object that contains subTex
type Texture struct {
	subTex sprite.SubTex
}

// NewTexture returns a new Texture instance
func (glpeer *GLPeer) NewTexture(s sprite.SubTex) *Texture {
	return &Texture{
		subTex: s,
	}
}

// ReleaseTexture releases specified texture
func (glpeer *GLPeer) ReleaseTexture(t *Texture) {
	glpeer.mu.Lock()
	defer glpeer.mu.Unlock()
	t.subTex.T.Release()
}
<|endoftext|>


<commit_before>// Copyright 2018 ETH Zurich
//
// 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"
	"crypto/sha256"
	"flag"
	"fmt"
	"hash"
	"net"
	"os"
	"reflect"
	"strings"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"github.com/syndtr/gocapability/capability"
	"golang.org/x/crypto/pbkdf2"

	"github.com/scionproto/scion/go/border/braccept/tpkt"
	"github.com/scionproto/scion/go/lib/common"
	"github.com/scionproto/scion/go/lib/keyconf"
	"github.com/scionproto/scion/go/lib/scrypto"
)

type ifInfo struct {
	hostDev string
	contDev string
	mac     net.HardwareAddr
	handle  *pcap.Handle
}

const (
	snapshot_len int32         = 1024
	promiscuous  bool          = true
	timeout      time.Duration = 500 * time.Millisecond
)

var (
	borderID        string
	devInfoFilePath string
	keysDirPath     string
	testIdx         int
)

func init() {
	flag.StringVar(&borderID, "borderID", "", "Border Router ID")
	flag.StringVar(&devInfoFilePath, "devInfoFilePath", "", "Device information file path")
	flag.StringVar(&keysDirPath, "keysDirPath", "", "AS keys directory path")
	flag.IntVar(&testIdx, "testIndex", -1, "Run specific test")
}

var (
	devByName map[string]*ifInfo
	devList   []*ifInfo
	hashMac   hash.Hash
)

func main() {
	os.Exit(realMain())
}

func realMain() int {
	if err := checkFlags(); err != nil {
		flag.Usage()
		fmt.Fprintf(os.Stderr, "%s\n", err)
		return 1
	}
	if err := parseDevInfo(devInfoFilePath); err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		return 1
	}
	if err := generateKeys(keysDirPath); err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		return 1
	}
	timerIdx := len(devList)
	cases := make([]reflect.SelectCase, timerIdx+1)
	for i, ifi := range devList {
		var err error
		ifi.handle, err = pcap.OpenLive(ifi.hostDev, snapshot_len, promiscuous, pcap.BlockForever)
		if err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err)
			return 1
		}
		packetSource := gopacket.NewPacketSource(ifi.handle, ifi.handle.LinkType())
		ch := packetSource.Packets()
		cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)}
		defer ifi.handle.Close()
	}
	// Now that everything is set up, drop CAP_NET_ADMIN
	caps, err := capability.NewPid(0)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error retrieving capabilities: %s\n", err)
		return 1
	}
	caps.Clear(capability.CAPS)
	caps.Apply(capability.CAPS)

	registerScionPorts()

	var brTests []*BRTest
	switch borderID {
	case "core-brA":
		brTests = genTestsCoreBrA(hashMac)
	case "core-brB":
		brTests = genTestsCoreBrB(hashMac)
	case "core-brC":
		brTests = genTestsCoreBrC(hashMac)
	case "brA":
		brTests = genTestsBrA(hashMac)
	case "brB":
		brTests = genTestsBrB(hashMac)
	case "brC":
		brTests = genTestsBrC(hashMac)
	case "brD":
		brTests = genTestsBrD(hashMac)
	default:
		fmt.Fprintf(os.Stderr, "Wrong Border Router ID %s\n", borderID)
		return 1
	}
	fmt.Printf("Acceptance tests for %s:\n", borderID)
	var failures int
	baseIdx := 1
	if testIdx != -1 {
		brTests = brTests[testIdx-1 : testIdx]
		baseIdx = testIdx
	}
	for i := range brTests {
		t := brTests[i]
		if err := doTest(t, cases); err != nil {
			fmt.Printf("%d. %s\n%s\n\n", baseIdx+i, t.Summary(false), err)
			failures += 1
		} else {
			fmt.Printf("%d. %s\n", baseIdx+i, t.Summary(true))
		}
	}
	return failures
}

func checkFlags() error {
	flag.Parse()
	if borderID == "" {
		return fmt.Errorf("ERROR: Missing borderID flag")
	}
	if keysDirPath == "" {
		return fmt.Errorf("ERROR: Missing keysDirPath flag")
	}
	if devInfoFilePath == "" {
		return fmt.Errorf("ERROR: Missing devInfoFilePath flag")
	}
	return nil
}

func parseDevInfo(fn string) error {
	f, err := os.Open(fn)
	if err != nil {
		return err
	}
	defer f.Close()
	scanner := bufio.NewScanner(f)
	devByName = make(map[string]*ifInfo)
	for scanner.Scan() {
		field := strings.Split(scanner.Text(), " ")
		elem := &ifInfo{hostDev: field[0], contDev: field[1]}
		elem.mac, err = net.ParseMAC(field[2])
		if err != nil {
			return err
		}
		devList = append(devList, elem)
		devByName[elem.contDev] = elem
	}
	return nil
}

func generateKeys(fn string) error {
	// Load master keys
	masterKeys, err := keyconf.LoadMaster(fn)
	if err != nil {
		return err
	}
	// This uses 16B keys with 1000 hash iterations, which is the same as the
	// defaults used by pycrypto.
	hfGenKey := pbkdf2.Key(masterKeys.Key0, common.RawBytes("Derive OF Key"), 1000, 16, sha256.New)
	// First check for MAC creation errors.
	hashMac, err = scrypto.InitMac(hfGenKey)
	return err
}

// registerScionPorts basically register the following UDP ports in gopacket such as SCION is the
// next layer. In other words, map the following ports to expect SCION as the payload.
func registerScionPorts() {
	// Bind ports to SCION layer
	layers.RegisterUDPPortLayerType(layers.UDPPort(30041), tpkt.LayerTypeScion)
	for i := 30000; i < 30010; i += 1 {
		layers.RegisterUDPPortLayerType(layers.UDPPort(i), tpkt.LayerTypeScion)
	}
	for i := 50000; i < 50010; i += 1 {
		layers.RegisterUDPPortLayerType(layers.UDPPort(i), tpkt.LayerTypeScion)
	}
}

// doTest runs a test, which involves generating the packet, sending it in the specified
// interface, then comparing any packets coming from the border router against the expected
// packets from the test.
// It returns true if the test was successful, ie. all expected packets and no others were received,
// otherwise it returns false.
func doTest(t *BRTest, cases []reflect.SelectCase) error {
	devInfo, ok := devByName[t.In.Dev]
	if !ok {
		return fmt.Errorf("No device information for: %s\n", t.In.Dev)
	}
	raw, err := t.In.Pack(devInfo.mac)
	if err != nil {
		return err
	}
	err = devInfo.handle.WritePacketData(raw)
	if err != nil {
		return err
	}
	return checkRecvPkts(t, cases)
}

// checkRecvPkts compares packets received in any interface against the expected packets
// from the test, checking that they have been received on the expected interface.
// The logic here is to always wait for the timeout to finish receiving packets, thus we
// can check that only the expected packets were received.
func checkRecvPkts(t *BRTest, cases []reflect.SelectCase) error {
	timerIdx := len(devList)
	timerCh := time.After(timeout)
	// Add timeout channel as the last select case.
	cases[timerIdx] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(timerCh)}
	expPkts := append([]*tpkt.ExpPkt(nil), t.Out...)
	var errStr []string
	for {
		idx, pktV, ok := reflect.Select(cases)
		if !ok {
			cases[idx].Chan = reflect.ValueOf(nil)
			errStr = append(errStr, fmt.Sprintf("Unexpected interface %s/%s closed",
				devList[idx].hostDev, devList[idx].contDev))
			break
		}
		if idx == timerIdx {
			// Timeout receiving packets
			if len(expPkts) > 0 {
				errStr = append(errStr, fmt.Sprintf("Timeout receiving packets"))
			}
			break
		}
		// Packet received
		pkt := pktV.Interface().(gopacket.Packet)
		if _, e := checkPkt(t.Ignore, idx, pkt); e == nil {
			// Packet is to be ignored
			continue
		}
		i, e := checkPkt(expPkts, idx, pkt)
		if e != nil {
			errStr = append(errStr, fmt.Sprintf("%s", e))
			continue
		}
		// Remove matched packet from expected packets
		expPkts = append(expPkts[:i], expPkts[i+1:]...)
	}
	if len(errStr) > 0 {
		return fmt.Errorf(strings.Join(errStr, "\n"))
	}
	return nil
}

// checkPkt compare a given packet against all the possible expected packets,
// It returns the index of the expected packet matched or an error with a pretty-print
// packet dump of the unmatched packet.
func checkPkt(expPkts []*tpkt.ExpPkt, devIdx int, pkt gopacket.Packet) (int, error) {
	var errStr []string
	for i := range expPkts {
		if devList[devIdx].contDev != expPkts[i].Dev {
			continue
		}
		if err := expPkts[i].Match(pkt); err != nil {
			errStr = append(errStr, fmt.Sprintf("[ERROR] %s\n", err))
			continue
		}
		// Expected packet matched!
		return i, nil
	}
	if len(expPkts) == 0 {
		errStr = append(errStr,
			fmt.Sprintf("[ERROR] Packet received when no packet was expected\n"))
	}
	if scnLayer := pkt.Layer(tpkt.LayerTypeScion); scnLayer != nil {
		// Set Raw to nil so it is not displayed on pretty printed packet format.
		// At this point, the received packet is not used anymore
		scn := scnLayer.(*tpkt.ScionLayer)
		scn.Path.Raw = nil
	}
	errStr = append(errStr, fmt.Sprintf("Unexpected packet on interface %s:\n\n%v",
		devList[devIdx].contDev, pkt))
	return -1, fmt.Errorf(strings.Join(errStr, "\n"))
}
<commit_msg>braccept: use log to have timestamps on messages (#2315)<commit_after>// Copyright 2018 ETH Zurich
//
// 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"
	"crypto/sha256"
	"flag"
	"fmt"
	"hash"
	"net"
	"os"
	"reflect"
	"strings"
	"time"

	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
	"github.com/google/gopacket/pcap"
	"github.com/syndtr/gocapability/capability"
	"golang.org/x/crypto/pbkdf2"

	"github.com/scionproto/scion/go/border/braccept/tpkt"
	"github.com/scionproto/scion/go/lib/common"
	"github.com/scionproto/scion/go/lib/keyconf"
	"github.com/scionproto/scion/go/lib/log"
	"github.com/scionproto/scion/go/lib/scrypto"
)

type ifInfo struct {
	hostDev string
	contDev string
	mac     net.HardwareAddr
	handle  *pcap.Handle
}

const (
	snapshot_len int32         = 1024
	promiscuous  bool          = true
	timeout      time.Duration = 500 * time.Millisecond
)

var (
	borderID        string
	devInfoFilePath string
	keysDirPath     string
	testIdx         int
)

func init() {
	flag.StringVar(&borderID, "borderID", "", "Border Router ID")
	flag.StringVar(&devInfoFilePath, "devInfoFilePath", "", "Device information file path")
	flag.StringVar(&keysDirPath, "keysDirPath", "", "AS keys directory path")
	flag.IntVar(&testIdx, "testIndex", -1, "Run specific test")
}

var (
	devByName map[string]*ifInfo
	devList   []*ifInfo
	hashMac   hash.Hash
)

func main() {
	os.Exit(realMain())
}

func realMain() int {
	log.ConsoleLevel = "info"
	log.AddLogConsFlags()
	if err := checkFlags(); err != nil {
		flag.Usage()
		fmt.Fprintf(os.Stderr, "%s\n", err)
		return 1
	}
	if err := log.SetupFromFlags(""); err != nil {
		flag.Usage()
		fmt.Fprintf(os.Stderr, "%s\n", err)
		return 1
	}
	defer log.LogPanicAndExit()
	if err := parseDevInfo(devInfoFilePath); err != nil {
		log.Crit("", "err", err)
		return 1
	}
	if err := generateKeys(keysDirPath); err != nil {
		log.Crit("", "err", err)
		return 1
	}
	timerIdx := len(devList)
	cases := make([]reflect.SelectCase, timerIdx+1)
	for i, ifi := range devList {
		var err error
		ifi.handle, err = pcap.OpenLive(ifi.hostDev, snapshot_len, promiscuous, pcap.BlockForever)
		if err != nil {
			log.Crit("", "err", err)
			return 1
		}
		packetSource := gopacket.NewPacketSource(ifi.handle, ifi.handle.LinkType())
		ch := packetSource.Packets()
		cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(ch)}
		defer ifi.handle.Close()
	}
	// Now that everything is set up, drop CAP_NET_ADMIN
	caps, err := capability.NewPid(0)
	if err != nil {
		log.Crit("Error retrieving capabilities", "err", err)
		return 1
	}
	caps.Clear(capability.CAPS)
	caps.Apply(capability.CAPS)

	registerScionPorts()

	var brTests []*BRTest
	switch borderID {
	case "core-brA":
		brTests = genTestsCoreBrA(hashMac)
	case "core-brB":
		brTests = genTestsCoreBrB(hashMac)
	case "core-brC":
		brTests = genTestsCoreBrC(hashMac)
	case "brA":
		brTests = genTestsBrA(hashMac)
	case "brB":
		brTests = genTestsBrB(hashMac)
	case "brC":
		brTests = genTestsBrC(hashMac)
	case "brD":
		brTests = genTestsBrD(hashMac)
	default:
		log.Crit("Wrong Border Router ID", "brID", borderID)
		return 1
	}
	log.Info("Acceptance tests:", "brID", borderID)
	var failures int
	baseIdx := 1
	if testIdx != -1 {
		brTests = brTests[testIdx-1 : testIdx]
		baseIdx = testIdx
	}
	for i := range brTests {
		t := brTests[i]
		if err := doTest(t, cases); err != nil {
			log.Error(fmt.Sprintf("%d. %s\n%s\n\n", baseIdx+i, t.Summary(false), err))
			failures += 1
		} else {
			log.Info(fmt.Sprintf("%d. %s\n", baseIdx+i, t.Summary(true)))
		}
	}
	return failures
}

func checkFlags() error {
	flag.Parse()
	if borderID == "" {
		return fmt.Errorf("ERROR: Missing borderID flag")
	}
	if keysDirPath == "" {
		return fmt.Errorf("ERROR: Missing keysDirPath flag")
	}
	if devInfoFilePath == "" {
		return fmt.Errorf("ERROR: Missing devInfoFilePath flag")
	}
	return nil
}

func parseDevInfo(fn string) error {
	f, err := os.Open(fn)
	if err != nil {
		return err
	}
	defer f.Close()
	scanner := bufio.NewScanner(f)
	devByName = make(map[string]*ifInfo)
	for scanner.Scan() {
		field := strings.Split(scanner.Text(), " ")
		elem := &ifInfo{hostDev: field[0], contDev: field[1]}
		elem.mac, err = net.ParseMAC(field[2])
		if err != nil {
			return err
		}
		devList = append(devList, elem)
		devByName[elem.contDev] = elem
	}
	return nil
}

func generateKeys(fn string) error {
	// Load master keys
	masterKeys, err := keyconf.LoadMaster(fn)
	if err != nil {
		return err
	}
	// This uses 16B keys with 1000 hash iterations, which is the same as the
	// defaults used by pycrypto.
	hfGenKey := pbkdf2.Key(masterKeys.Key0, common.RawBytes("Derive OF Key"), 1000, 16, sha256.New)
	// First check for MAC creation errors.
	hashMac, err = scrypto.InitMac(hfGenKey)
	return err
}

// registerScionPorts basically register the following UDP ports in gopacket such as SCION is the
// next layer. In other words, map the following ports to expect SCION as the payload.
func registerScionPorts() {
	// Bind ports to SCION layer
	layers.RegisterUDPPortLayerType(layers.UDPPort(30041), tpkt.LayerTypeScion)
	for i := 30000; i < 30010; i += 1 {
		layers.RegisterUDPPortLayerType(layers.UDPPort(i), tpkt.LayerTypeScion)
	}
	for i := 50000; i < 50010; i += 1 {
		layers.RegisterUDPPortLayerType(layers.UDPPort(i), tpkt.LayerTypeScion)
	}
}

// doTest runs a test, which involves generating the packet, sending it in the specified
// interface, then comparing any packets coming from the border router against the expected
// packets from the test.
// It returns true if the test was successful, ie. all expected packets and no others were received,
// otherwise it returns false.
func doTest(t *BRTest, cases []reflect.SelectCase) error {
	devInfo, ok := devByName[t.In.Dev]
	if !ok {
		return fmt.Errorf("No device information for: %s\n", t.In.Dev)
	}
	raw, err := t.In.Pack(devInfo.mac)
	if err != nil {
		return err
	}
	err = devInfo.handle.WritePacketData(raw)
	if err != nil {
		return err
	}
	return checkRecvPkts(t, cases)
}

// checkRecvPkts compares packets received in any interface against the expected packets
// from the test, checking that they have been received on the expected interface.
// The logic here is to always wait for the timeout to finish receiving packets, thus we
// can check that only the expected packets were received.
func checkRecvPkts(t *BRTest, cases []reflect.SelectCase) error {
	timerIdx := len(devList)
	timerCh := time.After(timeout)
	// Add timeout channel as the last select case.
	cases[timerIdx] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(timerCh)}
	expPkts := append([]*tpkt.ExpPkt(nil), t.Out...)
	var errStr []string
	for {
		idx, pktV, ok := reflect.Select(cases)
		if !ok {
			cases[idx].Chan = reflect.ValueOf(nil)
			errStr = append(errStr, fmt.Sprintf("Unexpected interface %s/%s closed",
				devList[idx].hostDev, devList[idx].contDev))
			break
		}
		if idx == timerIdx {
			// Timeout receiving packets
			if len(expPkts) > 0 {
				errStr = append(errStr, fmt.Sprintf("Timeout receiving packets"))
			}
			break
		}
		// Packet received
		pkt := pktV.Interface().(gopacket.Packet)
		if _, e := checkPkt(t.Ignore, idx, pkt); e == nil {
			// Packet is to be ignored
			continue
		}
		i, e := checkPkt(expPkts, idx, pkt)
		if e != nil {
			errStr = append(errStr, fmt.Sprintf("%s", e))
			continue
		}
		// Remove matched packet from expected packets
		expPkts = append(expPkts[:i], expPkts[i+1:]...)
	}
	if len(errStr) > 0 {
		return fmt.Errorf(strings.Join(errStr, "\n"))
	}
	return nil
}

// checkPkt compare a given packet against all the possible expected packets,
// It returns the index of the expected packet matched or an error with a pretty-print
// packet dump of the unmatched packet.
func checkPkt(expPkts []*tpkt.ExpPkt, devIdx int, pkt gopacket.Packet) (int, error) {
	var errStr []string
	for i := range expPkts {
		if devList[devIdx].contDev != expPkts[i].Dev {
			continue
		}
		if err := expPkts[i].Match(pkt); err != nil {
			errStr = append(errStr, fmt.Sprintf("[ERROR] %s\n", err))
			continue
		}
		// Expected packet matched!
		return i, nil
	}
	if len(expPkts) == 0 {
		errStr = append(errStr,
			fmt.Sprintf("[ERROR] Packet received when no packet was expected\n"))
	}
	if scnLayer := pkt.Layer(tpkt.LayerTypeScion); scnLayer != nil {
		// Set Raw to nil so it is not displayed on pretty printed packet format.
		// At this point, the received packet is not used anymore
		scn := scnLayer.(*tpkt.ScionLayer)
		scn.Path.Raw = nil
	}
	errStr = append(errStr, fmt.Sprintf("Unexpected packet on interface %s:\n\n%v",
		devList[devIdx].contDev, pkt))
	return -1, fmt.Errorf(strings.Join(errStr, "\n"))
}
<|endoftext|>