golang.org/x/sys@v0.20.1-0.20240517151509-673e0f94c16d/unix/syscall_solaris_test.go (about)

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  //go:build solaris
     6  
     7  package unix_test
     8  
     9  import (
    10  	"fmt"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"runtime"
    15  	"strings"
    16  	"testing"
    17  
    18  	"golang.org/x/sys/unix"
    19  )
    20  
    21  // getOneRetry wraps EventPort.GetOne which in turn wraps a syscall that can be
    22  // interrupted causing us to receive EINTR.
    23  // To prevent our tests from flaking, we retry the syscall until it works
    24  // rather than get unexpected results in our tests.
    25  func getOneRetry(t *testing.T, p *unix.EventPort, timeout *unix.Timespec) (e *unix.PortEvent, err error) {
    26  	t.Helper()
    27  	for {
    28  		e, err = p.GetOne(timeout)
    29  		if err != unix.EINTR {
    30  			break
    31  		}
    32  	}
    33  	return e, err
    34  }
    35  
    36  // getRetry wraps EventPort.Get which in turn wraps a syscall that can be
    37  // interrupted causing us to receive EINTR.
    38  // To prevent our tests from flaking, we retry the syscall until it works
    39  // rather than get unexpected results in our tests.
    40  func getRetry(t *testing.T, p *unix.EventPort, s []unix.PortEvent, min int, timeout *unix.Timespec) (n int, err error) {
    41  	t.Helper()
    42  	for {
    43  		n, err = p.Get(s, min, timeout)
    44  		if err != unix.EINTR {
    45  			break
    46  		}
    47  		// If we did get EINTR, make sure we got 0 events
    48  		if n != 0 {
    49  			t.Fatalf("EventPort.Get returned events on EINTR.\ngot: %d\nexpected: 0", n)
    50  		}
    51  	}
    52  	return n, err
    53  }
    54  
    55  func TestStatvfs(t *testing.T) {
    56  	if err := unix.Statvfs("", nil); err == nil {
    57  		t.Fatal(`Statvfs("") expected failure`)
    58  	}
    59  
    60  	statvfs := unix.Statvfs_t{}
    61  	if err := unix.Statvfs("/", &statvfs); err != nil {
    62  		t.Errorf(`Statvfs("/") failed: %v`, err)
    63  	}
    64  
    65  	if t.Failed() {
    66  		mount, err := exec.Command("mount").CombinedOutput()
    67  		if err != nil {
    68  			t.Logf("mount: %v\n%s", err, mount)
    69  		} else {
    70  			t.Logf("mount: %s", mount)
    71  		}
    72  	}
    73  }
    74  
    75  func TestSysconf(t *testing.T) {
    76  	n, err := unix.Sysconf(3 /* SC_CLK_TCK */)
    77  	if err != nil {
    78  		t.Fatalf("Sysconf: %v", err)
    79  	}
    80  	t.Logf("Sysconf(SC_CLK_TCK) = %d", n)
    81  }
    82  
    83  // Event Ports
    84  
    85  func TestBasicEventPort(t *testing.T) {
    86  	tmpfile, err := os.Create(filepath.Join(t.TempDir(), "eventport"))
    87  	if err != nil {
    88  		t.Fatal(err)
    89  	}
    90  	defer tmpfile.Close()
    91  	path := tmpfile.Name()
    92  
    93  	stat, err := os.Stat(path)
    94  	if err != nil {
    95  		t.Fatalf("Failed to stat %s: %v", path, err)
    96  	}
    97  	port, err := unix.NewEventPort()
    98  	if err != nil {
    99  		t.Fatalf("NewEventPort failed: %v", err)
   100  	}
   101  	defer port.Close()
   102  	cookie := stat.Mode()
   103  	err = port.AssociatePath(path, stat, unix.FILE_MODIFIED, cookie)
   104  	if err != nil {
   105  		t.Errorf("AssociatePath failed: %v", err)
   106  	}
   107  	if !port.PathIsWatched(path) {
   108  		t.Errorf("PathIsWatched unexpectedly returned false")
   109  	}
   110  	err = port.DissociatePath(path)
   111  	if err != nil {
   112  		t.Errorf("DissociatePath failed: %v", err)
   113  	}
   114  	err = port.AssociatePath(path, stat, unix.FILE_MODIFIED, cookie)
   115  	if err != nil {
   116  		t.Errorf("AssociatePath failed: %v", err)
   117  	}
   118  	bs := []byte{42}
   119  	tmpfile.Write(bs)
   120  	timeout := new(unix.Timespec)
   121  	timeout.Nsec = 100
   122  	pevent, err := getOneRetry(t, port, timeout)
   123  	if err == unix.ETIME {
   124  		t.Errorf("GetOne timed out: %v", err)
   125  	}
   126  	if err != nil {
   127  		t.Fatalf("GetOne failed: %v", err)
   128  	}
   129  	if pevent.Path != path {
   130  		t.Errorf("Path mismatch: %v != %v", pevent.Path, path)
   131  	}
   132  	err = port.AssociatePath(path, stat, unix.FILE_MODIFIED, cookie)
   133  	if err != nil {
   134  		t.Errorf("AssociatePath failed: %v", err)
   135  	}
   136  	err = port.AssociatePath(path, stat, unix.FILE_MODIFIED, cookie)
   137  	if err == nil {
   138  		t.Errorf("Unexpected success associating already associated path")
   139  	}
   140  }
   141  
   142  func TestEventPortFds(t *testing.T) {
   143  	_, path, _, _ := runtime.Caller(0)
   144  	stat, err := os.Stat(path)
   145  	cookie := stat.Mode()
   146  	port, err := unix.NewEventPort()
   147  	if err != nil {
   148  		t.Errorf("NewEventPort failed: %v", err)
   149  	}
   150  	defer port.Close()
   151  	r, w, err := os.Pipe()
   152  	if err != nil {
   153  		t.Errorf("unable to create a pipe: %v", err)
   154  	}
   155  	defer w.Close()
   156  	defer r.Close()
   157  	fd := r.Fd()
   158  
   159  	port.AssociateFd(fd, unix.POLLIN, cookie)
   160  	if !port.FdIsWatched(fd) {
   161  		t.Errorf("FdIsWatched unexpectedly returned false")
   162  	}
   163  	err = port.DissociateFd(fd)
   164  	err = port.AssociateFd(fd, unix.POLLIN, cookie)
   165  	bs := []byte{42}
   166  	w.Write(bs)
   167  	n, err := port.Pending()
   168  	if n != 1 {
   169  		t.Errorf("Pending() failed: %v, %v", n, err)
   170  	}
   171  	timeout := new(unix.Timespec)
   172  	timeout.Nsec = 100
   173  	pevent, err := getOneRetry(t, port, timeout)
   174  	if err == unix.ETIME {
   175  		t.Errorf("GetOne timed out: %v", err)
   176  	}
   177  	if err != nil {
   178  		t.Fatalf("GetOne failed: %v", err)
   179  	}
   180  	if pevent.Fd != fd {
   181  		t.Errorf("Fd mismatch: %v != %v", pevent.Fd, fd)
   182  	}
   183  	var c = pevent.Cookie
   184  	if c == nil {
   185  		t.Errorf("Cookie missing: %v != %v", cookie, c)
   186  		return
   187  	}
   188  	if c != cookie {
   189  		t.Errorf("Cookie mismatch: %v != %v", cookie, c)
   190  	}
   191  	port.AssociateFd(fd, unix.POLLIN, cookie)
   192  	err = port.AssociateFd(fd, unix.POLLIN, cookie)
   193  	if err == nil {
   194  		t.Errorf("unexpected success associating already associated fd")
   195  	}
   196  }
   197  
   198  func TestEventPortErrors(t *testing.T) {
   199  	tmpfile, err := os.CreateTemp("", "eventport")
   200  	if err != nil {
   201  		t.Errorf("unable to create a tempfile: %v", err)
   202  	}
   203  	path := tmpfile.Name()
   204  	stat, _ := os.Stat(path)
   205  	os.Remove(path)
   206  	port, _ := unix.NewEventPort()
   207  	defer port.Close()
   208  	err = port.AssociatePath(path, stat, unix.FILE_MODIFIED, nil)
   209  	if err == nil {
   210  		t.Errorf("unexpected success associating nonexistant file")
   211  	}
   212  	err = port.DissociatePath(path)
   213  	if err == nil {
   214  		t.Errorf("unexpected success dissociating unassociated path")
   215  	}
   216  	timeout := new(unix.Timespec)
   217  	timeout.Nsec = 1
   218  	_, err = getOneRetry(t, port, timeout)
   219  	if err != unix.ETIME {
   220  		t.Errorf("port.GetOne(%v) returned error %v, want %v", timeout, err, unix.ETIME)
   221  	}
   222  	err = port.DissociateFd(uintptr(0))
   223  	if err == nil {
   224  		t.Errorf("unexpected success dissociating unassociated fd")
   225  	}
   226  	events := make([]unix.PortEvent, 4)
   227  	_, err = getRetry(t, port, events, 5, nil)
   228  	if err == nil {
   229  		t.Errorf("unexpected success calling Get with min greater than len of slice")
   230  	}
   231  	_, err = getRetry(t, port, nil, 1, nil)
   232  	if err == nil {
   233  		t.Errorf("unexpected success calling Get with nil slice")
   234  	}
   235  	_, err = getRetry(t, port, nil, 0, nil)
   236  	if err == nil {
   237  		t.Errorf("unexpected success calling Get with nil slice")
   238  	}
   239  }
   240  
   241  func ExamplePortEvent() {
   242  	type MyCookie struct {
   243  		Name string
   244  	}
   245  	cookie := MyCookie{"Cookie Monster"}
   246  	port, err := unix.NewEventPort()
   247  	if err != nil {
   248  		fmt.Printf("NewEventPort failed: %v\n", err)
   249  		return
   250  	}
   251  	defer port.Close()
   252  	r, w, err := os.Pipe()
   253  	if err != nil {
   254  		fmt.Printf("os.Pipe() failed: %v\n", err)
   255  		return
   256  	}
   257  	defer w.Close()
   258  	defer r.Close()
   259  	fd := r.Fd()
   260  
   261  	port.AssociateFd(fd, unix.POLLIN, cookie)
   262  
   263  	bs := []byte{42}
   264  	w.Write(bs)
   265  	timeout := new(unix.Timespec)
   266  	timeout.Sec = 1
   267  	var pevent *unix.PortEvent
   268  	for {
   269  		pevent, err = port.GetOne(timeout)
   270  		if err != unix.EINTR {
   271  			break
   272  		}
   273  	}
   274  	if err != nil {
   275  		fmt.Printf("didn't get the expected event: %v\n", err)
   276  	}
   277  
   278  	// Use a type assertion to convert the received cookie back to its original type
   279  	c := pevent.Cookie.(MyCookie)
   280  	fmt.Printf("%s", c.Name)
   281  	//Output: Cookie Monster
   282  }
   283  
   284  func TestPortEventSlices(t *testing.T) {
   285  	port, err := unix.NewEventPort()
   286  	if err != nil {
   287  		t.Fatalf("NewEventPort failed: %v", err)
   288  	}
   289  	// Create, associate, and delete 6 files
   290  	for i := 0; i < 6; i++ {
   291  		tmpfile, err := os.CreateTemp("", "eventport")
   292  		if err != nil {
   293  			t.Fatalf("unable to create tempfile: %v", err)
   294  		}
   295  		path := tmpfile.Name()
   296  		stat, err := os.Stat(path)
   297  		if err != nil {
   298  			t.Fatalf("unable to stat tempfile: %v", err)
   299  		}
   300  		err = port.AssociatePath(path, stat, unix.FILE_MODIFIED, nil)
   301  		if err != nil {
   302  			t.Fatalf("unable to AssociatePath tempfile: %v", err)
   303  		}
   304  		err = os.Remove(path)
   305  		if err != nil {
   306  			t.Fatalf("unable to Remove tempfile: %v", err)
   307  		}
   308  	}
   309  	n, err := port.Pending()
   310  	if err != nil {
   311  		t.Errorf("Pending failed: %v", err)
   312  	}
   313  	if n != 6 {
   314  		t.Errorf("expected 6 pending events, got %d", n)
   315  	}
   316  	timeout := new(unix.Timespec)
   317  	timeout.Nsec = 1
   318  	events := make([]unix.PortEvent, 4)
   319  	n, err = getRetry(t, port, events, 3, timeout)
   320  	if err != nil {
   321  		t.Errorf("Get failed: %v", err)
   322  	}
   323  	if n != 4 {
   324  		t.Errorf("expected 4 events, got %d", n)
   325  	}
   326  	e := events[:n]
   327  	for _, p := range e {
   328  		if p.Events != unix.FILE_DELETE {
   329  			t.Errorf("unexpected event. got %v, expected %v", p.Events, unix.FILE_DELETE)
   330  		}
   331  	}
   332  	n, err = getRetry(t, port, events, 3, timeout)
   333  	if err != unix.ETIME {
   334  		t.Errorf("unexpected error. got %v, expected %v", err, unix.ETIME)
   335  	}
   336  	if n != 2 {
   337  		t.Errorf("expected 2 events, got %d", n)
   338  	}
   339  	e = events[:n]
   340  	for _, p := range e {
   341  		if p.Events != unix.FILE_DELETE {
   342  			t.Errorf("unexpected event. got %v, expected %v", p.Events, unix.FILE_DELETE)
   343  		}
   344  	}
   345  
   346  	r, w, err := os.Pipe()
   347  	if err != nil {
   348  		t.Fatalf("unable to create a pipe: %v", err)
   349  	}
   350  	port.AssociateFd(r.Fd(), unix.POLLIN, nil)
   351  	port.AssociateFd(w.Fd(), unix.POLLOUT, nil)
   352  	bs := []byte{41}
   353  	w.Write(bs)
   354  
   355  	n, err = getRetry(t, port, events, 1, timeout)
   356  	if err != nil {
   357  		t.Errorf("Get failed: %v", err)
   358  	}
   359  	if n != 2 {
   360  		t.Errorf("expected 2 events, got %d", n)
   361  	}
   362  	err = w.Close()
   363  	if err != nil {
   364  		t.Errorf("w.Close() failed: %v", err)
   365  	}
   366  	err = r.Close()
   367  	if err != nil {
   368  		t.Errorf("r.Close() failed: %v", err)
   369  	}
   370  	err = port.Close()
   371  	if err != nil {
   372  		t.Errorf("port.Close() failed: %v", err)
   373  	}
   374  }
   375  
   376  func TestLifreqSetName(t *testing.T) {
   377  	var l unix.Lifreq
   378  	err := l.SetName("12345678901234356789012345678901234567890")
   379  	if err == nil {
   380  		t.Fatal(`Lifreq.SetName should reject names that are too long`)
   381  	}
   382  	err = l.SetName("tun0")
   383  	if err != nil {
   384  		t.Errorf(`Lifreq.SetName("tun0") failed: %v`, err)
   385  	}
   386  }
   387  
   388  func TestLifreqGetMTU(t *testing.T) {
   389  	// Find links and their MTU using CLI tooling
   390  	// $ dladm show-link -p -o link,mtu
   391  	// net0:1500
   392  	out, err := exec.Command("dladm", "show-link", "-p", "-o", "link,mtu").Output()
   393  	if err != nil {
   394  		t.Fatalf("unable to use dladm to find data links: %v", err)
   395  	}
   396  	lines := strings.Split(string(out), "\n")
   397  	tc := make(map[string]string)
   398  	for _, line := range lines {
   399  		v := strings.Split(line, ":")
   400  		if len(v) == 2 {
   401  			tc[v[0]] = v[1]
   402  		}
   403  	}
   404  	ip_fd, err := unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0)
   405  	if err != nil {
   406  		t.Fatalf("could not open udp socket: %v", err)
   407  	}
   408  	var l unix.Lifreq
   409  	for link, mtu := range tc {
   410  		err = l.SetName(link)
   411  		if err != nil {
   412  			t.Fatalf("Lifreq.SetName(%q) failed: %v", link, err)
   413  		}
   414  		if err = unix.IoctlLifreq(ip_fd, unix.SIOCGLIFMTU, &l); err != nil {
   415  			t.Fatalf("unable to SIOCGLIFMTU: %v", err)
   416  		}
   417  		m := l.GetLifruUint()
   418  		if fmt.Sprintf("%d", m) != mtu {
   419  			t.Errorf("unable to read MTU correctly: expected %s, got %d", mtu, m)
   420  		}
   421  	}
   422  }