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
5 years ago
by
Eric
Browse files
Options
Browse Files
Download
Plain Diff
fix commmand parse
parents
2e4496e2
50388696
commadparse
No related merge requests found
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
import
(
"io"
"bytes"
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/utils"
"regexp"
"strings"
"sync"
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/utils"
)
var
ps1Pattern
=
regexp
.
MustCompile
(
`^\[?.*@.*\]?[\\$#]\s|mysql>\s`
)
...
...
@@ -21,91 +20,29 @@ func NewCmdParser(sid, name string) *CmdParser {
type
CmdParser
struct
{
id
string
name
string
buf
bytes
.
Buffer
term
*
utils
.
Terminal
reader
io
.
ReadCloser
writer
io
.
WriteCloser
currentLines
[]
string
lock
*
sync
.
Mutex
maxLength
int
currentLength
int
closed
chan
struct
{}
}
func
(
cp
*
CmdParser
)
WriteData
(
p
[]
byte
)
(
int
,
error
)
{
select
{
case
<-
cp
.
closed
:
return
0
,
io
.
EOF
default
:
}
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
:
cp
.
lock
.
Lock
()
defer
cp
.
lock
.
Unlock
()
if
cp
.
buf
.
Len
()
>=
1024
{
return
0
,
nil
}
return
cp
.
reader
.
Read
(
p
)
return
cp
.
buf
.
Write
(
p
)
}
func
(
cp
*
CmdParser
)
Close
()
error
{
select
{
case
<-
cp
.
closed
:
logger
.
Infof
(
"session ID: %s, parser name: %s"
,
cp
.
id
,
cp
.
name
)
return
nil
default
:
close
(
cp
.
closed
)
}
_
=
cp
.
reader
.
Close
()
return
cp
.
writer
.
Close
()
}
func
(
cp
*
CmdParser
)
initial
()
{
cp
.
reader
,
cp
.
writer
=
io
.
Pipe
()
cp
.
currentLines
=
make
([]
string
,
0
)
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
{
...
...
@@ -114,16 +51,11 @@ func (cp *CmdParser) parsePS1(s string) string {
// Parse 解析命令或输出
func
(
cp
*
CmdParser
)
Parse
()
string
{
select
{
case
<-
cp
.
closed
:
default
:
cp
.
writer
.
Write
([]
byte
(
"
\r
"
))
}
cp
.
lock
.
Lock
()
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
)
cp
.
currentLines
=
make
([]
string
,
0
)
cp
.
currentLength
=
0
cp
.
buf
.
Reset
()
return
output
}
This diff is collapsed.
Click to expand it.
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
import
(
"bytes"
"fmt"
"unicode/utf8"
"github.com/jumpserver/koko/pkg/logger"
)
func
ParseTerminalData
(
p
[]
byte
)
(
lines
[]
string
,
ok
bool
)
{
c
:=
bytes
.
NewReader
(
p
)
pasteActive
:=
false
ok
=
true
var
line
[]
rune
var
pos
int
var
remainder
[]
byte
var
inBuf
[
256
]
byte
for
{
rest
:=
remainder
lineOk
:=
false
for
!
lineOk
{
var
key
rune
key
,
rest
=
bytesToKey
(
rest
,
pasteActive
)
if
key
==
utf8
.
RuneError
{
type
terminalParser
struct
{
// line is the current line being entered.
line
[]
rune
// pos is the logical position of the cursor in line
pos
int
// pasteActive is true iff there is a bracketed paste operation in
// progress.
pasteActive
bool
// maxLine is the greatest value of cursorY so far.
maxLine
int
// 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
}
if
!
pasteActive
{
if
key
==
keyPasteStart
{
pasteActive
=
true
if
len
(
line
)
==
0
{
pos
--
}
continue
for
pos
>
0
{
if
t
.
line
[
pos
]
==
' '
{
pos
++
break
}
}
else
if
key
==
keyPasteEnd
{
pasteActive
=
false
continue
pos
--
}
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
{
case
keyBackspace
:
if
pos
==
0
{
continue
if
t
.
pos
==
0
{
return
}
line
,
pos
=
EraseNPreviousChars
(
1
,
pos
,
line
)
t
.
eraseNPreviousChars
(
1
)
case
keyAltLeft
:
// move left by a word.
pos
-=
CountToLeftWord
(
pos
,
line
)
t
.
pos
-=
t
.
countToLeftWord
(
)
case
keyAltRight
:
// move right by a word.
pos
+=
CountToRightWord
(
pos
,
line
)
t
.
pos
+=
t
.
countToRightWord
(
)
case
keyLeft
:
if
pos
==
0
{
continue
if
t
.
pos
==
0
{
return
}
pos
--
t
.
pos
--
case
keyRight
:
if
pos
==
len
(
line
)
{
continue
if
t
.
pos
==
len
(
t
.
line
)
{
return
}
pos
++
t
.
pos
++
case
keyHome
:
if
pos
==
0
{
continue
if
t
.
pos
==
0
{
return
}
pos
=
0
t
.
pos
=
0
case
keyEnd
:
if
pos
==
len
(
line
)
{
continue
if
t
.
pos
==
len
(
t
.
line
)
{
return
}
pos
=
len
(
line
)
t
.
pos
=
len
(
t
.
line
)
case
keyUp
:
line
=
[]
rune
{}
pos
=
0
ok
=
false
entry
,
ok
:=
t
.
history
.
NthPreviousEntry
(
t
.
historyIndex
+
1
)
if
!
ok
{
return
""
,
false
}
if
t
.
historyIndex
==
-
1
{
t
.
historyPending
=
string
(
t
.
line
)
}
t
.
historyIndex
++
runes
:=
[]
rune
(
entry
)
t
.
setLine
(
runes
,
len
(
runes
))
case
keyDown
:
line
=
[]
rune
{}
pos
=
0
ok
=
false
switch
t
.
historyIndex
{
case
-
1
:
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
:
lines
=
append
(
lines
,
string
(
line
))
line
=
line
[
:
0
]
pos
=
0
lineOk
=
true
line
=
string
(
t
.
line
)
ok
=
true
t
.
line
=
t
.
line
[
:
0
]
t
.
pos
=
0
t
.
maxLine
=
0
case
keyDeleteWord
:
// 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
:
line
=
line
[
:
pos
]
t
.
line
=
t
.
line
[
:
t
.
pos
]
case
keyCtrlD
:
// Erase the character under the current position.
// The EOF case when the line is empty is handled in
// readLine().
if
pos
<
len
(
line
)
{
pos
++
line
,
pos
=
EraseNPreviousChars
(
1
,
pos
,
line
)
if
t
.
pos
<
len
(
t
.
line
)
{
t
.
pos
++
t
.
eraseNPreviousChars
(
1
)
}
case
keyCtrlU
:
line
=
line
[
:
0
]
t
.
eraseNPreviousChars
(
t
.
pos
)
case
keyClearScreen
:
// Erases the screen and moves the cursor to the home position.
t
.
setLine
(
t
.
line
,
t
.
pos
)
default
:
if
!
isPrintable
(
key
)
{
fmt
.
Println
(
"could not printable: "
,
[]
byte
(
string
(
key
)),
" "
,
key
)
ok
=
false
continue
return
}
line
,
pos
=
AddKeyToLine
(
key
,
pos
,
line
)
if
len
(
t
.
line
)
==
maxLineLength
{
return
}
t
.
addKeyToLine
(
key
)
}
if
len
(
rest
)
>
0
{
n
:=
copy
(
inBuf
[
:
],
rest
)
remainder
=
inBuf
[
:
n
]
}
else
{
remainder
=
nil
return
}
// addKeyToLine inserts the given key at the current position in the current
// 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
// containing a partial key sequence
readBuf
:=
inBuf
[
len
(
remainder
)
:
]
func
(
t
*
terminalParser
)
parseLines
(
p
[]
byte
)
(
lines
[]
string
)
{
var
err
error
var
n
int
n
,
err
:=
c
.
Read
(
readBuf
)
if
err
!=
nil
{
if
len
(
line
)
>
0
{
lines
=
append
(
lines
,
string
(
line
))
}
else
if
len
(
rest
)
>
0
{
lines
=
append
(
lines
,
string
(
rest
))
lines
=
make
([]
string
,
0
,
3
)
lineIsPasted
:=
t
.
pasteActive
reader
:=
bytes
.
NewBuffer
(
p
)
for
{
rest
:=
t
.
remainder
line
:=
""
lineOk
:=
false
for
!
lineOk
{
var
key
rune
key
,
rest
=
bytesToKey
(
rest
,
t
.
pasteActive
)
if
key
==
utf8
.
RuneError
{
break
}
return
if
!
t
.
pasteActive
{
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
)]
}
}
func
EraseNPreviousChars
(
n
,
cPos
int
,
line
[]
rune
)
([]
rune
,
int
)
{
if
n
==
0
{
return
line
,
cPos
if
key
==
keyPasteStart
{
t
.
pasteActive
=
true
if
len
(
t
.
line
)
==
0
{
lineIsPasted
=
true
}
if
cPos
<
n
{
n
=
cPos
continue
}
cPos
-=
n
copy
(
line
[
cPos
:
],
line
[
n
+
cPos
:
])
return
line
[
:
len
(
line
)
-
n
],
cPos
}
func
CountToLeftWord
(
currentPos
int
,
line
[]
rune
)
int
{
if
currentPos
==
0
{
return
0
}
else
if
key
==
keyPasteEnd
{
t
.
pasteActive
=
false
continue
}
pos
:=
currentPos
-
1
for
pos
>
0
{
if
line
[
pos
]
!=
' '
{
break
if
!
t
.
pasteActive
{
lineIsPasted
=
false
}
pos
--
line
,
lineOk
=
t
.
handleKey
(
key
)
}
for
pos
>
0
{
if
line
[
pos
]
==
' '
{
pos
++
break
if
len
(
rest
)
>
0
{
n
:=
copy
(
t
.
inBuf
[
:
],
rest
)
t
.
remainder
=
t
.
inBuf
[
:
n
]
}
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
{
pos
:=
currentPos
for
pos
<
len
(
line
)
{
if
line
[
pos
]
==
' '
{
break
n
,
err
=
reader
.
Read
(
readBuf
)
if
err
!=
nil
&&
n
==
0
{
if
len
(
t
.
line
)
>
0
{
lines
=
append
(
lines
,
string
(
t
.
line
))
}
pos
++
if
len
(
t
.
remainder
)
>
0
{
continue
}
for
pos
<
len
(
line
)
{
if
line
[
pos
]
!=
' '
{
break
return
}
else
if
err
==
nil
&&
n
==
0
{
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
)
{
if
len
(
line
)
==
cap
(
line
)
{
newLine
:=
make
([]
rune
,
len
(
line
),
2
*
(
1
+
len
(
line
)))
copy
(
newLine
,
line
)
line
=
newLine
func
ParseTerminalData
(
p
[]
byte
)
(
lines
[]
string
)
{
t
:=
terminalParser
{
historyIndex
:
-
1
,
}
line
=
line
[
:
len
(
line
)
+
1
]
copy
(
line
[
pos
+
1
:
],
line
[
pos
:
])
line
[
pos
]
=
key
pos
++
return
line
,
pos
return
t
.
parseLines
(
p
)
}
This diff is collapsed.
Click to expand it.
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