当前位置: 首页> 财经> 创投人物 > linux下实现ipmitool raw用户接口

linux下实现ipmitool raw用户接口

时间:2025/7/22 9:28:22来源:https://blog.csdn.net/jj12345jj198999/article/details/142220527 浏览次数:0次

1、背景介绍

目前主流的服务器上都包含BMC芯片,用来实现对服务器的板级监控和管理。X86/飞腾可以配AST2500系列BMC芯片,鲲鹏可以配1711 BMC芯片。主CPU与BMC芯片之间可以通过标准的IPMI协议进行通讯,Linux中也提供了openipmi设备驱动以及ipmitool工具方便用户直接在终端中输入指令实现数据传输。

如果是x86搭配AST2500,在安装了openipmi设备驱动和ipmitool工具后,打开终端输入

ipmitool raw 6 4,此时显示返回值会是55 00,表明主CPU与BMC之间数据通路正常。

2、用户接口实现

当然,除了直接用system来调用终端中输入的ipmitool命令脚本外,也可以通过popen方式来操作,以下就是一个例子

int getSlot()
{FILE *fstream=NULL;char buff[1024];int errno=0;int temp1,temp2;memset(buff,0,sizeof(buff));if(NULL==(fstream=popen("ipmitool raw 6 0x2a","r"))){fprintf(stderr,"execute command failed:%s",strerror(errno));return -1;}while(NULL!=fgets(buff,sizeof(buff),fstream)){sscanf(buff, " %x %x", &temp1,&temp2);//	printf("temp1 is %d temp2 is %d %d\n",temp1,temp2,(~temp2)&0x1F);}gslot=(~temp2)&0x1F;gchasis=(~temp1)&0x7F;return 1;
}

不过无论是system("ipmitool raw 6 0x2a")还是上面这种popen方式,都需要执行shell命令,开销都不会太小,执行速度往往比较慢。

在Linux系统编程中,system调用和通过fstream(特别是std::ifstreamstd::ofstream,但更直接相关的是popen)调用外部脚本或程序,是两种常见的与外部程序交互的方式。它们各有特点,适用于不同的场景。下面详细解释这两种方式的区别:

1. system调用

  • 定义system函数是C标准库中的一个函数,用于执行一个命令字符串。它启动一个新的shell来执行指定的命令,并等待命令执行完成。
  • 用法int system(const char *command);
  • 特点
    • 简单易用,适合执行简单的外部命令。
    • 它会启动一个新的shell进程,因此会有额外的开销。
    • 不能直接获取命令的输出或输入,除非通过重定向(如system("command > output.txt"))。
    • 难以处理复杂的交互或实时数据流。
    • 返回值是命令的退出状态,而不是命令的输出。

2. popen调用

  • 定义popen函数是POSIX标准定义的,用于创建一个管道,并通过该管道执行一个shell命令,同时允许程序与这个命令进行输入输出交互。
  • 用法FILE *popen(const char *command, const char *type);
  • 特点
    • 允许程序与执行的命令进行输入输出交互。
    • 可以通过freadfwrite等标准I/O函数读取命令的输出或向命令发送输入。
    • 适用于需要处理命令输出或输入的复杂场景。
    • 相比systempopen提供了更细粒度的控制。
    • 仍然需要处理shell注入等安全问题。

3. 两者之间的主要区别

  • 交互性system不提供直接的输入输出交互能力,而popen允许程序与执行的命令进行输入输出交互。
  • 开销system通过启动新的shell来执行命令,因此通常比popen有更高的开销。
  • 用途system适合执行简单的、不需要交互的命令;而popen适合需要处理命令输出或输入的复杂场景。
  • 安全性:两者都需要注意避免shell注入等安全问题,但popen由于允许更细粒度的控制,可能更容易实现安全的命令执行。

如果需要更快的执行速度,可以采用ioctl方式进行操作,这个可以从ipmitool这个工具的源码中去找到具体的实现方法,以下就是用ioctl方式实现ipmitool raw 6 4的示例代码。


/** Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved.* * Redistribution and use in source and binary forms, with or without* modification, are permitted provided that the following conditions* are met:* * Redistribution of source code must retain the above copyright* notice, this list of conditions and the following disclaimer.* * Redistribution in binary form must reproduce the above copyright* notice, this list of conditions and the following disclaimer in the* documentation and/or other materials provided with the distribution.* * Neither the name of Sun Microsystems, Inc. or the names of* contributors may be used to endorse or promote products derived* from this software without specific prior written permission.* * This software is provided "AS IS," without a warranty of any kind.* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,* INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A* PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.* SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE* FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING* OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL* SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,* OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR* PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF* LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.*/
#define _POSIX_C_SOURCE 1#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/time.h>#define IPMI_BMC_CHANNEL		0xf
#define IPMI_SYSTEM_INTERFACE_ADDR_TYPE	0x0c
#define IPMI_BUF_SIZE 1024
#define IPMI_MAX_MD_SIZE 0x20
#define IPMI_OPENIPMI_READ_TIMEOUT 1
#define IPMI_MAX_ADDR_SIZE		0x20typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;struct ipmi_msg {unsigned char netfn;unsigned char cmd;unsigned short data_len;unsigned char *data;
};struct ipmi_req {unsigned char *addr;unsigned int addr_len;long msgid;struct ipmi_msg msg;
};struct ipmi_recv {int recv_type;unsigned char *addr;unsigned int addr_len;long msgid;struct ipmi_msg msg;
};struct ipmi_system_interface_addr {int addr_type;short channel;unsigned char lun;
};struct ipmi_rs {uint8_t ccode;uint8_t data[IPMI_BUF_SIZE];/** Looks like this is the length of the entire packet, including the RMCP* stuff, then modified to be the length of the extra IPMI message data*/int data_len;struct {uint8_t netfn;uint8_t cmd;uint8_t seq;uint8_t lun;} msg;struct {uint8_t authtype;uint32_t seq;uint32_t id;uint8_t bEncrypted;	/* IPMI v2 only */uint8_t bAuthenticated;	/* IPMI v2 only */uint8_t payloadtype;	/* IPMI v2 only *//* This is the total length of the payload orIPMI message.  IPMI v2.0 requires this tobe 2 bytes.  Not really used for much. */uint16_t msglen;} session;/** A union of the different possible payload meta-data*/union {struct {uint8_t rq_addr;uint8_t netfn;uint8_t rq_lun;uint8_t rs_addr;uint8_t rq_seq;uint8_t rs_lun;uint8_t cmd;} ipmi_response;struct {uint8_t message_tag;uint8_t rakp_return_code;uint8_t max_priv_level;uint32_t console_id;uint32_t bmc_id;uint8_t auth_alg;uint8_t integrity_alg;uint8_t crypt_alg;} open_session_response;struct {uint8_t message_tag;uint8_t rakp_return_code;uint32_t console_id;uint8_t bmc_rand[16];	/* Random number generated by the BMC */uint8_t bmc_guid[16];uint8_t key_exchange_auth_code[IPMI_MAX_MD_SIZE];} rakp2_message;struct {uint8_t message_tag;uint8_t rakp_return_code;uint32_t console_id;uint8_t integrity_check_value[IPMI_MAX_MD_SIZE];} rakp4_message;struct {uint8_t packet_sequence_number;uint8_t acked_packet_number;uint8_t accepted_character_count;uint8_t is_nack;	/* bool */uint8_t transfer_unavailable;	/* bool */uint8_t sol_inactive;	/* bool */uint8_t transmit_overrun;	/* bool */uint8_t break_detected;	/* bool */} sol_packet;} payload;
};struct ipmi_addr {int addr_type;short channel;char data[IPMI_MAX_ADDR_SIZE];
};#define IPMI_IOC_MAGIC			'i'
#define IPMICTL_SEND_COMMAND		_IOR(IPMI_IOC_MAGIC, 13, struct ipmi_req)
#define IPMICTL_RECEIVE_MSG_TRUNC	_IOWR(IPMI_IOC_MAGIC, 11, struct ipmi_recv)int fd=-1;int ipmidev_open()
{fd=open("/dev/ipmi0", O_RDWR);if(fd<0){printf("open ipmidev failed\n");return -1;}return fd;
}void ipmidev_close()
{if (fd >= 0){close(fd);}
}int ipmi_cmd_test(int netfun,int cmd)
{struct ipmi_system_interface_addr bmc_addr = {.addr_type = IPMI_SYSTEM_INTERFACE_ADDR_TYPE,.channel = IPMI_BMC_CHANNEL,};struct ipmi_req _req;struct ipmi_recv _recv;static struct ipmi_rs rsp;struct ipmi_addr addr;struct timeval read_timeout;int rset;int i=0;int retval=0;memset(&_req, 0, sizeof(struct ipmi_req));bmc_addr.lun =0;_req.addr = (unsigned char *) &bmc_addr;_req.addr_len = sizeof(bmc_addr);_req.msgid = 0;_req.msg.data = 0;_req.msg.data_len = 0x0;_req.msg.netfn =netfun;_req.msg.cmd =cmd;if(ioctl(fd, IPMICTL_SEND_COMMAND, &_req) < 0){printf( "Unable to send command\n");return -1;}read_timeout.tv_sec = IPMI_OPENIPMI_READ_TIMEOUT;read_timeout.tv_usec = 0;retval = select(fd, &rset, NULL, NULL, &read_timeout);_recv.addr = (unsigned char *) &addr;_recv.addr_len = sizeof(addr);_recv.msg.data =  rsp.data;_recv.msg.data_len =  sizeof(rsp.data);/* get data */if (ioctl(fd, IPMICTL_RECEIVE_MSG_TRUNC, &_recv) < 0) {printf( "Unable to recv command\n");return -1;}for (i = 0; i<_recv.msg.data_len ; i++)printf("%02x ", _recv.msg.data[i]);printf("\n");return 1;
}int main()
{ipmidev_open();ipmi_cmd_test(6,4);ipmidev_close();return 0;
}

上述代码中首先需要打开Ipmi设备,只要驱动加载了/dev下面就能找到ipmi0设备,然后调用ioctl给该设备发送数据并等待回应数据,注意这里需要延迟一段时间等待文件可读

	read_timeout.tv_sec = IPMI_OPENIPMI_READ_TIMEOUT;read_timeout.tv_usec = 0;retval = select(fd, &rset, NULL, NULL, &read_timeout);

这段代码是在使用select函数来监视一个或多个文件描述符(在这个例子中为fd),以等待文件描述符变得可读,同时设置了一个超时时间。下面是对这段代码的详细解释:

  1. 设置超时时间
    • read_timeout.tv_sec = 10;:这行代码设置超时时间的秒数为10秒。
    • read_timeout.tv_usec = 0;:这行代码设置超时时间的微秒数为0。微秒数(tv_usec)和秒数(tv_sec)共同构成了超时时间的总长度。因此,这里的超时时间总共是10秒。
  2. select函数调用
    • retval = select(fd, &rset, NULL, NULL, &read_timeout);:这行代码调用了select函数,用于监视文件描述符集合rset中的文件描述符是否变得可读。

当文件可读后再调用ioctl获取返回值即可,注意ipmi协议中返回值第一个字节是completion code,正常情况下是0,所以这里打印出来的返回值会比直接在shell中输入ipmitool 命令返回值多一个字节。

这里拿鲲鹏平台进行测试验证,shell中输入ipmitool 命令返回值如下: 

执行上述程序返回值如下:

可以看到返回值是正确的,这样就可以不需要执行shell脚本实现主芯片与BMC芯片的交互。

关键字:linux下实现ipmitool raw用户接口

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: