<commit_before>package process

// Logic for this file is largely based on:
// https://github.com/jarib/childprocess/blob/783f7a00a1678b5d929062564ef5ae76822dfd62/lib/childprocess/unix/process.rb

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"sync"
	"sync/atomic"
	"syscall"
	"time"

	"github.com/buildkite/agent/logger"
	"github.com/mattn/go-shellwords"
)

type Process struct {
	Pid        int
	PTY        bool
	Timestamp  bool
	Script     string
	Env        []string
	ExitStatus string

	buffer  outputBuffer
	command *exec.Cmd

	// This callback is called when the process offically starts
	StartCallback func()

	// For every line in the process output, this callback will be called
	// with the contents of the line if its filter returns true.
	LineCallback       func(string)
	LinePreProcessor   func(string) string
	LineCallbackFilter func(string) bool

	// Running is stored as an int32 so we can use atomic operations to
	// set/get it (it's accessed by multiple goroutines)
	running int32
}

// If you change header parsing here make sure to change it in the
// buildkite.com frontend logic, too

var headerExpansionRegex = regexp.MustCompile("^(?:\\^\\^\\^\\s+\\+\\+\\+)\\s*$")

func (p *Process) Start() error {
	args, err := shellwords.Parse(p.Script)
	if err != nil {
		return err
	}

	p.command = exec.Command(args[0], args[1:]...)

	// Copy the current processes ENV and merge in the new ones. We do this
	// so the sub process gets PATH and stuff. We merge our path in over
	// the top of the current one so the ENV from Buildkite and the agent
	// take precedence over the agent
	currentEnv := os.Environ()
	p.command.Env = append(currentEnv, p.Env...)

	var waitGroup sync.WaitGroup

	lineReaderPipe, lineWriterPipe := io.Pipe()

	var multiWriter io.Writer
	if p.Timestamp {
		multiWriter = io.MultiWriter(lineWriterPipe)
	} else {
		multiWriter = io.MultiWriter(&p.buffer, lineWriterPipe)
	}

	// Toggle between running in a pty
	if p.PTY {
		pty, err := StartPTY(p.command)
		if err != nil {
			p.ExitStatus = "1"
			return err
		}

		p.Pid = p.command.Process.Pid
		p.setRunning(true)

		waitGroup.Add(1)

		go func() {
			logger.Debug("[Process] Starting to copy PTY to the buffer")

			// Copy the pty to our buffer. This will block until it
			// EOF's or something breaks.
			_, err = io.Copy(multiWriter, pty)
			if e, ok := err.(*os.PathError); ok && e.Err == syscall.EIO {
				// We can safely ignore this error, because
				// it's just the PTY telling us that it closed
				// successfully.  See:
				// https://github.com/buildkite/agent/pull/34#issuecomment-46080419
				err = nil
			}

			if err != nil {
				logger.Error("[Process] PTY output copy failed with error: %T: %v", err, err)
			} else {
				logger.Debug("[Process] PTY has finished being copied to the buffer")
			}

			waitGroup.Done()
		}()
	} else {
		p.command.Stdout = multiWriter
		p.command.Stderr = multiWriter
		p.command.Stdin = nil

		err := p.command.Start()
		if err != nil {
			p.ExitStatus = "1"
			return err
		}

		p.Pid = p.command.Process.Pid
		p.setRunning(true)
	}

	logger.Info("[Process] Process is running with PID: %d", p.Pid)

	// Add the line callback routine to the waitGroup
	waitGroup.Add(1)

	go func() {
		logger.Debug("[LineScanner] Starting to read lines")

		reader := bufio.NewReader(lineReaderPipe)

		var appending []byte
		var lineCallbackWaitGroup sync.WaitGroup

		for {
			line, isPrefix, err := reader.ReadLine()
			if err != nil {
				if err == io.EOF {
					logger.Debug("[LineScanner] Encountered EOF")
					break
				}

				logger.Error("[LineScanner] Failed to read: (%T: %v)", err, err)
			}

			// If isPrefix is true, that means we've got a really
			// long line incoming, and we'll keep appending to it
			// until isPrefix is false (which means the long line
			// has ended.
			if isPrefix && appending == nil {
				logger.Debug("[LineScanner] Line is too long to read, going to buffer it until it finishes")
				// bufio.ReadLine returns a slice which is only valid until the next invocation
				// since it points to its own internal buffer array. To accumulate the entire
				// result we make a copy of the first prefix, and insure there is spare capacity
				// for future appends to minimize the need for resizing on append.
				appending = make([]byte, len(line), (cap(line))*2)
				copy(appending, line)

				continue
			}

			// Should we be appending?
			if appending != nil {
				appending = append(appending, line...)

				// No more isPrefix! Line is finished!
				if !isPrefix {
					logger.Debug("[LineScanner] Finished buffering long line")
					line = appending

					// Reset appending back to nil
					appending = nil
				} else {
					continue
				}
			}

			// If we're timestamping this main thread will take
			// the hit of running the regex so we can build up
			// the timestamped buffer without breaking headers,
			// otherwise we let the goroutines take the perf hit.

			checkedForCallback := false
			lineHasCallback := false
			lineString := p.LinePreProcessor(string(line))

			// Create the prefixed buffer
			if p.Timestamp {
				lineHasCallback = p.LineCallbackFilter(lineString)
				checkedForCallback = true
				if lineHasCallback || headerExpansionRegex.MatchString(lineString) {
					// Don't timestamp special lines (e.g. header)
					p.buffer.WriteString(fmt.Sprintf("%s\n", line))
				} else {
					currentTime := time.Now().UTC().Format(time.RFC3339)
					p.buffer.WriteString(fmt.Sprintf("[%s] %s\n", currentTime, line))
				}
			}

			if lineHasCallback || !checkedForCallback {
				lineCallbackWaitGroup.Add(1)
				go func(line string) {
					defer lineCallbackWaitGroup.Done()
					if (checkedForCallback && lineHasCallback) || p.LineCallbackFilter(lineString) {
						p.LineCallback(line)
					}
				}(lineString)
			}
		}

		// We need to make sure all the line callbacks have finish before
		// finish up the process
		logger.Debug("[LineScanner] Waiting for callbacks to finish")
		lineCallbackWaitGroup.Wait()

		logger.Debug("[LineScanner] Finished")
		waitGroup.Done()
	}()

	// Call the StartCallback
	go p.StartCallback()

	// Wait until the process has finished. The returned error is nil if the command runs,
	// has no problems copying stdin, stdout, and stderr, and exits with a zero exit status.
	waitResult := p.command.Wait()

	// Close the line writer pipe
	lineWriterPipe.Close()

	// The process is no longer running at this point
	p.setRunning(false)

	// Find the exit status of the script
	p.ExitStatus = getExitStatus(waitResult)

	logger.Info("Process with PID: %d finished with Exit Status: %s", p.Pid, p.ExitStatus)

	// Sometimes (in docker containers) io.Copy never seems to finish. This is a mega
	// hack around it. If it doesn't finish after 1 second, just continue.
	logger.Debug("[Process] Waiting for routines to finish")
	err = timeoutWait(&waitGroup)
	if err != nil {
		logger.Debug("[Process] Timed out waiting for wait group: (%T: %v)", err, err)
	}

	// No error occurred so we can return nil
	return nil
}

func (p *Process) Output() string {
	return p.buffer.String()
}

func (p *Process) Kill() error {
	var err error
	if runtime.GOOS == "windows" {
		// Sending Interrupt on Windows is not implemented.
		// https://golang.org/src/os/exec.go?s=3842:3884#L110
		err = exec.Command("CMD", "/C", "TASKKILL", "/F", "/PID", strconv.Itoa(p.Pid)).Run()
	} else {
		// Send a sigterm
		err = p.signal(syscall.SIGTERM)
	}
	if err != nil {
		return err
	}

	// Make a channel that we'll use as a timeout
	c := make(chan int, 1)
	checking := true

	// Start a routine that checks to see if the process
	// is still alive.
	go func() {
		for checking {
			logger.Debug("[Process] Checking to see if PID: %d is still alive", p.Pid)

			foundProcess, err := os.FindProcess(p.Pid)

			// Can't find the process at all
			if err != nil {
				logger.Debug("[Process] Could not find process with PID: %d", p.Pid)

				break
			}

			// We have some information about the process
			if foundProcess != nil {
				processState, err := foundProcess.Wait()

				if err != nil || processState.Exited() {
					logger.Debug("[Process] Process with PID: %d has exited.", p.Pid)

					break
				}
			}

			// Retry in a moment
			sleepTime := time.Duration(1 * time.Second)
			time.Sleep(sleepTime)
		}

		c <- 1
	}()

	// Timeout this process after 3 seconds
	select {
	case _ = <-c:
		// Was successfully terminated
	case <-time.After(10 * time.Second):
		// Stop checking in the routine above
		checking = false

		// Forcefully kill the thing
		err = p.signal(syscall.SIGKILL)

		if err != nil {
			return err
		}
	}

	return nil
}

func (p *Process) signal(sig os.Signal) error {
	if p.command != nil && p.command.Process != nil {
		logger.Debug("[Process] Sending signal: %s to PID: %d", sig.String(), p.Pid)

		err := p.command.Process.Signal(sig)
		if err != nil {
			logger.Error("[Process] Failed to send signal: %s to PID: %d (%T: %v)", sig.String(), p.Pid, err, err)
			return err
		}
	} else {
		logger.Debug("[Process] No process to signal yet")
	}

	return nil
}

// Returns whether or not the process is running
func (p *Process) IsRunning() bool {
	return atomic.LoadInt32(&p.running) != 0
}

// Sets the running flag of the process
func (p *Process) setRunning(r bool) {
	// Use the atomic package to avoid race conditions when setting the
	// `running` value from multiple routines
	if r {
		atomic.StoreInt32(&p.running, 1)
	} else {
		atomic.StoreInt32(&p.running, 0)
	}
}

// https://github.com/hnakamur/commango/blob/fe42b1cf82bf536ce7e24dceaef6656002e03743/os/executil/executil.go#L29
// TODO: Can this be better?
func getExitStatus(waitResult error) string {
	exitStatus := -1

	if waitResult != nil {
		if err, ok := waitResult.(*exec.ExitError); ok {
			if s, ok := err.Sys().(syscall.WaitStatus); ok {
				exitStatus = s.ExitStatus()
			} else {
				logger.Error("[Process] Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus.")
			}
		} else {
			logger.Error("[Process] Unexpected error type in getExitStatus: %#v", waitResult)
		}
	} else {
		exitStatus = 0
	}

	return fmt.Sprintf("%d", exitStatus)
}

func timeoutWait(waitGroup *sync.WaitGroup) error {
	// Make a chanel that we'll use as a timeout
	c := make(chan int, 1)

	// Start waiting for the routines to finish
	go func() {
		waitGroup.Wait()
		c <- 1
	}()

	select {
	case _ = <-c:
		return nil
	case <-time.After(10 * time.Second):
		return errors.New("Timeout")
	}

	return nil
}

// outputBuffer is a goroutine safe bytes.Buffer
type outputBuffer struct {
	sync.RWMutex
	buf bytes.Buffer
}

// Write appends the contents of p to the buffer, growing the buffer as needed. It returns
// the number of bytes written.
func (ob *outputBuffer) Write(p []byte) (n int, err error) {
	ob.Lock()
	defer ob.Unlock()
	return ob.buf.Write(p)
}

// WriteString appends the contents of s to the buffer, growing the buffer as needed. It returns
// the number of bytes written.
func (ob *outputBuffer) WriteString(s string) (n int, err error) {
	return ob.Write([]byte(s))
}

// String returns the contents of the unread portion of the buffer
// as a string.  If the Buffer is a nil pointer, it returns "<nil>".
func (ob *outputBuffer) String() string {
	ob.RLock()
	defer ob.RUnlock()
	return ob.buf.String()
}
<commit_msg>Handle windows paths in process.Process<commit_after>package process

// Logic for this file is largely based on:
// https://github.com/jarib/childprocess/blob/783f7a00a1678b5d929062564ef5ae76822dfd62/lib/childprocess/unix/process.rb

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strconv"
	"sync"
	"sync/atomic"
	"syscall"
	"time"

	"github.com/buildkite/agent/logger"
	"github.com/mattn/go-shellwords"
)

type Process struct {
	Pid        int
	PTY        bool
	Timestamp  bool
	Script     string
	Env        []string
	ExitStatus string

	buffer  outputBuffer
	command *exec.Cmd

	// This callback is called when the process offically starts
	StartCallback func()

	// For every line in the process output, this callback will be called
	// with the contents of the line if its filter returns true.
	LineCallback       func(string)
	LinePreProcessor   func(string) string
	LineCallbackFilter func(string) bool

	// Running is stored as an int32 so we can use atomic operations to
	// set/get it (it's accessed by multiple goroutines)
	running int32
}

// If you change header parsing here make sure to change it in the
// buildkite.com frontend logic, too

var headerExpansionRegex = regexp.MustCompile("^(?:\\^\\^\\^\\s+\\+\\+\\+)\\s*$")

func (p *Process) Start() error {
	// Shellwords has trouble parsing windows paths
	args, err := shellwords.Parse(filepath.ToSlash(p.Script))
	if err != nil {
		return err
	}

	p.command = exec.Command(args[0], args[1:]...)

	// Copy the current processes ENV and merge in the new ones. We do this
	// so the sub process gets PATH and stuff. We merge our path in over
	// the top of the current one so the ENV from Buildkite and the agent
	// take precedence over the agent
	currentEnv := os.Environ()
	p.command.Env = append(currentEnv, p.Env...)

	var waitGroup sync.WaitGroup

	lineReaderPipe, lineWriterPipe := io.Pipe()

	var multiWriter io.Writer
	if p.Timestamp {
		multiWriter = io.MultiWriter(lineWriterPipe)
	} else {
		multiWriter = io.MultiWriter(&p.buffer, lineWriterPipe)
	}

	// Toggle between running in a pty
	if p.PTY {
		pty, err := StartPTY(p.command)
		if err != nil {
			p.ExitStatus = "1"
			return err
		}

		p.Pid = p.command.Process.Pid
		p.setRunning(true)

		waitGroup.Add(1)

		go func() {
			logger.Debug("[Process] Starting to copy PTY to the buffer")

			// Copy the pty to our buffer. This will block until it
			// EOF's or something breaks.
			_, err = io.Copy(multiWriter, pty)
			if e, ok := err.(*os.PathError); ok && e.Err == syscall.EIO {
				// We can safely ignore this error, because
				// it's just the PTY telling us that it closed
				// successfully.  See:
				// https://github.com/buildkite/agent/pull/34#issuecomment-46080419
				err = nil
			}

			if err != nil {
				logger.Error("[Process] PTY output copy failed with error: %T: %v", err, err)
			} else {
				logger.Debug("[Process] PTY has finished being copied to the buffer")
			}

			waitGroup.Done()
		}()
	} else {
		p.command.Stdout = multiWriter
		p.command.Stderr = multiWriter
		p.command.Stdin = nil

		err := p.command.Start()
		if err != nil {
			p.ExitStatus = "1"
			return err
		}

		p.Pid = p.command.Process.Pid
		p.setRunning(true)
	}

	logger.Info("[Process] Process is running with PID: %d", p.Pid)

	// Add the line callback routine to the waitGroup
	waitGroup.Add(1)

	go func() {
		logger.Debug("[LineScanner] Starting to read lines")

		reader := bufio.NewReader(lineReaderPipe)

		var appending []byte
		var lineCallbackWaitGroup sync.WaitGroup

		for {
			line, isPrefix, err := reader.ReadLine()
			if err != nil {
				if err == io.EOF {
					logger.Debug("[LineScanner] Encountered EOF")
					break
				}

				logger.Error("[LineScanner] Failed to read: (%T: %v)", err, err)
			}

			// If isPrefix is true, that means we've got a really
			// long line incoming, and we'll keep appending to it
			// until isPrefix is false (which means the long line
			// has ended.
			if isPrefix && appending == nil {
				logger.Debug("[LineScanner] Line is too long to read, going to buffer it until it finishes")
				// bufio.ReadLine returns a slice which is only valid until the next invocation
				// since it points to its own internal buffer array. To accumulate the entire
				// result we make a copy of the first prefix, and insure there is spare capacity
				// for future appends to minimize the need for resizing on append.
				appending = make([]byte, len(line), (cap(line))*2)
				copy(appending, line)

				continue
			}

			// Should we be appending?
			if appending != nil {
				appending = append(appending, line...)

				// No more isPrefix! Line is finished!
				if !isPrefix {
					logger.Debug("[LineScanner] Finished buffering long line")
					line = appending

					// Reset appending back to nil
					appending = nil
				} else {
					continue
				}
			}

			// If we're timestamping this main thread will take
			// the hit of running the regex so we can build up
			// the timestamped buffer without breaking headers,
			// otherwise we let the goroutines take the perf hit.

			checkedForCallback := false
			lineHasCallback := false
			lineString := p.LinePreProcessor(string(line))

			// Create the prefixed buffer
			if p.Timestamp {
				lineHasCallback = p.LineCallbackFilter(lineString)
				checkedForCallback = true
				if lineHasCallback || headerExpansionRegex.MatchString(lineString) {
					// Don't timestamp special lines (e.g. header)
					p.buffer.WriteString(fmt.Sprintf("%s\n", line))
				} else {
					currentTime := time.Now().UTC().Format(time.RFC3339)
					p.buffer.WriteString(fmt.Sprintf("[%s] %s\n", currentTime, line))
				}
			}

			if lineHasCallback || !checkedForCallback {
				lineCallbackWaitGroup.Add(1)
				go func(line string) {
					defer lineCallbackWaitGroup.Done()
					if (checkedForCallback && lineHasCallback) || p.LineCallbackFilter(lineString) {
						p.LineCallback(line)
					}
				}(lineString)
			}
		}

		// We need to make sure all the line callbacks have finish before
		// finish up the process
		logger.Debug("[LineScanner] Waiting for callbacks to finish")
		lineCallbackWaitGroup.Wait()

		logger.Debug("[LineScanner] Finished")
		waitGroup.Done()
	}()

	// Call the StartCallback
	go p.StartCallback()

	// Wait until the process has finished. The returned error is nil if the command runs,
	// has no problems copying stdin, stdout, and stderr, and exits with a zero exit status.
	waitResult := p.command.Wait()

	// Close the line writer pipe
	lineWriterPipe.Close()

	// The process is no longer running at this point
	p.setRunning(false)

	// Find the exit status of the script
	p.ExitStatus = getExitStatus(waitResult)

	logger.Info("Process with PID: %d finished with Exit Status: %s", p.Pid, p.ExitStatus)

	// Sometimes (in docker containers) io.Copy never seems to finish. This is a mega
	// hack around it. If it doesn't finish after 1 second, just continue.
	logger.Debug("[Process] Waiting for routines to finish")
	err = timeoutWait(&waitGroup)
	if err != nil {
		logger.Debug("[Process] Timed out waiting for wait group: (%T: %v)", err, err)
	}

	// No error occurred so we can return nil
	return nil
}

func (p *Process) Output() string {
	return p.buffer.String()
}

func (p *Process) Kill() error {
	var err error
	if runtime.GOOS == "windows" {
		// Sending Interrupt on Windows is not implemented.
		// https://golang.org/src/os/exec.go?s=3842:3884#L110
		err = exec.Command("CMD", "/C", "TASKKILL", "/F", "/PID", strconv.Itoa(p.Pid)).Run()
	} else {
		// Send a sigterm
		err = p.signal(syscall.SIGTERM)
	}
	if err != nil {
		return err
	}

	// Make a channel that we'll use as a timeout
	c := make(chan int, 1)
	checking := true

	// Start a routine that checks to see if the process
	// is still alive.
	go func() {
		for checking {
			logger.Debug("[Process] Checking to see if PID: %d is still alive", p.Pid)

			foundProcess, err := os.FindProcess(p.Pid)

			// Can't find the process at all
			if err != nil {
				logger.Debug("[Process] Could not find process with PID: %d", p.Pid)

				break
			}

			// We have some information about the process
			if foundProcess != nil {
				processState, err := foundProcess.Wait()

				if err != nil || processState.Exited() {
					logger.Debug("[Process] Process with PID: %d has exited.", p.Pid)

					break
				}
			}

			// Retry in a moment
			sleepTime := time.Duration(1 * time.Second)
			time.Sleep(sleepTime)
		}

		c <- 1
	}()

	// Timeout this process after 3 seconds
	select {
	case _ = <-c:
		// Was successfully terminated
	case <-time.After(10 * time.Second):
		// Stop checking in the routine above
		checking = false

		// Forcefully kill the thing
		err = p.signal(syscall.SIGKILL)

		if err != nil {
			return err
		}
	}

	return nil
}

func (p *Process) signal(sig os.Signal) error {
	if p.command != nil && p.command.Process != nil {
		logger.Debug("[Process] Sending signal: %s to PID: %d", sig.String(), p.Pid)

		err := p.command.Process.Signal(sig)
		if err != nil {
			logger.Error("[Process] Failed to send signal: %s to PID: %d (%T: %v)", sig.String(), p.Pid, err, err)
			return err
		}
	} else {
		logger.Debug("[Process] No process to signal yet")
	}

	return nil
}

// Returns whether or not the process is running
func (p *Process) IsRunning() bool {
	return atomic.LoadInt32(&p.running) != 0
}

// Sets the running flag of the process
func (p *Process) setRunning(r bool) {
	// Use the atomic package to avoid race conditions when setting the
	// `running` value from multiple routines
	if r {
		atomic.StoreInt32(&p.running, 1)
	} else {
		atomic.StoreInt32(&p.running, 0)
	}
}

// https://github.com/hnakamur/commango/blob/fe42b1cf82bf536ce7e24dceaef6656002e03743/os/executil/executil.go#L29
// TODO: Can this be better?
func getExitStatus(waitResult error) string {
	exitStatus := -1

	if waitResult != nil {
		if err, ok := waitResult.(*exec.ExitError); ok {
			if s, ok := err.Sys().(syscall.WaitStatus); ok {
				exitStatus = s.ExitStatus()
			} else {
				logger.Error("[Process] Unimplemented for system where exec.ExitError.Sys() is not syscall.WaitStatus.")
			}
		} else {
			logger.Error("[Process] Unexpected error type in getExitStatus: %#v", waitResult)
		}
	} else {
		exitStatus = 0
	}

	return fmt.Sprintf("%d", exitStatus)
}

func timeoutWait(waitGroup *sync.WaitGroup) error {
	// Make a chanel that we'll use as a timeout
	c := make(chan int, 1)

	// Start waiting for the routines to finish
	go func() {
		waitGroup.Wait()
		c <- 1
	}()

	select {
	case _ = <-c:
		return nil
	case <-time.After(10 * time.Second):
		return errors.New("Timeout")
	}

	return nil
}

// outputBuffer is a goroutine safe bytes.Buffer
type outputBuffer struct {
	sync.RWMutex
	buf bytes.Buffer
}

// Write appends the contents of p to the buffer, growing the buffer as needed. It returns
// the number of bytes written.
func (ob *outputBuffer) Write(p []byte) (n int, err error) {
	ob.Lock()
	defer ob.Unlock()
	return ob.buf.Write(p)
}

// WriteString appends the contents of s to the buffer, growing the buffer as needed. It returns
// the number of bytes written.
func (ob *outputBuffer) WriteString(s string) (n int, err error) {
	return ob.Write([]byte(s))
}

// String returns the contents of the unread portion of the buffer
// as a string.  If the Buffer is a nil pointer, it returns "<nil>".
func (ob *outputBuffer) String() string {
	ob.RLock()
	defer ob.RUnlock()
	return ob.buf.String()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/MG-RAST/AWE/lib/conf"
	"github.com/MG-RAST/AWE/lib/core/cwl"
	"github.com/MG-RAST/AWE/lib/logger"
	"github.com/davecgh/go-spew/spew"
	"os"
	"path"
	"time"
)

func main() {

	conf.LOG_OUTPUT = "console"

	err := conf.Init_conf("submitter")

	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: error reading conf file: "+err.Error())
		os.Exit(1)
	}

	logger.Initialize("client")

	if conf.CWL_JOB == "" {
		logger.Error("cwl job file missing")
		time.Sleep(time.Second)
		os.Exit(1)
	}

	inputfile_path := path.Dir(conf.CWL_JOB)
	fmt.Printf("job path: %s\n", inputfile_path) // needed to resolve relative paths

	job_doc, err := cwl.ParseJob(conf.CWL_JOB)
	if err != nil {
		logger.Error("error parsing cwl job: %v", err)
		time.Sleep(time.Second)
		os.Exit(1)
	}

	fmt.Println("Job input:")
	spew.Dump(*job_doc)

}
<commit_msg>added processInputData<commit_after>package main

import (
	//"encoding/json"
	"fmt"
	"github.com/MG-RAST/AWE/lib/conf"
	"github.com/MG-RAST/AWE/lib/core/cwl"
	cwl_types "github.com/MG-RAST/AWE/lib/core/cwl/types"
	"github.com/MG-RAST/AWE/lib/logger"
	"github.com/davecgh/go-spew/spew"
	"gopkg.in/yaml.v2"
	"os"
	"path"
	"reflect"
	"time"
)

func processInputData(native interface{}) (err error) {
	fmt.Printf("processInputData\n")
	switch native.(type) {
	case *cwl.Job_document:

		job_doc_ptr := native.(*cwl.Job_document)

		job_doc := *job_doc_ptr

		for key, value := range job_doc {

			fmt.Printf("recurse into key: %s\n", key)
			err = processInputData(value)
			if err != nil {
				return
			}

		}

		return

	case *cwl_types.String:
		fmt.Printf("found string\n")
		return
	case *cwl_types.File:
		fmt.Printf("found File\n")
		return
	default:
		err = fmt.Errorf("(processInputData) No handler for type \"%s\"\n", reflect.TypeOf(native))
		return
	}
	panic("argh")
	return
}

func main() {

	conf.LOG_OUTPUT = "console"

	err := conf.Init_conf("submitter")

	if err != nil {
		fmt.Fprintf(os.Stderr, "ERROR: error reading conf file: "+err.Error())
		os.Exit(1)
	}

	logger.Initialize("client")

	if conf.CWL_JOB == "" {
		logger.Error("cwl job file missing")
		time.Sleep(time.Second)
		os.Exit(1)
	}

	inputfile_path := path.Dir(conf.CWL_JOB)
	fmt.Printf("job path: %s\n", inputfile_path) // needed to resolve relative paths

	job_doc, err := cwl.ParseJob(conf.CWL_JOB)
	if err != nil {
		logger.Error("error parsing cwl job: %v", err)
		time.Sleep(time.Second)
		os.Exit(1)
	}

	fmt.Println("Job input:")
	spew.Dump(*job_doc)

	data, err := yaml.Marshal(*job_doc)
	if err != nil {
		fmt.Printf("error: %s", err.Error())
		os.Exit(1)
	}

	fmt.Printf("json:\n%s\n", string(data[:]))

	// process input files

	err = processInputData(job_doc)
	if err != nil {
		fmt.Printf("error: %s", err.Error())
		os.Exit(1)
	}

}
<|endoftext|>


<commit_before>// mathematical function templates
//
// they come in pairs: function per se, and its derivative.
// the derivative function has "d_" prepended to function name.
// if function has more than one argument, then the first derivative
// argument is the index we're taking derivative with respect to
//
package templates

import "math"

func tan(a float64) float64 {
	return math.Tan(a)
}
func d_tan(a float64) float64 {
	return math.Pow(1/math.Cos(a), 2)
}

func abs(a float64) float64 {
	return math.Abs(a)
}
func d_abs(a float64) float64 {
	switch {
	case a > 0:
		return +1
	case a < 0:
		return -1
	default:
		panic("illegal derivative when abs(0)=0")
	}
}

func atan(a float64) float64 {
	return math.Atan(a)
}
func d_atan(a float64) float64 {
	return 1 / (1 + a*a)
}

func tanh(a float64) float64 {
	return math.Tanh(a)
}
func d_tanh(a float64) float64 {
	return 1 - math.Pow(math.Tanh(a), 2)
}

func sin(a float64) float64 {
	return math.Sin(a)
}
func d_sin(a float64) float64 {
	return math.Cos(a)
}

func asin(a float64) float64 {
	return math.Asin(a)
}
func d_asin(a float64) float64 {
	return 1 / math.Sqrt(1-a*a)
}

func sinh(a float64) float64 {
	return math.Sinh(a)
}
func d_sinh(a float64) float64 {
	return math.Cosh(a)
}

func cos(a float64) float64 {
	return math.Cos(a)
}
func d_cos(a float64) float64 {
	return -math.Sin(a)
}

func acos(a float64) float64 {
	return math.Acos(a)
}
func d_acos(a float64) float64 {
	return -1 / math.Sqrt(1-a*a)
}

func cosh(a float64) float64 {
	return math.Cosh(a)
}
func d_cosh(a float64) float64 {
	return math.Sinh(a)
}

func add(a, b float64) float64 {
	return a + b
}
func d_add(i int, a, b float64) float64 {
	if i < 0 || i > 1 {
		panic("illegal index")
	}
	return 1
}

func multiply(a, b float64) float64 {
	return a * b
}
func d_multiply(i int, a, b float64) float64 {
	switch i {
	case 0:
		return b
	case 1:
		return a
	default:
		panic("illegal index")
	}
}

func subtract(a, b float64) float64 {
	return a - b
}
func d_subtract(i int, a, b float64) float64 {
	switch i {
	case 0:
		return 1
	case 1:
		return -1
	default:
		panic("illegal index")
	}
}

func divide(a, b float64) float64 {
	return a / b
}
func d_divide(i int, a, b float64) float64 {
	switch i {
	case 0:
		return 1 / b
	case 1:
		return -a / (b * b)
	default:
		panic("illegal index")
	}
}

func sqrt(a float64) float64 {
	return math.Sqrt(a)
}
func d_sqrt(a float64) float64 {
	return 0.5 * math.Pow(a, -0.5)
}

func exp(a float64) float64 {
	return math.Exp(a)
}
func d_exp(a float64) float64 {
	return math.Exp(a)
}

// 2^a --- sort of like exponentiation base 2
func exp2(a float64) float64 {
	return math.Pow(2, a)
}
func d_exp2(a float64) float64 {
	return math.Log(2) * math.Pow(2, a)
}

func log(a float64) float64 {
	return math.Log(a)
}
func d_log(a float64) float64 {
	return 1 / a
}

func log2(a float64) float64 {
	return math.Log2(a)
}
func d_log2(a float64) float64 {
	return 1 / (a * math.Log(2))
}

func pow(a, b float64) float64 {
	return math.Pow(a, b)
}
func d_pow(i int, a, b float64) float64 {
	switch i {
	case 0:
		return b * math.Pow(a, b-1)
	case 1:
		return math.Pow(a, b) * math.Log(a)
	default:
		panic("illegal index")
	}
}
<commit_msg>better exp2<commit_after>// mathematical function templates
//
// they come in pairs: function per se, and its derivative.
// the derivative function has "d_" prepended to function name.
// if function has more than one argument, then the first derivative
// argument is the index we're taking derivative with respect to
//
package templates

import "math"

func tan(a float64) float64 {
	return math.Tan(a)
}
func d_tan(a float64) float64 {
	return math.Pow(1/math.Cos(a), 2)
}

func abs(a float64) float64 {
	return math.Abs(a)
}
func d_abs(a float64) float64 {
	switch {
	case a > 0:
		return +1
	case a < 0:
		return -1
	default:
		panic("illegal derivative when abs(0)=0")
	}
}

func atan(a float64) float64 {
	return math.Atan(a)
}
func d_atan(a float64) float64 {
	return 1 / (1 + a*a)
}

func tanh(a float64) float64 {
	return math.Tanh(a)
}
func d_tanh(a float64) float64 {
	return 1 - math.Pow(math.Tanh(a), 2)
}

func sin(a float64) float64 {
	return math.Sin(a)
}
func d_sin(a float64) float64 {
	return math.Cos(a)
}

func asin(a float64) float64 {
	return math.Asin(a)
}
func d_asin(a float64) float64 {
	return 1 / math.Sqrt(1-a*a)
}

func sinh(a float64) float64 {
	return math.Sinh(a)
}
func d_sinh(a float64) float64 {
	return math.Cosh(a)
}

func cos(a float64) float64 {
	return math.Cos(a)
}
func d_cos(a float64) float64 {
	return -math.Sin(a)
}

func acos(a float64) float64 {
	return math.Acos(a)
}
func d_acos(a float64) float64 {
	return -1 / math.Sqrt(1-a*a)
}

func cosh(a float64) float64 {
	return math.Cosh(a)
}
func d_cosh(a float64) float64 {
	return math.Sinh(a)
}

func add(a, b float64) float64 {
	return a + b
}
func d_add(i int, a, b float64) float64 {
	if i < 0 || i > 1 {
		panic("illegal index")
	}
	return 1
}

func multiply(a, b float64) float64 {
	return a * b
}
func d_multiply(i int, a, b float64) float64 {
	switch i {
	case 0:
		return b
	case 1:
		return a
	default:
		panic("illegal index")
	}
}

func subtract(a, b float64) float64 {
	return a - b
}
func d_subtract(i int, a, b float64) float64 {
	switch i {
	case 0:
		return 1
	case 1:
		return -1
	default:
		panic("illegal index")
	}
}

func divide(a, b float64) float64 {
	return a / b
}
func d_divide(i int, a, b float64) float64 {
	switch i {
	case 0:
		return 1 / b
	case 1:
		return -a / (b * b)
	default:
		panic("illegal index")
	}
}

func sqrt(a float64) float64 {
	return math.Sqrt(a)
}
func d_sqrt(a float64) float64 {
	return 0.5 * math.Pow(a, -0.5)
}

func exp(a float64) float64 {
	return math.Exp(a)
}
func d_exp(a float64) float64 {
	return math.Exp(a)
}

// 2^a --- sort of like exponentiation base 2
func exp2(a float64) float64 {
	return math.Exp2(a)
}
func d_exp2(a float64) float64 {
	return math.Log(2) * math.Pow(2, a)
}

func log(a float64) float64 {
	return math.Log(a)
}
func d_log(a float64) float64 {
	return 1 / a
}

func log2(a float64) float64 {
	return math.Log2(a)
}
func d_log2(a float64) float64 {
	return 1 / (a * math.Log(2))
}

func pow(a, b float64) float64 {
	return math.Pow(a, b)
}
func d_pow(i int, a, b float64) float64 {
	switch i {
	case 0:
		return b * math.Pow(a, b-1)
	case 1:
		return math.Pow(a, b) * math.Log(a)
	default:
		panic("illegal index")
	}
}
<|endoftext|>


<commit_before><commit_msg>improved docs<commit_after><|endoftext|>


<commit_before>package socks5

import (
	"fmt"
	"io"
	"log"
	"net"
	"strings"
	"time"
)

const (
	connectCommand   = uint8(1)
	bindCommand      = uint8(2)
	associateCommand = uint8(3)
	ipv4Address      = uint8(1)
	fqdnAddress      = uint8(3)
	ipv6Address      = uint8(4)
)

const (
	successReply uint8 = iota
	serverFailure
	ruleFailure
	networkUnreachable
	hostUnreachable
	connectionRefused
	ttlExpired
	commandNotSupported
	addrTypeNotSupported
)

var (
	unrecognizedAddrType = fmt.Errorf("Unrecognized address type")
)

// AddressRewriter is used to rewrite a destination transparently
type AddressRewriter interface {
	Rewrite(addr *AddrSpec) *AddrSpec
}

// AddrSpec is used to return the target AddrSpec
// which may be specified as IPv4, IPv6, or a FQDN
type AddrSpec struct {
	FQDN string
	IP   net.IP
	Port int
}

type conn interface {
	Write([]byte) (int, error)
	RemoteAddr() net.Addr
}

func (a *AddrSpec) String() string {
	if a.FQDN != "" {
		return fmt.Sprintf("%s (%s):%d", a.FQDN, a.IP, a.Port)
	}
	return fmt.Sprintf("%s:%d", a.IP, a.Port)
}

// handleRequest is used for request processing after authentication
func (s *Server) handleRequest(conn conn, bufConn io.Reader) error {
	// Read the version byte
	header := []byte{0, 0, 0}
	if _, err := io.ReadAtLeast(bufConn, header, 3); err != nil {
		return fmt.Errorf("Failed to get command version: %v", err)
	}

	// Ensure we are compatible
	if header[0] != socks5Version {
		return fmt.Errorf("Unsupported command version: %v", header[0])
	}

	// Read in the destination address
	dest, err := readAddrSpec(bufConn)
	if err != nil {
		if err == unrecognizedAddrType {
			if err := sendReply(conn, addrTypeNotSupported, nil); err != nil {
				return fmt.Errorf("Failed to send reply: %v", err)
			}
		}
		return fmt.Errorf("Failed to read destination address: %v", err)
	}

		if s.AdvancedDial == false{

			// Resolve the address if we have a FQDN
			if dest.FQDN != "" {
				addr, err := s.config.Resolver.Resolve(dest.FQDN)
				if err != nil {
					if err := sendReply(conn, hostUnreachable, nil); err != nil {
						return fmt.Errorf("Failed to send reply: %v", err)
					}
					return fmt.Errorf("Failed to resolve destination '%v': %v", dest.FQDN, err)
				}
				dest.IP = addr
			}

			// Apply any address rewrites
			realDest := dest
			if s.config.Rewriter != nil {
				realDest = s.config.Rewriter.Rewrite(dest)
			}

			// Switch on the command
			switch header[1] {
			case connectCommand:
				return s.handleConnect(conn, bufConn, dest, realDest)
			case bindCommand:
				return s.handleBind(conn, bufConn, dest, realDest)
			case associateCommand:
				return s.handleAssociate(conn, bufConn, dest, realDest)
			default:
				if err := sendReply(conn, commandNotSupported, nil); err != nil {
					return fmt.Errorf("Failed to send reply: %v", err)
				}
				return fmt.Errorf("Unsupported command: %v", header[1])
			}
		}else{

			//AdvancedServe
			if(dest.FQDN==""){
				if dest.IP.To4()!=nil{
					//This is a ipv4 addr
					avdst:=dest.IP.String()+":"+strconv.Itoa(dest.Port)
				}else{
					//or a ipv6 addr
					avdst:="["+dest.IP.String()+"]:"+strconv.Itoa(dest.Port)
				}
			}

			s.AdvancedServe(,)

		}

}

// handleConnect is used to handle a connect command
func (s *Server) handleConnect(conn conn, bufConn io.Reader, dest, realDest *AddrSpec) error {
	// Check if this is allowed
	client := conn.RemoteAddr().(*net.TCPAddr)
	if !s.config.Rules.AllowConnect(realDest.IP, realDest.Port, client.IP, client.Port) {
		if err := sendReply(conn, ruleFailure, nil); err != nil {
			return fmt.Errorf("Failed to send reply: %v", err)
		}
		return fmt.Errorf("Connect to %v blocked by rules", dest)
	}

	// Attempt to connect
	addr := net.TCPAddr{IP: realDest.IP, Port: realDest.Port}
	target, err := net.DialTCP("tcp", nil, &addr)
	if err != nil {
		msg := err.Error()
		resp := hostUnreachable
		if strings.Contains(msg, "refused") {
			resp = connectionRefused
		} else if strings.Contains(msg, "network is unreachable") {
			resp = networkUnreachable
		}
		if err := sendReply(conn, resp, nil); err != nil {
			return fmt.Errorf("Failed to send reply: %v", err)
		}
		return fmt.Errorf("Connect to %v failed: %v", dest, err)
	}
	defer target.Close()

	// Send success
	local := target.LocalAddr().(*net.TCPAddr)
	bind := AddrSpec{IP: local.IP, Port: local.Port}
	if err := sendReply(conn, successReply, &bind); err != nil {
		return fmt.Errorf("Failed to send reply: %v", err)
	}

	// Start proxying
	errCh := make(chan error, 2)
	go proxy("target", target, bufConn, errCh)
	go proxy("client", conn, target, errCh)

	// Wait
	select {
	case e := <-errCh:
		return e
	}
}

// handleBind is used to handle a connect command
func (s *Server) handleBind(conn conn, bufConn io.Reader, dest, realDest *AddrSpec) error {
	// Check if this is allowed
	client := conn.RemoteAddr().(*net.TCPAddr)
	if !s.config.Rules.AllowBind(realDest.IP, realDest.Port, client.IP, client.Port) {
		if err := sendReply(conn, ruleFailure, nil); err != nil {
			return fmt.Errorf("Failed to send reply: %v", err)
		}
		return fmt.Errorf("Bind to %v blocked by rules", dest)
	}

	// TODO: Support bind
	if err := sendReply(conn, commandNotSupported, nil); err != nil {
		return fmt.Errorf("Failed to send reply: %v", err)
	}
	return nil
}

// handleAssociate is used to handle a connect command
func (s *Server) handleAssociate(conn conn, bufConn io.Reader, dest, realDest *AddrSpec) error {
	// Check if this is allowed
	client := conn.RemoteAddr().(*net.TCPAddr)
	if !s.config.Rules.AllowAssociate(realDest.IP, realDest.Port, client.IP, client.Port) {
		if err := sendReply(conn, ruleFailure, nil); err != nil {
			return fmt.Errorf("Failed to send reply: %v", err)
		}
		return fmt.Errorf("Associate to %v blocked by rules", dest)
	}

	// TODO: Support associate
	if err := sendReply(conn, commandNotSupported, nil); err != nil {
		return fmt.Errorf("Failed to send reply: %v", err)
	}
	return nil
}

// readAddrSpec is used to read AddrSpec.
// Expects an address type byte, follwed by the address and port
func readAddrSpec(r io.Reader) (*AddrSpec, error) {
	d := &AddrSpec{}

	// Get the address type
	addrType := []byte{0}
	if _, err := r.Read(addrType); err != nil {
		return nil, err
	}

	// Handle on a per type basis
	switch addrType[0] {
	case ipv4Address:
		addr := make([]byte, 4)
		if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil {
			return nil, err
		}
		d.IP = net.IP(addr)

	case ipv6Address:
		addr := make([]byte, 16)
		if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil {
			return nil, err
		}
		d.IP = net.IP(addr)

	case fqdnAddress:
		if _, err := r.Read(addrType); err != nil {
			return nil, err
		}
		addrLen := int(addrType[0])
		fqdn := make([]byte, addrLen)
		if _, err := io.ReadAtLeast(r, fqdn, addrLen); err != nil {
			return nil, err
		}
		d.FQDN = string(fqdn)

	default:
		return nil, unrecognizedAddrType
	}

	// Read the port
	port := []byte{0, 0}
	if _, err := io.ReadAtLeast(r, port, 2); err != nil {
		return nil, err
	}
	d.Port = (int(port[0]) << 8) | int(port[1])

	return d, nil
}

// sendReply is used to send a reply message
func sendReply(w io.Writer, resp uint8, addr *AddrSpec) error {
	// Format the address
	var addrType uint8
	var addrBody []byte
	var addrPort uint16
	switch {
	case addr == nil:
		addrType = ipv4Address
		addrBody = []byte{0, 0, 0, 0}
		addrPort = 0

	case addr.FQDN != "":
		addrType = fqdnAddress
		addrBody = append([]byte{byte(len(addr.FQDN))}, addr.FQDN...)
		addrPort = uint16(addr.Port)

	case addr.IP.To4() != nil:
		addrType = ipv4Address
		addrBody = []byte(addr.IP.To4())
		addrPort = uint16(addr.Port)

	case addr.IP.To16() != nil:
		addrType = ipv6Address
		addrBody = []byte(addr.IP.To16())
		addrPort = uint16(addr.Port)

	default:
		return fmt.Errorf("Failed to format address: %v", addr)
	}

	// Format the message
	msg := make([]byte, 6+len(addrBody))
	msg[0] = socks5Version
	msg[1] = resp
	msg[2] = 0 // Reserved
	msg[3] = addrType
	copy(msg[4:], addrBody)
	msg[4+len(addrBody)] = byte(addrPort >> 8)
	msg[4+len(addrBody)+1] = byte(addrPort & 0xff)

	// Send the message
	_, err := w.Write(msg)
	return err
}

// proxy is used to suffle data from src to destination, and sends errors
// down a dedicated channel
func proxy(name string, dst io.Writer, src io.Reader, errCh chan error) {
	// Copy
	n, err := io.Copy(dst, src)

	// Log, and sleep. This is jank but allows the otherside
	// to finish a pending copy
	log.Printf("[DEBUG] socks: Copied %d bytes to %s", n, name)
	time.Sleep(10 * time.Millisecond)

	// Send any errors
	errCh <- err
}
<commit_msg>finialize sock5 docker part<commit_after>package socks5

import (
	"fmt"
	"io"
	"log"
	"net"
	"strings"
	"time"
)

const (
	connectCommand   = uint8(1)
	bindCommand      = uint8(2)
	associateCommand = uint8(3)
	ipv4Address      = uint8(1)
	fqdnAddress      = uint8(3)
	ipv6Address      = uint8(4)
)

const (
	successReply uint8 = iota
	serverFailure
	ruleFailure
	networkUnreachable
	hostUnreachable
	connectionRefused
	ttlExpired
	commandNotSupported
	addrTypeNotSupported
)

var (
	unrecognizedAddrType = fmt.Errorf("Unrecognized address type")
)

// AddressRewriter is used to rewrite a destination transparently
type AddressRewriter interface {
	Rewrite(addr *AddrSpec) *AddrSpec
}

// AddrSpec is used to return the target AddrSpec
// which may be specified as IPv4, IPv6, or a FQDN
type AddrSpec struct {
	FQDN string
	IP   net.IP
	Port int
}

type conn interface {
	Write([]byte) (int, error)
	RemoteAddr() net.Addr
}

func (a *AddrSpec) String() string {
	if a.FQDN != "" {
		return fmt.Sprintf("%s (%s):%d", a.FQDN, a.IP, a.Port)
	}
	return fmt.Sprintf("%s:%d", a.IP, a.Port)
}

// handleRequest is used for request processing after authentication
func (s *Server) handleRequest(conn conn, bufConn io.Reader) error {
	// Read the version byte
	header := []byte{0, 0, 0}
	if _, err := io.ReadAtLeast(bufConn, header, 3); err != nil {
		return fmt.Errorf("Failed to get command version: %v", err)
	}

	// Ensure we are compatible
	if header[0] != socks5Version {
		return fmt.Errorf("Unsupported command version: %v", header[0])
	}

	// Read in the destination address
	dest, err := readAddrSpec(bufConn)
	if err != nil {
		if err == unrecognizedAddrType {
			if err := sendReply(conn, addrTypeNotSupported, nil); err != nil {
				return fmt.Errorf("Failed to send reply: %v", err)
			}
		}
		return fmt.Errorf("Failed to read destination address: %v", err)
	}

		if s.AdvancedDial == false{

			// Resolve the address if we have a FQDN
			if dest.FQDN != "" {
				addr, err := s.config.Resolver.Resolve(dest.FQDN)
				if err != nil {
					if err := sendReply(conn, hostUnreachable, nil); err != nil {
						return fmt.Errorf("Failed to send reply: %v", err)
					}
					return fmt.Errorf("Failed to resolve destination '%v': %v", dest.FQDN, err)
				}
				dest.IP = addr
			}

			// Apply any address rewrites
			realDest := dest
			if s.config.Rewriter != nil {
				realDest = s.config.Rewriter.Rewrite(dest)
			}

			// Switch on the command
			switch header[1] {
			case connectCommand:
				return s.handleConnect(conn, bufConn, dest, realDest)
			case bindCommand:
				return s.handleBind(conn, bufConn, dest, realDest)
			case associateCommand:
				return s.handleAssociate(conn, bufConn, dest, realDest)
			default:
				if err := sendReply(conn, commandNotSupported, nil); err != nil {
					return fmt.Errorf("Failed to send reply: %v", err)
				}
				return fmt.Errorf("Unsupported command: %v", header[1])
			}
		}else{

			//AdvancedServe
			if(dest.FQDN==""){
				if dest.IP.To4()!=nil{
					//This is a ipv4 addr
					avdst:=dest.IP.String()+":"+strconv.Itoa(dest.Port)
				}else{
					//or a ipv6 addr
					avdst:="["+dest.IP.String()+"]:"+strconv.Itoa(dest.Port)
				}
			}

			connsti,ok:=s.AdvancedServe(avdst)

			if ok!=0 {
				sendReply(conn, "Bad Connection", nil)
				return nil
			}else{
				//send success
				sendReply(conn, successReply,nil)

				//is it OK to send nil as Local addr? if not I will create a fack addr

				go proxy("target", connsti, bufConn, errCh)
				go proxy("client", conn, connsti, errCh

			}

		}

}

// handleConnect is used to handle a connect command
func (s *Server) handleConnect(conn conn, bufConn io.Reader, dest, realDest *AddrSpec) error {
	// Check if this is allowed
	client := conn.RemoteAddr().(*net.TCPAddr)
	if !s.config.Rules.AllowConnect(realDest.IP, realDest.Port, client.IP, client.Port) {
		if err := sendReply(conn, ruleFailure, nil); err != nil {
			return fmt.Errorf("Failed to send reply: %v", err)
		}
		return fmt.Errorf("Connect to %v blocked by rules", dest)
	}

	// Attempt to connect
	addr := net.TCPAddr{IP: realDest.IP, Port: realDest.Port}
	target, err := net.DialTCP("tcp", nil, &addr)
	if err != nil {
		msg := err.Error()
		resp := hostUnreachable
		if strings.Contains(msg, "refused") {
			resp = connectionRefused
		} else if strings.Contains(msg, "network is unreachable") {
			resp = networkUnreachable
		}
		if err := sendReply(conn, resp, nil); err != nil {
			return fmt.Errorf("Failed to send reply: %v", err)
		}
		return fmt.Errorf("Connect to %v failed: %v", dest, err)
	}
	defer target.Close()

	// Send success
	local := target.LocalAddr().(*net.TCPAddr)
	bind := AddrSpec{IP: local.IP, Port: local.Port}
	if err := sendReply(conn, successReply, &bind); err != nil {
		return fmt.Errorf("Failed to send reply: %v", err)
	}

	// Start proxying
	errCh := make(chan error, 2)
	go proxy("target", target, bufConn, errCh)
	go proxy("client", conn, target, errCh)

	// Wait
	select {
	case e := <-errCh:
		return e
	}
}

// handleBind is used to handle a connect command
func (s *Server) handleBind(conn conn, bufConn io.Reader, dest, realDest *AddrSpec) error {
	// Check if this is allowed
	client := conn.RemoteAddr().(*net.TCPAddr)
	if !s.config.Rules.AllowBind(realDest.IP, realDest.Port, client.IP, client.Port) {
		if err := sendReply(conn, ruleFailure, nil); err != nil {
			return fmt.Errorf("Failed to send reply: %v", err)
		}
		return fmt.Errorf("Bind to %v blocked by rules", dest)
	}

	// TODO: Support bind
	if err := sendReply(conn, commandNotSupported, nil); err != nil {
		return fmt.Errorf("Failed to send reply: %v", err)
	}
	return nil
}

// handleAssociate is used to handle a connect command
func (s *Server) handleAssociate(conn conn, bufConn io.Reader, dest, realDest *AddrSpec) error {
	// Check if this is allowed
	client := conn.RemoteAddr().(*net.TCPAddr)
	if !s.config.Rules.AllowAssociate(realDest.IP, realDest.Port, client.IP, client.Port) {
		if err := sendReply(conn, ruleFailure, nil); err != nil {
			return fmt.Errorf("Failed to send reply: %v", err)
		}
		return fmt.Errorf("Associate to %v blocked by rules", dest)
	}

	// TODO: Support associate
	if err := sendReply(conn, commandNotSupported, nil); err != nil {
		return fmt.Errorf("Failed to send reply: %v", err)
	}
	return nil
}

// readAddrSpec is used to read AddrSpec.
// Expects an address type byte, follwed by the address and port
func readAddrSpec(r io.Reader) (*AddrSpec, error) {
	d := &AddrSpec{}

	// Get the address type
	addrType := []byte{0}
	if _, err := r.Read(addrType); err != nil {
		return nil, err
	}

	// Handle on a per type basis
	switch addrType[0] {
	case ipv4Address:
		addr := make([]byte, 4)
		if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil {
			return nil, err
		}
		d.IP = net.IP(addr)

	case ipv6Address:
		addr := make([]byte, 16)
		if _, err := io.ReadAtLeast(r, addr, len(addr)); err != nil {
			return nil, err
		}
		d.IP = net.IP(addr)

	case fqdnAddress:
		if _, err := r.Read(addrType); err != nil {
			return nil, err
		}
		addrLen := int(addrType[0])
		fqdn := make([]byte, addrLen)
		if _, err := io.ReadAtLeast(r, fqdn, addrLen); err != nil {
			return nil, err
		}
		d.FQDN = string(fqdn)

	default:
		return nil, unrecognizedAddrType
	}

	// Read the port
	port := []byte{0, 0}
	if _, err := io.ReadAtLeast(r, port, 2); err != nil {
		return nil, err
	}
	d.Port = (int(port[0]) << 8) | int(port[1])

	return d, nil
}

// sendReply is used to send a reply message
func sendReply(w io.Writer, resp uint8, addr *AddrSpec) error {
	// Format the address
	var addrType uint8
	var addrBody []byte
	var addrPort uint16
	switch {
	case addr == nil:
		addrType = ipv4Address
		addrBody = []byte{0, 0, 0, 0}
		addrPort = 0

	case addr.FQDN != "":
		addrType = fqdnAddress
		addrBody = append([]byte{byte(len(addr.FQDN))}, addr.FQDN...)
		addrPort = uint16(addr.Port)

	case addr.IP.To4() != nil:
		addrType = ipv4Address
		addrBody = []byte(addr.IP.To4())
		addrPort = uint16(addr.Port)

	case addr.IP.To16() != nil:
		addrType = ipv6Address
		addrBody = []byte(addr.IP.To16())
		addrPort = uint16(addr.Port)

	default:
		return fmt.Errorf("Failed to format address: %v", addr)
	}

	// Format the message
	msg := make([]byte, 6+len(addrBody))
	msg[0] = socks5Version
	msg[1] = resp
	msg[2] = 0 // Reserved
	msg[3] = addrType
	copy(msg[4:], addrBody)
	msg[4+len(addrBody)] = byte(addrPort >> 8)
	msg[4+len(addrBody)+1] = byte(addrPort & 0xff)

	// Send the message
	_, err := w.Write(msg)
	return err
}

// proxy is used to suffle data from src to destination, and sends errors
// down a dedicated channel
func proxy(name string, dst io.Writer, src io.Reader, errCh chan error) {
	// Copy
	n, err := io.Copy(dst, src)

	// Log, and sleep. This is jank but allows the otherside
	// to finish a pending copy
	log.Printf("[DEBUG] socks: Copied %d bytes to %s", n, name)
	time.Sleep(10 * time.Millisecond)

	// Send any errors
	errCh <- err
}
<|endoftext|>


<commit_before>package irsdk

type SessionData struct {
	WeekendInfo   WeekendInfo   `yaml:"WeekendInfo"`
	SessionInfo   SessionInfo   `yaml:"SessionInfo"`
	CameraInfo    CameraInfo    `yaml:"CameraInfo"`
	RadioInfo     RadioInfo     `yaml:"RadioInfo"`
	DriverInfo    DriverInfo    `yaml:"DriverInfo"`
	SplitTimeInfo SplitTimeInfo `yaml:"SplitTimeInfo"`
}

// https://github.com/smyrman/units
// quantity + unit
type unit string

type WeekendInfo struct {
	// TrackName string -> TrackName string `yaml:"TrackName"`
	// vim: s/\(\t\(.\{-}\) \).*/\0 `yaml:"\2"`
	TrackName             string `yaml:"TrackName"`
	TrackID               int    `yaml:"TrackID"`
	TrackLength           string `yaml:"TrackLength"`
	TrackDisplayName      string `yaml:"TrackDisplayName"`
	TrackDisplayShortName string `yaml:"TrackDisplayShortName"`
	TrackConfigName       string `yaml:"TrackConfigName"`
	TrackCity             string `yaml:"TrackCity"`
	TrackCountry          string `yaml:"TrackCountry"`
	TrackAltitude         unit   `yaml:"TrackAltitude"`
	TrackLatitude         unit   `yaml:"TrackLatitude"`
	TrackLongitude        unit   `yaml:"TrackLongitude"`
	TrackNumTurns         int    `yaml:"TrackNumTurns"`
	TrackPitSpeedLimit    unit   `yaml:"TrackPitSpeedLimit"`
	TrackType             string `yaml:"TrackType"`
	TrackWeatherType      string `yaml:"TrackWeatherType"`
	TrackSkies            string `yaml:"TrackSkies"`
	TrackSurfaceTemp      unit   `yaml:"TrackSurfaceTemp"`
	TrackAirTemp          unit   `yaml:"TrackAirTemp"`
	TrackAirPressure      unit   `yaml:"TrackAirPressure"`
	TrackWindVel          unit   `yaml:"TrackWindVel"`
	TrackWindDir          unit   `yaml:"TrackWindDir"`
	TrackRelativeHumidity unit   `yaml:"TrackRelativeHumidity"`
	TrackFogLevel         unit   `yaml:"TrackFogLevel"`
	SeriesID              int    `yaml:"SeriesID"`
	SeasonID              int    `yaml:"SeasonID"`
	SessionID             int    `yaml:"SessionID"`
	SubSessionID          int    `yaml:"SubSessionID"`
	LeagueID              int    `yaml:"LeagueID"`
	Official              int    `yaml:"Official"`
	RaceWeek              int    `yaml:"RaceWeek"`
	EventType             string `yaml:"EventType"`
	Category              string `yaml:"Category"`
	SimMode               string `yaml:"SimMode"`
	TeamRacing            int    `yaml:"TeamRacing"`
	MinDrivers            int    `yaml:"MinDrivers"`
	MaxDrivers            int    `yaml:"MaxDrivers"`
}

type SessionInfo struct {
	Sessions []Session `yaml:"Sessions"`
}

type Session struct {
	SessionNum             int `yaml:"SessionNum"`
	SessionLaps            string `yaml:"SessionLaps"`
	SessionTime            string `yaml:"SessionTime"`
	SessionNumLapsToAvg    int `yaml:"SessionNumLapsToAvg"`
	SessionType            string `yaml:"SessionType"`
	ResultsPositions       []ResultPosition `yaml:"ResultsPositions"`
	ResultsFastestLap      []ResultFastestLap `yaml:"ResultsFastestLap"`
	ResultsAverageLapTime  float32 `yaml:"ResultsAverageLapTime"`
	ResultsNumCautionFlags int `yaml:"ResultsNumCautionFlags"`
	ResultsNumCautionLaps  int `yaml:"ResultsNumCautionLaps"`
	ResultsNumLeadChanges  int `yaml:"ResultsNumLeadChanges"`
	ResultsLapsComplete    int `yaml:"ResultsLapsComplete"`
	ResultsOfficial        int `yaml:"ResultsOfficial"`
}

type ResultPosition struct {
	CarIdx        int `yaml:"CarIdx"`
	Position      int `yaml:"Position"`
	ClassPosition int `yaml:"ClassPosition"`
	FastestTime   float32 `yaml:"FastestTime"`
	Lap           int `yaml:"Lap"`
	LastTime      float32 `yaml:"LastTime"`
	LapsComplete  int `yaml:"LapsComplete"`
	LapsDriven    int `yaml:"LapsDriven"`
	ReasonOutId   int `yaml:"ReasonOutId"`
}

type ResultFastestLap struct {
	CarIdx      int `yaml:"CarIdx"`
	FastestLap  int `yaml:"FastestLap"`
	FastestTime float32 `yaml:"FastestTime"`
}

type CameraInfo struct {
	Groups []CameraGroup `yaml:"Groups"`
}

type CameraGroup struct {
	Groupnum  int      `yaml:"Groupnum"`
	GroupName string   `yaml:"GroupName"`
	Cameras   []Camera `yaml:"Cameras"`
}

type Camera struct {
	CameraNum  int    `yaml:"CameraNum:"`
	CameraName string `yaml:"CameraName"`
}

type RadioInfo struct {
	SelectedRadioNum int     `yaml:"SelectedRadioNum"`
	Radios           []Radio `yaml:"Radios"`
}

type Radio struct {
	RadioNum            int         `yaml:"RadioNum"`
	HopCount            int         `yaml:"HopCount"`
	NumFrequencies      int         `yaml:"NumFrequencies"`
	TunedToFrequencyNum int         `yaml:"TunedToFrequencyNum"`
	ScanningIsOn        intToBool   `yaml:"ScanningIsOn"`
	Frequencies         []Frequency `yaml:"Frequencies"`
}

type Frequency struct {
	FrequencyNum  int       `yaml:"FrequencyNum"`
	FrequencyName string    `yaml:"FrequencyName"`
	Priority      int       `yaml:"Priority"`
	CarIdx        int       `yaml:"CarIdx"`
	EntryIdx      int       `yaml:"EntryIdx"`
	ClubID        int       `yaml:"ClubID"`
	CanScan       intToBool `yaml:"CanScan"`
	CanSquawk     intToBool `yaml:"CanSquawk"`
	Muted         intToBool `yaml:"Muted"`
	IsMutable     intToBool `yaml:"IsMutable"`
	IsDeletable   intToBool `yaml:"IsDeletable"`
}

type DriverInfo struct {
	DriverCarIdx          int      `yaml:"DriverCarIdx"`
	DriverHeadPosX        float32  `yaml:"DriverHeadPosX"`
	DriverHeadPosY        float32  `yaml:"DriverHeadPosY"`
	DriverHeadPosZ        float32  `yaml:"DriverHeadPosZ"`
	DriverCarIdleRPM      float32  `yaml:"DriverCarIdleRPM"`
	DriverCarRedLine      float32  `yaml:"DriverCarRedLine"`
	DriverCarFuelKgPerLtr float32  `yaml:"DriverCarFuelKgPerLtr"`
	DriverCarSLFirstRPM   float32  `yaml:"DriverCarSLFirstRPM"`
	DriverCarSLShiftRPM   float32  `yaml:"DriverCarSLShiftRPM"`
	DriverCarSLLastRPM    float32  `yaml:"DriverCarSLLastRPM"`
	DriverCarSLBlinkRPM   float32  `yaml:"DriverCarSLBlinkRPM"`
	DriverPitTrkPct       float32  `yaml:"DriverPitTrkPct"`
	Drivers               []Driver `yaml:"Drivers"`
}

type Driver struct {
	CarIdx     int    `yaml:"CarIdx"`
	UserName   string `yaml:"UserName"`
	AbbrevName string `yaml:"AbbrevName"`
	Initials   string `yaml:"Initials"`
	UserID     int    `yaml:"UserID"`
	TeamID     int    `yaml:"TeamID"`
	TeamName   string `yaml:"TeamName"`
	// Or shoud CarNumber be an int?
	CarNumber             string `yaml:"CarNumber"`
	CarNumberRaw          int    `yaml:"CarNumberRaw"`
	CarPath               string `yaml:"CarPath"`
	CarClassID            int    `yaml:"CarClassID"`
	CarID                 int    `yaml:"CarID"`
	CarScreenName         string `yaml:"CarScreenName"`
	CarScreenNameShort    string `yaml:"CarScreenNameShort"`
	CarClassShortName     string `yaml:"CarClassShortName"`
	CarClassRelSpeed      int    `yaml:"CarClassRelSpeed"`
	CarClassLicenseLevel  int    `yaml:"CarClassLicenseLevel"`
	CarClassMaxFuel       unit   `yaml:"CarClassMaxFuel"`
	CarClassWeightPenalty unit   `yaml:"CarClassWeightPenalty"`
	// CarClassColor: 0xffffff
	IRating     int    `yaml:"IRating"`
	LicLevel    int    `yaml:"LicLevel"`
	LicSubLevel int    `yaml:"LicSubLevel"`
	LicString   string `yaml:"LicString"`
	// LicColor: 0xfc8a27
	IsSpectator intToBool `yaml:"IsSpectator"`
	// CarDesignStr: 0,FFFFFF,ED2129,2A3795
	// HelmetDesignStr: 56,000000,000000,000000
	// SuitDesignStr: 0,000000,000000,000000
	// CarNumberDesignStr: 0,0,FFFFFF,777777,000000
	CarSponsor_1 int `yaml:"CarSponsor_1"`
	CarSponsor_2 int `yaml:"CarSponsor_2"`
}

type SplitTimeInfo struct {
	Sectors []Sector `yaml:"Sectors"`
}

type Sector struct {
	SectorNum      int     `yaml:"SectorNum"`
	SectorStartPct float32 `yaml:"SectorStartPct"`
}

// type intToBool bool

type intToBool bool

func (i *intToBool) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var intResult int
	err := unmarshal(&intResult)
	if err != nil {
		return err
	}

	if intResult > 0 {
		*i = true
	} else {
		*i = false
	}

	return nil
}
<commit_msg>Completed sessionstruct<commit_after>package irsdk

import "fmt"

type SessionData struct {
	WeekendInfo   WeekendInfo   `yaml:"WeekendInfo"`
	SessionInfo   SessionInfo   `yaml:"SessionInfo"`
	CameraInfo    CameraInfo    `yaml:"CameraInfo"`
	RadioInfo     RadioInfo     `yaml:"RadioInfo"`
	DriverInfo    DriverInfo    `yaml:"DriverInfo"`
	SplitTimeInfo SplitTimeInfo `yaml:"SplitTimeInfo"`
}

// https://github.com/smyrman/units
// quantity + unit
type unit string

type WeekendInfo struct {
	// TrackName string -> TrackName string `yaml:"TrackName"`
	// vim: s/\(\t\(.\{-}\) \).*/\0 `yaml:"\2"`
	TrackName              string           `yaml:"TrackName"`
	TrackID                int              `yaml:"TrackID"`
	TrackLength            string           `yaml:"TrackLength"`
	TrackDisplayName       string           `yaml:"TrackDisplayName"`
	TrackDisplayShortName  string           `yaml:"TrackDisplayShortName"`
	TrackConfigName        string           `yaml:"TrackConfigName"`
	TrackCity              string           `yaml:"TrackCity"`
	TrackCountry           string           `yaml:"TrackCountry"`
	TrackAltitude          unit             `yaml:"TrackAltitude"`
	TrackLatitude          unit             `yaml:"TrackLatitude"`
	TrackLongitude         unit             `yaml:"TrackLongitude"`
	TrackNumTurns          int              `yaml:"TrackNumTurns"`
	TrackPitSpeedLimit     unit             `yaml:"TrackPitSpeedLimit"`
	TrackType              string           `yaml:"TrackType"`
	TrackWeatherType       string           `yaml:"TrackWeatherType"`
	TrackSkies             string           `yaml:"TrackSkies"`
	TrackSurfaceTemp       unit             `yaml:"TrackSurfaceTemp"`
	TrackAirTemp           unit             `yaml:"TrackAirTemp"`
	TrackAirPressure       unit             `yaml:"TrackAirPressure"`
	TrackWindVel           unit             `yaml:"TrackWindVel"`
	TrackWindDir           unit             `yaml:"TrackWindDir"`
	TrackRelativeHumidity  unit             `yaml:"TrackRelativeHumidity"`
	TrackFogLevel          unit             `yaml:"TrackFogLevel"`
	SeriesID               int              `yaml:"SeriesID"`
	SeasonID               int              `yaml:"SeasonID"`
	SessionID              int              `yaml:"SessionID"`
	SubSessionID           int              `yaml:"SubSessionID"`
	LeagueID               int              `yaml:"LeagueID"`
	Official               int              `yaml:"Official"`
	RaceWeek               int              `yaml:"RaceWeek"`
	EventType              string           `yaml:"EventType"`
	Category               string           `yaml:"Category"`
	SimMode                string           `yaml:"SimMode"`
	TeamRacing             int              `yaml:"TeamRacing"`
	MinDrivers             int              `yaml:"MinDrivers"`
	MaxDrivers             int              `yaml:"MaxDrivers"`
	DCRuleSet              string           `yaml:"DCRuleSet"`
	QualifierMustStartRace intToBool        `yaml:"QualifierMustStartRace"`
	NumCarClasses          int              `yaml:"NumCarClasses"`
	NumCarTypes            int              `yaml:"NumCarTypes"`
	WeekendOptions         WeekendOptions   `yaml:"WeekendOptions"`
	TelemetryOptions       TelemetryOptions `yaml:"TelemetryOptions"`
}

type WeekendOptions struct {
	NumStarters         int       `yaml:"NumStarters"`
	StartingGrid        string    `yaml:"StartingGrid"`
	QualifyScoring      string    `yaml:"QualifyScoring"`
	CourseCautions      bool      `yaml:"CourseCautions"`
	StandingStart       intToBool `yaml:"StandingStart"`
	Restarts            string    `yaml:"Restarts"`
	WeatherType         string    `yaml:"WeatherType"`
	Skies               string    `yaml:"Skies"`
	WindDirection       unit      `yaml:"WindDirection"`
	WindSpeed           unit      `yaml:"WindSpeed"`
	WeatherTemp         unit      `yaml:"WeatherTemp"`
	RelativeHumidity    unit      `yaml:"RelativeHumidity"`
	FogLevel            unit      `yaml:"FogLevel"`
	Unofficial          intToBool `yaml:"Unofficial"`
	CommercialMode      string    `yaml:"CommercialMode"`
	NightMode           intToBool `yaml:"NightMode"`
	IsFixedSetup        intToBool `yaml:"IsFixedSetup"`
	StrictLapsChecking  string    `yaml:"StrictLapsChecking"`
	HasOpenRegistration intToBool `yaml:"HasOpenRegistration"`
	HardcoreLevel       int       `yaml:"HardcoreLevel"`
}

type TelemetryOptions struct {
	TelemetryDiskFile string `yaml:"TelemetryDiskFile"`
}

type SessionInfo struct {
	Sessions []Session `yaml:"Sessions"`
}

type Session struct {
	SessionNum             int                `yaml:"SessionNum"`
	SessionLaps            string             `yaml:"SessionLaps"`
	SessionTime            string             `yaml:"SessionTime"`
	SessionNumLapsToAvg    int                `yaml:"SessionNumLapsToAvg"`
	SessionType            string             `yaml:"SessionType"`
	ResultsPositions       []ResultPosition   `yaml:"ResultsPositions"`
	ResultsFastestLap      []ResultFastestLap `yaml:"ResultsFastestLap"`
	ResultsAverageLapTime  float32M           `yaml:"ResultsAverageLapTime"`
	ResultsNumCautionFlags int                `yaml:"ResultsNumCautionFlags"`
	ResultsNumCautionLaps  int                `yaml:"ResultsNumCautionLaps"`
	ResultsNumLeadChanges  int                `yaml:"ResultsNumLeadChanges"`
	ResultsLapsComplete    int                `yaml:"ResultsLapsComplete"`
	ResultsOfficial        int                `yaml:"ResultsOfficial"`
}

type ResultPosition struct {
	CarIdx        int      `yaml:"CarIdx"`
	Position      int      `yaml:"Position"`
	ClassPosition int      `yaml:"ClassPosition"`
	FastestTime   float32M `yaml:"FastestTime"`
	Lap           int      `yaml:"Lap"`
	LastTime      float32M `yaml:"LastTime"`
	LapsComplete  int      `yaml:"LapsComplete"`
	LapsDriven    int      `yaml:"LapsDriven"`
	ReasonOutId   int      `yaml:"ReasonOutId"`
}

type ResultFastestLap struct {
	CarIdx      int      `yaml:"CarIdx"`
	FastestLap  int      `yaml:"FastestLap"`
	FastestTime float32M `yaml:"FastestTime"`
}

type CameraInfo struct {
	Groups []CameraGroup `yaml:"Groups"`
}

type CameraGroup struct {
	GroupNum  int      `yaml:"GroupNum"`
	GroupName string   `yaml:"GroupName"`
	IsScenic  bool     `yaml:"IsSenic"`
	Cameras   []Camera `yaml:"Cameras"`
}

type Camera struct {
	CameraNum  int    `yaml:"CameraNum"`
	CameraName string `yaml:"CameraName"`
}

type RadioInfo struct {
	SelectedRadioNum int     `yaml:"SelectedRadioNum"`
	Radios           []Radio `yaml:"Radios"`
}

type Radio struct {
	RadioNum            int         `yaml:"RadioNum"`
	HopCount            int         `yaml:"HopCount"`
	NumFrequencies      int         `yaml:"NumFrequencies"`
	TunedToFrequencyNum int         `yaml:"TunedToFrequencyNum"`
	ScanningIsOn        intToBool   `yaml:"ScanningIsOn"`
	Frequencies         []Frequency `yaml:"Frequencies"`
}

type Frequency struct {
	FrequencyNum  int       `yaml:"FrequencyNum"`
	FrequencyName string    `yaml:"FrequencyName"`
	Priority      int       `yaml:"Priority"`
	CarIdx        int       `yaml:"CarIdx"`
	EntryIdx      int       `yaml:"EntryIdx"`
	ClubID        int       `yaml:"ClubID"`
	CanScan       intToBool `yaml:"CanScan"`
	CanSquawk     intToBool `yaml:"CanSquawk"`
	Muted         intToBool `yaml:"Muted"`
	IsMutable     intToBool `yaml:"IsMutable"`
	IsDeletable   intToBool `yaml:"IsDeletable"`
}

type DriverInfo struct {
	DriverCarIdx          int      `yaml:"DriverCarIdx"`
	DriverHeadPosX        float32M `yaml:"DriverHeadPosX"`
	DriverHeadPosY        float32M `yaml:"DriverHeadPosY"`
	DriverHeadPosZ        float32M `yaml:"DriverHeadPosZ"`
	DriverCarIdleRPM      float32M `yaml:"DriverCarIdleRPM"`
	DriverCarRedLine      float32M `yaml:"DriverCarRedLine"`
	DriverCarFuelKgPerLtr float32M `yaml:"DriverCarFuelKgPerLtr"`
	DriverCarSLFirstRPM   float32M `yaml:"DriverCarSLFirstRPM"`
	DriverCarSLShiftRPM   float32M `yaml:"DriverCarSLShiftRPM"`
	DriverCarSLLastRPM    float32M `yaml:"DriverCarSLLastRPM"`
	DriverCarSLBlinkRPM   float32M `yaml:"DriverCarSLBlinkRPM"`
	DriverPitTrkPct       float32M `yaml:"DriverPitTrkPct"`
	Drivers               []Driver `yaml:"Drivers"`
}

type Driver struct {
	CarIdx     int    `yaml:"CarIdx"`
	UserName   string `yaml:"UserName"`
	AbbrevName string `yaml:"AbbrevName"`
	Initials   string `yaml:"Initials"`
	UserID     int    `yaml:"UserID"`
	TeamID     int    `yaml:"TeamID"`
	TeamName   string `yaml:"TeamName"`
	// Or shoud CarNumber be an int?
	CarNumber             string `yaml:"CarNumber"`
	CarNumberRaw          int    `yaml:"CarNumberRaw"`
	CarPath               string `yaml:"CarPath"`
	CarClassID            int    `yaml:"CarClassID"`
	CarID                 int    `yaml:"CarID"`
	CarScreenName         string `yaml:"CarScreenName"`
	CarScreenNameShort    string `yaml:"CarScreenNameShort"`
	CarClassShortName     string `yaml:"CarClassShortName"`
	CarClassRelSpeed      int    `yaml:"CarClassRelSpeed"`
	CarClassLicenseLevel  int    `yaml:"CarClassLicenseLevel"`
	CarClassMaxFuel       unit   `yaml:"CarClassMaxFuel"`
	CarClassWeightPenalty unit   `yaml:"CarClassWeightPenalty"`
	// CarClassColor: 0xffffff
	IRating     int    `yaml:"IRating"`
	LicLevel    int    `yaml:"LicLevel"`
	LicSubLevel int    `yaml:"LicSubLevel"`
	LicString   string `yaml:"LicString"`
	// LicColor: 0xfc8a27
	IsSpectator intToBool `yaml:"IsSpectator"`
	// CarDesignStr: 0,FFFFFF,ED2129,2A3795
	// HelmetDesignStr: 56,000000,000000,000000
	// SuitDesignStr: 0,000000,000000,000000
	// CarNumberDesignStr: 0,0,FFFFFF,777777,000000
	CarSponsor_1 int `yaml:"CarSponsor_1"`
	CarSponsor_2 int `yaml:"CarSponsor_2"`
}

type SplitTimeInfo struct {
	Sectors []Sector `yaml:"Sectors"`
}

type Sector struct {
	SectorNum      int     `yaml:"SectorNum"`
	SectorStartPct float32 `yaml:"SectorStartPct"`
}

type intToBool bool

func (i *intToBool) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var intResult int
	err := unmarshal(&intResult)
	if err != nil {
		return err
	}

	if intResult > 0 {
		*i = true
	} else {
		*i = false
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Danko Miocevic. 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.

// Author: Danko Miocevic

package main

import (
	"os"
	"runtime"
	"syscall"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"golang.org/x/net/context"
)

// FS struct holds information about the
// entire filesystem.
// It contains the mount point specified by the user.
type FS struct {
	mPoint string
}

var _ = fs.FS(&FS{})

func (f *FS) Root() (fs.Node, error) {
	n := &Dir{
		fs:     f,
		artist: "",
		album:  "",
		mPoint: f.mPoint,
	}
	return n, nil
}

func (f *FS) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
	if runtime.GOOS == "darwin" {
		var stat syscall.Statfs_t
		wd, err := os.Getwd()
		if err != nil {
			return err
		}
		syscall.Statfs(wd, &stat)

		resp.Blocks = stat.Blocks
		resp.Bfree = stat.Bfree
		resp.Bavail = stat.Bavail
		resp.Bsize = uint32(stat.Bsize)
	}
	return nil
}
<commit_msg>Add fs size to other platforms.<commit_after>// Copyright 2016 Danko Miocevic. 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.

// Author: Danko Miocevic

package main

import (
	"os"
	"syscall"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"golang.org/x/net/context"
)

// FS struct holds information about the
// entire filesystem.
// It contains the mount point specified by the user.
type FS struct {
	mPoint string
}

var _ = fs.FS(&FS{})

func (f *FS) Root() (fs.Node, error) {
	n := &Dir{
		fs:     f,
		artist: "",
		album:  "",
		mPoint: f.mPoint,
	}
	return n, nil
}

func (f *FS) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.StatfsResponse) error {
	var stat syscall.Statfs_t
	wd, err := os.Getwd()
	if err != nil {
		return err
	}
	syscall.Statfs(wd, &stat)

	resp.Blocks = stat.Blocks
	resp.Bfree = stat.Bfree
	resp.Bavail = stat.Bavail
	resp.Bsize = uint32(stat.Bsize)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"golang.org/x/net/context"

	"github.com/samthor/valuefs/db"
)

// ValueFS implements the file system and its root directory.
type ValueFS struct {
	Store db.API
}

func (vfs *ValueFS) Root() (fs.Node, error) {
	return vfs, nil
}

func (vfs *ValueFS) Attr() fuse.Attr {
	return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0555}
}

func (vfs *ValueFS) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	list := vfs.Store.List()
	out := make([]fuse.Dirent, len(list))
	for i, rec := range list {
		out[i] = fuse.Dirent{
			Name:  rec.Name,
			Inode: rec.Node(),
			Type:  fuse.DT_File,
		}
	}
	return out, nil
}

func (vfs *ValueFS) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
	name, ok := matchLatestPath(req.Name)
	if !ok {
		return nil, nil, fuse.ENOENT
	}

	rec := vfs.Store.Load(name, true)
	if rec == nil {
		return nil, nil, fuse.ENOENT
	}

	vf := &ValueFileLatest{
		ValueFile: ValueFile{
			ValueFS: vfs,
			Record:  rec,
		},
	}
	return vf, vf, nil
}

func (vfs *ValueFS) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
	if req.Dir {
		return fuse.EIO // should never happen, do nothing
	}
	name, ok := matchLatestPath(req.Name)
	if !ok {
		return fuse.ENOENT
	}

	// TODO: remove round-trip of loading data
	rec := vfs.Store.Load(name, false)
	if rec != nil {
		vfs.Store.Clear(rec)
		// Note that replying to the RemoveRequest seems to occasionally NPE. It's
		// not obviously required.
	}
	return nil
}

func (vfs *ValueFS) Lookup(ctx context.Context, name string) (fs.Node, error) {
	name, view, ok := matchPath(name)
	if !ok {
		return nil, fuse.ENOENT
	}

	rec := vfs.Store.Load(name, false)
	if rec == nil {
		return nil, fuse.ENOENT
	}

	vf := ValueFile{vfs, rec}
	if view == nil {
		return &ValueFileLatest{ValueFile: vf}, nil
	}

	sample := vfs.Store.Get(rec, view)
	if sample == nil {
		// TODO: store for later
		return nil, fuse.ENOENT
	}
	return &ValueFileView{
		ValueFile: vf,
		View:      view,
	}, nil
}

type ValueFile struct {
	*ValueFS
	*db.Record
}

// ValueFileLatest wraps the latest value from a Record.
type ValueFileLatest struct {
	ValueFile
	*db.Sample
	Bytes []byte
}

func (vf *ValueFileLatest) Attr() fuse.Attr {
	sample := vf.ValueFS.Store.Get(vf.Record, nil)
	vf.Sample = sample
	vf.Bytes = sample.Bytes()

	var mtime time.Time
	var node uint64 = vf.Record.Node()
	if vf.Sample != nil {
		mtime = vf.Sample.When
		node = uint64(vf.Sample.When.UnixNano())
	}

	return fuse.Attr{
		Inode: node,
		Mode:  0664,
		Size:  uint64(len(vf.Bytes)),
		Mtime: mtime,
		Ctime: vf.Record.When,
	}
}

func (vf *ValueFileLatest) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
	if req.Offset != 0 {
		return fuse.EIO
	}

	s := strings.TrimSpace(string(req.Data))
	parsed, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return fuse.EIO
	}

	log.Printf("got write: %v => %v", vf.Name, parsed)

	store := vf.ValueFS.Store
	store.Write(vf.Record, parsed)
	resp.Size = len(req.Data)
	return nil
}

func (vf *ValueFileLatest) ReadAll(ctx context.Context) ([]byte, error) {
	return vf.Bytes, nil
}

type ValueFileView struct {
	ValueFile
	*db.View
	*db.Sample
	Bytes []byte
}

func (vf *ValueFileView) Attr() fuse.Attr {
	// reload on every Attr call.
	// we MUST do this because if data is written, it becomes new...
	sample := vf.ValueFS.Store.Get(vf.Record, vf.View)
	vf.Sample = sample
	vf.Bytes = sample.Bytes()

	when := vf.Record.When
	node := vf.Record.Node()
	if vf.Sample != nil {
		when = vf.Sample.When // using inode of sample
		node = uint64(vf.Sample.When.UnixNano())
	} else {
		// Use the inode of the record itself. This happens in two cases-
		//   1) no sample data
		//   2) no result from mode
		// Both are fine, although
	}

	return fuse.Attr{
		Inode: node,
		Mode:  0444,
		Size:  uint64(len(vf.Bytes)),
		Mtime: when,
		Ctime: when,
	}
}

func (vf *ValueFileView) ReadAll(ctx context.Context) ([]byte, error) {
	return vf.Bytes, nil
}
<commit_msg>allow append writes<commit_after>package main

import (
	"log"
	"os"
	"strconv"
	"strings"
	"time"

	"bazil.org/fuse"
	"bazil.org/fuse/fs"
	"golang.org/x/net/context"

	"github.com/samthor/valuefs/db"
)

// ValueFS implements the file system and its root directory.
type ValueFS struct {
	Store db.API
}

func (vfs *ValueFS) Root() (fs.Node, error) {
	return vfs, nil
}

func (vfs *ValueFS) Attr() fuse.Attr {
	return fuse.Attr{Inode: 1, Mode: os.ModeDir | 0555}
}

func (vfs *ValueFS) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
	list := vfs.Store.List()
	out := make([]fuse.Dirent, len(list))
	for i, rec := range list {
		out[i] = fuse.Dirent{
			Name:  rec.Name,
			Inode: rec.Node(),
			Type:  fuse.DT_File,
		}
	}
	return out, nil
}

func (vfs *ValueFS) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
	name, ok := matchLatestPath(req.Name)
	if !ok {
		return nil, nil, fuse.ENOENT
	}

	rec := vfs.Store.Load(name, true)
	if rec == nil {
		return nil, nil, fuse.ENOENT
	}

	vf := &ValueFileLatest{
		ValueFile: ValueFile{
			ValueFS: vfs,
			Record:  rec,
		},
	}
	return vf, vf, nil
}

func (vfs *ValueFS) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
	if req.Dir {
		return fuse.EIO // should never happen, do nothing
	}
	name, ok := matchLatestPath(req.Name)
	if !ok {
		return fuse.ENOENT
	}

	// TODO: remove round-trip of loading data
	rec := vfs.Store.Load(name, false)
	if rec != nil {
		vfs.Store.Clear(rec)
		// Note that replying to the RemoveRequest seems to occasionally NPE. It's
		// not obviously required.
	}
	return nil
}

func (vfs *ValueFS) Lookup(ctx context.Context, name string) (fs.Node, error) {
	name, view, ok := matchPath(name)
	if !ok {
		return nil, fuse.ENOENT
	}

	rec := vfs.Store.Load(name, false)
	if rec == nil {
		return nil, fuse.ENOENT
	}

	vf := ValueFile{vfs, rec}
	if view == nil {
		return &ValueFileLatest{ValueFile: vf}, nil
	}

	sample := vfs.Store.Get(rec, view)
	if sample == nil {
		// TODO: store for later
		return nil, fuse.ENOENT
	}
	return &ValueFileView{
		ValueFile: vf,
		View:      view,
	}, nil
}

type ValueFile struct {
	*ValueFS
	*db.Record
}

// ValueFileLatest wraps the latest value from a Record.
type ValueFileLatest struct {
	ValueFile
	*db.Sample
	Bytes []byte
}

func (vf *ValueFileLatest) Attr() fuse.Attr {
	sample := vf.ValueFS.Store.Get(vf.Record, nil)
	vf.Sample = sample
	vf.Bytes = sample.Bytes()

	var mtime time.Time
	var node uint64 = vf.Record.Node()
	if vf.Sample != nil {
		mtime = vf.Sample.When
		node = uint64(vf.Sample.When.UnixNano())
	}

	return fuse.Attr{
		Inode: node,
		Mode:  0664,
		Size:  uint64(len(vf.Bytes)),
		Mtime: mtime,
		Ctime: vf.Record.When,
	}
}

func (vf *ValueFileLatest) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error {
	// allow 'append' writes anywhere after bytes (and if not loaded yet, just allow)
	if int(req.Offset) < len(vf.Bytes) {
		return fuse.EIO
	}

	s := strings.TrimSpace(string(req.Data))
	parsed, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return fuse.EIO
	}

	log.Printf("got write: %v => %v", vf.Name, parsed)

	store := vf.ValueFS.Store
	store.Write(vf.Record, parsed)
	resp.Size = len(req.Data)
	return nil
}

func (vf *ValueFileLatest) ReadAll(ctx context.Context) ([]byte, error) {
	return vf.Bytes, nil
}

type ValueFileView struct {
	ValueFile
	*db.View
	*db.Sample
	Bytes []byte
}

func (vf *ValueFileView) Attr() fuse.Attr {
	// reload on every Attr call.
	// we MUST do this because if data is written, it becomes new...
	sample := vf.ValueFS.Store.Get(vf.Record, vf.View)
	vf.Sample = sample
	vf.Bytes = sample.Bytes()

	when := vf.Record.When
	node := vf.Record.Node()
	if vf.Sample != nil {
		when = vf.Sample.When // using inode of sample
		node = uint64(vf.Sample.When.UnixNano())
	} else {
		// Use the inode of the record itself. This happens in two cases-
		//   1) no sample data
		//   2) no result from mode
		// Both are fine, although
	}

	return fuse.Attr{
		Inode: node,
		Mode:  0444,
		Size:  uint64(len(vf.Bytes)),
		Mtime: when,
		Ctime: when,
	}
}

func (vf *ValueFileView) ReadAll(ctx context.Context) ([]byte, error) {
	return vf.Bytes, nil
}
<|endoftext|>


<commit_before>package dockerutil

import (
	"fmt"
	"strings"
)

type ConfigType struct {
	MediaType string
	Size      int
	Digest    string
}

type Layer struct {
	MediaType string
	Size      int
	Digest    string
}

type Manifest struct {
	SchemaVersion int
	MediaType     string
	Config        ConfigType
	Layers        []Layer
}

type ThinImageLayer struct {
	Digest string `json:"digest"`
	Url    string `json:"url,omitempty"`
}

type ThinImage struct {
	Version    string           `json:"version"`
	MinVersion string           `json:"min_version,omitempty"`
	Origin     string           `json:"origin,omitempty"`
	Layers     []ThinImageLayer `json:"layers"`
	Comment    string           `json:"comment,omitempty"`
}

var thinImageVersion = "1.0"

// m is the manifest of the original image
// repoLocation is where inside the repo we saved the several layers
// origin is an ecoding fo the original referencese and original registry
// I believe origin is quite useless but maybe is better to preserv it for
// ergonomic reasons.
func MakeThinImage(m Manifest, layersMapping map[string]string, origin string) (ThinImage, error) {
	layers := make([]ThinImageLayer, len(m.Layers))

	url_base := "cvmfs://"

	fmt.Println(layersMapping)
	fmt.Println(m.Layers)

	for i, layer := range m.Layers {
		digest := strings.Split(layer.Digest, ":")[1]
		location, ok := layersMapping[layer.Digest]
		if !ok {
			err := fmt.Errorf("Impossible to create thin image, missing layer")
			return ThinImage{}, err
		}
		// the location comes as /cvmfs/$reponame/$path
		// we need to remove the /cvmfs/ part, which are 7 chars
		url := url_base + "/" + location[7:]
		layers[i] = ThinImageLayer{Digest: digest, Url: url}
	}

	return ThinImage{Layers: layers,
		Origin:  origin,
		Version: thinImageVersion}, nil
}
<commit_msg>remove another slas<commit_after>package dockerutil

import (
	"fmt"
	"strings"
)

type ConfigType struct {
	MediaType string
	Size      int
	Digest    string
}

type Layer struct {
	MediaType string
	Size      int
	Digest    string
}

type Manifest struct {
	SchemaVersion int
	MediaType     string
	Config        ConfigType
	Layers        []Layer
}

type ThinImageLayer struct {
	Digest string `json:"digest"`
	Url    string `json:"url,omitempty"`
}

type ThinImage struct {
	Version    string           `json:"version"`
	MinVersion string           `json:"min_version,omitempty"`
	Origin     string           `json:"origin,omitempty"`
	Layers     []ThinImageLayer `json:"layers"`
	Comment    string           `json:"comment,omitempty"`
}

var thinImageVersion = "1.0"

// m is the manifest of the original image
// repoLocation is where inside the repo we saved the several layers
// origin is an ecoding fo the original referencese and original registry
// I believe origin is quite useless but maybe is better to preserv it for
// ergonomic reasons.
func MakeThinImage(m Manifest, layersMapping map[string]string, origin string) (ThinImage, error) {
	layers := make([]ThinImageLayer, len(m.Layers))

	url_base := "cvmfs://"

	fmt.Println(layersMapping)
	fmt.Println(m.Layers)

	for i, layer := range m.Layers {
		digest := strings.Split(layer.Digest, ":")[1]
		location, ok := layersMapping[layer.Digest]
		if !ok {
			err := fmt.Errorf("Impossible to create thin image, missing layer")
			return ThinImage{}, err
		}
		// the location comes as /cvmfs/$reponame/$path
		// we need to remove the /cvmfs/ part, which are 7 chars
		url := url_base + location[7:]
		layers[i] = ThinImageLayer{Digest: digest, Url: url}
	}

	return ThinImage{Layers: layers,
		Origin:  origin,
		Version: thinImageVersion}, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/u-root/u-root/uroot"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
)

type copyfiles struct {
	dir  string
	spec string
}

type GoDirs struct {
	Dir        string
	Imports       []string
	GoFiles    []string
	SFiles     []string
	HFiles     []string
	Goroot     bool
	ImportPath string
}

const (
	devcpio   = "scripts/dev.cpio"
	urootPath = "src/github.com/u-root/u-root"
	// huge suckage here. the 'old' usage is going away but it not gone yet. Just suck in old6a for now.
	// I don't want to revive the 'letter' stuff.
	// This has gotten kind of ugly. But [0] is source, [1] is dest, and [2..] is the list.
	// FIXME. this is ugly.
)

var (
	goList = `{{.Goroot}}
go
pkg/include
VERSION.cache`
	urootList = `{{.Gopath}}
`
	config struct {
		Goroot          string
		Godotdot        string
		Godot           string
		Arch            string
		Goos            string
		Gopath          string
		Urootpath       string
		TempDir         string
		Go              string
		Debug           bool
		Fail            bool
		TestChroot      bool
		RemoveDir       bool
		InitialCpio     string
		UseExistingInit bool
	}
	Dirs        map[string]bool
	Imports        map[string]bool
	GorootFiles map[string]bool
	UrootFiles  map[string]bool
	letter      = map[string]string{
		"amd64": "6",
		"386":   "8",
		"arm":   "5",
		"ppc":   "9",
	}
	// the whitelist is a list of u-root tools that we feel
	// can replace existing tools. It is, sadly, a very short
	// list at present.
	whitelist = []string{"date"}
	debug     = nodebug
)

func nodebug(string, ...interface{}) {}

func getenvOrDefault(e, defaultValue string) string {
	v := os.Getenv(e)
	if v == "" {
		v = defaultValue
	}
	return v
}

func lsr(n string, w *os.File) error {
	n = n + "/"
	err := filepath.Walk(n, func(name string, fi os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		cn := strings.TrimPrefix(name, n)
		fmt.Fprintf(w, "%v\n", cn)
		return nil
	})
	return err
}

// It's annoying asking them to set lots of things. So let's try to figure it out.
func guessgoarch() {
	config.Arch = os.Getenv("GOARCH")
	if config.Arch != "" {
		config.Arch = path.Clean(config.Arch)
		return
	}
	log.Printf("GOARCH is not set, trying to guess")
	u, err := uroot.Uname()
	if err != nil {
		log.Printf("uname failed, using default amd64")
		config.Arch = "amd64"
	} else {
		switch {
		case u.Machine == "i686" || u.Machine == "i386" || u.Machine == "x86":
			config.Arch = "386"
		case u.Machine == "x86_64" || u.Machine == "amd64":
			config.Arch = "amd64"
		case u.Machine == "armv7l" || u.Machine == "armv6l":
			config.Arch = "arm"
		case u.Machine == "ppc" || u.Machine == "ppc64":
			config.Arch = "ppc64"
		default:
			log.Printf("Unrecognized arch")
			config.Fail = true
		}
	}
}
func guessgoroot() {
	config.Goroot = os.Getenv("GOROOT")
	if config.Goroot != "" {
		config.Goroot = path.Clean(config.Goroot)
		log.Printf("Using %v from the environment as the GOROOT", config.Goroot)
		config.Godotdot = path.Dir(config.Goroot)
		return
	}
	log.Print("Goroot is not set, trying to find a go binary")
	p := os.Getenv("PATH")
	paths := strings.Split(p, ":")
	for _, v := range paths {
		g := path.Join(v, "go")
		if _, err := os.Stat(g); err == nil {
			config.Goroot = path.Dir(path.Dir(v))
			config.Godotdot = path.Dir(config.Goroot)
			log.Printf("Guessing that goroot is %v from $PATH", config.Goroot)
			return
		}
	}
	log.Printf("GOROOT is not set and can't find a go binary in %v", p)
	config.Fail = true
}

func guessgopath() {
	defer func() {
		config.Godotdot = path.Dir(config.Goroot)
	}()
	gopath := os.Getenv("GOPATH")
	if gopath != "" {
		config.Gopath = gopath
		config.Urootpath = path.Join(gopath, urootPath)
		return
	}
	// It's a good chance they're running this from the u-root source directory
	log.Fatalf("Fix up guessgopath")
	cwd, err := os.Getwd()
	if err != nil {
		log.Printf("GOPATH was not set and I can't get the wd: %v", err)
		config.Fail = true
		return
	}
	// walk up the cwd until we find a u-root entry. See if cmds/init/init.go exists.
	for c := cwd; c != "/"; c = path.Dir(c) {
		if path.Base(c) != "u-root" {
			continue
		}
		check := path.Join(c, "cmds/init/init.go")
		if _, err := os.Stat(check); err != nil {
			//log.Printf("Could not stat %v", check)
			continue
		}
		config.Gopath = c
		log.Printf("Guessing %v as GOPATH", c)
		os.Setenv("GOPATH", c)
		return
	}
	config.Fail = true
	log.Printf("GOPATH was not set, and I can't see a u-root-like name in %v", cwd)
	return
}

// goListPkg takes one package name, and computes all the files it needs to build,
// seperating them into Go tree files and uroot files. For now we just 'go list'
// but hopefully later we can do this programatically.
func goListPkg(name string) (*GoDirs, error) {
	cmd := exec.Command("go", "list", "-json", name)
	cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
	debug("Run %v @ %v", cmd, cmd.Dir)
	j, err := cmd.CombinedOutput()
	if err != nil {
		return nil, err
	}

	var p GoDirs
	if err := json.Unmarshal([]byte(j), &p); err != nil {
		return nil, err
	}

	debug("%v, %v %v %v", p, p.GoFiles, p.SFiles, p.HFiles)
	for _, v := range append(append(p.GoFiles, p.SFiles...), p.HFiles...) {
		if p.Goroot {
			GorootFiles[path.Join(p.ImportPath, v)] = true
		} else {
			UrootFiles[path.Join(p.ImportPath, v)] = true
		}
	}

	return &p, nil
}

// addGoFiles Computes the set of Go files to be added to the initramfs.
func addGoFiles() error {
	var pkgList []string
	// Walk the cmds/ directory, and for each directory in there, add its files and all its
	// dependencies

	err := filepath.Walk(path.Join(config.Urootpath, "cmds"), func(name string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Printf(" WALK FAIL%v: %v\n", name, err)
			// That's ok, sometimes things are not there.
			return filepath.SkipDir
		}
		if fi.Name() == "cmds" {
			return nil
		}
		if !fi.IsDir() {
			return nil
		}
		pkgList = append(pkgList, path.Join("github.com/u-root/u-root/cmds", fi.Name()))
		return filepath.SkipDir
	})
	if err != nil {
		log.Printf("Walking cmds/: %v\n", err)
	}
	// It would be nice to run go list -json with lots of package names but it produces invalid JSON.
	// It produces a stream thatis {}{}{} at the top level and the decoders don't like that.
	// TODO: fix it later. Maybe use template after all. For now this is more than adequate.
	for _, v := range pkgList {
		p, err := goListPkg(v)
		if err != nil {
			log.Fatalf("%v", err)
		}
		debug("cmd p is %v", p)
		for _,v := range p.Imports {
			Imports[v] = true
		}
	}

/*
	for v := range Deps {
		if _, err := goListPkg(v); err != nil {
			log.Fatalf("%v", err)
		}
	}
*/
	return nil
}

// sad news. If I concat the Go cpio with the other cpios, for reasons I don't understand,
// the kernel can't unpack it. Don't know why, don't care. Need to create one giant cpio and unpack that.
// It's not size related: if the go archive is first or in the middle it still fails.
func main() {
	flag.BoolVar(&config.Debug, "d", false, "Debugging")
	flag.Parse()
	if config.Debug {
		debug = log.Printf
	}

	Dirs = make(map[string]bool)
	Imports = make(map[string]bool)
	GorootFiles = make(map[string]bool)
	UrootFiles = make(map[string]bool)
	guessgoarch()
	config.Go = ""
	config.Goos = "linux"
	guessgoroot()
	guessgopath()
	if config.Fail {
		log.Fatal("Setup failed")
	}

	if err := addGoFiles(); err != nil {
		log.Fatalf("%v", err)
	}

	for i := range Imports {
		log.Printf("Dep: %v", i)
	}

}
<commit_msg>getimports: It Just Works<commit_after>package main

import (
	"go/build"
	"flag"
	"github.com/u-root/u-root/uroot"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
)

type copyfiles struct {
	dir  string
	spec string
}

type GoDirs struct {
	Dir        string
	Imports       []string
	GoFiles    []string
	SFiles     []string
	HFiles     []string
	Goroot     bool
	ImportPath string
}

const (
	devcpio   = "scripts/dev.cpio"
	urootPath = "src/github.com/u-root/u-root"
	// huge suckage here. the 'old' usage is going away but it not gone yet. Just suck in old6a for now.
	// I don't want to revive the 'letter' stuff.
	// This has gotten kind of ugly. But [0] is source, [1] is dest, and [2..] is the list.
	// FIXME. this is ugly.
)

var (
	goList = `{{.Goroot}}
go
pkg/include
VERSION.cache`
	urootList = `{{.Gopath}}
`
	config struct {
		Goroot          string
		Godotdot        string
		Godot           string
		Arch            string
		Goos            string
		Gopath          string
		Urootpath       string
		TempDir         string
		Go              string
		Debug           bool
		Fail            bool
		TestChroot      bool
		RemoveDir       bool
		InitialCpio     string
		UseExistingInit bool
	}
	Dirs        map[string]bool
	Imports        map[string]bool
	GorootFiles map[string]bool
	UrootFiles  map[string]bool
	letter      = map[string]string{
		"amd64": "6",
		"386":   "8",
		"arm":   "5",
		"ppc":   "9",
	}
	// the whitelist is a list of u-root tools that we feel
	// can replace existing tools. It is, sadly, a very short
	// list at present.
	whitelist = []string{"date"}
	debug     = nodebug
)

func nodebug(string, ...interface{}) {}

// It's annoying asking them to set lots of things. So let's try to figure it out.
func guessgoarch() {
	config.Arch = os.Getenv("GOARCH")
	if config.Arch != "" {
		config.Arch = path.Clean(config.Arch)
		return
	}
	log.Printf("GOARCH is not set, trying to guess")
	u, err := uroot.Uname()
	if err != nil {
		log.Printf("uname failed, using default amd64")
		config.Arch = "amd64"
	} else {
		switch {
		case u.Machine == "i686" || u.Machine == "i386" || u.Machine == "x86":
			config.Arch = "386"
		case u.Machine == "x86_64" || u.Machine == "amd64":
			config.Arch = "amd64"
		case u.Machine == "armv7l" || u.Machine == "armv6l":
			config.Arch = "arm"
		case u.Machine == "ppc" || u.Machine == "ppc64":
			config.Arch = "ppc64"
		default:
			log.Printf("Unrecognized arch")
			config.Fail = true
		}
	}
}
func guessgoroot() {
	config.Goroot = os.Getenv("GOROOT")
	if config.Goroot != "" {
		config.Goroot = path.Clean(config.Goroot)
		log.Printf("Using %v from the environment as the GOROOT", config.Goroot)
		config.Godotdot = path.Dir(config.Goroot)
		return
	}
	log.Print("Goroot is not set, trying to find a go binary")
	p := os.Getenv("PATH")
	paths := strings.Split(p, ":")
	for _, v := range paths {
		g := path.Join(v, "go")
		if _, err := os.Stat(g); err == nil {
			config.Goroot = path.Dir(path.Dir(v))
			config.Godotdot = path.Dir(config.Goroot)
			log.Printf("Guessing that goroot is %v from $PATH", config.Goroot)
			return
		}
	}
	log.Printf("GOROOT is not set and can't find a go binary in %v", p)
	config.Fail = true
}

func guessgopath() {
	defer func() {
		config.Godotdot = path.Dir(config.Goroot)
	}()
	gopath := os.Getenv("GOPATH")
	if gopath != "" {
		config.Gopath = gopath
		config.Urootpath = path.Join(gopath, urootPath)
		return
	}
	// It's a good chance they're running this from the u-root source directory
	log.Fatalf("Fix up guessgopath")
	cwd, err := os.Getwd()
	if err != nil {
		log.Printf("GOPATH was not set and I can't get the wd: %v", err)
		config.Fail = true
		return
	}
	// walk up the cwd until we find a u-root entry. See if cmds/init/init.go exists.
	for c := cwd; c != "/"; c = path.Dir(c) {
		if path.Base(c) != "u-root" {
			continue
		}
		check := path.Join(c, "cmds/init/init.go")
		if _, err := os.Stat(check); err != nil {
			//log.Printf("Could not stat %v", check)
			continue
		}
		config.Gopath = c
		log.Printf("Guessing %v as GOPATH", c)
		os.Setenv("GOPATH", c)
		return
	}
	config.Fail = true
	log.Printf("GOPATH was not set, and I can't see a u-root-like name in %v", cwd)
	return
}

// addGoFiles Computes the set of Go files to be added to the initramfs.
func addGoFiles() error {
	var pkgList []string
	// Walk the cmds/ directory, and for each directory in there, add its files and all its
	// dependencies

	err := filepath.Walk(path.Join(config.Urootpath, "cmds"), func(name string, fi os.FileInfo, err error) error {
		if err != nil {
			log.Printf(" WALK FAIL%v: %v\n", name, err)
			// That's ok, sometimes things are not there.
			return filepath.SkipDir
		}
		if fi.Name() == "cmds" {
			return nil
		}
		if !fi.IsDir() {
			return nil
		}
		pkgList = append(pkgList, path.Join("github.com/u-root/u-root/cmds", fi.Name()))
		return filepath.SkipDir
	})
	if err != nil {
		log.Printf("Walking cmds/: %v\n", err)
	}
	// It would be nice to run go list -json with lots of package names but it produces invalid JSON.
	// It produces a stream thatis {}{}{} at the top level and the decoders don't like that.
	// TODO: fix it later. Maybe use template after all. For now this is more than adequate.
	for _, v := range pkgList {
		p, err := build.Default.Import(v, "", 0)
		if err != nil {
			log.Printf("Error on %v: %v", v, err)
			continue
		}
		log.Printf("v %v  Groot %v %v ", v, p.Goroot, p.Imports)
		if err != nil {
			log.Fatalf("%v", err)
		}
		debug("cmd p is %v", p)
		for _,v := range p.Imports {
			log.Printf("Check %v", v)
			if ! p.Goroot {
				Imports[v] = true
			}
		}
	}

	return nil
}

func main() {
	flag.BoolVar(&config.Debug, "d", false, "Debugging")
	flag.Parse()
	if config.Debug {
		debug = log.Printf
	}

	Dirs = make(map[string]bool)
	Imports = make(map[string]bool)
	GorootFiles = make(map[string]bool)
	UrootFiles = make(map[string]bool)
	guessgoarch()
	config.Go = ""
	config.Goos = "linux"
	guessgoroot()
	guessgopath()
	if config.Fail {
		log.Fatal("Setup failed")
	}

	if err := addGoFiles(); err != nil {
		log.Fatalf("%v", err)
	}

	for i := range Imports {
		log.Printf("Dep: %v", i)
		_, err := build.Default.Import(i, "", build.FindOnly)
		if err == nil {
			debug("Package %v exists, not getting it", i)
			continue
		}
		debug("go get %v", i)
		cmd := exec.Command("go", "get", "-a", i)
		cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
		debug("Run %v @ %v", cmd, cmd.Dir)
		j, err := cmd.CombinedOutput()
		if err != nil {
			log.Printf("Go get failed: err %v, output \n%v\n", err, j)
		}
		debug("We got %v", i)
	}

}
<|endoftext|>