Update App version to 0.0.2

This commit is contained in:
luckfox-eng29
2025-09-16 11:03:46 +08:00
parent 8fbd6bcf0d
commit 15d276652c
45 changed files with 3347 additions and 252 deletions

View File

@@ -36,8 +36,9 @@ type NetworkConfig struct {
Hostname null.String `json:"hostname,omitempty" validate_type:"hostname"`
Domain null.String `json:"domain,omitempty" validate_type:"hostname"`
IPv4Mode null.String `json:"ipv4_mode,omitempty" one_of:"dhcp,static,disabled" default:"dhcp"`
IPv4Static *IPv4StaticConfig `json:"ipv4_static,omitempty" required_if:"IPv4Mode=static"`
IPv4Mode null.String `json:"ipv4_mode,omitempty" one_of:"dhcp,static,disabled" default:"dhcp"`
IPv4RequestAddress null.String `json:"ipv4_request_address,omitempty"`
IPv4Static *IPv4StaticConfig `json:"ipv4_static,omitempty" required_if:"IPv4Mode=static"`
IPv6Mode null.String `json:"ipv6_mode,omitempty" one_of:"slaac,dhcpv6,slaac_and_dhcpv6,static,link_local,disabled" default:"slaac"`
IPv6Static *IPv6StaticConfig `json:"ipv6_static,omitempty" required_if:"IPv6Mode=static"`

View File

@@ -95,6 +95,7 @@ func NewNetworkInterfaceState(opts *NetworkInterfaceOptions) (*NetworkInterfaceS
opts.OnDhcpLeaseChange(lease)
},
RequestAddress: s.config.IPv4RequestAddress.String,
})
s.dhcpClient = dhcpClient

View File

@@ -3,10 +3,13 @@ package udhcpc
import (
"errors"
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"reflect"
"sync"
"syscall"
"time"
"github.com/fsnotify/fsnotify"
@@ -21,20 +24,22 @@ const (
)
type DHCPClient struct {
InterfaceName string
leaseFile string
pidFile string
lease *Lease
logger *zerolog.Logger
process *os.Process
onLeaseChange func(lease *Lease)
InterfaceName string
leaseFile string
pidFile string
requestAddress string
lease *Lease
logger *zerolog.Logger
process *os.Process
onLeaseChange func(lease *Lease)
}
type DHCPClientOptions struct {
InterfaceName string
PidFile string
Logger *zerolog.Logger
OnLeaseChange func(lease *Lease)
InterfaceName string
PidFile string
Logger *zerolog.Logger
OnLeaseChange func(lease *Lease)
RequestAddress string
}
var defaultLogger = zerolog.New(os.Stdout).Level(zerolog.InfoLevel)
@@ -46,11 +51,12 @@ func NewDHCPClient(options *DHCPClientOptions) *DHCPClient {
l := options.Logger.With().Str("interface", options.InterfaceName).Logger()
return &DHCPClient{
InterfaceName: options.InterfaceName,
logger: &l,
leaseFile: fmt.Sprintf(DHCPLeaseFile, options.InterfaceName),
pidFile: options.PidFile,
onLeaseChange: options.OnLeaseChange,
InterfaceName: options.InterfaceName,
logger: &l,
leaseFile: fmt.Sprintf(DHCPLeaseFile, options.InterfaceName),
pidFile: options.PidFile,
onLeaseChange: options.OnLeaseChange,
requestAddress: options.RequestAddress,
}
}
@@ -80,8 +86,8 @@ func (c *DHCPClient) watchLink() {
for update := range ch {
if update.Link.Attrs().Name == c.InterfaceName {
if update.IfInfomsg.Flags&unix.IFF_RUNNING != 0 {
fmt.Printf("[watchLink]link is up, starting udhcpc")
if update.Flags&unix.IFF_RUNNING != 0 {
c.logger.Info().Msg("link is up, starting udhcpc")
go c.runUDHCPC()
} else {
c.logger.Info().Msg("link is down")
@@ -90,10 +96,45 @@ func (c *DHCPClient) watchLink() {
}
}
type udhcpcOutput struct {
mu *sync.Mutex
logger *zerolog.Event
}
func (w *udhcpcOutput) Write(p []byte) (n int, err error) {
w.mu.Lock()
defer w.mu.Unlock()
w.logger.Msg(string(p))
return len(p), nil
}
func (c *DHCPClient) runUDHCPC() {
cmd := exec.Command("udhcpc", "-i", c.InterfaceName, "-t", "1")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if c.requestAddress != "" {
ip := net.ParseIP(c.requestAddress)
if ip != nil && ip.To4() != nil {
cmd.Args = append(cmd.Args, "-r", c.requestAddress)
}
}
udhcpcOutputLock := sync.Mutex{}
udhcpcStdout := &udhcpcOutput{
mu: &udhcpcOutputLock,
logger: c.logger.Debug().Str("pipe", "stdout"),
}
udhcpcStderr := &udhcpcOutput{
mu: &udhcpcOutputLock,
logger: c.logger.Debug().Str("pipe", "stderr"),
}
cmd.Stdout = udhcpcStdout
cmd.Stderr = udhcpcStderr
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
Pdeathsig: syscall.SIGKILL,
}
if err := cmd.Run(); err != nil {
c.logger.Error().Err(err).Msg("failed to run udhcpc")
}
@@ -113,6 +154,7 @@ func (c *DHCPClient) Run() error {
}
defer watcher.Close()
go c.runUDHCPC()
go c.watchLink()
go func() {

View File

@@ -34,6 +34,7 @@ const (
FileStateFileWrite // update file content without checking
FileStateMounted
FileStateMountedConfigFS
FileStateMountedFunctionFS
FileStateSymlink
FileStateSymlinkInOrderConfigFS // configfs is a shithole, so we need to check if the symlinks are created in the correct order
FileStateSymlinkNotInOrderConfigFS
@@ -52,6 +53,7 @@ var FileStateString = map[FileState]string{
FileStateSymlink: "SYMLINK",
FileStateSymlinkInOrderConfigFS: "SYMLINK_IN_ORDER_CONFIGFS",
FileStateTouch: "TOUCH",
FileStateMountedFunctionFS: "FUNCTIONFS_MOUNTED",
}
const (
@@ -78,6 +80,7 @@ const (
FileChangeResolvedActionRemoveDirectory
FileChangeResolvedActionTouch
FileChangeResolvedActionMountConfigFS
FileChangeResolvedActionMountFunctionFS
)
var FileChangeResolvedActionString = map[FileChangeResolvedAction]string{
@@ -96,6 +99,7 @@ var FileChangeResolvedActionString = map[FileChangeResolvedAction]string{
FileChangeResolvedActionRemoveDirectory: "DIR_REMOVE",
FileChangeResolvedActionTouch: "TOUCH",
FileChangeResolvedActionMountConfigFS: "CONFIGFS_MOUNT",
FileChangeResolvedActionMountFunctionFS: "FUNCTIONFS_MOUNT",
}
type ChangeSet struct {
@@ -147,6 +151,8 @@ func (f *RequestedFileChange) String() string {
s = fmt.Sprintf("write: %s with content [%s]", f.Path, f.ExpectedContent)
case FileStateMountedConfigFS:
s = fmt.Sprintf("configfs: %s", f.Path)
case FileStateMountedFunctionFS:
s = fmt.Sprintf("functionfs: %s", f.Path)
case FileStateTouch:
s = fmt.Sprintf("touch: %s", f.Path)
case FileStateUnknown:
@@ -298,6 +304,8 @@ func (fc *FileChange) getFileChangeResolvedAction() FileChangeResolvedAction {
return FileChangeResolvedActionWriteFile
case FileStateTouch:
return FileChangeResolvedActionTouch
case FileStateMountedFunctionFS:
return FileChangeResolvedActionMountFunctionFS
}
// get the actual state of the file
@@ -424,6 +432,8 @@ func (c *ChangeSet) applyChange(change *FileChange) error {
return os.Chtimes(change.Path, time.Now(), time.Now())
case FileChangeResolvedActionMountConfigFS:
return mountConfigFS(change.Path)
case FileChangeResolvedActionMountFunctionFS:
return mountFunctionFS(change.Path)
case FileChangeResolvedActionDoNothing:
return nil
default:

View File

@@ -2,6 +2,7 @@ package usbgadget
import (
"fmt"
"time"
"github.com/rs/zerolog"
"github.com/sourcegraph/tf-dag/dag"
@@ -127,6 +128,11 @@ func (c *ChangeSetResolver) applyChanges() error {
l.Str("action", actionStr).Str("change", change.String()).Msg("applying change")
err := c.changeset.applyChange(change)
if err != nil {
time.Sleep(500 * time.Millisecond)
err = c.changeset.applyChange(change)
}
if err != nil {
if change.IgnoreErrors {
c.l.Warn().Str("change", change.String()).Err(err).Msg("ignoring error")

View File

@@ -1,8 +1,11 @@
package usbgadget
import (
"bufio"
"fmt"
"os"
"os/exec"
"strings"
)
type gadgetConfigItem struct {
@@ -51,6 +54,8 @@ var defaultGadgetConfig = map[string]gadgetConfigItem{
"configuration": "Config 1: HID",
},
},
// mtp
"mtp": mtpConfig,
// keyboard HID
"keyboard": keyboardConfig,
// mouse HID
@@ -91,6 +96,8 @@ func (u *UsbGadget) isGadgetConfigItemEnabled(itemKey string) bool {
return u.enabledDevices.MassStorage
case "mass_storage_lun0":
return u.enabledDevices.MassStorage
case "mtp":
return u.enabledDevices.Mtp
case "audio":
return u.enabledDevices.Audio
default:
@@ -184,6 +191,45 @@ func mountConfigFS(path string) error {
return nil
}
func mountFunctionFS(path string) error {
err := os.MkdirAll("/dev/ffs-mtp", 0755)
if err != nil {
return fmt.Errorf("failed to create mtp dev dir: %w", err)
}
mounted := false
if f, err := os.Open("/proc/mounts"); err == nil {
scanner := bufio.NewScanner(f)
for scanner.Scan() {
if strings.Contains(scanner.Text(), functionFSPath) {
mounted = true
break
}
}
f.Close()
}
if !mounted {
err := exec.Command("mount", "-t", "functionfs", "mtp", path).Run()
if err != nil {
return fmt.Errorf("failed to mount functionfs: %w", err)
}
}
umtprdRunning := false
if out, err := exec.Command("pgrep", "-x", "umtprd").Output(); err == nil && len(out) > 0 {
umtprdRunning = true
}
if !umtprdRunning {
cmd := exec.Command("umtprd")
if err := cmd.Start(); err != nil {
return fmt.Errorf("failed to exec binary: %w", err)
}
}
return nil
}
func (u *UsbGadget) Init() error {
u.configLock.Lock()
defer u.configLock.Unlock()

View File

@@ -131,6 +131,16 @@ func (tx *UsbGadgetTransaction) MountConfigFS() {
})
}
func (tx *UsbGadgetTransaction) MountFunctionFS() {
tx.addFileChange("mtp", RequestedFileChange{
Path: functionFSPath,
Key: "mtp",
ExpectedState: FileStateMountedFunctionFS,
Description: "mount functionfs",
DependsOn: []string{"reorder-symlinks"},
})
}
func (tx *UsbGadgetTransaction) CreateConfigPath() {
tx.mkdirAll(
"gadget",
@@ -164,7 +174,12 @@ func (tx *UsbGadgetTransaction) WriteGadgetConfig() {
deps = tx.writeGadgetItemConfig(item, deps)
}
tx.WriteUDC()
if tx.isGadgetConfigItemEnabled("mtp") {
tx.MountFunctionFS()
tx.WriteUDC(true)
} else {
tx.WriteUDC(false)
}
}
func (tx *UsbGadgetTransaction) getDisableKeys() []string {
@@ -315,17 +330,28 @@ func (tx *UsbGadgetTransaction) addReorderSymlinkChange(path string, target stri
})
}
func (tx *UsbGadgetTransaction) WriteUDC() {
func (tx *UsbGadgetTransaction) WriteUDC(mtpServer bool) {
// bound the gadget to a UDC (USB Device Controller)
path := path.Join(tx.kvmGadgetPath, "UDC")
tx.addFileChange("udc", RequestedFileChange{
Key: "udc",
Path: path,
ExpectedState: FileStateFileContentMatch,
ExpectedContent: []byte(tx.udc),
DependsOn: []string{"reorder-symlinks"},
Description: "write UDC",
})
if mtpServer {
tx.addFileChange("udc", RequestedFileChange{
Key: "udc",
Path: path,
ExpectedState: FileStateFileContentMatch,
ExpectedContent: []byte(tx.udc),
DependsOn: []string{"mtp"},
Description: "write UDC",
})
} else {
tx.addFileChange("udc", RequestedFileChange{
Key: "udc",
Path: path,
ExpectedState: FileStateFileContentMatch,
ExpectedContent: []byte(tx.udc),
DependsOn: []string{"reorder-symlinks"},
Description: "write UDC",
})
}
}
func (tx *UsbGadgetTransaction) RebindUsb(ignoreUnbindError bool) {

View File

@@ -1,3 +1,4 @@
package usbgadget
const dwc3Path = "/sys/bus/platform/drivers/dwc3"
const udcPath = "/sys/kernel/config/usb_gadget/kvm/UDC"

View File

@@ -23,3 +23,10 @@ var massStorageLun0Config = gadgetConfigItem{
"inquiry_string": "KVM Virtual Media",
},
}
var mtpConfig = gadgetConfigItem{
order: 3003,
device: "ffs.mtp",
path: []string{"functions", "ffs.mtp"},
configPath: []string{"ffs.mtp"},
}

View File

@@ -80,7 +80,7 @@ func (u *UsbGadget) IsUDCBound() (bool, error) {
// BindUDC binds the gadget to the UDC.
func (u *UsbGadget) BindUDC() error {
err := os.WriteFile(path.Join(dwc3Path, "bind"), []byte(u.udc), 0644)
err := os.WriteFile(path.Join(udcPath, "bind"), []byte(u.udc), 0644)
if err != nil {
return fmt.Errorf("error binding UDC: %w", err)
}
@@ -89,7 +89,7 @@ func (u *UsbGadget) BindUDC() error {
// UnbindUDC unbinds the gadget from the UDC.
func (u *UsbGadget) UnbindUDC() error {
err := os.WriteFile(path.Join(dwc3Path, "unbind"), []byte(u.udc), 0644)
err := os.WriteFile(path.Join(udcPath, " "), []byte(u.udc), 0644)
if err != nil {
return fmt.Errorf("error unbinding UDC: %w", err)
}

View File

@@ -20,6 +20,7 @@ type Devices struct {
RelativeMouse bool `json:"relative_mouse"`
Keyboard bool `json:"keyboard"`
MassStorage bool `json:"mass_storage"`
Mtp bool `json:"mtp"`
Audio bool `json:"audio"`
}
@@ -88,6 +89,9 @@ type UsbGadget struct {
const configFSPath = "/sys/kernel/config"
const gadgetPath = "/sys/kernel/config/usb_gadget"
const functionFSPath = "/dev/ffs-mtp"
const umtprdPath = "/usr/sbin/umtprd"
var defaultLogger = logging.GetSubsystemLogger("usbgadget")
// NewUsbGadget creates a new UsbGadget.