null

Портирование OpenIPMI на FreeBSD

В ходе подготовки к проведению меропрятия Sun Tech Days 2010 я столкнулся с необходимостью мониторинга большого количества оборудования разных вендоров. В результате небольшого обзора существующих открытых систем мониторинга я остановил свой выбор на Zabbix. Среди используемого оборудования были сервера Sun: x4150 и x4200. Сбор информации для системы мониторинга на уровне ОС обеспечивал агент Zabbix, сбор информации на уровне аппаратной платформы обеспечивал сервисный процессор ILOM, доступный по протоколу IPMI. Zabbix может контролировать работу IPMI-совместимых устройств, в том случае, если он собран с поддержкой этого протокола. Однако на момент запуска системы в коллекции портов FreeBSD для zabbix-server отсутствовала опция сборки, отвечающая за поддержку IPMI, поэтому я попробовал ее добавить.

Для сборки Zabbix с поддержкой IPMI в системе необходимо наличие пакета OpenIPMI, которого также не оказалось в коллекции портов. Поэтому я решил создать такой порт. Но прежде чем писать Makefile я попробовал собрать пакет OpenIPMI традиционным способом - configure/make/make install. В списке обязательных зависимостей OpenIPMI значились:

  * popt
  * curses, ncurses or termcap

в списке опциональных:

  * netsnmp or ucdsnmp
  * openssl
  * glib
  * swig
  * perl
  * python
  * Tcl/Tk
  * Tkinter/Tix
  * gdbm

Для начала я решил обойтись достаточным для моей задачи минимумом:

$ tar xf OpenIPMI-2.0.16.tar.gz 
$ cd OpenIPMI-2.0.16
$ CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib" \
	./configure --with-openssl  --prefix=/var/tmp/openipmi


Но начав сборку, почти сразу же получил ошибку:

$ gmake
...
 gcc -DHAVE_CONFIG_H -I. -I. -I.. -I/usr/local/include -Wall -Wsign-compare -I../include 
-DIPMI_CHECK_LOCKS -g -O2 -MT oem_intel.lo -MD -MP -MF .deps/oem_intel.Tpo -c oem_intel.c  
-fPIC -DPIC -o .libs/oem_intel.o
oem_intel.c:34:20: error: alloca.h: No such file or directory
gmake[2]: *** [oem_intel.lo] Ошибка 1

 

решение быстро нашлось в списках рассылки:

FreeBSD has no system-wide 'alloca.h'. Instead it uses a routine in 
'stdlib.h' for this. Here is a copy of the section in /usr/include/stdlib.h:

/*
  * The alloca() function can't be implemented in C, and on some
  * platforms it can't be implemented at all as a callable function.
  * The GNU C compiler provides a built-in alloca() which we can use;
  * in all other cases, provide a prototype, mainly to pacify various
  * incarnations of lint.  On platforms where alloca() is not in libc,
  * programs which use it will fail to link when compiled with non-GNU
  * compilers.
  */

 

Поправив все заголовочные файлы, в которых упоминался alloca.h, я продолжил сборку и в скором времени получил еще одну ошибку:

 gcc -DHAVE_CONFIG_H -I. -I. -I.. -I/usr/local/include -Wall -Wsign-compare -I../include 
-DIPMI_CHECK_LOCKS -g -O2 -MT fru_spd_decode.lo -MD -MP -MF .deps/fru_spd_decode.Tpo 
-c fru_spd_decode.c  -fPIC -DPIC -o .libs/fru_spd_decode.o
fru_spd_decode.c:42:20: error: values.h: No such file or directory
gmake[2]: *** [fru_spd_decode.lo] Ошибка 1

 

Эта проблема также легко решается заменой (согласно стадарта) values.h на limits.h. Но на этом ошибки не закончились:

gcc -DHAVE_CONFIG_H -I. -I. -I.. -I/usr/local/include -Wall -Wsign-compare -I../include -g 
-O2 -MT selector.lo -MD -MP -MF .deps/selector.Tpo -c selector.c  -fPIC -DPIC 
-o .libs/selector.o
In file included from selector.c:52:
/usr/include/malloc.h:3:2: error: #error "<malloc.h> has been replaced by <stdlib.h>"
selector.c: In function 'sel_alloc_selector':

 

Такая ошибка наиболее часто встречается при сборке программ на FreeBSD. После удаления ненужных ссылок на malloc.h сборка прервалась снова:

gcc -Wall -Wsign-compare -I../include -g -O2 -o .libs/test_handlers test_handlers.o  
-L/usr/local/lib ./.libs/libOpenIPMIposix.so ./.libs/libOpenIPMIpthread.so -lpthread 
/home/tiamat/Downloads/OpenIPMI-2.0.16/utils/.libs/libOpenIPMIutils.so 
../utils/.libs/libOpenIPMIutils.so /usr/local/lib/libgdbm.so -lcurses  -Wl,--rpath 
-Wl,/var/tmp/openipmi/lib -Wl,--rpath -Wl,/usr/local/lib
/home/tiamat/Downloads/OpenIPMI-2.0.16/utils/.libs/libOpenIPMIutils.so: undefined 
reference to `backtrace'
gmake[2]: *** [test_handlers] Ошибка 1

 

Ошибка связана с отсутствием в libc соответствующей функции и присутствии в системе пакета libexecinfo:

$ pkg_info libexecinfo-1.1_3
Information for libexecinfo-1.1_3:

Comment:
A library for inspecting program's backtrace


Required by:
samba34-3.4.5_1


Description:
This is a quick-n-dirty BSD licensed clone of backtrace facility found
in the GNU libc, mainly intended for porting linuxish code to BSD
platforms, however it can be used at any platform which has a gcc
compiler.

WWW: http://www.gnu.org/software/libc/manual/html_node/Backtraces.html

 

Поправив configure.in и пересоздав при помощи autotools файл configure я продолжил:

libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I. -I.. -I/usr/local/include -Wall 
-Wsign-compare -I../include -I../libedit -DIPMI_CHECK_LOCKS -D_REENTRANT 
-I/usr/local/include/glib-2.0 -I/usr/local/lib/glib-2.0/include -g -O2 -MT out_fru.lo 
-MD -MP -MF .deps/out_fru.Tpo -c out_fru.c  -fPIC -DPIC -o .libs/out_fru.o
out_fru.c: In function 'traverse_fru_node_tree':
out_fru.c:123: error: 'MAXINT' undeclared (first use in this function)
out_fru.c:123: error: (Each undeclared identifier is reported only once
out_fru.c:123: error: for each function it appears in.)
out_fru.c: In function 'ipmi_cmdlang_dump_fru_info':
out_fru.c:160: error: 'MAXINT' undeclared (first use in this function)
gmake[2]: *** [out_fru.lo] Ошибка 1

 

Вместо MAXINT надобно применять (опять-таки в соответствии со стандартом) INT_MAX:

if gcc -DHAVE_CONFIG_H -I. -I. -I..   -I/usr/local/include -Wall -Wsign-compare 
-I../include -I../libedit -DIPMI_CHECK_LOCKS -D_REENTRANT -I/usr/local/include/glib-2.0 
-I/usr/local/lib/glib-2.0/include    -g -O2 -MT ipmish.o -MD -MP -MF ".deps/ipmish.Tpo" 
-c -o ipmish.o ipmish.c; \
	then mv -f ".deps/ipmish.Tpo" ".deps/ipmish.Po"; else rm -f ".deps/ipmish.Tpo"; 
exit 1; fi
ipmish.c: In function 'setup_term':
ipmish.c:655: error: 'SIGPWR' undeclared (first use in this function)

 

Такого сигнала в ОС FreeBSD просто нет:

$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGEMT	 8) SIGFPE	 9) SIGKILL	10) SIGBUS
11) SIGSEGV	12) SIGSYS	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGURG	17) SIGSTOP	18) SIGTSTP	19) SIGCONT	20) SIGCHLD
21) SIGTTIN	22) SIGTTOU	23) SIGIO	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGINFO	30) SIGUSR1
31) SIGUSR2

 

Соответственно добавляем неообходимые #ifdef и продолжаем:

libtool: compile:  gcc -DHAVE_CONFIG_H -I. -I. -I.. -I/usr/local/include -Wall 
-Wsign-compare -I../include -DIPMI_CHECK_LOCKS -g -O2 -MT lanserv_ipmi.lo -MD -MP 
-MF .deps/lanserv_ipmi.Tpo -c lanserv_ipmi.c  -fPIC -DPIC -o .libs/lanserv_ipmi.o
In file included from ./OpenIPMI/lanserv.h:63,
                 from lanserv_ipmi.c:68:
/usr/include/resolv.h:161: error: array type has incomplete element type
/usr/include/resolv.h:175: error: field 'addr' has incomplete type
/usr/include/resolv.h:199: error: field 'sin' has incomplete type
In file included from lanserv_ipmi.c:68:
./OpenIPMI/lanserv.h:502: error: field 's_addr4' has incomplete type
./OpenIPMI/lanserv.h:504: error: field 's_addr6' has incomplete type
gmake[3]: *** [lanserv_ipmi.lo] Ошибка 1

 

В man resolver ясно сказано:

LIBRARY
     Standard C Library (libc, -lc)

SYNOPSIS
     #include <sys/types.h>
     #include <netinet/in.h>
     #include <arpa/nameser.h>
     #include <resolv.h>

 

а в исходных файлах явно не хватает нужных файлов. Добавляем их и идем дальше:

if gcc -DHAVE_CONFIG_H -I. -I. -I..   -I/usr/local/include -Wall -Wsign-compare -I../include 
-DIPMI_CHECK_LOCKS  -g -O2 -MT emu_cmd.o -MD -MP -MF ".deps/emu_cmd.Tpo" -c -o emu_cmd.o 
emu_cmd.c; \
	then mv -f ".deps/emu_cmd.Tpo" ".deps/emu_cmd.Po"; else rm -f ".deps/emu_cmd.Tpo"; 
exit 1; fi
In file included from emu_cmd.c:6:
emu.h:12: warning: 'struct timeval' declared inside parameter list
emu.h:12: warning: its scope is only this definition or declaration, which is probably not what you want
emu.h:18: warning: 'struct timeval' declared inside parameter list
emu_cmd.c: In function 'sleep_cmd':
emu_cmd.c:790: error: storage size of 'tv' isn't known
emu_cmd.c:790: warning: unused variable 'tv'


Опять же - для использования timeval необходим заголовочный файл sys/time.h. Добавляем его и на этом сборка успешно завершается!

Но собранная программа не всегда означает рабочую программу. При запуске ipmicmd выдает ошибку:

$ /var/tmp/openipmi/bin/ipmicmd  lan -U root -P passwd 10.2.2.1
=> f 0 6 1
Error sending command: 16

 

Копания в исходном тексте программы показали, что возникает ошибка errno=22(Invalid argument) при попытке соединится с удаленным хостом в момент вызова sendto:

lib/ipmi_lan.c:

    rv = sendto(lan->fd->fd, tmsg, pos, 0,
                (struct sockaddr *) &(lan->cparm.ip_addr[addr_num]),
                sizeof(sockaddr_ip_t));
    if (rv == -1) {


При этом последний аргумент sendto равен 28. В соответствии со страницей руководства (man sendto) последний аргумент представляет собой длину адреса получателя:

    ssize_t
     sendto(int s, const void *msg, size_t len, int flags,
         const struct sockaddr *to, socklen_t tolen);


    The address of the target is given by to with tolen specifying its size.
     The length of the message is given by len.  If the message is too long to
     pass atomically through the underlying protocol, the error EMSGSIZE is
     returned, and the message is not transmitted.

     No indication of failure to deliver is implicit in a send().  Locally
     detected errors are indicated by a return value of -1.

 

Для IPv4 эта длина составляет 16 байт и в нашем случае это справедливо:

(gdb) p lan->fd->fd
$1 = 3
(gdb) p *tmsg
$2 = 6 '\006'
(gdb) print pos
$3 = 23
(gdb) p lan->cparm.ip_addr[addr_num]
$4 = {s_ipsock = {s_addr = {sa_len = 16 '\020', sa_family = 2 '\002',
      sa_data = "\002o\n\002\027\210\000\000\000\000\000\000\000"}, s_addr4 = {
      sin_len = 16 '\020', sin_family = 2 '\002', sin_port = 28418,
      sin_addr = {s_addr = 2283209226},
      sin_zero = "\000\000\000\000\000\000\000"}, s_addr6 = {
      sin6_len = 16 '\020', sin6_family = 2 '\002', sin6_port = 28418,
      sin6_flowinfo = 2283209226, sin6_addr = {__u6_addr = {
          __u6_addr8 = '\0' <repeats 15 times>, __u6_addr16 = {0, 0, 0, 0, 0,
            0, 0, 0}, __u6_addr32 = {0, 0, 0, 0}}}, sin6_scope_id = 0}}}

(gdb) p *(struct sockaddr *) &(lan->cparm.ip_addr[addr_num])
$2 = {sa_len = 16 '\020', sa_family = 2 '\002',
  sa_data = "\002o\n\002\027\210\000\000\000\000\000\000\000"}

 

В исходных текстах OpenIPMI адрес хранится в структуре sockaddr_ip_t:

/* Because sizeof(sockaddr_in6) > sizeof(sockaddr_in), this structure
 * is used as a replacement of struct sockaddr. */
typedef struct sockaddr_ip_s {
    union
        {
            struct sockaddr     s_addr;
            struct sockaddr_in  s_addr4;
#ifdef PF_INET6
            struct sockaddr_in6 s_addr6;
#endif
        } s_ipsock;
} sockaddr_ip_t;

 

Вот способом хранения и объясняется ошибка - независимо от используемого адреса (IPv4 или IPv6) размер этой структуры всегда равен наибольшему составляющему объединение элементу. А данном случае это struct sockaddr_in6 s_addr6, размер которой и дает неправильный tolen=28 в sendto.

Наибол
е примечателен тот факт, что несмотря на очевидную ошибку весь это код успешно работает на Linux! В качестве иллюстрации вышесказанного можно воспользоваться примером из статьи Википедии про Berkeley sockets. В этой статье есть пример клиент-сервера для UDP протокола. Если модифицировать клиента таким образом, чтобы он использовал в качестве последнего аргумента sendto что-нибудь отличное от sizeof (struct sockaddr_in) то мы получим программу, успешно работающую на Linux и не работающую (с ошибкой 22 Invalid argument) на FreeBSD.

Оформив все изменения в виде патча, обеспечивающего возможность сборки OpenIPMI на FreeBSD:

diff -ur OpenIPMI/cmdlang/ipmish.c OpenIPMI-patched/cmdlang/ipmish.c
--- OpenIPMI/cmdlang/ipmish.c	2006-04-06 18:13:46.000000000 +0400
+++ OpenIPMI-patched/cmdlang/ipmish.c	2010-04-19 16:47:19.708782708 +0400
@@ -652,7 +652,9 @@
     signal(SIGPIPE, cleanup_sig);
     signal(SIGUSR1, cleanup_sig);
     signal(SIGUSR2, cleanup_sig);
+#ifdef SIGPWR
     signal(SIGPWR, cleanup_sig);
+#endif
 
     stifle_history(500);
     rl_callback_handler_install("> ", rl_ipmish_cb_handler);
diff -ur OpenIPMI/cmdlang/out_fru.c OpenIPMI-patched/cmdlang/out_fru.c
--- OpenIPMI/cmdlang/out_fru.c	2006-10-25 23:04:08.000000000 +0400
+++ OpenIPMI-patched/cmdlang/out_fru.c	2010-04-16 00:20:11.176952295 +0400
@@ -33,7 +33,7 @@
 
 #include <errno.h>
 #include <string.h>
-#include <values.h>
+#include <limits.h>
 #include <OpenIPMI/ipmi_bits.h>
 #include <OpenIPMI/ipmi_fru.h>
 #include <OpenIPMI/ipmi_cmdlang.h>
@@ -120,7 +120,7 @@
 	    if (intval != -1)
 		ipmi_cmdlang_out_int(cmd_info, "Element Count", intval);
 	    else
-		intval = MAXINT;
+		intval = INT_MAX;
 	    traverse_fru_node_tree(cmd_info, sub_node, intval);
 	    ipmi_cmdlang_up(cmd_info);
 	    break;
@@ -157,7 +157,7 @@
     rv = ipmi_fru_get_root_node(fru, &type, &node);
     if (!rv) {
 	ipmi_cmdlang_out(cmd_info, "Type", type);
-	rv = traverse_fru_node_tree(cmd_info, node, MAXINT);
+	rv = traverse_fru_node_tree(cmd_info, node, INT_MAX);
 	if (rv)
 	    cmdlang->errstr = "Error traversing FRU node tree";
     } else {
diff -ur OpenIPMI/configure.in OpenIPMI-patched/configure.in
--- OpenIPMI/configure.in	2009-03-17 03:56:38.000000000 +0300
+++ OpenIPMI-patched/configure.in	2010-04-19 17:03:02.663825318 +0400
@@ -22,9 +22,6 @@
 
 AC_SUBST(OPENIPMI_SMI)
 
-# Check for execinfo.h
-AC_CHECK_HEADERS(execinfo.h)
-
 SNMPLIBS=
 
 # Where do we find the UCD SNMP includes and libs
diff -ur OpenIPMI/lanserv/OpenIPMI/lanserv.h OpenIPMI-patched/lanserv/OpenIPMI/lanserv.h
--- OpenIPMI/lanserv/OpenIPMI/lanserv.h	2008-03-12 20:53:21.000000000 +0300
+++ OpenIPMI-patched/lanserv/OpenIPMI/lanserv.h	2010-04-16 00:50:52.790912170 +0400
@@ -60,7 +60,10 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
 #include <resolv.h>
+#include <netdb.h>
 
 #include <OpenIPMI/ipmi_auth.h>
 
diff -ur OpenIPMI/lanserv/emu.h OpenIPMI-patched/lanserv/emu.h
--- OpenIPMI/lanserv/emu.h	2006-10-24 20:50:30.000000000 +0400
+++ OpenIPMI-patched/lanserv/emu.h	2010-04-16 00:52:25.971815329 +0400
@@ -2,6 +2,7 @@
 #ifndef __EMU_IPMI_
 #define __EMU_IPMI_
 
+#include <sys/time.h>
 #include <OpenIPMI/ipmi_types.h>
 
 typedef struct emu_data_s emu_data_t;
diff -ur OpenIPMI/lanserv/lanserv.c OpenIPMI-patched/lanserv/lanserv.c
--- OpenIPMI/lanserv/lanserv.c	2006-05-02 17:54:53.000000000 +0400
+++ OpenIPMI-patched/lanserv/lanserv.c	2010-04-16 00:22:33.562882071 +0400
@@ -68,7 +68,6 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <popt.h> /* Option parsing made easy */
-#include <malloc.h>
 #include <sys/ioctl.h>
 #if HAVE_SYSLOG
 #include <syslog.h>
diff -ur OpenIPMI/lanserv/lanserv_emu.c OpenIPMI-patched/lanserv/lanserv_emu.c
--- OpenIPMI/lanserv/lanserv_emu.c	2006-10-24 20:50:30.000000000 +0400
+++ OpenIPMI-patched/lanserv/lanserv_emu.c	2010-04-16 00:22:40.728197495 +0400
@@ -69,7 +69,6 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <popt.h> /* Option parsing made easy */
-#include <malloc.h>
 #include <sys/ioctl.h>
 #include <termios.h>
 
diff -ur OpenIPMI/lib/fru_spd_decode.c OpenIPMI-patched/lib/fru_spd_decode.c
--- OpenIPMI/lib/fru_spd_decode.c	2008-04-04 02:29:10.000000000 +0400
+++ OpenIPMI-patched/lib/fru_spd_decode.c	2010-04-16 00:16:54.724804327 +0400
@@ -39,7 +39,7 @@
 #include <string.h>
 #include <stdint.h>
 #include <errno.h>
-#include <values.h>
+#include <limits.h>
 
 #include <OpenIPMI/ipmiif.h>
 #include <OpenIPMI/ipmi_fru.h>
diff -ur OpenIPMI/lib/oem_intel.c OpenIPMI-patched/lib/oem_intel.c
--- OpenIPMI/lib/oem_intel.c	2007-07-12 08:06:25.000000000 +0400
+++ OpenIPMI-patched/lib/oem_intel.c	2010-04-16 00:16:31.304802343 +0400
@@ -31,7 +31,6 @@
  *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include <alloca.h>
 #include <string.h>
 #include <stdlib.h>
 
diff -ur OpenIPMI/sample/dump_sensors.c OpenIPMI-patched/sample/dump_sensors.c
--- OpenIPMI/sample/dump_sensors.c	2008-12-09 21:33:12.000000000 +0300
+++ OpenIPMI-patched/sample/dump_sensors.c	2010-04-16 00:24:00.078553756 +0400
@@ -30,7 +30,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <malloc.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
diff -ur OpenIPMI/sample/ipmi_serial_bmc_emu.c OpenIPMI-patched/sample/ipmi_serial_bmc_emu.c
--- OpenIPMI/sample/ipmi_serial_bmc_emu.c	2008-11-07 18:20:29.000000000 +0300
+++ OpenIPMI-patched/sample/ipmi_serial_bmc_emu.c	2010-04-16 00:41:20.549690171 +0400
@@ -37,7 +37,6 @@
 #include <netinet/tcp.h>
 #include <netdb.h>
 #include <errno.h>
-#include <malloc.h>
 #include <string.h>
 #include <ctype.h>
 #include <unistd.h>
diff -ur OpenIPMI/sample/sample.c OpenIPMI-patched/sample/sample.c
--- OpenIPMI/sample/sample.c	2008-04-21 17:41:17.000000000 +0400
+++ OpenIPMI-patched/sample/sample.c	2010-04-16 00:23:23.448742984 +0400
@@ -31,7 +31,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <malloc.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
diff -ur OpenIPMI/sample/sample2.c OpenIPMI-patched/sample/sample2.c
--- OpenIPMI/sample/sample2.c	2006-07-13 02:56:58.000000000 +0400
+++ OpenIPMI-patched/sample/sample2.c	2010-04-16 00:23:30.035289877 +0400
@@ -31,7 +31,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <malloc.h>
 #include <unistd.h>
 #include <signal.h>
 
diff -ur OpenIPMI/sample/sample3.c OpenIPMI-patched/sample/sample3.c
--- OpenIPMI/sample/sample3.c	2006-07-13 02:56:58.000000000 +0400
+++ OpenIPMI-patched/sample/sample3.c	2010-04-16 00:23:32.145295199 +0400
@@ -30,7 +30,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <malloc.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
diff -ur OpenIPMI/sample/solterm.c OpenIPMI-patched/sample/solterm.c
--- OpenIPMI/sample/solterm.c	2006-04-05 02:30:42.000000000 +0400
+++ OpenIPMI-patched/sample/solterm.c	2010-04-16 00:23:14.621666637 +0400
@@ -33,7 +33,6 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <malloc.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
diff -ur OpenIPMI/unix/selector.c OpenIPMI-patched/unix/selector.c
--- OpenIPMI/unix/selector.c	2008-09-08 00:48:27.000000000 +0400
+++ OpenIPMI-patched/unix/selector.c	2010-04-16 12:40:55.391123058 +0400
@@ -49,7 +49,6 @@
 #include <syslog.h>
 #include <signal.h>
 #include <string.h>
-#include <malloc.h>
 
 typedef struct fd_state_s
 {
@@ -809,7 +808,7 @@
 sel_alloc_selector(os_handler_t *os_hnd, selector_t **new_selector)
 {
     selector_t *sel;
-    int        i;
+    unsigned int        i;
     int        rv;
 
     sel = malloc(sizeof(*sel));
diff -ur OpenIPMI/unix/test_heap.c OpenIPMI-patched/unix/test_heap.c
--- OpenIPMI/unix/test_heap.c	2004-09-02 20:51:27.000000000 +0400
+++ OpenIPMI-patched/unix/test_heap.c	2010-04-16 00:17:33.238117052 +0400
@@ -36,7 +36,6 @@
 #include <signal.h>
 #include <string.h>
 #include <time.h>
-#include <malloc.h>
 
 #define HEAP_EXPORT_NAME(s) test_ ## s
 typedef struct heap_val_s { int a; } heap_val_t;

 И патча, исправляющего вызов sendto:

diff -ur OpenIPMI/lib/ipmi_lan.c OpenIPMI-patch/lib/ipmi_lan.c
--- OpenIPMI/lib/ipmi_lan.c	2009-06-17 16:55:22.000000000 +0400
+++ OpenIPMI-patch/lib/ipmi_lan.c	2010-04-27 17:46:18.075276480 +0400
@@ -1810,6 +1810,7 @@
     ipmi_payload_t *payload = NULL;
     unsigned char  oem_iana[3] = {0, 0, 0};
     unsigned int   oem_payload_id = 0;
+    socklen_t      to_len;
 
     if ((addr->addr_type >= IPMI_RMCPP_ADDR_START)
 	&& (addr->addr_type <= IPMI_RMCPP_ADDR_END))
@@ -1915,9 +1916,15 @@
 
    add_stat(lan->ipmi, STAT_XMIT_PACKETS, 1);
 
+#ifdef PF_INET6
+    if (((struct sockaddr)(lan->cparm.ip_addr[addr_num].s_ipsock.s_addr)).sa_family==AF_INET6)
+	to_len = sizeof(struct sockaddr_in6);
+    else
+#endif
+	to_len = sizeof(struct sockaddr_in);
     rv = sendto(lan->fd->fd, tmsg, pos, 0,
 		(struct sockaddr *) &(lan->cparm.ip_addr[addr_num]),
-		sizeof(sockaddr_ip_t));
+		to_len);
     if (rv == -1)
 	rv = errno;
     else

 я написал автору OpenIPMI - Corey Minyard. Переписка заняла пару дней, по истечении которых Corey принял все патчи, позволяющие собрать OpenIPMI на FreeBSD:

http://openipmi.cvs.sourceforge.net/viewvc/openipmi/OpenIPMI/ChangeLog?view=log

2010-04-21 Alex Deiter <alex.deiter@gmail.com>
   
* cmdlang/ipmish.c, cmdlang/out_fru.c, configure.in,
  lanserv/OpenIPMI/lanserv.h, lanserv/emu.h, lanserv/lanserv.c,
  lanserv/lanserv_emu.c, lib/fru_spd_decode.c, lib/oem_intel.c,
  sample/dump_sensors.c, sample/ipmi_serial_bmc_emu.c,
  sample/sample.c, sample/sample2.c, sample/sample3.c
  sample/solterm.c, unix/selector.c, unix/test_heap.c:
  Get OpenIPMI to compile under FreeBSD.

Патч для sendto он посчитал необходимым кардинально переписать, добавив длину адреса получателя в структуру struct sockaddr_ip_s:

2010-04-27 Corey Minyard <cminyard@mvista.com>

* lib/sensor.c: Use unsigned int, not double, to hold the raw
  sensor value.
* lib/ipmi_lan.c: Pass in the correct socket length to sendto,
  not just the maximum possible size.

Далее я попробовал собрать пакет со всеми возможными зависимостями и получил еще несколько ошибок при сборке Python и Perl модулей:

libtool: link: gcc -Wall -o .libs/OpenIPMI.so OpenIPMI_wrap.o
OpenIPMI_perl.o  ../../unix/.libs/libOpenIPMIposix.so -L/usr/local/lib
../../lib/.libs/libOpenIPMI.so ../../utils/.libs/libOpenIPMIutils.so
../../cmdlang/.libs/libOpenIPMIcmdlang.so
/home/tiamat/tmp/port/OpenIPMI/lib/.libs/libOpenIPMI.so -lm
/home/tiamat/tmp/port/OpenIPMI/utils/.libs/libOpenIPMIutils.so
-lcrypto -Wl,-rpath -Wl,/var/tmp/openipmi/lib
/usr/lib/crt1.o(.text+0x8a): In function `_start':
: undefined reference to `main'
OpenIPMI_wrap.o(.text+0xed0): In function `SWIG_Perl_ConvertPtr':
/home/tiamat/tmp/port/OpenIPMI/swig/perl/OpenIPMI_wrap.c:1119:
undefined reference to `Perl_Gthr_key_ptr'
OpenIPMI_wrap.o(.text+0xee3):/home/tiamat/tmp/port/OpenIPMI/swig/perl/OpenIPMI_wrap.c:1119:
undefined reference to `Perl_mg_get'
OpenIPMI_wrap.o(.text+0xeed):/home/tiamat/tmp/port/OpenIPMI/swig/perl/OpenIPMI_wrap.c:1122:
undefined reference to `Perl_Gthr_key_ptr'
OpenIPMI_wrap.o(.text+0xf00):/home/tiamat/tmp/port/OpenIPMI/swig/perl/OpenIPMI_wrap.c:1122:
undefined reference to `Perl_sv_isobject'
...
gcc -Wall -DHAVE_CONFIG_H -I/usr/local/include/python2.6 -I ../.. -I
../../include -I ../../include -I ../../swig/python -ggdb -fPIC
-DPYTHON_HAS_POSIX_THREADS=1 -c OpenIPMI_wrap.c
OpenIPMI_wrap.c: In function 'parse_ip_addr':
OpenIPMI_wrap.c:2979: error: 'PF_UNSPEC' undeclared (first use in this function)
OpenIPMI_wrap.c:2979: error: (Each undeclared identifier is reported only once
OpenIPMI_wrap.c:2979: error: for each function it appears in.)
OpenIPMI_wrap.c:2980: error: 'SOCK_DGRAM' undeclared (first use in
this function)
OpenIPMI_wrap.c:2988: error: 'PF_INET' undeclared (first use in this function)
OpenIPMI_wrap.c: In function 'vswig_call_cb_rv':

 

Также я обнаружил, что все разделяемые библиотеки OpenIPMI почему-то слинкованы с libncurses:

$ ldd /var/tmp/openipmi/lib/lib*.so|grep curs
       libncurses.so.8 => /lib/libncurses.so.8 (0x80096f000)
       libncurses.so.8 => /lib/libncurses.so.8 (0x8013a2000)
       libncurses.so.8 => /lib/libncurses.so.8 (0x8014c1000)
       libncurses.so.8 => /lib/libncurses.so.8 (0x800a6c000)
       libncurses.so.8 => /lib/libncurses.so.8 (0x801000000)
       libncurses.so.8 => /lib/libncurses.so.8 (0x80098b000)
       libncurses.so.8 => /lib/libncurses.so.8 (0x800966000)

 

Написав еще несколько патчей:

diff -ur OpenIPMI.orig/swig/OpenIPMI.i OpenIPMI/swig/OpenIPMI.i
--- OpenIPMI.orig/swig/OpenIPMI.i	2008-04-13 07:00:11.000000000 +0400
+++ OpenIPMI/swig/OpenIPMI.i	2010-04-28 00:24:03.562178026 +0400
@@ -36,6 +36,8 @@
 %{
 
 #include <config.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 
 #ifdef HAVE_GETADDRINFO
 #include <netdb.h>
diff -ur OpenIPMI.orig/swig/python/Makefile.am OpenIPMI/swig/python/Makefile.am
--- OpenIPMI.orig/swig/python/Makefile.am	2010-03-24 19:52:23.000000000 +0300
+++ OpenIPMI/swig/python/Makefile.am	2010-04-28 00:34:47.684857305 +0400
@@ -5,52 +5,39 @@
 	    -I $(top_builddir) \
 	    -I $(top_builddir)/include \
 	    -I $(top_srcdir)/include \
-	    -I $(top_srcdir)/swig/python
+	    -I $(top_srcdir)/swig/python \
+	    -DPYTHON_HAS_POSIX_THREADS=@PYTHON_HAS_POSIX_THREADS@
 
-CC = @CC@ -Wall
-CFLAGS = @CFLAGS@ -fPIC -DPYTHON_HAS_POSIX_THREADS=@PYTHON_HAS_POSIX_THREADS@
-DEFS = @DEFS@
+pythonlibdir=$(PYTHON_INSTALL_LIB_DIR)
+PYPATH=$(top_builddir)/swig/python:$(top_builddir)/swig/python/.libs:$(srcdir)/openipmigui
 
-COMPILE = $(CC) $(DEFS) $(AM_CFLAGS) $(CFLAGS)
+pythonlib_LTLIBRARIES = _OpenIPMI.la
 
-all-local: _OpenIPMI.so
-
-OpenIPMI_SRC = OpenIPMI_wrap.c
-OpenIPMI_OBJ = OpenIPMI_wrap.o
+_OpenIPMI_la_SOURCES = OpenIPMI_wrap.c
+_OpenIPMI_la_LDFLAGS = -module -avoid-version
+_OpenIPMI_la_LIBADD = $(OPENIPMI_SWIG_LIBS) $(PYTHON_POSIX_LIB)
 
 EXTRA_DIST = OpenIPMI_lang.i OpenIPMI.h openipmigui.py sample.py
 
-_OpenIPMI.so: $(OpenIPMI_OBJ)
-	$(LIBTOOL) --mode=link $(CC) -shared -o $@ $^ $(OPENIPMI_SWIG_LIBS)
-	rm _OpenIPMI.so
-	mv .libs/_OpenIPMI.so .
-	rm -rf .libs
-
-OpenIPMI_wrap.o OpenIPMI.py: OpenIPMI_wrap.c OpenIPMI.h
-	$(COMPILE) -c $<
-
-OpenIPMI.pyc: OpenIPMI.py _OpenIPMI.so
-	-$(pythonprog) -c 'import OpenIPMI.py'
+OpenIPMI.pyc: OpenIPMI.py _OpenIPMI.la
+	-PYTHONPATH=$(PYPATH) $(pythonprog) -c 'import OpenIPMI.py'
 
-OpenIPMI.pyo: OpenIPMI.py _OpenIPMI.so
-	-$(pythonprog) -O -c 'import OpenIPMI.py'
+OpenIPMI.pyo: OpenIPMI.py _OpenIPMI.la
+	-PYTHONPATH=$(PYPATH) $(pythonprog) -O -c 'import OpenIPMI.py'
 
-OpenIPMI_wrap.c: $(top_srcdir)/swig/OpenIPMI.i OpenIPMI_lang.i
+OpenIPMI_wrap.c OpenIPMI.py: $(top_srcdir)/swig/OpenIPMI.i OpenIPMI_lang.i
 	$(SWIG) $(DEFS) -python -o $@ -I$(top_srcdir)/swig/python $<
 
-CLEANFILES = $(OpenIPMI_OBJ) _OpenIPMI.so OpenIPMI_wrap.c \
-	OpenIPMI.py OpenIPMI.pyo OpenIPMI.pyc
+CLEANFILES = OpenIPMI_wrap.c OpenIPMI.py OpenIPMI.pyo OpenIPMI.pyc
 
-install-exec-local: _OpenIPMI.so OpenIPMI.py OpenIPMI.pyc OpenIPMI.pyo
+install-exec-local: _OpenIPMI.la OpenIPMI.py OpenIPMI.pyc OpenIPMI.pyo
 	$(INSTALL) -d $(DESTDIR)$(PYTHON_INSTALL_DIR)
-	$(INSTALL) -d $(DESTDIR)$(PYTHON_INSTALL_LIB_DIR)
-	$(LIBTOOL) --mode=install $(INSTALL_PROGRAM) _OpenIPMI.so "$(DESTDIR)$(PYTHON_INSTALL_LIB_DIR)/"
 	$(INSTALL_DATA) OpenIPMI.py "$(DESTDIR)$(PYTHON_INSTALL_DIR)"
 	$(INSTALL_DATA) OpenIPMI.pyc "$(DESTDIR)$(PYTHON_INSTALL_DIR)"
 	$(INSTALL_DATA) OpenIPMI.pyo "$(DESTDIR)$(PYTHON_INSTALL_DIR)"
 	if test "x$(PYTHON_GUI_DIR)" = "xopenipmigui"; then \
 	    $(INSTALL) -d $(DESTDIR)$(bindir); \
-	    $(INSTALL_PROGRAM) openipmigui.py "$(DESTDIR)$(bindir)/openipmigui";\
+	    $(INSTALL_SCRIPT) openipmigui.py "$(DESTDIR)$(bindir)/openipmigui";\
 	fi
 
 uninstall-local:
@@ -60,7 +47,5 @@
 	rm -f "$(DESTDIR)$(PYTHON_INSTALL_DIR)/OpenIPMI.pyo"
 	rm -f "$(DESTDIR)$(bindir)/openipmigui"
 
-PYPATH=$(top_builddir)/swig/python:$(srcdir)/openipmigui
-
 rungui:
 	LD_LIBRARY_PATH=$(top_builddir)/glib/.libs LD_PRELOAD=$(OPENIPMI_SWIG_SO):$(top_builddir)/swig/python/_OpenIPMI.so PYTHONPATH=$(PYPATH) $(pythonprog) $(top_srcdir)/swig/python/openipmigui.py
diff -ur OpenIPMI.orig/swig/python/openipmigui/Makefile.am OpenIPMI/swig/python/openipmigui/Makefile.am
--- OpenIPMI.orig/swig/python/openipmigui/Makefile.am	2007-10-02 18:48:05.000000000 +0400
+++ OpenIPMI/swig/python/openipmigui/Makefile.am	2010-04-28 00:35:58.905028305 +0400
@@ -35,12 +35,12 @@
 	done)
 	-rmdir "$(DESTDIR)$(PYTHON_INSTALL_DIR)/openipmigui"
 
-PYPATH=$(top_builddir)/swig/python:$(srcdir)
+PYPATH=$(top_builddir)/swig/python:$(top_builddir)/swig/python/.libs:$(srcdir)
 
 .py.pyc:
-	LD_PRELOAD=$(OPENIPMI_SWIG_SO):$(top_builddir)/swig/python/_OpenIPMI.so PYTHONPATH=$(PYPATH) $(pythonprog) -c 'import $*'
+	LD_PRELOAD=$(OPENIPMI_SWIG_SO):$(top_builddir)/swig/python/.libs/_OpenIPMI.so PYTHONPATH=$(PYPATH) $(pythonprog) -c 'import $*'
 
 .py.pyo:
-	LD_PRELOAD=$(OPENIPMI_SWIG_SO):$(top_builddir)/swig/python/_OpenIPMI.so PYTHONPATH=$(PYPATH) $(pythonprog) -O -c 'import $*'
+	LD_PRELOAD=$(OPENIPMI_SWIG_SO):$(top_builddir)/swig/python/.libs/_OpenIPMI.so PYTHONPATH=$(PYPATH) $(pythonprog) -O -c 'import $*'
 
 CLEANFILES = $(PYC_FILES) $(PYO_FILES)

diff -ur OpenIPMI.orig/swig/perl/Makefile.am OpenIPMI/swig/perl/Makefile.am
--- OpenIPMI.orig/swig/perl/Makefile.am	2008-04-11 09:26:04.000000000 +0400
+++ OpenIPMI/swig/perl/Makefile.am	2010-04-28 00:01:14.696236964 +0400
@@ -16,19 +16,19 @@
 all: OpenIPMI.so
 
 OpenIPMI_SRC = OpenIPMI_wrap.c OpenIPMI_perl.c
-OpenIPMI_OBJ = OpenIPMI_wrap.o OpenIPMI_perl.o
+OpenIPMI_OBJ = OpenIPMI_wrap.lo OpenIPMI_perl.lo
 
 OpenIPMI.so: $(OpenIPMI_OBJ)
-	$(LIBTOOL) --mode=link $(CC) -shared -o $@ $^ $(OPENIPMI_SWIG_LIBS)
-	rm OpenIPMI.so
+	$(LIBTOOL) --mode=link $(CC) -shared -module -avoid-version -o $(@:.so=.la) $^ $(OPENIPMI_SWIG_LIBS) $(PERL_POSIX_LIB) -rpath $(PERL_INSTALL_DIR)/auto/OpenIPMI
+	rm -f OpenIPMI.so
 	mv .libs/OpenIPMI.so .
 	rm -rf .libs
 
-OpenIPMI_wrap.o: OpenIPMI_wrap.c OpenIPMI.h
-	$(COMPILE) -c $<
+OpenIPMI_wrap.lo: OpenIPMI_wrap.c OpenIPMI.h
+	$(LIBTOOL) --mode=compile $(COMPILE) -c $<
 
-OpenIPMI_perl.o: OpenIPMI_perl.c OpenIPMI.h
-	$(COMPILE) -c $<
+OpenIPMI_perl.lo: OpenIPMI_perl.c OpenIPMI.h
+	$(LIBTOOL) --mode=compile $(COMPILE) -c $<
 
 OpenIPMI_wrap.c OpenIPMI.pm: $(top_srcdir)/swig/OpenIPMI.i OpenIPMI_lang.i
 	$(SWIG) $(DEFS) -perl5 -o $@ -I$(top_srcdir)/swig/perl $<
@@ -38,7 +38,7 @@
 install-exec-local: OpenIPMI.so OpenIPMI.pm
 	$(INSTALL) -d "$(DESTDIR)$(PERL_INSTALL_DIR)/auto/OpenIPMI"
 	$(INSTALL_PROGRAM) OpenIPMI.so "$(DESTDIR)$(PERL_INSTALL_DIR)/auto/OpenIPMI"
-	$(INSTALL_PROGRAM) OpenIPMI.pm "$(DESTDIR)$(PERL_INSTALL_DIR)"
+	$(INSTALL_DATA) OpenIPMI.pm "$(DESTDIR)$(PERL_INSTALL_DIR)"
 
 uninstall-local:
 	$(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(PERL_INSTALL_DIR)/auto/OpenIPMI/OpenIPMI.so"

diff -ur OpenIPMI.orig/cmdlang/Makefile.am OpenIPMI/cmdlang/Makefile.am
--- OpenIPMI.orig/cmdlang/Makefile.am	2007-10-02 18:48:05.000000000 +0400
+++ OpenIPMI/cmdlang/Makefile.am	2010-04-27 23:39:13.064860630 +0400
@@ -33,6 +33,7 @@
 		$(top_builddir)/lib/libOpenIPMI.la \
 		$(top_builddir)/unix/libOpenIPMIposix.la \
 		$(top_builddir)/libedit/libedit.a \
+		$(TERM_LIBS) \
 		$(SNMPLIBS) $(GLIB_LIB) $(GLIB_LIBS) \
 		$(TCL_LIB) $(TCL_LIBS) \
 		$(OPENSSLLIBS) $(GDBM_LIB)
diff -ur OpenIPMI.orig/configure.in OpenIPMI/configure.in
--- OpenIPMI.orig/configure.in	2010-04-21 18:10:54.000000000 +0400
+++ OpenIPMI/configure.in	2010-04-27 23:45:01.647949542 +0400
@@ -849,8 +849,8 @@
    *-sun-*) AC_DEFINE([_SUNOS], [], [Solaris's term.h does horrid things.]);;
 esac
 
-AC_CHECK_LIB(curses, tgetent,,
-   [AC_CHECK_LIB(ncurses, tgetent,,
+AC_CHECK_LIB(curses, tgetent, TERM_LIBS=-lcurses,
+   [AC_CHECK_LIB(ncurses, tgetent, TERM_LIBS=-lncurses,
       [AC_MSG_ERROR([libtermcap, libcurses or libncurses are required!])] )] )
 
 # Checks for header files.
@@ -888,6 +888,7 @@
 AC_CHECK_FUNCS([endpwent isascii memchr memset re_comp regcomp strcasecmp strchr strcspn strdup strerror strrchr strstr strtol issetugid])
 EL_GETPW_R_POSIX
 EL_GETPW_R_DRAFT
+AC_SUBST(TERM_LIBS)
 
 # End of libedit inclusions
 
diff -ur OpenIPMI.orig/sample/Makefile.am OpenIPMI/sample/Makefile.am
--- OpenIPMI.orig/sample/Makefile.am	2008-09-08 00:48:27.000000000 +0400
+++ OpenIPMI/sample/Makefile.am	2010-04-27 23:42:08.861654636 +0400
@@ -52,7 +52,7 @@
 rmcp_ping_SOURCES = rmcp_ping.c
 
 ipmi_serial_bmc_emu_SOURCES = ipmi_serial_bmc_emu.c
-ipmi_serial_bmc_emu_LDADD = $(top_builddir)/libedit/libedit.a
+ipmi_serial_bmc_emu_LDADD = $(top_builddir)/libedit/libedit.a $(TERM_LIBS)
 ipmi_serial_bmc_emu_CFLAGS = -I $(top_srcdir)/libedit
 
 EXTRA_DIST = example_oem.c

 

я попросил автора выпустить по этому поводу новую версию OpenIPMI, которая включала бы в себя все сделанные изменения. Corey Minyard не заставил себя долго ждать и через пару дней для загрузки стал доступен релиз OpenIPMI 2.0.17, на основе которого я сделал порт OpenIPMI для FreeBSD.

Этот PR еще не добавлен в коллекцию портов, поэтому пока я не отправлял патч для zabbix-server, добавляющий IPMI опцию сборки:

$ diff -u net-mgmt/zabbix-server/Makefile.orig net-mgmt/zabbix-server/Makefile
--- net-mgmt/zabbix-server/Makefile.orig	2010-04-29 11:11:54.509380512 +0400
+++ net-mgmt/zabbix-server/Makefile	2010-04-29 11:47:25.881947696 +0400
@@ -60,6 +60,7 @@
 		SQLITE "Use SQLite backend" off \
 		IPV6 "Support for IPv6" on \
 		LDAP "Support for checking LDAP servers" on \
+		IPMI "Support for IPMI" off \
 		JABBER "Use jabber media type" on \
 		FPING "Use fping for pinging hosts" on \
 		SSH "Use libssh2 for SSH-based checks" off
@@ -87,6 +88,11 @@
 CONFIGURE_ARGS+=	--with-ldap
 .endif
 
+.ifndef WITHOUT_IPMI
+LIB_DEPENDS+=	OpenIPMI:${PORTSDIR}/sysutils/OpenIPMI
+CONFIGURE_ARGS+=	--with-openipmi
+.endif
+
 .ifndef WITHOUT_JABBER
 LIB_DEPENDS+=	iksemel:${PORTSDIR}/textproc/iksemel
 CONFIGURE_ARGS+=	--with-jabber

 

Тем не менее все эти изменения прошли тестирование и с успехом могут использоваться при мониторинге IPMI-совместимых систем. Об этом я как-нибудь напишу отдельную заметку.

P.S. 14.05.2010 в коллекцию портов FreeBSD добавлено приложение sysutils/openipmi.

P.P.S. 01.06.2010 открыт PR 147287

P.P.P.S. 30.06.2010 для zabbix-server в коллекции портов FreeBSD добавлена поддержка IPMI: PR 146581

Не делайте из еды культа!

Очень люблю готовить и вкусно покушать. А чтобы времени на эти увлекательные занятия оставалось как можно больше, я стараюсь автоматизировать любые задачи, которые оказываются в поле моей профессиональной деятельности.
В своем скромном дневнике я буду делиться с Вами рецептами блюд, которые удаются мне особенно хорошо