service_unix.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. // Copyright 2015 Daniel Theophanes.
  2. // Use of this source code is governed by a zlib-style
  3. // license that can be found in the LICENSE file.
  4. // +build linux darwin solaris aix freebsd
  5. package service
  6. import (
  7. "bytes"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "log/syslog"
  12. "os/exec"
  13. "syscall"
  14. )
  15. func newSysLogger(name string, errs chan<- error) (Logger, error) {
  16. w, err := syslog.New(syslog.LOG_INFO, name)
  17. if err != nil {
  18. return nil, err
  19. }
  20. return sysLogger{w, errs}, nil
  21. }
  22. type sysLogger struct {
  23. *syslog.Writer
  24. errs chan<- error
  25. }
  26. func (s sysLogger) send(err error) error {
  27. if err != nil && s.errs != nil {
  28. s.errs <- err
  29. }
  30. return err
  31. }
  32. func (s sysLogger) Error(v ...interface{}) error {
  33. return s.send(s.Writer.Err(fmt.Sprint(v...)))
  34. }
  35. func (s sysLogger) Warning(v ...interface{}) error {
  36. return s.send(s.Writer.Warning(fmt.Sprint(v...)))
  37. }
  38. func (s sysLogger) Info(v ...interface{}) error {
  39. return s.send(s.Writer.Info(fmt.Sprint(v...)))
  40. }
  41. func (s sysLogger) Errorf(format string, a ...interface{}) error {
  42. return s.send(s.Writer.Err(fmt.Sprintf(format, a...)))
  43. }
  44. func (s sysLogger) Warningf(format string, a ...interface{}) error {
  45. return s.send(s.Writer.Warning(fmt.Sprintf(format, a...)))
  46. }
  47. func (s sysLogger) Infof(format string, a ...interface{}) error {
  48. return s.send(s.Writer.Info(fmt.Sprintf(format, a...)))
  49. }
  50. func run(command string, arguments ...string) error {
  51. _, _, err := runCommand(command, false, arguments...)
  52. return err
  53. }
  54. func runWithOutput(command string, arguments ...string) (int, string, error) {
  55. return runCommand(command, true, arguments...)
  56. }
  57. func runCommand(command string, readStdout bool, arguments ...string) (int, string, error) {
  58. cmd := exec.Command(command, arguments...)
  59. var output string
  60. var stdout io.ReadCloser
  61. var err error
  62. if readStdout {
  63. // Connect pipe to read Stdout
  64. stdout, err = cmd.StdoutPipe()
  65. if err != nil {
  66. // Failed to connect pipe
  67. return 0, "", fmt.Errorf("%q failed to connect stdout pipe: %v", command, err)
  68. }
  69. }
  70. // Connect pipe to read Stderr
  71. stderr, err := cmd.StderrPipe()
  72. if err != nil {
  73. // Failed to connect pipe
  74. return 0, "", fmt.Errorf("%q failed to connect stderr pipe: %v", command, err)
  75. }
  76. // Do not use cmd.Run()
  77. if err := cmd.Start(); err != nil {
  78. // Problem while copying stdin, stdout, or stderr
  79. return 0, "", fmt.Errorf("%q failed: %v", command, err)
  80. }
  81. // Zero exit status
  82. // Darwin: launchctl can fail with a zero exit status,
  83. // so check for emtpy stderr
  84. if command == "launchctl" {
  85. slurp, _ := ioutil.ReadAll(stderr)
  86. if len(slurp) > 0 && !bytes.HasSuffix(slurp, []byte("Operation now in progress\n")) {
  87. return 0, "", fmt.Errorf("%q failed with stderr: %s", command, slurp)
  88. }
  89. }
  90. if readStdout {
  91. out, err := ioutil.ReadAll(stdout)
  92. if err != nil {
  93. return 0, "", fmt.Errorf("%q failed while attempting to read stdout: %v", command, err)
  94. } else if len(out) > 0 {
  95. output = string(out)
  96. }
  97. }
  98. if err := cmd.Wait(); err != nil {
  99. exitStatus, ok := isExitError(err)
  100. if ok {
  101. // Command didn't exit with a zero exit status.
  102. return exitStatus, output, err
  103. }
  104. // An error occurred and there is no exit status.
  105. return 0, output, fmt.Errorf("%q failed: %v", command, err)
  106. }
  107. return 0, output, nil
  108. }
  109. func isExitError(err error) (int, bool) {
  110. if exiterr, ok := err.(*exec.ExitError); ok {
  111. if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
  112. return status.ExitStatus(), true
  113. }
  114. }
  115. return 0, false
  116. }