├── .gitignore ├── fixtures ├── image.raw.xz ├── image.tar.xz ├── freeze.service ├── start-stop.service ├── mask-unmask.service ├── enable-disable.service ├── subscribe-events-set.service ├── subscribe-events.service ├── oneshot.service ├── attach-processes.service ├── start-failed.service └── reload.service ├── examples ├── activation │ ├── httpserver │ │ ├── hello.socket │ │ ├── hello.service │ │ ├── README.md │ │ └── httpserver.go │ ├── listen.go │ ├── activation.go │ └── udpconn.go └── journal │ ├── run.sh │ └── main.go ├── NOTICE ├── go.mod ├── go.sum ├── .golangci.yml ├── unit ├── doc.go ├── section.go ├── option.go ├── end_to_end_test.go ├── serialize.go ├── escape.go ├── escape_test.go ├── option_test.go ├── serialize_test.go └── deserialize.go ├── activation ├── files_windows.go ├── common_test.go ├── packetconns.go ├── packetconns_test.go ├── listeners_test.go ├── files_unix.go ├── files_test.go └── listeners.go ├── .github ├── workflows │ ├── containers.yml │ └── go.yml └── dependabot.yml ├── daemon ├── sdnotify_other.go ├── sdnotify_unix.go ├── sdnotify_test.go ├── watchdog.go ├── watchdog_test.go ├── sdnotify_linux_test.go └── sdnotify.go ├── util ├── util_stub.go ├── util_test.go ├── util.go └── util_cgo.go ├── sdjournal ├── functions_test.go ├── functions.go └── read.go ├── dbus ├── set.go ├── set_test.go ├── subscription_set_test.go ├── subscription_set.go ├── dbus_test.go ├── subscription_test.go ├── dbus.go └── properties.go ├── internal └── dlopen │ ├── dlopen_example.go │ ├── dlopen.go │ └── dlopen_test.go ├── journal ├── journal_windows.go ├── journal.go ├── journal_test.go ├── journal_unix_test.go └── journal_unix.go ├── DCO ├── CONTRIBUTING.md ├── README.md ├── code-of-conduct.md ├── scripts └── ci-runner.sh ├── import1 ├── dbus_test.go └── dbus.go ├── login1 └── dbus_test.go └── machine1 ├── dbus_test.go └── dbus.go /.gitignore: -------------------------------------------------------------------------------- 1 | test_bins 2 | -------------------------------------------------------------------------------- /fixtures/image.raw.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreos/go-systemd/HEAD/fixtures/image.raw.xz -------------------------------------------------------------------------------- /fixtures/image.tar.xz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coreos/go-systemd/HEAD/fixtures/image.tar.xz -------------------------------------------------------------------------------- /fixtures/freeze.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=freeze unit test 3 | 4 | [Service] 5 | ExecStart=/bin/sleep 400 6 | -------------------------------------------------------------------------------- /fixtures/start-stop.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=start stop test 3 | 4 | [Service] 5 | ExecStart=/bin/sleep 400 6 | -------------------------------------------------------------------------------- /fixtures/mask-unmask.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=mask unmask test 3 | 4 | [Service] 5 | ExecStart=/bin/sleep 400 6 | -------------------------------------------------------------------------------- /fixtures/enable-disable.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=enable disable test 3 | 4 | [Service] 5 | ExecStart=/bin/sleep 400 6 | -------------------------------------------------------------------------------- /fixtures/subscribe-events-set.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=start stop test 3 | 4 | [Service] 5 | ExecStart=/bin/sleep 400 6 | -------------------------------------------------------------------------------- /fixtures/subscribe-events.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=start stop test 3 | 4 | [Service] 5 | ExecStart=/bin/sleep 400 6 | -------------------------------------------------------------------------------- /fixtures/oneshot.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=list jobs test 3 | 4 | [Service] 5 | Type=oneshot 6 | ExecStart=/bin/sleep 30 7 | -------------------------------------------------------------------------------- /examples/activation/httpserver/hello.socket: -------------------------------------------------------------------------------- 1 | [Socket] 2 | ListenStream=127.0.0.1:8076 3 | 4 | [Install] 5 | WantedBy=sockets.target 6 | -------------------------------------------------------------------------------- /fixtures/attach-processes.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=attach processes test 3 | 4 | [Service] 5 | Delegate=yes 6 | ExecStart=/bin/sleep 400 7 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | CoreOS Project 2 | Copyright 2018 CoreOS, Inc 3 | 4 | This product includes software developed at CoreOS, Inc. 5 | (http://www.coreos.com/). 6 | -------------------------------------------------------------------------------- /fixtures/start-failed.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=starting a failed test 3 | 4 | [Service] 5 | ExecStartPre=/bin/false 6 | ExecStart=/bin/sleep 400 7 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/coreos/go-systemd/v22 2 | 3 | go 1.23 4 | 5 | require ( 6 | github.com/godbus/dbus/v5 v5.1.0 7 | golang.org/x/sys v0.1.0 8 | ) 9 | -------------------------------------------------------------------------------- /fixtures/reload.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=reload test 3 | 4 | [Service] 5 | ExecStart=/bin/bash -c "trap '' HUP; /bin/sleep 400" 6 | ExecReload=-/bin/systemctl kill -s HUP reload.service 7 | -------------------------------------------------------------------------------- /examples/activation/httpserver/hello.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Hello World HTTP 3 | Requires=network.target 4 | After=multi-user.target 5 | 6 | [Service] 7 | Type=simple 8 | ExecStart=/usr/local/bin/httpserver 9 | 10 | [Install] 11 | WantedBy=multi-user.target 12 | -------------------------------------------------------------------------------- /examples/journal/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | go build 6 | 7 | echo "Running directly" 8 | ./journal 9 | 10 | echo "Running through systemd" 11 | unit_name="run-$(systemd-id128 new)" 12 | systemd-run -u "$unit_name" --user --wait --quiet ./journal 13 | journalctl --user -u "$unit_name" 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= 2 | github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 3 | golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= 4 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 5 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | formatters: 4 | enable: 5 | - gofumpt 6 | 7 | linters: 8 | settings: 9 | staticcheck: 10 | checks: 11 | - all 12 | - -ST1003 # https://staticcheck.dev/docs/checks/#ST1003 Should not use underscores in Go names. 13 | - -ST1005 # https://staticcheck.dev/docs/checks/#ST1005 Error strings should not be capitalized. 14 | govet: 15 | enable: 16 | - nilness 17 | exclusions: 18 | presets: 19 | - std-error-handling 20 | -------------------------------------------------------------------------------- /examples/activation/httpserver/README.md: -------------------------------------------------------------------------------- 1 | ## socket activated http server 2 | 3 | This is a simple example of using socket activation with systemd to serve a 4 | simple HTTP server on http://127.0.0.1:8076 5 | 6 | To try it out `go get` the httpserver and run it under the systemd-activate helper 7 | 8 | ```bash 9 | export GOPATH="$PWD" 10 | go get github.com/coreos/go-systemd/examples/activation/httpserver 11 | systemd-socket-activate -l 127.0.0.1:8076 ./bin/httpserver 12 | ``` 13 | 14 | Then curl the URL and you will notice that it starts up: 15 | 16 | ``` 17 | curl 127.0.0.1:8076 18 | hello socket activated world! 19 | ``` 20 | -------------------------------------------------------------------------------- /unit/doc.go: -------------------------------------------------------------------------------- 1 | // Package unit provides utilities for parsing, serializing, and manipulating 2 | // systemd unit files. It supports both reading unit file content into Go data 3 | // structures and writing Go data structures back to unit file format. 4 | // 5 | // The package provides functionality to: 6 | // - Parse systemd unit files into [UnitOption] and [UnitSection] structures 7 | // - Serialize Go structures back into unit file format 8 | // - Escape and unescape unit names according to systemd conventions 9 | // 10 | // Unit files are configuration files that describe how systemd should manage 11 | // services, sockets, devices, and other system resources. 12 | package unit 13 | -------------------------------------------------------------------------------- /activation/files_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package activation 16 | 17 | import "os" 18 | 19 | func Files(unsetEnv bool) []*os.File { 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/containers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Containers 3 | 4 | on: 5 | push: 6 | branches: [main] 7 | pull_request: 8 | branches: [main] 9 | 10 | permissions: 11 | contents: read 12 | 13 | jobs: 14 | distro-test: 15 | name: "Distro test" 16 | runs-on: ubuntu-latest 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | baseimage: ['debian:bullseye', 'debian:trixie', 'ubuntu:20.04', 'ubuntu:24.04', 'fedora'] 21 | go: [1.23, 1.24, 1.25] 22 | steps: 23 | - uses: actions/checkout@v6 24 | - name: Run tests in container 25 | run: ./scripts/ci-runner.sh run_in_ct ${{ matrix.baseimage }} ${{ matrix.go }} 26 | 27 | all-done: 28 | needs: 29 | - distro-test 30 | runs-on: ubuntu-latest 31 | steps: 32 | - run: echo "All jobs completed" 33 | -------------------------------------------------------------------------------- /daemon/sdnotify_other.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !unix 16 | 17 | package daemon 18 | 19 | // SdNotifyMonotonicUsec returns the empty string on unsupported platforms. 20 | func SdNotifyMonotonicUsec() string { 21 | return "" 22 | } 23 | -------------------------------------------------------------------------------- /util/util_stub.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build !cgo 16 | 17 | package util 18 | 19 | func getRunningSlice() (string, error) { return "", ErrNoCGO } 20 | 21 | func runningFromSystemService() (bool, error) { return false, ErrNoCGO } 22 | 23 | func currentUnitName() (string, error) { return "", ErrNoCGO } 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Maintained in https://github.com/coreos/repo-templates 2 | # Do not edit downstream. 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: "github-actions" 7 | directory: "/" 8 | schedule: 9 | interval: "weekly" 10 | labels: ["skip-notes"] 11 | open-pull-requests-limit: 3 12 | - package-ecosystem: gomod 13 | directory: / 14 | schedule: 15 | interval: weekly 16 | open-pull-requests-limit: 10 17 | labels: 18 | - area/dependencies 19 | 20 | # Group all updates together in a single PR. We can remove some 21 | # updates from a combined update PR via comments to dependabot: 22 | # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/managing-pull-requests-for-dependency-updates#managing-dependabot-pull-requests-for-grouped-updates-with-comment-commands 23 | groups: 24 | build: 25 | patterns: 26 | - "*" 27 | -------------------------------------------------------------------------------- /sdjournal/functions_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 RedHat, Inc. 2 | // Copyright 2015 CoreOS, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package sdjournal 17 | 18 | import "testing" 19 | 20 | func TestGetFunction(t *testing.T) { 21 | f, err := getFunction("sd_journal_open") 22 | if err != nil { 23 | t.Errorf("Error getting an existing function: %s", err) 24 | } 25 | 26 | if f == nil { 27 | t.Error("Got nil function pointer") 28 | } 29 | 30 | _, err = getFunction("non_existent_function") 31 | 32 | if err == nil { 33 | t.Error("Expected to get an error, got nil") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /examples/journal/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License 14 | 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | 21 | "github.com/coreos/go-systemd/v22/journal" 22 | ) 23 | 24 | func main() { 25 | ok, err := journal.StderrIsJournalStream() 26 | if err != nil { 27 | panic(err) 28 | } 29 | 30 | if ok { 31 | // use journal native protocol 32 | _ = journal.Send("this is a message logged through the native protocol", journal.PriInfo, nil) 33 | } else { 34 | // use stderr 35 | fmt.Fprintln(os.Stderr, "this is a message logged through stderr") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /activation/common_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package activation 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | ) 21 | 22 | // exampleCmd returns the command line for the specified example binary. 23 | func exampleCmd(binaryName string) (string, []string) { 24 | sourcePath := fmt.Sprintf("../examples/activation/%s.go", binaryName) 25 | sourceCmdLine := []string{"go", "run", sourcePath} 26 | binaryPath := fmt.Sprintf("../test_bins/%s.example", binaryName) 27 | if _, err := os.Stat(binaryPath); err != nil && os.IsNotExist(err) { 28 | return sourceCmdLine[0], sourceCmdLine[1:] 29 | } 30 | return binaryPath, []string{binaryPath} 31 | } 32 | -------------------------------------------------------------------------------- /dbus/set.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package dbus 16 | 17 | type set struct { 18 | data map[string]bool 19 | } 20 | 21 | func (s *set) Add(value string) { 22 | s.data[value] = true 23 | } 24 | 25 | func (s *set) Remove(value string) { 26 | delete(s.data, value) 27 | } 28 | 29 | func (s *set) Contains(value string) (exists bool) { 30 | _, exists = s.data[value] 31 | return 32 | } 33 | 34 | func (s *set) Length() int { 35 | return len(s.data) 36 | } 37 | 38 | func (s *set) Values() (values []string) { 39 | for val := range s.data { 40 | values = append(values, val) 41 | } 42 | return 43 | } 44 | 45 | func newSet() *set { 46 | return &set{make(map[string]bool)} 47 | } 48 | -------------------------------------------------------------------------------- /examples/activation/httpserver/httpserver.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build ignore 16 | // +build ignore 17 | 18 | package main 19 | 20 | import ( 21 | "io" 22 | "net/http" 23 | 24 | "github.com/coreos/go-systemd/v22/activation" 25 | ) 26 | 27 | func HelloServer(w http.ResponseWriter, req *http.Request) { 28 | io.WriteString(w, "hello socket activated world!\n") 29 | } 30 | 31 | func main() { 32 | listeners, err := activation.Listeners() 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | if len(listeners) != 1 { 38 | panic("Unexpected number of socket activation fds") 39 | } 40 | 41 | http.HandleFunc("/", HelloServer) 42 | http.Serve(listeners[0], nil) 43 | } 44 | -------------------------------------------------------------------------------- /daemon/sdnotify_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build unix 16 | 17 | package daemon 18 | 19 | import ( 20 | "strconv" 21 | 22 | "golang.org/x/sys/unix" 23 | ) 24 | 25 | // SdNotifyMonotonicUsec returns a MONOTONIC_USEC=... assignment for the current time 26 | // with a trailing newline included. This is typically used with [SdNotifyReloading]. 27 | // 28 | // If the monotonic clock is not available on the system, the empty string is returned. 29 | func SdNotifyMonotonicUsec() string { 30 | var ts unix.Timespec 31 | if err := unix.ClockGettime(unix.CLOCK_MONOTONIC, &ts); err != nil { 32 | // Monotonic clock is not available on this system. 33 | return "" 34 | } 35 | return "MONOTONIC_USEC=" + strconv.FormatInt(ts.Nano()/1000, 10) + "\n" 36 | } 37 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | branches: [main] 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | build: 13 | name: "Build" 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | go: ['1.23.x', '1.24.x', '1.25.x'] 18 | steps: 19 | - run: sudo apt-get -qq update 20 | - name: Install libsystemd-dev 21 | run: sudo apt-get install libsystemd-dev 22 | - uses: actions/checkout@v6 23 | - name: Setup go 24 | uses: actions/setup-go@v6 25 | with: 26 | go-version: ${{ matrix.go }} 27 | - name: Go build (source) 28 | run: ./scripts/ci-runner.sh build_source 29 | - name: Go build (tests) 30 | run: ./scripts/ci-runner.sh build_tests 31 | lint: 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Install dependencies 35 | run: | 36 | sudo apt-get -qq update 37 | sudo apt-get -qq install libsystemd-dev 38 | - uses: actions/checkout@v6 39 | - uses: actions/setup-go@v6 40 | with: 41 | go-version: stable 42 | - uses: golangci/golangci-lint-action@v9 43 | with: 44 | version: v2.3 45 | 46 | all-done: 47 | needs: 48 | - build 49 | runs-on: ubuntu-latest 50 | steps: 51 | - run: echo "All jobs completed" 52 | -------------------------------------------------------------------------------- /activation/packetconns.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package activation 16 | 17 | import ( 18 | "net" 19 | ) 20 | 21 | // PacketConns returns a slice containing a net.PacketConn for each matching socket type 22 | // passed to this process. 23 | // 24 | // The order of the file descriptors is preserved in the returned slice. 25 | // Nil values are used to fill any gaps. For example if systemd were to return file descriptors 26 | // corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn} 27 | func PacketConns() ([]net.PacketConn, error) { 28 | files := Files(true) 29 | conns := make([]net.PacketConn, len(files)) 30 | 31 | for i, f := range files { 32 | if pc, err := net.FilePacketConn(f); err == nil { 33 | conns[i] = pc 34 | f.Close() 35 | } 36 | } 37 | return conns, nil 38 | } 39 | -------------------------------------------------------------------------------- /unit/section.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package unit 16 | 17 | // UnitEntry is a single line entry in a Unit file. 18 | type UnitEntry struct { 19 | Name string 20 | Value string 21 | } 22 | 23 | // UnitSection is a section in a Unit file. The section name 24 | // and a list of entries in that section. 25 | type UnitSection struct { 26 | Section string 27 | Entries []*UnitEntry 28 | } 29 | 30 | // String implements the stringify interface for UnitEntry 31 | func (u *UnitEntry) String() string { 32 | return "{Name: " + u.Name + ", " + "Value: " + u.Value + "}" 33 | } 34 | 35 | // String implements the stringify interface for UnitSection 36 | func (s *UnitSection) String() string { 37 | result := "{Section: " + s.Section 38 | for _, e := range s.Entries { 39 | result += e.String() 40 | } 41 | 42 | result += "}" 43 | return result 44 | } 45 | -------------------------------------------------------------------------------- /internal/dlopen/dlopen_example.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | //go:build linux 16 | 17 | package dlopen 18 | 19 | // #include 20 | // #include 21 | // 22 | // int 23 | // my_strlen(void *f, const char *s) 24 | // { 25 | // size_t (*strlen)(const char *); 26 | // 27 | // strlen = (size_t (*)(const char *))f; 28 | // return strlen(s); 29 | // } 30 | import "C" 31 | 32 | import ( 33 | "unsafe" 34 | ) 35 | 36 | func strlen(libs []string, s string) (int, error) { 37 | h, err := GetHandle(libs) 38 | if err != nil { 39 | return -1, err 40 | } 41 | defer h.Close() 42 | 43 | f := "strlen" 44 | cs := C.CString(s) 45 | defer C.free(unsafe.Pointer(cs)) 46 | 47 | strlen, err := h.GetSymbolPointer(f) 48 | if err != nil { 49 | return -1, err 50 | } 51 | 52 | len := C.my_strlen(strlen, cs) 53 | 54 | return int(len), nil 55 | } 56 | -------------------------------------------------------------------------------- /journal/journal_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package journal provides write bindings to the local systemd journal. 16 | // It is implemented in pure Go and connects to the journal directly over its 17 | // unix socket. 18 | // 19 | // To read from the journal, see the "sdjournal" package, which wraps the 20 | // sd-journal a C API. 21 | // 22 | // http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html 23 | package journal 24 | 25 | import ( 26 | "errors" 27 | ) 28 | 29 | func Enabled() bool { 30 | return false 31 | } 32 | 33 | func Send(message string, priority Priority, vars map[string]string) error { 34 | return errors.New("could not initialize socket to journald") 35 | } 36 | 37 | func StderrIsJournalStream() (bool, error) { 38 | return false, nil 39 | } 40 | 41 | func StdoutIsJournalStream() (bool, error) { 42 | return false, nil 43 | } 44 | -------------------------------------------------------------------------------- /dbus/set_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package dbus 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | // TestBasicSetActions asserts that Add & Remove behavior is correct 22 | func TestBasicSetActions(t *testing.T) { 23 | s := newSet() 24 | 25 | if s.Contains("foo") { 26 | t.Fatal("set should not contain 'foo'") 27 | } 28 | 29 | s.Add("foo") 30 | 31 | if !s.Contains("foo") { 32 | t.Fatal("set should contain 'foo'") 33 | } 34 | 35 | v := s.Values() 36 | if len(v) != 1 { 37 | t.Fatal("set.Values did not report correct number of values") 38 | } 39 | if v[0] != "foo" { 40 | t.Fatal("set.Values did not report value") 41 | } 42 | 43 | s.Remove("foo") 44 | 45 | if s.Contains("foo") { 46 | t.Fatal("set should not contain 'foo'") 47 | } 48 | 49 | v = s.Values() 50 | if len(v) != 0 { 51 | t.Fatal("set.Values did not report correct number of values") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /journal/journal.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package journal provides write bindings to the local systemd journal. 16 | // It is implemented in pure Go and connects to the journal directly over its 17 | // unix socket. 18 | // 19 | // To read from the journal, see the "sdjournal" package, which wraps the 20 | // sd-journal a C API. 21 | // 22 | // http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html 23 | package journal 24 | 25 | import ( 26 | "fmt" 27 | ) 28 | 29 | // Priority of a journal message 30 | type Priority int 31 | 32 | const ( 33 | PriEmerg Priority = iota 34 | PriAlert 35 | PriCrit 36 | PriErr 37 | PriWarning 38 | PriNotice 39 | PriInfo 40 | PriDebug 41 | ) 42 | 43 | // Print prints a message to the local systemd journal using Send(). 44 | func Print(priority Priority, format string, a ...any) error { 45 | return Send(fmt.Sprintf(format, a...), priority, nil) 46 | } 47 | -------------------------------------------------------------------------------- /DCO: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 660 York Street, Suite 102, 6 | San Francisco, CA 94110 USA 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this 9 | license document, but changing it is not allowed. 10 | 11 | 12 | Developer's Certificate of Origin 1.1 13 | 14 | By making a contribution to this project, I certify that: 15 | 16 | (a) The contribution was created in whole or in part by me and I 17 | have the right to submit it under the open source license 18 | indicated in the file; or 19 | 20 | (b) The contribution is based upon previous work that, to the best 21 | of my knowledge, is covered under an appropriate open source 22 | license and I have the right under that license to submit that 23 | work with modifications, whether created in whole or in part 24 | by me, under the same open source license (unless I am 25 | permitted to submit under a different license), as indicated 26 | in the file; or 27 | 28 | (c) The contribution was provided directly to me by some other 29 | person who certified (a), (b) or (c) and I have not modified 30 | it. 31 | 32 | (d) I understand and agree that this project and the contribution 33 | are public and that a record of the contribution (including all 34 | personal information I submit with it, including my sign-off) is 35 | maintained indefinitely and may be redistributed consistent with 36 | this project or the open source license(s) involved. 37 | -------------------------------------------------------------------------------- /examples/activation/listen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build ignore 16 | // +build ignore 17 | 18 | // Activation example used by the activation unit tests. 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "os" 24 | 25 | "github.com/coreos/go-systemd/v22/activation" 26 | ) 27 | 28 | func fixListenPid() { 29 | if os.Getenv("FIX_LISTEN_PID") != "" { 30 | // HACK: real systemd would set LISTEN_PID before exec'ing but 31 | // this is too difficult in golang for the purpose of a test. 32 | // Do not do this in real code. 33 | os.Setenv("LISTEN_PID", fmt.Sprintf("%d", os.Getpid())) 34 | } 35 | } 36 | 37 | func main() { 38 | fixListenPid() 39 | 40 | listenersWithNames, err := activation.ListenersWithNames() 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" || os.Getenv("LISTEN_FDNAMES") != "" { 46 | panic("Can not unset envs") 47 | } 48 | 49 | c0, _ := listenersWithNames["fd1"][0].Accept() 50 | c1, _ := listenersWithNames["fd2"][0].Accept() 51 | 52 | // Write out the expected strings to the two pipes 53 | c0.Write([]byte("Hello world: fd1")) 54 | c1.Write([]byte("Goodbye world: fd2")) 55 | 56 | return 57 | } 58 | -------------------------------------------------------------------------------- /sdjournal/functions.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 RedHat, Inc. 2 | // Copyright 2015 CoreOS, Inc. 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | package sdjournal 17 | 18 | import ( 19 | "sync" 20 | "unsafe" 21 | 22 | "github.com/coreos/go-systemd/v22/internal/dlopen" 23 | ) 24 | 25 | var ( 26 | // lazy initialized 27 | libsystemdHandle *dlopen.LibHandle 28 | 29 | libsystemdMutex = &sync.Mutex{} 30 | libsystemdFunctions = map[string]unsafe.Pointer{} 31 | libsystemdNames = []string{ 32 | // systemd < 209 33 | "libsystemd-journal.so.0", 34 | "libsystemd-journal.so", 35 | 36 | // systemd >= 209 merged libsystemd-journal into libsystemd proper 37 | "libsystemd.so.0", 38 | "libsystemd.so", 39 | } 40 | ) 41 | 42 | func getFunction(name string) (unsafe.Pointer, error) { 43 | libsystemdMutex.Lock() 44 | defer libsystemdMutex.Unlock() 45 | 46 | if libsystemdHandle == nil { 47 | h, err := dlopen.GetHandle(libsystemdNames) 48 | if err != nil { 49 | return nil, err 50 | } 51 | 52 | libsystemdHandle = h 53 | } 54 | 55 | f, ok := libsystemdFunctions[name] 56 | if !ok { 57 | var err error 58 | f, err = libsystemdHandle.GetSymbolPointer(name) 59 | if err != nil { 60 | return nil, err 61 | } 62 | 63 | libsystemdFunctions[name] = f 64 | } 65 | 66 | return f, nil 67 | } 68 | -------------------------------------------------------------------------------- /unit/option.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package unit 16 | 17 | import ( 18 | "fmt" 19 | ) 20 | 21 | // UnitOption represents an option in a systemd unit file. 22 | type UnitOption struct { 23 | Section string 24 | Name string 25 | Value string 26 | } 27 | 28 | // NewUnitOption returns a new UnitOption instance with pre-set values. 29 | func NewUnitOption(section, name, value string) *UnitOption { 30 | return &UnitOption{Section: section, Name: name, Value: value} 31 | } 32 | 33 | func (uo *UnitOption) String() string { 34 | return fmt.Sprintf("{Section: %q, Name: %q, Value: %q}", uo.Section, uo.Name, uo.Value) 35 | } 36 | 37 | // Match compares two UnitOptions and returns true if they are identical. 38 | func (uo *UnitOption) Match(other *UnitOption) bool { 39 | return uo.Section == other.Section && 40 | uo.Name == other.Name && 41 | uo.Value == other.Value 42 | } 43 | 44 | // AllMatch compares two slices of UnitOptions and returns true if they are 45 | // identical. 46 | func AllMatch(u1 []*UnitOption, u2 []*UnitOption) bool { 47 | length := len(u1) 48 | if length != len(u2) { 49 | return false 50 | } 51 | 52 | for i := range length { 53 | if !u1[i].Match(u2[i]) { 54 | return false 55 | } 56 | } 57 | 58 | return true 59 | } 60 | -------------------------------------------------------------------------------- /examples/activation/activation.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build ignore 16 | // +build ignore 17 | 18 | // Activation example used by the activation unit tests. 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "os" 24 | 25 | "github.com/coreos/go-systemd/v22/activation" 26 | ) 27 | 28 | func fixListenPid() { 29 | if os.Getenv("FIX_LISTEN_PID") != "" { 30 | // HACK: real systemd would set LISTEN_PID before exec'ing but 31 | // this is too difficult in golang for the purpose of a test. 32 | // Do not do this in real code. 33 | os.Setenv("LISTEN_PID", fmt.Sprintf("%d", os.Getpid())) 34 | } 35 | } 36 | 37 | func main() { 38 | fixListenPid() 39 | 40 | files := activation.Files(false) 41 | 42 | if len(files) == 0 { 43 | panic("No files") 44 | } 45 | 46 | if os.Getenv("LISTEN_PID") == "" || os.Getenv("LISTEN_FDS") == "" || os.Getenv("LISTEN_FDNAMES") == "" { 47 | panic("Should not unset envs") 48 | } 49 | 50 | files = activation.Files(true) 51 | 52 | if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" || os.Getenv("LISTEN_FDNAMES") != "" { 53 | panic("Can not unset envs") 54 | } 55 | 56 | // Write out the expected strings to the two pipes 57 | files[0].Write([]byte("Hello world: " + files[0].Name())) 58 | files[1].Write([]byte("Goodbye world: " + files[1].Name())) 59 | 60 | return 61 | } 62 | -------------------------------------------------------------------------------- /dbus/subscription_set_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package dbus 16 | 17 | import ( 18 | "testing" 19 | "time" 20 | ) 21 | 22 | // TestSubscribeUnit exercises the basics of subscription of a particular unit. 23 | func TestSubscriptionSetUnit(t *testing.T) { 24 | target := "subscribe-events-set.service" 25 | 26 | conn, err := New() 27 | if err != nil { 28 | t.Fatal(err) 29 | } 30 | 31 | err = conn.Subscribe() 32 | if err != nil { 33 | t.Fatal(err) 34 | } 35 | 36 | subSet := conn.NewSubscriptionSet() 37 | evChan, errChan := subSet.Subscribe() 38 | 39 | subSet.Add(target) 40 | setupUnit(target, conn, t) 41 | linkUnit(target, conn, t) 42 | 43 | reschan := make(chan string) 44 | _, err = conn.StartUnit(target, "replace", reschan) 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | 49 | job := <-reschan 50 | if job != "done" { 51 | t.Fatal("Couldn't start", target) 52 | } 53 | 54 | timeout := make(chan bool, 1) 55 | go func() { 56 | time.Sleep(3 * time.Second) 57 | close(timeout) 58 | }() 59 | 60 | for { 61 | select { 62 | case changes := <-evChan: 63 | tCh, ok := changes[target] 64 | 65 | if !ok { 66 | t.Fatal("Unexpected event:", changes) 67 | } 68 | 69 | if tCh.ActiveState == "active" && tCh.Name == target { 70 | goto success 71 | } 72 | case err = <-errChan: 73 | t.Fatal(err) 74 | case <-timeout: 75 | t.Fatal("Reached timeout") 76 | } 77 | } 78 | 79 | success: 80 | return 81 | } 82 | -------------------------------------------------------------------------------- /activation/packetconns_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package activation 16 | 17 | import ( 18 | "net" 19 | "os" 20 | "os/exec" 21 | "testing" 22 | ) 23 | 24 | // TestActivation forks out a copy of activation.go example and reads back two 25 | // strings from the pipes that are passed in. 26 | func TestPacketConns(t *testing.T) { 27 | arg0, cmdline := exampleCmd("udpconn") 28 | cmd := exec.Command(arg0, cmdline...) 29 | 30 | u1, err := net.ListenUDP("udp", &net.UDPAddr{Port: 9999}) 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | u2, err := net.ListenUDP("udp", &net.UDPAddr{Port: 1234}) 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | 39 | f1, _ := u1.File() 40 | f2, _ := u2.File() 41 | 42 | cmd.ExtraFiles = []*os.File{ 43 | f1, 44 | f2, 45 | } 46 | 47 | r1, err := net.Dial("udp", "127.0.0.1:9999") 48 | if err != nil { 49 | t.Fatal(err) 50 | } 51 | if _, err := r1.Write([]byte("Hi")); err != nil { 52 | t.Fatal(err) 53 | } 54 | 55 | r2, err := net.Dial("udp", "127.0.0.1:1234") 56 | if err != nil { 57 | t.Fatal(err) 58 | } 59 | if _, err := r2.Write([]byte("Hi")); err != nil { 60 | t.Fatal(err) 61 | } 62 | 63 | cmd.Env = os.Environ() 64 | cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "LISTEN_FDNAMES=fd1:fd2", "FIX_LISTEN_PID=1") 65 | 66 | out, err := cmd.CombinedOutput() 67 | if err != nil { 68 | t.Fatalf("Cmd output '%s', err: '%s'\n", out, err) 69 | } 70 | 71 | correctStringWritten(t, r1, "Hello world") 72 | correctStringWritten(t, r2, "Goodbye world") 73 | } 74 | -------------------------------------------------------------------------------- /util/util_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | func testIsRunningSystemd(t *testing.T) { 22 | if !IsRunningSystemd() { 23 | t.Skip("Not running on a systemd host") 24 | } 25 | } 26 | 27 | func TestRunningFromSystemService(t *testing.T) { 28 | testIsRunningSystemd(t) 29 | t.Parallel() 30 | 31 | // tests shouldn't be running as a service 32 | s, err := RunningFromSystemService() 33 | if err != nil { 34 | t.Error(err.Error()) 35 | } else if s { 36 | t.Errorf("tests aren't expected to run as a service") 37 | } 38 | } 39 | 40 | func TestCurrentUnitName(t *testing.T) { 41 | testIsRunningSystemd(t) 42 | 43 | fromService, err := RunningFromSystemService() 44 | if err != nil || !fromService { 45 | t.Skip("Not running from a systemd service") 46 | } 47 | 48 | s, err := CurrentUnitName() 49 | if err != nil { 50 | t.Error(err.Error()) 51 | } 52 | if s == "" { 53 | t.Error("CurrentUnitName returned a empty string") 54 | } 55 | } 56 | 57 | func TestGetMachineID(t *testing.T) { 58 | testIsRunningSystemd(t) 59 | 60 | id, err := GetMachineID() 61 | if err != nil { 62 | t.Error(err.Error()) 63 | } 64 | if id == "" { 65 | t.Error("GetMachineID returned a empty string") 66 | } 67 | } 68 | 69 | func TestGetRunningSlice(t *testing.T) { 70 | testIsRunningSystemd(t) 71 | 72 | s, err := getRunningSlice() 73 | if err != nil { 74 | t.Error(err.Error()) 75 | } 76 | if s == "" { 77 | t.Error("getRunningSlice returned a empty string") 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /daemon/sdnotify_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package daemon 16 | 17 | import ( 18 | "fmt" 19 | "net" 20 | "os" 21 | "testing" 22 | ) 23 | 24 | func TestSdNotify(t *testing.T) { 25 | testDir := t.TempDir() 26 | 27 | notifySocket := testDir + "/notify-socket.sock" 28 | laddr := net.UnixAddr{ 29 | Name: notifySocket, 30 | Net: "unixgram", 31 | } 32 | _, err := net.ListenUnixgram("unixgram", &laddr) 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | tests := []struct { 38 | unsetEnv bool 39 | envSocket string 40 | 41 | wsent bool 42 | werr bool 43 | }{ 44 | // (true, nil) - notification supported, data has been sent 45 | {false, notifySocket, true, false}, 46 | // (false, err) - notification supported, but failure happened 47 | {true, testDir + "/missing.sock", false, true}, 48 | // (false, nil) - notification not supported 49 | {true, "", false, false}, 50 | } 51 | 52 | for i, tt := range tests { 53 | t.Setenv("NOTIFY_SOCKET", tt.envSocket) 54 | sent, err := SdNotify(tt.unsetEnv, fmt.Sprintf("TestSdNotify test message #%d", i)) 55 | 56 | if sent != tt.wsent { 57 | t.Errorf("#%d: expected send result %t, got %t", i, tt.wsent, sent) 58 | } 59 | if tt.werr && err == nil { 60 | t.Errorf("#%d: want non-nil err, got nil", i) 61 | } else if !tt.werr && err != nil { 62 | t.Errorf("#%d: want nil err, got %v", i, err) 63 | } 64 | if tt.unsetEnv && tt.envSocket != "" && os.Getenv("NOTIFY_SOCKET") != "" { 65 | t.Errorf("#%d: environment variable not cleaned up", i) 66 | } 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /dbus/subscription_set.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package dbus 16 | 17 | import ( 18 | "time" 19 | ) 20 | 21 | // SubscriptionSet returns a subscription set which is like conn.Subscribe but 22 | // can filter to only return events for a set of units. 23 | type SubscriptionSet struct { 24 | *set 25 | conn *Conn 26 | } 27 | 28 | func (s *SubscriptionSet) filter(unit string) bool { 29 | return !s.Contains(unit) 30 | } 31 | 32 | // Subscribe starts listening for dbus events for all of the units in the set. 33 | // Returns channels identical to conn.SubscribeUnits. 34 | func (s *SubscriptionSet) Subscribe() (<-chan map[string]*UnitStatus, <-chan error) { 35 | // TODO: Make fully evented by using systemd 209 with properties changed values 36 | return s.conn.SubscribeUnitsCustom(time.Second, 0, 37 | mismatchUnitStatus, 38 | func(unit string) bool { return s.filter(unit) }, 39 | ) 40 | } 41 | 42 | // NewSubscriptionSet returns a new subscription set. 43 | func (c *Conn) NewSubscriptionSet() *SubscriptionSet { 44 | return &SubscriptionSet{newSet(), c} 45 | } 46 | 47 | // mismatchUnitStatus returns true if the provided UnitStatus objects 48 | // are not equivalent. false is returned if the objects are equivalent. 49 | // Only the Name, Description and state-related fields are used in 50 | // the comparison. 51 | func mismatchUnitStatus(u1, u2 *UnitStatus) bool { 52 | return u1.Name != u2.Name || 53 | u1.Description != u2.Description || 54 | u1.LoadState != u2.LoadState || 55 | u1.ActiveState != u2.ActiveState || 56 | u1.SubState != u2.SubState 57 | } 58 | -------------------------------------------------------------------------------- /activation/listeners_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package activation 16 | 17 | import ( 18 | "net" 19 | "os" 20 | "os/exec" 21 | "testing" 22 | ) 23 | 24 | // TestActivation forks out a copy of activation.go example and reads back two 25 | // strings from the pipes that are passed in. 26 | func TestListeners(t *testing.T) { 27 | arg0, cmdline := exampleCmd("listen") 28 | cmd := exec.Command(arg0, cmdline...) 29 | 30 | l1, err := net.Listen("tcp", ":9999") 31 | if err != nil { 32 | t.Fatal(err) 33 | } 34 | l2, err := net.Listen("tcp", ":1234") 35 | if err != nil { 36 | t.Fatal(err) 37 | } 38 | 39 | t1 := l1.(*net.TCPListener) 40 | t2 := l2.(*net.TCPListener) 41 | 42 | f1, _ := t1.File() 43 | f2, _ := t2.File() 44 | 45 | cmd.ExtraFiles = []*os.File{ 46 | f1, 47 | f2, 48 | } 49 | 50 | r1, err := net.Dial("tcp", "127.0.0.1:9999") 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | if _, err := r1.Write([]byte("Hi")); err != nil { 55 | t.Fatal(err) 56 | } 57 | 58 | r2, err := net.Dial("tcp", "127.0.0.1:1234") 59 | if err != nil { 60 | t.Fatal(err) 61 | } 62 | if _, err := r2.Write([]byte("Hi")); err != nil { 63 | t.Fatal(err) 64 | } 65 | 66 | cmd.Env = os.Environ() 67 | cmd.Env = append(cmd.Env, "LISTEN_FDS=2", "LISTEN_FDNAMES=fd1:fd2", "FIX_LISTEN_PID=1") 68 | 69 | out, err := cmd.CombinedOutput() 70 | if err != nil { 71 | t.Fatalf("Unexpected error: %v (command output: %s)", err, out) 72 | } 73 | 74 | correctStringWritten(t, r1, "Hello world: fd1") 75 | correctStringWritten(t, r2, "Goodbye world: fd2") 76 | } 77 | -------------------------------------------------------------------------------- /examples/activation/udpconn.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build ignore 16 | // +build ignore 17 | 18 | // Activation example used by the activation unit tests. 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | "net" 24 | "os" 25 | 26 | "github.com/coreos/go-systemd/v22/activation" 27 | ) 28 | 29 | func fixListenPid() { 30 | if os.Getenv("FIX_LISTEN_PID") != "" { 31 | // HACK: real systemd would set LISTEN_PID before exec'ing but 32 | // this is too difficult in golang for the purpose of a test. 33 | // Do not do this in real code. 34 | os.Setenv("LISTEN_PID", fmt.Sprintf("%d", os.Getpid())) 35 | } 36 | } 37 | 38 | func main() { 39 | fixListenPid() 40 | 41 | pc, err := activation.PacketConns() 42 | if err != nil { 43 | panic(err) 44 | } 45 | 46 | if os.Getenv("LISTEN_PID") != "" || os.Getenv("LISTEN_FDS") != "" || os.Getenv("LISTEN_FDNAMES") != "" { 47 | panic("Can not unset envs") 48 | } 49 | 50 | udp1, ok := pc[0].(*net.UDPConn) 51 | if !ok { 52 | panic("packetConn 1 not UDP") 53 | } 54 | udp2, ok := pc[1].(*net.UDPConn) 55 | if !ok { 56 | panic("packetConn 2 not UDP") 57 | } 58 | 59 | _, addr1, err := udp1.ReadFromUDP(nil) 60 | if err != nil { 61 | panic(err) 62 | } 63 | _, addr2, err := udp2.ReadFromUDP(nil) 64 | if err != nil { 65 | panic(err) 66 | } 67 | 68 | // Write out the expected strings to the two pipes 69 | _, err = udp1.WriteToUDP([]byte("Hello world"), addr1) 70 | if err != nil { 71 | panic(err) 72 | } 73 | _, err = udp2.WriteToUDP([]byte("Goodbye world"), addr2) 74 | if err != nil { 75 | panic(err) 76 | } 77 | 78 | return 79 | } 80 | -------------------------------------------------------------------------------- /unit/end_to_end_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 CoreOS, Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package unit 16 | 17 | import ( 18 | "bytes" 19 | "io" 20 | "testing" 21 | ) 22 | 23 | func TestDeserializeAndReserialize(t *testing.T) { 24 | tests := []struct { 25 | in string 26 | wout string 27 | }{ 28 | { 29 | `[Service] 30 | ExecStart=/bin/bash -c "while true; do echo \"ping\"; sleep 1; done" 31 | `, 32 | `[Service] 33 | ExecStart=/bin/bash -c "while true; do echo \"ping\"; sleep 1; done" 34 | `, 35 | }, 36 | { 37 | `[Unit] 38 | Description= Unnecessarily wrapped \ 39 | words here`, 40 | `[Unit] 41 | Description=Unnecessarily wrapped \ 42 | words here 43 | `, 44 | }, 45 | { 46 | `[Unit] 47 | Description=Demo \ 48 | 49 | Requires=docker.service 50 | `, 51 | `[Unit] 52 | Description=Demo \ 53 | 54 | Requires=docker.service 55 | `, 56 | }, 57 | { 58 | `; comment alpha 59 | # comment bravo 60 | [Unit] 61 | ; comment charlie 62 | # comment delta 63 | #Description=Foo 64 | Description=Bar 65 | ; comment echo 66 | # comment foxtrot 67 | `, 68 | `[Unit] 69 | Description=Bar 70 | `, 71 | }, 72 | } 73 | for i, tt := range tests { 74 | ds, err := Deserialize(bytes.NewBufferString(tt.in)) 75 | if err != nil { 76 | t.Errorf("case %d: unexpected error parsing unit: %v", i, err) 77 | continue 78 | } 79 | out, err := io.ReadAll(Serialize(ds)) 80 | if err != nil { 81 | t.Errorf("case %d: unexpected error serializing unit: %v", i, err) 82 | continue 83 | } 84 | if g := string(out); g != tt.wout { 85 | t.Errorf("case %d: incorrect output", i) 86 | t.Logf("Expected:\n%#v", tt.wout) 87 | t.Logf("Actual:\n%#v", g) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | CoreOS projects are [Apache 2.0 licensed](LICENSE) and accept contributions via 4 | GitHub pull requests. This document outlines some of the conventions on 5 | development workflow, commit message formatting, contact points and other 6 | resources to make it easier to get your contribution accepted. 7 | 8 | # Certificate of Origin 9 | 10 | By contributing to this project you agree to the Developer Certificate of 11 | Origin (DCO). This document was created by the Linux Kernel community and is a 12 | simple statement that you, as a contributor, have the legal right to make the 13 | contribution. See the [DCO](DCO) file for details. 14 | 15 | ## Getting Started 16 | 17 | - Fork the repository on GitHub 18 | - Read the [README](README.md) for build and test instructions 19 | - Play with the project, submit bugs, submit patches! 20 | 21 | ## Contribution Flow 22 | 23 | This is a rough outline of what a contributor's workflow looks like: 24 | 25 | - Create a topic branch from where you want to base your work (usually main). 26 | - Make commits of logical units. 27 | - Make sure your commit messages are in the proper format (see below). 28 | - Push your changes to a topic branch in your fork of the repository. 29 | - Make sure the tests pass, and add any new tests as appropriate. 30 | - Submit a pull request to the original repository. 31 | 32 | Thanks for your contributions! 33 | 34 | ### Format of the Commit Message 35 | 36 | We follow a rough convention for commit messages that is designed to answer two 37 | questions: what changed and why. The subject line should feature the what and 38 | the body of the commit should describe the why. 39 | 40 | ``` 41 | scripts: add the test-cluster command 42 | 43 | this uses tmux to setup a test cluster that you can easily kill and 44 | start for debugging. 45 | 46 | Fixes #38 47 | ``` 48 | 49 | The format can be described more formally as follows: 50 | 51 | ``` 52 | : 53 | 54 | 55 | 56 |