<commit_before>package sdbot

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/mikopits/sdbot/utilities"
)

// LoginTime contains the unix login times as values to each particular room
// the bot has joined. This allows us to ignore messages that occurred before
// the bot has logged in.
var LoginTime map[string]int = make(map[string]int)

var interrupt chan os.Signal

// Connection represents the connection to the websocket.
type Connection struct {
	Bot       *Bot
	Connected bool
	LoginTime map[string]int
	conn      *websocket.Conn
	queue     chan string
}

// NewConnection creates a new connection for a bot.
func NewConnection(b *Bot) *Connection {
	return &Connection{
		Bot:       b,
		LoginTime: make(map[string]int),
		queue:     make(chan string, 64),
	}
}

// TODO Automatic reconnection to the socket.
// Connects to the server websocket and initialize reading and writing threads.
func (c *Connection) connect() {
	host := c.Bot.Config.Server + ":" + c.Bot.Config.Port
	u := url.URL{Scheme: "ws", Host: host, Path: "/showdown/websocket"}
	Info(fmt.Sprintf("Connecting to %s...", u.String()))

	var res *http.Response
	var err error
	dialer := websocket.DefaultDialer
	c.conn, res, err = dialer.Dial(u.String(), http.Header{
		"Origin": []string{"https://play.pokemonshowdown.com"},
	})
	CheckErr(err)

	c.Connected = true

	defer res.Body.Close()
	defer c.conn.Close()

	var wg sync.WaitGroup
	wg.Add(1)
	c.startReading()
	c.startSending()
	defer wg.Done()
	wg.Wait()
}

// ErrUnexpectedMessageType is returned when we receive a message from the
// websocket that isn't a websocket.TextMessage or a normal closure.
var ErrUnexpectedMessageType = errors.New("sdbot: unexpected message type from the websocket")

// Listens for messages from the websocket.
// TODO gracefully break out of this loop on interrupt.
func (c *Connection) startReading() {
	go func() {
		for {
			msgType, msg, err := c.conn.ReadMessage()
			CheckErr(err)

			if msgType != websocket.TextMessage || msgType == -1 {
				Fatalf("sdbot: got message type %d from server", msgType)
			}

			var room string
			messages := strings.Split(string(msg), "\n")

			if string(messages[0][0]) == ">" {
				room, messages = messages[0], messages[1:]
			}

			for _, rawmessage := range messages {
				s, err := utilities.Encode(rawmessage, utilities.UTF8)
				CheckErr(err)
				c.parse(fmt.Sprintf("%s\n%s", room, s))
			}
		}
	}()
}

// Initiates the message sending goroutine.
func (c *Connection) startSending() {
	go func() {
		interrupt = make(chan os.Signal, 1)
		signal.Notify(interrupt, os.Interrupt)
		done := make(chan struct{})

		for {
			select {
			case msg := <-c.queue:
				send(c, msg)
				ms := 1000.0 / c.Bot.Config.MessagesPerSecond
				time.Sleep(time.Duration(ms) * time.Millisecond)
			case <-interrupt:
				Warn("Process was interrupted. Closing connection...")

				// Send a close frame and wait for the server to close the connection.
				err := c.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
				if err != nil {
					Error(err)
					return
				}

				// FIXME None of this seems to work as intended.
				// Close all plugin goroutines gracefully.
				var wg sync.WaitGroup
				wg.Add(1)
				c.Bot.StopTimedPlugins()
				c.Bot.UnregisterPlugins()
				defer wg.Done()
				wg.Wait()

				select {
				case <-done:
				case <-time.After(time.Second):
					os.Exit(0)
				}
				c.conn.Close()
				c.Connected = false
				return
			}
		}
	}()
}

// QueueMessage adds a message to the outgoing queue.
func (c *Connection) QueueMessage(msg string) {
	c.queue <- msg
}

// Sends a message upstream to the websocket ignoring the message queue.
func send(c *Connection, s string) {
	enc, err := utilities.Encode(s, utilities.UTF8)
	CheckErr(err)
	logOutgoingAll(loggers, s)

	err = c.conn.WriteMessage(websocket.TextMessage, []byte(enc))
	CheckErr(err)
}

// Parses the message and difers it to a relevant handler.
func (c *Connection) parse(s string) {
	m := NewMessage(s, c.Bot)

	// Log the incoming messages to every logger.
	logIncomingAll(loggers, s)

	cmd := strings.ToLower(m.Command)

	if cmd == ":" {
		c.LoginTime[m.Room.Name] = m.Timestamp
	}

	callHandler(handlers, cmd, m)
}
<commit_msg>connection: log fatal message when websocket does not give a message of type websocket.TextMessage<commit_after>package sdbot

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/websocket"
	"github.com/mikopits/sdbot/utilities"
)

// LoginTime contains the unix login times as values to each particular room
// the bot has joined. This allows us to ignore messages that occurred before
// the bot has logged in.
var LoginTime map[string]int = make(map[string]int)

var interrupt chan os.Signal

// Connection represents the connection to the websocket.
type Connection struct {
	Bot       *Bot
	Connected bool
	LoginTime map[string]int
	conn      *websocket.Conn
	queue     chan string
}

// NewConnection creates a new connection for a bot.
func NewConnection(b *Bot) *Connection {
	return &Connection{
		Bot:       b,
		LoginTime: make(map[string]int),
		queue:     make(chan string, 64),
	}
}

// TODO Automatic reconnection to the socket.
// Connects to the server websocket and initialize reading and writing threads.
func (c *Connection) connect() {
	host := c.Bot.Config.Server + ":" + c.Bot.Config.Port
	u := url.URL{Scheme: "ws", Host: host, Path: "/showdown/websocket"}
	Info(fmt.Sprintf("Connecting to %s...", u.String()))

	var res *http.Response
	var err error
	dialer := websocket.DefaultDialer
	c.conn, res, err = dialer.Dial(u.String(), http.Header{
		"Origin": []string{"https://play.pokemonshowdown.com"},
	})
	CheckErr(err)

	c.Connected = true

	defer res.Body.Close()
	defer c.conn.Close()

	var wg sync.WaitGroup
	wg.Add(1)
	c.startReading()
	c.startSending()
	defer wg.Done()
	wg.Wait()
}

// ErrUnexpectedMessageType is returned when we receive a message from the
// websocket that isn't a websocket.TextMessage or a normal closure.
var ErrUnexpectedMessageType = errors.New("sdbot: unexpected message type from the websocket")

// Listens for messages from the websocket.
// TODO gracefully break out of this loop on interrupt.
func (c *Connection) startReading() {
	go func() {
		for {
			msgType, msg, err := c.conn.ReadMessage()
			CheckErr(err)

			if msgType != websocket.TextMessage {
				Fatalf("sdbot: got message type %d from websocket", msgType)
			}

			var room string
			messages := strings.Split(string(msg), "\n")

			if string(messages[0][0]) == ">" {
				room, messages = messages[0], messages[1:]
			}

			for _, rawmessage := range messages {
				s, err := utilities.Encode(rawmessage, utilities.UTF8)
				CheckErr(err)
				c.parse(fmt.Sprintf("%s\n%s", room, s))
			}
		}
	}()
}

// Initiates the message sending goroutine.
func (c *Connection) startSending() {
	go func() {
		interrupt = make(chan os.Signal, 1)
		signal.Notify(interrupt, os.Interrupt)
		done := make(chan struct{})

		for {
			select {
			case msg := <-c.queue:
				send(c, msg)
				ms := 1000.0 / c.Bot.Config.MessagesPerSecond
				time.Sleep(time.Duration(ms) * time.Millisecond)
			case <-interrupt:
				Warn("Process was interrupted. Closing connection...")

				// Send a close frame and wait for the server to close the connection.
				err := c.conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
				if err != nil {
					Error(err)
					return
				}

				// FIXME None of this seems to work as intended.
				// Close all plugin goroutines gracefully.
				var wg sync.WaitGroup
				wg.Add(1)
				c.Bot.StopTimedPlugins()
				c.Bot.UnregisterPlugins()
				defer wg.Done()
				wg.Wait()

				select {
				case <-done:
				case <-time.After(time.Second):
					os.Exit(0)
				}
				c.conn.Close()
				c.Connected = false
				return
			}
		}
	}()
}

// QueueMessage adds a message to the outgoing queue.
func (c *Connection) QueueMessage(msg string) {
	c.queue <- msg
}

// Sends a message upstream to the websocket ignoring the message queue.
func send(c *Connection, s string) {
	enc, err := utilities.Encode(s, utilities.UTF8)
	CheckErr(err)
	logOutgoingAll(loggers, s)

	err = c.conn.WriteMessage(websocket.TextMessage, []byte(enc))
	CheckErr(err)
}

// Parses the message and difers it to a relevant handler.
func (c *Connection) parse(s string) {
	m := NewMessage(s, c.Bot)

	// Log the incoming messages to every logger.
	logIncomingAll(loggers, s)

	cmd := strings.ToLower(m.Command)

	if cmd == ":" {
		c.LoginTime[m.Room.Name] = m.Timestamp
	}

	callHandler(handlers, cmd, m)
}
<|endoftext|>


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

package fuse

import (
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"runtime"
	"sync"
	"syscall"

	"golang.org/x/net/context"

	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/internal/buffer"
	"github.com/jacobsa/fuse/internal/fusekernel"
	"github.com/jacobsa/fuse/internal/fuseshim"
)

// Ask the Linux kernel for larger read requests.
//
// As of 2015-03-26, the behavior in the kernel is:
//
//  *  (http://goo.gl/bQ1f1i, http://goo.gl/HwBrR6) Set the local variable
//     ra_pages to be init_response->max_readahead divided by the page size.
//
//  *  (http://goo.gl/gcIsSh, http://goo.gl/LKV2vA) Set
//     backing_dev_info::ra_pages to the min of that value and what was sent
//     in the request's max_readahead field.
//
//  *  (http://goo.gl/u2SqzH) Use backing_dev_info::ra_pages when deciding
//     how much to read ahead.
//
//  *  (http://goo.gl/JnhbdL) Don't read ahead at all if that field is zero.
//
// Reading a page at a time is a drag. Ask for a larger size.
const maxReadahead = 1 << 20

// A connection to the fuse kernel process.
type Connection struct {
	debugLogger *log.Logger
	errorLogger *log.Logger
	wrapped     *fuseshim.Conn

	// The device through which we're talking to the kernel, and the protocol
	// version that we're using to talk to it.
	dev      *os.File
	protocol fusekernel.Protocol

	// The context from which all op contexts inherit.
	parentCtx context.Context

	// For logging purposes only.
	nextOpID uint32

	mu sync.Mutex

	// A map from fuse "unique" request ID (*not* the op ID for logging used
	// above) to a function that cancel's its associated context.
	//
	// GUARDED_BY(mu)
	cancelFuncs map[uint64]func()
}

// Create a connection wrapping the supplied file descriptor connected to the
// kernel. You must eventually call c.close().
//
// The loggers may be nil.
func newConnection(
	parentCtx context.Context,
	debugLogger *log.Logger,
	errorLogger *log.Logger,
	dev *os.File) (c *Connection, err error) {
	// Create an initialized a wrapped fuseshim connection.
	wrapped := &fuseshim.Conn{
		Dev: dev,
	}

	err = fuseshim.InitMount(wrapped, maxReadahead, 0)
	if err != nil {
		err = fmt.Errorf("fuseshim.InitMount: %v", err)
		return
	}

	// Create an object wrapping it.
	c = &Connection{
		debugLogger: debugLogger,
		errorLogger: errorLogger,
		wrapped:     wrapped,
		dev:         wrapped.Dev,
		protocol:    wrapped.Protocol(),
		parentCtx:   parentCtx,
		cancelFuncs: make(map[uint64]func()),
	}

	return
}

// Log information for an operation with the given ID. calldepth is the depth
// to use when recovering file:line information with runtime.Caller.
func (c *Connection) debugLog(
	opID uint32,
	calldepth int,
	format string,
	v ...interface{}) {
	if c.debugLogger == nil {
		return
	}

	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"Op 0x%08x %24s] %v",
		opID,
		fileLine,
		fmt.Sprintf(format, v...))

	// Print it.
	c.debugLogger.Println(msg)
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) recordCancelFunc(
	fuseID uint64,
	f func()) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.cancelFuncs[fuseID]; ok {
		panic(fmt.Sprintf("Already have cancel func for request %v", fuseID))
	}

	c.cancelFuncs[fuseID] = f
}

// Set up state for an op that is about to be returned to the user, given its
// underlying fuse opcode and request ID.
//
// Return a context that should be used for the op.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) beginOp(
	opCode uint32,
	fuseID uint64) (ctx context.Context) {
	// Start with the parent context.
	ctx = c.parentCtx

	// Set up a cancellation function.
	//
	// Special case: On Darwin, osxfuse aggressively reuses "unique" request IDs.
	// This matters for Forget requests, which have no reply associated and
	// therefore have IDs that are immediately eligible for reuse. For these, we
	// should not record any state keyed on their ID.
	//
	// Cf. https://github.com/osxfuse/osxfuse/issues/208
	if opCode != fusekernel.OpForget {
		var cancel func()
		ctx, cancel = context.WithCancel(ctx)
		c.recordCancelFunc(fuseID, cancel)
	}

	return
}

// Clean up all state associated with an op to which the user has responded,
// given its underlying fuse opcode and request ID. This must be called before
// a response is sent to the kernel, to avoid a race where the request's ID
// might be reused by osxfuse.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) finishOp(
	opCode uint32,
	fuseID uint64) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// Even though the op is finished, context.WithCancel requires us to arrange
	// for the cancellation function to be invoked. We also must remove it from
	// our map.
	//
	// Special case: we don't do this for Forget requests. See the note in
	// beginOp above.
	if opCode != fusekernel.OpForget {
		cancel, ok := c.cancelFuncs[fuseID]
		if !ok {
			panic(fmt.Sprintf("Unknown request ID in finishOp: %v", fuseID))
		}

		cancel()
		delete(c.cancelFuncs, fuseID)
	}
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) handleInterrupt(fuseID uint64) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// NOTE(jacobsa): fuse.txt in the Linux kernel documentation
	// (https://goo.gl/H55Dnr) defines the kernel <-> userspace protocol for
	// interrupts.
	//
	// In particular, my reading of it is that an interrupt request cannot be
	// delivered to userspace before the original request. The part about the
	// race and EAGAIN appears to be aimed at userspace programs that
	// concurrently process requests (cf. http://goo.gl/BES2rs).
	//
	// So in this method if we can't find the ID to be interrupted, it means that
	// the request has already been replied to.
	//
	// Cf. https://github.com/osxfuse/osxfuse/issues/208
	// Cf. http://comments.gmane.org/gmane.comp.file-systems.fuse.devel/14675
	cancel, ok := c.cancelFuncs[fuseID]
	if !ok {
		return
	}

	cancel()
}

func (c *Connection) allocateInMessage() (m *buffer.InMessage) {
	// TODO(jacobsa): Use a freelist.
	m = new(buffer.InMessage)
	return
}

func (c *Connection) destroyInMessage(m *buffer.InMessage) {
	// TODO(jacobsa): Use a freelist.
}

// Read the next message from the kernel. The message must later be destroyed
// using destroyInMessage.
func (c *Connection) readMessage() (m *buffer.InMessage, err error) {
	// Allocate a message.
	m = c.allocateInMessage()

	// Loop past transient errors.
	for {
		// Attempt a reaed.
		err = m.Init(c.dev)

		// Special cases:
		//
		//  *  ENODEV means fuse has hung up.
		//
		//  *  EINTR means we should try again. (This seems to happen often on
		//     OS X, cf. http://golang.org/issue/11180)
		//
		if pe, ok := err.(*os.PathError); ok {
			switch pe.Err {
			case syscall.ENODEV:
				err = io.EOF

			case syscall.EINTR:
				err = nil
				continue
			}
		}

		if err != nil {
			c.destroyInMessage(m)
			m = nil
			return
		}

		return
	}
}

// Write the supplied message to the kernel.
func (c *Connection) writeMessage(msg []byte) (err error) {
	// Avoid the retry loop in os.File.Write.
	n, err := syscall.Write(int(c.dev.Fd()), msg)
	if err != nil {
		return
	}

	if n != len(msg) {
		err = fmt.Errorf("Wrote %d bytes; expected %d", n, len(msg))
		return
	}

	return
}

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	// Keep going until we find a request we know how to convert.
	for {
		// Read the next message from the kernel.
		var m *buffer.InMessage
		m, err = c.readMessage()
		if err != nil {
			return
		}

		// Choose an ID for this operation for the purposes of logging.
		opID := c.nextOpID
		c.nextOpID++

		// Set up op dependencies.
		opCtx := c.beginOp(m.Header().Opcode, m.Header().Unique)

		var debugLogForOp func(int, string, ...interface{})
		if c.debugLogger != nil {
			debugLogForOp = func(calldepth int, format string, v ...interface{}) {
				c.debugLog(opID, calldepth+1, format, v...)
			}
		}

		sendReply := func(
			op fuseops.Op,
			fuseID uint64,
			replyMsg []byte,
			opErr error) (err error) {
			// Make sure we destroy the message, as required by readMessage.
			defer c.destroyInMessage(m)

			// Clean up state for this op.
			c.finishOp(m.Header().Opcode, m.Header().Unique)

			// Debug logging
			if c.debugLogger != nil {
				if opErr == nil {
					op.Logf("-> OK: %s", op.DebugString())
				} else {
					op.Logf("-> error: %v", opErr)
				}
			}

			// Error logging
			if opErr != nil && c.errorLogger != nil {
				c.errorLogger.Printf("(%s) error: %v", op.ShortDesc(), opErr)
			}

			// Send the reply to the kernel.
			err = c.writeMessage(replyMsg)
			if err != nil {
				err = fmt.Errorf("writeMessage: %v", err)
				return
			}

			return
		}

		// Convert the message to an Op.
		op, err = fuseops.Convert(
			opCtx,
			m,
			c.protocol,
			debugLogForOp,
			c.errorLogger,
			sendReply)

		if err != nil {
			err = fmt.Errorf("fuseops.Convert: %v", err)
			return
		}

		// Log the receipt of the operation.
		c.debugLog(opID, 1, "<- %v", op.ShortDesc())

		// Special case: responding to statfs is required to make mounting work on
		// OS X. We don't currently expose the capability for the file system to
		// intercept this.
		if _, ok := op.(*fuseops.InternalStatFSOp); ok {
			op.Respond(nil)
			continue
		}

		// Special case: handle interrupt requests.
		if interruptOp, ok := op.(*fuseops.InternalInterruptOp); ok {
			c.handleInterrupt(interruptOp.FuseID)
			continue
		}

		return
	}
}

func (c *Connection) waitForReady() (err error) {
	<-c.wrapped.Ready
	err = c.wrapped.MountError
	return
}

// Close the connection. Must not be called until operations that were read
// from the connection have been responded to.
func (c *Connection) close() (err error) {
	// Posix doesn't say that close can be called concurrently with read or
	// write, but luckily we exclude the possibility of a race by requiring the
	// user to respond to all ops first.
	err = c.dev.Close()
	return
}
<commit_msg>Killed some dead code.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package fuse

import (
	"fmt"
	"io"
	"log"
	"os"
	"path"
	"runtime"
	"sync"
	"syscall"

	"golang.org/x/net/context"

	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/fuse/internal/buffer"
	"github.com/jacobsa/fuse/internal/fusekernel"
	"github.com/jacobsa/fuse/internal/fuseshim"
)

// Ask the Linux kernel for larger read requests.
//
// As of 2015-03-26, the behavior in the kernel is:
//
//  *  (http://goo.gl/bQ1f1i, http://goo.gl/HwBrR6) Set the local variable
//     ra_pages to be init_response->max_readahead divided by the page size.
//
//  *  (http://goo.gl/gcIsSh, http://goo.gl/LKV2vA) Set
//     backing_dev_info::ra_pages to the min of that value and what was sent
//     in the request's max_readahead field.
//
//  *  (http://goo.gl/u2SqzH) Use backing_dev_info::ra_pages when deciding
//     how much to read ahead.
//
//  *  (http://goo.gl/JnhbdL) Don't read ahead at all if that field is zero.
//
// Reading a page at a time is a drag. Ask for a larger size.
const maxReadahead = 1 << 20

// A connection to the fuse kernel process.
type Connection struct {
	debugLogger *log.Logger
	errorLogger *log.Logger
	wrapped     *fuseshim.Conn

	// The device through which we're talking to the kernel, and the protocol
	// version that we're using to talk to it.
	dev      *os.File
	protocol fusekernel.Protocol

	// The context from which all op contexts inherit.
	parentCtx context.Context

	// For logging purposes only.
	nextOpID uint32

	mu sync.Mutex

	// A map from fuse "unique" request ID (*not* the op ID for logging used
	// above) to a function that cancel's its associated context.
	//
	// GUARDED_BY(mu)
	cancelFuncs map[uint64]func()
}

// Create a connection wrapping the supplied file descriptor connected to the
// kernel. You must eventually call c.close().
//
// The loggers may be nil.
func newConnection(
	parentCtx context.Context,
	debugLogger *log.Logger,
	errorLogger *log.Logger,
	dev *os.File) (c *Connection, err error) {
	// Create an initialized a wrapped fuseshim connection.
	wrapped := &fuseshim.Conn{
		Dev: dev,
	}

	err = fuseshim.InitMount(wrapped, maxReadahead, 0)
	if err != nil {
		err = fmt.Errorf("fuseshim.InitMount: %v", err)
		return
	}

	// Create an object wrapping it.
	c = &Connection{
		debugLogger: debugLogger,
		errorLogger: errorLogger,
		wrapped:     wrapped,
		dev:         wrapped.Dev,
		protocol:    wrapped.Protocol(),
		parentCtx:   parentCtx,
		cancelFuncs: make(map[uint64]func()),
	}

	return
}

// Log information for an operation with the given ID. calldepth is the depth
// to use when recovering file:line information with runtime.Caller.
func (c *Connection) debugLog(
	opID uint32,
	calldepth int,
	format string,
	v ...interface{}) {
	if c.debugLogger == nil {
		return
	}

	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"Op 0x%08x %24s] %v",
		opID,
		fileLine,
		fmt.Sprintf(format, v...))

	// Print it.
	c.debugLogger.Println(msg)
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) recordCancelFunc(
	fuseID uint64,
	f func()) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.cancelFuncs[fuseID]; ok {
		panic(fmt.Sprintf("Already have cancel func for request %v", fuseID))
	}

	c.cancelFuncs[fuseID] = f
}

// Set up state for an op that is about to be returned to the user, given its
// underlying fuse opcode and request ID.
//
// Return a context that should be used for the op.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) beginOp(
	opCode uint32,
	fuseID uint64) (ctx context.Context) {
	// Start with the parent context.
	ctx = c.parentCtx

	// Set up a cancellation function.
	//
	// Special case: On Darwin, osxfuse aggressively reuses "unique" request IDs.
	// This matters for Forget requests, which have no reply associated and
	// therefore have IDs that are immediately eligible for reuse. For these, we
	// should not record any state keyed on their ID.
	//
	// Cf. https://github.com/osxfuse/osxfuse/issues/208
	if opCode != fusekernel.OpForget {
		var cancel func()
		ctx, cancel = context.WithCancel(ctx)
		c.recordCancelFunc(fuseID, cancel)
	}

	return
}

// Clean up all state associated with an op to which the user has responded,
// given its underlying fuse opcode and request ID. This must be called before
// a response is sent to the kernel, to avoid a race where the request's ID
// might be reused by osxfuse.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) finishOp(
	opCode uint32,
	fuseID uint64) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// Even though the op is finished, context.WithCancel requires us to arrange
	// for the cancellation function to be invoked. We also must remove it from
	// our map.
	//
	// Special case: we don't do this for Forget requests. See the note in
	// beginOp above.
	if opCode != fusekernel.OpForget {
		cancel, ok := c.cancelFuncs[fuseID]
		if !ok {
			panic(fmt.Sprintf("Unknown request ID in finishOp: %v", fuseID))
		}

		cancel()
		delete(c.cancelFuncs, fuseID)
	}
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) handleInterrupt(fuseID uint64) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// NOTE(jacobsa): fuse.txt in the Linux kernel documentation
	// (https://goo.gl/H55Dnr) defines the kernel <-> userspace protocol for
	// interrupts.
	//
	// In particular, my reading of it is that an interrupt request cannot be
	// delivered to userspace before the original request. The part about the
	// race and EAGAIN appears to be aimed at userspace programs that
	// concurrently process requests (cf. http://goo.gl/BES2rs).
	//
	// So in this method if we can't find the ID to be interrupted, it means that
	// the request has already been replied to.
	//
	// Cf. https://github.com/osxfuse/osxfuse/issues/208
	// Cf. http://comments.gmane.org/gmane.comp.file-systems.fuse.devel/14675
	cancel, ok := c.cancelFuncs[fuseID]
	if !ok {
		return
	}

	cancel()
}

func (c *Connection) allocateInMessage() (m *buffer.InMessage) {
	// TODO(jacobsa): Use a freelist.
	m = new(buffer.InMessage)
	return
}

func (c *Connection) destroyInMessage(m *buffer.InMessage) {
	// TODO(jacobsa): Use a freelist.
}

// Read the next message from the kernel. The message must later be destroyed
// using destroyInMessage.
func (c *Connection) readMessage() (m *buffer.InMessage, err error) {
	// Allocate a message.
	m = c.allocateInMessage()

	// Loop past transient errors.
	for {
		// Attempt a reaed.
		err = m.Init(c.dev)

		// Special cases:
		//
		//  *  ENODEV means fuse has hung up.
		//
		//  *  EINTR means we should try again. (This seems to happen often on
		//     OS X, cf. http://golang.org/issue/11180)
		//
		if pe, ok := err.(*os.PathError); ok {
			switch pe.Err {
			case syscall.ENODEV:
				err = io.EOF

			case syscall.EINTR:
				err = nil
				continue
			}
		}

		if err != nil {
			c.destroyInMessage(m)
			m = nil
			return
		}

		return
	}
}

// Write the supplied message to the kernel.
func (c *Connection) writeMessage(msg []byte) (err error) {
	// Avoid the retry loop in os.File.Write.
	n, err := syscall.Write(int(c.dev.Fd()), msg)
	if err != nil {
		return
	}

	if n != len(msg) {
		err = fmt.Errorf("Wrote %d bytes; expected %d", n, len(msg))
		return
	}

	return
}

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	// Keep going until we find a request we know how to convert.
	for {
		// Read the next message from the kernel.
		var m *buffer.InMessage
		m, err = c.readMessage()
		if err != nil {
			return
		}

		// Choose an ID for this operation for the purposes of logging.
		opID := c.nextOpID
		c.nextOpID++

		// Set up op dependencies.
		opCtx := c.beginOp(m.Header().Opcode, m.Header().Unique)

		var debugLogForOp func(int, string, ...interface{})
		if c.debugLogger != nil {
			debugLogForOp = func(calldepth int, format string, v ...interface{}) {
				c.debugLog(opID, calldepth+1, format, v...)
			}
		}

		sendReply := func(
			op fuseops.Op,
			fuseID uint64,
			replyMsg []byte,
			opErr error) (err error) {
			// Make sure we destroy the message, as required by readMessage.
			defer c.destroyInMessage(m)

			// Clean up state for this op.
			c.finishOp(m.Header().Opcode, m.Header().Unique)

			// Debug logging
			if c.debugLogger != nil {
				if opErr == nil {
					op.Logf("-> OK: %s", op.DebugString())
				} else {
					op.Logf("-> error: %v", opErr)
				}
			}

			// Error logging
			if opErr != nil && c.errorLogger != nil {
				c.errorLogger.Printf("(%s) error: %v", op.ShortDesc(), opErr)
			}

			// Send the reply to the kernel.
			err = c.writeMessage(replyMsg)
			if err != nil {
				err = fmt.Errorf("writeMessage: %v", err)
				return
			}

			return
		}

		// Convert the message to an Op.
		op, err = fuseops.Convert(
			opCtx,
			m,
			c.protocol,
			debugLogForOp,
			c.errorLogger,
			sendReply)

		if err != nil {
			err = fmt.Errorf("fuseops.Convert: %v", err)
			return
		}

		// Log the receipt of the operation.
		c.debugLog(opID, 1, "<- %v", op.ShortDesc())

		// Special case: responding to statfs is required to make mounting work on
		// OS X. We don't currently expose the capability for the file system to
		// intercept this.
		if _, ok := op.(*fuseops.InternalStatFSOp); ok {
			op.Respond(nil)
			continue
		}

		// Special case: handle interrupt requests.
		if interruptOp, ok := op.(*fuseops.InternalInterruptOp); ok {
			c.handleInterrupt(interruptOp.FuseID)
			continue
		}

		return
	}
}

// Close the connection. Must not be called until operations that were read
// from the connection have been responded to.
func (c *Connection) close() (err error) {
	// Posix doesn't say that close can be called concurrently with read or
	// write, but luckily we exclude the possibility of a race by requiring the
	// user to respond to all ops first.
	err = c.dev.Close()
	return
}
<|endoftext|>


<commit_before>/*
Run Action

Allows to run any available command or script in the filesystem or
in host environment.

Yaml syntax:
 - action: run
   chroot: bool
   postprocess: bool
   script: script name
   command: command line

Properties 'command' and 'script' are mutually exclusive.

- command -- command with arguments; the command expected to be accessible in
host's or chrooted environment -- depending on 'chroot' property.

- script -- script with arguments; script must be located in recipe directory.

Optional properties:

- chroot -- run script or command in target filesystem if set to true.
In other case the command or script is executed within the build process, with
access to the filesystem and the image. In both cases it is run with root privileges.

- postprocess -- if set script or command is executed after all other commands and
has access to the image file.

Properties 'chroot' and 'postprocess' are mutually exclusive.
*/
package actions

import (
	"errors"
	"github.com/go-debos/fakemachine"
	"path"
	"strings"

	"github.com/go-debos/debos"
)

type RunAction struct {
	debos.BaseAction `yaml:",inline"`
	Chroot           bool
	PostProcess      bool
	Script           string
	Command          string
}

func (run *RunAction) Verify(context *debos.DebosContext) error {
	if run.PostProcess && run.Chroot {
		return errors.New("Cannot run postprocessing in the chroot")
	}
	return nil
}

func (run *RunAction) PreMachine(context *debos.DebosContext, m *fakemachine.Machine,
	args *[]string) error {

	if run.Script == "" {
		return nil
	}

	run.Script = debos.CleanPathAt(run.Script, context.RecipeDir)
	// Expect we have no blank spaces in path
	scriptpath := strings.Split(run.Script, " ")

	if !run.PostProcess {
		m.AddVolume(path.Dir(scriptpath[0]))
	}

	return nil
}

func (run *RunAction) doRun(context debos.DebosContext) error {
	run.LogStart()
	var cmdline []string
	var label string
	var cmd debos.Command

	if run.Chroot {
		cmd = debos.NewChrootCommandForContext(context)
	} else {
		cmd = debos.Command{}
	}

	if run.Script != "" {
		script := strings.SplitN(run.Script, " ", 2)
		script[0] = debos.CleanPathAt(script[0], context.RecipeDir)
		if run.Chroot {
			scriptpath := path.Dir(script[0])
			cmd.AddBindMount(scriptpath, "/script")
			script[0] = strings.Replace(script[0], scriptpath, "/script", 1)
		}
		cmdline = []string{strings.Join(script, " ")}
		label = path.Base(run.Script)
	} else {
		cmdline = []string{run.Command}
		label = run.Command
	}

	// Command/script with options passed as single string
	cmdline = append([]string{"sh", "-c"}, cmdline...)

	if !run.Chroot && !run.PostProcess {
		cmd.AddEnvKey("ROOTDIR", context.Rootdir)
	}

	return cmd.Run(label, cmdline...)
}

func (run *RunAction) Run(context *debos.DebosContext) error {
	if run.PostProcess {
		/* This runs in postprocessing instead */
		return nil
	}
	return run.doRun(*context)
}

func (run *RunAction) PostMachine(context debos.DebosContext) error {
	if !run.PostProcess {
		return nil
	}
	return run.doRun(context)
}
<commit_msg>Add IMAGE environment variable<commit_after>/*
Run Action

Allows to run any available command or script in the filesystem or
in host environment.

Yaml syntax:
 - action: run
   chroot: bool
   postprocess: bool
   script: script name
   command: command line

Properties 'command' and 'script' are mutually exclusive.

- command -- command with arguments; the command expected to be accessible in
host's or chrooted environment -- depending on 'chroot' property.

- script -- script with arguments; script must be located in recipe directory.

Optional properties:

- chroot -- run script or command in target filesystem if set to true.
In other case the command or script is executed within the build process, with
access to the filesystem and the image. In both cases it is run with root privileges.

- postprocess -- if set script or command is executed after all other commands and
has access to the image file.

Properties 'chroot' and 'postprocess' are mutually exclusive.
*/
package actions

import (
	"errors"
	"github.com/go-debos/fakemachine"
	"path"
	"strings"

	"github.com/go-debos/debos"
)

type RunAction struct {
	debos.BaseAction `yaml:",inline"`
	Chroot           bool
	PostProcess      bool
	Script           string
	Command          string
}

func (run *RunAction) Verify(context *debos.DebosContext) error {
	if run.PostProcess && run.Chroot {
		return errors.New("Cannot run postprocessing in the chroot")
	}
	return nil
}

func (run *RunAction) PreMachine(context *debos.DebosContext, m *fakemachine.Machine,
	args *[]string) error {

	if run.Script == "" {
		return nil
	}

	run.Script = debos.CleanPathAt(run.Script, context.RecipeDir)
	// Expect we have no blank spaces in path
	scriptpath := strings.Split(run.Script, " ")

	if !run.PostProcess {
		m.AddVolume(path.Dir(scriptpath[0]))
	}

	return nil
}

func (run *RunAction) doRun(context debos.DebosContext) error {
	run.LogStart()
	var cmdline []string
	var label string
	var cmd debos.Command

	if run.Chroot {
		cmd = debos.NewChrootCommandForContext(context)
	} else {
		cmd = debos.Command{}
	}

	if run.Script != "" {
		script := strings.SplitN(run.Script, " ", 2)
		script[0] = debos.CleanPathAt(script[0], context.RecipeDir)
		if run.Chroot {
			scriptpath := path.Dir(script[0])
			cmd.AddBindMount(scriptpath, "/script")
			script[0] = strings.Replace(script[0], scriptpath, "/script", 1)
		}
		cmdline = []string{strings.Join(script, " ")}
		label = path.Base(run.Script)
	} else {
		cmdline = []string{run.Command}
		label = run.Command
	}

	// Command/script with options passed as single string
	cmdline = append([]string{"sh", "-c"}, cmdline...)

	if !run.PostProcess {
		if !run.Chroot {
			cmd.AddEnvKey("ROOTDIR", context.Rootdir)
		}
		if context.Image != "" {
			cmd.AddEnvKey("IMAGE", context.Image)
		}
	}

	return cmd.Run(label, cmdline...)
}

func (run *RunAction) Run(context *debos.DebosContext) error {
	if run.PostProcess {
		/* This runs in postprocessing instead */
		return nil
	}
	return run.doRun(*context)
}

func (run *RunAction) PostMachine(context debos.DebosContext) error {
	if !run.PostProcess {
		return nil
	}
	return run.doRun(context)
}
<|endoftext|>


<commit_before>package version

import (
	"fmt"
	"regexp"
	"strings"
)

// Constraint represents a single constraint for a version, such as
// ">= 1.0".
type Constraint struct {
	f        constraintFunc
	check    *Version
	original string
}

// Constraints is a slice of constraints. We make a custom type so that
// we can add methods to it.
type Constraints []*Constraint

type constraintFunc func(v, c *Version) bool

var constraintOperators map[string]constraintFunc

var constraintRegexp *regexp.Regexp

func init() {
	constraintOperators = map[string]constraintFunc{
		"":   constraintEqual,
		"=":  constraintEqual,
		"!=": constraintNotEqual,
		">":  constraintGreaterThan,
		"<":  constraintLessThan,
		">=": constraintGreaterThanEqual,
		"<=": constraintLessThanEqual,
		"~>": constraintPessimistic,
	}

	ops := make([]string, 0, len(constraintOperators))
	for k, _ := range constraintOperators {
		ops = append(ops, regexp.QuoteMeta(k))
	}

	constraintRegexp = regexp.MustCompile(fmt.Sprintf(
		`^\s*(%s)\s*(%s)\s*$`,
		strings.Join(ops, "|"),
		VersionRegexpRaw))
}

// NewConstraint will parse one or more constraints from the given
// constraint string. The string must be a comma-separated list of
// constraints.
func NewConstraint(v string) (Constraints, error) {
	vs := strings.Split(v, ",")
	result := make([]*Constraint, len(vs))
	for i, single := range vs {
		c, err := parseSingle(single)
		if err != nil {
			return nil, err
		}

		result[i] = c
	}

	return Constraints(result), nil
}

// Check tests if a version satisfies all the constraints.
func (cs Constraints) Check(v *Version) bool {
	for _, c := range cs {
		if !c.Check(v) {
			return false
		}
	}

	return true
}

// Returns the string format of the constraints
func (cs Constraints) String() string {
	csStr := make([]string, len(cs))
	for i, c := range cs {
		csStr[i] = c.String()
	}

	return strings.Join(csStr, ",")
}

// Check tests if a constraint is validated by the given version.
func (c *Constraint) Check(v *Version) bool {
	return c.f(v, c.check)
}

func (c *Constraint) String() string {
	return c.original
}

func parseSingle(v string) (*Constraint, error) {
	matches := constraintRegexp.FindStringSubmatch(v)
	if matches == nil {
		return nil, fmt.Errorf("Malformed constraint: %s", v)
	}

	check, err := NewVersion(matches[2])
	if err != nil {
		return nil, err
	}

	return &Constraint{
		f:        constraintOperators[matches[1]],
		check:    check,
		original: v,
	}, nil
}

//-------------------------------------------------------------------
// Constraint functions
//-------------------------------------------------------------------

func constraintEqual(v, c *Version) bool {
	return v.Equal(c)
}

func constraintNotEqual(v, c *Version) bool {
	return !v.Equal(c)
}

func constraintGreaterThan(v, c *Version) bool {
	return v.Compare(c) == 1
}

func constraintLessThan(v, c *Version) bool {
	return v.Compare(c) == -1
}

func constraintGreaterThanEqual(v, c *Version) bool {
	return v.Compare(c) >= 0
}

func constraintLessThanEqual(v, c *Version) bool {
	return v.Compare(c) <= 0
}

func constraintPessimistic(v, c *Version) bool {
	// If the version being checked is naturally less than the constraint, then there
	// is no way for the version to be valid against the constraint
	if v.LessThan(c) {
		return false
	}
	// We'll use this more than once, so grab the length now so it's a little cleaner
	// to write the later checks
	cs := len(c.segments)

	// If the version being checked has less specificity than the constraint, then there
	// is no way for the version to be valid against the constraint
	if cs > len(v.segments) {
		return false
	}

	// Check the segments in the constraint against those in the version. If the version
	// being checked, at any point, does not have the same values in each index of the
	// constraints segments, then it cannot be valid against the constraint.
	for i := 0; i < c.si-1; i++ {
		if v.segments[i] != c.segments[i] {
			return false
		}
	}

	// Check the last part of the segment in the constraint. If the version segment at
	// this index is less than the constraints segment at this index, then it cannot
	// be valid against the constraint
	if c.segments[cs-1] > v.segments[cs-1] {
		return false
	}

	// If nothing has rejected the version by now, it's valid
	return true
}
<commit_msg>Removing a superfluous _ from a range iterator<commit_after>package version

import (
	"fmt"
	"regexp"
	"strings"
)

// Constraint represents a single constraint for a version, such as
// ">= 1.0".
type Constraint struct {
	f        constraintFunc
	check    *Version
	original string
}

// Constraints is a slice of constraints. We make a custom type so that
// we can add methods to it.
type Constraints []*Constraint

type constraintFunc func(v, c *Version) bool

var constraintOperators map[string]constraintFunc

var constraintRegexp *regexp.Regexp

func init() {
	constraintOperators = map[string]constraintFunc{
		"":   constraintEqual,
		"=":  constraintEqual,
		"!=": constraintNotEqual,
		">":  constraintGreaterThan,
		"<":  constraintLessThan,
		">=": constraintGreaterThanEqual,
		"<=": constraintLessThanEqual,
		"~>": constraintPessimistic,
	}

	ops := make([]string, 0, len(constraintOperators))
	for k := range constraintOperators {
		ops = append(ops, regexp.QuoteMeta(k))
	}

	constraintRegexp = regexp.MustCompile(fmt.Sprintf(
		`^\s*(%s)\s*(%s)\s*$`,
		strings.Join(ops, "|"),
		VersionRegexpRaw))
}

// NewConstraint will parse one or more constraints from the given
// constraint string. The string must be a comma-separated list of
// constraints.
func NewConstraint(v string) (Constraints, error) {
	vs := strings.Split(v, ",")
	result := make([]*Constraint, len(vs))
	for i, single := range vs {
		c, err := parseSingle(single)
		if err != nil {
			return nil, err
		}

		result[i] = c
	}

	return Constraints(result), nil
}

// Check tests if a version satisfies all the constraints.
func (cs Constraints) Check(v *Version) bool {
	for _, c := range cs {
		if !c.Check(v) {
			return false
		}
	}

	return true
}

// Returns the string format of the constraints
func (cs Constraints) String() string {
	csStr := make([]string, len(cs))
	for i, c := range cs {
		csStr[i] = c.String()
	}

	return strings.Join(csStr, ",")
}

// Check tests if a constraint is validated by the given version.
func (c *Constraint) Check(v *Version) bool {
	return c.f(v, c.check)
}

func (c *Constraint) String() string {
	return c.original
}

func parseSingle(v string) (*Constraint, error) {
	matches := constraintRegexp.FindStringSubmatch(v)
	if matches == nil {
		return nil, fmt.Errorf("Malformed constraint: %s", v)
	}

	check, err := NewVersion(matches[2])
	if err != nil {
		return nil, err
	}

	return &Constraint{
		f:        constraintOperators[matches[1]],
		check:    check,
		original: v,
	}, nil
}

//-------------------------------------------------------------------
// Constraint functions
//-------------------------------------------------------------------

func constraintEqual(v, c *Version) bool {
	return v.Equal(c)
}

func constraintNotEqual(v, c *Version) bool {
	return !v.Equal(c)
}

func constraintGreaterThan(v, c *Version) bool {
	return v.Compare(c) == 1
}

func constraintLessThan(v, c *Version) bool {
	return v.Compare(c) == -1
}

func constraintGreaterThanEqual(v, c *Version) bool {
	return v.Compare(c) >= 0
}

func constraintLessThanEqual(v, c *Version) bool {
	return v.Compare(c) <= 0
}

func constraintPessimistic(v, c *Version) bool {
	// If the version being checked is naturally less than the constraint, then there
	// is no way for the version to be valid against the constraint
	if v.LessThan(c) {
		return false
	}
	// We'll use this more than once, so grab the length now so it's a little cleaner
	// to write the later checks
	cs := len(c.segments)

	// If the version being checked has less specificity than the constraint, then there
	// is no way for the version to be valid against the constraint
	if cs > len(v.segments) {
		return false
	}

	// Check the segments in the constraint against those in the version. If the version
	// being checked, at any point, does not have the same values in each index of the
	// constraints segments, then it cannot be valid against the constraint.
	for i := 0; i < c.si-1; i++ {
		if v.segments[i] != c.segments[i] {
			return false
		}
	}

	// Check the last part of the segment in the constraint. If the version segment at
	// this index is less than the constraints segment at this index, then it cannot
	// be valid against the constraint
	if c.segments[cs-1] > v.segments[cs-1] {
		return false
	}

	// If nothing has rejected the version by now, it's valid
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"os"

	"github.com/jzipfler/htw-ava/server"
	"github.com/jzipfler/htw-ava/utils"
)

var (
	filename    string
	managerName string
	logFile     string
	ipAddress   string
	port        int
)

func init() {
	flag.StringVar(&filename, "filename", "path/to/file.txt", "A file that is managed by this process.")
	flag.StringVar(&managerName, "name", "Manager A", "Define the name of this manager.")
	flag.StringVar(&logFile, "logFile", "path/to/logfile.txt", "This parameter can be used to print the logging output to the given file.")
	flag.StringVar(&ipAddress, "ipAddress", "127.0.0.1", "The ip address of the actual starting node.")
	flag.IntVar(&port, "port", 15100, "The port of the actual starting node.")
}

func main() {
	if len(os.Args) == 1 {
		flag.Usage()
		os.Exit(0)
	}
	flag.Parse()

	if filename == "path/to/file.txt" {
		log.Fatalf("A file is required.\n%s\n", utils.ERROR_FOOTER)
	}

	if writable, err := utils.CheckIfFileIsReadableAndWritebale(filename); !writable {
		log.Fatalf("%s\n%s\n", err.Error(), utils.ERROR_FOOTER)
	}

	utils.InitializeLogger(logFile, "")
	utils.PrintMessage(fmt.Sprintf("File \"%s\" is now managed by this process.", filename))

	serverObject := server.New()

	serverObject.SetClientName(managerName)
	serverObject.SetIpAddressAsString(ipAddress)
	serverObject.SetPort(port)
	serverObject.SetUsedProtocol("tcp")

	if err := server.StartServer(serverObject, nil); err != nil {
		log.Fatalln("Could not start server. --> Exit.")
		os.Exit(1)
	}
	defer server.StopServer()
}
<commit_msg>Changed the initial check for parameters.<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"os"

	"github.com/jzipfler/htw-ava/server"
	"github.com/jzipfler/htw-ava/utils"
)

var (
	filename    string
	managerName string
	logFile     string
	ipAddress   string
	port        int
)

func init() {
	flag.StringVar(&filename, "filename", "path/to/file.txt", "A file that is managed by this process.")
	flag.StringVar(&managerName, "name", "Manager A", "Define the name of this manager.")
	flag.StringVar(&logFile, "logFile", "path/to/logfile.txt", "This parameter can be used to print the logging output to the given file.")
	flag.StringVar(&ipAddress, "ipAddress", "127.0.0.1", "The ip address of the actual starting node.")
	flag.IntVar(&port, "port", 15100, "The port of the actual starting node.")
}

func main() {

	if filename == "path/to/file.txt" {
		log.Printf("A filename is required.\n%s\n\n", utils.ERROR_FOOTER)
		flag.Usage()
		os.Exit(0)
	}

	flag.Parse()

	if exists := utils.CheckIfFileExists(filename); exists {
		if writable, err := utils.CheckIfFileIsReadableAndWritebale(filename); !writable {
			log.Fatalf("%s\n%s\n", err.Error(), utils.ERROR_FOOTER)
		}
	} else {
		os.Create(filename)
	}

	utils.InitializeLogger(logFile, "")
	utils.PrintMessage(fmt.Sprintf("File \"%s\" is now managed by this process.", filename))

	serverObject := server.New()

	serverObject.SetClientName(managerName)
	serverObject.SetIpAddressAsString(ipAddress)
	serverObject.SetPort(port)
	serverObject.SetUsedProtocol("tcp")

	if err := server.StartServer(serverObject, nil); err != nil {
		log.Fatalln("Could not start server. --> Exit.")
		os.Exit(1)
	}
	defer server.StopServer()
}
<|endoftext|>


<commit_before>// Package transport implements an encrypted and authenticated bytestream
// connection. The key-exchange is like SIGMA-I, with the exception that
// instead of unvirersally verifiable signatures, nacl/box between one party's
// ephemeral key and the other's long-term key is used for authentication (this
// provides deniability). Subsequent messages are encrypted using nacl/box with
// the message counter as an implicit nonce.
package transport

import (
	"bytes"
	"crypto/rand"
	"encoding/binary"
	"errors"
	"io"
	"net"
	"time"

	"code.google.com/p/go.crypto/nacl/box"
)

// Conn is an encrypted and authenticated connection that is NOT concurrency-safe
type Conn struct {
	unencrypted           net.Conn
	readNonce, writeNonce uint64
	key                   [32]byte
	readBuf, writeBuf     []byte
	maxFrameSize          int
}

var nullNonce = [24]byte{}

// Handshake establishes an encrypted and authenticated connection. unencrypted
// is the underlying connection that will be used for the handshake and the
// following calls to ReadFrame and WriteFrame. The connection should not be
// used after calling Handshake. pk and sk are the Curve25519 public and
// private keys of the caller. If expectedPK is not nil, pk will not be
// revealed to the other party unless they prove that they hold the secret key
// that corresponds to expectedPK.  Note that both sides of a connection using
// this option will result in a deadlock. The public key of the other party is
// returned along with the wrapped connection.
func Handshake(unencrypted net.Conn, pk, sk, expectedPK *[32]byte, maxFrameSize int) (*Conn, *[32]byte, error) {
	ourEphemeralPublic, ourEphemeralSecret, err := box.GenerateKey(rand.Reader)
	if err != nil {
		return nil, nil, err
	}
	var theirEphemeralPublic, theirPK [32]byte
	var readErr, writeErr error
	writeDone, readDone := make(chan struct{}), make(chan chan struct{})
	go func() { _, writeErr = unencrypted.Write(ourEphemeralPublic[:]); close(writeDone) }()
	go func() { _, readErr = io.ReadFull(unencrypted, theirEphemeralPublic[:]); close(readDone) }()
	if <-writeDone; writeErr != nil {
		return nil, nil, writeErr
	}
	if <-readDone; readErr != nil {
		return nil, nil, readErr
	}

	ret := &Conn{unencrypted: unencrypted, maxFrameSize: maxFrameSize,
		readBuf:  make([]byte, binary.MaxVarintLen64+box.Overhead+maxFrameSize),
		writeBuf: make([]byte, binary.MaxVarintLen64+box.Overhead+maxFrameSize)}
	if bytes.Compare(ourEphemeralPublic[:], theirEphemeralPublic[:]) < 0 {
		ret.writeNonce = 1
	} else {
		ret.readNonce = 1
	}
	box.Precompute(&ret.key, &theirEphemeralPublic, ourEphemeralSecret)

	writeDone, readDone = make(chan struct{}), make(chan chan struct{})
	go func() {
		defer close(readDone)
		var theirHandshake [32 + (box.Overhead + 32 + 32)]byte // theirPK, box(theirEphPK, ourEphPK)
		if _, readErr = ret.ReadFrame(theirHandshake[:]); readErr != nil {
			return
		}
		copy(theirPK[:], theirHandshake[:32])
		hs, ok := box.Open(nil, theirHandshake[32:], &nullNonce, &theirPK, ourEphemeralSecret)
		if !ok || !bytes.Equal(hs, append(theirEphemeralPublic[:], ourEphemeralPublic[:]...)) {
			readErr = errors.New("authentication failed (ephemeral pk mismatch)")
			return
		}
		if expectedPK != nil && !bytes.Equal(theirPK[:], expectedPK[:]) {
			readErr = errors.New("authentication failed (observed pk != expected pk)")
			return
		}
	}()
	go func() {
		defer close(writeDone)
		ourHandshake := box.Seal(pk[:], append(ourEphemeralPublic[:], theirEphemeralPublic[:]...),
			&nullNonce, &theirEphemeralPublic, sk)
		if expectedPK != nil {
			if <-readDone; readErr != nil { // only talk to the right server
				return
			}
		}
		_, writeErr = ret.WriteFrame(ourHandshake)
	}()
	if <-readDone; readErr != nil {
		return nil, nil, readErr
	}
	if <-writeDone; writeErr != nil {
		return nil, nil, writeErr
	}
	for i := range ourEphemeralSecret {
		ourEphemeralSecret[i] = 0
	}
	return ret, &theirPK, nil
}

// WriteFrame(b) writes the frame to the connection in a length-value-encoded
// for so it can be read using ReadFrame on the other side. Returns len(b).
func (c *Conn) WriteFrame(b []byte) (int, error) {
	if len(b) > c.maxFrameSize {
		return 0, errors.New("write frame too large")
	}
	var nonce [24]byte
	binary.LittleEndian.PutUint64(nonce[:], c.writeNonce)
	c.writeNonce += 2
	i := binary.PutUvarint(c.writeBuf, uint64(box.Overhead+len(b)))
	buf := box.SealAfterPrecomputation(c.writeBuf[:i], b, &nonce, &c.key)
	if _, err := c.unencrypted.Write(buf); err != nil {
		return 0, err
	}
	return len(b), nil
}

type byteReader struct{ io.Reader }

func (r byteReader) ReadByte() (byte, error) {
	var ret [1]byte
	_, err := io.ReadFull(r, ret[:])
	return ret[0], err
}

// ReadFrame(b) reads a single frame into b and returns an integer n such that
// b[:n] is the frame after possibly modifying b. If b does not have enough
// space (maxFrameSize bytes), this function may panic.
func (c *Conn) ReadFrame(b []byte) (int, error) {
	var nonce [24]byte
	binary.LittleEndian.PutUint64(nonce[:], c.readNonce)
	c.readNonce += 2
	size, err := binary.ReadUvarint(byteReader{c.unencrypted})
	if err != nil {
		return 0, err
	}
	if _, err := io.ReadFull(c.unencrypted, c.readBuf[:size]); err != nil {
		return 0, err
	}
	b2, ok := box.OpenAfterPrecomputation(b[:0], c.readBuf[:size], &nonce, &c.key)
	if !ok {
		return 0, errors.New("authentication failed")
	}
	if &b[0] != &b2[0] {
		panic("ReadFrame buffer space accounting failed")
	}
	return int(size - box.Overhead), nil
}

func (c *Conn) Close() error {
	for i := range c.key {
		c.key[i] = 0
	}
	return c.unencrypted.Close()
}

func (c *Conn) SetDeadline(t time.Time) error      { return c.unencrypted.SetDeadline(t) }
func (c *Conn) SetWriteDeadline(t time.Time) error { return c.unencrypted.SetWriteDeadline(t) }
func (c *Conn) SetReadDeadline(t time.Time) error  { return c.unencrypted.SetReadDeadline(t) }
<commit_msg>transport doc<commit_after>// Package transport implements an encrypted and authenticated connection for
// transferring byte slices over a network. The key-exchange is like SIGMA-I,
// with the exception that instead of unvirersally verifiable signatures,
// nacl/box between one party's ephemeral key and the other's long-term key is
// used for authentication (this provides deniability). Subsequent messages are
// encrypted using nacl/box with the message counter as an implicit nonce.
package transport

import (
	"bytes"
	"crypto/rand"
	"encoding/binary"
	"errors"
	"io"
	"net"
	"time"

	"code.google.com/p/go.crypto/nacl/box"
)

// Conn is an encrypted and authenticated connection that is NOT concurrency-safe
type Conn struct {
	unencrypted           net.Conn
	readNonce, writeNonce uint64
	key                   [32]byte
	readBuf, writeBuf     []byte
	maxFrameSize          int
}

var nullNonce = [24]byte{}

// Handshake establishes an encrypted and authenticated connection. unencrypted
// is the underlying connection that will be used for the handshake and the
// following calls to ReadFrame and WriteFrame. The connection should not be
// used after calling Handshake. pk and sk are the Curve25519 public and
// private keys of the caller. If expectedPK is not nil, pk will not be
// revealed to the other party unless they prove that they hold the secret key
// that corresponds to expectedPK.  Note that both sides of a connection using
// this option will result in a deadlock. The public key of the other party is
// returned along with the wrapped connection.
func Handshake(unencrypted net.Conn, pk, sk, expectedPK *[32]byte, maxFrameSize int) (*Conn, *[32]byte, error) {
	ourEphemeralPublic, ourEphemeralSecret, err := box.GenerateKey(rand.Reader)
	if err != nil {
		return nil, nil, err
	}
	var theirEphemeralPublic, theirPK [32]byte
	var readErr, writeErr error
	writeDone, readDone := make(chan struct{}), make(chan chan struct{})
	go func() { _, writeErr = unencrypted.Write(ourEphemeralPublic[:]); close(writeDone) }()
	go func() { _, readErr = io.ReadFull(unencrypted, theirEphemeralPublic[:]); close(readDone) }()
	if <-writeDone; writeErr != nil {
		return nil, nil, writeErr
	}
	if <-readDone; readErr != nil {
		return nil, nil, readErr
	}

	ret := &Conn{unencrypted: unencrypted, maxFrameSize: maxFrameSize,
		readBuf:  make([]byte, binary.MaxVarintLen64+box.Overhead+maxFrameSize),
		writeBuf: make([]byte, binary.MaxVarintLen64+box.Overhead+maxFrameSize)}
	if bytes.Compare(ourEphemeralPublic[:], theirEphemeralPublic[:]) < 0 {
		ret.writeNonce = 1
	} else {
		ret.readNonce = 1
	}
	box.Precompute(&ret.key, &theirEphemeralPublic, ourEphemeralSecret)

	writeDone, readDone = make(chan struct{}), make(chan chan struct{})
	go func() {
		defer close(readDone)
		var theirHandshake [32 + (box.Overhead + 32 + 32)]byte // theirPK, box(theirEphPK, ourEphPK)
		if _, readErr = ret.ReadFrame(theirHandshake[:]); readErr != nil {
			return
		}
		copy(theirPK[:], theirHandshake[:32])
		hs, ok := box.Open(nil, theirHandshake[32:], &nullNonce, &theirPK, ourEphemeralSecret)
		if !ok || !bytes.Equal(hs, append(theirEphemeralPublic[:], ourEphemeralPublic[:]...)) {
			readErr = errors.New("authentication failed (ephemeral pk mismatch)")
			return
		}
		if expectedPK != nil && !bytes.Equal(theirPK[:], expectedPK[:]) {
			readErr = errors.New("authentication failed (observed pk != expected pk)")
			return
		}
	}()
	go func() {
		defer close(writeDone)
		ourHandshake := box.Seal(pk[:], append(ourEphemeralPublic[:], theirEphemeralPublic[:]...),
			&nullNonce, &theirEphemeralPublic, sk)
		if expectedPK != nil {
			if <-readDone; readErr != nil { // only talk to the right server
				return
			}
		}
		_, writeErr = ret.WriteFrame(ourHandshake)
	}()
	if <-readDone; readErr != nil {
		return nil, nil, readErr
	}
	if <-writeDone; writeErr != nil {
		return nil, nil, writeErr
	}
	for i := range ourEphemeralSecret {
		ourEphemeralSecret[i] = 0
	}
	return ret, &theirPK, nil
}

// WriteFrame(b) writes the frame to the connection in a length-value-encoded
// for so it can be read using ReadFrame on the other side. Returns len(b).
func (c *Conn) WriteFrame(b []byte) (int, error) {
	if len(b) > c.maxFrameSize {
		return 0, errors.New("write frame too large")
	}
	var nonce [24]byte
	binary.LittleEndian.PutUint64(nonce[:], c.writeNonce)
	c.writeNonce += 2
	i := binary.PutUvarint(c.writeBuf, uint64(box.Overhead+len(b)))
	buf := box.SealAfterPrecomputation(c.writeBuf[:i], b, &nonce, &c.key)
	if _, err := c.unencrypted.Write(buf); err != nil {
		return 0, err
	}
	return len(b), nil
}

type byteReader struct{ io.Reader }

func (r byteReader) ReadByte() (byte, error) {
	var ret [1]byte
	_, err := io.ReadFull(r, ret[:])
	return ret[0], err
}

// ReadFrame(b) reads a single frame into b and returns an integer n such that
// b[:n] is the frame after possibly modifying b. If b does not have enough
// space (maxFrameSize bytes), this function may panic.
func (c *Conn) ReadFrame(b []byte) (int, error) {
	var nonce [24]byte
	binary.LittleEndian.PutUint64(nonce[:], c.readNonce)
	c.readNonce += 2
	size, err := binary.ReadUvarint(byteReader{c.unencrypted})
	if err != nil {
		return 0, err
	}
	if _, err := io.ReadFull(c.unencrypted, c.readBuf[:size]); err != nil {
		return 0, err
	}
	b2, ok := box.OpenAfterPrecomputation(b[:0], c.readBuf[:size], &nonce, &c.key)
	if !ok {
		return 0, errors.New("authentication failed")
	}
	if &b[0] != &b2[0] {
		panic("ReadFrame buffer space accounting failed")
	}
	return int(size - box.Overhead), nil
}

func (c *Conn) Close() error {
	for i := range c.key {
		c.key[i] = 0
	}
	return c.unencrypted.Close()
}

func (c *Conn) SetDeadline(t time.Time) error      { return c.unencrypted.SetDeadline(t) }
func (c *Conn) SetWriteDeadline(t time.Time) error { return c.unencrypted.SetWriteDeadline(t) }
func (c *Conn) SetReadDeadline(t time.Time) error  { return c.unencrypted.SetReadDeadline(t) }
<|endoftext|>


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

package model

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

	"github.com/juju/errors"
	"github.com/pingcap/tidb/terror"
)

// ActionType is the type for DDL action.
type ActionType byte

// List DDL actions.
const (
	ActionNone            ActionType = 0
	ActionCreateSchema    ActionType = 1
	ActionDropSchema      ActionType = 2
	ActionCreateTable     ActionType = 3
	ActionDropTable       ActionType = 4
	ActionAddColumn       ActionType = 5
	ActionDropColumn      ActionType = 6
	ActionAddIndex        ActionType = 7
	ActionDropIndex       ActionType = 8
	ActionAddForeignKey   ActionType = 9
	ActionDropForeignKey  ActionType = 10
	ActionTruncateTable   ActionType = 11
	ActionModifyColumn    ActionType = 12
	ActionRebaseAutoID    ActionType = 13
	ActionRenameTable     ActionType = 14
	ActionSetDefaultValue ActionType = 15
	ActionShardRowID      ActionType = 16
)

func (action ActionType) String() string {
	switch action {
	case ActionCreateSchema:
		return "create schema"
	case ActionDropSchema:
		return "drop schema"
	case ActionCreateTable:
		return "create table"
	case ActionDropTable:
		return "drop table"
	case ActionAddColumn:
		return "add column"
	case ActionDropColumn:
		return "drop column"
	case ActionAddIndex:
		return "add index"
	case ActionDropIndex:
		return "drop index"
	case ActionAddForeignKey:
		return "add foreign key"
	case ActionDropForeignKey:
		return "drop foreign key"
	case ActionTruncateTable:
		return "truncate table"
	case ActionModifyColumn:
		return "modify column"
	case ActionRebaseAutoID:
		return "rebase auto_increment ID"
	case ActionRenameTable:
		return "rename table"
	case ActionSetDefaultValue:
		return "set default value"
	case ActionShardRowID:
		return "shard row ID"
	default:
		return "none"
	}
}

// HistoryInfo is used for binlog.
type HistoryInfo struct {
	SchemaVersion int64
	DBInfo        *DBInfo
	TableInfo     *TableInfo
}

// AddDBInfo adds schema version and schema information that are used for binlog.
// dbInfo is added in the following operations: create database, drop database.
func (h *HistoryInfo) AddDBInfo(schemaVer int64, dbInfo *DBInfo) {
	h.SchemaVersion = schemaVer
	h.DBInfo = dbInfo
}

// AddTableInfo adds schema version and table information that are used for binlog.
// tblInfo is added except for the following operations: create database, drop database.
func (h *HistoryInfo) AddTableInfo(schemaVer int64, tblInfo *TableInfo) {
	h.SchemaVersion = schemaVer
	h.TableInfo = tblInfo
}

// Clean cleans history information.
func (h *HistoryInfo) Clean() {
	h.SchemaVersion = 0
	h.DBInfo = nil
	h.TableInfo = nil
}

// Job is for a DDL operation.
type Job struct {
	ID       int64         `json:"id"`
	Type     ActionType    `json:"type"`
	SchemaID int64         `json:"schema_id"`
	TableID  int64         `json:"table_id"`
	State    JobState      `json:"state"`
	Error    *terror.Error `json:"err"`
	// ErrorCount will be increased, every time we meet an error when running job.
	ErrorCount int64 `json:"err_count"`
	// RowCount means the number of rows that are processed.
	RowCount int64         `json:"row_count"`
	Mu       sync.Mutex    `json:"-"`
	Args     []interface{} `json:"-"`
	// RawArgs : We must use json raw message to delay parsing special args.
	RawArgs     json.RawMessage `json:"raw_args"`
	SchemaState SchemaState     `json:"schema_state"`
	// SnapshotVer means snapshot version for this job.
	SnapshotVer uint64 `json:"snapshot_ver"`
	// StartTS uses timestamp allocated by TSO.
	// Now it's the TS when we put the job to TiKV queue.
	StartTS uint64 `json:"start_ts"`
	// Query string of the ddl job.
	Query      string       `json:"query"`
	BinlogInfo *HistoryInfo `json:"binlog"`

	// Version indicates the DDL job version. For old jobs, it will be 0.
	Version int64 `json:"version"`
}

// FinishTableJob is called when a job is finished.
// It updates the job's state information and adds tblInfo to the binlog.
func (job *Job) FinishTableJob(jobState JobState, schemaState SchemaState, ver int64, tblInfo *TableInfo) {
	job.State = jobState
	job.SchemaState = schemaState
	job.BinlogInfo.AddTableInfo(ver, tblInfo)
}

// FinishDBJob is called when a job is finished.
// It updates the job's state information and adds dbInfo the binlog.
func (job *Job) FinishDBJob(jobState JobState, schemaState SchemaState, ver int64, dbInfo *DBInfo) {
	job.State = jobState
	job.SchemaState = schemaState
	job.BinlogInfo.AddDBInfo(ver, dbInfo)
}

// tsConvert2Time converts timestamp to time.
func tsConvert2Time(ts uint64) time.Time {
	t := int64(ts >> 18) // 18 is for the logical time.
	return time.Unix(t/1e3, (t%1e3)*1e6)
}

// SetRowCount sets the number of rows. Make sure it can pass `make race`.
func (job *Job) SetRowCount(count int64) {
	job.Mu.Lock()
	defer job.Mu.Unlock()

	job.RowCount = count
}

// GetRowCount gets the number of rows. Make sure it can pass `make race`.
func (job *Job) GetRowCount() int64 {
	job.Mu.Lock()
	defer job.Mu.Unlock()

	return job.RowCount
}

// Encode encodes job with json format.
// updateRawArgs is used to determine whether to update the raw args.
func (job *Job) Encode(updateRawArgs bool) ([]byte, error) {
	var err error
	if updateRawArgs {
		job.RawArgs, err = json.Marshal(job.Args)
		if err != nil {
			return nil, errors.Trace(err)
		}
	}

	var b []byte
	job.Mu.Lock()
	defer job.Mu.Unlock()
	b, err = json.Marshal(job)

	return b, errors.Trace(err)
}

// Decode decodes job from the json buffer, we must use DecodeArgs later to
// decode special args for this job.
func (job *Job) Decode(b []byte) error {
	err := json.Unmarshal(b, job)
	return errors.Trace(err)
}

// DecodeArgs decodes job args.
func (job *Job) DecodeArgs(args ...interface{}) error {
	job.Args = args
	err := json.Unmarshal(job.RawArgs, &job.Args)
	return errors.Trace(err)
}

// String implements fmt.Stringer interface.
func (job *Job) String() string {
	rowCount := job.GetRowCount()
	return fmt.Sprintf("ID:%d, Type:%s, State:%s, SchemaState:%s, SchemaID:%d, TableID:%d, RowCount:%d, ArgLen:%d, start time: %v",
		job.ID, job.Type, job.State, job.SchemaState, job.SchemaID, job.TableID, rowCount, len(job.Args), tsConvert2Time(job.StartTS))
}

// IsFinished returns whether job is finished or not.
// If the job state is Done or Cancelled, it is finished.
func (job *Job) IsFinished() bool {
	return job.State == JobStateDone || job.State == JobStateRollbackDone || job.State == JobStateCancelled
}

// IsCancelled returns whether the job is cancelled or not.
func (job *Job) IsCancelled() bool {
	return job.State == JobStateCancelled || job.State == JobStateRollbackDone
}

// IsRollingback returns whether the job is rolling back or not.
func (job *Job) IsRollingback() bool {
	return job.State == JobStateRollingback
}

// IsCancelling returns whether the job is cancelling or not.
func (job *Job) IsCancelling() bool {
	return job.State == JobStateCancelling
}

// IsSynced returns whether the DDL modification is synced among all TiDB servers.
func (job *Job) IsSynced() bool {
	return job.State == JobStateSynced
}

// IsDone returns whether job is done.
func (job *Job) IsDone() bool {
	return job.State == JobStateDone
}

// IsRunning returns whether job is still running or not.
func (job *Job) IsRunning() bool {
	return job.State == JobStateRunning
}

// JobState is for job state.
type JobState byte

// List job states.
const (
	JobStateNone    JobState = 0
	JobStateRunning JobState = 1
	// When DDL encountered an unrecoverable error at reorganization state,
	// some keys has been added already, we need to remove them.
	// JobStateRollingback is the state to do the rolling back job.
	JobStateRollingback  JobState = 2
	JobStateRollbackDone JobState = 3
	JobStateDone         JobState = 4
	JobStateCancelled    JobState = 5
	// JobStateSynced is used to mark the information about the completion of this job
	// has been synchronized to all servers.
	JobStateSynced JobState = 6
	// JobStateCancelling is used to mark the DDL job is cancelled by the client, but the DDL work hasn't handle it.
	JobStateCancelling JobState = 7
)

// String implements fmt.Stringer interface.
func (s JobState) String() string {
	switch s {
	case JobStateRunning:
		return "running"
	case JobStateRollingback:
		return "rollingback"
	case JobStateRollbackDone:
		return "rollback done"
	case JobStateDone:
		return "done"
	case JobStateCancelled:
		return "cancelled"
	case JobStateCancelling:
		return "cancelling"
	case JobStateSynced:
		return "synced"
	default:
		return "none"
	}
}

// SchemaDiff contains the schema modification at a particular schema version.
// It is used to reduce schema reload cost.
type SchemaDiff struct {
	Version  int64      `json:"version"`
	Type     ActionType `json:"type"`
	SchemaID int64      `json:"schema_id"`
	TableID  int64      `json:"table_id"`

	// OldTableID is the table ID before truncate, only used by truncate table DDL.
	OldTableID int64 `json:"old_table_id"`
	// OldSchemaID is the schema ID before rename table, only used by rename table DDL.
	OldSchemaID int64 `json:"old_schema_id"`
}
<commit_msg>model:use map instead of switch statment (#5735)<commit_after>// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package model

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

	"github.com/juju/errors"
	"github.com/pingcap/tidb/terror"
)

// ActionType is the type for DDL action.
type ActionType byte

// List DDL actions.
const (
	ActionNone            ActionType = 0
	ActionCreateSchema    ActionType = 1
	ActionDropSchema      ActionType = 2
	ActionCreateTable     ActionType = 3
	ActionDropTable       ActionType = 4
	ActionAddColumn       ActionType = 5
	ActionDropColumn      ActionType = 6
	ActionAddIndex        ActionType = 7
	ActionDropIndex       ActionType = 8
	ActionAddForeignKey   ActionType = 9
	ActionDropForeignKey  ActionType = 10
	ActionTruncateTable   ActionType = 11
	ActionModifyColumn    ActionType = 12
	ActionRebaseAutoID    ActionType = 13
	ActionRenameTable     ActionType = 14
	ActionSetDefaultValue ActionType = 15
	ActionShardRowID      ActionType = 16
)

var actionMap = map[ActionType]string{
	ActionCreateSchema:    "create schema",
	ActionDropSchema:      "drop schema",
	ActionCreateTable:     "create table",
	ActionDropTable:       "drop table",
	ActionAddColumn:       "add column",
	ActionDropColumn:      "drop column",
	ActionAddIndex:        "add index",
	ActionDropIndex:       "drop index",
	ActionAddForeignKey:   "add foreign key",
	ActionDropForeignKey:  "drop foreign key",
	ActionTruncateTable:   "truncate table",
	ActionModifyColumn:    "modify column",
	ActionRebaseAutoID:    "rebase auto_increment ID",
	ActionRenameTable:     "rename table",
	ActionSetDefaultValue: "set default value",
	ActionShardRowID:      "shard row ID",
}

// String return current ddl action in string
func (action ActionType) String() string {
	if v, ok := actionMap[action]; ok {
		return v
	}
	return "none"
}

// HistoryInfo is used for binlog.
type HistoryInfo struct {
	SchemaVersion int64
	DBInfo        *DBInfo
	TableInfo     *TableInfo
}

// AddDBInfo adds schema version and schema information that are used for binlog.
// dbInfo is added in the following operations: create database, drop database.
func (h *HistoryInfo) AddDBInfo(schemaVer int64, dbInfo *DBInfo) {
	h.SchemaVersion = schemaVer
	h.DBInfo = dbInfo
}

// AddTableInfo adds schema version and table information that are used for binlog.
// tblInfo is added except for the following operations: create database, drop database.
func (h *HistoryInfo) AddTableInfo(schemaVer int64, tblInfo *TableInfo) {
	h.SchemaVersion = schemaVer
	h.TableInfo = tblInfo
}

// Clean cleans history information.
func (h *HistoryInfo) Clean() {
	h.SchemaVersion = 0
	h.DBInfo = nil
	h.TableInfo = nil
}

// Job is for a DDL operation.
type Job struct {
	ID       int64         `json:"id"`
	Type     ActionType    `json:"type"`
	SchemaID int64         `json:"schema_id"`
	TableID  int64         `json:"table_id"`
	State    JobState      `json:"state"`
	Error    *terror.Error `json:"err"`
	// ErrorCount will be increased, every time we meet an error when running job.
	ErrorCount int64 `json:"err_count"`
	// RowCount means the number of rows that are processed.
	RowCount int64         `json:"row_count"`
	Mu       sync.Mutex    `json:"-"`
	Args     []interface{} `json:"-"`
	// RawArgs : We must use json raw message to delay parsing special args.
	RawArgs     json.RawMessage `json:"raw_args"`
	SchemaState SchemaState     `json:"schema_state"`
	// SnapshotVer means snapshot version for this job.
	SnapshotVer uint64 `json:"snapshot_ver"`
	// StartTS uses timestamp allocated by TSO.
	// Now it's the TS when we put the job to TiKV queue.
	StartTS uint64 `json:"start_ts"`
	// Query string of the ddl job.
	Query      string       `json:"query"`
	BinlogInfo *HistoryInfo `json:"binlog"`

	// Version indicates the DDL job version. For old jobs, it will be 0.
	Version int64 `json:"version"`
}

// FinishTableJob is called when a job is finished.
// It updates the job's state information and adds tblInfo to the binlog.
func (job *Job) FinishTableJob(jobState JobState, schemaState SchemaState, ver int64, tblInfo *TableInfo) {
	job.State = jobState
	job.SchemaState = schemaState
	job.BinlogInfo.AddTableInfo(ver, tblInfo)
}

// FinishDBJob is called when a job is finished.
// It updates the job's state information and adds dbInfo the binlog.
func (job *Job) FinishDBJob(jobState JobState, schemaState SchemaState, ver int64, dbInfo *DBInfo) {
	job.State = jobState
	job.SchemaState = schemaState
	job.BinlogInfo.AddDBInfo(ver, dbInfo)
}

// tsConvert2Time converts timestamp to time.
func tsConvert2Time(ts uint64) time.Time {
	t := int64(ts >> 18) // 18 is for the logical time.
	return time.Unix(t/1e3, (t%1e3)*1e6)
}

// SetRowCount sets the number of rows. Make sure it can pass `make race`.
func (job *Job) SetRowCount(count int64) {
	job.Mu.Lock()
	defer job.Mu.Unlock()

	job.RowCount = count
}

// GetRowCount gets the number of rows. Make sure it can pass `make race`.
func (job *Job) GetRowCount() int64 {
	job.Mu.Lock()
	defer job.Mu.Unlock()

	return job.RowCount
}

// Encode encodes job with json format.
// updateRawArgs is used to determine whether to update the raw args.
func (job *Job) Encode(updateRawArgs bool) ([]byte, error) {
	var err error
	if updateRawArgs {
		job.RawArgs, err = json.Marshal(job.Args)
		if err != nil {
			return nil, errors.Trace(err)
		}
	}

	var b []byte
	job.Mu.Lock()
	defer job.Mu.Unlock()
	b, err = json.Marshal(job)

	return b, errors.Trace(err)
}

// Decode decodes job from the json buffer, we must use DecodeArgs later to
// decode special args for this job.
func (job *Job) Decode(b []byte) error {
	err := json.Unmarshal(b, job)
	return errors.Trace(err)
}

// DecodeArgs decodes job args.
func (job *Job) DecodeArgs(args ...interface{}) error {
	job.Args = args
	err := json.Unmarshal(job.RawArgs, &job.Args)
	return errors.Trace(err)
}

// String implements fmt.Stringer interface.
func (job *Job) String() string {
	rowCount := job.GetRowCount()
	return fmt.Sprintf("ID:%d, Type:%s, State:%s, SchemaState:%s, SchemaID:%d, TableID:%d, RowCount:%d, ArgLen:%d, start time: %v",
		job.ID, job.Type, job.State, job.SchemaState, job.SchemaID, job.TableID, rowCount, len(job.Args), tsConvert2Time(job.StartTS))
}

// IsFinished returns whether job is finished or not.
// If the job state is Done or Cancelled, it is finished.
func (job *Job) IsFinished() bool {
	return job.State == JobStateDone || job.State == JobStateRollbackDone || job.State == JobStateCancelled
}

// IsCancelled returns whether the job is cancelled or not.
func (job *Job) IsCancelled() bool {
	return job.State == JobStateCancelled || job.State == JobStateRollbackDone
}

// IsRollingback returns whether the job is rolling back or not.
func (job *Job) IsRollingback() bool {
	return job.State == JobStateRollingback
}

// IsCancelling returns whether the job is cancelling or not.
func (job *Job) IsCancelling() bool {
	return job.State == JobStateCancelling
}

// IsSynced returns whether the DDL modification is synced among all TiDB servers.
func (job *Job) IsSynced() bool {
	return job.State == JobStateSynced
}

// IsDone returns whether job is done.
func (job *Job) IsDone() bool {
	return job.State == JobStateDone
}

// IsRunning returns whether job is still running or not.
func (job *Job) IsRunning() bool {
	return job.State == JobStateRunning
}

// JobState is for job state.
type JobState byte

// List job states.
const (
	JobStateNone    JobState = 0
	JobStateRunning JobState = 1
	// When DDL encountered an unrecoverable error at reorganization state,
	// some keys has been added already, we need to remove them.
	// JobStateRollingback is the state to do the rolling back job.
	JobStateRollingback  JobState = 2
	JobStateRollbackDone JobState = 3
	JobStateDone         JobState = 4
	JobStateCancelled    JobState = 5
	// JobStateSynced is used to mark the information about the completion of this job
	// has been synchronized to all servers.
	JobStateSynced JobState = 6
	// JobStateCancelling is used to mark the DDL job is cancelled by the client, but the DDL work hasn't handle it.
	JobStateCancelling JobState = 7
)

// String implements fmt.Stringer interface.
func (s JobState) String() string {
	switch s {
	case JobStateRunning:
		return "running"
	case JobStateRollingback:
		return "rollingback"
	case JobStateRollbackDone:
		return "rollback done"
	case JobStateDone:
		return "done"
	case JobStateCancelled:
		return "cancelled"
	case JobStateCancelling:
		return "cancelling"
	case JobStateSynced:
		return "synced"
	default:
		return "none"
	}
}

// SchemaDiff contains the schema modification at a particular schema version.
// It is used to reduce schema reload cost.
type SchemaDiff struct {
	Version  int64      `json:"version"`
	Type     ActionType `json:"type"`
	SchemaID int64      `json:"schema_id"`
	TableID  int64      `json:"table_id"`

	// OldTableID is the table ID before truncate, only used by truncate table DDL.
	OldTableID int64 `json:"old_table_id"`
	// OldSchemaID is the schema ID before rename table, only used by rename table DDL.
	OldSchemaID int64 `json:"old_schema_id"`
}
<|endoftext|>


<commit_before>package digests

import (
	"testing"

	"github.com/ion-channel/ionic/scanner"
	"github.com/ion-channel/ionic/scans"

	"github.com/franela/goblin"
	. "github.com/onsi/gomega"
)

func TestDependenciesDigests(t *testing.T) {
	g := goblin.Goblin(t)
	RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) })

	g.Describe("Dependencies", func() {
		g.It("should produce digests", func() {
			s := &scanner.ScanStatus{}
			e := scans.NewEval()
			e.TranslatedResults = &scans.TranslatedResults{
				Type: "dependency",
				Data: scans.DependencyResults{
					Dependencies: nil,
					Meta: scans.DependencyMeta{
						FirstDegreeCount:     2,
						NoVersionCount:       1,
						TotalUniqueCount:     115,
						UpdateAvailableCount: 2,
					},
				},
			}

			ds, err := dependencyDigests(s, e)
			Expect(err).To(BeNil())
			Expect(len(ds)).To(Equal(4))
			Expect(ds[0].Title).To(Equal("dependencies outdated"))
			Expect(string(ds[0].Data)).To(Equal(`{"count":2}`))
			Expect(ds[0].Pending).To(BeFalse())
			Expect(ds[0].Errored).To(BeFalse())

			Expect(ds[1].Title).To(Equal("dependency no version specified"))
			Expect(string(ds[1].Data)).To(Equal(`{"count":1}`))
			Expect(ds[1].Warning).To(BeTrue())
			Expect(ds[1].Pending).To(BeFalse())
			Expect(ds[1].Errored).To(BeFalse())

			Expect(ds[2].Title).To(Equal("direct dependencies"))
			Expect(string(ds[2].Data)).To(Equal(`{"count":2}`))
			Expect(ds[2].Pending).To(BeFalse())
			Expect(ds[2].Errored).To(BeFalse())

			Expect(ds[3].Title).To(Equal("transitive dependencies"))
			Expect(string(ds[3].Data)).To(Equal(`{"count":113}`))
			Expect(ds[3].Pending).To(BeFalse())
			Expect(ds[3].Errored).To(BeFalse())
		})
	})
}
<commit_msg>adds unit tests to transitive dependencies warning<commit_after>package digests

import (
	"testing"

	"github.com/ion-channel/ionic/scanner"
	"github.com/ion-channel/ionic/scans"

	"github.com/franela/goblin"
	. "github.com/onsi/gomega"
)

func TestDependenciesDigests(t *testing.T) {
	g := goblin.Goblin(t)
	RegisterFailHandler(func(m string, _ ...int) { g.Fail(m) })

	g.Describe("Dependencies", func() {
		g.It("should produce digests", func() {
			s := &scanner.ScanStatus{}
			e := scans.NewEval()
			e.TranslatedResults = &scans.TranslatedResults{
				Type: "dependency",
				Data: scans.DependencyResults{
					Dependencies: nil,
					Meta: scans.DependencyMeta{
						FirstDegreeCount:     2,
						NoVersionCount:       1,
						TotalUniqueCount:     115,
						UpdateAvailableCount: 2,
					},
				},
			}

			ds, err := dependencyDigests(s, e)
			Expect(err).To(BeNil())
			Expect(len(ds)).To(Equal(4))
			Expect(ds[0].Title).To(Equal("dependencies outdated"))
			Expect(string(ds[0].Data)).To(Equal(`{"count":2}`))
			Expect(ds[0].Pending).To(BeFalse())
			Expect(ds[0].Errored).To(BeFalse())

			Expect(ds[1].Title).To(Equal("dependency no version specified"))
			Expect(string(ds[1].Data)).To(Equal(`{"count":1}`))
			Expect(ds[1].Warning).To(BeTrue())
			Expect(ds[1].Pending).To(BeFalse())
			Expect(ds[1].Errored).To(BeFalse())

			Expect(ds[2].Title).To(Equal("direct dependencies"))
			Expect(string(ds[2].Data)).To(Equal(`{"count":2}`))
			Expect(ds[2].Pending).To(BeFalse())
			Expect(ds[2].Errored).To(BeFalse())

			Expect(ds[3].Title).To(Equal("transitive dependencies"))
			Expect(string(ds[3].Data)).To(Equal(`{"count":113}`))
			Expect(ds[3].Pending).To(BeFalse())
			Expect(ds[3].Errored).To(BeFalse())
		})

		g.It("should have no warning with transitive dependencies", func() {
			s := &scanner.ScanStatus{}
			e := scans.NewEval()
			e.TranslatedResults = &scans.TranslatedResults{
				Type: "dependency",
				Data: scans.DependencyResults{
					Dependencies: nil,
					Meta: scans.DependencyMeta{
						FirstDegreeCount:     2,
						NoVersionCount:       1,
						TotalUniqueCount:     115,
						UpdateAvailableCount: 2,
					},
				},
			}

			ds, err := dependencyDigests(s, e)
			Expect(err).To(BeNil())
			Expect(ds[3].Warning).To(BeFalse())
			Expect(ds[3].WarningMessage).To(BeEmpty())
			Expect(string(ds[3].Data)).To(ContainSubstring("count\":113"))
			Expect(string(ds[3].Title)).To(Equal("transitive dependencies"))
		})

		g.It("should have a warning with transitive dependencies", func() {
			s := &scanner.ScanStatus{}
			e := scans.NewEval()
			e.TranslatedResults = &scans.TranslatedResults{
				Type: "dependency",
				Data: scans.DependencyResults{
					Dependencies: nil,
					Meta: scans.DependencyMeta{
						FirstDegreeCount:     2,
						NoVersionCount:       1,
						TotalUniqueCount:     2,
						UpdateAvailableCount: 2,
					},
				},
			}

			ds, err := dependencyDigests(s, e)
			Expect(err).To(BeNil())
			Expect(ds[3].Warning).To(BeTrue())
			Expect(ds[3].WarningMessage).To(Equal("no transitive dependencies found"))
			Expect(string(ds[3].Data)).To(ContainSubstring("count\":0"))
			Expect(string(ds[3].Title)).To(Equal("transitive dependencies"))
		})
	})

}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/user"
)

type iobeamConfig struct {
	Name string `json:"profile"`
}

const (
	CLI_VERSION      = "0.2.0"
	pathSeparator    = string(os.PathSeparator)
	dotDirName       = ".iobeam"
	defaultConfig    = "profile"
	profileFileName  = "profile.config"
	DefaultApiServer = "https://api.iobeam.com"
)

func InitConfig() (*iobeamConfig, error) {
	c := &iobeamConfig{
		Name: "default",
	}
	err := c.save()
	if err != nil {
		return nil, err
	}
	return c, nil
}

func GetDotDir() string {
	user, err := user.Current()

	if err != nil {
		return os.TempDir()
	}

	return user.HomeDir + pathSeparator + dotDirName
}

func defaultConfigPath() string {
	return GetDotDir() + pathSeparator + defaultConfig
}

func makeAllOnPath(path string) error {
	err := os.MkdirAll(path, 0700)
	if err != nil {
		return err
	}
	return nil
}

func saveJson(path string, obj interface{}) error {
	_ = os.Remove(path) // error only if it does not exist, ignore.

	file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		return err
	}

	defer file.Close()
	return json.NewEncoder(file).Encode(obj)
}

func readJson(path string, obj interface{}) error {
	file, err := os.Open(path)

	if err != nil {
		return err
	}

	defer file.Close()
	return json.NewDecoder(file).Decode(obj)
}

// save writes the config to disk in the user's .iobeam directory.
func (c *iobeamConfig) save() error {
	err := makeAllOnPath(GetDotDir())
	if err != nil {
		return err
	}
	return saveJson(defaultConfigPath(), c)
}

func (c *iobeamConfig) read(path string) error {
	return readJson(path, c)
}

func readConfig(path string) (*iobeamConfig, error) {
	c := new(iobeamConfig)

	err := c.read(path)
	if err != nil {
		return nil, err
	}

	return c, err
}

func ReadDefaultConfig() (*iobeamConfig, error) {
	return readConfig(defaultConfigPath())
}

type Profile struct {
	Name            string `json:"-"`
	Server          string `json:"server"`
	ActiveProject   uint64 `json:"active_project"`
	ActiveUser      uint64 `json:"active_user"`
	ActiveUserEmail string `json:"activer_user_email"`
	// TODO: Don't export active fields.
}

func InitProfile(name string) (*Profile, error) {
	return InitProfileWithServer(name, DefaultApiServer)
}

func InitProfileWithServer(name, server string) (*Profile, error) {
	p := &Profile{
		Name:          name,
		Server:        server,
		ActiveProject: 0,
		ActiveUser:    0,
	}
	err := p.save()
	if err != nil {
		return nil, err
	}
	return p, nil
}

func baseProfilePath(name string) string {
	return GetDotDir() + pathSeparator + name
}

func profilePath(name string) string {
	// e.x. ~user/.iobeam/default/profile.config
	return baseProfilePath(name) + pathSeparator + profileFileName
}

func (p *Profile) save() error {
	err := makeAllOnPath(p.GetDir())
	if err != nil {
		return err
	}
	return saveJson(profilePath(p.Name), p)
}

func (p *Profile) read() error {
	return readJson(p.GetFile(), p)
}

func (p *Profile) GetDir() string {
	return baseProfilePath(p.Name)
}

func (p *Profile) GetFile() string {
	return profilePath(p.Name)
}

func (p *Profile) UpdateActiveUser(uid uint64, email string) error {
	p.ActiveUser = uid
	p.ActiveUserEmail = email
	return p.save()
}

func (p *Profile) UpdateActiveProject(pid uint64) error {
	p.ActiveProject = pid
	return p.save()
}

func ReadProfile(name string) (*Profile, error) {
	p := new(Profile)
	p.Name = name

	err := p.read()
	if err != nil {
		return nil, err
	}

	return p, nil
}

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

	list := make([]string, 0)
	for _, f := range files {
		if f.IsDir() {
			list = append(list, f.Name())
		}
	}

	return list, nil
}

func SwitchProfile(name string) error {
	path := baseProfilePath(name)
	_, err := os.Stat(path)
	if err != nil {
		return err
	}

	c := &iobeamConfig{
		Name: name,
	}
	err = c.save()
	return err
}
<commit_msg>version 0.2.1<commit_after>package config

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"os/user"
)

type iobeamConfig struct {
	Name string `json:"profile"`
}

const (
	CLI_VERSION      = "0.2.1"
	pathSeparator    = string(os.PathSeparator)
	dotDirName       = ".iobeam"
	defaultConfig    = "profile"
	profileFileName  = "profile.config"
	DefaultApiServer = "https://api.iobeam.com"
)

func InitConfig() (*iobeamConfig, error) {
	c := &iobeamConfig{
		Name: "default",
	}
	err := c.save()
	if err != nil {
		return nil, err
	}
	return c, nil
}

func GetDotDir() string {
	user, err := user.Current()

	if err != nil {
		return os.TempDir()
	}

	return user.HomeDir + pathSeparator + dotDirName
}

func defaultConfigPath() string {
	return GetDotDir() + pathSeparator + defaultConfig
}

func makeAllOnPath(path string) error {
	err := os.MkdirAll(path, 0700)
	if err != nil {
		return err
	}
	return nil
}

func saveJson(path string, obj interface{}) error {
	_ = os.Remove(path) // error only if it does not exist, ignore.

	file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		return err
	}

	defer file.Close()
	return json.NewEncoder(file).Encode(obj)
}

func readJson(path string, obj interface{}) error {
	file, err := os.Open(path)

	if err != nil {
		return err
	}

	defer file.Close()
	return json.NewDecoder(file).Decode(obj)
}

// save writes the config to disk in the user's .iobeam directory.
func (c *iobeamConfig) save() error {
	err := makeAllOnPath(GetDotDir())
	if err != nil {
		return err
	}
	return saveJson(defaultConfigPath(), c)
}

func (c *iobeamConfig) read(path string) error {
	return readJson(path, c)
}

func readConfig(path string) (*iobeamConfig, error) {
	c := new(iobeamConfig)

	err := c.read(path)
	if err != nil {
		return nil, err
	}

	return c, err
}

func ReadDefaultConfig() (*iobeamConfig, error) {
	return readConfig(defaultConfigPath())
}

type Profile struct {
	Name            string `json:"-"`
	Server          string `json:"server"`
	ActiveProject   uint64 `json:"active_project"`
	ActiveUser      uint64 `json:"active_user"`
	ActiveUserEmail string `json:"activer_user_email"`
	// TODO: Don't export active fields.
}

func InitProfile(name string) (*Profile, error) {
	return InitProfileWithServer(name, DefaultApiServer)
}

func InitProfileWithServer(name, server string) (*Profile, error) {
	p := &Profile{
		Name:          name,
		Server:        server,
		ActiveProject: 0,
		ActiveUser:    0,
	}
	err := p.save()
	if err != nil {
		return nil, err
	}
	return p, nil
}

func baseProfilePath(name string) string {
	return GetDotDir() + pathSeparator + name
}

func profilePath(name string) string {
	// e.x. ~user/.iobeam/default/profile.config
	return baseProfilePath(name) + pathSeparator + profileFileName
}

func (p *Profile) save() error {
	err := makeAllOnPath(p.GetDir())
	if err != nil {
		return err
	}
	return saveJson(profilePath(p.Name), p)
}

func (p *Profile) read() error {
	return readJson(p.GetFile(), p)
}

func (p *Profile) GetDir() string {
	return baseProfilePath(p.Name)
}

func (p *Profile) GetFile() string {
	return profilePath(p.Name)
}

func (p *Profile) UpdateActiveUser(uid uint64, email string) error {
	p.ActiveUser = uid
	p.ActiveUserEmail = email
	return p.save()
}

func (p *Profile) UpdateActiveProject(pid uint64) error {
	p.ActiveProject = pid
	return p.save()
}

func ReadProfile(name string) (*Profile, error) {
	p := new(Profile)
	p.Name = name

	err := p.read()
	if err != nil {
		return nil, err
	}

	return p, nil
}

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

	list := make([]string, 0)
	for _, f := range files {
		if f.IsDir() {
			list = append(list, f.Name())
		}
	}

	return list, nil
}

func SwitchProfile(name string) error {
	path := baseProfilePath(name)
	_, err := os.Stat(path)
	if err != nil {
		return err
	}

	c := &iobeamConfig{
		Name: name,
	}
	err = c.save()
	return err
}
<|endoftext|>