server.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. package plugin
  2. import (
  3. "crypto/tls"
  4. "encoding/base64"
  5. "errors"
  6. "fmt"
  7. "io/ioutil"
  8. "log"
  9. "net"
  10. "os"
  11. "os/signal"
  12. "runtime"
  13. "strconv"
  14. "sync/atomic"
  15. "github.com/hashicorp/go-hclog"
  16. "google.golang.org/grpc"
  17. )
  18. // CoreProtocolVersion is the ProtocolVersion of the plugin system itself.
  19. // We will increment this whenever we change any protocol behavior. This
  20. // will invalidate any prior plugins but will at least allow us to iterate
  21. // on the core in a safe way. We will do our best to do this very
  22. // infrequently.
  23. const CoreProtocolVersion = 1
  24. // HandshakeConfig is the configuration used by client and servers to
  25. // handshake before starting a plugin connection. This is embedded by
  26. // both ServeConfig and ClientConfig.
  27. //
  28. // In practice, the plugin host creates a HandshakeConfig that is exported
  29. // and plugins then can easily consume it.
  30. type HandshakeConfig struct {
  31. // ProtocolVersion is the version that clients must match on to
  32. // agree they can communicate. This should match the ProtocolVersion
  33. // set on ClientConfig when using a plugin.
  34. ProtocolVersion uint
  35. // MagicCookieKey and value are used as a very basic verification
  36. // that a plugin is intended to be launched. This is not a security
  37. // measure, just a UX feature. If the magic cookie doesn't match,
  38. // we show human-friendly output.
  39. MagicCookieKey string
  40. MagicCookieValue string
  41. }
  42. // ServeConfig configures what sorts of plugins are served.
  43. type ServeConfig struct {
  44. // HandshakeConfig is the configuration that must match clients.
  45. HandshakeConfig
  46. // TLSProvider is a function that returns a configured tls.Config.
  47. TLSProvider func() (*tls.Config, error)
  48. // Plugins are the plugins that are served.
  49. Plugins map[string]Plugin
  50. // GRPCServer should be non-nil to enable serving the plugins over
  51. // gRPC. This is a function to create the server when needed with the
  52. // given server options. The server options populated by go-plugin will
  53. // be for TLS if set. You may modify the input slice.
  54. //
  55. // Note that the grpc.Server will automatically be registered with
  56. // the gRPC health checking service. This is not optional since go-plugin
  57. // relies on this to implement Ping().
  58. GRPCServer func([]grpc.ServerOption) *grpc.Server
  59. // Logger is used to pass a logger into the server. If none is provided the
  60. // server will create a default logger.
  61. Logger hclog.Logger
  62. }
  63. // Protocol returns the protocol that this server should speak.
  64. func (c *ServeConfig) Protocol() Protocol {
  65. result := ProtocolNetRPC
  66. if c.GRPCServer != nil {
  67. result = ProtocolGRPC
  68. }
  69. return result
  70. }
  71. // Serve serves the plugins given by ServeConfig.
  72. //
  73. // Serve doesn't return until the plugin is done being executed. Any
  74. // errors will be outputted to os.Stderr.
  75. //
  76. // This is the method that plugins should call in their main() functions.
  77. func Serve(opts *ServeConfig) {
  78. // Validate the handshake config
  79. if opts.MagicCookieKey == "" || opts.MagicCookieValue == "" {
  80. fmt.Fprintf(os.Stderr,
  81. "Misconfigured ServeConfig given to serve this plugin: no magic cookie\n"+
  82. "key or value was set. Please notify the plugin author and report\n"+
  83. "this as a bug.\n")
  84. os.Exit(1)
  85. }
  86. // First check the cookie
  87. if os.Getenv(opts.MagicCookieKey) != opts.MagicCookieValue {
  88. fmt.Fprintf(os.Stderr,
  89. "This binary is a plugin. These are not meant to be executed directly.\n"+
  90. "Please execute the program that consumes these plugins, which will\n"+
  91. "load any plugins automatically\n")
  92. os.Exit(1)
  93. }
  94. // Logging goes to the original stderr
  95. log.SetOutput(os.Stderr)
  96. logger := opts.Logger
  97. if logger == nil {
  98. // internal logger to os.Stderr
  99. logger = hclog.New(&hclog.LoggerOptions{
  100. Level: hclog.Trace,
  101. Output: os.Stderr,
  102. JSONFormat: true,
  103. })
  104. }
  105. // Create our new stdout, stderr files. These will override our built-in
  106. // stdout/stderr so that it works across the stream boundary.
  107. stdout_r, stdout_w, err := os.Pipe()
  108. if err != nil {
  109. fmt.Fprintf(os.Stderr, "Error preparing plugin: %s\n", err)
  110. os.Exit(1)
  111. }
  112. stderr_r, stderr_w, err := os.Pipe()
  113. if err != nil {
  114. fmt.Fprintf(os.Stderr, "Error preparing plugin: %s\n", err)
  115. os.Exit(1)
  116. }
  117. // Register a listener so we can accept a connection
  118. listener, err := serverListener()
  119. if err != nil {
  120. logger.Error("plugin init error", "error", err)
  121. return
  122. }
  123. // Close the listener on return. We wrap this in a func() on purpose
  124. // because the "listener" reference may change to TLS.
  125. defer func() {
  126. listener.Close()
  127. }()
  128. var tlsConfig *tls.Config
  129. if opts.TLSProvider != nil {
  130. tlsConfig, err = opts.TLSProvider()
  131. if err != nil {
  132. logger.Error("plugin tls init", "error", err)
  133. return
  134. }
  135. }
  136. // Create the channel to tell us when we're done
  137. doneCh := make(chan struct{})
  138. // Build the server type
  139. var server ServerProtocol
  140. switch opts.Protocol() {
  141. case ProtocolNetRPC:
  142. // If we have a TLS configuration then we wrap the listener
  143. // ourselves and do it at that level.
  144. if tlsConfig != nil {
  145. listener = tls.NewListener(listener, tlsConfig)
  146. }
  147. // Create the RPC server to dispense
  148. server = &RPCServer{
  149. Plugins: opts.Plugins,
  150. Stdout: stdout_r,
  151. Stderr: stderr_r,
  152. DoneCh: doneCh,
  153. }
  154. case ProtocolGRPC:
  155. // Create the gRPC server
  156. server = &GRPCServer{
  157. Plugins: opts.Plugins,
  158. Server: opts.GRPCServer,
  159. TLS: tlsConfig,
  160. Stdout: stdout_r,
  161. Stderr: stderr_r,
  162. DoneCh: doneCh,
  163. }
  164. default:
  165. panic("unknown server protocol: " + opts.Protocol())
  166. }
  167. // Initialize the servers
  168. if err := server.Init(); err != nil {
  169. logger.Error("protocol init", "error", err)
  170. return
  171. }
  172. // Build the extra configuration
  173. extra := ""
  174. if v := server.Config(); v != "" {
  175. extra = base64.StdEncoding.EncodeToString([]byte(v))
  176. }
  177. if extra != "" {
  178. extra = "|" + extra
  179. }
  180. logger.Debug("plugin address", "network", listener.Addr().Network(), "address", listener.Addr().String())
  181. // Output the address and service name to stdout so that core can bring it up.
  182. fmt.Printf("%d|%d|%s|%s|%s%s\n",
  183. CoreProtocolVersion,
  184. opts.ProtocolVersion,
  185. listener.Addr().Network(),
  186. listener.Addr().String(),
  187. opts.Protocol(),
  188. extra)
  189. os.Stdout.Sync()
  190. // Eat the interrupts
  191. ch := make(chan os.Signal, 1)
  192. signal.Notify(ch, os.Interrupt)
  193. go func() {
  194. var count int32 = 0
  195. for {
  196. <-ch
  197. newCount := atomic.AddInt32(&count, 1)
  198. logger.Debug("plugin received interrupt signal, ignoring", "count", newCount)
  199. }
  200. }()
  201. // Set our new out, err
  202. os.Stdout = stdout_w
  203. os.Stderr = stderr_w
  204. // Accept connections and wait for completion
  205. go server.Serve(listener)
  206. <-doneCh
  207. }
  208. func serverListener() (net.Listener, error) {
  209. if runtime.GOOS == "windows" {
  210. return serverListener_tcp()
  211. }
  212. return serverListener_unix()
  213. }
  214. func serverListener_tcp() (net.Listener, error) {
  215. minPort, err := strconv.ParseInt(os.Getenv("PLUGIN_MIN_PORT"), 10, 32)
  216. if err != nil {
  217. return nil, err
  218. }
  219. maxPort, err := strconv.ParseInt(os.Getenv("PLUGIN_MAX_PORT"), 10, 32)
  220. if err != nil {
  221. return nil, err
  222. }
  223. for port := minPort; port <= maxPort; port++ {
  224. address := fmt.Sprintf("127.0.0.1:%d", port)
  225. listener, err := net.Listen("tcp", address)
  226. if err == nil {
  227. return listener, nil
  228. }
  229. }
  230. return nil, errors.New("Couldn't bind plugin TCP listener")
  231. }
  232. func serverListener_unix() (net.Listener, error) {
  233. tf, err := ioutil.TempFile("", "plugin")
  234. if err != nil {
  235. return nil, err
  236. }
  237. path := tf.Name()
  238. // Close the file and remove it because it has to not exist for
  239. // the domain socket.
  240. if err := tf.Close(); err != nil {
  241. return nil, err
  242. }
  243. if err := os.Remove(path); err != nil {
  244. return nil, err
  245. }
  246. l, err := net.Listen("unix", path)
  247. if err != nil {
  248. return nil, err
  249. }
  250. // Wrap the listener in rmListener so that the Unix domain socket file
  251. // is removed on close.
  252. return &rmListener{
  253. Listener: l,
  254. Path: path,
  255. }, nil
  256. }
  257. // rmListener is an implementation of net.Listener that forwards most
  258. // calls to the listener but also removes a file as part of the close. We
  259. // use this to cleanup the unix domain socket on close.
  260. type rmListener struct {
  261. net.Listener
  262. Path string
  263. }
  264. func (l *rmListener) Close() error {
  265. // Close the listener itself
  266. if err := l.Listener.Close(); err != nil {
  267. return err
  268. }
  269. // Remove the file
  270. return os.Remove(l.Path)
  271. }