|
package main |
|
|
|
import ( |
|
"io" |
|
"log" |
|
"math" |
|
"net/rpc" |
|
"os" |
|
"os/signal" |
|
"time" |
|
) |
|
|
|
func main() { |
|
l := log.New(os.Stderr, "", 0) |
|
c := make(chan os.Signal, 1) |
|
signal.Notify(c, os.Interrupt) |
|
go func() { |
|
for sig := range c { |
|
l.Printf("done: %s\n", sig) |
|
os.Exit(1) |
|
} |
|
}() |
|
NewServerServe(hookRPCWrap{pluginRPC{}}) |
|
|
|
} |
|
|
|
// HookRPC is the RPC interface needed both for the server and the client. |
|
type HookRPC interface { |
|
Sqrt(a float64, b *float64) error |
|
} |
|
|
|
// hookRPCWrap is just for filtering out the non-HookRPC functions, |
|
// to keep net/rpc calm (otherwise it may spurt warnings). |
|
type hookRPCWrap struct { |
|
HookRPC |
|
} |
|
|
|
type pluginRPC struct{} |
|
|
|
func (p pluginRPC) Sqrt(a float64, b *float64) error { |
|
*b = math.Sqrt(a) |
|
return nil |
|
} |
|
|
|
// NewServer creates a new server using the process' stdin and stdout. |
|
func NewServerServe(rcvr HookRPC) { |
|
s := rpc.NewServer() |
|
s.RegisterName("Hook", rcvr) |
|
s.ServeConn(struct { |
|
io.Reader |
|
io.Writer |
|
io.Closer |
|
}{os.Stdin, os.Stdout, |
|
multiCloser{[]io.Closer{os.Stdout, os.Stdin, procCloser{}}}, |
|
}) |
|
} |
|
|
|
type multiCloser struct { |
|
closers []io.Closer |
|
} |
|
|
|
// Close all the underlying closers, in sequence. |
|
// Return the first error. |
|
func (mc multiCloser) Close() error { |
|
var err error |
|
for _, c := range mc.closers { |
|
if closeErr := c.Close(); closeErr != nil && err == nil { |
|
err = closeErr |
|
} |
|
} |
|
return err |
|
} |
|
|
|
type procCloser struct { |
|
*os.Process |
|
} |
|
|
|
// Close the underlying process by killing it. |
|
// If there's no such process, then commit suicide. |
|
func (pc procCloser) Close() error { |
|
if pc.Process == nil { |
|
os.Exit(0) |
|
return nil |
|
} |
|
c := make(chan error, 1) |
|
go func() { _, err := pc.Process.Wait(); c <- err }() |
|
if err := pc.Process.Signal(os.Interrupt); err != nil { |
|
return err |
|
} |
|
select { |
|
case err := <-c: |
|
return err |
|
case <-time.After(1 * time.Second): |
|
return pc.Process.Kill() |
|
} |
|
return nil |
|
} |