/*
* Copyright (C) 2018 "IoT.bzh"
* Author Sebastien Douheret <sebastien@iot.bzh>
*
* 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 xdsserver
import (
"fmt"
"gerrit.automotivelinux.org/gerrit/src/xds/xds-server.git/lib/xsapiv1"
"github.com/syncthing/syncthing/lib/sync"
)
// Terminals Represent a XDS terminals
type Terminals struct {
*Context
terms map[string]*ITERMINAL
}
// Mutex to make add/delete atomic
var tmMutex = sync.NewMutex()
// TerminalsConstructor Create a new instance of Model Terminal
func TerminalsConstructor(ctx *Context) *Terminals {
return &Terminals{
Context: ctx,
terms: make(map[string]*ITERMINAL),
}
}
// New Create a new terminal
func (t *Terminals) New(cfg xsapiv1.TerminalConfig, targetID string) (*xsapiv1.TerminalConfig, error) {
tmMutex.Lock()
defer tmMutex.Unlock()
var newT ITERMINAL
// For now, only SSH term is supported
switch cfg.Type {
case xsapiv1.TypeTermSSH:
newT = NewTermSSH(t.Context, cfg, targetID)
default:
return nil, fmt.Errorf("terminal type not set")
}
termCfg := newT.GetConfig()
t.terms[termCfg.ID] = &newT
termCfg.Status = xsapiv1.StatusTermEnable
// Notify terminal add
if err := t.events.Emit(xsapiv1.EVTTargetTerminalAdd, &termCfg, ""); err != nil {
t.Log.Errorf("WS Emit EVTTargetTerminalAdd : %v", err)
}
return &termCfg, nil
}
// Free a specific terminal
func (t *Terminals) Free(id string) (*xsapiv1.TerminalConfig, error) {
tmMutex.Lock()
defer tmMutex.Unlock()
tc := t.Get(id)
if tc == nil {
return nil, fmt.Errorf("Unknown id")
}
if _, err := (*tc).Close(); err != nil {
return nil, err
}
resTerm := (*tc).GetConfig()
delete(t.terms, id)
// Notify terminal state change or add
if err := t.events.Emit(xsapiv1.EVTTargetTerminalRemove, &resTerm, ""); err != nil {
t.Log.Errorf("WS Emit EVTTargetTerminalRemove : %v", err)
}
return &resTerm, nil
}
// Get returns the terminal config or nil if not existing
func (t *Terminals) Get(id string) *ITERMINAL {
if id == "" {
return nil
}
tc, exist := t.terms[id]
if !exist {
return nil
}
return tc
}
// GetConfigArr returns the config of all terminals as an array
func (t *Terminals) GetConfigArr() []xsapiv1.TerminalConfig {
tmMutex.Lock()
defer tmMutex.Unlock()
return t.getConfigArrUnsafe()
}
// getConfigArrUnsafe Same as GetConfigArr without mutex protection
func (t *Terminals) getConfigArrUnsafe() []xsapiv1.TerminalConfig {
conf := []xsapiv1.TerminalConfig{}
for _, v := range t.terms {
conf = append(conf, (*v).GetConfig())
}
return conf
}
// Open adds a new terminal
func (t *Terminals) Open(id string, sess *ClientSession) (*xsapiv1.TerminalConfig, error) {
tc := t.Get(id)
if tc == nil {
return nil, fmt.Errorf("Unknown id")
}
if sess.IOSocket == nil {
return nil, fmt.Errorf("Websocket not established")
}
term, err := (*tc).Open(sess.IOSocket, sess.ID)
// Notify term state change
if errEmit := t.events.Emit(xsapiv1.EVTTargetTerminalStateChange, &term, sess.ID); errEmit != nil {
t.Log.Errorf("WS Emit EVTTargetTerminalStateChange : %v", errEmit)
}
return term, err
}
// Close a specific terminal
func (t *Terminals) Close(id string, sess *ClientSession) (*xsapiv1.TerminalConfig, error) {
tc := t.Get(id)
if tc == nil {
return nil, fmt.Errorf("Unknown id")
}
term, err := (*tc).Close()
// Notify term state change
if errEmit := t.events.Emit(xsapiv1.EVTTargetTerminalStateChange, &term, sess.ID); errEmit != nil {
t.Log.Errorf("WS Emit EVTTargetTerminalStateChange : %v", errEmit)
}
return term, err
}
// Resize a specific terminal
func (t *Terminals) Resize(id string, cols, rows uint16, sess *ClientSession) (*xsapiv1.TerminalConfig, error) {
tmMutex.Lock()
defer tmMutex.Unlock()
tc := t.Get(id)
if tc == nil {
return nil, fmt.Errorf("Unknown id")
}
term, err := (*tc).Resize(cols, rows)
// Notify term state change
if errEmit := t.events.Emit(xsapiv1.EVTTargetTerminalStateChange, &term, sess.ID); errEmit != nil {
t.Log.Errorf("WS Emit EVTTargetTerminalStateChange : %v", errEmit)
}
return term, err
}
// Signal Send a Signal a specific terminal
func (t *Terminals) Signal(id, sigName string) error {
tmMutex.Lock()
defer tmMutex.Unlock()
tc := t.Get(id)
if tc == nil {
return fmt.Errorf("Unknown id")
}
return (*tc).Signal(sigName)
}