utils.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. package types
  2. import (
  3. "bytes"
  4. "encoding/gob"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "path/filepath"
  10. "runtime/debug"
  11. "strconv"
  12. "strings"
  13. "time"
  14. "github.com/crowdsecurity/crowdsec/pkg/cwversion"
  15. log "github.com/sirupsen/logrus"
  16. "gopkg.in/natefinch/lumberjack.v2"
  17. )
  18. var logFormatter log.Formatter
  19. var LogOutput *lumberjack.Logger //io.Writer
  20. var logLevel log.Level
  21. func SetDefaultLoggerConfig(cfgMode string, cfgFolder string, cfgLevel log.Level, maxSize int, maxFiles int, maxAge int, compress *bool) error {
  22. /*Configure logs*/
  23. if cfgMode == "file" {
  24. _maxsize := 500
  25. if maxSize != 0 {
  26. _maxsize = maxSize
  27. }
  28. _maxfiles := 3
  29. if maxFiles != 0 {
  30. _maxfiles = maxFiles
  31. }
  32. _maxage := 28
  33. if maxAge != 0 {
  34. _maxage = maxAge
  35. }
  36. _compress := true
  37. if compress != nil {
  38. _compress = *compress
  39. }
  40. /*cf. https://github.com/natefinch/lumberjack/issues/82
  41. let's create the file beforehand w/ the right perms */
  42. fname := cfgFolder + "/crowdsec.log"
  43. // check if file exists
  44. _, err := os.Stat(fname)
  45. // create file if not exists, purposefully ignore errors
  46. if os.IsNotExist(err) {
  47. file, _ := os.OpenFile(fname, os.O_RDWR|os.O_CREATE, 0600)
  48. file.Close()
  49. }
  50. LogOutput = &lumberjack.Logger{
  51. Filename: fname,
  52. MaxSize: _maxsize,
  53. MaxBackups: _maxfiles,
  54. MaxAge: _maxage,
  55. Compress: _compress,
  56. }
  57. log.SetOutput(LogOutput)
  58. } else if cfgMode != "stdout" {
  59. return fmt.Errorf("log mode '%s' unknown", cfgMode)
  60. }
  61. logLevel = cfgLevel
  62. log.SetLevel(logLevel)
  63. logFormatter = &log.TextFormatter{TimestampFormat: "02-01-2006 15:04:05", FullTimestamp: true}
  64. log.SetFormatter(logFormatter)
  65. return nil
  66. }
  67. func ConfigureLogger(clog *log.Logger) error {
  68. /*Configure logs*/
  69. if LogOutput != nil {
  70. clog.SetOutput(LogOutput)
  71. }
  72. if logFormatter != nil {
  73. clog.SetFormatter(logFormatter)
  74. }
  75. clog.SetLevel(logLevel)
  76. return nil
  77. }
  78. func Clone(a, b interface{}) error {
  79. buff := new(bytes.Buffer)
  80. enc := gob.NewEncoder(buff)
  81. dec := gob.NewDecoder(buff)
  82. if err := enc.Encode(a); err != nil {
  83. return fmt.Errorf("failed cloning %T", a)
  84. }
  85. if err := dec.Decode(b); err != nil {
  86. return fmt.Errorf("failed cloning %T", b)
  87. }
  88. return nil
  89. }
  90. func WriteStackTrace(iErr interface{}) string {
  91. tmpfile, err := ioutil.TempFile("/tmp/", "crowdsec-crash.*.txt")
  92. if err != nil {
  93. log.Fatal(err)
  94. }
  95. if _, err := tmpfile.Write([]byte(fmt.Sprintf("error : %+v\n", iErr))); err != nil {
  96. tmpfile.Close()
  97. log.Fatal(err)
  98. }
  99. if _, err := tmpfile.Write([]byte(cwversion.ShowStr())); err != nil {
  100. tmpfile.Close()
  101. log.Fatal(err)
  102. }
  103. if _, err := tmpfile.Write(debug.Stack()); err != nil {
  104. tmpfile.Close()
  105. log.Fatal(err)
  106. }
  107. if err := tmpfile.Close(); err != nil {
  108. log.Fatal(err)
  109. }
  110. return tmpfile.Name()
  111. }
  112. //CatchPanic is a util func that we should call from all go-routines to ensure proper stacktrace handling
  113. func CatchPanic(component string) {
  114. if r := recover(); r != nil {
  115. log.Errorf("crowdsec - goroutine %s crashed : %s", component, r)
  116. log.Errorf("please report this error to https://github.com/crowdsecurity/crowdsec/")
  117. filename := WriteStackTrace(r)
  118. log.Errorf("stacktrace/report is written to %s : please join it to your issue", filename)
  119. log.Fatalf("crowdsec stopped")
  120. }
  121. }
  122. func ParseDuration(d string) (time.Duration, error) {
  123. durationStr := d
  124. if strings.HasSuffix(d, "d") {
  125. days := strings.Split(d, "d")[0]
  126. if len(days) == 0 {
  127. return 0, fmt.Errorf("'%s' can't be parsed as duration", d)
  128. }
  129. daysInt, err := strconv.Atoi(days)
  130. if err != nil {
  131. return 0, err
  132. }
  133. durationStr = strconv.Itoa(daysInt*24) + "h"
  134. }
  135. duration, err := time.ParseDuration(durationStr)
  136. if err != nil {
  137. return 0, err
  138. }
  139. return duration, nil
  140. }
  141. /*help to copy the file, ioutil doesn't offer the feature*/
  142. func copyFileContents(src, dst string) (err error) {
  143. in, err := os.Open(src)
  144. if err != nil {
  145. return
  146. }
  147. defer in.Close()
  148. out, err := os.Create(dst)
  149. if err != nil {
  150. return
  151. }
  152. defer func() {
  153. cerr := out.Close()
  154. if err == nil {
  155. err = cerr
  156. }
  157. }()
  158. if _, err = io.Copy(out, in); err != nil {
  159. return
  160. }
  161. err = out.Sync()
  162. return
  163. }
  164. /*copy the file, ioutile doesn't offer the feature*/
  165. func CopyFile(sourceSymLink, destinationFile string) (err error) {
  166. sourceFile, err := filepath.EvalSymlinks(sourceSymLink)
  167. if err != nil {
  168. log.Infof("Not a symlink : %s", err)
  169. sourceFile = sourceSymLink
  170. }
  171. sourceFileStat, err := os.Stat(sourceFile)
  172. if err != nil {
  173. return
  174. }
  175. if !sourceFileStat.Mode().IsRegular() {
  176. // cannot copy non-regular files (e.g., directories,
  177. // symlinks, devices, etc.)
  178. return fmt.Errorf("copyFile: non-regular source file %s (%q)", sourceFileStat.Name(), sourceFileStat.Mode().String())
  179. }
  180. destinationFileStat, err := os.Stat(destinationFile)
  181. if err != nil {
  182. if !os.IsNotExist(err) {
  183. return
  184. }
  185. } else {
  186. if !(destinationFileStat.Mode().IsRegular()) {
  187. return fmt.Errorf("copyFile: non-regular destination file %s (%q)", destinationFileStat.Name(), destinationFileStat.Mode().String())
  188. }
  189. if os.SameFile(sourceFileStat, destinationFileStat) {
  190. return
  191. }
  192. }
  193. if err = os.Link(sourceFile, destinationFile); err == nil {
  194. return
  195. }
  196. err = copyFileContents(sourceFile, destinationFile)
  197. return
  198. }
  199. func StrPtr(s string) *string {
  200. return &s
  201. }
  202. func Int32Ptr(i int32) *int32 {
  203. return &i
  204. }
  205. func BoolPtr(b bool) *bool {
  206. return &b
  207. }
  208. func InSlice(str string, slice []string) bool {
  209. for _, item := range slice {
  210. if str == item {
  211. return true
  212. }
  213. }
  214. return false
  215. }