the base
This commit is contained in:
24
lib/osal/tmux/readme.md
Normal file
24
lib/osal/tmux/readme.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# TMUX
|
||||
|
||||
|
||||
TMUX is a very capable process manager.
|
||||
|
||||
### Concepts
|
||||
|
||||
- tmux = is the factory, it represents the tmux process manager, linked to a node
|
||||
- session = is a set of windows, it has a name and groups windows
|
||||
- window = is typically one process running (you can have panes but in our implementation we skip this)
|
||||
|
||||
|
||||
## structure
|
||||
|
||||
tmux library provides functions for managing tmux sessions
|
||||
|
||||
- session is the top one
|
||||
- then windows (is where you see the app running)
|
||||
- then panes in windows (we don't support yet)
|
||||
|
||||
|
||||
## to attach to a tmux session
|
||||
|
||||
> TODO:
|
||||
86
lib/osal/tmux/testdata/tmux_session_test.v
vendored
Normal file
86
lib/osal/tmux/testdata/tmux_session_test.v
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
module tmux
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.installers.tmux
|
||||
|
||||
// fn testsuite_end() {
|
||||
|
||||
//
|
||||
// }
|
||||
|
||||
fn testsuite_begin() {
|
||||
mut tmux := Tmux{}
|
||||
|
||||
if tmux.is_running()! {
|
||||
tmux.stop()!
|
||||
}
|
||||
}
|
||||
|
||||
fn test_session_create() {
|
||||
// installer := tmux.get_install(
|
||||
// panic('could not install tmux: ${err}')
|
||||
// }
|
||||
|
||||
mut tmux := Tmux{}
|
||||
tmux.start() or { panic('cannot start tmux: ${err}') }
|
||||
|
||||
mut s := Session{
|
||||
tmux: &tmux
|
||||
windows: map[string]&Window{}
|
||||
name: 'testsession'
|
||||
}
|
||||
|
||||
mut s2 := Session{
|
||||
tmux: &tmux
|
||||
windows: map[string]&Window{}
|
||||
name: 'testsession2'
|
||||
}
|
||||
|
||||
// test testsession exists after session_create
|
||||
mut tmux_ls := osal.execute_silent('tmux ls') or { panic("can't exec: ${err}") }
|
||||
assert !tmux_ls.contains('testsession: 1 windows')
|
||||
s.create() or { panic('Cannot create session: ${err}') }
|
||||
tmux_ls = osal.execute_silent('tmux ls') or { panic("can't exec: ${err}") }
|
||||
assert tmux_ls.contains('testsession: 1 windows')
|
||||
|
||||
// test multiple session_create for same tmux
|
||||
tmux_ls = osal.execute_silent('tmux ls') or { panic("can't exec: ${err}") }
|
||||
assert !tmux_ls.contains('testsession2: 1 windows')
|
||||
s2.create() or { panic('Cannot create session: ${err}') }
|
||||
tmux_ls = osal.execute_silent('tmux ls') or { panic("can't exec: ${err}") }
|
||||
assert tmux_ls.contains('testsession2: 1 windows')
|
||||
|
||||
// test session_create with duplicate session
|
||||
mut create_err := ''
|
||||
s2.create() or { create_err = err.msg() }
|
||||
assert create_err != ''
|
||||
assert create_err.contains('duplicate session: testsession2')
|
||||
tmux_ls = osal.execute_silent('tmux ls') or { panic("can't exec: ${err}") }
|
||||
assert tmux_ls.contains('testsession2: 1 windows')
|
||||
|
||||
s.stop() or { panic('Cannot stop session: ${err}') }
|
||||
s2.stop() or { panic('Cannot stop session: ${err}') }
|
||||
}
|
||||
|
||||
// fn test_session_stop() {
|
||||
|
||||
//
|
||||
// installer := tmux.get_install(
|
||||
|
||||
// mut tmux := Tmux {
|
||||
// node: node_ssh
|
||||
// }
|
||||
|
||||
// mut s := Session{
|
||||
// tmux: &tmux // reference back
|
||||
// windows: map[string]&Window{}
|
||||
// name: 'testsession3'
|
||||
// }
|
||||
|
||||
// s.create() or { panic("Cannot create session: $err") }
|
||||
// mut tmux_ls := osal.execute_silent('tmux ls') or { panic("can't exec: $err") }
|
||||
// assert tmux_ls.contains("testsession3: 1 windows")
|
||||
// s.stop() or { panic("Cannot stop session: $err")}
|
||||
// tmux_ls = osal.execute_silent('tmux ls') or { panic("can't exec: $err") }
|
||||
// assert !tmux_ls.contains("testsession3: 1 windows")
|
||||
// }
|
||||
67
lib/osal/tmux/testdata/tmux_window_test.v
vendored
Normal file
67
lib/osal/tmux/testdata/tmux_window_test.v
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
module tmux
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.installers.tmux
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
// uses single tmux instance for all tests
|
||||
__global (
|
||||
tmux Tmux
|
||||
)
|
||||
|
||||
fn init() {
|
||||
tmux = get_remote('185.69.166.152')!
|
||||
|
||||
// reset tmux for tests
|
||||
if tmux.is_running() {
|
||||
tmux.stop() or { panic('Cannot stop tmux') }
|
||||
}
|
||||
}
|
||||
|
||||
fn testsuite_end() {
|
||||
if tmux.is_running() {
|
||||
tmux.stop()!
|
||||
}
|
||||
}
|
||||
|
||||
fn test_window_new() {
|
||||
tmux.start() or { panic("can't start tmux: ${err}") }
|
||||
|
||||
// test window new with only name arg
|
||||
window_args := WindowArgs{
|
||||
name: 'TestWindow'
|
||||
}
|
||||
|
||||
assert !tmux.sessions.keys().contains('main')
|
||||
|
||||
mut window := tmux.window_new(window_args) or { panic("Can't create new window: ${err}") }
|
||||
assert tmux.sessions.keys().contains('main')
|
||||
window.delete() or { panic('Cant delete window') }
|
||||
}
|
||||
|
||||
// // tests creating duplicate windows
|
||||
// fn test_window_new0() {
|
||||
|
||||
//
|
||||
// installer := tmux.get_install(
|
||||
|
||||
// mut tmux := Tmux {
|
||||
// node: node_ssh
|
||||
// }
|
||||
|
||||
// window_args := WindowArgs {
|
||||
// name: 'TestWindow0'
|
||||
// }
|
||||
|
||||
// // console.print_debug(tmux)
|
||||
// mut window := tmux.window_new(window_args) or {
|
||||
// panic("Can't create new window: $err")
|
||||
// }
|
||||
// assert tmux.sessions.keys().contains('main')
|
||||
// mut window_dup := tmux.window_new(window_args) or {
|
||||
// panic("Can't create new window: $err")
|
||||
// }
|
||||
// console.print_debug(node_ssh.exec('tmux ls') or { panic("fail:$err")})
|
||||
// window.delete() or { panic("Cant delete window") }
|
||||
// // console.print_debug(tmux)
|
||||
// }
|
||||
116
lib/osal/tmux/tmux.v
Normal file
116
lib/osal/tmux/tmux.v
Normal file
@@ -0,0 +1,116 @@
|
||||
module tmux
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
// import freeflowuniverse.herolib.session
|
||||
import os
|
||||
import time
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
@[heap]
|
||||
pub struct Tmux {
|
||||
pub mut:
|
||||
sessions []&Session
|
||||
sessionid string // unique link to job
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct TmuxNewArgs {
|
||||
sessionid string
|
||||
}
|
||||
|
||||
// return tmux instance
|
||||
pub fn new(args TmuxNewArgs) !Tmux {
|
||||
mut t := Tmux{
|
||||
sessionid: args.sessionid
|
||||
}
|
||||
t.load()!
|
||||
t.scan()!
|
||||
return t
|
||||
}
|
||||
|
||||
// loads tmux session, populate the object
|
||||
pub fn (mut tmux Tmux) load() ! {
|
||||
isrunning := tmux.is_running()!
|
||||
if !isrunning {
|
||||
tmux.start()!
|
||||
}
|
||||
// console.print_debug("SCAN")
|
||||
tmux.scan()!
|
||||
}
|
||||
|
||||
pub fn (mut t Tmux) stop() ! {
|
||||
$if debug {
|
||||
console.print_debug('Stopping tmux...')
|
||||
}
|
||||
|
||||
t.sessions = []&Session{}
|
||||
t.scan()!
|
||||
|
||||
for _, mut session in t.sessions {
|
||||
session.stop()!
|
||||
}
|
||||
|
||||
cmd := 'tmux kill-server'
|
||||
_ := osal.exec(cmd: cmd, stdout: false, name: 'tmux_kill_server', ignore_error: true) or {
|
||||
panic('bug')
|
||||
}
|
||||
os.log('TMUX - All sessions stopped .')
|
||||
}
|
||||
|
||||
pub fn (mut t Tmux) start() ! {
|
||||
cmd := 'tmux new-sess -d -s main'
|
||||
_ := osal.exec(cmd: cmd, stdout: false, name: 'tmux_start') or {
|
||||
return error("Can't execute ${cmd} \n${err}")
|
||||
}
|
||||
// scan and add default bash window created with session init
|
||||
time.sleep(time.Duration(100 * time.millisecond))
|
||||
t.scan()!
|
||||
}
|
||||
|
||||
// print list of tmux sessions
|
||||
pub fn (mut t Tmux) list_print() {
|
||||
// os.log('TMUX - Start listing ....')
|
||||
for _, session in t.sessions {
|
||||
for _, window in session.windows {
|
||||
console.print_debug(window)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get all windows as found in all sessions
|
||||
pub fn (mut t Tmux) windows_get() []&Window {
|
||||
mut res := []&Window{}
|
||||
// os.log('TMUX - Start listing ....')
|
||||
for _, session in t.sessions {
|
||||
for _, window in session.windows {
|
||||
res << window
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// checks whether tmux server is running
|
||||
pub fn (mut t Tmux) is_running() !bool {
|
||||
res := osal.exec(cmd: 'tmux info', stdout: false, name: 'tmux_info', raise_error: false) or {
|
||||
panic('bug')
|
||||
}
|
||||
if res.error.contains('no server running') {
|
||||
// console.print_debug(" TMUX NOT RUNNING")
|
||||
return false
|
||||
}
|
||||
if res.error.contains('no current client') {
|
||||
return true
|
||||
}
|
||||
if res.exit_code > 0 {
|
||||
return error('could not execute tmux info.\n${res}')
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
pub fn (mut t Tmux) str() string {
|
||||
mut out := '# Tmux\n\n'
|
||||
for s in t.sessions {
|
||||
out += '${*s}\n'
|
||||
}
|
||||
return out
|
||||
}
|
||||
95
lib/osal/tmux/tmux_scan.v
Normal file
95
lib/osal/tmux/tmux_scan.v
Normal file
@@ -0,0 +1,95 @@
|
||||
module tmux
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
fn (mut t Tmux) scan_add(line string) !&Window {
|
||||
// console.print_debug(" -- scan add: $line")
|
||||
if line.count('|') < 4 {
|
||||
return error(@FN + 'expects line with at least 5 params separated by |')
|
||||
}
|
||||
|
||||
line_arr := line.split('|')
|
||||
session_name := line_arr[0]
|
||||
window_name := line_arr[1]
|
||||
window_id := line_arr[2]
|
||||
pane_active := line_arr[3]
|
||||
pane_id := line_arr[4]
|
||||
pane_pid := line_arr[5]
|
||||
pane_start_command := line_arr[6] or { '' }
|
||||
|
||||
wid := (window_id.replace('@', '')).int()
|
||||
|
||||
// os.log('TMUX FOUND: $line\n ++ $session_name:$window_name wid:$window_id pid:$pane_pid entrypoint:$pane_start_command')
|
||||
mut s := t.session_get(session_name)!
|
||||
|
||||
mut active := false
|
||||
if pane_active == '1' {
|
||||
active = true
|
||||
}
|
||||
|
||||
mut name := texttools.name_fix(window_name)
|
||||
|
||||
mut w := Window{
|
||||
session: s
|
||||
name: name
|
||||
}
|
||||
|
||||
if !(s.window_exist(name: window_name, id: wid)) {
|
||||
// console.print_debug("window not exists")
|
||||
s.windows << &w
|
||||
} else {
|
||||
w = s.window_get(name: window_name, id: wid)!
|
||||
}
|
||||
|
||||
w.id = wid
|
||||
w.active = active
|
||||
w.pid = pane_pid.int()
|
||||
w.paneid = (pane_id.replace('%', '')).int()
|
||||
w.cmd = pane_start_command
|
||||
|
||||
return &w
|
||||
}
|
||||
|
||||
// scan the system to detect sessions .
|
||||
pub fn (mut t Tmux) scan() ! {
|
||||
// os.log('TMUX - Scanning ....')
|
||||
|
||||
cmd_list_session := "tmux list-sessions -F '#{session_name}'"
|
||||
exec_list := osal.exec(cmd: cmd_list_session, stdout: false, name: 'tmux_list') or {
|
||||
return error('could not execute list sessions.\n${err}')
|
||||
}
|
||||
|
||||
// console.print_debug('execlist out for sessions: ${exec_list}')
|
||||
|
||||
// make sure we have all sessions
|
||||
for line in exec_list.output.split_into_lines() {
|
||||
session_name := line.trim(' \n').to_lower()
|
||||
if session_name == '' {
|
||||
continue
|
||||
}
|
||||
if t.session_exist(session_name) {
|
||||
continue
|
||||
}
|
||||
mut s := Session{
|
||||
tmux: &t // reference back
|
||||
name: session_name
|
||||
}
|
||||
t.sessions << &s
|
||||
}
|
||||
|
||||
console.print_debug(t)
|
||||
|
||||
// mut done := map[string]bool{}
|
||||
cmd := "tmux list-panes -a -F '#{session_name}|#{window_name}|#{window_id}|#{pane_active}|#{pane_id}|#{pane_pid}|#{pane_start_command}'"
|
||||
out := osal.execute_silent(cmd) or { return error("Can't execute ${cmd} \n${err}") }
|
||||
|
||||
// $if debug{console.print_debug('tmux list panes out:\n${out}')}
|
||||
|
||||
for line in out.split_into_lines() {
|
||||
if line.contains('|') {
|
||||
t.scan_add(line)!
|
||||
}
|
||||
}
|
||||
}
|
||||
153
lib/osal/tmux/tmux_session.v
Normal file
153
lib/osal/tmux/tmux_session.v
Normal file
@@ -0,0 +1,153 @@
|
||||
module tmux
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import os
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
@[heap]
|
||||
struct Session {
|
||||
pub mut:
|
||||
tmux &Tmux @[str: skip] // reference back
|
||||
windows []&Window // session has windows
|
||||
name string
|
||||
}
|
||||
|
||||
// get session (session has windows) .
|
||||
// returns none if not found
|
||||
pub fn (mut t Tmux) session_get(name_ string) !&Session {
|
||||
name := texttools.name_fix(name_)
|
||||
for s in t.sessions {
|
||||
if s.name == name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return error('Can not find session with name: \'${name_}\', out of loaded sessions.')
|
||||
}
|
||||
|
||||
pub fn (mut t Tmux) session_exist(name_ string) bool {
|
||||
name := texttools.name_fix(name_)
|
||||
t.session_get(name) or { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
pub fn (mut t Tmux) session_delete(name_ string) ! {
|
||||
if !(t.session_exist(name_)) {
|
||||
return
|
||||
}
|
||||
name := texttools.name_fix(name_)
|
||||
mut i := 0
|
||||
for mut s in t.sessions {
|
||||
if s.name == name {
|
||||
s.stop()!
|
||||
break
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
t.sessions.delete(i)
|
||||
}
|
||||
|
||||
@[params]
|
||||
pub struct SessionCreateArgs {
|
||||
pub mut:
|
||||
name string @[required]
|
||||
reset bool
|
||||
}
|
||||
|
||||
// create session, if reset will re-create
|
||||
pub fn (mut t Tmux) session_create(args SessionCreateArgs) !&Session {
|
||||
name := texttools.name_fix(args.name)
|
||||
if !(t.session_exist(name)) {
|
||||
$if debug {
|
||||
console.print_header(' tmux - create session: ${args}')
|
||||
}
|
||||
mut s2 := Session{
|
||||
tmux: t // reference back
|
||||
name: name
|
||||
}
|
||||
s2.create()!
|
||||
t.sessions << &s2
|
||||
}
|
||||
mut s := t.session_get(name)!
|
||||
if args.reset {
|
||||
$if debug {
|
||||
console.print_header(' tmux - session ${name} will be restarted.')
|
||||
}
|
||||
s.restart()!
|
||||
}
|
||||
t.scan()!
|
||||
return s
|
||||
}
|
||||
|
||||
pub fn (mut s Session) create() ! {
|
||||
res_opt := "-P -F '#\{window_id\}'"
|
||||
cmd := "tmux new-session ${res_opt} -d -s ${s.name} 'sh'"
|
||||
window_id_ := osal.execute_silent(cmd) or {
|
||||
return error("Can't create tmux session ${s.name} \n${cmd}\n${err}")
|
||||
}
|
||||
|
||||
cmd3 := 'tmux set-option remain-on-exit on'
|
||||
osal.execute_silent(cmd3) or { return error("Can't execute ${cmd3}\n${err}") }
|
||||
|
||||
window_id := window_id_.trim(' \n')
|
||||
cmd2 := "tmux rename-window -t ${window_id} 'notused'"
|
||||
osal.execute_silent(cmd2) or {
|
||||
return error("Can't rename window ${window_id} to notused \n${cmd2}\n${err}")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn (mut s Session) restart() ! {
|
||||
s.stop()!
|
||||
s.create()!
|
||||
}
|
||||
|
||||
pub fn (mut s Session) stop() ! {
|
||||
osal.execute_silent('tmux kill-session -t ${s.name}') or {
|
||||
return error("Can't delete session ${s.name} - This may happen when session is not found: ${err}")
|
||||
}
|
||||
}
|
||||
|
||||
// get all windows as found in a session
|
||||
pub fn (mut s Session) windows_get() []&Window {
|
||||
mut res := []&Window{}
|
||||
// os.log('TMUX - Start listing ....')
|
||||
for _, window in s.windows {
|
||||
res << window
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (mut s Session) windownames_get() []string {
|
||||
mut res := []string{}
|
||||
for _, window in s.windows {
|
||||
res << window.name
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn (mut s Session) str() string {
|
||||
mut out := '## Session: ${s.name}\n\n'
|
||||
for _, w in s.windows {
|
||||
out += '${*w}\n'
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// pub fn (mut s Session) activate()! {
|
||||
// active_session := s.tmux.redis.get('tmux:active_session') or { 'No active session found' }
|
||||
// if active_session != 'No active session found' && s.name != active_session {
|
||||
// s.tmuxexecutor.db.exec('tmux attach-session -t $active_session') or {
|
||||
// return error('Fail to attach to current active session: $active_session \n$err')
|
||||
// }
|
||||
// s.tmuxexecutor.db.exec('tmux switch -t $s.name') or {
|
||||
// return error("Can't switch to session $s.name \n$err")
|
||||
// }
|
||||
// s.tmux.redis.set('tmux:active_session', s.name) or { panic('Failed to set tmux:active_session') }
|
||||
// os.log('SESSION - Session: $s.name activated ')
|
||||
// } else if active_session == 'No active session found' {
|
||||
// s.tmux.redis.set('tmux:active_session', s.name) or { panic('Failed to set tmux:active_session') }
|
||||
// os.log('SESSION - Session: $s.name activated ')
|
||||
// } else {
|
||||
// os.log('SESSION - Session: $s.name already activate ')
|
||||
// }
|
||||
// }
|
||||
118
lib/osal/tmux/tmux_test.v
Normal file
118
lib/osal/tmux/tmux_test.v
Normal file
@@ -0,0 +1,118 @@
|
||||
module tmux
|
||||
|
||||
import freeflowuniverse.herolib.osal
|
||||
// import freeflowuniverse.herolib.installers.tmux
|
||||
import os
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
const testpath = os.dir(@FILE) + '/testdata'
|
||||
|
||||
// make sure tmux isn't running prior to test
|
||||
fn testsuite_begin() {
|
||||
mut tmux := get_remote('185.69.166.152')!
|
||||
if tmux.is_running() {
|
||||
tmux.stop()!
|
||||
}
|
||||
}
|
||||
|
||||
// make sure tmux isn't running after test
|
||||
fn testsuite_end() {
|
||||
mut tmux := get_remote('185.69.166.152')!
|
||||
|
||||
if tmux.is_running() {
|
||||
tmux.stop()!
|
||||
}
|
||||
}
|
||||
|
||||
fn test_start() ! {
|
||||
mut tmux := get_remote('185.69.166.152')!
|
||||
|
||||
// test server is running after start()
|
||||
tmux.start() or { panic('cannot start tmux: ${err}') }
|
||||
mut tmux_ls := osal.execute_silent('tmux ls') or { panic('Cannot execute tmux ls: ${err}') }
|
||||
// test started tmux contains windows
|
||||
assert tmux_ls.contains('init: 1 windows')
|
||||
tmux.stop() or { panic('cannot stop tmux: ${err}') }
|
||||
}
|
||||
|
||||
fn test_stop() ! {
|
||||
mut tmux := get_remote('185.69.166.152')!
|
||||
|
||||
// test server is running after start()
|
||||
tmux.start() or { panic('cannot start tmux: ${err}') }
|
||||
assert tmux.is_running()
|
||||
tmux.stop() or { panic('cannot stop tmux: ${err}') }
|
||||
assert !tmux.is_running()
|
||||
}
|
||||
|
||||
fn test_windows_get() ! {
|
||||
mut tmux := get_remote('185.69.166.152')!
|
||||
|
||||
// test windows_get when only starting window is running
|
||||
tmux.start()!
|
||||
mut windows := tmux.windows_get()
|
||||
assert windows.len == 1
|
||||
|
||||
// test getting newly created window
|
||||
tmux.window_new(WindowArgs{ name: 'testwindow' })!
|
||||
windows = tmux.windows_get()
|
||||
unsafe {
|
||||
assert windows.keys().contains('testwindow')
|
||||
}
|
||||
assert windows['testwindow'].name == 'testwindow'
|
||||
assert windows['testwindow'].active
|
||||
tmux.stop()!
|
||||
}
|
||||
|
||||
// TODO: fix test
|
||||
fn test_scan() ! {
|
||||
console.print_debug('-----Testing scan------')
|
||||
mut tmux := get_remote('185.69.166.152')!
|
||||
tmux.start()!
|
||||
|
||||
// check bash window is initialized
|
||||
mut new_windows := tmux.windows_get()
|
||||
unsafe {
|
||||
assert new_windows.keys() == ['bash']
|
||||
}
|
||||
// test scan, should return no windows
|
||||
mut windows := tmux.windows_get()
|
||||
unsafe {
|
||||
assert windows.keys().len == 0
|
||||
}
|
||||
// test scan with window in tmux but not in tmux struct
|
||||
// mocking a failed command to see if scan identifies
|
||||
tmux.sessions['init'].windows['test'] = &Window{
|
||||
session: tmux.sessions['init']
|
||||
name: 'test'
|
||||
}
|
||||
new_windows = tmux.windows_get()
|
||||
panic('new windows ${new_windows.keys()}')
|
||||
unsafe {
|
||||
assert new_windows.keys().len == 1
|
||||
}
|
||||
new_windows = tmux.scan()!
|
||||
tmux.stop()!
|
||||
}
|
||||
|
||||
// //TODO: fix test
|
||||
// fn test_scan_add() ! {
|
||||
// console.print_debug("-----Testing scan_add------")
|
||||
|
||||
//
|
||||
// mut tmux := Tmux { node: node_ssh }
|
||||
// windows := tmux.scan_add("line")!
|
||||
// }
|
||||
|
||||
// remaining tests are run synchronously to avoid conflicts
|
||||
fn test_tmux_window() {
|
||||
res := os.execute('${os.quoted_path(@VEXE)} test ${testpath}/tmux_window_test.v')
|
||||
// assert res.exit_code == 1
|
||||
// assert res.output.contains('other_test.v does not exist')
|
||||
}
|
||||
|
||||
fn test_tmux_scan() {
|
||||
res := os.execute('${os.quoted_path(@VEXE)} test ${testpath}/tmux_window_test.v')
|
||||
// assert res.exit_code == 1
|
||||
// assert res.output.contains('other_test.v does not exist')
|
||||
}
|
||||
257
lib/osal/tmux/tmux_window.v
Normal file
257
lib/osal/tmux/tmux_window.v
Normal file
@@ -0,0 +1,257 @@
|
||||
module tmux
|
||||
|
||||
import os
|
||||
import freeflowuniverse.herolib.osal
|
||||
import freeflowuniverse.herolib.core.texttools
|
||||
import freeflowuniverse.herolib.data.ourtime
|
||||
import time
|
||||
import freeflowuniverse.herolib.ui.console
|
||||
|
||||
@[heap]
|
||||
struct Window {
|
||||
pub mut:
|
||||
session &Session @[skip]
|
||||
name string
|
||||
id int
|
||||
active bool
|
||||
pid int
|
||||
paneid int
|
||||
cmd string
|
||||
env map[string]string
|
||||
}
|
||||
|
||||
pub struct WindowArgs {
|
||||
pub mut:
|
||||
name string
|
||||
cmd string
|
||||
env map[string]string
|
||||
reset bool
|
||||
}
|
||||
|
||||
// window_name is the name of the window in session main (will always be called session main)
|
||||
// cmd to execute e.g. bash file
|
||||
// environment arguments to use
|
||||
// reset, if reset it will create window even if it does already exist, will destroy it
|
||||
// ```
|
||||
// struct WindowArgs {
|
||||
// pub mut:
|
||||
// name string
|
||||
// cmd string
|
||||
// env map[string]string
|
||||
// reset bool
|
||||
// }
|
||||
// ```
|
||||
pub fn (mut t Tmux) window_new(args WindowArgs) !Window {
|
||||
mut s := t.session_create(name: 'main', reset: false)!
|
||||
mut w := s.window_new(args)!
|
||||
return w
|
||||
}
|
||||
|
||||
// is always in the main tmux
|
||||
pub fn (mut t Tmux) window_delete(args WindowGetArgs) ! {
|
||||
mut s := t.session_create(name: 'main', reset: false)!
|
||||
s.window_delete(name: args.name)!
|
||||
}
|
||||
|
||||
// window_name is the name of the window in session main (will always be called session main)
|
||||
// cmd to execute e.g. bash file
|
||||
// environment arguments to use
|
||||
// reset, if reset it will create window even if it does already exist, will destroy it
|
||||
// ```
|
||||
// struct WindowArgs {
|
||||
// pub mut:
|
||||
// name string
|
||||
// cmd string
|
||||
// env map[string]string
|
||||
// reset bool
|
||||
// }
|
||||
// ```
|
||||
pub fn (mut s Session) window_new(args WindowArgs) !Window {
|
||||
$if debug {
|
||||
console.print_header(' start window: \n${args}')
|
||||
}
|
||||
namel := texttools.name_fix(args.name)
|
||||
if s.window_exist(name: namel) {
|
||||
if args.reset {
|
||||
s.window_delete(name: namel)!
|
||||
} else {
|
||||
return error('cannot create new window it already exists, window ${namel} in session:${s.name}')
|
||||
}
|
||||
}
|
||||
mut w := Window{
|
||||
session: &s
|
||||
name: namel
|
||||
cmd: args.cmd
|
||||
env: args.env
|
||||
}
|
||||
s.windows << &w
|
||||
w.create()!
|
||||
s.window_delete(name: 'notused')!
|
||||
return w
|
||||
}
|
||||
|
||||
pub struct WindowGetArgs {
|
||||
pub mut:
|
||||
name string
|
||||
cmd string
|
||||
id int
|
||||
}
|
||||
|
||||
fn (mut s Session) window_exist(args_ WindowGetArgs) bool {
|
||||
mut args := args_
|
||||
s.window_get(args) or { return false }
|
||||
return true
|
||||
}
|
||||
|
||||
pub fn (mut s Session) window_get(args_ WindowGetArgs) !&Window {
|
||||
mut args := args_
|
||||
args.name = texttools.name_fix(args.name)
|
||||
for w in s.windows {
|
||||
if w.name == args.name {
|
||||
if (args.id > 0 && w.id == args.id) || args.id == 0 {
|
||||
return w
|
||||
}
|
||||
}
|
||||
}
|
||||
return error('Cannot find window ${args.name} in session:${s.name}')
|
||||
}
|
||||
|
||||
pub fn (mut s Session) window_delete(args_ WindowGetArgs) ! {
|
||||
// $if debug { console.print_debug(" - window delete: $args_")}
|
||||
mut args := args_
|
||||
args.name = texttools.name_fix(args.name)
|
||||
if !(s.window_exist(args)) {
|
||||
return
|
||||
}
|
||||
mut i := 0
|
||||
for mut w in s.windows {
|
||||
if w.name == args.name {
|
||||
if (args.id > 0 && w.id == args.id) || args.id == 0 {
|
||||
w.stop()!
|
||||
break
|
||||
}
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
s.windows.delete(i) // i is now the one in the list which needs to be removed
|
||||
}
|
||||
|
||||
pub fn (mut w Window) create() ! {
|
||||
// tmux new-window -P -c /tmp -e good=1 -e bad=0 -n koekoe -t main bash
|
||||
if w.cmd.contains('\n') {
|
||||
// means is multiline need to write it
|
||||
// scriptpath string // is the path where the script will be put which is executed
|
||||
// scriptkeep bool // means we don't remove the script
|
||||
os.mkdir_all('/tmp/tmux/${w.session.name}')!
|
||||
cmd_new := osal.exec_string(
|
||||
cmd: w.cmd
|
||||
scriptpath: '/tmp/tmux/${w.session.name}/${w.name}.sh'
|
||||
scriptkeep: true
|
||||
)!
|
||||
w.cmd = cmd_new
|
||||
}
|
||||
|
||||
// console.print_debug(w)
|
||||
|
||||
if w.active == false {
|
||||
res_opt := "-P -F '#{session_name}|#{window_name}|#{window_id}|#{pane_active}|#{pane_id}|#{pane_pid}|#{pane_start_command}'"
|
||||
cmd := 'tmux new-window ${res_opt} -t ${w.session.name} -n ${w.name} \'/bin/bash -c ${w.cmd}\''
|
||||
console.print_debug(cmd)
|
||||
res := osal.exec(cmd: cmd, stdout: false, name: 'tmux_window_create') or {
|
||||
return error("Can't create new window ${w.name} \n${cmd}\n${err}")
|
||||
}
|
||||
// now look at output to get the window id = wid
|
||||
line_arr := res.output.split('|')
|
||||
wid := line_arr[2] or { panic('cannot split line for window create.\n${line_arr}') }
|
||||
w.id = wid.replace('@', '').int()
|
||||
$if debug {
|
||||
console.print_header(' WINDOW - Window: ${w.name} created in session: ${w.session.name}')
|
||||
}
|
||||
} else {
|
||||
return error('cannot create window, it already exists.\n${w.name}:${w.id}:${w.cmd}')
|
||||
}
|
||||
}
|
||||
|
||||
// do some good checks if the window is still active
|
||||
// not implemented yet
|
||||
pub fn (mut w Window) check() ! {
|
||||
panic('not implemented yet')
|
||||
}
|
||||
|
||||
// restart the window
|
||||
pub fn (mut w Window) restart() ! {
|
||||
w.stop()!
|
||||
w.create()!
|
||||
}
|
||||
|
||||
// stop the window
|
||||
pub fn (mut w Window) stop() ! {
|
||||
osal.exec(
|
||||
cmd: 'tmux kill-window -t @${w.id}'
|
||||
stdout: false
|
||||
name: 'tmux_kill-window'
|
||||
die: false
|
||||
) or { return error("Can't kill window with id:${w.id}") }
|
||||
w.pid = 0
|
||||
w.active = false
|
||||
}
|
||||
|
||||
pub fn (window Window) str() string {
|
||||
return ' - name:${window.name} wid:${window.id} active:${window.active} pid:${window.pid} cmd:${window.cmd}'
|
||||
}
|
||||
|
||||
// will select the current window so with tmux a we can go there .
|
||||
// to login into a session do `tmux a -s mysessionname`
|
||||
fn (mut w Window) activate() ! {
|
||||
cmd2 := 'tmux select-window -t %${w.id}'
|
||||
osal.execute_silent(cmd2) or {
|
||||
return error("Couldn't select window ${w.name} \n${cmd2}\n${err}")
|
||||
}
|
||||
}
|
||||
|
||||
// show the environment
|
||||
pub fn (mut w Window) environment_print() ! {
|
||||
res := osal.execute_silent('tmux show-environment -t %${w.paneid}') or {
|
||||
return error('Couldnt show enviroment cmd: ${w.cmd} \n${err}')
|
||||
}
|
||||
os.log(res)
|
||||
}
|
||||
|
||||
// capture the output
|
||||
pub fn (mut w Window) output_print() ! {
|
||||
o := w.output()!
|
||||
console.print_debug(o)
|
||||
}
|
||||
|
||||
// capture the output
|
||||
pub fn (mut w Window) output() !string {
|
||||
//-S is start, minus means go in history, otherwise its only the active output
|
||||
// tmux capture-pane -t your-session-name:your-window-number -S -1000
|
||||
cmd := 'tmux capture-pane -t ${w.session.name}:@${w.id} -S -1000 && tmux show-buffer'
|
||||
res := osal.execute_silent(cmd) or {
|
||||
return error('Couldnt show enviroment cmd: ${w.cmd} \n${err}')
|
||||
}
|
||||
return texttools.remove_empty_lines(res)
|
||||
}
|
||||
|
||||
pub fn (mut w Window) output_wait(c_ string, timeoutsec int) ! {
|
||||
mut t := ourtime.now()
|
||||
start := t.unix()
|
||||
c := c_.replace('\n', '')
|
||||
for i in 0 .. 2000 {
|
||||
o := w.output()!
|
||||
// console.print_debug(o)
|
||||
$if debug {
|
||||
console.print_debug(" - tmux ${w.name}: wait for: '${c}'")
|
||||
}
|
||||
// need to replace \n because can be wrapped because of size of pane
|
||||
if o.replace('\n', '').contains(c) {
|
||||
return
|
||||
}
|
||||
mut t2 := ourtime.now()
|
||||
if t2.unix() > start + timeoutsec {
|
||||
return error('timeout on output wait for tmux.\n${w} .\nwaiting for:\n${c}')
|
||||
}
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user