Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F98635796
PhabricatorSSHPassthruCommand.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Wed, Jan 15, 03:10
Size
4 KB
Mime Type
text/x-php
Expires
Fri, Jan 17, 03:10 (2 d)
Engine
blob
Format
Raw Data
Handle
23610187
Attached To
rPH Phabricator
PhabricatorSSHPassthruCommand.php
View Options
<?php
/**
* Proxy an IO channel to an underlying command, with optional callbacks. This
* is a mostly a more general version of @{class:PhutilExecPassthru}. This
* class is used to proxy Git, SVN and Mercurial traffic to the commands which
* can actually serve it.
*
* Largely, this just reads an IO channel (like stdin from SSH) and writes
* the results into a command channel (like a command's stdin). Then it reads
* the command channel (like the command's stdout) and writes it into the IO
* channel (like stdout from SSH):
*
* IO Channel Command Channel
* stdin -> stdin
* stdout <- stdout
* stderr <- stderr
*
* You can provide **read and write callbacks** which are invoked as data
* is passed through this class. They allow you to inspect and modify traffic.
*
* IO Channel Passthru Command Channel
* stdout -> willWrite -> stdin
* stdin <- willRead <- stdout
* stderr <- (identity) <- stderr
*
* Primarily, this means:
*
* - the **IO Channel** can be a @{class:PhutilProtocolChannel} if the
* **write callback** can convert protocol messages into strings; and
* - the **write callback** can inspect and reject requests over the channel,
* e.g. to enforce policies.
*
* In practice, this is used when serving repositories to check each command
* issued over SSH and determine if it is a read command or a write command.
* Writes can then be checked for appropriate permissions.
*/
final
class
PhabricatorSSHPassthruCommand
extends
Phobject
{
private
$commandChannel
;
private
$ioChannel
;
private
$errorChannel
;
private
$execFuture
;
private
$willWriteCallback
;
private
$willReadCallback
;
public
function
setCommandChannelFromExecFuture
(
ExecFuture
$exec_future
)
{
$exec_channel
=
new
PhutilExecChannel
(
$exec_future
);
$exec_channel
->
setStderrHandler
(
array
(
$this
,
'writeErrorIOCallback'
));
$this
->
execFuture
=
$exec_future
;
$this
->
commandChannel
=
$exec_channel
;
return
$this
;
}
public
function
setIOChannel
(
PhutilChannel
$io_channel
)
{
$this
->
ioChannel
=
$io_channel
;
return
$this
;
}
public
function
setErrorChannel
(
PhutilChannel
$error_channel
)
{
$this
->
errorChannel
=
$error_channel
;
return
$this
;
}
public
function
setWillReadCallback
(
$will_read_callback
)
{
$this
->
willReadCallback
=
$will_read_callback
;
return
$this
;
}
public
function
setWillWriteCallback
(
$will_write_callback
)
{
$this
->
willWriteCallback
=
$will_write_callback
;
return
$this
;
}
public
function
writeErrorIOCallback
(
PhutilChannel
$channel
,
$data
)
{
$this
->
errorChannel
->
write
(
$data
);
}
public
function
execute
()
{
$command_channel
=
$this
->
commandChannel
;
$io_channel
=
$this
->
ioChannel
;
$error_channel
=
$this
->
errorChannel
;
if
(!
$command_channel
)
{
throw
new
Exception
(
"Set a command channel before calling execute()!"
);
}
if
(!
$io_channel
)
{
throw
new
Exception
(
"Set an IO channel before calling execute()!"
);
}
if
(!
$error_channel
)
{
throw
new
Exception
(
"Set an error channel before calling execute()!"
);
}
$channels
=
array
(
$command_channel
,
$io_channel
,
$error_channel
);
while
(
true
)
{
PhutilChannel
::
waitForAny
(
$channels
);
$io_channel
->
update
();
$command_channel
->
update
();
$error_channel
->
update
();
$done
=
!
$command_channel
->
isOpen
();
$in_message
=
$io_channel
->
read
();
$in_message
=
$this
->
willWriteData
(
$in_message
);
if
(
$in_message
!==
null
)
{
$command_channel
->
write
(
$in_message
);
}
$out_message
=
$command_channel
->
read
();
$out_message
=
$this
->
willReadData
(
$out_message
);
if
(
$out_message
!==
null
)
{
$io_channel
->
write
(
$out_message
);
}
// If we have nothing left on stdin, close stdin on the subprocess.
if
(!
$io_channel
->
isOpenForReading
())
{
// TODO: This should probably be part of PhutilExecChannel?
$this
->
execFuture
->
write
(
''
);
}
if
(
$done
)
{
break
;
}
}
list
(
$err
)
=
$this
->
execFuture
->
resolve
();
return
$err
;
}
public
function
willWriteData
(
$message
)
{
if
(
$this
->
willWriteCallback
)
{
return
call_user_func
(
$this
->
willWriteCallback
,
$this
,
$message
);
}
else
{
if
(
strlen
(
$message
))
{
return
$message
;
}
else
{
return
null
;
}
}
}
public
function
willReadData
(
$message
)
{
if
(
$this
->
willReadCallback
)
{
return
call_user_func
(
$this
->
willReadCallback
,
$this
,
$message
);
}
else
{
if
(
strlen
(
$message
))
{
return
$message
;
}
else
{
return
null
;
}
}
}
}
Event Timeline
Log In to Comment