hostinfo_linux.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. // +build linux,!android
  5. package controlclient
  6. import (
  7. "bytes"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "strings"
  12. "syscall"
  13. "go4.org/mem"
  14. "tailscale.com/util/lineread"
  15. "tailscale.com/version/distro"
  16. )
  17. func init() {
  18. osVersion = osVersionLinux
  19. }
  20. func osVersionLinux() string {
  21. dist := distro.Get()
  22. propFile := "/etc/os-release"
  23. switch dist {
  24. case distro.Synology:
  25. propFile = "/etc.defaults/VERSION"
  26. case distro.OpenWrt:
  27. propFile = "/etc/openwrt_release"
  28. }
  29. m := map[string]string{}
  30. lineread.File(propFile, func(line []byte) error {
  31. eq := bytes.IndexByte(line, '=')
  32. if eq == -1 {
  33. return nil
  34. }
  35. k, v := string(line[:eq]), strings.Trim(string(line[eq+1:]), `"'`)
  36. m[k] = v
  37. return nil
  38. })
  39. var un syscall.Utsname
  40. syscall.Uname(&un)
  41. var attrBuf strings.Builder
  42. attrBuf.WriteString("; kernel=")
  43. for _, b := range un.Release {
  44. if b == 0 {
  45. break
  46. }
  47. attrBuf.WriteByte(byte(b))
  48. }
  49. if inContainer() {
  50. attrBuf.WriteString("; container")
  51. }
  52. attr := attrBuf.String()
  53. id := m["ID"]
  54. switch id {
  55. case "debian":
  56. slurp, _ := ioutil.ReadFile("/etc/debian_version")
  57. return fmt.Sprintf("Debian %s (%s)%s", bytes.TrimSpace(slurp), m["VERSION_CODENAME"], attr)
  58. case "ubuntu":
  59. return fmt.Sprintf("Ubuntu %s%s", m["VERSION"], attr)
  60. case "", "centos": // CentOS 6 has no /etc/os-release, so its id is ""
  61. if cr, _ := ioutil.ReadFile("/etc/centos-release"); len(cr) > 0 { // "CentOS release 6.10 (Final)
  62. return fmt.Sprintf("%s%s", bytes.TrimSpace(cr), attr)
  63. }
  64. fallthrough
  65. case "fedora", "rhel", "alpine":
  66. // Their PRETTY_NAME is fine as-is for all versions I tested.
  67. fallthrough
  68. default:
  69. if v := m["PRETTY_NAME"]; v != "" {
  70. return fmt.Sprintf("%s%s", v, attr)
  71. }
  72. }
  73. switch dist {
  74. case distro.Synology:
  75. return fmt.Sprintf("Synology %s%s", m["productversion"], attr)
  76. case distro.OpenWrt:
  77. return fmt.Sprintf("OpenWrt %s%s", m["DISTRIB_RELEASE"], attr)
  78. }
  79. return fmt.Sprintf("Other%s", attr)
  80. }
  81. func inContainer() (ret bool) {
  82. lineread.File("/proc/1/cgroup", func(line []byte) error {
  83. if mem.Contains(mem.B(line), mem.S("/docker/")) ||
  84. mem.Contains(mem.B(line), mem.S("/lxc/")) {
  85. ret = true
  86. return io.EOF // arbitrary non-nil error to stop loop
  87. }
  88. return nil
  89. })
  90. return
  91. }