github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/containers/podman/state.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build linux 16 17 package podman 18 19 import ( 20 "database/sql" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "strings" 25 "time" 26 27 bolt "go.etcd.io/bbolt" 28 29 // SQLite driver needed for parsing db.sql files. 30 _ "modernc.org/sqlite" 31 ) 32 33 // state interface must be implemented by each repository returning podman containers info 34 type state interface { 35 Close() error 36 AllContainers() ([]*container, error) 37 } 38 39 var _ state = &boltState{} 40 41 type boltState struct { 42 conn *bolt.DB 43 } 44 45 func newBoltState(path string) (state, error) { 46 db, err := bolt.Open(path, 0444, &bolt.Options{Timeout: 1 * time.Second}) 47 if err != nil { 48 return nil, err 49 } 50 return &boltState{conn: db}, nil 51 } 52 53 // AllContainers return all the pods 54 func (s *boltState) AllContainers() ([]*container, error) { 55 ctrs := []*container{} 56 57 err := s.conn.View(func(tx *bolt.Tx) error { 58 allCtrsBucket := tx.Bucket([]byte("all-ctrs")) 59 if allCtrsBucket == nil { 60 return errors.New("allCtrs bucket not found in DB") 61 } 62 63 ctrBuckets := tx.Bucket([]byte("ctr")) 64 if ctrBuckets == nil { 65 return errors.New("containers bucket not found in DB") 66 } 67 68 return allCtrsBucket.ForEach(func(id, name []byte) error { 69 ctrBucket := ctrBuckets.Bucket(id) 70 if ctrBucket == nil { 71 return fmt.Errorf("state is inconsistent - container ID %s in all containers, but container not found", string(id)) 72 } 73 74 ctr := new(container) 75 ctr.config = new(containerConfig) 76 ctr.state = new(containerState) 77 78 configBytes := ctrBucket.Get([]byte("config")) 79 if err := json.Unmarshal(configBytes, ctr.config); err != nil { 80 return fmt.Errorf("unmarshalling container %s config: %w", string(id), err) 81 } 82 83 stateBytes := ctrBucket.Get([]byte("state")) 84 if err := json.Unmarshal(stateBytes, ctr.state); err != nil { 85 return fmt.Errorf("unmarshalling container %s state: %w", string(id), err) 86 } 87 88 ctrs = append(ctrs, ctr) 89 return nil 90 }) 91 }) 92 if err != nil { 93 return nil, err 94 } 95 96 return ctrs, nil 97 } 98 99 // Close closes the bolt db connection 100 func (s *boltState) Close() error { 101 return s.conn.Close() 102 } 103 104 var _ state = &sqliteState{} 105 106 type sqliteState struct { 107 conn *sql.DB 108 } 109 110 func newSqliteState(path string) (state, error) { 111 db, err := sql.Open("sqlite", path) 112 if err != nil { 113 return nil, err 114 } 115 return &sqliteState{conn: db}, nil 116 } 117 118 // AllContainers return all the pods 119 func (s *sqliteState) AllContainers() ([]*container, error) { 120 //nolint:noctx // this function is defined by an interface, so we can't pass a context through to here 121 rows, err := s.conn.Query("SELECT ContainerConfig.JSON, ContainerState.JSON AS StateJSON FROM ContainerConfig INNER JOIN ContainerState ON ContainerConfig.ID = ContainerState.ID;") 122 if err != nil { 123 return nil, err 124 } 125 defer rows.Close() 126 127 var ctrs []*container 128 for rows.Next() { 129 var configJSON, stateJSON string 130 if err := rows.Scan(&configJSON, &stateJSON); err != nil { 131 return nil, fmt.Errorf("scanning container from database: %w", err) 132 } 133 134 ctr := new(container) 135 ctr.config = new(containerConfig) 136 ctr.state = new(containerState) 137 138 if err := json.Unmarshal([]byte(configJSON), ctr.config); err != nil { 139 return nil, fmt.Errorf("unmarshalling container config: %w", err) 140 } 141 if err := json.Unmarshal([]byte(stateJSON), ctr.state); err != nil { 142 return nil, fmt.Errorf("unmarshalling container %s state: %w", ctr.config.ID, err) 143 } 144 ctrs = append(ctrs, ctr) 145 } 146 if err := rows.Err(); err != nil { 147 return nil, err 148 } 149 return ctrs, nil 150 } 151 152 // Close closes the bolt db connection 153 func (s *sqliteState) Close() error { 154 return s.conn.Close() 155 } 156 157 func getDBState(path string) (state, error) { 158 switch { 159 case strings.HasSuffix(path, "bolt_state.db"): 160 return newBoltState(path) 161 case strings.HasSuffix(path, "db.sql"): 162 return newSqliteState(path) 163 default: 164 return nil, fmt.Errorf("cannot create state from %s, database not implemented", path) 165 } 166 }