33package metrics
44
55import (
6+ "encoding/json"
67 "errors"
78 "os"
89 "os/exec"
9- "strconv"
1010 "strings"
1111 "time"
1212)
1313
14+ // SmbStatusServerID represents a server_id output field
15+ type SmbStatusServerID struct {
16+ PID string `json:"pid"`
17+ TaskID string `json:"task_id"`
18+ VNN string `json:"vnn"`
19+ UniqueID string `json:"unique_id"`
20+ }
21+
22+ // SmbStatusEncryption represents a encryption output field
23+ type SmbStatusEncryption struct {
24+ Cipher string `json:"cipher"`
25+ Degree string `json:"degree"`
26+ }
27+
28+ // SmbStatusSigning represents a signing output field
29+ type SmbStatusSigning struct {
30+ Cipher string `json:"cipher"`
31+ Degree string `json:"degree"`
32+ }
33+
1434// SmbStatusShare represents a single entry from the output of 'smbstatus -S'
1535type SmbStatusShare struct {
16- Service string
17- PID int64
18- Machine string
19- ConnectedAt string
20- ConnectedTime time.Time
21- Encryption string
22- Signing string
36+ Service string `json:"service"`
37+ ServerID SmbStatusServerID `json:"server_id"`
38+ Machine string `json:"machine"`
39+ ConnectedAt string `json:"connected_at"`
40+ Encryption SmbStatusEncryption `json:"encryption"`
41+ Signing SmbStatusSigning `json:"signing"`
42+ }
43+
44+ // SmbStatusSession represents a session output field
45+ type SmbStatusSession struct {
46+ SessionID string `json:"session_id"`
47+ ServerID SmbStatusServerID `json:"server_id"`
48+ UID int `json:"uid"`
49+ GID int `json:"gid"`
50+ Username string `json:"username"`
51+ Groupname string `json:"groupname"`
52+ RemoteMachine string `json:"remote_machine"`
53+ Hostname string `json:"hostname"`
54+ SessionDialect string `json:"session_dialect"`
55+ Encryption SmbStatusEncryption `json:"encryption"`
56+ Signing SmbStatusSigning `json:"signing"`
57+ }
58+
59+ // SmbStatusFileID represents a fileid output field
60+ type SmbStatusFileID struct {
61+ DevID int64 `json:"devid"`
62+ Inode int64 `json:"inode"`
63+ Extid int32 `json:"extid"`
64+ }
65+
66+ // SmbStatusLockedFile represents a locked-file output field
67+ type SmbStatusLockedFile struct {
68+ ServicePath string `json:"service_path"`
69+ Filename string `json:"filename"`
70+ FileID SmbStatusFileID `json:"fileid"`
71+ NumPendingDeletes int `json:"num_pending_deletes"`
72+ }
73+
74+ // SmbStatusJSON represents output of 'smbstatus --json'
75+ type SmbStatusJSON struct {
76+ Timestamp string `json:"timestamp"`
77+ Version string `json:"version"`
78+ SmbConf string `json:"smb_conf"`
79+ Sessions map [string ]SmbStatusSession `json:"sessions"`
80+ TCons map [string ]SmbStatusShare `json:"tcons"`
81+ LockedFiles map [string ]SmbStatusLockedFile `json:"locked_files"`
2382}
2483
2584// SmbStatusProc represents a single entry from the output of 'smbstatus -p'
2685type SmbStatusProc struct {
27- PID int64
86+ PID string
2887 Username string
2988 Group string
3089 Machine string
@@ -35,8 +94,8 @@ type SmbStatusProc struct {
3594
3695// SmbStatusLock represents a single entry from the output of 'smbstatus -L'
3796type SmbStatusLock struct {
38- PID int64
39- UserID int64
97+ PID string
98+ UserID string
4099 DenyMode string
41100 Access string
42101 RW string
@@ -78,11 +137,29 @@ func RunSmbStatusVersion() (string, error) {
78137
79138// RunSmbStatusShares executes 'smbstatus -S' on host container
80139func RunSmbStatusShares () ([]SmbStatusShare , error ) {
81- dat , err := executeSmbStatusCommand ("-S" )
140+ // Case 1: using new json output
141+ dat , err := executeSmbStatusCommand ("-S --json" )
142+ if err == nil {
143+ return parseSmbStatusSharesAsJSON (dat )
144+ }
145+ // Case 2: fallback to raw-text output
146+ dat , err = executeSmbStatusCommand ("-S" )
147+ if err == nil {
148+ return parseSmbStatusShares (dat )
149+ }
150+ return []SmbStatusShare {}, err
151+ }
152+
153+ func parseSmbStatusSharesAsJSON (dat string ) ([]SmbStatusShare , error ) {
154+ shares := []SmbStatusShare {}
155+ res , err := parseSmbStatusJSON (dat )
82156 if err != nil {
83- return []SmbStatusShare {}, err
157+ return shares , err
158+ }
159+ for _ , share := range res .TCons {
160+ shares = append (shares , share )
84161 }
85- return parseSmbStatusShares ( dat )
162+ return shares , nil
86163}
87164
88165// RunSmbStatusLocks executes 'smbstatus -L' on host container
@@ -106,15 +183,19 @@ func RunSmbStatusProcs() ([]SmbStatusProc, error) {
106183// SmbStatusSharesByMachine converts the output of RunSmbStatusShares into map
107184// indexed by machine's host
108185func SmbStatusSharesByMachine () (map [string ][]SmbStatusShare , error ) {
109- ret := map [string ][]SmbStatusShare {}
110186 shares , err := RunSmbStatusShares ()
111187 if err != nil {
112- return ret , err
188+ return map [ string ][] SmbStatusShare {} , err
113189 }
190+ return makeSmbSharesMap (shares ), nil
191+ }
192+
193+ func makeSmbSharesMap (shares []SmbStatusShare ) map [string ][]SmbStatusShare {
194+ ret := map [string ][]SmbStatusShare {}
114195 for _ , share := range shares {
115196 ret [share .Machine ] = append (ret [share .Machine ], share )
116197 }
117- return ret , nil
198+ return ret
118199}
119200
120201func executeSmbStatusCommand (args ... string ) (string , error ) {
@@ -175,18 +256,11 @@ func parseSmbStatusShares(data string) ([]SmbStatusShare, error) {
175256 // Parse data into internal repr
176257 share := SmbStatusShare {}
177258 share .Service = parseSubstr (ln , serviceIndex )
178- pid , err := parseInt64 (ln , pidIndex )
179- if err != nil {
180- return shares , err
181- }
182- share .PID = pid
259+ share .ServerID .PID = parseSubstr (ln , pidIndex )
183260 share .Machine = parseSubstr (ln , machineIndex )
184261 share .ConnectedAt = parseSubstr2 (ln , connectedAtIndex , encryptionIndex )
185- share .Encryption = parseSubstr (ln , encryptionIndex )
186- share .Signing = parseSubstr (ln , signingIndex )
187- if t , err := parseTime (share .ConnectedAt ); err == nil {
188- share .ConnectedTime = t
189- }
262+ share .Encryption .Cipher = parseSubstr (ln , encryptionIndex )
263+ share .Signing .Cipher = parseSubstr (ln , signingIndex )
190264
191265 // Ignore "IPC$"
192266 if share .Service == "IPC$" {
@@ -239,11 +313,7 @@ func parseSmbStatusProcs(data string) ([]SmbStatusProc, error) {
239313 }
240314 // Parse data into internal repr
241315 proc := SmbStatusProc {}
242- pid , err := parseInt64 (ln , pidIndex )
243- if err != nil {
244- return procs , err
245- }
246- proc .PID = pid
316+ proc .PID = parseSubstr (ln , pidIndex )
247317 proc .Username = parseSubstr (ln , usernameIndex )
248318 proc .Group = parseSubstr (ln , groupIndex )
249319 proc .Machine = parseSubstr (ln , machineIndex )
@@ -300,16 +370,8 @@ func parseSmbStatusLocks(data string) ([]SmbStatusLock, error) {
300370 }
301371 // Parse data into internal repr
302372 lock := SmbStatusLock {}
303- pid , err := parseInt64 (ln , pidIndex )
304- if err != nil {
305- return locks , err
306- }
307- lock .PID = pid
308- user , err := parseInt64 (ln , userIndex )
309- if err != nil {
310- return locks , err
311- }
312- lock .UserID = user
373+ lock .PID = parseSubstr (ln , pidIndex )
374+ lock .UserID = parseSubstr (ln , userIndex )
313375 lock .DenyMode = parseSubstr (ln , denyModeIndex )
314376 lock .Access = parseSubstr (ln , accessIndex )
315377 lock .RW = parseSubstr (ln , rwIndex )
@@ -320,10 +382,6 @@ func parseSmbStatusLocks(data string) ([]SmbStatusLock, error) {
320382 return locks , nil
321383}
322384
323- func parseInt64 (s string , startIndex int ) (int64 , error ) {
324- return strconv .ParseInt (parseSubstr (s , startIndex ), 10 , 64 )
325- }
326-
327385func parseSubstr (s string , startIndex int ) string {
328386 sub := strings .TrimSpace (s [startIndex :])
329387 fields := strings .Fields (sub )
@@ -337,7 +395,7 @@ func parseSubstr2(s string, startIndex, endIndex int) string {
337395 return strings .TrimSpace (s [startIndex :endIndex ])
338396}
339397
340- func parseTime (s string ) (time.Time , error ) {
398+ func ParseTime (s string ) (time.Time , error ) {
341399 layouts := []string {
342400 time .ANSIC ,
343401 time .UnixDate ,
@@ -351,3 +409,11 @@ func parseTime(s string) (time.Time, error) {
351409 // samba's lib/util/time.c uses non standad layout...
352410 return time.Time {}, errors .New ("unknow time format " + s )
353411}
412+
413+ // parseSmbStatusJSON parses to output of 'smbstatus --json' into internal
414+ // representation.
415+ func parseSmbStatusJSON (data string ) (* SmbStatusJSON , error ) {
416+ res := SmbStatusJSON {}
417+ err := json .Unmarshal ([]byte (data ), & res )
418+ return & res , err
419+ }
0 commit comments