Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
24730ebd
Commit
24730ebd
authored
Nov 07, 2015
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
web terminal记录日志
parent
43c07707
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
205 additions
and
150 deletions
+205
-150
connect.py
connect.py
+137
-102
settings.py
jumpserver/settings.py
+1
-1
run_websocket.py
run_websocket.py
+54
-32
log_online.html
templates/jlog/log_online.html
+13
-15
No files found.
connect.py
View file @
24730ebd
...
...
@@ -201,7 +201,43 @@ def deal_command(str_r, ssh):
else
:
return
''
def
remove_control_char
(
str_r
):
def
newline_code_in
(
strings
):
for
i
in
[
'
\r
'
,
'
\r\n
'
,
'
\n
'
]:
if
i
in
strings
:
#print "new line"
return
True
return
False
class
Tty
(
object
):
"""
A virtual tty class
一个虚拟终端类,实现连接ssh和记录日志,基类
"""
def
__init__
(
self
,
username
,
asset_name
):
self
.
username
=
username
self
.
asset_name
=
asset_name
self
.
ip
=
None
self
.
port
=
22
self
.
channel
=
None
self
.
user
=
None
self
.
asset
=
None
self
.
role
=
None
self
.
ssh
=
None
self
.
connect_info
=
None
self
.
login_type
=
'ssh'
@staticmethod
def
is_output
(
strings
):
newline_char
=
[
'
\n
'
,
'
\r
'
,
'
\r\n
'
]
for
char
in
newline_char
:
if
char
in
strings
:
return
True
return
False
@staticmethod
def
remove_control_char
(
str_r
):
"""
处理日志特殊字符
"""
...
...
@@ -221,26 +257,92 @@ def remove_control_char(str_r):
return
line_filtered
def
get_log_file
(
self
):
"""
Logging user command and output.
记录用户的日志
"""
tty_log_dir
=
os
.
path
.
join
(
log_dir
,
'tty'
)
timestamp_start
=
int
(
time
.
time
())
date_start
=
time
.
strftime
(
'
%
Y
%
m
%
d'
,
time
.
localtime
(
timestamp_start
))
time_start
=
time
.
strftime
(
'
%
H
%
M
%
S'
,
time
.
localtime
(
timestamp_start
))
today_connect_log_dir
=
os
.
path
.
join
(
tty_log_dir
,
date_start
)
log_file_path
=
os
.
path
.
join
(
today_connect_log_dir
,
'
%
s_
%
s_
%
s'
%
(
self
.
username
,
self
.
asset_name
,
time_start
))
def
newline_code_in
(
strings
):
for
i
in
[
'
\r
'
,
'
\r\n
'
,
'
\n
'
]:
if
i
in
strings
:
#print "new line"
return
True
return
False
if
self
.
login_type
==
'ssh'
:
pid
=
os
.
getpid
()
remote_ip
=
os
.
popen
(
"who -m | awk '{ print $5 }'"
)
.
read
()
.
strip
(
'()
\n
'
)
else
:
pid
=
0
remote_ip
=
'Web'
try
:
is_dir
(
today_connect_log_dir
,
mode
=
0777
)
except
OSError
:
raise
ServerError
(
'Create
%
s failed, Please modify
%
s permission.'
%
(
today_connect_log_dir
,
tty_log_dir
))
try
:
log_file_f
=
open
(
log_file_path
+
'.log'
,
'a'
)
log_time_f
=
open
(
log_file_path
+
'.time'
,
'a'
)
except
IOError
:
raise
ServerError
(
'Create logfile failed, Please modify
%
s permission.'
%
today_connect_log_dir
)
log
=
Log
(
user
=
self
.
username
,
host
=
self
.
asset_name
,
remote_ip
=
remote_ip
,
log_path
=
log_file_path
,
start_time
=
datetime
.
datetime
.
now
(),
pid
=
pid
)
log_file_f
.
write
(
'Start at
%
s
\n
'
%
datetime
.
datetime
.
now
())
log
.
save
()
return
log_file_f
,
log_time_f
,
log
def
get_connect_info
(
self
):
"""
获取需要登陆的主机的信息和映射用户的账号密码
"""
# 1. get ip, port
# 2. get 映射用户
# 3. get 映射用户的账号,密码或者key
# self.connect_info = {'user': '', 'asset': '', 'ip': '', 'port': 0, 'role_name': '', 'role_pass': '', 'role_key': ''}
self
.
connect_info
=
{
'user'
:
'a'
,
'asset'
:
'b'
,
'ip'
:
'127.0.0.1'
,
'port'
:
22
,
'role_name'
:
'root'
,
'role_pass'
:
'redhat'
,
'role_key'
:
''
}
return
self
.
connect_info
def
get_connection
(
self
):
"""
获取连接成功后的ssh
"""
connect_info
=
self
.
get_connect_info
()
# 发起ssh连接请求 Make a ssh connection
ssh
=
paramiko
.
SSHClient
()
ssh
.
load_system_host_keys
()
ssh
.
set_missing_host_key_policy
(
paramiko
.
AutoAddPolicy
())
try
:
if
connect_info
.
get
(
'role_pass'
):
ssh
.
connect
(
connect_info
.
get
(
'ip'
),
port
=
connect_info
.
get
(
'port'
),
username
=
connect_info
.
get
(
'role_name'
),
password
=
connect_info
.
get
(
'role_pass'
),
look_for_keys
=
False
)
else
:
ssh
.
connect
(
connect_info
.
get
(
'ip'
),
port
=
connect_info
.
get
(
'port'
),
username
=
connect_info
.
get
(
'role_name'
),
key_filename
=
connect_info
.
get
(
'role_key'
),
look_for_keys
=
False
)
except
paramiko
.
ssh_exception
.
AuthenticationException
,
paramiko
.
ssh_exception
.
SSHException
:
raise
ServerError
(
'认证失败 Authentication Error.'
)
except
socket
.
error
:
raise
ServerError
(
'端口可能不对 Connect SSH Socket Port Error, Please Correct it.'
)
else
:
self
.
ssh
=
ssh
return
ssh
class
Jtty
(
object
):
class
SshTty
(
Tty
):
"""
A virtual tty class
一个虚拟终端类,实现连接ssh和记录日志
"""
def
__init__
(
self
,
username
,
ip
):
self
.
chan
=
None
self
.
username
=
username
self
.
ip
=
ip
# self.user = user
# self.asset = asset
@staticmethod
def
get_win_size
():
...
...
@@ -263,49 +365,16 @@ class Jtty(object):
"""
try
:
win_size
=
self
.
get_win_size
()
self
.
chan
.
resize_pty
(
height
=
win_size
[
0
],
width
=
win_size
[
1
])
self
.
chan
nel
.
resize_pty
(
height
=
win_size
[
0
],
width
=
win_size
[
1
])
except
Exception
:
pass
def
log_record
(
self
):
"""
Logging user command and output.
记录用户的日志
"""
tty_log_dir
=
os
.
path
.
join
(
log_dir
,
'tty'
)
timestamp_start
=
int
(
time
.
time
())
date_start
=
time
.
strftime
(
'
%
Y
%
m
%
d'
,
time
.
localtime
(
timestamp_start
))
time_start
=
time
.
strftime
(
'
%
H
%
M
%
S'
,
time
.
localtime
(
timestamp_start
))
today_connect_log_dir
=
os
.
path
.
join
(
tty_log_dir
,
date_start
)
log_file_path
=
os
.
path
.
join
(
today_connect_log_dir
,
'
%
s_
%
s_
%
s'
%
(
self
.
username
,
self
.
ip
,
time_start
))
pid
=
os
.
getpid
()
pts
=
os
.
popen
(
"ps axu | grep
%
s | grep -v grep | awk '{ print $7 }'"
%
pid
)
.
read
()
.
strip
()
ip_list
=
os
.
popen
(
"who | grep
%
s | awk '{ print $5 }'"
%
pts
)
.
read
()
.
strip
(
'()
\n
'
)
try
:
is_dir
(
today_connect_log_dir
)
except
OSError
:
raise
ServerError
(
'Create
%
s failed, Please modify
%
s permission.'
%
(
today_connect_log_dir
,
tty_log_dir
))
try
:
# log_file_f = open('/opt/jumpserver/logs/tty/20151102/a_b_191034.log', 'a')
log_file_f
=
open
(
log_file_path
+
'.log'
,
'a'
)
log_time_f
=
open
(
log_file_path
+
'.time'
,
'a'
)
except
IOError
:
raise
ServerError
(
'Create logfile failed, Please modify
%
s permission.'
%
today_connect_log_dir
)
log
=
Log
(
user
=
self
.
username
,
host
=
self
.
ip
,
remote_ip
=
ip_list
,
log_path
=
log_file_path
,
start_time
=
datetime
.
datetime
.
now
(),
pid
=
pid
)
log_file_f
.
write
(
'Start time is
%
s
\n
'
%
datetime
.
datetime
.
now
())
log
.
save
()
return
log_file_f
,
log_time_f
,
ip_list
,
log
def
posix_shell
(
self
,
ssh
):
def
posix_shell
(
self
):
"""
Use paramiko channel connect server interactive.
使用paramiko模块的channel,连接后端,进入交互式
"""
log_file_f
,
log_time_f
,
ip_list
,
log
=
self
.
log_record
()
log_file_f
,
log_time_f
,
log
=
self
.
get_log_file
()
old_tty
=
termios
.
tcgetattr
(
sys
.
stdin
)
pre_timestamp
=
time
.
time
()
input_r
=
''
...
...
@@ -314,29 +383,29 @@ class Jtty(object):
try
:
tty
.
setraw
(
sys
.
stdin
.
fileno
())
tty
.
setcbreak
(
sys
.
stdin
.
fileno
())
self
.
chan
.
settimeout
(
0.0
)
self
.
chan
nel
.
settimeout
(
0.0
)
while
True
:
try
:
r
,
w
,
e
=
select
.
select
([
self
.
chan
,
sys
.
stdin
],
[],
[])
r
,
w
,
e
=
select
.
select
([
self
.
chan
nel
,
sys
.
stdin
],
[],
[])
except
Exception
:
pass
if
self
.
chan
in
r
:
if
self
.
chan
nel
in
r
:
try
:
x
=
self
.
chan
.
recv
(
1024
)
x
=
self
.
chan
nel
.
recv
(
1024
)
if
len
(
x
)
==
0
:
break
sys
.
stdout
.
write
(
x
)
sys
.
stdout
.
flush
()
log_file_f
.
write
(
x
)
now_timestamp
=
time
.
time
()
log_time_f
.
write
(
'
%
s
%
s
\n
'
%
(
round
(
now_timestamp
-
pre_timestamp
,
4
),
len
(
x
)))
log_file_f
.
write
(
x
)
pre_timestamp
=
now_timestamp
log_file_f
.
flush
()
log_time_f
.
flush
()
if
input_mode
and
not
newline_code_in
(
x
):
if
input_mode
and
not
self
.
is_output
(
x
):
input_r
+=
x
except
socket
.
timeout
:
...
...
@@ -348,14 +417,16 @@ class Jtty(object):
input_mode
=
True
if
str
(
x
)
in
[
'
\r
'
,
'
\n
'
,
'
\r\n
'
]:
input_r
=
deal_command
(
input_r
,
ssh
)
# input_r = deal_command(input_r,ssh)
input_r
=
self
.
remove_control_char
(
input_r
)
TtyLog
(
log
=
log
,
datetime
=
datetime
.
datetime
.
now
(),
cmd
=
input_r
)
.
save
()
input_r
=
''
input_mode
=
False
if
len
(
x
)
==
0
:
break
self
.
chan
.
send
(
x
)
self
.
chan
nel
.
send
(
x
)
finally
:
termios
.
tcsetattr
(
sys
.
stdin
,
termios
.
TCSADRAIN
,
old_tty
)
...
...
@@ -365,42 +436,6 @@ class Jtty(object):
log
.
end_time
=
datetime
.
datetime
.
now
()
log
.
save
()
def
get_connect_item
(
self
):
"""
get args for connect: ip, port, username, passwd
获取连接需要的参数,也就是服务ip, 端口, 用户账号和密码
"""
# if not self.asset.is_active:
# raise ServerError('该主机被禁用 Host %s is not active.' % self.ip)
#
# if not self.user.is_active:
# raise ServerError('该用户被禁用 User %s is not active.' % self.username)
# password = CRYPTOR.decrypt(self.])
# return self.username, password, self.ip, int(self.asset.port)
return
'root'
,
'redhat'
,
'127.0.0.1'
,
22
def
get_connection
(
self
):
"""
Get the ssh connection for reuse
获取连接套接字
"""
username
,
password
,
ip
,
port
=
self
.
get_connect_item
()
logger
.
debug
(
"username:
%
s, password:
%
s, ip:
%
s, port:
%
s"
%
(
username
,
password
,
ip
,
port
))
# 发起ssh连接请求 Make a ssh connection
ssh
=
paramiko
.
SSHClient
()
ssh
.
load_system_host_keys
()
ssh
.
set_missing_host_key_policy
(
paramiko
.
AutoAddPolicy
())
try
:
ssh
.
connect
(
ip
,
port
=
port
,
username
=
username
,
password
=
password
)
except
paramiko
.
ssh_exception
.
AuthenticationException
,
paramiko
.
ssh_exception
.
SSHException
:
raise
ServerError
(
'认证错误 Authentication Error.'
)
except
socket
.
error
:
raise
ServerError
(
'端口可能不对 Connect SSH Socket Port Error, Please Correct it.'
)
else
:
return
ssh
def
connect
(
self
):
"""
Connect server.
...
...
@@ -415,7 +450,7 @@ class Jtty(object):
# 获取连接的隧道并设置窗口大小 Make a channel and set windows size
global
channel
win_size
=
self
.
get_win_size
()
self
.
chan
=
channel
=
ssh
.
invoke_shell
(
height
=
win_size
[
0
],
width
=
win_size
[
1
]
)
self
.
chan
nel
=
channel
=
ssh
.
invoke_shell
(
height
=
win_size
[
0
],
width
=
win_size
[
1
],
term
=
'xterm'
)
try
:
signal
.
signal
(
signal
.
SIGWINCH
,
self
.
set_win_size
)
except
:
...
...
@@ -424,17 +459,17 @@ class Jtty(object):
# 设置PS1并提示 Set PS1 and msg it
#channel.send(ps1)
#channel.send(login_msg)
channel
.
send
(
'echo ${SSH_TTY}
\n
'
)
global
SSH_TTY
while
not
channel
.
recv_ready
():
time
.
sleep
(
1
)
tmp
=
channel
.
recv
(
1024
)
#
channel.send('echo ${SSH_TTY}\n')
#
global SSH_TTY
#
while not channel.recv_ready():
#
time.sleep(1)
#
tmp = channel.recv(1024)
#print 'ok'+tmp+'ok'
# SSH_TTY = re.search(r'(?<=/dev/).*', tmp).group().strip()
SSH_TTY
=
''
#
SSH_TTY = ''
channel
.
send
(
'clear
\n
'
)
# Make ssh interactive tunnel
self
.
posix_shell
(
ssh
)
self
.
posix_shell
()
# Shutdown channel socket
channel
.
close
()
...
...
jumpserver/settings.py
View file @
24730ebd
...
...
@@ -15,7 +15,7 @@ import getpass
config
=
ConfigParser
.
ConfigParser
()
BASE_DIR
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
))
BASE_DIR
=
os
.
path
.
abspath
(
os
.
path
.
dirname
(
os
.
path
.
dirname
(
__file__
)
))
config
.
read
(
os
.
path
.
join
(
BASE_DIR
,
'jumpserver.conf'
))
DB_HOST
=
config
.
get
(
'db'
,
'host'
)
...
...
run_websocket.py
View file @
24730ebd
# coding: utf-8
import
time
import
datetime
import
json
import
os
import
sys
...
...
@@ -25,6 +26,8 @@ from pyinotify import WatchManager, Notifier, ProcessEvent, IN_DELETE, IN_CREATE
import
struct
,
fcntl
,
signal
,
socket
,
select
,
fnmatch
import
paramiko
from
connect
import
Tty
from
connect
import
TtyLog
try
:
import
simplejson
as
json
...
...
@@ -124,12 +127,6 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
MonitorHandler
.
threads
.
append
(
thread
)
self
.
stream
.
set_nodelay
(
True
)
print
len
(
MonitorHandler
.
threads
),
len
(
MonitorHandler
.
clients
)
def
on_message
(
self
,
message
):
self
.
write_message
(
'Connect WebSocket Success. <br/>'
)
# 监控日志,发生变动发向客户端
try
:
for
t
in
MonitorHandler
.
threads
:
if
t
.
is_alive
():
...
...
@@ -143,6 +140,12 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
MonitorHandler
.
clients
.
remove
(
self
)
MonitorHandler
.
threads
.
remove
(
MonitorHandler
.
threads
[
client_index
])
print
len
(
MonitorHandler
.
threads
),
len
(
MonitorHandler
.
clients
)
def
on_message
(
self
,
message
):
# 监控日志,发生变动发向客户端
pass
def
on_close
(
self
):
# 客户端主动关闭
# self.close()
...
...
@@ -153,28 +156,34 @@ class MonitorHandler(tornado.websocket.WebSocketHandler):
MonitorHandler
.
threads
.
remove
(
MonitorHandler
.
threads
[
client_index
])
class
WebTty
(
Tty
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
WebTty
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
login_type
=
'web'
self
.
ws
=
None
self
.
input_r
=
''
self
.
input_mode
=
False
class
WebTerminalHandler
(
tornado
.
websocket
.
WebSocketHandler
):
tasks
=
[]
def
__init__
(
self
,
*
args
,
**
kwargs
):
self
.
chan
=
None
self
.
ssh
=
None
self
.
term
=
None
self
.
channel
=
None
self
.
log_file_f
=
None
self
.
log_time_f
=
None
self
.
log
=
None
super
(
WebTerminalHandler
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
def
check_origin
(
self
,
origin
):
return
True
def
open
(
self
):
self
.
ssh
=
paramiko
.
SSHClient
()
self
.
ssh
.
set_missing_host_key_policy
(
paramiko
.
AutoAddPolicy
())
try
:
self
.
ssh
.
connect
(
'127.0.0.1'
,
22
,
'root'
,
'redhat'
)
except
:
self
.
write_message
(
json
.
loads
({
'data'
:
'Connect server Error'
}))
self
.
close
()
self
.
chan
=
self
.
ssh
.
invoke_shell
(
term
=
'xterm'
)
WebTerminalHandler
.
tasks
.
append
(
threading
.
Thread
(
target
=
self
.
_forward_outbound
))
self
.
term
=
WebTty
(
'a'
,
'b'
)
self
.
term
.
get_connection
()
self
.
channel
=
self
.
term
.
ssh
.
invoke_shell
(
term
=
'xterm'
)
WebTerminalHandler
.
tasks
.
append
(
MyThread
(
target
=
self
.
forward_outbound
))
for
t
in
WebTerminalHandler
.
tasks
:
if
t
.
is_alive
():
...
...
@@ -186,37 +195,50 @@ class WebTerminalHandler(tornado.websocket.WebSocketHandler):
data
=
json
.
loads
(
message
)
if
not
data
:
return
if
'resize'
in
data
:
self
.
chan
.
resize_pty
(
data
[
'resize'
]
.
get
(
'width'
,
80
),
data
[
'resize'
]
.
get
(
'height'
,
24
))
if
'data'
in
data
:
self
.
chan
.
send
(
data
[
'data'
])
if
data
.
get
(
'data'
):
self
.
term
.
input_mode
=
True
if
str
(
data
[
'data'
])
in
[
'
\r
'
,
'
\n
'
,
'
\r\n
'
]:
TtyLog
(
log
=
self
.
log
,
datetime
=
datetime
.
datetime
.
now
(),
cmd
=
self
.
term
.
remove_control_char
(
self
.
term
.
input_r
))
.
save
()
self
.
term
.
input_r
=
''
self
.
term
.
input_mode
=
False
self
.
channel
.
send
(
data
[
'data'
])
def
on_close
(
self
):
self
.
write_message
(
json
.
dumps
({
'data'
:
'close websocket'
}))
print
'On_close'
self
.
log_file_f
.
write
(
'End time is
%
s'
%
datetime
.
datetime
.
now
())
self
.
log
.
is_finished
=
True
self
.
log
.
end_time
=
datetime
.
datetime
.
now
()
self
.
log
.
save
()
self
.
close
()
def
_
forward_outbound
(
self
):
""" Forward outbound traffic (ssh -> websockets) """
def
forward_outbound
(
self
):
self
.
log_file_f
,
self
.
log_time_f
,
self
.
log
=
self
.
term
.
get_log_file
()
try
:
data
=
''
pre_timestamp
=
time
.
time
()
while
True
:
r
,
w
,
e
=
select
.
select
([
self
.
chan
,
sys
.
stdin
],
[],
[])
if
self
.
chan
in
r
:
recv
=
self
.
chan
.
recv
(
1024
)
print
recv
r
,
w
,
e
=
select
.
select
([
self
.
channel
,
sys
.
stdin
],
[],
[])
if
self
.
channel
in
r
:
recv
=
self
.
channel
.
recv
(
1024
)
if
not
len
(
recv
):
return
data
+=
recv
try
:
self
.
write_message
(
json
.
dumps
({
'data'
:
data
}))
now_timestamp
=
time
.
time
()
self
.
log_time_f
.
write
(
'
%
s
%
s
\n
'
%
(
round
(
now_timestamp
-
pre_timestamp
,
4
),
len
(
data
)))
self
.
log_file_f
.
write
(
data
)
pre_timestamp
=
now_timestamp
self
.
log_file_f
.
flush
()
self
.
log_time_f
.
flush
()
if
self
.
term
.
input_mode
and
not
self
.
term
.
is_output
(
data
):
self
.
term
.
input_r
+=
data
data
=
''
except
UnicodeDecodeError
:
pass
finally
:
self
.
close
()
if
__name__
==
'__main__'
:
tornado
.
options
.
parse_command_line
()
app
=
Application
()
...
...
templates/jlog/log_online.html
View file @
24730ebd
...
...
@@ -127,8 +127,20 @@
var
file_path
=
obj
.
attr
(
'file_path'
);
var
wsUri
=
'{{ web_monitor_uri }}'
;
var
socket
=
new
WebSocket
(
wsUri
+
'?file_path='
+
file_path
);
var
term
=
new
Terminal
({
cols
:
80
,
rows
:
24
,
screenKeys
:
false
});
var
tag
=
$
(
'<div id="term" style="height:500px; overflow: auto;background-color: rgba(0, 0, 0, 0);border: none"></div>'
);
term
.
open
();
term
.
resize
(
80
,
24
);
socket
.
onopen
=
function
(
evt
){
socket
.
send
(
file_path
)
socket
.
send
(
'hello'
);
term
.
write
(
'~.~ Connect WebSocket Success.~.~
\
r
\
n'
);
};
window
.
onbeforeunload
=
function
(){
...
...
@@ -138,29 +150,15 @@
var
username
=
obj
.
closest
(
'tr'
).
find
(
'#username'
).
text
();
var
ip
=
obj
.
closest
(
'tr'
).
find
(
'#ip'
).
text
();
BootstrapDialog
.
show
({
message
:
function
(){
//服务器端认证
{
#
socket
.
send
(
'login'
,
{
userid
:
message
.
id
,
filename
:
message
.
filename
,
username
:
username
,
seed
:
seed
});
#
}
var
term
=
new
Terminal
({
cols
:
80
,
rows
:
24
,
screenKeys
:
false
});
var
tag
=
$
(
'<div id="term" style="height:500px; overflow: auto;background-color: rgba(0, 0, 0, 0);border: none"></div>'
);
term
.
open
();
term
.
resize
(
80
,
24
);
window
.
setTimeout
(
function
(){
$
(
'.terminal'
).
detach
().
appendTo
(
'#term'
);
socket
.
onmessage
=
function
(
evt
){
term
.
write
(
evt
.
data
);
}},
1000
);
tag
[
0
].
style
.
color
=
"#00FF00"
;
return
tag
[
0
];
}
,
title
:
'Jumpserver实时监控 '
+
' 登录用户名: '
+
'<span class="text-info">'
+
username
+
'</span>'
+
' 登录主机: '
+
'<span class="text-info">'
+
ip
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment