1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
|
package ipmi
import (
"context"
"fmt"
"math/rand"
"os"
"unsafe"
"github.com/bougou/go-ipmi/open"
)
type openipmi struct {
myAddr uint8
msgID int64
targetAddr uint8
targetChannel uint8
targetIPMBAddr uint8
transitAddr uint8
transitLUN uint8
file *os.File // /dev/ipmi0
}
// ConnectOpen try to initialize the client by open the device of linux ipmi driver.
func (c *Client) ConnectOpen(ctx context.Context, devnum int32) error {
c.Debugf("Using ipmi device %d\n", devnum)
// try the following devices
var (
ipmiDev1 string = fmt.Sprintf("/dev/ipmi%d", devnum)
ipmiDev2 string = fmt.Sprintf("/dev/ipmi/%d", devnum)
ipmiDev3 string = fmt.Sprintf("/dev/ipmidev/%d", devnum)
)
var file *os.File
var tryOpenFile = func(ipmiDev string) {
if file != nil {
return
}
if f, err := os.OpenFile(ipmiDev, os.O_RDWR, 0); err != nil {
c.Debugf("can not open ipmi dev (%s), err: %w\n", ipmiDev, err)
} else {
file = f
c.Debugf("opened ipmi dev file: %v\n", ipmiDev)
}
}
tryOpenFile(ipmiDev1)
tryOpenFile(ipmiDev2)
tryOpenFile(ipmiDev3)
if file == nil {
return fmt.Errorf("ipmi dev file not opened")
}
c.Debugf("opened ipmi dev file: %v, descriptor is: %d\n", file, file.Fd())
// set opened ipmi dev file
c.openipmi.file = file
var receiveEvents uint32 = 1
if err := open.IOCTL(c.openipmi.file.Fd(), open.IPMICTL_SET_GETS_EVENTS_CMD, uintptr(unsafe.Pointer(&receiveEvents))); err != nil {
return fmt.Errorf("ioctl failed, cloud not enable event receiver, err: %w", err)
}
return nil
}
// closeOpen closes the ipmi dev file.
func (c *Client) closeOpen(ctx context.Context) error {
if err := c.openipmi.file.Close(); err != nil {
return fmt.Errorf("close open file failed, err: %w", err)
}
return nil
}
func (c *Client) exchangeOpen(ctx context.Context, request Request, response Response) error {
if c.openipmi.targetAddr != 0 && c.openipmi.targetAddr != c.openipmi.myAddr {
} else {
// otherwise use system interface
c.Debugf("\nSending request [%s] (%#02x) to System Interface\n", request.Command().Name, request.Command().ID)
}
recv, err := c.openSendRequest(ctx, request)
if err != nil {
return fmt.Errorf("openSendRequest failed, err: %w", err)
}
c.DebugBytes("recv data", recv, 16)
c.Debugf("\n\n")
// recv[0] is cc
if len(recv) < 1 {
return fmt.Errorf("recv data at least contains one completion code byte")
}
ccode := recv[0]
if ccode != 0x00 {
return &ResponseError{
completionCode: CompletionCode(ccode),
description: fmt.Sprintf("ipmiRes CompletionCode (%#02x) is not normal: %s", ccode, StrCC(response, ccode)),
}
}
var unpackData = []byte{}
if len(recv) > 1 {
unpackData = recv[1:]
}
if err := response.Unpack(unpackData); err != nil {
return &ResponseError{
completionCode: CompletionCode(recv[0]),
description: fmt.Sprintf("unpack response failed, err: %s", err),
}
}
c.Debug("<< Command Response", response)
return nil
}
func (c *Client) openSendRequest(ctx context.Context, request Request) ([]byte, error) {
var dataPtr *byte
cmdData := request.Pack()
c.DebugBytes("cmdData", cmdData, 16)
if len(cmdData) > 0 {
dataPtr = &cmdData[0]
}
c.DebugBytes("cmd data", cmdData, 16)
msg := &open.IPMI_MSG{
NetFn: uint8(request.Command().NetFn),
Cmd: uint8(request.Command().ID),
Data: dataPtr,
DataLen: uint16(len(cmdData)),
}
addr := &open.IPMI_SYSTEM_INTERFACE_ADDR{
AddrType: open.IPMI_SYSTEM_INTERFACE_ADDR_TYPE,
Channel: open.IPMI_BMC_CHANNEL,
LUN: 0,
}
commandContext := GetCommandContext(ctx)
if commandContext != nil {
c.Debug("Got CommandContext:", commandContext)
if commandContext.responderAddr != nil {
}
if commandContext.responderLUN != nil {
addr.LUN = *commandContext.responderLUN
}
if commandContext.requesterAddr != nil {
}
if commandContext.requesterLUN != nil {
}
}
req := &open.IPMI_REQ{
Addr: addr,
AddrLen: int(unsafe.Sizeof(addr)),
MsgID: rand.Int63(),
Msg: *msg,
}
c.Debug("IPMI_REQ", req)
return open.SendCommand(c.openipmi.file, req, c.timeout)
}
|