/lsblk.go

https://gitlab.com/JamesClonk/cryptmount · Go · 199 lines · 164 code · 28 blank · 7 comment · 30 complexity · 41304d25b4c0dcff673abbbcc82aebe7 MD5 · raw file

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. "regexp"
  6. "strconv"
  7. "strings"
  8. )
  9. var (
  10. LSBLK_CMD = `lsblk -f -a -p --pairs -n -b -o NAME,FSTYPE,MOUNTPOINT,LABEL,UUID,PARTLABEL,PARTUUID,SIZE,TYPE`
  11. LSBLK_RX = regexp.MustCompile(`(\w+)=(?:"(.*?)")`)
  12. )
  13. const (
  14. _ = iota
  15. KBYTE float64 = 1 << (10 * iota)
  16. MBYTE
  17. GBYTE
  18. TBYTE
  19. )
  20. type Disk struct {
  21. Name string
  22. Size string
  23. SizeH string
  24. Volumes Volumes
  25. HasLUKS bool
  26. }
  27. type Volume struct {
  28. Name string
  29. Fstype string
  30. IsLUKS bool
  31. MappedName string
  32. IsMapped bool
  33. MappedVolumes []MappedVolume
  34. Mountpoint string
  35. IsMounted bool
  36. Label string
  37. Uuid string
  38. Size string
  39. SizeH string
  40. Type string
  41. }
  42. type MappedVolume struct {
  43. Name string
  44. Fstype string
  45. Mountpoint string
  46. IsMounted bool
  47. Label string
  48. Uuid string
  49. Size string
  50. SizeH string
  51. Type string
  52. }
  53. type Volumes []Volume
  54. func Lsdsk() []Disk {
  55. disks := make([]Disk, 0)
  56. var disk Disk
  57. for _, volume := range lsblk() {
  58. if volume.Type == "disk" {
  59. // add new disk
  60. disk = Disk{
  61. Name: volume.Name,
  62. Size: volume.Size,
  63. SizeH: volume.SizeH,
  64. Volumes: make([]Volume, 0),
  65. }
  66. disks = append(disks, disk)
  67. } else {
  68. // add volume to disk
  69. if volume.IsLUKS {
  70. disks[len(disks)-1].HasLUKS = true
  71. }
  72. disks[len(disks)-1].Volumes = append(disks[len(disks)-1].Volumes, volume)
  73. }
  74. }
  75. return disks
  76. }
  77. func lsblk() (volumes Volumes) {
  78. for _, line := range strings.Split(system(LSBLK_CMD), "\n") {
  79. line = strings.Trim(line, "\t\n\r ")
  80. if line == "" {
  81. continue
  82. }
  83. if strings.Contains(line, `TYPE="crypt"`) {
  84. // mapped value
  85. volume := MappedVolume{}
  86. vol := reflect.ValueOf(&volume).Elem()
  87. parseVolume(line, vol)
  88. // volume checks
  89. volume.IsMounted = volume.Mountpoint != ""
  90. // volumes[len(volumes)-1] => parent
  91. volumes[len(volumes)-1].IsMapped = true
  92. volumes[len(volumes)-1].MappedName = volume.Name
  93. volumes[len(volumes)-1].IsMounted = true
  94. volumes[len(volumes)-1].MappedVolumes = append(volumes[len(volumes)-1].MappedVolumes, volume)
  95. } else if strings.Contains(line, `TYPE="disk"`) || strings.Contains(line, `TYPE="part"`) {
  96. // normal volume
  97. volume := Volume{}
  98. vol := reflect.ValueOf(&volume).Elem()
  99. parseVolume(line, vol)
  100. // volume checks
  101. volume.IsMounted = volume.Mountpoint != ""
  102. if volume.Fstype == "crypto_LUKS" { // LUKS volume
  103. volume.IsLUKS = true
  104. }
  105. volumes = append(volumes, volume)
  106. }
  107. }
  108. return
  109. }
  110. func parseVolume(line string, vol reflect.Value) {
  111. pairs := LSBLK_RX.FindAllStringSubmatch(line, -1)
  112. for _, pair := range pairs {
  113. if len(pair) != 3 {
  114. continue
  115. }
  116. key, value := strings.Title(strings.ToLower(pair[1])), pair[2]
  117. field := vol.FieldByName(key)
  118. if field.IsValid() {
  119. field.SetString(value)
  120. if key == "Size" { // human readable format for size
  121. if size, err := formatByteSize(value); err == nil {
  122. field2 := vol.FieldByName("SizeH")
  123. if field2.IsValid() {
  124. field2.SetString(size)
  125. }
  126. }
  127. }
  128. }
  129. }
  130. }
  131. func formatByteSize(value string) (string, error) {
  132. size, err := strconv.ParseFloat(value, 64)
  133. if err != nil {
  134. return "", err
  135. }
  136. switch {
  137. case size >= TBYTE:
  138. return fmt.Sprintf("%.1fT", size/TBYTE), nil
  139. case size >= GBYTE:
  140. return fmt.Sprintf("%.1fG", size/GBYTE), nil
  141. case size >= MBYTE:
  142. return fmt.Sprintf("%.1fM", size/MBYTE), nil
  143. case size >= KBYTE:
  144. return fmt.Sprintf("%.1fK", size/KBYTE), nil
  145. }
  146. return fmt.Sprintf("%.1fB", size), nil
  147. }
  148. func (volumes *Volumes) filter(predicate func(Volume) bool) *Volumes {
  149. var newVolumes Volumes
  150. for _, volume := range *volumes {
  151. if predicate(volume) {
  152. newVolumes = append(newVolumes, volume)
  153. }
  154. }
  155. return &newVolumes
  156. }
  157. func (volumes *Volumes) luksOnly() *Volumes {
  158. return volumes.filter(func(v Volume) bool {
  159. return v.IsLUKS
  160. })
  161. }
  162. func (volumes *Volumes) mounted() *Volumes {
  163. return volumes.filter(func(v Volume) bool {
  164. return v.IsMounted
  165. })
  166. }
  167. func (volumes *Volumes) unmounted() *Volumes {
  168. return volumes.filter(func(v Volume) bool {
  169. return !v.IsMounted
  170. })
  171. }