client.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. package plugin
  2. import (
  3. "bufio"
  4. "crypto/subtle"
  5. "crypto/tls"
  6. "errors"
  7. "fmt"
  8. "hash"
  9. "io"
  10. "io/ioutil"
  11. "log"
  12. "net"
  13. "os"
  14. "os/exec"
  15. "path/filepath"
  16. "strconv"
  17. "strings"
  18. "sync"
  19. "sync/atomic"
  20. "time"
  21. "unicode"
  22. hclog "github.com/hashicorp/go-hclog"
  23. )
  24. // If this is 1, then we've called CleanupClients. This can be used
  25. // by plugin RPC implementations to change error behavior since you
  26. // can expected network connection errors at this point. This should be
  27. // read by using sync/atomic.
  28. var Killed uint32 = 0
  29. // This is a slice of the "managed" clients which are cleaned up when
  30. // calling Cleanup
  31. var managedClients = make([]*Client, 0, 5)
  32. var managedClientsLock sync.Mutex
  33. // Error types
  34. var (
  35. // ErrProcessNotFound is returned when a client is instantiated to
  36. // reattach to an existing process and it isn't found.
  37. ErrProcessNotFound = errors.New("Reattachment process not found")
  38. // ErrChecksumsDoNotMatch is returned when binary's checksum doesn't match
  39. // the one provided in the SecureConfig.
  40. ErrChecksumsDoNotMatch = errors.New("checksums did not match")
  41. // ErrSecureNoChecksum is returned when an empty checksum is provided to the
  42. // SecureConfig.
  43. ErrSecureConfigNoChecksum = errors.New("no checksum provided")
  44. // ErrSecureNoHash is returned when a nil Hash object is provided to the
  45. // SecureConfig.
  46. ErrSecureConfigNoHash = errors.New("no hash implementation provided")
  47. // ErrSecureConfigAndReattach is returned when both Reattach and
  48. // SecureConfig are set.
  49. ErrSecureConfigAndReattach = errors.New("only one of Reattach or SecureConfig can be set")
  50. )
  51. // Client handles the lifecycle of a plugin application. It launches
  52. // plugins, connects to them, dispenses interface implementations, and handles
  53. // killing the process.
  54. //
  55. // Plugin hosts should use one Client for each plugin executable. To
  56. // dispense a plugin type, use the `Client.Client` function, and then
  57. // cal `Dispense`. This awkward API is mostly historical but is used to split
  58. // the client that deals with subprocess management and the client that
  59. // does RPC management.
  60. //
  61. // See NewClient and ClientConfig for using a Client.
  62. type Client struct {
  63. config *ClientConfig
  64. exited bool
  65. doneLogging chan struct{}
  66. l sync.Mutex
  67. address net.Addr
  68. process *os.Process
  69. client ClientProtocol
  70. protocol Protocol
  71. logger hclog.Logger
  72. }
  73. // ClientConfig is the configuration used to initialize a new
  74. // plugin client. After being used to initialize a plugin client,
  75. // that configuration must not be modified again.
  76. type ClientConfig struct {
  77. // HandshakeConfig is the configuration that must match servers.
  78. HandshakeConfig
  79. // Plugins are the plugins that can be consumed.
  80. Plugins map[string]Plugin
  81. // One of the following must be set, but not both.
  82. //
  83. // Cmd is the unstarted subprocess for starting the plugin. If this is
  84. // set, then the Client starts the plugin process on its own and connects
  85. // to it.
  86. //
  87. // Reattach is configuration for reattaching to an existing plugin process
  88. // that is already running. This isn't common.
  89. Cmd *exec.Cmd
  90. Reattach *ReattachConfig
  91. // SecureConfig is configuration for verifying the integrity of the
  92. // executable. It can not be used with Reattach.
  93. SecureConfig *SecureConfig
  94. // TLSConfig is used to enable TLS on the RPC client.
  95. TLSConfig *tls.Config
  96. // Managed represents if the client should be managed by the
  97. // plugin package or not. If true, then by calling CleanupClients,
  98. // it will automatically be cleaned up. Otherwise, the client
  99. // user is fully responsible for making sure to Kill all plugin
  100. // clients. By default the client is _not_ managed.
  101. Managed bool
  102. // The minimum and maximum port to use for communicating with
  103. // the subprocess. If not set, this defaults to 10,000 and 25,000
  104. // respectively.
  105. MinPort, MaxPort uint
  106. // StartTimeout is the timeout to wait for the plugin to say it
  107. // has started successfully.
  108. StartTimeout time.Duration
  109. // If non-nil, then the stderr of the client will be written to here
  110. // (as well as the log). This is the original os.Stderr of the subprocess.
  111. // This isn't the output of synced stderr.
  112. Stderr io.Writer
  113. // SyncStdout, SyncStderr can be set to override the
  114. // respective os.Std* values in the plugin. Care should be taken to
  115. // avoid races here. If these are nil, then this will automatically be
  116. // hooked up to os.Stdin, Stdout, and Stderr, respectively.
  117. //
  118. // If the default values (nil) are used, then this package will not
  119. // sync any of these streams.
  120. SyncStdout io.Writer
  121. SyncStderr io.Writer
  122. // AllowedProtocols is a list of allowed protocols. If this isn't set,
  123. // then only netrpc is allowed. This is so that older go-plugin systems
  124. // can show friendly errors if they see a plugin with an unknown
  125. // protocol.
  126. //
  127. // By setting this, you can cause an error immediately on plugin start
  128. // if an unsupported protocol is used with a good error message.
  129. //
  130. // If this isn't set at all (nil value), then only net/rpc is accepted.
  131. // This is done for legacy reasons. You must explicitly opt-in to
  132. // new protocols.
  133. AllowedProtocols []Protocol
  134. // Logger is the logger that the client will used. If none is provided,
  135. // it will default to hclog's default logger.
  136. Logger hclog.Logger
  137. }
  138. // ReattachConfig is used to configure a client to reattach to an
  139. // already-running plugin process. You can retrieve this information by
  140. // calling ReattachConfig on Client.
  141. type ReattachConfig struct {
  142. Protocol Protocol
  143. Addr net.Addr
  144. Pid int
  145. }
  146. // SecureConfig is used to configure a client to verify the integrity of an
  147. // executable before running. It does this by verifying the checksum is
  148. // expected. Hash is used to specify the hashing method to use when checksumming
  149. // the file. The configuration is verified by the client by calling the
  150. // SecureConfig.Check() function.
  151. //
  152. // The host process should ensure the checksum was provided by a trusted and
  153. // authoritative source. The binary should be installed in such a way that it
  154. // can not be modified by an unauthorized user between the time of this check
  155. // and the time of execution.
  156. type SecureConfig struct {
  157. Checksum []byte
  158. Hash hash.Hash
  159. }
  160. // Check takes the filepath to an executable and returns true if the checksum of
  161. // the file matches the checksum provided in the SecureConfig.
  162. func (s *SecureConfig) Check(filePath string) (bool, error) {
  163. if len(s.Checksum) == 0 {
  164. return false, ErrSecureConfigNoChecksum
  165. }
  166. if s.Hash == nil {
  167. return false, ErrSecureConfigNoHash
  168. }
  169. file, err := os.Open(filePath)
  170. if err != nil {
  171. return false, err
  172. }
  173. defer file.Close()
  174. _, err = io.Copy(s.Hash, file)
  175. if err != nil {
  176. return false, err
  177. }
  178. sum := s.Hash.Sum(nil)
  179. return subtle.ConstantTimeCompare(sum, s.Checksum) == 1, nil
  180. }
  181. // This makes sure all the managed subprocesses are killed and properly
  182. // logged. This should be called before the parent process running the
  183. // plugins exits.
  184. //
  185. // This must only be called _once_.
  186. func CleanupClients() {
  187. // Set the killed to true so that we don't get unexpected panics
  188. atomic.StoreUint32(&Killed, 1)
  189. // Kill all the managed clients in parallel and use a WaitGroup
  190. // to wait for them all to finish up.
  191. var wg sync.WaitGroup
  192. managedClientsLock.Lock()
  193. for _, client := range managedClients {
  194. wg.Add(1)
  195. go func(client *Client) {
  196. client.Kill()
  197. wg.Done()
  198. }(client)
  199. }
  200. managedClientsLock.Unlock()
  201. log.Println("[DEBUG] plugin: waiting for all plugin processes to complete...")
  202. wg.Wait()
  203. }
  204. // Creates a new plugin client which manages the lifecycle of an external
  205. // plugin and gets the address for the RPC connection.
  206. //
  207. // The client must be cleaned up at some point by calling Kill(). If
  208. // the client is a managed client (created with NewManagedClient) you
  209. // can just call CleanupClients at the end of your program and they will
  210. // be properly cleaned.
  211. func NewClient(config *ClientConfig) (c *Client) {
  212. if config.MinPort == 0 && config.MaxPort == 0 {
  213. config.MinPort = 10000
  214. config.MaxPort = 25000
  215. }
  216. if config.StartTimeout == 0 {
  217. config.StartTimeout = 1 * time.Minute
  218. }
  219. if config.Stderr == nil {
  220. config.Stderr = ioutil.Discard
  221. }
  222. if config.SyncStdout == nil {
  223. config.SyncStdout = ioutil.Discard
  224. }
  225. if config.SyncStderr == nil {
  226. config.SyncStderr = ioutil.Discard
  227. }
  228. if config.AllowedProtocols == nil {
  229. config.AllowedProtocols = []Protocol{ProtocolNetRPC}
  230. }
  231. if config.Logger == nil {
  232. config.Logger = hclog.New(&hclog.LoggerOptions{
  233. Output: hclog.DefaultOutput,
  234. Level: hclog.Trace,
  235. Name: "plugin",
  236. })
  237. }
  238. c = &Client{
  239. config: config,
  240. logger: config.Logger,
  241. }
  242. if config.Managed {
  243. managedClientsLock.Lock()
  244. managedClients = append(managedClients, c)
  245. managedClientsLock.Unlock()
  246. }
  247. return
  248. }
  249. // Client returns the protocol client for this connection.
  250. //
  251. // Subsequent calls to this will return the same client.
  252. func (c *Client) Client() (ClientProtocol, error) {
  253. _, err := c.Start()
  254. if err != nil {
  255. return nil, err
  256. }
  257. c.l.Lock()
  258. defer c.l.Unlock()
  259. if c.client != nil {
  260. return c.client, nil
  261. }
  262. switch c.protocol {
  263. case ProtocolNetRPC:
  264. c.client, err = newRPCClient(c)
  265. case ProtocolGRPC:
  266. c.client, err = newGRPCClient(c)
  267. default:
  268. return nil, fmt.Errorf("unknown server protocol: %s", c.protocol)
  269. }
  270. if err != nil {
  271. c.client = nil
  272. return nil, err
  273. }
  274. return c.client, nil
  275. }
  276. // Tells whether or not the underlying process has exited.
  277. func (c *Client) Exited() bool {
  278. c.l.Lock()
  279. defer c.l.Unlock()
  280. return c.exited
  281. }
  282. // End the executing subprocess (if it is running) and perform any cleanup
  283. // tasks necessary such as capturing any remaining logs and so on.
  284. //
  285. // This method blocks until the process successfully exits.
  286. //
  287. // This method can safely be called multiple times.
  288. func (c *Client) Kill() {
  289. // Grab a lock to read some private fields.
  290. c.l.Lock()
  291. process := c.process
  292. addr := c.address
  293. doneCh := c.doneLogging
  294. c.l.Unlock()
  295. // If there is no process, we never started anything. Nothing to kill.
  296. if process == nil {
  297. return
  298. }
  299. // We need to check for address here. It is possible that the plugin
  300. // started (process != nil) but has no address (addr == nil) if the
  301. // plugin failed at startup. If we do have an address, we need to close
  302. // the plugin net connections.
  303. graceful := false
  304. if addr != nil {
  305. // Close the client to cleanly exit the process.
  306. client, err := c.Client()
  307. if err == nil {
  308. err = client.Close()
  309. // If there is no error, then we attempt to wait for a graceful
  310. // exit. If there was an error, we assume that graceful cleanup
  311. // won't happen and just force kill.
  312. graceful = err == nil
  313. if err != nil {
  314. // If there was an error just log it. We're going to force
  315. // kill in a moment anyways.
  316. c.logger.Warn("error closing client during Kill", "err", err)
  317. }
  318. }
  319. }
  320. // If we're attempting a graceful exit, then we wait for a short period
  321. // of time to allow that to happen. To wait for this we just wait on the
  322. // doneCh which would be closed if the process exits.
  323. if graceful {
  324. select {
  325. case <-doneCh:
  326. return
  327. case <-time.After(250 * time.Millisecond):
  328. }
  329. }
  330. // If graceful exiting failed, just kill it
  331. process.Kill()
  332. // Wait for the client to finish logging so we have a complete log
  333. <-doneCh
  334. }
  335. // Starts the underlying subprocess, communicating with it to negotiate
  336. // a port for RPC connections, and returning the address to connect via RPC.
  337. //
  338. // This method is safe to call multiple times. Subsequent calls have no effect.
  339. // Once a client has been started once, it cannot be started again, even if
  340. // it was killed.
  341. func (c *Client) Start() (addr net.Addr, err error) {
  342. c.l.Lock()
  343. defer c.l.Unlock()
  344. if c.address != nil {
  345. return c.address, nil
  346. }
  347. // If one of cmd or reattach isn't set, then it is an error. We wrap
  348. // this in a {} for scoping reasons, and hopeful that the escape
  349. // analysis will pop the stock here.
  350. {
  351. cmdSet := c.config.Cmd != nil
  352. attachSet := c.config.Reattach != nil
  353. secureSet := c.config.SecureConfig != nil
  354. if cmdSet == attachSet {
  355. return nil, fmt.Errorf("Only one of Cmd or Reattach must be set")
  356. }
  357. if secureSet && attachSet {
  358. return nil, ErrSecureConfigAndReattach
  359. }
  360. }
  361. // Create the logging channel for when we kill
  362. c.doneLogging = make(chan struct{})
  363. if c.config.Reattach != nil {
  364. // Verify the process still exists. If not, then it is an error
  365. p, err := os.FindProcess(c.config.Reattach.Pid)
  366. if err != nil {
  367. return nil, err
  368. }
  369. // Attempt to connect to the addr since on Unix systems FindProcess
  370. // doesn't actually return an error if it can't find the process.
  371. conn, err := net.Dial(
  372. c.config.Reattach.Addr.Network(),
  373. c.config.Reattach.Addr.String())
  374. if err != nil {
  375. p.Kill()
  376. return nil, ErrProcessNotFound
  377. }
  378. conn.Close()
  379. // Goroutine to mark exit status
  380. go func(pid int) {
  381. // Wait for the process to die
  382. pidWait(pid)
  383. // Log so we can see it
  384. c.logger.Debug("reattached plugin process exited")
  385. // Mark it
  386. c.l.Lock()
  387. defer c.l.Unlock()
  388. c.exited = true
  389. // Close the logging channel since that doesn't work on reattach
  390. close(c.doneLogging)
  391. }(p.Pid)
  392. // Set the address and process
  393. c.address = c.config.Reattach.Addr
  394. c.process = p
  395. c.protocol = c.config.Reattach.Protocol
  396. if c.protocol == "" {
  397. // Default the protocol to net/rpc for backwards compatibility
  398. c.protocol = ProtocolNetRPC
  399. }
  400. return c.address, nil
  401. }
  402. env := []string{
  403. fmt.Sprintf("%s=%s", c.config.MagicCookieKey, c.config.MagicCookieValue),
  404. fmt.Sprintf("PLUGIN_MIN_PORT=%d", c.config.MinPort),
  405. fmt.Sprintf("PLUGIN_MAX_PORT=%d", c.config.MaxPort),
  406. }
  407. stdout_r, stdout_w := io.Pipe()
  408. stderr_r, stderr_w := io.Pipe()
  409. cmd := c.config.Cmd
  410. cmd.Env = append(cmd.Env, os.Environ()...)
  411. cmd.Env = append(cmd.Env, env...)
  412. cmd.Stdin = os.Stdin
  413. cmd.Stderr = stderr_w
  414. cmd.Stdout = stdout_w
  415. if c.config.SecureConfig != nil {
  416. if ok, err := c.config.SecureConfig.Check(cmd.Path); err != nil {
  417. return nil, fmt.Errorf("error verifying checksum: %s", err)
  418. } else if !ok {
  419. return nil, ErrChecksumsDoNotMatch
  420. }
  421. }
  422. c.logger.Debug("starting plugin", "path", cmd.Path, "args", cmd.Args)
  423. err = cmd.Start()
  424. if err != nil {
  425. return
  426. }
  427. // Set the process
  428. c.process = cmd.Process
  429. // Make sure the command is properly cleaned up if there is an error
  430. defer func() {
  431. r := recover()
  432. if err != nil || r != nil {
  433. cmd.Process.Kill()
  434. }
  435. if r != nil {
  436. panic(r)
  437. }
  438. }()
  439. // Start goroutine to wait for process to exit
  440. exitCh := make(chan struct{})
  441. go func() {
  442. // Make sure we close the write end of our stderr/stdout so
  443. // that the readers send EOF properly.
  444. defer stderr_w.Close()
  445. defer stdout_w.Close()
  446. // Wait for the command to end.
  447. cmd.Wait()
  448. // Log and make sure to flush the logs write away
  449. c.logger.Debug("plugin process exited", "path", cmd.Path)
  450. os.Stderr.Sync()
  451. // Mark that we exited
  452. close(exitCh)
  453. // Set that we exited, which takes a lock
  454. c.l.Lock()
  455. defer c.l.Unlock()
  456. c.exited = true
  457. }()
  458. // Start goroutine that logs the stderr
  459. go c.logStderr(stderr_r)
  460. // Start a goroutine that is going to be reading the lines
  461. // out of stdout
  462. linesCh := make(chan []byte)
  463. go func() {
  464. defer close(linesCh)
  465. buf := bufio.NewReader(stdout_r)
  466. for {
  467. line, err := buf.ReadBytes('\n')
  468. if line != nil {
  469. linesCh <- line
  470. }
  471. if err == io.EOF {
  472. return
  473. }
  474. }
  475. }()
  476. // Make sure after we exit we read the lines from stdout forever
  477. // so they don't block since it is an io.Pipe
  478. defer func() {
  479. go func() {
  480. for _ = range linesCh {
  481. }
  482. }()
  483. }()
  484. // Some channels for the next step
  485. timeout := time.After(c.config.StartTimeout)
  486. // Start looking for the address
  487. c.logger.Debug("waiting for RPC address", "path", cmd.Path)
  488. select {
  489. case <-timeout:
  490. err = errors.New("timeout while waiting for plugin to start")
  491. case <-exitCh:
  492. err = errors.New("plugin exited before we could connect")
  493. case lineBytes := <-linesCh:
  494. // Trim the line and split by "|" in order to get the parts of
  495. // the output.
  496. line := strings.TrimSpace(string(lineBytes))
  497. parts := strings.SplitN(line, "|", 6)
  498. if len(parts) < 4 {
  499. err = fmt.Errorf(
  500. "Unrecognized remote plugin message: %s\n\n"+
  501. "This usually means that the plugin is either invalid or simply\n"+
  502. "needs to be recompiled to support the latest protocol.", line)
  503. return
  504. }
  505. // Check the core protocol. Wrapped in a {} for scoping.
  506. {
  507. var coreProtocol int64
  508. coreProtocol, err = strconv.ParseInt(parts[0], 10, 0)
  509. if err != nil {
  510. err = fmt.Errorf("Error parsing core protocol version: %s", err)
  511. return
  512. }
  513. if int(coreProtocol) != CoreProtocolVersion {
  514. err = fmt.Errorf("Incompatible core API version with plugin. "+
  515. "Plugin version: %s, Ours: %d\n\n"+
  516. "To fix this, the plugin usually only needs to be recompiled.\n"+
  517. "Please report this to the plugin author.", parts[0], CoreProtocolVersion)
  518. return
  519. }
  520. }
  521. // Parse the protocol version
  522. var protocol int64
  523. protocol, err = strconv.ParseInt(parts[1], 10, 0)
  524. if err != nil {
  525. err = fmt.Errorf("Error parsing protocol version: %s", err)
  526. return
  527. }
  528. // Test the API version
  529. if uint(protocol) != c.config.ProtocolVersion {
  530. err = fmt.Errorf("Incompatible API version with plugin. "+
  531. "Plugin version: %s, Ours: %d", parts[1], c.config.ProtocolVersion)
  532. return
  533. }
  534. switch parts[2] {
  535. case "tcp":
  536. addr, err = net.ResolveTCPAddr("tcp", parts[3])
  537. case "unix":
  538. addr, err = net.ResolveUnixAddr("unix", parts[3])
  539. default:
  540. err = fmt.Errorf("Unknown address type: %s", parts[3])
  541. }
  542. // If we have a server type, then record that. We default to net/rpc
  543. // for backwards compatibility.
  544. c.protocol = ProtocolNetRPC
  545. if len(parts) >= 5 {
  546. c.protocol = Protocol(parts[4])
  547. }
  548. found := false
  549. for _, p := range c.config.AllowedProtocols {
  550. if p == c.protocol {
  551. found = true
  552. break
  553. }
  554. }
  555. if !found {
  556. err = fmt.Errorf("Unsupported plugin protocol %q. Supported: %v",
  557. c.protocol, c.config.AllowedProtocols)
  558. return
  559. }
  560. }
  561. c.address = addr
  562. return
  563. }
  564. // ReattachConfig returns the information that must be provided to NewClient
  565. // to reattach to the plugin process that this client started. This is
  566. // useful for plugins that detach from their parent process.
  567. //
  568. // If this returns nil then the process hasn't been started yet. Please
  569. // call Start or Client before calling this.
  570. func (c *Client) ReattachConfig() *ReattachConfig {
  571. c.l.Lock()
  572. defer c.l.Unlock()
  573. if c.address == nil {
  574. return nil
  575. }
  576. if c.config.Cmd != nil && c.config.Cmd.Process == nil {
  577. return nil
  578. }
  579. // If we connected via reattach, just return the information as-is
  580. if c.config.Reattach != nil {
  581. return c.config.Reattach
  582. }
  583. return &ReattachConfig{
  584. Protocol: c.protocol,
  585. Addr: c.address,
  586. Pid: c.config.Cmd.Process.Pid,
  587. }
  588. }
  589. // Protocol returns the protocol of server on the remote end. This will
  590. // start the plugin process if it isn't already started. Errors from
  591. // starting the plugin are surpressed and ProtocolInvalid is returned. It
  592. // is recommended you call Start explicitly before calling Protocol to ensure
  593. // no errors occur.
  594. func (c *Client) Protocol() Protocol {
  595. _, err := c.Start()
  596. if err != nil {
  597. return ProtocolInvalid
  598. }
  599. return c.protocol
  600. }
  601. // dialer is compatible with grpc.WithDialer and creates the connection
  602. // to the plugin.
  603. func (c *Client) dialer(_ string, timeout time.Duration) (net.Conn, error) {
  604. // Connect to the client
  605. conn, err := net.Dial(c.address.Network(), c.address.String())
  606. if err != nil {
  607. return nil, err
  608. }
  609. if tcpConn, ok := conn.(*net.TCPConn); ok {
  610. // Make sure to set keep alive so that the connection doesn't die
  611. tcpConn.SetKeepAlive(true)
  612. }
  613. // If we have a TLS config we wrap our connection. We only do this
  614. // for net/rpc since gRPC uses its own mechanism for TLS.
  615. if c.protocol == ProtocolNetRPC && c.config.TLSConfig != nil {
  616. conn = tls.Client(conn, c.config.TLSConfig)
  617. }
  618. return conn, nil
  619. }
  620. func (c *Client) logStderr(r io.Reader) {
  621. bufR := bufio.NewReader(r)
  622. for {
  623. line, err := bufR.ReadString('\n')
  624. if line != "" {
  625. c.config.Stderr.Write([]byte(line))
  626. line = strings.TrimRightFunc(line, unicode.IsSpace)
  627. l := c.logger.Named(filepath.Base(c.config.Cmd.Path))
  628. entry, err := parseJSON(line)
  629. // If output is not JSON format, print directly to Debug
  630. if err != nil {
  631. l.Debug(line)
  632. } else {
  633. out := flattenKVPairs(entry.KVPairs)
  634. l = l.With("timestamp", entry.Timestamp.Format(hclog.TimeFormat))
  635. switch hclog.LevelFromString(entry.Level) {
  636. case hclog.Trace:
  637. l.Trace(entry.Message, out...)
  638. case hclog.Debug:
  639. l.Debug(entry.Message, out...)
  640. case hclog.Info:
  641. l.Info(entry.Message, out...)
  642. case hclog.Warn:
  643. l.Warn(entry.Message, out...)
  644. case hclog.Error:
  645. l.Error(entry.Message, out...)
  646. }
  647. }
  648. }
  649. if err == io.EOF {
  650. break
  651. }
  652. }
  653. // Flag that we've completed logging for others
  654. close(c.doneLogging)
  655. }