1
// The cmd/jujuc/server package allows a process to expose an RPC interface that
2
// allows client processes to delegate execution of cmd.Commands to a server
3
// process (with the exposed commands amenable to specialisation by context id).
9
"launchpad.net/juju/go/cmd"
17
// Request contains the information necessary to run a Command remotely.
25
// Response contains the return code and output generated by a Request.
26
type Response struct {
32
// CmdGetter looks up a Command implementation connected to a particular Context.
33
type CmdGetter func(contextId, cmdName string) (cmd.Command, error)
35
// Jujuc implements the jujuc command in the form required by net/rpc.
40
// badReqErr returns an error indicating a bad Request.
41
func badReqErr(format string, v ...interface{}) error {
42
return fmt.Errorf("bad request: "+format, v...)
45
// Main runs the Command specified by req, and fills in resp.
46
func (j *Jujuc) Main(req Request, resp *Response) error {
47
if req.CommandName == "" {
48
return badReqErr("command not specified")
50
if !filepath.IsAbs(req.Dir) {
51
return badReqErr("Dir is not absolute")
53
c, err := j.getCmd(req.ContextId, req.CommandName)
55
return badReqErr("%s", err)
57
var stdout, stderr bytes.Buffer
58
ctx := &cmd.Context{req.Dir, &stdout, &stderr}
59
resp.Code = cmd.Main(c, ctx, req.Args)
60
resp.Stdout = stdout.Bytes()
61
resp.Stderr = stderr.Bytes()
65
// Server implements a server that serves command invocations via
66
// a unix domain socket.
76
// NewServer creates an RPC server bound to socketPath, which can execute
77
// remote command invocations against an appropriate Context. It will not
78
// actually do so until Run is called.
79
func NewServer(getCmd CmdGetter, socketPath string) (*Server, error) {
80
server := rpc.NewServer()
81
if err := server.Register(&Jujuc{getCmd}); err != nil {
84
listener, err := net.Listen("unix", socketPath)
89
socketPath: socketPath,
92
closed: make(chan bool),
93
closing: make(chan bool),
98
// Run accepts new connections until it encounters an error, or until Close is
99
// called, and then blocks until all existing connections have been closed.
100
func (s *Server) Run() (err error) {
103
conn, err = s.listener.Accept()
108
go func(conn net.Conn) {
109
s.server.ServeConn(conn)
115
// Someone has called Close(), so it is overwhelmingly likely that
116
// the error from Accept is a direct result of the Listener being
117
// closed, and can therefore be safely ignored.
126
// Close immediately stops accepting connections, and blocks until all existing
127
// connections have been closed.
128
func (s *Server) Close() {
131
os.Remove(s.socketPath)