Merge branch 'go_interface_library' into 'master'
Go interface library See merge request bboehmke/raspi-alpine-builder!1
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1 +1,2 @@
 | 
				
			|||||||
.idea/
 | 
					.idea/
 | 
				
			||||||
 | 
					test_*
 | 
				
			||||||
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					module gitlab.com/bboehmke/raspi-alpine-builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go 1.13
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46
 | 
				
			||||||
 | 
						github.com/stretchr/testify v1.4.0
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										14
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
				
			|||||||
 | 
					github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46 h1:rs0kDBt2zF4/CM9rO5/iH+U22jnTygPlqWgX55Ufcxg=
 | 
				
			||||||
 | 
					github.com/GehirnInc/crypt v0.0.0-20190301055215-6c0105aabd46/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo=
 | 
				
			||||||
 | 
					github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 | 
				
			||||||
 | 
					github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
 | 
					github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 | 
				
			||||||
 | 
					github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
				
			||||||
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
				
			||||||
							
								
								
									
										171
									
								
								network.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								network.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,171 @@
 | 
				
			|||||||
 | 
					package alpine_builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// network configuration
 | 
				
			||||||
 | 
					var networkConfig = "/data/etc/network/interfaces"
 | 
				
			||||||
 | 
					var networkInterface = "eth0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// regular expressions
 | 
				
			||||||
 | 
					var reSectionStart = regexp.MustCompile("^(iface|mapping|auto|allow-|source)")
 | 
				
			||||||
 | 
					var reIface = regexp.MustCompile("^iface (\\w+) inet (\\w+)")
 | 
				
			||||||
 | 
					var reStaticAddress = regexp.MustCompile("^(\\s*)address ([0-9.]+)")
 | 
				
			||||||
 | 
					var reStaticNetmask = regexp.MustCompile("^(\\s*)netmask ([0-9.]+)")
 | 
				
			||||||
 | 
					var reStaticGateway = regexp.MustCompile("^(\\s*)gateway ([0-9.]+)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NetworkInfo represents the actual network configuration
 | 
				
			||||||
 | 
					type NetworkInfo struct {
 | 
				
			||||||
 | 
						IsStatic bool
 | 
				
			||||||
 | 
						Address  string
 | 
				
			||||||
 | 
						Netmask  string
 | 
				
			||||||
 | 
						Gateway  string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetNetworkInfo from config file
 | 
				
			||||||
 | 
					func GetNetworkInfo() (*NetworkInfo, error) {
 | 
				
			||||||
 | 
						// load config file
 | 
				
			||||||
 | 
						file, err := os.Open(networkConfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to open network config: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer file.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var info *NetworkInfo
 | 
				
			||||||
 | 
						scanner := bufio.NewScanner(file)
 | 
				
			||||||
 | 
						for scanner.Scan() {
 | 
				
			||||||
 | 
							line := strings.TrimSpace(scanner.Text())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// check for section start
 | 
				
			||||||
 | 
							if reSectionStart.MatchString(line) {
 | 
				
			||||||
 | 
								infLine := reIface.FindStringSubmatch(line)
 | 
				
			||||||
 | 
								if infLine != nil {
 | 
				
			||||||
 | 
									// interface found
 | 
				
			||||||
 | 
									if infLine[1] == networkInterface {
 | 
				
			||||||
 | 
										info = &NetworkInfo{
 | 
				
			||||||
 | 
											IsStatic: infLine[2] == "static",
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									// info set -> stop here
 | 
				
			||||||
 | 
									if info != nil {
 | 
				
			||||||
 | 
										break
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							} else if info != nil {
 | 
				
			||||||
 | 
								// interface config
 | 
				
			||||||
 | 
								address := reStaticAddress.FindStringSubmatch(line)
 | 
				
			||||||
 | 
								if address != nil {
 | 
				
			||||||
 | 
									info.Address = address[2]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								netmask := reStaticNetmask.FindStringSubmatch(line)
 | 
				
			||||||
 | 
								if netmask != nil {
 | 
				
			||||||
 | 
									info.Netmask = netmask[2]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								gateway := reStaticGateway.FindStringSubmatch(line)
 | 
				
			||||||
 | 
								if gateway != nil {
 | 
				
			||||||
 | 
									info.Gateway = gateway[2]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if info != nil {
 | 
				
			||||||
 | 
							return info, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil, errors.New("invalid network config")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NetworkEnableDHCP configures the network to use DHCP client
 | 
				
			||||||
 | 
					func NetworkEnableDHCP() error {
 | 
				
			||||||
 | 
						// load config file
 | 
				
			||||||
 | 
						file, err := os.Open(networkConfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to open network config: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer file.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var buffer bytes.Buffer
 | 
				
			||||||
 | 
						var infFound bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scanner := bufio.NewScanner(file)
 | 
				
			||||||
 | 
						for scanner.Scan() {
 | 
				
			||||||
 | 
							line := strings.TrimSpace(scanner.Text())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// check for section start
 | 
				
			||||||
 | 
							if reSectionStart.MatchString(line) {
 | 
				
			||||||
 | 
								infLine := reIface.FindStringSubmatch(line)
 | 
				
			||||||
 | 
								if infLine != nil {
 | 
				
			||||||
 | 
									// interface found
 | 
				
			||||||
 | 
									if infLine[1] == networkInterface {
 | 
				
			||||||
 | 
										infFound = true
 | 
				
			||||||
 | 
										buffer.WriteString(fmt.Sprintf("iface %s inet dhcp\n", networkInterface))
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if infFound {
 | 
				
			||||||
 | 
										infFound = false
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								buffer.WriteString(line + "\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							} else if !infFound {
 | 
				
			||||||
 | 
								buffer.WriteString(line + "\n")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write config file
 | 
				
			||||||
 | 
						return ioutil.WriteFile(networkConfig, buffer.Bytes(), os.ModePerm)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NetworkSetStatic IP configuration
 | 
				
			||||||
 | 
					func NetworkSetStatic(address, netmask, gateway string) error {
 | 
				
			||||||
 | 
						// load config file
 | 
				
			||||||
 | 
						file, err := os.Open(networkConfig)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to open network config: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer file.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var buffer bytes.Buffer
 | 
				
			||||||
 | 
						var infFound bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scanner := bufio.NewScanner(file)
 | 
				
			||||||
 | 
						for scanner.Scan() {
 | 
				
			||||||
 | 
							line := strings.TrimSpace(scanner.Text())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// check for section start
 | 
				
			||||||
 | 
							if reSectionStart.MatchString(line) {
 | 
				
			||||||
 | 
								infLine := reIface.FindStringSubmatch(line)
 | 
				
			||||||
 | 
								if infLine != nil {
 | 
				
			||||||
 | 
									// interface found
 | 
				
			||||||
 | 
									if infLine[1] == networkInterface {
 | 
				
			||||||
 | 
										infFound = true
 | 
				
			||||||
 | 
										buffer.WriteString(fmt.Sprintf("iface %s inet static\n", networkInterface))
 | 
				
			||||||
 | 
										buffer.WriteString(fmt.Sprintf("  address %s\n", address))
 | 
				
			||||||
 | 
										buffer.WriteString(fmt.Sprintf("  netmask %s\n", netmask))
 | 
				
			||||||
 | 
										buffer.WriteString(fmt.Sprintf("  gateway %s\n", gateway))
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if infFound {
 | 
				
			||||||
 | 
										infFound = false
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								buffer.WriteString(line + "\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							} else if !infFound {
 | 
				
			||||||
 | 
								buffer.WriteString(line + "\n")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write config file
 | 
				
			||||||
 | 
						return ioutil.WriteFile(networkConfig, buffer.Bytes(), os.ModePerm)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										116
									
								
								network_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								network_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
				
			|||||||
 | 
					package alpine_builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						networkConfig = "test_interfaces"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestGetNetworkInfo(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// file does not exist
 | 
				
			||||||
 | 
						_, err := GetNetworkInfo()
 | 
				
			||||||
 | 
						ass.EqualError(err, "failed to open network config: open test_interfaces: no such file or directory")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// invalid config file
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(networkConfig, []byte(""), os.ModePerm))
 | 
				
			||||||
 | 
						_, err = GetNetworkInfo()
 | 
				
			||||||
 | 
						ass.EqualError(err, "invalid network config")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// dynamic config
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(networkConfig, []byte(`auto lo
 | 
				
			||||||
 | 
					iface lo inet loopback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto eth0
 | 
				
			||||||
 | 
					iface eth0 inet dhcp`), os.ModePerm))
 | 
				
			||||||
 | 
						info, err := GetNetworkInfo()
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
						ass.False(info.IsStatic)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// static config
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(networkConfig, []byte(`auto lo
 | 
				
			||||||
 | 
					iface lo inet loopback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto eth0
 | 
				
			||||||
 | 
					iface eth0 inet static
 | 
				
			||||||
 | 
					  address 1.2.3.4
 | 
				
			||||||
 | 
					  netmask 255.255.255.0
 | 
				
			||||||
 | 
					  gateway 4.3.2.1`), os.ModePerm))
 | 
				
			||||||
 | 
						info, err = GetNetworkInfo()
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
						ass.True(info.IsStatic)
 | 
				
			||||||
 | 
						ass.Equal("1.2.3.4", info.Address)
 | 
				
			||||||
 | 
						ass.Equal("255.255.255.0", info.Netmask)
 | 
				
			||||||
 | 
						ass.Equal("4.3.2.1", info.Gateway)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(networkConfig)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNetworkEnableDHCP(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// file does not exist
 | 
				
			||||||
 | 
						_, err := GetNetworkInfo()
 | 
				
			||||||
 | 
						ass.EqualError(err, "failed to open network config: open test_interfaces: no such file or directory")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(networkConfig, []byte(`auto lo
 | 
				
			||||||
 | 
					iface lo inet loopback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto eth0
 | 
				
			||||||
 | 
					iface eth0 inet static
 | 
				
			||||||
 | 
					  address 1.2.3.4
 | 
				
			||||||
 | 
					  netmask 255.255.255.0
 | 
				
			||||||
 | 
					  gateway 4.3.2.1`), os.ModePerm))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.NoError(NetworkEnableDHCP())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(networkConfig)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.Equal(`auto lo
 | 
				
			||||||
 | 
					iface lo inet loopback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto eth0
 | 
				
			||||||
 | 
					iface eth0 inet dhcp
 | 
				
			||||||
 | 
					`, string(data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(networkConfig)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestNetworkSetStatic(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// file does not exist
 | 
				
			||||||
 | 
						_, err := GetNetworkInfo()
 | 
				
			||||||
 | 
						ass.EqualError(err, "failed to open network config: open test_interfaces: no such file or directory")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(networkConfig, []byte(`auto lo
 | 
				
			||||||
 | 
					iface lo inet loopback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto eth0
 | 
				
			||||||
 | 
					iface eth0 inet dhcp`), os.ModePerm))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.NoError(NetworkSetStatic("1.2.3.4", "255.255.255.0", "4.3.2.1"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(networkConfig)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.Equal(`auto lo
 | 
				
			||||||
 | 
					iface lo inet loopback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					auto eth0
 | 
				
			||||||
 | 
					iface eth0 inet static
 | 
				
			||||||
 | 
					  address 1.2.3.4
 | 
				
			||||||
 | 
					  netmask 255.255.255.0
 | 
				
			||||||
 | 
					  gateway 4.3.2.1
 | 
				
			||||||
 | 
					`, string(data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(networkConfig)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -99,7 +99,7 @@ EOF
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# prepare network
 | 
					# prepare network
 | 
				
			||||||
chroot_exec rc-update add networking default
 | 
					chroot_exec rc-update add networking default
 | 
				
			||||||
ln -fs /data/etc/interfaces ${ROOTFS_PATH}/etc/network/interfaces
 | 
					ln -fs /data/etc/network/interfaces ${ROOTFS_PATH}/etc/network/interfaces
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# run local before network -> local brings up the interface
 | 
					# run local before network -> local brings up the interface
 | 
				
			||||||
sed -i '/^\tneed/ s/$/ local/' ${ROOTFS_PATH}/etc/init.d/networking
 | 
					sed -i '/^\tneed/ s/$/ local/' ${ROOTFS_PATH}/etc/init.d/networking
 | 
				
			||||||
@@ -200,8 +200,8 @@ root_pw=\$(mkpasswd -m sha-512 -s "${DEFAULT_ROOT_PASSWORD}")
 | 
				
			|||||||
echo "root:\${root_pw}:0:0:::::" > /data/etc/shadow
 | 
					echo "root:\${root_pw}:0:0:::::" > /data/etc/shadow
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# interface
 | 
					# interface
 | 
				
			||||||
if [ ! -f /data/etc/interfaces ]; then
 | 
					if [ ! -f /data/etc/network/interfaces ]; then
 | 
				
			||||||
cat > /data/etc/interfaces <<EOF2
 | 
					cat > /data/etc/network/interfaces <<EOF2
 | 
				
			||||||
auto lo
 | 
					auto lo
 | 
				
			||||||
iface lo inet loopback
 | 
					iface lo inet loopback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
 #!/bin/bash
 | 
					#!/bin/bash
 | 
				
			||||||
set -e
 | 
					set -e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# get current partition index
 | 
					# get current partition index
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
 #!/bin/bash
 | 
					#!/bin/bash
 | 
				
			||||||
set -e
 | 
					set -e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
image_file=$1
 | 
					image_file=$1
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -71,7 +71,7 @@ int main(int argc, char* argv[]) {
 | 
				
			|||||||
        data[1] = 0;
 | 
					        data[1] = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // boot partition
 | 
					        // boot partition
 | 
				
			||||||
        data[2] = 2; // A=2, B=3
 | 
					        data[2] = 2; // A=1, B=2
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // handle commands
 | 
					    // handle commands
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										86
									
								
								system.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								system.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					package alpine_builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GehirnInc/crypt"
 | 
				
			||||||
 | 
						_ "github.com/GehirnInc/crypt/sha256_crypt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// system configurations
 | 
				
			||||||
 | 
					var systemShadow = "/data/etc/shadow"
 | 
				
			||||||
 | 
					var systemDropbearConfig = "/data/etc/dropbear/dropbear.conf"
 | 
				
			||||||
 | 
					var systemDropbearRestart = "rc-service dropbear start"
 | 
				
			||||||
 | 
					var systemShutdown = "poweroff"
 | 
				
			||||||
 | 
					var systemReboot = "reboot"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SystemSetRootPassword update shadow file
 | 
				
			||||||
 | 
					func SystemSetRootPassword(password string) error {
 | 
				
			||||||
 | 
						crypter := crypt.SHA256.New()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// generate password line
 | 
				
			||||||
 | 
						hash, err := crypter.Generate([]byte(password), nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to generate hash: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						line := fmt.Sprintf("root:%s:0:0:::::", hash)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write shadow file
 | 
				
			||||||
 | 
						return ioutil.WriteFile(systemShadow, []byte(line), os.ModePerm)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SystemEnableSSH server
 | 
				
			||||||
 | 
					func SystemEnableSSH() error {
 | 
				
			||||||
 | 
						err := ioutil.WriteFile(systemDropbearConfig, []byte("DROPBEAR_OPTS=\"\""), os.ModePerm)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to write ssh config: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmdSplit := strings.Split(systemDropbearRestart, " ")
 | 
				
			||||||
 | 
						cmd := exec.Command(cmdSplit[0], cmdSplit[1:]...)
 | 
				
			||||||
 | 
						err = cmd.Run()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to restart ssh server: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SystemDisableSSH server
 | 
				
			||||||
 | 
					func SystemDisableSSH() error {
 | 
				
			||||||
 | 
						err := ioutil.WriteFile(systemDropbearConfig, []byte("DROPBEAR_OPTS=\"-p 127.0.0.1:22\""), os.ModePerm)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to write ssh config: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cmdSplit := strings.Split(systemDropbearRestart, " ")
 | 
				
			||||||
 | 
						cmd := exec.Command(cmdSplit[0], cmdSplit[1:]...)
 | 
				
			||||||
 | 
						err = cmd.Run()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to restart ssh server: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SystemShutdown start shutdown of system
 | 
				
			||||||
 | 
					func SystemShutdown() error {
 | 
				
			||||||
 | 
						cmd := exec.Command(systemShutdown)
 | 
				
			||||||
 | 
						err := cmd.Run()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to start system shutdown: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SystemReboot start reboot of system
 | 
				
			||||||
 | 
					func SystemReboot() error {
 | 
				
			||||||
 | 
						cmd := exec.Command(systemReboot)
 | 
				
			||||||
 | 
						err := cmd.Run()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to start system reboot: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										75
									
								
								system_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								system_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					package alpine_builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/GehirnInc/crypt"
 | 
				
			||||||
 | 
						_ "github.com/GehirnInc/crypt/sha256_crypt"
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						systemShadow = "test_shadow"
 | 
				
			||||||
 | 
						systemDropbearConfig = "test_dropbear"
 | 
				
			||||||
 | 
						systemDropbearRestart = "restart_dropbear_command"
 | 
				
			||||||
 | 
						systemReboot = "reboot_command"
 | 
				
			||||||
 | 
						systemShutdown = "shutdown_command"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSystemSetRootPassword(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.NoError(SystemSetRootPassword("password"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(systemShadow)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
						hash := strings.Split(string(data), ":")[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						crypter := crypt.SHA256.New()
 | 
				
			||||||
 | 
						ass.NoError(crypter.Verify(hash, []byte("password")))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(systemShadow)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSystemEnableSSH(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.EqualError(SystemEnableSSH(), "failed to restart ssh server: exec: \"restart_dropbear_command\": executable file not found in $PATH")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(systemDropbearConfig)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.Equal("DROPBEAR_OPTS=\"\"", string(data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(systemDropbearConfig)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSystemDisableSSH(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.EqualError(SystemDisableSSH(), "failed to restart ssh server: exec: \"restart_dropbear_command\": executable file not found in $PATH")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(systemDropbearConfig)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.Equal("DROPBEAR_OPTS=\"-p 127.0.0.1:22\"", string(data))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(systemDropbearConfig)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSystemReboot(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.EqualError(SystemReboot(),
 | 
				
			||||||
 | 
							"failed to start system reboot: exec: \"reboot_command\": executable file not found in $PATH")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSystemShutdown(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.EqualError(SystemShutdown(),
 | 
				
			||||||
 | 
							"failed to start system shutdown: exec: \"shutdown_command\": executable file not found in $PATH")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										143
									
								
								update.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								update.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
				
			|||||||
 | 
					package alpine_builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"compress/gzip"
 | 
				
			||||||
 | 
						"encoding/binary"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"hash/crc32"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var ubootFile = "/uboot/uboot.dat"
 | 
				
			||||||
 | 
					var ubootRemountRW = "mount -o remount,rw /uboot"
 | 
				
			||||||
 | 
					var ubootRemountRO = "mount -o remount,ro /uboot"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var rootPartitionA = "/dev/mmcblk0p1"
 | 
				
			||||||
 | 
					var rootPartitionB = "/dev/mmcblk0p2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// loadUbootDat file to byte array
 | 
				
			||||||
 | 
					func loadUbootDat() ([]byte, error) {
 | 
				
			||||||
 | 
						defaultData := make([]byte, 1024)
 | 
				
			||||||
 | 
						defaultData[0] = 1 // file version
 | 
				
			||||||
 | 
						defaultData[1] = 0 // boot counter
 | 
				
			||||||
 | 
						defaultData[2] = 1 // boot partition  A=1, B=2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(ubootFile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return defaultData, fmt.Errorf("failed to open file: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// invalid dat file?
 | 
				
			||||||
 | 
						if len(data) < 1024 {
 | 
				
			||||||
 | 
							return defaultData, errors.New("invalid dat file -> fallback to defaults")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						crc := binary.LittleEndian.Uint32(data[1020:])
 | 
				
			||||||
 | 
						bla := crc32.ChecksumIEEE(data[:1020])
 | 
				
			||||||
 | 
						if crc != bla {
 | 
				
			||||||
 | 
							return defaultData, errors.New("invalid crc -> fallback to defaults")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return data, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// saveUbootDat from byte array
 | 
				
			||||||
 | 
					func saveUbootDat(data []byte) error {
 | 
				
			||||||
 | 
						// update crc
 | 
				
			||||||
 | 
						binary.LittleEndian.PutUint32(data[1020:],
 | 
				
			||||||
 | 
							crc32.ChecksumIEEE(data[:1020]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// remount boot partition - writable
 | 
				
			||||||
 | 
						cmd := exec.Command(ubootRemountRW)
 | 
				
			||||||
 | 
						err := cmd.Run()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to remount RW: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// update uboot dat file
 | 
				
			||||||
 | 
						err = ioutil.WriteFile(ubootFile, data[:1024], os.ModePerm)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed write uboot dat: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// remount boot partition - readonly
 | 
				
			||||||
 | 
						cmd = exec.Command(ubootRemountRO)
 | 
				
			||||||
 | 
						err = cmd.Run()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("failed to remount RO: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UBootResetCounter to 0
 | 
				
			||||||
 | 
					func UBootResetCounter() error {
 | 
				
			||||||
 | 
						data, _ := loadUbootDat()
 | 
				
			||||||
 | 
						data[1] = 0
 | 
				
			||||||
 | 
						return saveUbootDat(data)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UBootActive returns the active partition. A=1, B=2
 | 
				
			||||||
 | 
					func UBootActive() uint8 {
 | 
				
			||||||
 | 
						data, _ := loadUbootDat()
 | 
				
			||||||
 | 
						return data[2]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UBootSetActive sets the active partition. A=1, B=2
 | 
				
			||||||
 | 
					func UBootSetActive(active uint8) error {
 | 
				
			||||||
 | 
						data, _ := loadUbootDat()
 | 
				
			||||||
 | 
						if active == 1 {
 | 
				
			||||||
 | 
							data[2] = 1
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							data[2] = 2
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return saveUbootDat(data)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UpdateSystem with the given image
 | 
				
			||||||
 | 
					func UpdateSystem(image string) error {
 | 
				
			||||||
 | 
						data, _ := loadUbootDat()
 | 
				
			||||||
 | 
						rootPart := rootPartitionA
 | 
				
			||||||
 | 
						if data[2] == 1 {
 | 
				
			||||||
 | 
							rootPart = rootPartitionB
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// open image file
 | 
				
			||||||
 | 
						inFile, err := os.Open(image)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer inFile.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// decompress image
 | 
				
			||||||
 | 
						inDecompress, err := gzip.NewReader(inFile)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer inDecompress.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// open root partition
 | 
				
			||||||
 | 
						out, err := os.OpenFile(rootPart,
 | 
				
			||||||
 | 
							os.O_WRONLY|os.O_TRUNC|os.O_SYNC, os.ModePerm)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer out.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write update
 | 
				
			||||||
 | 
						_, err = io.Copy(out, inDecompress)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// switch active partition
 | 
				
			||||||
 | 
						if data[2] == 1 {
 | 
				
			||||||
 | 
							return UBootSetActive(2)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return UBootSetActive(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										179
									
								
								update_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								update_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,179 @@
 | 
				
			|||||||
 | 
					package alpine_builder
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"compress/gzip"
 | 
				
			||||||
 | 
						"crypto/rand"
 | 
				
			||||||
 | 
						"encoding/binary"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						ubootFile = "test_uboot"
 | 
				
			||||||
 | 
						ubootRemountRW = "true"
 | 
				
			||||||
 | 
						ubootRemountRO = "true"
 | 
				
			||||||
 | 
						rootPartitionA = "test_rootA"
 | 
				
			||||||
 | 
						rootPartitionB = "test_rootB"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestLoadUbootDat(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// file missing
 | 
				
			||||||
 | 
						data, err := loadUbootDat()
 | 
				
			||||||
 | 
						ass.EqualError(err, "failed to open file: open test_uboot: no such file or directory")
 | 
				
			||||||
 | 
						ass.Equal([]byte{1, 0, 1}, data[:3])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// file with invalid data
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(ubootFile, []byte{}, os.ModePerm))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err = loadUbootDat()
 | 
				
			||||||
 | 
						ass.EqualError(err, "invalid dat file -> fallback to defaults")
 | 
				
			||||||
 | 
						ass.Equal([]byte{1, 0, 1}, data[:3])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// file with invalid CRC
 | 
				
			||||||
 | 
						testData := make([]byte, 1024)
 | 
				
			||||||
 | 
						testData[0] = 1
 | 
				
			||||||
 | 
						testData[1] = 2
 | 
				
			||||||
 | 
						testData[2] = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
				
			||||||
 | 
						data, err = loadUbootDat()
 | 
				
			||||||
 | 
						ass.EqualError(err, "invalid crc -> fallback to defaults")
 | 
				
			||||||
 | 
						ass.Equal([]byte{1, 0, 1}, data[:3])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// file with valid CRC
 | 
				
			||||||
 | 
						binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
				
			||||||
 | 
						data, err = loadUbootDat()
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
						ass.Equal(testData, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(ubootFile)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSaveUbootDat(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testData := make([]byte, 1024)
 | 
				
			||||||
 | 
						testData[0] = 1
 | 
				
			||||||
 | 
						testData[1] = 2
 | 
				
			||||||
 | 
						testData[2] = 2
 | 
				
			||||||
 | 
						ass.NoError(saveUbootDat(testData))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(ubootFile)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
						ass.Equal(testData, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(ubootFile)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUBootResetCounter(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write test file
 | 
				
			||||||
 | 
						testData := make([]byte, 1024)
 | 
				
			||||||
 | 
						testData[0] = 1
 | 
				
			||||||
 | 
						testData[1] = 2
 | 
				
			||||||
 | 
						testData[2] = 2
 | 
				
			||||||
 | 
						binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.NoError(UBootResetCounter())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(ubootFile)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
						ass.Zero(data[1])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(ubootFile)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUBootActive(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write test file
 | 
				
			||||||
 | 
						testData := make([]byte, 1024)
 | 
				
			||||||
 | 
						testData[0] = 1
 | 
				
			||||||
 | 
						testData[1] = 2
 | 
				
			||||||
 | 
						testData[2] = 2
 | 
				
			||||||
 | 
						binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.Equal(uint8(2), UBootActive())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(ubootFile)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUBootSetActive(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write test file
 | 
				
			||||||
 | 
						testData := make([]byte, 1024)
 | 
				
			||||||
 | 
						testData[0] = 1
 | 
				
			||||||
 | 
						testData[1] = 2
 | 
				
			||||||
 | 
						testData[2] = 2
 | 
				
			||||||
 | 
						binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.NoError(UBootSetActive(1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(ubootFile)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
						ass.Equal(uint8(1), data[2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove(ubootFile)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestUpdateSystem(t *testing.T) {
 | 
				
			||||||
 | 
						ass := assert.New(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// test uboot file
 | 
				
			||||||
 | 
						testData := make([]byte, 1024)
 | 
				
			||||||
 | 
						testData[0] = 1
 | 
				
			||||||
 | 
						testData[1] = 2
 | 
				
			||||||
 | 
						testData[2] = 2
 | 
				
			||||||
 | 
						binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// generate test image content
 | 
				
			||||||
 | 
						size := int64(1024 * 1024 * 5)
 | 
				
			||||||
 | 
						testImgData := make([]byte, size)
 | 
				
			||||||
 | 
						buffer := bytes.NewBuffer(testImgData)
 | 
				
			||||||
 | 
						_, err := io.CopyN(buffer, rand.Reader, size)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// write compressed image file
 | 
				
			||||||
 | 
						file, err := os.Create("test_image.gz")
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
						gzipWriter := gzip.NewWriter(file)
 | 
				
			||||||
 | 
						_, err = gzipWriter.Write(testImgData)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
						ass.NoError(gzipWriter.Close())
 | 
				
			||||||
 | 
						ass.NoError(file.Close())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ass.NoError(ioutil.WriteFile(rootPartitionA, nil, os.ModePerm))
 | 
				
			||||||
 | 
						ass.NoError(UpdateSystem("test_image.gz"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check if image was written
 | 
				
			||||||
 | 
						data, err := ioutil.ReadFile(rootPartitionA)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
						ass.Equal(testImgData, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check if uboot dat was updated
 | 
				
			||||||
 | 
						data, err = ioutil.ReadFile(ubootFile)
 | 
				
			||||||
 | 
						ass.NoError(err)
 | 
				
			||||||
 | 
						ass.Equal(uint8(1), data[2])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = os.Remove("test_image.gz")
 | 
				
			||||||
 | 
						_ = os.Remove(ubootFile)
 | 
				
			||||||
 | 
						_ = os.Remove(rootPartitionA)
 | 
				
			||||||
 | 
						_ = os.Remove(rootPartitionB)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user