uci

package module
v0.0.0-...-969b542 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 3, 2026 License: MIT Imports: 10 Imported by: 0

README

go-uci

WORK IN PROGRESS

You're looking at the pre-release documentation for the next major v2 version.

Some things aren't properly flushed out yet and may break at any moment. Use at your own peril.

UCI is OpenWrt's Unified Configuration Interface. It is used to configure OpenWrt router hardware using a simple DSL (and acompanying CLI tools). Configuration files are written into a central directory (/etc/config/*) which basically represents a key/value store.

This project makes it easy to interact with such a config tree by providing a native interface to that KV store. It has no external runtime dependencies.

For now, we only implements a superset of the actual UCI DSL, but improvements (patches or PRs) are very welcome. Refer to Rob Pike's Lexical Scanning in Go for implementation details on the parser/lexer.

Why?

We're currently experimenting with Go binaries on OpenWrt router hardware and need a way to interact with the system configuration. We could have created bindings for libuci, but the turnaround cycle in developing with CGO is a bit tedious. Also, since Go does not compile for our target platforms, we need to resort to GCCGO, which has other quirks.

The easiest solution therefore is a plain Go library, which can be used in Go (with or without CGO) and GCCGO without worrying about interoperability. A library also allows UCI to be used outside of OpenWrt systems (e.g. for provisioning).

Usage

TODO: update example

import "github.com/digineo/go-uci"

func main() {
    // use the default tree (/etc/config)
    if values, ok := uci.Get("system", "@system[0]", "hostname"); ok {
        fmt.Println("hostanme", values)
        //=> hostname [OpenWrt]
    }

    // use a custom tree
    u := uci.NewTree("/path/to/config")
    if values, ok := u.Get("network", "lan", "ifname"); ok {
        fmt.Println("network.lan.ifname", values)
        //=> network.lan.ifname [eth0.2]
    }
    if sectionExists := u.Set("network", "lan", "ipaddr", "192.168.7.1"); !sectionExists {
        _ = u.AddSection("network", "lan", "interface")
        _ = u.Set("network", "lan", "ipaddr", "192.168.7.1")
    }
    u.Commit() // or uci.Revert()
}

See [API documentation][godoc] for more details.

Contributing

Pull requests are welcome, especially if they increase test coverage.

Before submitting changes, please make sure the tests still pass:

$ go test github.com/digineo/go-uci/...

License

MIT License. Copyright (c) 2019 Dominik Menke, Digineo GmbH

https://www.digineo.de

See LICENSE file for details.

Documentation

Overview

Package uci implements a binding to OpenWrt's UCI (Unified Configuration Interface) files in pure Go.

The typical use case is reading and modifying UCI config options:

import "github.com/digineo/go-uci"

uci.Get("network", "lan", "ifname") //=> []string{"eth0.1"}, true
uci.SetType("network", "lan", uci.TypeOption, "ipaddr", "192.168.7.1")
uci.Commit() // or uci.Revert()

For more details head over to the OpenWrt wiki, or dive into UCI's C source code:

The lexer is heavily inspired by Rob Pike's 2011 GTUG Sydney talk "Lexical Scanning in Go" (https://talks.golang.org/2011/lex.slide, https://youtu.be/HxaD_trXwRE), which in turn was a presentation of an early version of Go's text/template parser. It follows, that this library borrows code from Go's standard library (BSD-style licensed).

The UCI grammar (for the purpose of this library) is defined as follows:

uci
		packageDecl*
		configDecl*

packageDecl
		`package` value CRLF configDecl*

configDecl
		`config` ident value? CRLF optionDecl*

optionDecl
		`option` ident value
		`list` ident value

ident
		[_a-zA-Z0-9]+

value
		`'` STRING `'`
		`"` STRING `"`
		ident

For now, UCI imports/exports (packageDecl production) are not supported yet. The STRING token (value production) is also somewhat vaguely defined, and needs to be aligned with the actual C implementation.

Index

Constants

View Source
const DefaultTreePath = "/etc/config"

DefaultTreePath points to the default UCI location.

Variables

View Source
var (
	ErrImplausibleSectionSelector = errors.New("implausible section selector: must be at least 5 characters long")
	ErrMustStartWithAt            = errors.New("invalid syntax: section selector must start with @ sign")
	ErrMultipleAtSigns            = errors.New("invalid syntax: multiple @ signs found")
	ErrMultipleOpenBrackets       = errors.New("invalid syntax: multiple open brackets found")
	ErrMultipleCloseBrackets      = errors.New("invalid syntax: multiple closed brackets found")
	ErrInvalidSectionSelector     = errors.New("invalid syntax: section selector must have format '@type[index]'")
)
View Source
var ErrUnnamedIndexOutOfBounds = errors.New("invalid name: index out of bounds")

Functions

func AddSection

func AddSection(config, section, typ string) error

AddSection delegates to the default tree. See Tree for details.

func Commit

func Commit() error

Commit delegates to the default tree. See Tree for details.

func Del

func Del(config, section, option string) error

Del delegates to the default tree. See Tree for details.

func DelSection

func DelSection(config, section string) error

DelSection delegates to the default tree. See Tree for details.

func Get

func Get(config, section, option string) ([]string, bool)

Get delegates to the default tree. See Tree for details.

func GetBool

func GetBool(config, section, option string) (bool, bool)

GetBool delegates to the default tree. See Tree for details.

func GetLast

func GetLast(config, section, option string) (string, bool)

GetLast delegates to the default tree. See Tree for details.

func GetSections

func GetSections(config, secType string) ([]string, error)

GetSections delegates to the default tree. See Tree for details.

func LoadConfig

func LoadConfig(name string, forceReload bool) error

LoadConfig delegates to the default tree. See Tree for details.

func Revert

func Revert(configs ...string)

Revert delegates to the default tree. See Tree for details.

Types

type ErrConfigAlreadyLoaded

type ErrConfigAlreadyLoaded struct {
	Name string
}

ErrConfigAlreadyLoaded is returned by LoadConfig, if the given config name is already present.

func (ErrConfigAlreadyLoaded) Error

func (err ErrConfigAlreadyLoaded) Error() string

type ErrSectionNotFound

type ErrSectionNotFound struct {
	Section string
}

ErrSectionNotFound is returned by Get

func (ErrSectionNotFound) Error

func (err ErrSectionNotFound) Error() string

type ErrSectionTypeMismatch

type ErrSectionTypeMismatch struct {
	Config, Section string // name
	ExistingType    string
	NewType         string
}

ErrSectionTypeMismatch is returned by AddSection if the section-to-add already exists with a different type.

func (ErrSectionTypeMismatch) Error

func (err ErrSectionTypeMismatch) Error() string

type ErrUnknownOptionType

type ErrUnknownOptionType struct {
	Type string
}

ErrUnknownOptionType is returned when trying to parse an invalid OptionType.

func (ErrUnknownOptionType) Error

func (err ErrUnknownOptionType) Error() string

type OptionType

type OptionType int
const (
	TypeOption OptionType = iota // option is not a list
	TypeList                     // option is a list
)

func (OptionType) MarshalJSON

func (ot OptionType) MarshalJSON() ([]byte, error)

MarshalJSON implements encoding/json.Marshaler.

func (*OptionType) UnmarshalJSON

func (ot *OptionType) UnmarshalJSON(b []byte) error

UnmarshalJSON implements encoding/json.Unmarshaler.

type ParseError

type ParseError struct {
	// contains filtered or unexported fields
}

func (ParseError) Error

func (err ParseError) Error() string

type SectionOption

type SectionOption struct {
	Name string     `json:"name,omitempty"` // Option name/key
	Type OptionType `json:"type,omitempty"` // Expected data type
}

SectionOption describes a configuration option available in a section. It provides metadata about an option (name and type) without its current value.

Use [Tree.GetSectionOptions] to list all available options for a section.

func GetSectionOptions

func GetSectionOptions(config, section string) ([]SectionOption, error)

GetSectionOptions delegates to the default tree. See Tree for details.

type Tree

type Tree interface {
	// LoadConfig reads a config file into memory and returns nil. If the
	// config is already loaded, and forceReload is false, an error of type
	// ErrConfigAlreadyLoaded is returned. Errors reading the config file
	// are returned verbatim.
	//
	// You don't need to explicitly call LoadConfig(): Accessing configs
	// (and their sections) via Get, Set, Add, Delete, DeleteAll will
	// load missing files automatically.
	LoadConfig(name string, forceReload bool) error

	// Commit writes all changes back to the system.
	//
	// Note: this is not transaction safe. If, for whatever reason, the
	// writing of any file fails, the succeeding files are left untouched
	// while the preceding files are not reverted.
	Commit() error

	// Revert undoes changes to the config files given as arguments. If
	// no argument is given, all changes are reverted. This clears the
	// internal memory and does not access the file system.
	Revert(configs ...string)

	// GetSections returns the names of all sections of a certain type
	// in a config, and an error indicating whether the operation was
	// successful.
	GetSections(config, secType string) ([]string, error)

	// GetSectionOptions returns metadata for all configuration options
	// available in the specified section.
	//
	// The returned slice contains option names and their expected types,
	// but not current values.
	//
	// Returns an error if:
	//   - the config was not loaded into the tree;
	//   - the section doesn't exist.
	GetSectionOptions(config string, section string) ([]SectionOption, error)

	// Get retrieves (all) values for a fully qualified option, and a
	// boolean indicating whether the config file and the config section
	// within exists.
	Get(config, section, option string) ([]string, bool)

	// GetLoaded works like Get but operates strictly on the in-memory configuration state.
	// It does not attempt to load the config file from disk if the config or option is
	// missing. This is useful for verifying changes (like deletions) before they are committed.
	GetLoaded(config, section, option string) ([]string, bool)

	// GetLast retrieves the last value that was defined for a fully
	// qualified option, and a boolean indicating whether the config file,
	// config section and the option exists.
	GetLast(config, section, option string) (string, bool)

	// GetBool works the same way as GetLast does but interprets the last
	// specified value as a boolean.  If the found value can't be
	// interpreted as either true or false, it will return nil and false.
	GetBool(config, section, option string) (bool, bool)

	// SetType replaces the fully qualified option with the given values.
	// It returns whether the config file and section exists. For new
	// files and sections, you first need to initialize them with
	// AddSection().
	SetType(config, section, option string, typ OptionType, values ...string) error

	// Del removes a fully qualified option.
	Del(config, section, option string) error

	// AddSection adds a new config section. If the section already exists,
	// and the types match (existing type and given type), nothing happens.
	// Otherwise an ErrSectionTypeMismatch is returned.
	AddSection(config, section, typ string) error

	// DelSection remove a config section and its options.
	DelSection(config, section string) error
}

Tree defines the base directory for UCI config files. The default value on OpenWrt devices point to /etc/config, so that is what the default tree uses as well (you can access the default tree with the package level functions with the same signature as in this interface).

func NewTree

func NewTree(root string) Tree

NewTree constructs new RootDir pointing to root.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL