Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
K
koko
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
koko
Commits
9295a3eb
Commit
9295a3eb
authored
Jan 08, 2020
by
Eric
Browse files
Options
Browse Files
Download
Plain Diff
fix commmand parse
parents
2e4496e2
50388696
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
253 additions
and
210 deletions
+253
-210
parsercmd.go
pkg/proxy/parsercmd.go
+13
-81
parser.go
pkg/utils/parser.go
+240
-129
No files found.
pkg/proxy/parsercmd.go
View file @
9295a3eb
package
proxy
package
proxy
import
(
import
(
"io"
"bytes"
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/utils"
"regexp"
"regexp"
"strings"
"strings"
"sync"
"sync"
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/utils"
)
)
var
ps1Pattern
=
regexp
.
MustCompile
(
`^\[?.*@.*\]?[\\$#]\s|mysql>\s`
)
var
ps1Pattern
=
regexp
.
MustCompile
(
`^\[?.*@.*\]?[\\$#]\s|mysql>\s`
)
...
@@ -21,91 +20,29 @@ func NewCmdParser(sid, name string) *CmdParser {
...
@@ -21,91 +20,29 @@ func NewCmdParser(sid, name string) *CmdParser {
type
CmdParser
struct
{
type
CmdParser
struct
{
id
string
id
string
name
string
name
string
buf
bytes
.
Buffer
term
*
utils
.
Terminal
reader
io
.
ReadCloser
writer
io
.
WriteCloser
currentLines
[]
string
lock
*
sync
.
Mutex
lock
*
sync
.
Mutex
maxLength
int
maxLength
int
currentLength
int
currentLength
int
closed
chan
struct
{}
}
}
func
(
cp
*
CmdParser
)
WriteData
(
p
[]
byte
)
(
int
,
error
)
{
func
(
cp
*
CmdParser
)
WriteData
(
p
[]
byte
)
(
int
,
error
)
{
select
{
cp
.
lock
.
Lock
()
case
<-
cp
.
closed
:
defer
cp
.
lock
.
Unlock
()
return
0
,
io
.
EOF
if
cp
.
buf
.
Len
()
>=
1024
{
default
:
return
0
,
nil
}
return
cp
.
writer
.
Write
(
p
)
}
func
(
cp
*
CmdParser
)
Write
(
p
[]
byte
)
(
int
,
error
)
{
select
{
case
<-
cp
.
closed
:
return
0
,
io
.
EOF
default
:
}
return
len
(
p
),
nil
}
func
(
cp
*
CmdParser
)
Read
(
p
[]
byte
)
(
int
,
error
)
{
select
{
case
<-
cp
.
closed
:
return
0
,
io
.
EOF
default
:
}
}
return
cp
.
reader
.
Read
(
p
)
return
cp
.
buf
.
Write
(
p
)
}
}
func
(
cp
*
CmdParser
)
Close
()
error
{
func
(
cp
*
CmdParser
)
Close
()
error
{
select
{
logger
.
Infof
(
"session ID: %s, parser name: %s"
,
cp
.
id
,
cp
.
name
)
case
<-
cp
.
closed
:
return
nil
return
nil
default
:
close
(
cp
.
closed
)
}
_
=
cp
.
reader
.
Close
()
return
cp
.
writer
.
Close
()
}
}
func
(
cp
*
CmdParser
)
initial
()
{
func
(
cp
*
CmdParser
)
initial
()
{
cp
.
reader
,
cp
.
writer
=
io
.
Pipe
()
cp
.
currentLines
=
make
([]
string
,
0
)
cp
.
lock
=
new
(
sync
.
Mutex
)
cp
.
lock
=
new
(
sync
.
Mutex
)
cp
.
maxLength
=
1024
cp
.
currentLength
=
0
cp
.
closed
=
make
(
chan
struct
{})
cp
.
term
=
utils
.
NewTerminal
(
cp
,
""
)
cp
.
term
.
SetEcho
(
false
)
go
func
()
{
logger
.
Infof
(
"Session %s: %s start"
,
cp
.
id
,
cp
.
name
)
defer
logger
.
Infof
(
"Session %s: %s close"
,
cp
.
id
,
cp
.
name
)
loop
:
for
{
line
,
err
:=
cp
.
term
.
ReadLine
()
if
err
!=
nil
{
select
{
case
<-
cp
.
closed
:
logger
.
Debugf
(
"Session %s %s term err: %s break loop"
,
cp
.
id
,
cp
.
name
,
err
)
break
loop
default
:
}
logger
.
Debugf
(
"Session %s %s term err: %s,loop continue"
,
cp
.
id
,
cp
.
name
,
err
)
goto
loop
}
cp
.
lock
.
Lock
()
cp
.
currentLength
+=
len
(
line
)
if
cp
.
currentLength
<
cp
.
maxLength
{
cp
.
currentLines
=
append
(
cp
.
currentLines
,
line
)
}
cp
.
lock
.
Unlock
()
}
}()
}
}
func
(
cp
*
CmdParser
)
parsePS1
(
s
string
)
string
{
func
(
cp
*
CmdParser
)
parsePS1
(
s
string
)
string
{
...
@@ -114,16 +51,11 @@ func (cp *CmdParser) parsePS1(s string) string {
...
@@ -114,16 +51,11 @@ func (cp *CmdParser) parsePS1(s string) string {
// Parse 解析命令或输出
// Parse 解析命令或输出
func
(
cp
*
CmdParser
)
Parse
()
string
{
func
(
cp
*
CmdParser
)
Parse
()
string
{
select
{
case
<-
cp
.
closed
:
default
:
cp
.
writer
.
Write
([]
byte
(
"
\r
"
))
}
cp
.
lock
.
Lock
()
cp
.
lock
.
Lock
()
defer
cp
.
lock
.
Unlock
()
defer
cp
.
lock
.
Unlock
()
output
:=
strings
.
TrimSpace
(
strings
.
Join
(
cp
.
currentLines
,
"
\r\n
"
))
lines
:=
utils
.
ParseTerminalData
(
cp
.
buf
.
Bytes
())
output
:=
strings
.
TrimSpace
(
strings
.
Join
(
lines
,
"
\r\n
"
))
output
=
cp
.
parsePS1
(
output
)
output
=
cp
.
parsePS1
(
output
)
cp
.
currentLines
=
make
([]
string
,
0
)
cp
.
buf
.
Reset
()
cp
.
currentLength
=
0
return
output
return
output
}
}
pkg/utils/parser.go
View file @
9295a3eb
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package
utils
package
utils
import
(
import
(
"bytes"
"bytes"
"fmt"
"unicode/utf8"
"unicode/utf8"
"github.com/jumpserver/koko/pkg/logger"
)
)
func
ParseTerminalData
(
p
[]
byte
)
(
lines
[]
string
,
ok
bool
)
{
c
:=
bytes
.
NewReader
(
p
)
type
terminalParser
struct
{
pasteActive
:=
false
ok
=
true
// line is the current line being entered.
var
line
[]
rune
line
[]
rune
var
pos
int
// pos is the logical position of the cursor in line
var
remainder
[]
byte
pos
int
var
inBuf
[
256
]
byte
// pasteActive is true iff there is a bracketed paste operation in
for
{
// progress.
rest
:=
remainder
pasteActive
bool
lineOk
:=
false
for
!
lineOk
{
// maxLine is the greatest value of cursorY so far.
var
key
rune
maxLine
int
key
,
rest
=
bytesToKey
(
rest
,
pasteActive
)
if
key
==
utf8
.
RuneError
{
// remainder contains the remainder of any partial key sequences after
// a read. It aliases into inBuf.
remainder
[]
byte
inBuf
[
256
]
byte
// history contains previously entered commands so that they can be
// accessed with the up and down keys.
history
stRingBuffer
// historyIndex stores the currently accessed history entry, where zero
// means the immediately previous entry.
historyIndex
int
// When navigating up and down the history it's possible to return to
// the incomplete, initial line. That value is stored in
// historyPending.
historyPending
string
}
func
(
t
*
terminalParser
)
setLine
(
newLine
[]
rune
,
newPos
int
)
{
t
.
line
=
newLine
t
.
pos
=
newPos
}
func
(
t
*
terminalParser
)
eraseNPreviousChars
(
n
int
)
{
if
n
==
0
{
return
}
if
t
.
pos
<
n
{
n
=
t
.
pos
}
t
.
pos
-=
n
copy
(
t
.
line
[
t
.
pos
:
],
t
.
line
[
n
+
t
.
pos
:
])
t
.
line
=
t
.
line
[
:
len
(
t
.
line
)
-
n
]
}
// countToLeftWord returns then number of characters from the cursor to the
// start of the previous word.
func
(
t
*
terminalParser
)
countToLeftWord
()
int
{
if
t
.
pos
==
0
{
return
0
}
pos
:=
t
.
pos
-
1
for
pos
>
0
{
if
t
.
line
[
pos
]
!=
' '
{
break
break
}
}
if
!
pasteActive
{
pos
--
if
key
==
keyPasteStart
{
pasteActive
=
true
if
len
(
line
)
==
0
{
}
}
continue
for
pos
>
0
{
if
t
.
line
[
pos
]
==
' '
{
pos
++
break
}
}
}
else
if
key
==
keyPasteEnd
{
pos
--
pasteActive
=
false
}
continue
return
t
.
pos
-
pos
}
// countToRightWord returns then number of characters from the cursor to the
// start of the next word.
func
(
t
*
terminalParser
)
countToRightWord
()
int
{
pos
:=
t
.
pos
for
pos
<
len
(
t
.
line
)
{
if
t
.
line
[
pos
]
==
' '
{
break
}
pos
++
}
for
pos
<
len
(
t
.
line
)
{
if
t
.
line
[
pos
]
!=
' '
{
break
}
pos
++
}
return
pos
-
t
.
pos
}
// handleKey processes the given key and, optionally, returns a line of text
// that the user has entered.
func
(
t
*
terminalParser
)
handleKey
(
key
rune
)
(
line
string
,
ok
bool
)
{
if
t
.
pasteActive
&&
key
!=
keyEnter
{
t
.
addKeyToLine
(
key
)
return
}
}
switch
key
{
switch
key
{
case
keyBackspace
:
case
keyBackspace
:
if
pos
==
0
{
if
t
.
pos
==
0
{
continue
return
}
}
line
,
pos
=
EraseNPreviousChars
(
1
,
pos
,
line
)
t
.
eraseNPreviousChars
(
1
)
case
keyAltLeft
:
case
keyAltLeft
:
// move left by a word.
// move left by a word.
pos
-=
CountToLeftWord
(
pos
,
line
)
t
.
pos
-=
t
.
countToLeftWord
(
)
case
keyAltRight
:
case
keyAltRight
:
// move right by a word.
// move right by a word.
pos
+=
CountToRightWord
(
pos
,
line
)
t
.
pos
+=
t
.
countToRightWord
(
)
case
keyLeft
:
case
keyLeft
:
if
pos
==
0
{
if
t
.
pos
==
0
{
continue
return
}
}
pos
--
t
.
pos
--
case
keyRight
:
case
keyRight
:
if
pos
==
len
(
line
)
{
if
t
.
pos
==
len
(
t
.
line
)
{
continue
return
}
}
pos
++
t
.
pos
++
case
keyHome
:
case
keyHome
:
if
pos
==
0
{
if
t
.
pos
==
0
{
continue
return
}
}
pos
=
0
t
.
pos
=
0
case
keyEnd
:
case
keyEnd
:
if
pos
==
len
(
line
)
{
if
t
.
pos
==
len
(
t
.
line
)
{
continue
return
}
}
pos
=
len
(
line
)
t
.
pos
=
len
(
t
.
line
)
case
keyUp
:
case
keyUp
:
line
=
[]
rune
{}
entry
,
ok
:=
t
.
history
.
NthPreviousEntry
(
t
.
historyIndex
+
1
)
pos
=
0
if
!
ok
{
ok
=
false
return
""
,
false
}
if
t
.
historyIndex
==
-
1
{
t
.
historyPending
=
string
(
t
.
line
)
}
t
.
historyIndex
++
runes
:=
[]
rune
(
entry
)
t
.
setLine
(
runes
,
len
(
runes
))
case
keyDown
:
case
keyDown
:
line
=
[]
rune
{}
switch
t
.
historyIndex
{
pos
=
0
case
-
1
:
ok
=
false
return
case
0
:
runes
:=
[]
rune
(
t
.
historyPending
)
t
.
setLine
(
runes
,
len
(
runes
))
t
.
historyIndex
--
default
:
entry
,
ok
:=
t
.
history
.
NthPreviousEntry
(
t
.
historyIndex
-
1
)
if
ok
{
t
.
historyIndex
--
runes
:=
[]
rune
(
entry
)
t
.
setLine
(
runes
,
len
(
runes
))
}
}
case
keyEnter
:
case
keyEnter
:
lines
=
append
(
lines
,
string
(
line
))
line
=
string
(
t
.
line
)
line
=
line
[
:
0
]
ok
=
true
pos
=
0
t
.
line
=
t
.
line
[
:
0
]
lineOk
=
true
t
.
pos
=
0
t
.
maxLine
=
0
case
keyDeleteWord
:
case
keyDeleteWord
:
// Delete zero or more spaces and then one or more characters.
// Delete zero or more spaces and then one or more characters.
line
,
pos
=
EraseNPreviousChars
(
CountToLeftWord
(
pos
,
line
),
pos
,
line
)
t
.
eraseNPreviousChars
(
t
.
countToLeftWord
()
)
case
keyDeleteLine
:
case
keyDeleteLine
:
line
=
line
[
:
pos
]
t
.
line
=
t
.
line
[
:
t
.
pos
]
case
keyCtrlD
:
case
keyCtrlD
:
// Erase the character under the current position.
// Erase the character under the current position.
// The EOF case when the line is empty is handled in
// The EOF case when the line is empty is handled in
// readLine().
// readLine().
if
pos
<
len
(
line
)
{
if
t
.
pos
<
len
(
t
.
line
)
{
pos
++
t
.
pos
++
line
,
pos
=
EraseNPreviousChars
(
1
,
pos
,
line
)
t
.
eraseNPreviousChars
(
1
)
}
}
case
keyCtrlU
:
case
keyCtrlU
:
line
=
line
[
:
0
]
t
.
eraseNPreviousChars
(
t
.
pos
)
case
keyClearScreen
:
case
keyClearScreen
:
// Erases the screen and moves the cursor to the home position.
t
.
setLine
(
t
.
line
,
t
.
pos
)
default
:
default
:
if
!
isPrintable
(
key
)
{
if
!
isPrintable
(
key
)
{
fmt
.
Println
(
"could not printable: "
,
[]
byte
(
string
(
key
)),
" "
,
key
)
return
ok
=
false
continue
}
}
line
,
pos
=
AddKeyToLine
(
key
,
pos
,
line
)
if
len
(
t
.
line
)
==
maxLineLength
{
return
}
}
t
.
addKeyToLine
(
key
)
}
}
if
len
(
rest
)
>
0
{
return
n
:=
copy
(
inBuf
[
:
],
rest
)
}
remainder
=
inBuf
[
:
n
]
}
else
{
// addKeyToLine inserts the given key at the current position in the current
remainder
=
nil
// line.
func
(
t
*
terminalParser
)
addKeyToLine
(
key
rune
)
{
if
len
(
t
.
line
)
==
cap
(
t
.
line
)
{
newLine
:=
make
([]
rune
,
len
(
t
.
line
),
2
*
(
1
+
len
(
t
.
line
)))
copy
(
newLine
,
t
.
line
)
t
.
line
=
newLine
}
}
t
.
line
=
t
.
line
[
:
len
(
t
.
line
)
+
1
]
copy
(
t
.
line
[
t
.
pos
+
1
:
],
t
.
line
[
t
.
pos
:
])
t
.
line
[
t
.
pos
]
=
key
t
.
pos
++
}
// remainder is a slice at the beginning of t.inBuf
func
(
t
*
terminalParser
)
parseLines
(
p
[]
byte
)
(
lines
[]
string
)
{
// containing a partial key sequence
var
err
error
readBuf
:=
inBuf
[
len
(
remainder
)
:
]
var
n
int
lines
=
make
([]
string
,
0
,
3
)
n
,
err
:=
c
.
Read
(
readBuf
)
lineIsPasted
:=
t
.
pasteActive
if
err
!=
nil
{
reader
:=
bytes
.
NewBuffer
(
p
)
if
len
(
line
)
>
0
{
for
{
lines
=
append
(
lines
,
string
(
line
))
rest
:=
t
.
remainder
}
else
if
len
(
rest
)
>
0
{
line
:=
""
lines
=
append
(
lines
,
string
(
rest
))
lineOk
:=
false
for
!
lineOk
{
var
key
rune
key
,
rest
=
bytesToKey
(
rest
,
t
.
pasteActive
)
if
key
==
utf8
.
RuneError
{
break
}
}
if
!
t
.
pasteActive
{
return
if
key
==
keyCtrlD
{
if
len
(
t
.
line
)
==
0
{
// as key has already handled, we need update remainder data,
t
.
remainder
=
rest
return
lines
}
}
remainder
=
inBuf
[
:
n
+
len
(
remainder
)]
}
}
}
if
key
==
keyPasteStart
{
t
.
pasteActive
=
true
func
EraseNPreviousChars
(
n
,
cPos
int
,
line
[]
rune
)
([]
rune
,
int
)
{
if
len
(
t
.
line
)
==
0
{
if
n
==
0
{
lineIsPasted
=
true
return
line
,
cPos
}
}
if
cPos
<
n
{
continue
n
=
cPos
}
}
cPos
-=
n
}
else
if
key
==
keyPasteEnd
{
copy
(
line
[
cPos
:
],
line
[
n
+
cPos
:
])
t
.
pasteActive
=
false
return
line
[
:
len
(
line
)
-
n
],
cPos
continue
}
func
CountToLeftWord
(
currentPos
int
,
line
[]
rune
)
int
{
if
currentPos
==
0
{
return
0
}
}
if
!
t
.
pasteActive
{
pos
:=
currentPos
-
1
lineIsPasted
=
false
for
pos
>
0
{
if
line
[
pos
]
!=
' '
{
break
}
}
pos
--
line
,
lineOk
=
t
.
handleKey
(
key
)
}
}
for
pos
>
0
{
if
len
(
rest
)
>
0
{
if
line
[
pos
]
==
' '
{
n
:=
copy
(
t
.
inBuf
[
:
],
rest
)
pos
++
t
.
remainder
=
t
.
inBuf
[
:
n
]
break
}
else
{
t
.
remainder
=
nil
}
}
pos
--
if
lineOk
{
if
lineIsPasted
{
err
=
ErrPasteIndicator
}
lines
=
append
(
lines
,
line
)
}
}
return
currentPos
-
pos
// t.remainder is a slice at the beginning of t.inBuf
}
// containing a partial key sequence
readBuf
:=
t
.
inBuf
[
len
(
t
.
remainder
)
:
]
var
n
int
func
CountToRightWord
(
currentPos
int
,
line
[]
rune
)
int
{
n
,
err
=
reader
.
Read
(
readBuf
)
pos
:=
currentPos
if
err
!=
nil
&&
n
==
0
{
for
pos
<
len
(
line
)
{
if
len
(
t
.
line
)
>
0
{
if
line
[
pos
]
==
' '
{
lines
=
append
(
lines
,
string
(
t
.
line
))
break
}
}
pos
++
if
len
(
t
.
remainder
)
>
0
{
continue
}
}
for
pos
<
len
(
line
)
{
return
if
line
[
pos
]
!=
' '
{
}
else
if
err
==
nil
&&
n
==
0
{
break
if
len
(
t
.
remainder
)
==
len
(
t
.
inBuf
)
{
logger
.
Errorf
(
"~~ 发生卡顿问题 ~~"
)
t
.
remainder
=
t
.
remainder
[
1
:
]
continue
}
}
pos
++
}
}
return
pos
-
currentPos
t
.
remainder
=
t
.
inBuf
[
:
n
+
len
(
t
.
remainder
)]
}
}
}
func
AddKeyToLine
(
key
rune
,
pos
int
,
line
[]
rune
)
([]
rune
,
int
)
{
func
ParseTerminalData
(
p
[]
byte
)
(
lines
[]
string
)
{
if
len
(
line
)
==
cap
(
line
)
{
t
:=
terminalParser
{
newLine
:=
make
([]
rune
,
len
(
line
),
2
*
(
1
+
len
(
line
)))
historyIndex
:
-
1
,
copy
(
newLine
,
line
)
line
=
newLine
}
}
line
=
line
[
:
len
(
line
)
+
1
]
return
t
.
parseLines
(
p
)
copy
(
line
[
pos
+
1
:
],
line
[
pos
:
])
line
[
pos
]
=
key
pos
++
return
line
,
pos
}
}
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