paths_unix.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build !windows && !wasm && !plan9 && !tamago
  4. package paths
  5. import (
  6. "fmt"
  7. "os"
  8. "path/filepath"
  9. "runtime"
  10. "golang.org/x/sys/unix"
  11. "tailscale.com/version/distro"
  12. )
  13. func init() {
  14. stateFileFunc = stateFileUnix
  15. ensureStateDirPerms = ensureStateDirPermsUnix
  16. }
  17. func statePath() string {
  18. if runtime.GOOS == "linux" && distro.Get() == distro.JetKVM {
  19. return "/userdata/tailscale/var/tailscaled.state"
  20. }
  21. switch runtime.GOOS {
  22. case "linux", "illumos", "solaris":
  23. return "/var/lib/tailscale/tailscaled.state"
  24. case "freebsd", "openbsd":
  25. return "/var/db/tailscale/tailscaled.state"
  26. case "darwin":
  27. return "/Library/Tailscale/tailscaled.state"
  28. case "aix":
  29. return "/var/tailscale/tailscaled.state"
  30. default:
  31. return ""
  32. }
  33. }
  34. func stateFileUnix() string {
  35. if distro.Get() == distro.Gokrazy {
  36. return "/perm/tailscaled/tailscaled.state"
  37. }
  38. path := statePath()
  39. if path == "" {
  40. return ""
  41. }
  42. try := path
  43. for range 3 { // check writability of the file, /var/lib/tailscale, and /var/lib
  44. err := unix.Access(try, unix.O_RDWR)
  45. if err == nil {
  46. return path
  47. }
  48. try = filepath.Dir(try)
  49. }
  50. if os.Getuid() == 0 {
  51. return ""
  52. }
  53. // For non-root users, fall back to $XDG_DATA_HOME/tailscale/*.
  54. return filepath.Join(xdgDataHome(), "tailscale", "tailscaled.state")
  55. }
  56. func xdgDataHome() string {
  57. if e := os.Getenv("XDG_DATA_HOME"); e != "" {
  58. return e
  59. }
  60. return filepath.Join(os.Getenv("HOME"), ".local/share")
  61. }
  62. func ensureStateDirPermsUnix(dir string) error {
  63. if filepath.Base(dir) != "tailscale" {
  64. return nil
  65. }
  66. fi, err := os.Stat(dir)
  67. if err != nil {
  68. return err
  69. }
  70. if !fi.IsDir() {
  71. return fmt.Errorf("expected %q to be a directory; is %v", dir, fi.Mode())
  72. }
  73. const perm = 0700
  74. if fi.Mode().Perm() == perm {
  75. // Already correct.
  76. return nil
  77. }
  78. return os.Chmod(dir, perm)
  79. }