]> ruderich.org/simon Gitweb - safcm/safcm.git/commitdiff
Add basic support for FreeBSD
authorSimon Ruderich <simon@ruderich.org>
Sat, 17 Apr 2021 06:10:17 +0000 (08:10 +0200)
committerSimon Ruderich <simon@ruderich.org>
Sat, 17 Apr 2021 06:10:17 +0000 (08:10 +0200)
Installing packages and starting/enabling services is not yet supported.
There are minor limitations when handling symlinks (see README.adoc).

.builds/freebsd.yml [new file with mode: 0644]
README.adoc
cmd/safcm-remote/ainsl/ainsl_test.go
cmd/safcm-remote/build.sh
cmd/safcm-remote/sync/files.go
cmd/safcm-remote/sync/files_test.go
cmd/safcm-remote/sync/filetest/filetest.go
cmd/safcm/config/files.go
cmd/safcm/config/files_test.go
rpc/dial.go

diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml
new file mode 100644 (file)
index 0000000..1a96cb6
--- /dev/null
@@ -0,0 +1,9 @@
+image: freebsd/latest
+packages:
+  - go
+  - gmake
+tasks:
+  - all: |
+      sudo ln -sf /usr/local/bin/gmake /usr/bin/make
+      cd safcm
+      ./ci/run
index 2471f92d1b9b4e0fdd5785c68af4adc8b3d1c7d8..f120694f97abdfe5adbc728996efccee8d7be8b0 100644 (file)
@@ -111,6 +111,12 @@ future, others are due to the design of safcm.
   result is similar -- but not identical -- to quoted strings in regular shell
   scripts which can be confusing.
 
+- Permissions of symlinks are ignored on BSD systems. They are always shown to
+  have `0777` as permissions even though the current umask controls the actual
+  permissions when creating new symlinks. Existing symlinks with different
+  permissions are not updated. Most BSDs ignore the permissions when following
+  symlinks which should reduce the impact of this limitation.
+
 
 == Requirements
 
@@ -128,6 +134,7 @@ future, others are due to the design of safcm.
   * Supported operating system:
     ** GNU/Linux with common commands (`uname`, `id`, `stat`, `sha512sum`,
        `cat`, `mktemp`, `rm`, `ln`, `chmod`)
+    ** FreeBSD (same commands, but uses `sha512`)
   * SSH server
   * to install packages:
     ** `apt-get` (Debian or derivative)
index 2526dca0860da6685453c29e6e36e6d5dddfac98..8e9b0c121a268747d2a234dab0e1a045927fd55f 100644 (file)
@@ -20,6 +20,7 @@ import (
        "io/fs"
        "os"
        "path/filepath"
+       "runtime"
        "syscall"
        "testing"
 
@@ -53,6 +54,12 @@ func TestHandle(t *testing.T) {
        }
        _, uid, _, gid := ft.CurrentUserAndGroup()
 
+       symlinkExists := "open file: too many levels of symbolic links"
+       if runtime.GOOS == "freebsd" {
+               // EMLINK instead of ELOOP
+               symlinkExists = "open file: too many links"
+       }
+
        tests := []struct {
                name       string
                path       string
@@ -268,7 +275,7 @@ func TestHandle(t *testing.T) {
                                },
                        },
                        nil,
-                       fmt.Errorf("open file: too many levels of symbolic links"),
+                       fmt.Errorf(symlinkExists),
                },
                {
                        "exists: fifo",
index 4f102c9ccbc1a66d0fa8dbebc9f1da8f53e0d3ee..7edd09078b0331e20c2793fa6b848d087ed4278c 100755 (executable)
@@ -34,6 +34,7 @@ dest=../../remote/helpers
 
 mkdir -p "$dest"
 
+build freebsd amd64
 build linux amd64
 build_arm linux arm 7
 # TODO: support more operating systems and architectures
index 3e3c7ec18357e8a01e21e22906e6a5e79cb8257f..f3886065803781d8b0c0613766fbe981e8d4f61e 100644 (file)
@@ -113,7 +113,7 @@ reopen:
        oldFh, err := OpenFileNoFollow(file.Path)
        if err != nil {
                err := err.(*fs.PathError)
-               if err.Err == syscall.ELOOP {
+               if err.Err == syscall.ELOOP || err.Err == syscall.EMLINK {
                        // Check if ELOOP was caused not by O_NOFOLLOW but by
                        // too many nested symlinks before the final path
                        // component.
@@ -149,6 +149,15 @@ reopen:
        if !change.Created {
                // Compare permissions
                change.Old.Mode = oldStat.Mode()
+               if change.Old.Mode.Type() == fs.ModeSymlink {
+                       // Some BSD systems permit changing permissions of
+                       // symlinks but ignore them on traversal. To keep it
+                       // simple we don't support that and always use 0777 for
+                       // symlink permissions (the value on GNU/Linux).
+                       //
+                       // TODO: Add proper support for symlinks on BSD
+                       change.Old.Mode |= 0777
+               }
                if change.Old.Mode != file.Mode {
                        if change.Old.Mode.Type() != file.Mode.Type() {
                                changeType = true
index e7864ba8b8080e489da5964663c1496308599bd1..91a85befb83c24d9ea494e991e6699318c0289f8 100644 (file)
@@ -261,43 +261,38 @@ func TestSyncFiles(t *testing.T) {
                        // don't want to modify the running system. Use this
                        // test (and the one below for triggers) as a basic
                        // check that absolute paths work.
+                       //
+                       // Use numeric IDs as not all systems use root/root;
+                       // for example BSDs use root/wheel.
                        "absolute paths: no change",
                        safcm.MsgSyncReq{
                                Files: map[string]*safcm.File{
                                        "/": {
                                                Path:      "/",
                                                Mode:      fs.ModeDir | 0755,
-                                               User:      "root",
-                                               Uid:       -1,
-                                               Group:     "root",
-                                               Gid:       -1,
+                                               Uid:       0,
+                                               Gid:       0,
                                                OrigGroup: "group",
                                        },
                                        "/etc": {
                                                Path:      "/etc",
                                                Mode:      fs.ModeDir | 0755,
-                                               User:      "root",
-                                               Uid:       -1,
-                                               Group:     "root",
-                                               Gid:       -1,
+                                               Uid:       0,
+                                               Gid:       0,
                                                OrigGroup: "group",
                                        },
                                        "/tmp": {
                                                Path:      "/tmp",
                                                Mode:      fs.ModeDir | 0777 | fs.ModeSticky,
-                                               User:      "root",
-                                               Uid:       -1,
-                                               Group:     "root",
-                                               Gid:       -1,
+                                               Uid:       0,
+                                               Gid:       0,
                                                OrigGroup: "group",
                                        },
                                        "/var/tmp": {
                                                Path:      "/var/tmp",
                                                Mode:      fs.ModeDir | 0777 | fs.ModeSticky,
-                                               User:      "root",
-                                               Uid:       -1,
-                                               Group:     "root",
-                                               Gid:       -1,
+                                               Uid:       0,
+                                               Gid:       0,
                                                OrigGroup: "group",
                                        },
                                },
@@ -756,10 +751,8 @@ func TestSyncFiles(t *testing.T) {
                                        "/": {
                                                Path:      "/",
                                                Mode:      fs.ModeDir | 0755,
-                                               User:      "root",
-                                               Uid:       -1,
-                                               Group:     "root",
-                                               Gid:       -1,
+                                               Uid:       0,
+                                               Gid:       0,
                                                OrigGroup: "group",
                                                TriggerCommands: []string{
                                                        "echo trigger /",
@@ -768,10 +761,8 @@ func TestSyncFiles(t *testing.T) {
                                        "/tmp": {
                                                Path:      "/tmp",
                                                Mode:      fs.ModeDir | 0777 | fs.ModeSticky,
-                                               User:      "root",
-                                               Uid:       -1,
-                                               Group:     "root",
-                                               Gid:       -1,
+                                               Uid:       0,
+                                               Gid:       0,
                                                OrigGroup: "group",
                                                TriggerCommands: []string{
                                                        "echo trigger /tmp",
index b6fcb7c9bd2daef63fac32b8e2e7512215136a5e..6a594e6ac37b13b0d77c87048ab0e86243293bbd 100644 (file)
@@ -63,6 +63,7 @@ func WalkDir(basePath string) ([]File, error) {
                                return err
                        }
                        f.Data = []byte(x)
+                       f.Mode |= 0777 // see sync/files.go
                }
                res = append(res, f)
                return nil
index 8d1a728c9601c929a964642abfac969ec2835cde..8f637701a60735c25ebb63969d075b8e1526edd9 100644 (file)
@@ -79,6 +79,7 @@ via "safcm fixperms".
                                return err
                        }
                        data = []byte(x)
+                       perm |= 0777 // see cmd/safcm-remote/sync/files.go
                } else {
                        return fmt.Errorf("%q: file type not supported", path)
                }
index 9f226cd7b502e17e28f8db54527e693241753923..46da59055ee602646257b77ebea609ab84e07081 100644 (file)
@@ -19,6 +19,7 @@ import (
        "fmt"
        "io/fs"
        "os"
+       "runtime"
        "syscall"
        "testing"
 
@@ -45,6 +46,9 @@ func TestLoadFiles(t *testing.T) {
                t.Fatal(err)
        }
 
+       // Regular users cannot create sticky files
+       skipInvalidSticky := runtime.GOOS == "freebsd"
+
        chmod("files-invalid-perm-dir/files", 0500)
        defer chmod("files-invalid-perm-dir/files", 0700)
        chmod("files-invalid-perm-dir/files/etc/", 0755)
@@ -58,9 +62,11 @@ func TestLoadFiles(t *testing.T) {
        chmod("files-invalid-perm-file-executable/files", 0755)
        chmod("files-invalid-perm-file-executable/files/etc", 0755)
        chmod("files-invalid-perm-file-executable/files/etc/rc.local", 0750)
-       chmod("files-invalid-perm-file-sticky/files", 0755)
-       chmod("files-invalid-perm-file-sticky/files/etc", 0755)
-       chmod("files-invalid-perm-file-sticky/files/etc/resolv.conf", 01644)
+       if !skipInvalidSticky {
+               chmod("files-invalid-perm-file-sticky/files", 0755)
+               chmod("files-invalid-perm-file-sticky/files/etc", 0755)
+               chmod("files-invalid-perm-file-sticky/files/etc/resolv.conf", 01644)
+       }
 
        err = syscall.Mkfifo("files-invalid-type/files/invalid", 0644)
        if err != nil {
@@ -79,18 +85,21 @@ via "safcm fixperms".
 
        tests := []struct {
                group  string
+               skip   bool
                exp    map[string]*safcm.File
                expErr error
        }{
 
                {
                        "empty",
+                       false,
                        nil,
                        nil,
                },
 
                {
                        "group",
+                       false,
                        map[string]*safcm.File{
                                "/": {
                                        Path: "/",
@@ -169,31 +178,37 @@ host3.example.net
 
                {
                        "files-invalid-type",
+                       false,
                        nil,
                        fmt.Errorf("files-invalid-type: \"files-invalid-type/files/invalid\": file type not supported"),
                },
                {
                        "files-invalid-perm-dir",
+                       false,
                        nil,
                        fmt.Errorf("files-invalid-perm-dir: \"files-invalid-perm-dir/files\": invalid permissions 0500" + errMsg),
                },
                {
                        "files-invalid-perm-dir-setgid",
+                       false,
                        nil,
                        fmt.Errorf("files-invalid-perm-dir-setgid: \"files-invalid-perm-dir-setgid/files/etc\": invalid permissions 02755" + errMsg),
                },
                {
                        "files-invalid-perm-file",
+                       false,
                        nil,
                        fmt.Errorf("files-invalid-perm-file: \"files-invalid-perm-file/files/etc/resolv.conf\": invalid permissions 0600" + errMsg),
                },
                {
                        "files-invalid-perm-file-executable",
+                       false,
                        nil,
                        fmt.Errorf("files-invalid-perm-file-executable: \"files-invalid-perm-file-executable/files/etc/rc.local\": invalid permissions 0750" + errMsg),
                },
                {
                        "files-invalid-perm-file-sticky",
+                       skipInvalidSticky,
                        nil,
                        fmt.Errorf("files-invalid-perm-file-sticky: \"files-invalid-perm-file-sticky/files/etc/resolv.conf\": invalid permissions 01644" + errMsg),
                },
@@ -201,6 +216,10 @@ host3.example.net
 
        for _, tc := range tests {
                t.Run(tc.group, func(t *testing.T) {
+                       if tc.skip {
+                               t.SkipNow()
+                       }
+
                        res, err := LoadFiles(tc.group)
                        testutil.AssertEqual(t, "res", res, tc.exp)
                        testutil.AssertErrorEqual(t, "err", err, tc.expErr)
index 680cb87df39ae0c999ce789d5d53bb168ed64b80..f300370aba3f75eb7ff55b7fc9eb0be2c15d9871 100644 (file)
@@ -112,6 +112,17 @@ compat_stat() {
 compat_sha512sum() {
        sha512sum "$1"
 }
+`
+       case "freebsd":
+               compat = `
+dir_stat='41777 0 0'
+file_stat="100700 $(id -u) $(id -g)"
+compat_stat() {
+       stat -f '%p %u %g' "$1"
+}
+compat_sha512sum() {
+       sha512 -q "$1"
+}
 `
        default:
                return fmt.Errorf("internal error: no support for %q", goos)
@@ -278,6 +289,8 @@ func connGetGoos(stdin io.Writer, stdout *bufio.Reader) (string, error) {
        switch x {
        case "GNU/Linux":
                goos = "linux"
+       case "FreeBSD":
+               goos = "freebsd"
        default:
                return "", fmt.Errorf("unsupported OS %q (`uname -o`)", x)
        }
@@ -298,7 +311,7 @@ func connGetGoarch(stdin io.Writer, stdout *bufio.Reader) (string, error) {
        // NOTE: Adapt cmd/safcm-remote/build.sh when adding new architectures
        var goarch string
        switch x {
-       case "x86_64":
+       case "x86_64", "amd64":
                goarch = "amd64"
        case "armv7l":
                goarch = "armv7l"