2017-09-11 13:48:07 +02:00
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/ftrvxmtrx/fd"
|
|
|
|
"github.com/mitchellh/mapstructure"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"io"
|
|
|
|
"net"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"path/filepath"
|
|
|
|
)
|
|
|
|
|
|
|
|
type StdinserverListenerFactory struct {
|
2017-09-11 15:45:10 +02:00
|
|
|
ClientIdentity string `mapstructure:"client_identity"`
|
2017-09-17 18:20:05 +02:00
|
|
|
sockaddr *net.UnixAddr
|
2017-09-11 13:48:07 +02:00
|
|
|
}
|
|
|
|
|
2017-09-17 18:20:05 +02:00
|
|
|
func parseStdinserverListenerFactory(c JobParsingContext, i map[string]interface{}) (f *StdinserverListenerFactory, err error) {
|
2017-09-11 13:48:07 +02:00
|
|
|
|
|
|
|
f = &StdinserverListenerFactory{}
|
|
|
|
|
|
|
|
if err = mapstructure.Decode(i, f); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "mapstructure error")
|
|
|
|
}
|
|
|
|
if !(len(f.ClientIdentity) > 0) {
|
|
|
|
err = errors.Errorf("must specify 'client_identity'")
|
|
|
|
return
|
|
|
|
}
|
2017-09-17 18:20:05 +02:00
|
|
|
|
|
|
|
f.sockaddr, err = stdinserverListenerSocket(c.Global.Serve.Stdinserver.SockDir, f.ClientIdentity)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-09-11 13:48:07 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-09-17 18:20:05 +02:00
|
|
|
func stdinserverListenerSocket(sockdir, clientIdentity string) (addr *net.UnixAddr, err error) {
|
|
|
|
sockpath := path.Join(sockdir, clientIdentity)
|
2017-09-11 13:48:07 +02:00
|
|
|
addr, err = net.ResolveUnixAddr("unix", sockpath)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "cannot resolve unix address")
|
|
|
|
}
|
|
|
|
return addr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *StdinserverListenerFactory) Listen() (al AuthenticatedChannelListener, err error) {
|
|
|
|
|
2017-09-17 18:20:05 +02:00
|
|
|
sockdir := filepath.Dir(f.sockaddr.Name)
|
2017-09-11 13:48:07 +02:00
|
|
|
sdstat, err := os.Stat(sockdir)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "cannot stat(2) sockdir '%s'", sockdir)
|
|
|
|
}
|
|
|
|
if !sdstat.IsDir() {
|
|
|
|
return nil, errors.Errorf("sockdir is not a directory: %s", sockdir)
|
|
|
|
}
|
|
|
|
p := sdstat.Mode().Perm()
|
|
|
|
if p&0007 != 0 {
|
|
|
|
return nil, errors.Errorf("sockdir must not be world-accessible (permissions are %#o)", p)
|
|
|
|
}
|
|
|
|
|
2017-09-17 18:20:05 +02:00
|
|
|
ul, err := net.ListenUnix("unix", f.sockaddr)
|
2017-09-11 13:48:07 +02:00
|
|
|
if err != nil {
|
2017-09-17 18:20:05 +02:00
|
|
|
return nil, errors.Wrapf(err, "cannot listen on unix socket %s", f.sockaddr)
|
2017-09-11 13:48:07 +02:00
|
|
|
}
|
|
|
|
|
2017-09-11 15:45:10 +02:00
|
|
|
l := &StdinserverListener{ul}
|
2017-09-11 13:48:07 +02:00
|
|
|
|
|
|
|
return l, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type StdinserverListener struct {
|
2017-09-11 15:45:10 +02:00
|
|
|
l *net.UnixListener
|
2017-09-11 13:48:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type fdRWC struct {
|
|
|
|
stdin, stdout *os.File
|
|
|
|
control *net.UnixConn
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f fdRWC) Read(p []byte) (n int, err error) {
|
|
|
|
return f.stdin.Read(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f fdRWC) Write(p []byte) (n int, err error) {
|
|
|
|
return f.stdout.Write(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f fdRWC) Close() (err error) {
|
|
|
|
f.stdin.Close()
|
|
|
|
f.stdout.Close()
|
|
|
|
return f.control.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *StdinserverListener) Accept() (ch io.ReadWriteCloser, err error) {
|
|
|
|
c, err := l.l.Accept()
|
|
|
|
if err != nil {
|
|
|
|
err = errors.Wrap(err, "error accepting on unix listener")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the stdin and stdout of the stdinserver command
|
|
|
|
files, err := fd.Get(c.(*net.UnixConn), 2, []string{"stdin", "stdout"})
|
|
|
|
if err != nil {
|
|
|
|
err = errors.Wrap(err, "error receiving fds from stdinserver command")
|
|
|
|
c.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
rwc := fdRWC{files[0], files[1], c.(*net.UnixConn)}
|
|
|
|
|
2017-09-11 15:45:10 +02:00
|
|
|
return rwc, nil
|
2017-09-11 13:48:07 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *StdinserverListener) Close() (err error) {
|
|
|
|
return l.l.Close() // removes socket file automatically
|
|
|
|
}
|