Documentation
¶
Overview ¶
Package generate renders systemd units from dotdrift configuration.
Index ¶
- Constants
- Variables
- func EscapePath(s string) string
- func ModuleDir(root string, sel Selection) (string, error)
- func RenderMountUnit(name string, spec profile.MountSpec, preset Entry, uid, gid int) string
- func RenderServiceUnit(unitName string) string
- func RenderSharesConf(smb profile.SmbSpec) string
- func RenderTimerUnit(name, startAt, unitName string) string
- func WriteModule(root string, sel Selection, input Input) error
- type Entry
- type Input
- type Registry
- type Selection
- type Volume
Constants ¶
const ( // KindVolume is a local block-device filesystem (e.g. btrfs, ntfs3). KindVolume = "volume" // KindNetwork is a remote export filesystem (e.g. nfs, cifs). KindNetwork = "network" )
Filesystem kinds.
const ( // LayerBase writes to <root>/modules/<id>. LayerBase = "base" // LayerHost writes to <root>/hosts/<hostname>/modules/<id>. LayerHost = "host" // LayerUser writes to <root>/users/<username>/modules/<id>. LayerUser = "user" )
Layer names for Selection.Layer.
const OverrideEnvVar = "DOTDRIFT_GENERATE_REGISTRY"
OverrideEnvVar overrides the user registry path; used by tests.
Variables ¶
var ErrNoRecommendation = errors.New("no recommended filesystem entry")
ErrNoRecommendation is returned by Recommend when no entry matches the requested family.
var KernelRelease = facts.KernelRelease
KernelRelease returns the running kernel release (`uname -r`). It aliases facts.KernelRelease (the one implementation); kept as a package-level var so existing tests can stub the lookup.
Functions ¶
func EscapePath ¶
EscapePath mirrors `systemd-escape --path` for mount-unit naming.
Behavior (verified against systemd 258):
- The path is normalized: empty and "." components are dropped, ".." pops the previous component (or is dropped at the root).
- The resulting components are joined with "-".
- An empty result (e.g. "/" or "") yields "-".
- Bytes outside [A-Za-z0-9:_.] are escaped as \xNN (UTF-8 bytes); a literal "-" becomes \x2d, and a "." as the very first character of the result is escaped as \x2e.
Unlike the real binary, EscapePath cannot fail: a non-normalized path such as "/a/../b" (which systemd-escape rejects) is normalized to "b".
func ModuleDir ¶
ModuleDir resolves the target directory for the selection without writing. Exported for the generate wizard (internal/tui), which locates the target module to mark already-managed volumes.
func RenderMountUnit ¶
RenderMountUnit renders the systemd .mount unit content for the mount named name. The unit's file name is derived by the caller as EscapePath(spec.Destination)+".mount" (systemd requires mount units to be named after their mount point).
A single "After=<deps>" line listing preset.After space-separated is emitted only when the preset declares dependencies (network presets; the reference script set AFTER only for nfs). Options resolve to spec.Options when non-empty, else preset.Options, with the bare tokens "uid"/"gid" expanded to uid=<uid>/gid=<gid> (see generate.go) and joined with commas.
func RenderServiceUnit ¶
RenderServiceUnit renders the oneshot .service wrapper that starts and stops the mount unit unitName (e.g. "mnt-synology-syn01.mount") and remains active so the mount survives the one-shot start. The caller writes the content to the same stem with a .service suffix.
func RenderSharesConf ¶
RenderSharesConf renders a Samba shares.conf with one [name] stanza per declared share, sorted by share name for byte-deterministic output and terminated by a single trailing newline. An empty Comment falls back to the share name (the reference script wrote "comment = $NAME"); an empty ValidUsers falls back to @<group> with the group defaulting to "smb" (the reference's SMB_DEFAULT_GROUP). An smb spec without shares renders empty.
func RenderTimerUnit ¶
RenderTimerUnit renders the .timer unit that activates the service unitName (e.g. "mnt-synology-syn01.service") on the OnCalendar expression startAt for the mount named name. Render it only when the mount's StartAt is non-empty — the caller decides and writes the content to the same stem with a .timer suffix so systemd pairs it with the service unit.
The Description references unitName rather than "<name>.service": dotdrift names service units by escaped destination stem, so the reference template's $NAME slot corresponds to the activated service unit — using the mount name there would point at a unit that does not exist. name identifies the mount for the caller (file naming, logging); the unit content carries only the service reference.
func WriteModule ¶
WriteModule materializes the module selected by sel under root from input. It validates the selection and every mount's filesystem type against the registry before writing anything, so an invalid input never leaves a half-written module behind.
Types ¶
type Entry ¶
type Entry struct {
// Type is the filesystem type key (e.g. "nfs", "ntfs3").
Type string `toml:"-"`
// Kind is KindVolume or KindNetwork.
Kind string `toml:"kind"`
// Family groups alternative drivers (e.g. "ntfs" for ntfs3/ntfs-3g);
// empty means the type is its own family. When non-empty it is also
// the Type= the rendered .mount unit announces — the kernel/mount
// helper selects the driver at mount time, so the unit names the
// generic type instead of pinning a driver.
Family string `toml:"family"`
// Options is the mount-option preset. The bare tokens "uid" and "gid"
// are placeholders substituted at render time.
Options []string `toml:"options"`
// After lists systemd unit dependencies for the generated mount unit.
After []string `toml:"after"`
// RecommendedIf gates the entry behind a kernel comparison
// ("kernel <op> <version>"); empty means always eligible.
RecommendedIf string `toml:"recommended_if"`
// Packages are the distro packages the filesystem type needs.
Packages []string `toml:"packages"`
// Absent lists distro packages to remove when the type is used (e.g.
// the ntfs3 preset removes ntfs-3g on kernel >= 7.2, mirroring the
// legacy script's cleanup).
Absent []string `toml:"absent"`
}
Entry is one filesystem preset from the registry.
type Input ¶
type Input struct {
// Mounts replaces the module's [mounts] section wholesale when
// non-nil; nil means mounts are not managed by this run (existing
// mounts, unit files, and their dotfile entries are left untouched).
Mounts map[string]profile.MountSpec
// Smb, when non-nil, replaces the module's [smb] section wholesale and
// (re)generates shares.conf. Nil means "smb is not managed by this
// run": an existing smb section, shares.conf, and smb.conf are all
// left untouched.
Smb *profile.SmbSpec
// UID and GID expand the bare "uid"/"gid" option tokens.
UID int
GID int
}
Input carries the resolved specs and the uid/gid used for mount-option token expansion (the caller resolves them via os/user).
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry is the merged filesystem preset registry: the embedded presets plus any user override entries.
func Load ¶
Load loads the embedded registry and merges the user override file whole-entry by filesystem type. pathOverride, when given, wins over the DOTDRIFT_GENERATE_REGISTRY env var, which wins over the default ~/.config/dotdrift/generate.toml. A missing override file is not an error; a malformed one is, and the error names its path.
type Selection ¶
type Selection struct {
// Layer is LayerBase, LayerHost, or LayerUser.
Layer string
// Hostname is required for LayerHost (the hosts/<hostname> directory).
Hostname string
// Username is required for LayerUser (the users/<username> directory).
Username string
// ModuleID is the module directory name.
ModuleID string
}
Selection names the module and the layer to write it at.
type Volume ¶
type Volume struct {
// UUID is the filesystem UUID, usable as a UUID= mount source.
UUID string
// FSType is the filesystem type as lsblk reports it (e.g. "btrfs",
// "ntfs"), not necessarily a registry type key.
FSType string
// Label is the filesystem label; empty when unset.
Label string
// Size is lsblk's human-readable size (e.g. "949.9G").
Size string
// Mountpoints lists current mountpoints in lsblk order; empty when
// the volume is not mounted.
Mountpoints []string
// Managed reports whether the volume's UUID already appears as a
// UUID=<uuid> source in the existing mount sources passed to Volumes.
Managed bool
}
Volume is one local block-device filesystem discovered via lsblk.
func Volumes ¶
Volumes lists local volumes discovered via lsblk, filtered to partitions (TYPE=part) with a non-empty UUID whose filesystem type the registry classifies as KindVolume — by entry type (btrfs) or by entry family (lsblk "ntfs" matches the ntfs family of ntfs3/ntfs-3g). Network-kind filesystems (nfs, cifs) and unknown types are excluded. sources marks already-managed volumes: an entry of the form "UUID=<uuid>" sets Managed on the matching volume. The result is sorted by UUID for deterministic output.