github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/ipmi/dev.go (about)

     1  // Copyright 2019-2022 the u-root 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  package ipmi
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  	"runtime"
    11  	"unsafe"
    12  
    13  	"golang.org/x/sys/unix"
    14  )
    15  
    16  type dev struct {
    17  	f *os.File
    18  	syscalls
    19  }
    20  
    21  // SendRequest uses unix.Syscall IOCTL to send a request to the BMC.
    22  func (d *dev) SendRequest(req *request) error {
    23  	_, _, err := d.syscall(unix.SYS_IOCTL, d.File().Fd(), _IPMICTL_SEND_COMMAND, uintptr(unsafe.Pointer(req)))
    24  	runtime.KeepAlive(req)
    25  	if err != 0 {
    26  		return fmt.Errorf("syscall failed with: %v", err)
    27  	}
    28  	return nil
    29  }
    30  
    31  // ReceiveResponse uses syscall Rawconn to read a response via unix.Syscall IOCTL from the BMC.
    32  // It takes the message ID of the request and awaits the response with the same message ID.
    33  func (d *dev) ReceiveResponse(msgID int64, resp *response, buf []byte) ([]byte, error) {
    34  	var result []byte
    35  	var rerr error
    36  	readMsg := func(fd uintptr) bool {
    37  		_, _, errno := d.syscall(unix.SYS_IOCTL, d.File().Fd(), _IPMICTL_RECEIVE_MSG_TRUNC, uintptr(unsafe.Pointer(resp)))
    38  		runtime.KeepAlive(resp)
    39  		if errno != 0 {
    40  			rerr = fmt.Errorf("ioctlGetRecv failed with %v", errno)
    41  			return false
    42  		}
    43  
    44  		if resp.msgid != msgID {
    45  			rerr = fmt.Errorf("received wrong message")
    46  			return false
    47  		}
    48  
    49  		if resp.msg.DataLen >= _IPMI_BUF_SIZE {
    50  			rerr = fmt.Errorf("data length received too large: %d > %d", resp.msg.DataLen, _IPMI_BUF_SIZE)
    51  		} else if buf[0] != 0 {
    52  			rerr = fmt.Errorf("invalid response, expected first byte of response to be 0, got: %v", buf[0])
    53  		} else {
    54  			result = buf[:resp.msg.DataLen:resp.msg.DataLen]
    55  			rerr = nil
    56  		}
    57  		return true
    58  	}
    59  
    60  	// Read response.
    61  	conn, err := d.fileSyscallConn(d.File())
    62  	if err != nil {
    63  		return nil, fmt.Errorf("failed to get file rawconn: %v", err)
    64  	}
    65  	if err := d.fileSetReadDeadline(d.File(), timeout); err != nil {
    66  		return nil, fmt.Errorf("failed to set read deadline: %v", err)
    67  	}
    68  	if err := d.connRead(readMsg, conn); err != nil {
    69  		return nil, fmt.Errorf("failed to read rawconn: %v", err)
    70  	}
    71  
    72  	return result, rerr
    73  }
    74  
    75  // File returns the file of Dev
    76  func (d *dev) File() *os.File {
    77  	return d.f
    78  }
    79  
    80  // Close closes the file attached to dev.
    81  func (d *dev) Close() error {
    82  	return d.f.Close()
    83  }
    84  
    85  // newDev takes a file and returns a new Dev
    86  func newDev(f *os.File) *dev {
    87  	return &dev{
    88  		f:        f,
    89  		syscalls: &realSyscalls{},
    90  	}
    91  }