...

Source file src/github.com/coreos/go-systemd/v22/journal/journal_unix_test.go

Documentation: github.com/coreos/go-systemd/v22/journal

     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  //go:build !windows
    16  // +build !windows
    17  
    18  package journal_test
    19  
    20  import (
    21  	"fmt"
    22  	"os"
    23  	"os/exec"
    24  	"syscall"
    25  	"testing"
    26  
    27  	"github.com/coreos/go-systemd/v22/journal"
    28  )
    29  
    30  func TestJournalStreamParsing(t *testing.T) {
    31  	if _, ok := os.LookupEnv("JOURNAL_STREAM"); ok {
    32  		t.Fatal("unset JOURNAL_STREAM before running this test")
    33  	}
    34  
    35  	t.Run("Missing", func(t *testing.T) {
    36  		ok, err := journal.StderrIsJournalStream()
    37  		if err != nil {
    38  			t.Fatal(err)
    39  		}
    40  		if ok {
    41  			t.Error("stderr shouldn't be connected to journal stream")
    42  		}
    43  	})
    44  	t.Run("Present", func(t *testing.T) {
    45  		f, stat := getUnixStreamSocket(t)
    46  		defer f.Close()
    47  		os.Setenv("JOURNAL_STREAM", fmt.Sprintf("%d:%d", stat.Dev, stat.Ino))
    48  		defer os.Unsetenv("JOURNAL_STREAM")
    49  		replaceStderr(int(f.Fd()), func() {
    50  			ok, err := journal.StderrIsJournalStream()
    51  			if err != nil {
    52  				t.Fatal(err)
    53  			}
    54  			if !ok {
    55  				t.Error("stderr should've been connected to journal stream")
    56  			}
    57  		})
    58  	})
    59  	t.Run("NotMatching", func(t *testing.T) {
    60  		f, stat := getUnixStreamSocket(t)
    61  		defer f.Close()
    62  		os.Setenv("JOURNAL_STREAM", fmt.Sprintf("%d:%d", stat.Dev+1, stat.Ino))
    63  		defer os.Unsetenv("JOURNAL_STREAM")
    64  		replaceStderr(int(f.Fd()), func() {
    65  			ok, err := journal.StderrIsJournalStream()
    66  			if err != nil {
    67  				t.Fatal(err)
    68  			}
    69  			if ok {
    70  				t.Error("stderr shouldn't be connected to journal stream")
    71  			}
    72  		})
    73  	})
    74  	t.Run("Malformed", func(t *testing.T) {
    75  		f, stat := getUnixStreamSocket(t)
    76  		defer f.Close()
    77  		os.Setenv("JOURNAL_STREAM", fmt.Sprintf("%d-%d", stat.Dev, stat.Ino))
    78  		defer os.Unsetenv("JOURNAL_STREAM")
    79  		replaceStderr(int(f.Fd()), func() {
    80  			_, err := journal.StderrIsJournalStream()
    81  			if err == nil {
    82  				t.Fatal("JOURNAL_STREAM is malformed, but no error returned")
    83  			}
    84  		})
    85  	})
    86  }
    87  
    88  func TestStderrIsJournalStream(t *testing.T) {
    89  	const (
    90  		message = "TEST_MESSAGE"
    91  	)
    92  
    93  	userOrSystem := "--user"
    94  	if os.Getuid() == 0 {
    95  		userOrSystem = "--system"
    96  	}
    97  
    98  	if _, ok := os.LookupEnv("JOURNAL_STREAM"); !ok {
    99  		// Re-execute this test under systemd (see the else branch),
   100  		// and observe its exit code.
   101  		args := []string{
   102  			"systemd-run",
   103  			userOrSystem,
   104  			"--wait",
   105  			"--quiet",
   106  			"--",
   107  			os.Args[0],
   108  			"-test.run=TestStderrIsJournalStream",
   109  			"-test.count=1", // inhibit caching
   110  		}
   111  
   112  		cmd := exec.Command(args[0], args[1:]...)
   113  		cmd.Stderr = os.Stderr
   114  		if err := cmd.Run(); err != nil {
   115  			t.Fatal(err)
   116  		}
   117  	} else {
   118  		ok, err := journal.StderrIsJournalStream()
   119  		if err != nil {
   120  			t.Fatal(err)
   121  		}
   122  		if !ok {
   123  			t.Fatal("StderrIsJournalStream should've returned true")
   124  		}
   125  
   126  		err = journal.Send(message, journal.PriInfo, nil)
   127  		if err != nil {
   128  			t.Fatal(err)
   129  		}
   130  	}
   131  }
   132  
   133  func ExampleStderrIsJournalStream() {
   134  	// NOTE: this is just an example. Production code
   135  	// will likely use this to setup a logging library
   136  	// to write messages to either journal or stderr.
   137  	ok, err := journal.StderrIsJournalStream()
   138  	if err != nil {
   139  		panic(err)
   140  	}
   141  
   142  	if ok {
   143  		// use journal native protocol
   144  		journal.Send("this is a message logged through the native protocol", journal.PriInfo, nil)
   145  	} else {
   146  		// use stderr
   147  		fmt.Fprintln(os.Stderr, "this is a message logged through stderr")
   148  	}
   149  }
   150  
   151  func replaceStderr(fd int, cb func()) {
   152  	savedStderr, err := syscall.Dup(syscall.Stderr)
   153  	if err != nil {
   154  		panic(err)
   155  	}
   156  	defer syscall.Close(savedStderr)
   157  	err = syscall.Dup2(fd, syscall.Stderr)
   158  	if err != nil {
   159  		panic(err)
   160  	}
   161  	defer func() {
   162  		err := syscall.Dup2(savedStderr, syscall.Stderr)
   163  		if err != nil {
   164  			panic(err)
   165  		}
   166  	}()
   167  	cb()
   168  }
   169  
   170  // getUnixStreamSocket returns a unix stream socket obtained with
   171  // socketpair(2), and its fstat result. Only one end of the socket pair
   172  // is returned, and the other end is closed immediately: we don't need
   173  // it for our purposes.
   174  func getUnixStreamSocket(t *testing.T) (*os.File, *syscall.Stat_t) {
   175  	fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
   176  	if err != nil {
   177  		t.Fatal(os.NewSyscallError("socketpair", err))
   178  	}
   179  	// we don't need the remote end for our tests
   180  	syscall.Close(fds[1])
   181  
   182  	file := os.NewFile(uintptr(fds[0]), "unix-stream")
   183  	stat, err := file.Stat()
   184  	if err != nil {
   185  		t.Fatal(err)
   186  	}
   187  	return file, stat.Sys().(*syscall.Stat_t)
   188  }
   189  

View as plain text