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

package main

import (
	"bufio"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/clearcontainers/proxy/api"

	"github.com/containers/virtcontainers/pkg/hyperstart"
)

// Represents a single qemu/hyperstart instance on the system
type vm struct {
	sync.Mutex

	containerID string

	logIO   *logrus.Entry
	logQemu *logrus.Entry

	hyperHandler *hyperstart.Hyperstart

	// Socket to the VM console
	console struct {
		socketPath string
		conn       net.Conn
	}

	// Used to allocate globally unique IO sequence numbers
	nextIoBase uint64

	// ios are hashed by their sequence numbers. If 2 sequence numbers are
	// allocated for one process (stdin/stdout and stderr) both sequence
	// numbers appear in this map.
	ioSessions map[uint64]*ioSession

	// tokenToSession associate a token to the corresponding ioSession
	tokenToSession map[Token]*ioSession

	// nullSession is a special I/O session used for containers and execcmd processes
	// when client of the proxy indicates they don't care about communicating with the
	// process inside the VM.
	nullSession ioSession

	// Used to wait for all VM-global goroutines to finish on Close()
	wg sync.WaitGroup

	// Channel to signal qemu has terminated.
	vmLost chan interface{}
}

// A set of I/O streams between a client and a process running inside the VM
type ioSession struct {
	// token is what identifies the I/O session to the external world
	token Token

	// containerID is the container ID related to this session
	containerID string

	// Back pointer to the VM this session is attached to.
	vm *vm

	nStreams int
	ioBase   uint64
	// Have we received the EOF paquet from hyperstart for this session?
	terminated bool

	// id  of the client owning that ioSession (the shim process, usually).
	clientID uint64

	// socket connected to the fd sent over to the client
	client net.Conn

	// Channel to signal a shim has been associated with this session (hyper
	// commands newcontainer and execcmd will wait for the shim to be ready
	// before forwarding the command to hyperstart)
	shimConnected chan interface{}

	// Channel to signal the process corresponding to that session has been
	// started. This is used to stop the shim from sending stdin data to a
	// non-existent process.
	processStarted chan interface{}
}

const (
	nullSessionStdout = 1 + iota
	nullSessionStderr
	firstIoBase
)

func newVM(id, ctlSerial, ioSerial string) *vm {
	h := hyperstart.NewHyperstart(ctlSerial, ioSerial, "unix")

	log := proxyLog.WithFields(logrus.Fields{"vm": id})

	vm := &vm{
		containerID:    id,
		logIO:          log.WithField("section", "io"),
		logQemu:        log.WithField("source", "qemu"),
		hyperHandler:   h,
		nextIoBase:     firstIoBase,
		ioSessions:     make(map[uint64]*ioSession),
		tokenToSession: make(map[Token]*ioSession),
		vmLost:         make(chan interface{}),
	}

	vm.nullSession = ioSession{
		vm:             vm,
		nStreams:       2,
		ioBase:         nullSessionStdout,
		shimConnected:  make(chan interface{}),
		processStarted: make(chan interface{}),
	}
	vm.ioSessions[nullSessionStdout] = &vm.nullSession
	vm.ioSessions[nullSessionStderr] = &vm.nullSession
	// Null sessions are always ready, there won't be a shim to wait for
	close(vm.nullSession.shimConnected)

	return vm
}

// ResetShim resets the session state related to the shim. Call this when we
// lose the connection to the shim, whether we close it ourselves or the shim
// crashes and the connection is closed.
func (session *ioSession) ResetShim() {
	session.vm.logIO.Debug("lost the shim for %s", session.token)
	if session == &session.vm.nullSession {
		return
	}
	session.shimConnected = make(chan interface{})
}

// setConsole() will make the proxy output the console data on stderr
func (vm *vm) setConsole(path string) {
	vm.console.socketPath = path
}

func (vm *vm) shortName() string {
	length := 8
	if len(vm.containerID) < 8 {
		length = len(vm.containerID)
	}
	return vm.containerID[0:length]
}

func (vm *vm) dump(data []byte) {
	if logrus.GetLevel() != logrus.DebugLevel {
		return
	}

	if len(data) == 0 {
		return
	}

	proxyLog.WithField("wm", vm.containerID).Debug("\n", hex.Dump(data))
}

func (vm *vm) findSessionBySeq(seq uint64) *ioSession {
	vm.Lock()<commit_msg>logging: Fix logging of hex messages<commit_after>// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bufio"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/clearcontainers/proxy/api"

	"github.com/containers/virtcontainers/pkg/hyperstart"
)

// Represents a single qemu/hyperstart instance on the system
type vm struct {
	sync.Mutex

	containerID string

	logIO   *logrus.Entry
	logQemu *logrus.Entry

	hyperHandler *hyperstart.Hyperstart

	// Socket to the VM console
	console struct {
		socketPath string
		conn       net.Conn
	}

	// Used to allocate globally unique IO sequence numbers
	nextIoBase uint64

	// ios are hashed by their sequence numbers. If 2 sequence numbers are
	// allocated for one process (stdin/stdout and stderr) both sequence
	// numbers appear in this map.
	ioSessions map[uint64]*ioSession

	// tokenToSession associate a token to the corresponding ioSession
	tokenToSession map[Token]*ioSession

	// nullSession is a special I/O session used for containers and execcmd processes
	// when client of the proxy indicates they don't care about communicating with the
	// process inside the VM.
	nullSession ioSession

	// Used to wait for all VM-global goroutines to finish on Close()
	wg sync.WaitGroup

	// Channel to signal qemu has terminated.
	vmLost chan interface{}
}

// A set of I/O streams between a client and a process running inside the VM
type ioSession struct {
	// token is what identifies the I/O session to the external world
	token Token

	// containerID is the container ID related to this session
	containerID string

	// Back pointer to the VM this session is attached to.
	vm *vm

	nStreams int
	ioBase   uint64
	// Have we received the EOF paquet from hyperstart for this session?
	terminated bool

	// id  of the client owning that ioSession (the shim process, usually).
	clientID uint64

	// socket connected to the fd sent over to the client
	client net.Conn

	// Channel to signal a shim has been associated with this session (hyper
	// commands newcontainer and execcmd will wait for the shim to be ready
	// before forwarding the command to hyperstart)
	shimConnected chan interface{}

	// Channel to signal the process corresponding to that session has been
	// started. This is used to stop the shim from sending stdin data to a
	// non-existent process.
	processStarted chan interface{}
}

const (
	nullSessionStdout = 1 + iota
	nullSessionStderr
	firstIoBase
)

func newVM(id, ctlSerial, ioSerial string) *vm {
	h := hyperstart.NewHyperstart(ctlSerial, ioSerial, "unix")

	log := proxyLog.WithFields(logrus.Fields{"vm": id})

	vm := &vm{
		containerID:    id,
		logIO:          log.WithField("section", "io"),
		logQemu:        log.WithField("source", "qemu"),
		hyperHandler:   h,
		nextIoBase:     firstIoBase,
		ioSessions:     make(map[uint64]*ioSession),
		tokenToSession: make(map[Token]*ioSession),
		vmLost:         make(chan interface{}),
	}

	vm.nullSession = ioSession{
		vm:             vm,
		nStreams:       2,
		ioBase:         nullSessionStdout,
		shimConnected:  make(chan interface{}),
		processStarted: make(chan interface{}),
	}
	vm.ioSessions[nullSessionStdout] = &vm.nullSession
	vm.ioSessions[nullSessionStderr] = &vm.nullSession
	// Null sessions are always ready, there won't be a shim to wait for
	close(vm.nullSession.shimConnected)

	return vm
}

// ResetShim resets the session state related to the shim. Call this when we
// lose the connection to the shim, whether we close it ourselves or the shim
// crashes and the connection is closed.
func (session *ioSession) ResetShim() {
	session.vm.logIO.Debug("lost the shim for %s", session.token)
	if session == &session.vm.nullSession {
		return
	}
	session.shimConnected = make(chan interface{})
}

// setConsole() will make the proxy output the console data on stderr
func (vm *vm) setConsole(path string) {
	vm.console.socketPath = path
}

func (vm *vm) shortName() string {
	length := 8
	if len(vm.containerID) < 8 {
		length = len(vm.containerID)
	}
	return vm.containerID[0:length]
}

func (vm *vm) dump(data []byte) {
	if logrus.GetLevel() != logrus.DebugLevel {
		return
	}

	if len(data) == 0 {
		return
	}

	// Format data across multiple lines in hex.
	//
	// This isn't ideal as each log entry should refer to a unique entity
	// but this particular data is pretty-printed for human-consumption.
	hexLines := strings.Split(hex.Dump(data), "\n")
	for _, x := range hexLines {
		proxyLog.WithField("wm", vm.containerID).Debug(x)
	}
}

func (vm *vm) findSessionBySeq(seq uint64) *ioSession {
	vm.Lock()
{'fraction_non_alphanumeric': 0.09194499017681729, 'fraction_numerical': 0.005304518664047151, 'mean_word_length': 3.598464317976513, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7066087', 'n_tokens_mistral': 3261, 'n_tokens_neox': 3099, 'n_words': 1182}