<commit_before>package enmime

import (
	"bytes"
	"errors"
	"io/ioutil"
	"mime"
	"net/mail"
	"net/smtp"
	"net/textproto"
	"os"
	"path/filepath"
	"reflect"
	"time"

	"github.com/jhillyerd/enmime/internal/stringutil"
)

// MailBuilder facilitates the easy construction of a MIME message.  Each manipulation method
// returns a copy of the receiver struct.  It can be considered immutable if the caller does not
// modify the string and byte slices passed in.  Immutability allows the headers or entire message
// to be reused across multiple threads.
type MailBuilder struct {
	to, cc, bcc          []mail.Address
	from                 mail.Address
	replyTo              mail.Address
	subject              string
	date                 time.Time
	header               textproto.MIMEHeader
	text, html           []byte
	inlines, attachments []*Part
	err                  error
}

// Builder returns an empty MailBuilder struct.
func Builder() MailBuilder {
	return MailBuilder{}
}

// Error returns the stored error from a file attachment/inline read or nil.
func (p MailBuilder) Error() error {
	return p.err
}

// Date returns a copy of MailBuilder with the specified Date header.
func (p MailBuilder) Date(date time.Time) MailBuilder {
	p.date = date
	return p
}

// From returns a copy of MailBuilder with the specified From header.
func (p MailBuilder) From(name, addr string) MailBuilder {
	p.from = mail.Address{Name: name, Address: addr}
	return p
}

// Subject returns a copy of MailBuilder with the specified Subject header.
func (p MailBuilder) Subject(subject string) MailBuilder {
	p.subject = subject
	return p
}

// To returns a copy of MailBuilder with this name & address appended to the To header.  name may be
// empty.
func (p MailBuilder) To(name, addr string) MailBuilder {
	p.to = append(p.to, mail.Address{Name: name, Address: addr})
	return p
}

// ToAddrs returns a copy of MailBuilder with the specified To addresses.
func (p MailBuilder) ToAddrs(to []mail.Address) MailBuilder {
	p.to = to
	return p
}

// CC returns a copy of MailBuilder with this name & address appended to the CC header.  name may be
// empty.
func (p MailBuilder) CC(name, addr string) MailBuilder {
	p.cc = append(p.cc, mail.Address{Name: name, Address: addr})
	return p
}

// CCAddrs returns a copy of MailBuilder with the specified CC addresses.
func (p MailBuilder) CCAddrs(cc []mail.Address) MailBuilder {
	p.cc = cc
	return p
}

// BCC returns a copy of MailBuilder with this name & address appended to the BCC list.  name may be
// empty.  This method only has an effect if the Send method is used to transmit the message, there
// is no effect on the parts returned by Build().
func (p MailBuilder) BCC(name, addr string) MailBuilder {
	p.bcc = append(p.bcc, mail.Address{Name: name, Address: addr})
	return p
}

// BCCAddrs returns a copy of MailBuilder with the specified as the blind CC list.  This method only
// has an effect if the Send method is used to transmit the message, there is no effect on the parts
// returned by Build().
func (p MailBuilder) BCCAddrs(bcc []mail.Address) MailBuilder {
	p.bcc = bcc
	return p
}

// ReplyTo returns a copy of MailBuilder with this name & address appended to the To header.  name
// may be empty.
func (p MailBuilder) ReplyTo(name, addr string) MailBuilder {
	p.replyTo = mail.Address{Name: name, Address: addr}
	return p
}

// Header returns a copy of MailBuilder with the specified value added to the named header.
func (p MailBuilder) Header(name, value string) MailBuilder {
	// Copy existing header map
	h := textproto.MIMEHeader{}
	for k, v := range p.header {
		h[k] = v
	}
	h.Add(name, value)
	p.header = h
	return p
}

// Text returns a copy of MailBuilder that will use the provided bytes for its text/plain Part.
func (p MailBuilder) Text(body []byte) MailBuilder {
	p.text = body
	return p
}

// HTML returns a copy of MailBuilder that will use the provided bytes for its text/html Part.
func (p MailBuilder) HTML(body []byte) MailBuilder {
	p.html = body
	return p
}

// AddAttachment returns a copy of MailBuilder that includes the specified attachment.
func (p MailBuilder) AddAttachment(b []byte, contentType string, fileName string) MailBuilder {
	part := NewPart(contentType)
	part.Content = b
	part.FileName = fileName
	part.Disposition = cdAttachment
	p.attachments = append(p.attachments, part)
	return p
}

// AddFileAttachment returns a copy of MailBuilder that includes the specified attachment.
// fileName, will be populated from the base name of path.  Content type will be detected from the
// path extension.
func (p MailBuilder) AddFileAttachment(path string) MailBuilder {
	// Only allow first p.err value
	if p.err != nil {
		return p
	}
	f, err := os.Open(path)
	if err != nil {
		p.err = err
		return p
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		p.err = err
		return p
	}
	name := filepath.Base(path)
	ctype := mime.TypeByExtension(filepath.Ext(name))
	return p.AddAttachment(b, ctype, name)
}

// AddInline returns a copy of MailBuilder that includes the specified inline.  fileName and
// contentID may be left empty.
func (p MailBuilder) AddInline(
	b []byte,
	contentType string,
	fileName string,
	contentID string,
) MailBuilder {
	part := NewPart(contentType)
	part.Content = b
	part.FileName = fileName
	part.Disposition = cdInline
	part.ContentID = contentID
	p.inlines = append(p.inlines, part)
	return p
}

// AddFileInline returns a copy of MailBuilder that includes the specified inline.  fileName and
// contentID will be populated from the base name of path.  Content type will be detected from the
// path extension.
func (p MailBuilder) AddFileInline(path string) MailBuilder {
	// Only allow first p.err value
	if p.err != nil {
		return p
	}
	f, err := os.Open(path)
	if err != nil {
		p.err = err
		return p
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		p.err = err
		return p
	}
	name := filepath.Base(path)
	ctype := mime.TypeByExtension(filepath.Ext(name))
	return p.AddInline(b, ctype, name, name)
}

// Build performs some basic validations, then constructs a tree of Part structs from the configured
// MailBuilder.  It will set the Date header to now if it was not explicitly set.
func (p MailBuilder) Build() (*Part, error) {
	if p.err != nil {
		return nil, p.err
	}
	// Validations
	if p.from.Address == "" {
		return nil, errors.New("from not set")
	}
	if p.subject == "" {
		return nil, errors.New("subject not set")
	}
	if len(p.to)+len(p.cc)+len(p.bcc) == 0 {
		return nil, errors.New("no recipients (to, cc, bcc) set")
	}
	// Fully loaded structure; the presence of text, html, inlines, and attachments will determine
	// how much is necessary:
	//
	//  multipart/mixed
	//  |- multipart/related
	//  |  |- multipart/alternative
	//  |  |  |- text/plain
	//  |  |  `- text/html
	//  |  `- inlines..
	//  `- attachments..
	//
	// We build this tree starting at the leaves, re-rooting as needed.
	var root, part *Part
	if p.text != nil || p.html == nil {
		root = NewPart(ctTextPlain)
		root.Content = p.text
		root.Charset = utf8
	}
	if p.html != nil {
		part = NewPart(ctTextHTML)
		part.Content = p.html
		part.Charset = utf8
		if root == nil {
			root = part
		} else {
			root.NextSibling = part
		}
	}
	if p.text != nil && p.html != nil {
		// Wrap Text & HTML bodies
		part = root
		root = NewPart(ctMultipartAltern)
		root.AddChild(part)
	}
	if len(p.inlines) > 0 {
		part = root
		root = NewPart(ctMultipartRelated)
		root.AddChild(part)
		for _, ip := range p.inlines {
			// Copy inline Part to isolate mutations
			part = &Part{}
			*part = *ip
			part.Header = make(textproto.MIMEHeader)
			root.AddChild(part)
		}
	}
	if len(p.attachments) > 0 {
		part = root
		root = NewPart(ctMultipartMixed)
		root.AddChild(part)
		for _, ap := range p.attachments {
			// Copy attachment Part to isolate mutations
			part = &Part{}
			*part = *ap
			part.Header = make(textproto.MIMEHeader)
			root.AddChild(part)
		}
	}
	// Headers
	h := root.Header
	h.Set(hnMIMEVersion, "1.0")
	h.Set("From", p.from.String())
	h.Set("Subject", p.subject)
	if len(p.to) > 0 {
		h.Set("To", stringutil.JoinAddress(p.to))
	}
	if len(p.cc) > 0 {
		h.Set("Cc", stringutil.JoinAddress(p.cc))
	}
	if p.replyTo.Address != "" {
		h.Set("Reply-To", p.replyTo.String())
	}
	date := p.date
	if date.IsZero() {
		date = time.Now()
	}
	h.Set("Date", date.Format(time.RFC1123Z))
	for k, v := range p.header {
		for _, s := range v {
			h.Add(k, s)
		}
	}
	return root, nil
}

// Send encodes the message and sends it via the SMTP server specified by addr.  Send uses
// net/smtp.SendMail, and accepts the same authentication parameters.
func (p MailBuilder) Send(addr string, a smtp.Auth) error {
	buf := &bytes.Buffer{}
	root, err := p.Build()
	if err != nil {
		return err
	}
	err = root.Encode(buf)
	if err != nil {
		return err
	}
	recips := make([]string, 0, len(p.to)+len(p.cc)+len(p.bcc))
	for _, a := range p.to {
		recips = append(recips, a.Address)
	}
	for _, a := range p.cc {
		recips = append(recips, a.Address)
	}
	for _, a := range p.bcc {
		recips = append(recips, a.Address)
	}
	return smtp.SendMail(addr, a, p.from.Address, recips, buf.Bytes())
}

// Equals uses the reflect package to test two MailBuilder structs for equality, primarily for unit
// tests.
func (p MailBuilder) Equals(o MailBuilder) bool {
	return reflect.DeepEqual(p, o)
}
<commit_msg>add SendWithReturnAddress (#179)<commit_after>package enmime

import (
	"bytes"
	"errors"
	"io/ioutil"
	"mime"
	"net/mail"
	"net/smtp"
	"net/textproto"
	"os"
	"path/filepath"
	"reflect"
	"time"

	"github.com/jhillyerd/enmime/internal/stringutil"
)

// MailBuilder facilitates the easy construction of a MIME message.  Each manipulation method
// returns a copy of the receiver struct.  It can be considered immutable if the caller does not
// modify the string and byte slices passed in.  Immutability allows the headers or entire message
// to be reused across multiple threads.
type MailBuilder struct {
	to, cc, bcc          []mail.Address
	from                 mail.Address
	replyTo              mail.Address
	subject              string
	date                 time.Time
	header               textproto.MIMEHeader
	text, html           []byte
	inlines, attachments []*Part
	err                  error
}

// Builder returns an empty MailBuilder struct.
func Builder() MailBuilder {
	return MailBuilder{}
}

// Error returns the stored error from a file attachment/inline read or nil.
func (p MailBuilder) Error() error {
	return p.err
}

// Date returns a copy of MailBuilder with the specified Date header.
func (p MailBuilder) Date(date time.Time) MailBuilder {
	p.date = date
	return p
}

// From returns a copy of MailBuilder with the specified From header.
func (p MailBuilder) From(name, addr string) MailBuilder {
	p.from = mail.Address{Name: name, Address: addr}
	return p
}

// Subject returns a copy of MailBuilder with the specified Subject header.
func (p MailBuilder) Subject(subject string) MailBuilder {
	p.subject = subject
	return p
}

// To returns a copy of MailBuilder with this name & address appended to the To header.  name may be
// empty.
func (p MailBuilder) To(name, addr string) MailBuilder {
	p.to = append(p.to, mail.Address{Name: name, Address: addr})
	return p
}

// ToAddrs returns a copy of MailBuilder with the specified To addresses.
func (p MailBuilder) ToAddrs(to []mail.Address) MailBuilder {
	p.to = to
	return p
}

// CC returns a copy of MailBuilder with this name & address appended to the CC header.  name may be
// empty.
func (p MailBuilder) CC(name, addr string) MailBuilder {
	p.cc = append(p.cc, mail.Address{Name: name, Address: addr})
	return p
}

// CCAddrs returns a copy of MailBuilder with the specified CC addresses.
func (p MailBuilder) CCAddrs(cc []mail.Address) MailBuilder {
	p.cc = cc
	return p
}

// BCC returns a copy of MailBuilder with this name & address appended to the BCC list.  name may be
// empty.  This method only has an effect if the Send method is used to transmit the message, there
// is no effect on the parts returned by Build().
func (p MailBuilder) BCC(name, addr string) MailBuilder {
	p.bcc = append(p.bcc, mail.Address{Name: name, Address: addr})
	return p
}

// BCCAddrs returns a copy of MailBuilder with the specified as the blind CC list.  This method only
// has an effect if the Send method is used to transmit the message, there is no effect on the parts
// returned by Build().
func (p MailBuilder) BCCAddrs(bcc []mail.Address) MailBuilder {
	p.bcc = bcc
	return p
}

// ReplyTo returns a copy of MailBuilder with this name & address appended to the To header.  name
// may be empty.
func (p MailBuilder) ReplyTo(name, addr string) MailBuilder {
	p.replyTo = mail.Address{Name: name, Address: addr}
	return p
}

// Header returns a copy of MailBuilder with the specified value added to the named header.
func (p MailBuilder) Header(name, value string) MailBuilder {
	// Copy existing header map
	h := textproto.MIMEHeader{}
	for k, v := range p.header {
		h[k] = v
	}
	h.Add(name, value)
	p.header = h
	return p
}

// Text returns a copy of MailBuilder that will use the provided bytes for its text/plain Part.
func (p MailBuilder) Text(body []byte) MailBuilder {
	p.text = body
	return p
}

// HTML returns a copy of MailBuilder that will use the provided bytes for its text/html Part.
func (p MailBuilder) HTML(body []byte) MailBuilder {
	p.html = body
	return p
}

// AddAttachment returns a copy of MailBuilder that includes the specified attachment.
func (p MailBuilder) AddAttachment(b []byte, contentType string, fileName string) MailBuilder {
	part := NewPart(contentType)
	part.Content = b
	part.FileName = fileName
	part.Disposition = cdAttachment
	p.attachments = append(p.attachments, part)
	return p
}

// AddFileAttachment returns a copy of MailBuilder that includes the specified attachment.
// fileName, will be populated from the base name of path.  Content type will be detected from the
// path extension.
func (p MailBuilder) AddFileAttachment(path string) MailBuilder {
	// Only allow first p.err value
	if p.err != nil {
		return p
	}
	f, err := os.Open(path)
	if err != nil {
		p.err = err
		return p
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		p.err = err
		return p
	}
	name := filepath.Base(path)
	ctype := mime.TypeByExtension(filepath.Ext(name))
	return p.AddAttachment(b, ctype, name)
}

// AddInline returns a copy of MailBuilder that includes the specified inline.  fileName and
// contentID may be left empty.
func (p MailBuilder) AddInline(
	b []byte,
	contentType string,
	fileName string,
	contentID string,
) MailBuilder {
	part := NewPart(contentType)
	part.Content = b
	part.FileName = fileName
	part.Disposition = cdInline
	part.ContentID = contentID
	p.inlines = append(p.inlines, part)
	return p
}

// AddFileInline returns a copy of MailBuilder that includes the specified inline.  fileName and
// contentID will be populated from the base name of path.  Content type will be detected from the
// path extension.
func (p MailBuilder) AddFileInline(path string) MailBuilder {
	// Only allow first p.err value
	if p.err != nil {
		return p
	}
	f, err := os.Open(path)
	if err != nil {
		p.err = err
		return p
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		p.err = err
		return p
	}
	name := filepath.Base(path)
	ctype := mime.TypeByExtension(filepath.Ext(name))
	return p.AddInline(b, ctype, name, name)
}

// Build performs some basic validations, then constructs a tree of Part structs from the configured
// MailBuilder.  It will set the Date header to now if it was not explicitly set.
func (p MailBuilder) Build() (*Part, error) {
	if p.err != nil {
		return nil, p.err
	}
	// Validations
	if p.from.Address == "" {
		return nil, errors.New("from not set")
	}
	if p.subject == "" {
		return nil, errors.New("subject not set")
	}
	if len(p.to)+len(p.cc)+len(p.bcc) == 0 {
		return nil, errors.New("no recipients (to, cc, bcc) set")
	}
	// Fully loaded structure; the presence of text, html, inlines, and attachments will determine
	// how much is necessary:
	//
	//  multipart/mixed
	//  |- multipart/related
	//  |  |- multipart/alternative
	//  |  |  |- text/plain
	//  |  |  `- text/html
	//  |  `- inlines..
	//  `- attachments..
	//
	// We build this tree starting at the leaves, re-rooting as needed.
	var root, part *Part
	if p.text != nil || p.html == nil {
		root = NewPart(ctTextPlain)
		root.Content = p.text
		root.Charset = utf8
	}
	if p.html != nil {
		part = NewPart(ctTextHTML)
		part.Content = p.html
		part.Charset = utf8
		if root == nil {
			root = part
		} else {
			root.NextSibling = part
		}
	}
	if p.text != nil && p.html != nil {
		// Wrap Text & HTML bodies
		part = root
		root = NewPart(ctMultipartAltern)
		root.AddChild(part)
	}
	if len(p.inlines) > 0 {
		part = root
		root = NewPart(ctMultipartRelated)
		root.AddChild(part)
		for _, ip := range p.inlines {
			// Copy inline Part to isolate mutations
			part = &Part{}
			*part = *ip
			part.Header = make(textproto.MIMEHeader)
			root.AddChild(part)
		}
	}
	if len(p.attachments) > 0 {
		part = root
		root = NewPart(ctMultipartMixed)
		root.AddChild(part)
		for _, ap := range p.attachments {
			// Copy attachment Part to isolate mutations
			part = &Part{}
			*part = *ap
			part.Header = make(textproto.MIMEHeader)
			root.AddChild(part)
		}
	}
	// Headers
	h := root.Header
	h.Set(hnMIMEVersion, "1.0")
	h.Set("From", p.from.String())
	h.Set("Subject", p.subject)
	if len(p.to) > 0 {
		h.Set("To", stringutil.JoinAddress(p.to))
	}
	if len(p.cc) > 0 {
		h.Set("Cc", stringutil.JoinAddress(p.cc))
	}
	if p.replyTo.Address != "" {
		h.Set("Reply-To", p.replyTo.String())
	}
	date := p.date
	if date.IsZero() {
		date = time.Now()
	}
	h.Set("Date", date.Format(time.RFC1123Z))
	for k, v := range p.header {
		for _, s := range v {
			h.Add(k, s)
		}
	}
	return root, nil
}

// SendWithReturnAddress encodes the message and sends it via the SMTP server specified by addr
// and from. Send uses net/smtp.SendMail, and accepts the same authentication parameters.
func (p MailBuilder) SendWithReturnAddress(addr, from string, a smtp.Auth) error {
	buf := &bytes.Buffer{}
	root, err := p.Build()
	if err != nil {
		return err
	}
	err = root.Encode(buf)
	if err != nil {
		return err
	}
	recips := make([]string, 0, len(p.to)+len(p.cc)+len(p.bcc))
	for _, a := range p.to {
		recips = append(recips, a.Address)
	}
	for _, a := range p.cc {
		recips = append(recips, a.Address)
	}
	for _, a := range p.bcc {
		recips = append(recips, a.Address)
	}
	return smtp.SendMail(addr, a, from, recips, buf.Bytes())
}

// Send encodes the message and sends it via the SMTP server specified by addr. Send uses
// net/smtp.SendMail, and accepts the same authentication parameters.
func (p MailBuilder) Send(addr string, a smtp.Auth) error {
	return p.SendWithReturnAddress(addr, p.from.Address, a)
}

// Equals uses the reflect package to test two MailBuilder structs for equality, primarily for unit
// tests.
func (p MailBuilder) Equals(o MailBuilder) bool {
	return reflect.DeepEqual(p, o)
}
<|endoftext|>


<commit_before>package main

import (
  "github.com/gorilla/websocket"
  "net/http"
  "log"
  fp "path/filepath"
  "gopkg.in/fsnotify.v1"
)

const (
  UPDATE_FILE = ".hotrod-update"
  BASE_DIR = "/app/"
)

var (
  upgrader  = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool { return true },
  }
)

func main() {
  http.HandleFunc("/ws", wsHandler)
  http.ListenAndServe(":8585", nil)
}

func reader(ws *websocket.Conn) {
    for {
        if _, _, err := ws.NextReader(); err != nil {
            ws.Close()
            break
        }
    }
}

func pingOn(filename, message string, ws *websocket.Conn) *fsnotify.Watcher {
  watcher, err := fsnotify.NewWatcher()
  if err != nil {
    log.Fatal(err)
  }
  go func() {
    for {
      select {
        case _ = <-watcher.Events:
          if err := ws.WriteMessage(websocket.TextMessage, []byte(message)); err != nil {
            return
          }
        case err := <-watcher.Errors:
          log.Println("error:", err)
      }
    }
  }()
  watcher.Add(filename)
  return watcher
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
  ws, err := upgrader.Upgrade(w, r, nil)
  if err != nil {
    if _, ok := err.(websocket.HandshakeError); !ok {
      log.Println(err)
    }
    return
  }
  watcher := pingOn(fp.Join(BASE_DIR, UPDATE_FILE), "UPDATE", ws)
  defer watcher.Close()
  reader(ws)
}<commit_msg>add 200 on root<commit_after>package main

import (
  "github.com/gorilla/websocket"
  "net/http"
  "log"
  fp "path/filepath"
  "gopkg.in/fsnotify.v1"
  "fmt"
)

const (
  UPDATE_FILE = ".hotrod-update"
  BASE_DIR = "/app/"
)

var (
  upgrader  = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool { return true },
  }
)

func baseHandler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprintf(w, "Hello, server running.")
}

func main() {
  http.HandleFunc("/", baseHandler)
  http.HandleFunc("/ws", wsHandler)
  http.ListenAndServe(":8585", nil)
}

func reader(ws *websocket.Conn) {
    for {
        if _, _, err := ws.NextReader(); err != nil {
            ws.Close()
            break
        }
    }
}

func pingOn(filename, message string, ws *websocket.Conn) *fsnotify.Watcher {
  watcher, err := fsnotify.NewWatcher()
  if err != nil {
    log.Fatal(err)
  }
  go func() {
    for {
      select {
        case _ = <-watcher.Events:
          if err := ws.WriteMessage(websocket.TextMessage, []byte(message)); err != nil {
            return
          }
        case err := <-watcher.Errors:
          log.Println("error:", err)
      }
    }
  }()
  watcher.Add(filename)
  return watcher
}

func wsHandler(w http.ResponseWriter, r *http.Request) {
  ws, err := upgrader.Upgrade(w, r, nil)
  if err != nil {
    if _, ok := err.(websocket.HandshakeError); !ok {
      log.Println(err)
    }
    return
  }
  watcher := pingOn(fp.Join(BASE_DIR, UPDATE_FILE), "UPDATE", ws)
  defer watcher.Close()
  reader(ws)
}<|endoftext|>


<commit_before>// +build linux,!cgo

package goserial

import (
	"fmt"
	"io"
	"os"
	"syscall"
	"unsafe"
)

func openPort(name string, c *Config) (rwc io.ReadWriteCloser, err error) {

	var rate uint32
	switch c.Baud {
	case 115200:
		rate = syscall.B115200
	case 57600:
		rate = syscall.B57600
	case 38400:
		rate = syscall.B38400
	case 19200:
		rate = syscall.B19200
	case 9600:
		rate = syscall.B9600
	case 4800:
		rate = syscall.B4800
	default:
		return nil, fmt.Errorf("Unknown baud rate %v", c.Baud)
	}

	var stop uint32
	switch c.StopBits {
	case StopBits1:
		stop = 0
	case StopBits2:
		stop = syscall.CSTOPB
	default:
		panic("should not happen if Config.check() was called before")
	}

	var size uint32
	switch c.Size {
	case Byte5:
		size = syscall.CS5
	case Byte6:
		size = syscall.CS6
	case Byte7:
		size = syscall.CS7
	case Byte8:
		size = syscall.CS8
	default:
		panic("should not happen if Config.check() was called before")
	}

	var parity uint32
	switch c.Parity {
	case ParityNone:
		parity = 0
	case ParityEven:
		parity = 2
	case ParityOdd:
		parity = 1
	default:
		panic("should not happen if Config.check() was called before")
	}

	f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0600)
	if err != nil {
		return nil, err
	}

	t := syscall.Termios{
		Iflag:  syscall.IGNPAR,
		Cflag:  stop | size | parity | syscall.CREAD | syscall.CLOCAL | rate,
		Cc:     [32]uint8{syscall.VMIN: 1},
		Ispeed: rate,
		Ospeed: rate,
	}

	_, _, errno := syscall.Syscall6(
		syscall.SYS_IOCTL,
		uintptr(f.Fd()),
		uintptr(syscall.TCSETS),
		uintptr(unsafe.Pointer(&t)),
		0,
		0,
		0,
	)
	if errno != 0 {
		f.Close()
		return nil, errno
	}

	err = syscall.SetNonblock(int(f.Fd()), false)
	if err != nil {
		f.Close()
		return nil, err
	}

	return f, nil
}
<commit_msg>fixed parity, sizae and stop bit handling<commit_after>// +build linux,!cgo

package goserial

import (
	"fmt"
	"io"
	"os"
	"syscall"
	"unsafe"
)

const CBAUD = 0010017

func openPort(name string, c *Config) (rwc io.ReadWriteCloser, err error) {
	f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
	if err != nil {
		return nil, err
	}

	fd := f.Fd()
	t := syscall.Termios{}
	if _, _, errno := syscall.Syscall6(
		syscall.SYS_IOCTL,
		uintptr(fd),
		uintptr(syscall.TCGETS),
		uintptr(unsafe.Pointer(&t)),
		0,
		0,
		0,
	); errno != 0 {
		return nil, errno
	}

	var rate uint32
	switch c.Baud {
	case 115200:
		rate = syscall.B115200
	case 57600:
		rate = syscall.B57600
	case 38400:
		rate = syscall.B38400
	case 19200:
		rate = syscall.B19200
	case 9600:
		rate = syscall.B9600
	case 4800:
		rate = syscall.B4800
	default:
		return nil, fmt.Errorf("Unknown baud rate %v", c.Baud)
	}

	if rate == 0 {
		return
	}
	t.Cflag = (t.Cflag &^ CBAUD) | rate
	t.Ispeed = rate
	t.Ospeed = rate

	switch c.StopBits {
	case StopBits1:
		t.Cflag &^= syscall.CSTOPB
	case StopBits2:
		t.Cflag |= syscall.CSTOPB
	default:
		panic(c.StopBits)
	}

	t.Cflag &^= syscall.CSIZE
	switch c.Size {
	case Byte5:
		t.Cflag |= syscall.CS5
	case Byte6:
		t.Cflag |= syscall.CS6
	case Byte7:
		t.Cflag |= syscall.CS7
	case Byte8:
		t.Cflag |= syscall.CS8
	default:
		panic(c.Size)
	}

	switch c.Parity {
	case ParityNone:
		t.Cflag &^= syscall.PARENB
	case ParityEven:
		t.Cflag |= syscall.PARENB
	case ParityOdd:
		t.Cflag |= syscall.PARENB
		t.Cflag |= syscall.PARODD
	default:
		panic(c.Parity)
	}

	if c.CRLFTranslate {
		t.Iflag |= syscall.ICRNL
	} else {
		t.Iflag &^= syscall.ICRNL
	}

	// Select raw mode
	t.Lflag &^= syscall.ICANON | syscall.ECHO | syscall.ECHOE | syscall.ISIG
	t.Oflag &^= syscall.OPOST

	defer func() {
		if err != nil && f != nil {
			f.Close()
		}
	}()

	t.Cc[syscall.VMIN] = 1
	t.Cc[syscall.VTIME] = 0
	if _, _, errno := syscall.Syscall6(
		syscall.SYS_IOCTL,
		uintptr(fd),
		uintptr(syscall.TCSETS),
		uintptr(unsafe.Pointer(&t)),
		0,
		0,
		0,
	); errno != 0 {
		return nil, errno
	}

	if err = syscall.SetNonblock(int(fd), false); err != nil {
		return
	}

	return f, nil
}
<|endoftext|>


<commit_before>package serial

import (
	"errors"
	"os"
	"syscall"
	"time"
	"unsafe"
)

type Termios syscall.Termios

var baud = map[int]uint32{
	0:       syscall.B0,
	50:      syscall.B50,
	75:      syscall.B75,
	110:     syscall.B110,
	134:     syscall.B134,
	150:     syscall.B150,
	200:     syscall.B200,
	300:     syscall.B300,
	600:     syscall.B600,
	1200:    syscall.B1200,
	1800:    syscall.B1800,
	2400:    syscall.B2400,
	4800:    syscall.B4800,
	9600:    syscall.B9600,
	19200:   syscall.B19200,
	38400:   syscall.B38400,
	57600:   syscall.B57600,
	115200:  syscall.B115200,
	230400:  syscall.B230400,
	460800:  syscall.B460800,
	500000:  syscall.B500000,
	576000:  syscall.B576000,
	921600:  syscall.B921600,
	1000000: syscall.B1000000,
	1152000: syscall.B1152000,
	1500000: syscall.B1500000,
	2000000: syscall.B2000000,
	2500000: syscall.B2500000,
	3000000: syscall.B3000000,
	3500000: syscall.B3500000,
	4000000: syscall.B4000000,
}

var bits = map[int]uint32{
	5: syscall.CS5,
	6: syscall.CS6,
	7: syscall.CS7,
	8: syscall.CS8,
}

// Constants not defined in syscall module
const (
	cbaud   = 0010017
	cbaudex = 0010000
	crtscts = 020000000000
	tcflsh  = 0x540B
)

// Constants for modem control silgnals mask
const (
	DTR = syscall.TIOCM_DTR
	RTS = syscall.TIOCM_RTS
	CTS = syscall.TIOCM_CTS
	CAR = syscall.TIOCM_CAR
	RNG = syscall.TIOCM_RNG
	DSR = syscall.TIOCM_DSR
)

func open(path string) (int, error) {
	fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
	if err != nil {
		return -1, err
	}
	return fd, nil
}

func (s *Serial) tcGetAttr(cfg *Termios) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		syscall.TCGETS,
		uintptr(unsafe.Pointer(cfg)),
	)
	if e != 0 {
		return os.NewSyscallError("tcgetattr", e)
	}
	return nil
}

func (s *Serial) tcSetAttr(cfg *Termios) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		syscall.TCSETS,
		uintptr(unsafe.Pointer(cfg)),
	)
	if e != 0 {
		return os.NewSyscallError("tcsetattr", e)
	}
	return nil
}

func (s *Serial) init() error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	t.Iflag = (0)
	t.Oflag = (0)
	t.Lflag = (0)
	t.Cflag = (baud[9600] | bits[8] | syscall.CLOCAL | syscall.HUPCL)
	t.Cc[syscall.VMIN] = 1
	t.Cc[syscall.VTIME] = 0
	t.Ispeed = baud[9600]
	t.Ospeed = baud[9600]
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setBits(b int) error {
	var t Termios
	bb, ok := bits[b]
	if !ok {
		return errors.New("Usupported bits number")
	}
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	t.Cflag &^= (syscall.CS5 | syscall.CS6 | syscall.CS7 | syscall.CS8)
	t.Cflag |= bb
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setSpeed(b int) error {
	var t Termios
	bb, ok := baud[b]
	if !ok {
		return errors.New("Unknown baud rate")
	}
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	t.Cflag &^= cbaud | cbaudex
	t.Cflag |= bb
	t.Ispeed = bb
	t.Ospeed = bb
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setParity(mode int) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	switch mode {
	case PAR_NONE:
		t.Cflag &^= syscall.PARENB
	case PAR_EVEN:
		t.Cflag |= syscall.PARENB
		t.Cflag &^= syscall.PARODD
	case PAR_ODD:
		t.Cflag |= syscall.PARENB
		t.Cflag |= syscall.PARODD
	default:
		return errors.New("invalid parity mode")
	}
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setStopBits2(two bool) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	if two {
		t.Cflag |= syscall.CSTOPB
	} else {
		t.Cflag &^= syscall.CSTOPB
	}

	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setHwFlowCtrl(hw bool) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	if hw {
		t.Cflag |= crtscts
	} else {
		t.Cflag &^= crtscts
	}
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setSwFlowCtrl(sw bool) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	if sw {
		t.Iflag |= (syscall.IXON | syscall.IXOFF | syscall.IXANY)
	} else {
		t.Iflag &^= (syscall.IXON | syscall.IXOFF | syscall.IXANY)
	}
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setLocal(local bool) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	if local {
		t.Cflag |= syscall.CLOCAL
	} else {
		t.Cflag &^= syscall.CLOCAL
	}
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setReadTimeout(vmin int, vtime time.Duration) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	t.Cc[syscall.VMIN] = uint8(vmin)
	t.Cc[syscall.VTIME] = uint8(vtime / (time.Second / 10))
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setHup(hup bool) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	if hup {
		t.Cflag |= syscall.HUPCL
	} else {
		t.Cflag &^= syscall.HUPCL
	}
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setCtrlBit(ctr int, level bool) error {
	var cmd uintptr
	if level {
		cmd = syscall.TIOCMBIS
	} else {
		cmd = syscall.TIOCMBIC
	}
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		cmd,
		uintptr(unsafe.Pointer(&ctr)),
	)
	if e != 0 {
		return os.NewSyscallError("setCtrlBit", e)
	}
	return nil
}

func (s *Serial) getCtrl() (int, error) {
	v := 0
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		uintptr(syscall.TIOCMGET),
		uintptr(unsafe.Pointer(&v)),
	)
	if e != 0 {
		return 0, os.NewSyscallError("getCtrl", e)
	}
	return v, nil
}

func (s *Serial) setCtrl(ctr int) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		uintptr(syscall.TIOCMSET),
		uintptr(unsafe.Pointer(&ctr)),
	)
	if e != 0 {
		return os.NewSyscallError("setCtrl", e)
	}
	return nil
}

func (s *Serial) inpWaiting() (int, error) {
	var v int
	cmd := uintptr(syscall.TIOCINQ)
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		cmd,
		uintptr(unsafe.Pointer(&v)),
	)
	if e != 0 {
		return 0, os.NewSyscallError("inpWaiting", e)
	}
	return v, nil
}

func (s *Serial) outWaiting() (int, error) {
	var v int
	cmd := uintptr(syscall.TIOCOUTQ)
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		cmd,
		uintptr(unsafe.Pointer(&v)),
	)
	if e != 0 {
		return 0, os.NewSyscallError("outWaiting", e)
	}
	return v, nil
}

func (s *Serial) flush(mode int) error {
	var v int
	cmd := uintptr(tcflsh)
	switch mode {
	case FLUSH_I:
		v = syscall.TCIFLUSH
	case FLUSH_O:
		v = syscall.TCOFLUSH
	case FLUSH_IO:
		v = syscall.TCIOFLUSH
	default:
		return errors.New("invalid flush mode")
	}
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		cmd,
		uintptr(v),
	)
	if e != 0 {
		return os.NewSyscallError("flush", e)
	}
	return nil
}
<commit_msg>Add cread flag by default<commit_after>package serial

import (
	"errors"
	"os"
	"syscall"
	"time"
	"unsafe"
)

type Termios syscall.Termios

var baud = map[int]uint32{
	0:       syscall.B0,
	50:      syscall.B50,
	75:      syscall.B75,
	110:     syscall.B110,
	134:     syscall.B134,
	150:     syscall.B150,
	200:     syscall.B200,
	300:     syscall.B300,
	600:     syscall.B600,
	1200:    syscall.B1200,
	1800:    syscall.B1800,
	2400:    syscall.B2400,
	4800:    syscall.B4800,
	9600:    syscall.B9600,
	19200:   syscall.B19200,
	38400:   syscall.B38400,
	57600:   syscall.B57600,
	115200:  syscall.B115200,
	230400:  syscall.B230400,
	460800:  syscall.B460800,
	500000:  syscall.B500000,
	576000:  syscall.B576000,
	921600:  syscall.B921600,
	1000000: syscall.B1000000,
	1152000: syscall.B1152000,
	1500000: syscall.B1500000,
	2000000: syscall.B2000000,
	2500000: syscall.B2500000,
	3000000: syscall.B3000000,
	3500000: syscall.B3500000,
	4000000: syscall.B4000000,
}

var bits = map[int]uint32{
	5: syscall.CS5,
	6: syscall.CS6,
	7: syscall.CS7,
	8: syscall.CS8,
}

// Constants not defined in syscall module
const (
	cbaud   = 0010017
	cbaudex = 0010000
	crtscts = 020000000000
	tcflsh  = 0x540B
)

// Constants for modem control silgnals mask
const (
	DTR = syscall.TIOCM_DTR
	RTS = syscall.TIOCM_RTS
	CTS = syscall.TIOCM_CTS
	CAR = syscall.TIOCM_CAR
	RNG = syscall.TIOCM_RNG
	DSR = syscall.TIOCM_DSR
)

func open(path string) (int, error) {
	fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0666)
	if err != nil {
		return -1, err
	}
	return fd, nil
}

func (s *Serial) tcGetAttr(cfg *Termios) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		syscall.TCGETS,
		uintptr(unsafe.Pointer(cfg)),
	)
	if e != 0 {
		return os.NewSyscallError("tcgetattr", e)
	}
	return nil
}

func (s *Serial) tcSetAttr(cfg *Termios) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		syscall.TCSETS,
		uintptr(unsafe.Pointer(cfg)),
	)
	if e != 0 {
		return os.NewSyscallError("tcsetattr", e)
	}
	return nil
}

func (s *Serial) init() error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	t.Iflag = (0)
	t.Oflag = (0)
	t.Lflag = (0)
	t.Cflag = (baud[9600] | bits[8] | syscall.CLOCAL | syscall.HUPCL | syscall.CREAD)
	t.Cc[syscall.VMIN] = 1
	t.Cc[syscall.VTIME] = 0
	t.Ispeed = baud[9600]
	t.Ospeed = baud[9600]
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setBits(b int) error {
	var t Termios
	bb, ok := bits[b]
	if !ok {
		return errors.New("Usupported bits number")
	}
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	t.Cflag &^= (syscall.CS5 | syscall.CS6 | syscall.CS7 | syscall.CS8)
	t.Cflag |= bb
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setSpeed(b int) error {
	var t Termios
	bb, ok := baud[b]
	if !ok {
		return errors.New("Unknown baud rate")
	}
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	t.Cflag &^= cbaud | cbaudex
	t.Cflag |= bb
	t.Ispeed = bb
	t.Ospeed = bb
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setParity(mode int) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	switch mode {
	case PAR_NONE:
		t.Cflag &^= syscall.PARENB
	case PAR_EVEN:
		t.Cflag |= syscall.PARENB
		t.Cflag &^= syscall.PARODD
	case PAR_ODD:
		t.Cflag |= syscall.PARENB
		t.Cflag |= syscall.PARODD
	default:
		return errors.New("invalid parity mode")
	}
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setStopBits2(two bool) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	if two {
		t.Cflag |= syscall.CSTOPB
	} else {
		t.Cflag &^= syscall.CSTOPB
	}

	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setHwFlowCtrl(hw bool) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	if hw {
		t.Cflag |= crtscts
	} else {
		t.Cflag &^= crtscts
	}
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setSwFlowCtrl(sw bool) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	if sw {
		t.Iflag |= (syscall.IXON | syscall.IXOFF | syscall.IXANY)
	} else {
		t.Iflag &^= (syscall.IXON | syscall.IXOFF | syscall.IXANY)
	}
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setLocal(local bool) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	if local {
		t.Cflag |= syscall.CLOCAL
	} else {
		t.Cflag &^= syscall.CLOCAL
	}
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setReadTimeout(vmin int, vtime time.Duration) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	t.Cc[syscall.VMIN] = uint8(vmin)
	t.Cc[syscall.VTIME] = uint8(vtime / (time.Second / 10))
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setHup(hup bool) error {
	var t Termios
	if err := s.tcGetAttr(&t); err != nil {
		return err
	}
	if hup {
		t.Cflag |= syscall.HUPCL
	} else {
		t.Cflag &^= syscall.HUPCL
	}
	if err := s.tcSetAttr(&t); err != nil {
		return err
	}
	return nil
}

func (s *Serial) setCtrlBit(ctr int, level bool) error {
	var cmd uintptr
	if level {
		cmd = syscall.TIOCMBIS
	} else {
		cmd = syscall.TIOCMBIC
	}
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		cmd,
		uintptr(unsafe.Pointer(&ctr)),
	)
	if e != 0 {
		return os.NewSyscallError("setCtrlBit", e)
	}
	return nil
}

func (s *Serial) getCtrl() (int, error) {
	v := 0
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		uintptr(syscall.TIOCMGET),
		uintptr(unsafe.Pointer(&v)),
	)
	if e != 0 {
		return 0, os.NewSyscallError("getCtrl", e)
	}
	return v, nil
}

func (s *Serial) setCtrl(ctr int) error {
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		uintptr(syscall.TIOCMSET),
		uintptr(unsafe.Pointer(&ctr)),
	)
	if e != 0 {
		return os.NewSyscallError("setCtrl", e)
	}
	return nil
}

func (s *Serial) inpWaiting() (int, error) {
	var v int
	cmd := uintptr(syscall.TIOCINQ)
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		cmd,
		uintptr(unsafe.Pointer(&v)),
	)
	if e != 0 {
		return 0, os.NewSyscallError("inpWaiting", e)
	}
	return v, nil
}

func (s *Serial) outWaiting() (int, error) {
	var v int
	cmd := uintptr(syscall.TIOCOUTQ)
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		cmd,
		uintptr(unsafe.Pointer(&v)),
	)
	if e != 0 {
		return 0, os.NewSyscallError("outWaiting", e)
	}
	return v, nil
}

func (s *Serial) flush(mode int) error {
	var v int
	cmd := uintptr(tcflsh)
	switch mode {
	case FLUSH_I:
		v = syscall.TCIFLUSH
	case FLUSH_O:
		v = syscall.TCOFLUSH
	case FLUSH_IO:
		v = syscall.TCIOFLUSH
	default:
		return errors.New("invalid flush mode")
	}
	_, _, e := syscall.Syscall(
		syscall.SYS_IOCTL,
		uintptr(s.f.Fd()),
		cmd,
		uintptr(v),
	)
	if e != 0 {
		return os.NewSyscallError("flush", e)
	}
	return nil
}
<|endoftext|>


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

package serial

/*

#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>

#include <linux/serial.h>

//int fcntl_wrapper(int fd, int cmd, int arg) {
//	return fcntl(fd, cmd, arg);
//}

// Gain exclusive access to serial port
void setTIOCEXCL(int handle) {
#if defined TIOCEXCL
	ioctl(handle, TIOCEXCL);
#endif
}

// Release exclusive access to serial port
void setTIOCNXCL(int handle) {
#if defined TIOCNXCL
	ioctl(handle, TIOCNXCL);
#endif
}

*/
import "C"
import "io/ioutil"
import "regexp"
import "syscall"
import "unsafe"

//sys ioctl(fd int, req uint64, data uintptr) (err error)
func ioctl(fd int, req uint64, data uintptr) (err error) {
	_, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data))
	if e1 != 0 {
		err = e1
	}
	return
}

// native syscall wrapper functions

func getExclusiveAccess(handle int) error {
	_, err := C.setTIOCEXCL(C.int(handle))
	return err
}

func releaseExclusiveAccess(handle int) error {
	_, err := C.setTIOCNXCL(C.int(handle))
	return err
}

func getTermSettings(handle int) (*syscall.Termios, error) {
	settings := &syscall.Termios{}
	err := ioctl(handle, syscall.TCGETS, uintptr(unsafe.Pointer(settings)))
	return settings, err
}

func setTermSettings(handle int, settings *syscall.Termios) error {
	return ioctl(handle, syscall.TCSETS, uintptr(unsafe.Pointer(settings)))
}

func getErrno(err error) int {
	return int(err.(syscall.Errno))
}

// OS dependent values

const devFolder = "/dev"
const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}"

// opaque type that implements SerialPort interface for linux
type linuxSerialPort struct {
	Handle int
}

func GetPortsList() ([]string, error) {
	files, err := ioutil.ReadDir(devFolder)
	if err != nil {
		return nil, err
	}

	ports := make([]string, len(files))
	found := 0
	for _, f := range files {
		// Skip folders
		if f.IsDir() {
			continue
		}

		// Keep only devices with the correct name
		match, err := regexp.MatchString(regexFilter, f.Name())
		if err != nil {
			return nil, err
		}
		if !match {
			continue
		}

		portName := devFolder + "/" + f.Name()

		// Check if serial port is real or is a placeholder serial port "ttySxx"
		if f.Name()[:4] == "ttyS" {
			port, err := OpenPort(portName, false)
			if err != nil {
				serr, ok := err.(*SerialPortError)
				if ok && serr.Code() == ERROR_INVALID_SERIAL_PORT {
					continue
				}
			} else {
				port.Close()
			}
		}

		// Save found serial port in the resulting list
		ports[found] = portName
		found++
	}

	ports = ports[:found]
	return ports, nil
}

func (port *linuxSerialPort) Close() error {
	releaseExclusiveAccess(port.Handle)
	return syscall.Close(port.Handle)
}

func (port *linuxSerialPort) Read(p []byte) (n int, err error) {
	return syscall.Read(port.Handle, p)
}

func (port *linuxSerialPort) Write(p []byte) (n int, err error) {
	return syscall.Write(port.Handle, p)
}

var baudrateMap = map[int]uint32{
	50:      syscall.B50,
	75:      syscall.B75,
	110:     syscall.B110,
	134:     syscall.B134,
	150:     syscall.B150,
	200:     syscall.B200,
	300:     syscall.B300,
	600:     syscall.B600,
	1200:    syscall.B1200,
	1800:    syscall.B1800,
	2400:    syscall.B2400,
	4800:    syscall.B4800,
	9600:    syscall.B9600,
	19200:   syscall.B19200,
	38400:   syscall.B38400,
	57600:   syscall.B57600,
	115200:  syscall.B115200,
	230400:  syscall.B230400,
	460800:  syscall.B460800,
	500000:  syscall.B500000,
	576000:  syscall.B576000,
	921600:  syscall.B921600,
	1000000: syscall.B1000000,
	1152000: syscall.B1152000,
	1500000: syscall.B1500000,
	2000000: syscall.B2000000,
	2500000: syscall.B2500000,
	3000000: syscall.B3000000,
	3500000: syscall.B3500000,
	4000000: syscall.B4000000,
}

func (port *linuxSerialPort) SetSpeed(speed int) error {
	baudrate, ok := baudrateMap[speed]
	if !ok {
		return &SerialPortError{code: ERROR_INVALID_PORT_SPEED}
	}
	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	// revert old baudrate
	var BAUDMASK uint32 = 0
	for _, rate := range baudrateMap {
		BAUDMASK |= rate
	}
	settings.Cflag &= ^uint32(BAUDMASK)
	// set new baudrate
	settings.Cflag |= baudrate
	settings.Ispeed = baudrate
	settings.Ospeed = baudrate
	return setTermSettings(port.Handle, settings)
}

func (port *linuxSerialPort) SetParity(parity Parity) error {
	const FIXED_PARITY_FLAG uint32 = 0 // may be CMSPAR or PAREXT

	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	switch parity {
	case PARITY_NONE:
		settings.Cflag &= ^uint32(syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG)
		settings.Iflag &= ^uint32(syscall.INPCK)
	case PARITY_ODD:
		settings.Cflag |= syscall.PARENB | syscall.PARODD
		settings.Cflag &= ^uint32(FIXED_PARITY_FLAG)
		settings.Iflag |= syscall.INPCK
	case PARITY_EVEN:
		settings.Cflag &= ^uint32(syscall.PARODD | FIXED_PARITY_FLAG)
		settings.Cflag |= syscall.PARENB
		settings.Iflag |= syscall.INPCK
	case PARITY_MARK:
		settings.Cflag |= syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG
		settings.Iflag |= syscall.INPCK
	case PARITY_SPACE:
		settings.Cflag &= ^uint32(syscall.PARODD)
		settings.Cflag |= syscall.PARENB | FIXED_PARITY_FLAG
		settings.Iflag |= syscall.INPCK
	}
	return setTermSettings(port.Handle, settings)
}

var databitsMap = map[int]uint32{
	5: syscall.CS5,
	6: syscall.CS6,
	7: syscall.CS7,
	8: syscall.CS8,
}

func (port *linuxSerialPort) SetDataBits(bits int) error {
	databits, ok := databitsMap[bits]
	if !ok {
		return &SerialPortError{code: ERROR_INVALID_PORT_DATA_BITS}
	}
	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	settings.Cflag &= ^uint32(syscall.CSIZE)
	settings.Cflag |= databits
	return setTermSettings(port.Handle, settings)
}

func (port *linuxSerialPort) SetStopBits(bits StopBits) error {
	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	switch bits {
	case STOPBITS_ONE:
		settings.Cflag &= ^uint32(syscall.CSTOPB)
	case STOPBITS_ONEPOINTFIVE, STOPBITS_TWO:
		settings.Cflag |= syscall.CSTOPB
	}
	return setTermSettings(port.Handle, settings)
}

func OpenPort(portName string, exclusive bool) (SerialPort, error) {
	handle, err := syscall.Open(portName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0)
	if err != nil {
		switch err {
		case syscall.EBUSY:
			return nil, &SerialPortError{code: ERROR_PORT_BUSY}
		case syscall.EACCES:
			return nil, &SerialPortError{code: ERROR_PERMISSION_DENIED}
		}
		return nil, err
	}

	// Setup serial port with defaults

	settings, err := getTermSettings(handle)
	if err != nil {
		syscall.Close(handle)
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}

	// Set local mode
	settings.Cflag |= C.CREAD | C.CLOCAL

	// Set raw mode
	settings.Lflag &= ^uint32(syscall.ICANON | syscall.ECHO | syscall.ECHOE | syscall.ECHOK | syscall.ECHONL | syscall.ECHOCTL | syscall.ECHOPRT | syscall.ECHOKE | syscall.ISIG | syscall.IEXTEN)
	settings.Iflag &= ^uint32(syscall.IXON | syscall.IXOFF | syscall.IXANY | syscall.INPCK | syscall.IGNPAR | syscall.PARMRK | syscall.ISTRIP | syscall.IGNBRK | syscall.BRKINT | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IUCLC)
	settings.Oflag &= ^uint32(syscall.OPOST)

	// Block reads until at least one char is available (no timeout)
	settings.Cc[syscall.VMIN] = 1
	settings.Cc[syscall.VTIME] = 0

	err = setTermSettings(handle, settings)
	if err != nil {
		syscall.Close(handle)
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}
	/*
	   settings->c_cflag &= ~CRTSCTS;
	*/
	syscall.SetNonblock(handle, false)

	if exclusive {
		getExclusiveAccess(handle)
	}

	serialPort := &linuxSerialPort{
		Handle: handle,
	}
	return serialPort, nil
}

// vi:ts=2
<commit_msg>linux: implemented last native calls through syscall package<commit_after>//
// Copyright 2014 Cristian Maglie. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package serial

import "io/ioutil"
import "regexp"
import "syscall"
import "unsafe"

//sys ioctl(fd int, req uint64, data uintptr) (err error)
func ioctl(fd int, req uint64, data uintptr) (err error) {
	_, _, e1 := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(data))
	if e1 != 0 {
		err = e1
	}
	return
}

// native syscall wrapper functions

func getExclusiveAccess(handle int) error {
	return ioctl(handle, syscall.TIOCEXCL, 0)
}

func releaseExclusiveAccess(handle int) error {
	return ioctl(handle, syscall.TIOCNXCL, 0)
}

func getTermSettings(handle int) (*syscall.Termios, error) {
	settings := &syscall.Termios{}
	err := ioctl(handle, syscall.TCGETS, uintptr(unsafe.Pointer(settings)))
	return settings, err
}

func setTermSettings(handle int, settings *syscall.Termios) error {
	return ioctl(handle, syscall.TCSETS, uintptr(unsafe.Pointer(settings)))
}

func getErrno(err error) int {
	return int(err.(syscall.Errno))
}

// OS dependent values

const devFolder = "/dev"
const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}"

// opaque type that implements SerialPort interface for linux
type linuxSerialPort struct {
	Handle int
}

func GetPortsList() ([]string, error) {
	files, err := ioutil.ReadDir(devFolder)
	if err != nil {
		return nil, err
	}

	ports := make([]string, len(files))
	found := 0
	for _, f := range files {
		// Skip folders
		if f.IsDir() {
			continue
		}

		// Keep only devices with the correct name
		match, err := regexp.MatchString(regexFilter, f.Name())
		if err != nil {
			return nil, err
		}
		if !match {
			continue
		}

		portName := devFolder + "/" + f.Name()

		// Check if serial port is real or is a placeholder serial port "ttySxx"
		if f.Name()[:4] == "ttyS" {
			port, err := OpenPort(portName, false)
			if err != nil {
				serr, ok := err.(*SerialPortError)
				if ok && serr.Code() == ERROR_INVALID_SERIAL_PORT {
					continue
				}
			} else {
				port.Close()
			}
		}

		// Save found serial port in the resulting list
		ports[found] = portName
		found++
	}

	ports = ports[:found]
	return ports, nil
}

func (port *linuxSerialPort) Close() error {
	releaseExclusiveAccess(port.Handle)
	return syscall.Close(port.Handle)
}

func (port *linuxSerialPort) Read(p []byte) (n int, err error) {
	return syscall.Read(port.Handle, p)
}

func (port *linuxSerialPort) Write(p []byte) (n int, err error) {
	return syscall.Write(port.Handle, p)
}

var baudrateMap = map[int]uint32{
	50:      syscall.B50,
	75:      syscall.B75,
	110:     syscall.B110,
	134:     syscall.B134,
	150:     syscall.B150,
	200:     syscall.B200,
	300:     syscall.B300,
	600:     syscall.B600,
	1200:    syscall.B1200,
	1800:    syscall.B1800,
	2400:    syscall.B2400,
	4800:    syscall.B4800,
	9600:    syscall.B9600,
	19200:   syscall.B19200,
	38400:   syscall.B38400,
	57600:   syscall.B57600,
	115200:  syscall.B115200,
	230400:  syscall.B230400,
	460800:  syscall.B460800,
	500000:  syscall.B500000,
	576000:  syscall.B576000,
	921600:  syscall.B921600,
	1000000: syscall.B1000000,
	1152000: syscall.B1152000,
	1500000: syscall.B1500000,
	2000000: syscall.B2000000,
	2500000: syscall.B2500000,
	3000000: syscall.B3000000,
	3500000: syscall.B3500000,
	4000000: syscall.B4000000,
}

func (port *linuxSerialPort) SetSpeed(speed int) error {
	baudrate, ok := baudrateMap[speed]
	if !ok {
		return &SerialPortError{code: ERROR_INVALID_PORT_SPEED}
	}
	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	// revert old baudrate
	var BAUDMASK uint32 = 0
	for _, rate := range baudrateMap {
		BAUDMASK |= rate
	}
	settings.Cflag &= ^uint32(BAUDMASK)
	// set new baudrate
	settings.Cflag |= baudrate
	settings.Ispeed = baudrate
	settings.Ospeed = baudrate
	return setTermSettings(port.Handle, settings)
}

func (port *linuxSerialPort) SetParity(parity Parity) error {
	const FIXED_PARITY_FLAG uint32 = 0 // may be CMSPAR or PAREXT

	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	switch parity {
	case PARITY_NONE:
		settings.Cflag &= ^uint32(syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG)
		settings.Iflag &= ^uint32(syscall.INPCK)
	case PARITY_ODD:
		settings.Cflag |= syscall.PARENB | syscall.PARODD
		settings.Cflag &= ^uint32(FIXED_PARITY_FLAG)
		settings.Iflag |= syscall.INPCK
	case PARITY_EVEN:
		settings.Cflag &= ^uint32(syscall.PARODD | FIXED_PARITY_FLAG)
		settings.Cflag |= syscall.PARENB
		settings.Iflag |= syscall.INPCK
	case PARITY_MARK:
		settings.Cflag |= syscall.PARENB | syscall.PARODD | FIXED_PARITY_FLAG
		settings.Iflag |= syscall.INPCK
	case PARITY_SPACE:
		settings.Cflag &= ^uint32(syscall.PARODD)
		settings.Cflag |= syscall.PARENB | FIXED_PARITY_FLAG
		settings.Iflag |= syscall.INPCK
	}
	return setTermSettings(port.Handle, settings)
}

var databitsMap = map[int]uint32{
	5: syscall.CS5,
	6: syscall.CS6,
	7: syscall.CS7,
	8: syscall.CS8,
}

func (port *linuxSerialPort) SetDataBits(bits int) error {
	databits, ok := databitsMap[bits]
	if !ok {
		return &SerialPortError{code: ERROR_INVALID_PORT_DATA_BITS}
	}
	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	settings.Cflag &= ^uint32(syscall.CSIZE)
	settings.Cflag |= databits
	return setTermSettings(port.Handle, settings)
}

func (port *linuxSerialPort) SetStopBits(bits StopBits) error {
	settings, err := getTermSettings(port.Handle)
	if err != nil {
		return err
	}
	switch bits {
	case STOPBITS_ONE:
		settings.Cflag &= ^uint32(syscall.CSTOPB)
	case STOPBITS_ONEPOINTFIVE, STOPBITS_TWO:
		settings.Cflag |= syscall.CSTOPB
	}
	return setTermSettings(port.Handle, settings)
}

func OpenPort(portName string, exclusive bool) (SerialPort, error) {
	handle, err := syscall.Open(portName, syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_NDELAY, 0)
	if err != nil {
		switch err {
		case syscall.EBUSY:
			return nil, &SerialPortError{code: ERROR_PORT_BUSY}
		case syscall.EACCES:
			return nil, &SerialPortError{code: ERROR_PERMISSION_DENIED}
		}
		return nil, err
	}

	// Setup serial port with defaults

	settings, err := getTermSettings(handle)
	if err != nil {
		syscall.Close(handle)
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}

	// Set local mode
	settings.Cflag |= syscall.CREAD | syscall.CLOCAL

	// Set raw mode
	settings.Lflag &= ^uint32(syscall.ICANON | syscall.ECHO | syscall.ECHOE | syscall.ECHOK | syscall.ECHONL | syscall.ECHOCTL | syscall.ECHOPRT | syscall.ECHOKE | syscall.ISIG | syscall.IEXTEN)
	settings.Iflag &= ^uint32(syscall.IXON | syscall.IXOFF | syscall.IXANY | syscall.INPCK | syscall.IGNPAR | syscall.PARMRK | syscall.ISTRIP | syscall.IGNBRK | syscall.BRKINT | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IUCLC)
	settings.Oflag &= ^uint32(syscall.OPOST)

	// Block reads until at least one char is available (no timeout)
	settings.Cc[syscall.VMIN] = 1
	settings.Cc[syscall.VTIME] = 0

	err = setTermSettings(handle, settings)
	if err != nil {
		syscall.Close(handle)
		return nil, &SerialPortError{code: ERROR_INVALID_SERIAL_PORT}
	}
	/*
	   settings->c_cflag &= ~CRTSCTS;
	*/
	syscall.SetNonblock(handle, false)

	if exclusive {
		getExclusiveAccess(handle)
	}

	serialPort := &linuxSerialPort{
		Handle: handle,
	}
	return serialPort, nil
}

// vi:ts=2
<|endoftext|>


<commit_before>package main

import (
	"github.com/gin-gonic/gin"
	"github.com/jessevdk/go-flags"
	"github.com/robbiet480/cec"
	"log"
	"os"
	"strconv"
	"strings"
)

type HTTPOptions struct {
	Host string `short:"i" long:"ip" description:"IP address to listen on" default:"0.0.0.0"`
	Port string `short:"p" long:"port" description:"TCP port to listen on" default:"8080"`
}

type CECOptions struct {
	Adapter string `short:"a" long:"adapter" description:"CEC adapter to connect to" default:"RPI"`
	Name    string `short:"n" long:"name" description:"OSD name to announce on the CEC bus" default:"cec-web"`
	Type    string `short:"t" long:"type" description:"The device type to announce as" default:"tv" default:"recording" default:"reserved" default:"playback" default:"audio" default:"tuner"`
}

type AudioOptions struct {
	AudioDevice string `short:"d" long:"audio-device" description:"The audio device to use for volume control and status" default:"Audio" default:"TV"`
	ResetVolume bool   `short:"z" long:"zero-volume" description:"Whether to reset the volume to 0 at startup" default:"false" default:"true"`
	StartVolume int    `short:"v" long:"initial-volume" description:"Provide an initial volume level" default:"0"`
	MaxVolume   int    `short:"c" long:"max-volume" description:"The maximum supported volume" default:"100"`
}

type Options struct {
	HTTP  HTTPOptions  `group:"HTTP Server Options"`
	CEC   CECOptions   `group:"CEC Options"`
	Audio AudioOptions `group:"Audio Options"`
}

var options Options
var parser = flags.NewParser(&options, flags.Default)

var volume_level = options.Audio.StartVolume
var input_number int
var is_muted = false

func main() {
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}
	cec.Open(options.CEC.Adapter, options.CEC.Name, options.CEC.Type)

	if cec.PollDevice(cec.GetLogicalAddressByName(options.Audio.AudioDevice)) != true {
		var word = "a"
		if options.Audio.AudioDevice == "Audio" {
			word = "an"
		}
		log.Println("You said you had " + word + " " + options.Audio.AudioDevice + " device but one cant be found!")
		os.Exit(1)
	}

	r := gin.Default()
	r.GET("/config", config)
	r.GET("/info", info)
	r.GET("/input", input_status)
	r.PUT("/input/:number", input_change)
	r.GET("/power/:device", power_status)
	r.PUT("/power/:device", power_on)
	r.DELETE("/power/:device", power_off)
	r.GET("/volume", vol_status)
	r.PUT("/volume/up", vol_up)
	r.PUT("/volume/down", vol_down)
	r.PUT("/volume/mute", vol_mute)
	r.GET("/volume/mute", vol_mute_status)
	r.PUT("/volume/reset", vol_reset)
	r.PUT("/volume/step/:direction/:steps", vol_step)
	r.PUT("/volume/set/:level", vol_set)
	r.PUT("/volume/force/:level", vol_force_set)
	r.PUT("/key/:device/:key", key)
	r.PUT("/channel/:device/:channel", change_channel)
	r.POST("/transmit", transmit)

	if options.Audio.ResetVolume != false {
		// Let's reset the volume level to 0
		log.Println("Resetting volume to 0")
		for i := 0; i < options.Audio.MaxVolume; i++ {
			addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
			log.Println("Sending VolumeDown")
			cec.Key(addr, "VolumeDown")
			volume_level = 0
		}
		log.Println("Volume has been set to 0")
	} else {
		log.Println("Not resetting volume to 0, assuming it is already at 0")
	}

	log.Println("Getting the current active input")

	for address, active := range cec.GetActiveDevices() {
		if (active) && (cec.IsActiveSource(address)) {
			input_str := strings.Split(cec.GetDevicePhysicalAddress(address), ".")[0]
			input_atoi, _ := strconv.Atoi(input_str)
			input_number = int(input_atoi)
		}
	}

	r.Run(options.HTTP.Host + ":" + options.HTTP.Port)
}

func config(c *gin.Context) {
	c.JSON(200, options)
}

func info(c *gin.Context) {
	c.JSON(200, cec.List())
}

func power_on(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.PowerOn(addr)
	c.String(200, "on")
}

func power_off(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.Standby(addr)
	c.String(200, "off")
}

func power_status(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	status := cec.GetDevicePowerStatus(addr)
	if status == "on" {
		c.String(200, "on")
	} else if status == "standby" {
		c.String(200, "off")
	} else {
		c.String(500, "invalid power state")
	}
}

func input_status(c *gin.Context) {
	c.String(200, "INPUT HDMI "+strconv.Itoa(input_number))
}

func input_change(c *gin.Context) {
	input := c.Params.ByName("number")
	cec.Transmit("3f:82:" + input + "0:00")
	input_atoi, _ := strconv.Atoi(input)
	input_number = int(input_atoi)
	c.String(200, "INPUT HDMI "+input)
}

func change_channel(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	channel := c.Params.ByName("channel")

	for _, number := range channel {
		cec.Key(addr, "0x2"+string(number))
	}

	c.String(200, channel)
}

func vol_step(c *gin.Context) {
	steps_str := c.Params.ByName("steps")
	steps_atoi, _ := strconv.Atoi(steps_str)
	steps := int(steps_atoi)
	direction := c.Params.ByName("direction")

	for i := 0; i < steps; i++ {
		if direction == "up" {
			addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
			cec.Key(addr, "VolumeUp")
			volume_level = volume_level + steps
		} else if direction == "down" {
			addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
			cec.Key(addr, "VolumeDown")
			volume_level = volume_level - steps
		} else {
			c.String(400, "Invalid direction. Valid directions are up or down.")
		}
	}

	c.String(204, "")
}

func vol_set(c *gin.Context) {
	level_str := c.Params.ByName("level")
	level_atoi, _ := strconv.Atoi(level_str)
	wanted_level := int(level_atoi)

	log.Println("Wanted_level is " + strconv.Itoa(wanted_level) + " and volume_level is " + strconv.Itoa(volume_level))

	if wanted_level > options.Audio.MaxVolume {
		c.String(400, "The maximum volume level is "+strconv.Itoa(options.Audio.MaxVolume))
	} else if wanted_level > volume_level { // Requested level is greater then current volume level
		log.Println("FIRST")
		var final_level = wanted_level - volume_level
		log.Println("Final_level is " + strconv.Itoa(final_level))
		for i := 0; i < final_level; i++ {
			addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
			cec.Key(addr, "VolumeUp")
		}
	} else if wanted_level < volume_level { // Requested level is less than current volume level
		log.Println("SECOND")
		var final_level = volume_level - wanted_level
		log.Println("Final_level is " + strconv.Itoa(final_level))
		for i := 0; i < final_level; i++ {
			addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
			cec.Key(addr, "VolumeDown")
		}
	}

	volume_level = wanted_level

	c.String(200, strconv.Itoa(volume_level))
}

func vol_force_set(c *gin.Context) {
	level_str := c.Params.ByName("level")
	level_atoi, _ := strconv.Atoi(level_str)
	volume_level = int(level_atoi)
	c.String(204, "")
}

func vol_status(c *gin.Context) {
	if is_muted == true {
		c.String(200, "muted")
	} else {
		c.String(200, strconv.Itoa(volume_level))
	}
}

func vol_up(c *gin.Context) {
	if volume_level == options.Audio.MaxVolume {
		c.String(400, "Volume already at maximum")
	} else {
		addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
		cec.Key(addr, "VolumeUp")
		volume_level = volume_level + 1
		c.String(204, "")
	}
}

func vol_down(c *gin.Context) {
	if volume_level == 0 {
		c.String(400, "Volume is already at minimum")
	} else {
		addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
		cec.Key(addr, "VolumeDown")
		volume_level = volume_level - 1
		c.String(204, "")
	}
}

func vol_mute(c *gin.Context) {
	cec.Mute()
	is_muted = true
	c.String(204, "")
}

func vol_mute_status(c *gin.Context) {
	c.String(200, strconv.FormatBool(is_muted))
}

func vol_reset(c *gin.Context) {
	for i := 0; i < options.Audio.MaxVolume; i++ {
		log.Println("Sending VolumeDown")
		addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)

		cec.Key(addr, "VolumeDown")
	}
	volume_level = 0
	c.String(200, strconv.Itoa(volume_level))
}

func key(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	key := c.Params.ByName("key")

	cec.Key(addr, key)
	c.String(204, "")
}

func transmit(c *gin.Context) {
	var commands []string
	c.Bind(&commands)

	for _, val := range commands {
		cec.Transmit(val)
	}
	c.String(204, "")
}
<commit_msg>Add multikey, another conveinence function<commit_after>package main

import (
	"github.com/gin-gonic/gin"
	"github.com/jessevdk/go-flags"
	"github.com/robbiet480/cec"
	"log"
	"os"
	"strconv"
	"strings"
	"time"
)

type HTTPOptions struct {
	Host string `short:"i" long:"ip" description:"IP address to listen on" default:"0.0.0.0"`
	Port string `short:"p" long:"port" description:"TCP port to listen on" default:"8080"`
}

type CECOptions struct {
	Adapter string `short:"a" long:"adapter" description:"CEC adapter to connect to" default:"RPI"`
	Name    string `short:"n" long:"name" description:"OSD name to announce on the CEC bus" default:"cec-web"`
	Type    string `short:"t" long:"type" description:"The device type to announce as" default:"tv" default:"recording" default:"reserved" default:"playback" default:"audio" default:"tuner"`
}

type AudioOptions struct {
	AudioDevice string `short:"d" long:"audio-device" description:"The audio device to use for volume control and status" default:"Audio" default:"TV"`
	ResetVolume bool   `short:"z" long:"zero-volume" description:"Whether to reset the volume to 0 at startup" default:"false" default:"true"`
	StartVolume int    `short:"v" long:"initial-volume" description:"Provide an initial volume level" default:"0"`
	MaxVolume   int    `short:"c" long:"max-volume" description:"The maximum supported volume" default:"100"`
}

type Options struct {
	HTTP  HTTPOptions  `group:"HTTP Server Options"`
	CEC   CECOptions   `group:"CEC Options"`
	Audio AudioOptions `group:"Audio Options"`
}

var options Options
var parser = flags.NewParser(&options, flags.Default)

var volume_level = options.Audio.StartVolume
var input_number int
var is_muted = false

func main() {
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}
	cec.Open(options.CEC.Adapter, options.CEC.Name, options.CEC.Type)

	if cec.PollDevice(cec.GetLogicalAddressByName(options.Audio.AudioDevice)) != true {
		var word = "a"
		if options.Audio.AudioDevice == "Audio" {
			word = "an"
		}
		log.Println("You said you had " + word + " " + options.Audio.AudioDevice + " device but one cant be found!")
		os.Exit(1)
	}

	r := gin.Default()
	r.GET("/config", config)
	r.GET("/info", info)
	r.GET("/input", input_status)
	r.PUT("/input/:number", input_change)
	r.GET("/power/:device", power_status)
	r.PUT("/power/:device", power_on)
	r.DELETE("/power/:device", power_off)
	r.GET("/volume", vol_status)
	r.PUT("/volume/up", vol_up)
	r.PUT("/volume/down", vol_down)
	r.PUT("/volume/mute", vol_mute)
	r.GET("/volume/mute", vol_mute_status)
	r.PUT("/volume/reset", vol_reset)
	r.PUT("/volume/step/:direction/:steps", vol_step)
	r.PUT("/volume/set/:level", vol_set)
	r.PUT("/volume/force/:level", vol_force_set)
	r.PUT("/key/:device/:key", key)
	r.PUT("/multikey/:device/:key/:delay/:key2", multi_key)
	r.PUT("/channel/:device/:channel", change_channel)
	r.POST("/transmit", transmit)

	if options.Audio.ResetVolume != false {
		// Let's reset the volume level to 0
		log.Println("Resetting volume to 0")
		for i := 0; i < options.Audio.MaxVolume; i++ {
			addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
			log.Println("Sending VolumeDown")
			cec.Key(addr, "VolumeDown")
			volume_level = 0
		}
		log.Println("Volume has been set to 0")
	} else {
		log.Println("Not resetting volume to 0, assuming it is already at 0")
	}

	log.Println("Getting the current active input")

	for address, active := range cec.GetActiveDevices() {
		if (active) && (cec.IsActiveSource(address)) {
			input_str := strings.Split(cec.GetDevicePhysicalAddress(address), ".")[0]
			input_atoi, _ := strconv.Atoi(input_str)
			input_number = int(input_atoi)
		}
	}

	r.Run(options.HTTP.Host + ":" + options.HTTP.Port)
}

func config(c *gin.Context) {
	c.JSON(200, options)
}

func info(c *gin.Context) {
	c.JSON(200, cec.List())
}

func power_on(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.PowerOn(addr)
	c.String(200, "on")
}

func power_off(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	cec.Standby(addr)
	c.String(200, "off")
}

func power_status(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))

	status := cec.GetDevicePowerStatus(addr)
	if status == "on" {
		c.String(200, "on")
	} else if status == "standby" {
		c.String(200, "off")
	} else {
		c.String(500, "invalid power state")
	}
}

func input_status(c *gin.Context) {
	c.String(200, "INPUT HDMI "+strconv.Itoa(input_number))
}

func input_change(c *gin.Context) {
	input := c.Params.ByName("number")
	cec.Transmit("3f:82:" + input + "0:00")
	input_atoi, _ := strconv.Atoi(input)
	input_number = int(input_atoi)
	c.String(200, "INPUT HDMI "+input)
}

func change_channel(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	channel := c.Params.ByName("channel")

	for _, number := range channel {
		cec.Key(addr, "0x2"+string(number))
	}

	c.String(200, channel)
}

func vol_step(c *gin.Context) {
	steps_str := c.Params.ByName("steps")
	steps_atoi, _ := strconv.Atoi(steps_str)
	steps := int(steps_atoi)
	direction := c.Params.ByName("direction")

	for i := 0; i < steps; i++ {
		if direction == "up" {
			addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
			cec.Key(addr, "VolumeUp")
			volume_level = volume_level + steps
		} else if direction == "down" {
			addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
			cec.Key(addr, "VolumeDown")
			volume_level = volume_level - steps
		} else {
			c.String(400, "Invalid direction. Valid directions are up or down.")
		}
	}

	c.String(204, "")
}

func vol_set(c *gin.Context) {
	level_str := c.Params.ByName("level")
	level_atoi, _ := strconv.Atoi(level_str)
	wanted_level := int(level_atoi)

	log.Println("Wanted_level is " + strconv.Itoa(wanted_level) + " and volume_level is " + strconv.Itoa(volume_level))

	if wanted_level > options.Audio.MaxVolume {
		c.String(400, "The maximum volume level is "+strconv.Itoa(options.Audio.MaxVolume))
	} else if wanted_level > volume_level { // Requested level is greater then current volume level
		log.Println("FIRST")
		var final_level = wanted_level - volume_level
		log.Println("Final_level is " + strconv.Itoa(final_level))
		for i := 0; i < final_level; i++ {
			addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
			cec.Key(addr, "VolumeUp")
		}
	} else if wanted_level < volume_level { // Requested level is less than current volume level
		log.Println("SECOND")
		var final_level = volume_level - wanted_level
		log.Println("Final_level is " + strconv.Itoa(final_level))
		for i := 0; i < final_level; i++ {
			addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
			cec.Key(addr, "VolumeDown")
		}
	}

	volume_level = wanted_level

	c.String(200, strconv.Itoa(volume_level))
}

func vol_force_set(c *gin.Context) {
	level_str := c.Params.ByName("level")
	level_atoi, _ := strconv.Atoi(level_str)
	volume_level = int(level_atoi)
	c.String(204, "")
}

func vol_status(c *gin.Context) {
	if is_muted == true {
		c.String(200, "muted")
	} else {
		c.String(200, strconv.Itoa(volume_level))
	}
}

func vol_up(c *gin.Context) {
	if volume_level == options.Audio.MaxVolume {
		c.String(400, "Volume already at maximum")
	} else {
		addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
		cec.Key(addr, "VolumeUp")
		volume_level = volume_level + 1
		c.String(204, "")
	}
}

func vol_down(c *gin.Context) {
	if volume_level == 0 {
		c.String(400, "Volume is already at minimum")
	} else {
		addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)
		cec.Key(addr, "VolumeDown")
		volume_level = volume_level - 1
		c.String(204, "")
	}
}

func vol_mute(c *gin.Context) {
	cec.Mute()
	is_muted = true
	c.String(204, "")
}

func vol_mute_status(c *gin.Context) {
	c.String(200, strconv.FormatBool(is_muted))
}

func vol_reset(c *gin.Context) {
	for i := 0; i < options.Audio.MaxVolume; i++ {
		log.Println("Sending VolumeDown")
		addr := cec.GetLogicalAddressByName(options.Audio.AudioDevice)

		cec.Key(addr, "VolumeDown")
	}
	volume_level = 0
	c.String(200, strconv.Itoa(volume_level))
}

func key(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	key := c.Params.ByName("key")

	cec.Key(addr, key)
	c.String(204, "")
}

func multi_key(c *gin.Context) {
	addr := cec.GetLogicalAddressByName(c.Params.ByName("device"))
	key := c.Params.ByName("key")
	key_two := c.Params.ByName("key2")
	delay, _ := strconv.Atoi(c.Params.ByName("delay"))

	cec.Key(addr, key)
	time.Sleep(time.Duration(delay) * time.Millisecond)
	cec.Key(addr, key_two)
	c.String(204, "")
}

func transmit(c *gin.Context) {
	var commands []string
	c.Bind(&commands)

	for _, val := range commands {
		cec.Transmit(val)
	}
	c.String(204, "")
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	_ "github.com/mattn/go-sqlite3"
	config "libchorizo/config"
	db "libchorizo/db"
	log "libchorizo/log"
	state "libchorizo/state"
	util "libchorizo/util"
	"os"
	"os/exec"
	"parse_update_script"
	"path/filepath"
	"strconv"
	"time"
)

type UpdateScriptResponse struct {
	ret_code      int
	system_id     int
	stdout        string
	stderr        string
	is_start      bool
	is_end        bool
	update_script *parse_update_script.UpdateScript
	db            *sql.DB
	api_url       string
}

// SystemReboot executes a shell command to reboot the host
func SystemReboot(exec_reboot bool) bool {
	// Sleep for 2 seconds to give time for the API post
	if exec_reboot == true {
		cmd := exec.Command("/sbin/shutdown", "-r", "now")
		err := cmd.Run()
		if err != nil {
			panic(err)
		}
	}
	time.Sleep(2 * time.Second)
	return exec_reboot
}

func InterpolateConfigOption(exec_path string, config_item string) (retval string) {
	retval = fmt.Sprintf("%s/%s", exec_path, config_item)
	return
}

// Main entry point
func main() {
	log := log.GetLogger()

	//exec_path, _ := os.Getwd()
	// In https://github.com/rtucker-mozilla/chorizo/issues/15
	// going to specify a specific config file path
	exec_path := "/etc/chorizo"
	HOSTNAME, _ := os.Hostname()
	config, config_err := config.ParseConfig(exec_path)
	if config_err != nil {
		log.Error("Unable to open config file")
	}
	var DB_FILE = InterpolateConfigOption(exec_path, config.Main.Dbfile)
	log.Debug("DB_FILE: ", DB_FILE)
	var CRONFILE = fmt.Sprintf("%s/%s", exec_path, config.Main.Cronfile)
	var SCRIPTPATH = fmt.Sprintf("%s/%s", exec_path, config.Main.Scriptpath)
	var APIURL = config.Main.APIUrl
	system_id, system_id_err := GetSystemId(APIURL, HOSTNAME)
	if system_id_err != nil {
		log.Warn("Could not retrieve system ID")
	}
	log.Debug("System ID:", system_id)
	log.Debug("GUIDHash: ", GUIDHash(HOSTNAME))
	db_created := db.CreateDbIfNotExists(DB_FILE)
	if db_created {
		log.Info("DB Created at path: ", DB_FILE)
	}
	db1, db_open_err := sql.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc", DB_FILE))
	if db_open_err != nil {
		panic("Unable to open existing database")
	}
	var current_locked = false
	start_state, start_state_err := state.GetMostRecentState(db1)
	if start_state.Finished == 0 && start_state.Id > 0 {
		current_locked = true
	}
	if start_state_err != nil && start_state.Finished == 0 {
		if start_state_err.Error() != "sql: no rows in result set" {
			current_locked = true
		}
	}
	log.Debug("At INIT: current_locked:", current_locked)
	if !util.HasScriptPath(SCRIPTPATH) {
		log.Error(fmt.Sprintf("Script Path %s does not exist.", SCRIPTPATH))
		os.Exit(2)
	}
	go db.DBPoll(db1, HOSTNAME, APIURL, 0)
	for {
		log.Debug("In LOOP: current_locked:", current_locked)
		cron_line, cron_err := util.ReadCronFile(CRONFILE)
		if cron_err != nil {
			log.Debug(fmt.Sprintf("%s", cron_err))
			os.Exit(2)
		}

		run_now, run_after, sleep_seconds := EvalCronLine(cron_line)
		if run_now == false && run_after == false && current_locked == false {
			time.Sleep(time.Duration(sleep_seconds) * time.Second)
			continue
		}

		if run_now == false && run_after == true && current_locked == false {
			time.Sleep(time.Duration(sleep_seconds+1) * time.Second)
		}

		scripts, _ := filepath.Glob(fmt.Sprintf("%s/*", SCRIPTPATH))
		UpdateScripts := []parse_update_script.UpdateScript{}

		var run_next = false
		for i := 0; i < len(scripts); i++ {
			script_path := scripts[i]
			if !util.ScriptValid(script_path) {
				continue
			}
			if run_next == false && current_locked && script_path != start_state.Last_script_completed && start_state.Last_script_completed != "" {
				// We are locked from running scripts
				run_next = false
				continue
			} else if current_locked && script_path == start_state.Last_script_completed && start_state.Last_script_completed != "" {
				// We are locked from running scripts
				// The last ran script is the current in the stack
				// unlock and continue to the next runnable script
				run_next = true
				continue
			} else {
				run_next = true
			}
			var uf parse_update_script.UpdateScriptFile
			var us parse_update_script.UpdateScript
			uf.FilePath = script_path
			parse_update_script.ReadFile(&uf)
			us.ParseScript(&uf)
			UpdateScripts = append(UpdateScripts, us)
		}
		exec_chan := make(chan UpdateScriptResponse)
		go ProcessEntry(exec_chan, db1)
		should_reboot := false
		for i := 0; i < len(UpdateScripts); i++ {
			if should_reboot == true {
				continue
			}
			exec_script := UpdateScripts[i].FilePath
			ret_code, stdout, stderr := ExecCommand(exec_script)
			usr := UpdateScriptResponse{}
			usr.ret_code = ret_code
			usr.stderr = stderr
			usr.stdout = stdout
			usr.update_script = &UpdateScripts[i]
			usr.db = db1
			usr.system_id = system_id
			usr.api_url = APIURL
			if i == 0 && !current_locked {
				usr.is_start = true
			} else {
				usr.is_start = false
			}
			if i == len(UpdateScripts)-1 {
				usr.is_end = true
			} else {
				usr.is_end = false
			}
			exec_chan <- usr
			go ProcessEntry(exec_chan, db1)
			exit_code_to_i, exit_code_to_i_err := strconv.Atoi(UpdateScripts[i].ScriptExitCodeReboot)

			if exit_code_to_i_err != nil {
				panic(exit_code_to_i_err)
			}
			if exit_code_to_i == ret_code {
				SystemReboot(true)
				should_reboot = true
			}

		}
		time.Sleep(60 * time.Second)
		current_locked = false
	}
}
<commit_msg>Using config struct directly<commit_after>package main

import (
	"database/sql"
	"fmt"
	_ "github.com/mattn/go-sqlite3"
	config "libchorizo/config"
	db "libchorizo/db"
	log "libchorizo/log"
	state "libchorizo/state"
	util "libchorizo/util"
	"os"
	"os/exec"
	"parse_update_script"
	"path/filepath"
	"strconv"
	"time"
)

type UpdateScriptResponse struct {
	ret_code      int
	system_id     int
	stdout        string
	stderr        string
	is_start      bool
	is_end        bool
	update_script *parse_update_script.UpdateScript
	db            *sql.DB
	api_url       string
}

// SystemReboot executes a shell command to reboot the host
func SystemReboot(exec_reboot bool) bool {
	// Sleep for 2 seconds to give time for the API post
	if exec_reboot == true {
		cmd := exec.Command("/sbin/shutdown", "-r", "now")
		err := cmd.Run()
		if err != nil {
			panic(err)
		}
	}
	time.Sleep(2 * time.Second)
	return exec_reboot
}

func InterpolateConfigOption(exec_path string, config_item string) (retval string) {
	retval = fmt.Sprintf("%s/%s", exec_path, config_item)
	return
}

// Main entry point
func main() {
	log := log.GetLogger()

	//exec_path, _ := os.Getwd()
	// In https://github.com/rtucker-mozilla/chorizo/issues/15
	// going to specify a specific config file path
	exec_path := "/etc/chorizo"
	config := NewConfig(exec_path)
	HOSTNAME, _ := os.Hostname()
	var APIURL = config.Main.APIUrl
	system_id, system_id_err := GetSystemId(APIURL, HOSTNAME)
	if system_id_err != nil {
		log.Warn("Could not retrieve system ID")
	}
	log.Debug("System ID:", system_id)
	log.Debug("GUIDHash: ", GUIDHash(HOSTNAME))
	db_created := db.CreateDbIfNotExists(config.Main.Dbfile)
	if db_created {
		log.Info("DB Created at path: ", config.Main.Dbfile)
	}
	db1, db_open_err := sql.Open("sqlite3", fmt.Sprintf("file:%s?cache=shared&mode=rwc", config.Main.Dbfile))
	if db_open_err != nil {
		panic("Unable to open existing database")
	}
	var current_locked = false
	start_state, start_state_err := state.GetMostRecentState(db1)
	if start_state.Finished == 0 && start_state.Id > 0 {
		current_locked = true
	}
	if start_state_err != nil && start_state.Finished == 0 {
		if start_state_err.Error() != "sql: no rows in result set" {
			current_locked = true
		}
	}
	log.Debug("At INIT: current_locked:", current_locked)
	if !util.HasScriptPath(config.Main.Scriptpath) {
		log.Error(fmt.Sprintf("Script Path %s does not exist.", config.Main.Scriptpath))
		os.Exit(2)
	}
	go db.DBPoll(db1, HOSTNAME, APIURL, 0)
	for {
		log.Debug("In LOOP: current_locked:", current_locked)
		cron_line, cron_err := util.ReadCronFile(config.Main.Cronfile)
		if cron_err != nil {
			log.Debug(fmt.Sprintf("%s", cron_err))
			os.Exit(2)
		}

		run_now, run_after, sleep_seconds := EvalCronLine(cron_line)
		if run_now == false && run_after == false && current_locked == false {
			time.Sleep(time.Duration(sleep_seconds) * time.Second)
			continue
		}

		if run_now == false && run_after == true && current_locked == false {
			time.Sleep(time.Duration(sleep_seconds+1) * time.Second)
		}

		scripts, _ := filepath.Glob(fmt.Sprintf("%s/*", config.Main.Scriptpath))
		UpdateScripts := []parse_update_script.UpdateScript{}

		var run_next = false
		for i := 0; i < len(scripts); i++ {
			script_path := scripts[i]
			if !util.ScriptValid(script_path) {
				continue
			}
			if run_next == false && current_locked && script_path != start_state.Last_script_completed && start_state.Last_script_completed != "" {
				// We are locked from running scripts
				run_next = false
				continue
			} else if current_locked && script_path == start_state.Last_script_completed && start_state.Last_script_completed != "" {
				// We are locked from running scripts
				// The last ran script is the current in the stack
				// unlock and continue to the next runnable script
				run_next = true
				continue
			} else {
				run_next = true
			}
			var uf parse_update_script.UpdateScriptFile
			var us parse_update_script.UpdateScript
			uf.FilePath = script_path
			parse_update_script.ReadFile(&uf)
			us.ParseScript(&uf)
			UpdateScripts = append(UpdateScripts, us)
		}
		exec_chan := make(chan UpdateScriptResponse)
		go ProcessEntry(exec_chan, db1)
		should_reboot := false
		for i := 0; i < len(UpdateScripts); i++ {
			if should_reboot == true {
				continue
			}
			exec_script := UpdateScripts[i].FilePath
			ret_code, stdout, stderr := ExecCommand(exec_script)
			usr := UpdateScriptResponse{}
			usr.ret_code = ret_code
			usr.stderr = stderr
			usr.stdout = stdout
			usr.update_script = &UpdateScripts[i]
			usr.db = db1
			usr.system_id = system_id
			usr.api_url = APIURL
			if i == 0 && !current_locked {
				usr.is_start = true
			} else {
				usr.is_start = false
			}
			if i == len(UpdateScripts)-1 {
				usr.is_end = true
			} else {
				usr.is_end = false
			}
			exec_chan <- usr
			go ProcessEntry(exec_chan, db1)
			exit_code_to_i, exit_code_to_i_err := strconv.Atoi(UpdateScripts[i].ScriptExitCodeReboot)

			if exit_code_to_i_err != nil {
				panic(exit_code_to_i_err)
			}
			if exit_code_to_i == ret_code {
				SystemReboot(true)
				should_reboot = true
			}

		}
		time.Sleep(60 * time.Second)
		current_locked = false
	}
}
<|endoftext|>


<commit_before>package watch

import (
	"io/ioutil"
	"os"
	"testing"
	"time"
)

func equal(a1, a2 []string) bool {
	if len(a1) != len(a2) {
		return false
	}
	for _, a := range a1 {
		if !exist(a2, a) {
			return false
		}
	}
	return true
}

type dummy struct {
	name string
}

func (d *dummy) Name() string {
	return d.name
}

func (d *dummy) Reload() {}

func TestWatch(t *testing.T) {
	tests := []struct {
		paths       []string
		expWatched  []string
		expWatchers []string
	}{
		{
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
		},
		{
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata"},
		},
		{
			[]string{"testdata/dummy.txt", "testdata/test.txt", "testdata"},
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata"},
		},
		// 2 path refer same file or dir but different(e.g abs path and relative path)
	}
	for i, test := range tests {
		watcher := NewWatcher()
		for _, name := range test.paths {
			watcher.Watch(&dummy{name})
		}
		if len(watcher.watched) != len(test.expWatched) {
			t.Errorf("Test %d: Expected len of watched %d, but got %d", i, len(test.expWatched), len(watcher.watched))
		}
		for _, p := range test.expWatched {
			if _, exist := watcher.watched[p]; !exist {
				t.Errorf("Test %d: Expected %s exist in watched", i, p)
			}
		}
		if !equal(test.expWatchers, watcher.watchers) {
			t.Errorf("Test %d: Expected watchers %v, but got %v", i, test.expWatchers, watcher.watchers)
		}
	}
}

func TestUnWatch(t *testing.T) {
	name := "testdata/dummy.txt"
	d := &dummy{name}
	watcher := NewWatcher()
	watcher.Watch(d)
	watcher.UnWatch(d)
	if len(watcher.watched) != 0 {
		t.Errorf("Expected watcheds be empty, but got %s", watcher.watched)
	}
}

func TestUnWatchDirectory(t *testing.T) {
	name := "testdata/dummy.txt"
	dir := "testdata"
	d := &dummy{name}
	d1 := NewWatchedDir(dir)
	watcher := NewWatcher()
	watcher.Watch(d)
	watcher.Watch(d1)
	if !equal(watcher.watchers, []string{"testdata"}) {
		t.Fatalf("Expected watchers be equal to %s, but got %s", watcher.watchers, []string{"testdata"})
	}
	watcher.UnWatch(d1)
	if !equal(watcher.watchers, []string{name}) {
		t.Errorf("Expected watchers be equal to %s, but got %s", watcher.watchers, []string{name})
	}
}

func TestUnWatchOneOfSubscribers(t *testing.T) {
	name := "testdata/dummy.txt"
	d1 := &dummy{name}
	d2 := &dummy{name}
	watcher := NewWatcher()
	watcher.Watch(d1)
	watcher.Watch(d2)
	if len(watcher.watched[name]) != 2 {
		t.Fatalf("Expected watched[%s] length be %d, but got %d", name, 2, len(watcher.watched[name]))
	}
	watcher.UnWatch(d1)
	if !equal(watcher.watchers, []string{name}) {
		t.Errorf("Expected watchers be equal to %s, but got %s", watcher.watchers, []string{name})
	}
	if len(watcher.watched[name]) != 1 {
		t.Errorf("Expected watched[%s] length be %d, but got %d", name, 1, len(watcher.watched[name]))
	}
}

type dumView struct {
	text string
	name string
}

func (d *dumView) Reload() {
	d.text = "Reloaded"
}

func (d *dumView) Name() string {
	return d.name
}

func (d *dumView) Text() string {
	return d.text
}

func TestObserve(t *testing.T) {
	name := "testdata/test.txt"
	watcher := NewWatcher()
	v := &dumView{name: name}
	watcher.Watch(v)
	go watcher.Observe()

	if err := ioutil.WriteFile(name, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text() != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text())
	}
	ioutil.WriteFile(name, []byte(""), 0644)
}

func TestObserveDirectory(t *testing.T) {
	dir := "testdata"
	name := "testdata/test.txt"
	watcher := NewWatcher()
	v := &dumView{name: name}
	watcher.Watch(v)
	watcher.Watch(NewWatchedDir(dir))
	go watcher.Observe()

	if !equal(watcher.watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{"testdata"}, watcher.watchers)
	}
	if err := ioutil.WriteFile(name, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text() != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text())
	}
	ioutil.WriteFile(name, []byte(""), 0644)
}

func TestCreateEvent(t *testing.T) {
	name := "testdata/new.txt"
	watcher := NewWatcher()
	v := &dumView{name: name}
	watcher.Watch(v)
	go watcher.Observe()

	if !equal(watcher.watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{name}, watcher.watchers)
	}
	if err := ioutil.WriteFile(name, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text() != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text())
	}
	os.Remove(name)
}

func TestDeleteEvent(t *testing.T) {
	name := "testdata/dummy.txt"
	watcher := NewWatcher()
	v := &dumView{name: name}
	watcher.Watch(v)
	go watcher.Observe()

	os.Remove(name)
	time.Sleep(time.Millisecond * 50)
	if !equal(watcher.watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{"testdata"}, watcher.watchers)
	}
	if v.Text() != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text())
	}
	ioutil.WriteFile(name, []byte(""), 0644)
}

func TestRenameEvent(t *testing.T) {
	name := "testdata/dummy.txt"
	watcher := NewWatcher()
	v := &dumView{name: name}
	watcher.Watch(v)
	go watcher.Observe()

	os.Rename(name, "testdata/rename.txt")
	time.Sleep(time.Millisecond * 50)
	if v.Text() != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text())
	}
	os.Rename("testdata/rename.txt", name)
}

func TestExist(t *testing.T) {
	test := struct {
		array []string
		elms  []string
		exps  []bool
	}{
		[]string{"a", "b", "c", "d"},
		[]string{"a", "t", "A"},
		[]bool{true, false, false},
	}
	for i, exp := range test.exps {
		if exist(test.array, test.elms[i]) != exp {
			t.Errorf("Expected in %v exist result of element %s be %v, but got %v", test.array, test.elms[i], exp, exist(test.array, test.elms[i]))
		}
	}
}

func TestRemove(t *testing.T) {
	tests := []struct {
		slice  []string
		remove string
		exp    []string
	}{
		{
			[]string{"a", "b", "c"},
			"a",
			[]string{"c", "b"},
		},
		{
			[]string{"a", "b", "c"},
			"k",
			[]string{"a", "b", "c"},
		},
	}
	for i, test := range tests {
		if exp := remove(test.slice, test.remove); !equal(exp, test.exp) {
			t.Errorf("Test %d: Expected %v be equal to %v", i, exp, test.exp)
		}
	}
}
<commit_msg>Fixed data races in watch_test<commit_after>package watch

import (
	"io/ioutil"
	"os"
	"sync"
	"testing"
	"time"
)

func equal(a1, a2 []string) bool {
	if len(a1) != len(a2) {
		return false
	}
	for _, a := range a1 {
		if !exist(a2, a) {
			return false
		}
	}
	return true
}

type dummy struct {
	name string
}

func (d *dummy) Name() string {
	return d.name
}

func (d *dummy) Reload() {}

func TestWatch(t *testing.T) {
	tests := []struct {
		paths       []string
		expWatched  []string
		expWatchers []string
	}{
		{
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata/dummy.txt", "testdata/test.txt"},
		},
		{
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata"},
		},
		{
			[]string{"testdata/dummy.txt", "testdata/test.txt", "testdata"},
			[]string{"testdata", "testdata/dummy.txt", "testdata/test.txt"},
			[]string{"testdata"},
		},
		// 2 path refer same file or dir but different(e.g abs path and relative path)
	}
	for i, test := range tests {
		watcher := NewWatcher()
		for _, name := range test.paths {
			watcher.Watch(&dummy{name})
		}
		if len(watcher.watched) != len(test.expWatched) {
			t.Errorf("Test %d: Expected len of watched %d, but got %d", i, len(test.expWatched), len(watcher.watched))
		}
		for _, p := range test.expWatched {
			if _, exist := watcher.watched[p]; !exist {
				t.Errorf("Test %d: Expected %s exist in watched", i, p)
			}
		}
		if !equal(test.expWatchers, watcher.watchers) {
			t.Errorf("Test %d: Expected watchers %v, but got %v", i, test.expWatchers, watcher.watchers)
		}
	}
}

func TestUnWatch(t *testing.T) {
	name := "testdata/dummy.txt"
	d := &dummy{name}
	watcher := NewWatcher()
	watcher.Watch(d)
	watcher.UnWatch(d)
	if len(watcher.watched) != 0 {
		t.Errorf("Expected watcheds be empty, but got %s", watcher.watched)
	}
}

func TestUnWatchDirectory(t *testing.T) {
	name := "testdata/dummy.txt"
	dir := "testdata"
	d := &dummy{name}
	d1 := NewWatchedDir(dir)
	watcher := NewWatcher()
	watcher.Watch(d)
	watcher.Watch(d1)
	if !equal(watcher.watchers, []string{"testdata"}) {
		t.Fatalf("Expected watchers be equal to %s, but got %s", watcher.watchers, []string{"testdata"})
	}
	watcher.UnWatch(d1)
	if !equal(watcher.watchers, []string{name}) {
		t.Errorf("Expected watchers be equal to %s, but got %s", watcher.watchers, []string{name})
	}
}

func TestUnWatchOneOfSubscribers(t *testing.T) {
	name := "testdata/dummy.txt"
	d1 := &dummy{name}
	d2 := &dummy{name}
	watcher := NewWatcher()
	watcher.Watch(d1)
	watcher.Watch(d2)
	if len(watcher.watched[name]) != 2 {
		t.Fatalf("Expected watched[%s] length be %d, but got %d", name, 2, len(watcher.watched[name]))
	}
	watcher.UnWatch(d1)
	if !equal(watcher.watchers, []string{name}) {
		t.Errorf("Expected watchers be equal to %s, but got %s", watcher.watchers, []string{name})
	}
	if len(watcher.watched[name]) != 1 {
		t.Errorf("Expected watched[%s] length be %d, but got %d", name, 1, len(watcher.watched[name]))
	}
}

type dumView struct {
	text string
	name string
	lock sync.Mutex
}

func (d *dumView) Reload() {
	d.lock.Lock()
	defer d.lock.Unlock()
	d.text = "Reloaded"
}

func (d *dumView) Name() string {
	return d.name
}

func (d *dumView) Text() string {
	d.lock.Lock()
	defer d.lock.Unlock()
	return d.text
}

func TestObserve(t *testing.T) {
	name := "testdata/test.txt"
	watcher := NewWatcher()
	v := &dumView{name: name}
	watcher.Watch(v)
	go watcher.Observe()

	if err := ioutil.WriteFile(name, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text() != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text())
	}
	ioutil.WriteFile(name, []byte(""), 0644)
}

func TestObserveDirectory(t *testing.T) {
	dir := "testdata"
	name := "testdata/test.txt"
	watcher := NewWatcher()
	v := &dumView{name: name}
	watcher.Watch(v)
	watcher.Watch(NewWatchedDir(dir))
	go watcher.Observe()

	if !equal(watcher.watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{"testdata"}, watcher.watchers)
	}
	if err := ioutil.WriteFile(name, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text() != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text())
	}
	ioutil.WriteFile(name, []byte(""), 0644)
}

func TestCreateEvent(t *testing.T) {
	name := "testdata/new.txt"
	watcher := NewWatcher()
	v := &dumView{name: name}
	watcher.Watch(v)
	go watcher.Observe()

	if !equal(watcher.watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{name}, watcher.watchers)
	}
	if err := ioutil.WriteFile(name, []byte("test"), 0644); err != nil {
		t.Fatalf("WriteFile error: %s", err)
	}
	time.Sleep(time.Millisecond * 50)
	if v.Text() != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text())
	}
	os.Remove(name)
}

func TestDeleteEvent(t *testing.T) {
	name := "testdata/dummy.txt"
	watcher := NewWatcher()
	v := &dumView{name: name}
	watcher.Watch(v)
	go watcher.Observe()

	os.Remove(name)
	time.Sleep(time.Millisecond * 50)
	watcher.lock.Lock()
	if !equal(watcher.watchers, []string{"testdata"}) {
		t.Errorf("Expected watchers be equal to %v, but got %v", []string{"testdata"}, watcher.watchers)
	}
	watcher.lock.Unlock()
	if v.Text() != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text())
	}
	ioutil.WriteFile(name, []byte(""), 0644)
}

func TestRenameEvent(t *testing.T) {
	name := "testdata/dummy.txt"
	watcher := NewWatcher()
	v := &dumView{name: name}
	watcher.Watch(v)
	go watcher.Observe()

	os.Rename(name, "testdata/rename.txt")
	time.Sleep(time.Millisecond * 50)
	if v.Text() != "Reloaded" {
		t.Errorf("Expected dumView Text %s, but got %s", "Reloaded", v.Text())
	}
	os.Rename("testdata/rename.txt", name)
}

func TestExist(t *testing.T) {
	test := struct {
		array []string
		elms  []string
		exps  []bool
	}{
		[]string{"a", "b", "c", "d"},
		[]string{"a", "t", "A"},
		[]bool{true, false, false},
	}
	for i, exp := range test.exps {
		if exist(test.array, test.elms[i]) != exp {
			t.Errorf("Expected in %v exist result of element %s be %v, but got %v", test.array, test.elms[i], exp, exist(test.array, test.elms[i]))
		}
	}
}

func TestRemove(t *testing.T) {
	tests := []struct {
		slice  []string
		remove string
		exp    []string
	}{
		{
			[]string{"a", "b", "c"},
			"a",
			[]string{"c", "b"},
		},
		{
			[]string{"a", "b", "c"},
			"k",
			[]string{"a", "b", "c"},
		},
	}
	for i, test := range tests {
		if exp := remove(test.slice, test.remove); !equal(exp, test.exp) {
			t.Errorf("Test %d: Expected %v be equal to %v", i, exp, test.exp)
		}
	}
}
<|endoftext|>


<commit_before>package ui

import (
	"io"
	"net/http"

	"os"

	"github.com/rancher/norman/parse"
	"github.com/sirupsen/logrus"
)

var uiURL = "https://releases.rancher.com/ui/latest2/index.html"

func Content() http.Handler {
	return http.FileServer(http.Dir("ui"))
}

func UI(next http.Handler) http.Handler {
	local := false
	_, err := os.Stat("ui/index.html")
	if err == nil {
		local = true
	}

	return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
		if parse.IsBrowser(req, true) {
			if local {
				http.ServeFile(resp, req, "ui/index.html")
			} else {
				ui(resp, req)
			}
		} else {
			next.ServeHTTP(resp, req)
		}
	})
}

func ui(resp http.ResponseWriter, req *http.Request) {
	if err := serveIndex(resp, req); err != nil {
		logrus.Errorf("failed to serve UI: %v", err)
		resp.WriteHeader(500)
	}
}

func serveIndex(resp http.ResponseWriter, req *http.Request) error {
	r, err := http.Get(uiURL)
	if err != nil {
		return err
	}
	defer r.Body.Close()

	_, err = io.Copy(resp, r.Body)
	return err
}
<commit_msg>master builds should use latest2 UI<commit_after>package ui

import (
	"io"
	"net/http"

	"os"

	"github.com/rancher/norman/parse"
	"github.com/rancher/rancher/pkg/settings"
	"github.com/sirupsen/logrus"
)

var uiURL = "https://releases.rancher.com/ui/latest2/index.html"

func Content() http.Handler {
	return http.FileServer(http.Dir("ui"))
}

func UI(next http.Handler) http.Handler {
	local := false
	_, err := os.Stat("ui/index.html")
	if err == nil {
		local = true
	}

	if local && settings.ServerVersion.Get() == "master" {
		local = false
	}

	return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
		if parse.IsBrowser(req, true) {
			if local {
				http.ServeFile(resp, req, "ui/index.html")
			} else {
				ui(resp, req)
			}
		} else {
			next.ServeHTTP(resp, req)
		}
	})
}

func ui(resp http.ResponseWriter, req *http.Request) {
	if err := serveIndex(resp, req); err != nil {
		logrus.Errorf("failed to serve UI: %v", err)
		resp.WriteHeader(500)
	}
}

func serveIndex(resp http.ResponseWriter, req *http.Request) error {
	r, err := http.Get(uiURL)
	if err != nil {
		return err
	}
	defer r.Body.Close()

	_, err = io.Copy(resp, r.Body)
	return err
}
<|endoftext|>