diff --git a/OpenIPMI.spec b/OpenIPMI.spec index 6349ee3..ea2c65e 100644 --- a/OpenIPMI.spec +++ b/OpenIPMI.spec @@ -3,14 +3,14 @@ Name: OpenIPMI Summary: %{name} - Library interface to IPMI -Version: 2.0.27 +Version: 2.0.29 Release: 2 License: LGPL URL: http://openipmi.sourceforge.net Group: Utilities Vendor: OpenIPMI Project Packager: Tariq Shureih -Source: %{name}-2.0.27.tar.gz +Source: %{name}-2.0.29.tar.gz Buildroot: /var/tmp/%{name}-root BuildRequires: pkgconfig, perl >= 5, swig >= 1.3 Summary: IPMI Library diff --git a/cmdlang/ipmish.c b/cmdlang/ipmish.c index 139da67..a4b8f0b 100644 --- a/cmdlang/ipmish.c +++ b/cmdlang/ipmish.c @@ -51,6 +51,7 @@ #include #include #include +#include #ifdef HAVE_GLIB #include diff --git a/configure b/configure index a1068a4..a28eb5d 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for OpenIPMI 2.0.27. +# Generated by GNU Autoconf 2.69 for OpenIPMI 2.0.29. # # Report bugs to . # @@ -590,8 +590,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='OpenIPMI' PACKAGE_TARNAME='OpenIPMI' -PACKAGE_VERSION='2.0.27' -PACKAGE_STRING='OpenIPMI 2.0.27' +PACKAGE_VERSION='2.0.29' +PACKAGE_STRING='OpenIPMI 2.0.29' PACKAGE_BUGREPORT='minyard@acm.org' PACKAGE_URL='' @@ -1421,7 +1421,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures OpenIPMI 2.0.27 to adapt to many kinds of systems. +\`configure' configures OpenIPMI 2.0.29 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1493,7 +1493,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of OpenIPMI 2.0.27:";; + short | recursive ) echo "Configuration of OpenIPMI 2.0.29:";; esac cat <<\_ACEOF @@ -1635,7 +1635,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -OpenIPMI configure 2.0.27 +OpenIPMI configure 2.0.29 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2004,7 +2004,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by OpenIPMI $as_me 2.0.27, which was +It was created by OpenIPMI $as_me 2.0.29, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2977,7 +2977,7 @@ fi # Define the identity of the package. PACKAGE='OpenIPMI' - VERSION='2.0.27' + VERSION='2.0.29' cat >>confdefs.h <<_ACEOF @@ -4290,7 +4290,7 @@ OPENIPMI_VERSION_MAJOR=2 OPENIPMI_VERSION_MINOR=0 -OPENIPMI_VERSION_RELEASE=27 +OPENIPMI_VERSION_RELEASE=29 @@ -15427,7 +15427,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by OpenIPMI $as_me 2.0.27, which was +This file was extended by OpenIPMI $as_me 2.0.29, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -15493,7 +15493,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -OpenIPMI config.status 2.0.27 +OpenIPMI config.status 2.0.29 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 0461041..48d7863 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ OPENIPMI_PKG_NAME=OpenIPMI -AC_INIT([OpenIPMI], [2.0.27], [minyard@acm.org], [OpenIPMI]) +AC_INIT([OpenIPMI], [2.0.29], [minyard@acm.org], [OpenIPMI]) AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([-Wall]) AC_CONFIG_HEADER([config.h]) @@ -9,7 +9,7 @@ AC_CONFIG_MACRO_DIR([m4]) AC_SUBST(OPENIPMI_VERSION_MAJOR, 2) AC_SUBST(OPENIPMI_VERSION_MINOR, 0) -AC_SUBST(OPENIPMI_VERSION_RELEASE, 27) +AC_SUBST(OPENIPMI_VERSION_RELEASE, 29) AC_SUBST(OPENIPMI_VERSION_EXTRA, ) diff --git a/doc/IPMI.pdf b/doc/IPMI.pdf index e5fba7b..9bec0cd 100644 Binary files a/doc/IPMI.pdf and b/doc/IPMI.pdf differ diff --git a/include/OpenIPMI/ipmiif.h b/include/OpenIPMI/ipmiif.h index 9665940..790d830 100644 --- a/include/OpenIPMI/ipmiif.h +++ b/include/OpenIPMI/ipmiif.h @@ -47,7 +47,7 @@ extern "C" { /* For version detection */ #define OPENIPMI_VERSION_MAJOR 2 #define OPENIPMI_VERSION_MINOR 0 -#define OPENIPMI_VERSION_RELEASE 27 +#define OPENIPMI_VERSION_RELEASE 29 #define OPENIPMI_VERSION_EXTRA #define OPENIPMI_STRINGX(x) #x #define OPENIPMI_XSTRING(x) OPENIPMI_STRINGX(x) diff --git a/include/OpenIPMI/selector.h b/include/OpenIPMI/selector.h index 1e87b61..ebd242f 100644 --- a/include/OpenIPMI/selector.h +++ b/include/OpenIPMI/selector.h @@ -51,8 +51,13 @@ typedef struct selector_s selector_t; /* You have to create a selector before you can use it. */ -/* Create a selector for use with threads. You have to pass in the - lock functions and a signal used to wake waiting threads. */ +/* + * Create a selector for use with threads. You have to pass in the + * lock functions and a signal used to wake waiting threads. + * + * Note that this function will block wake_sig in the calling thread, and you + * must have it blocked on all threads. + */ typedef struct sel_lock_s sel_lock_t; int sel_alloc_selector_thread(struct selector_s **new_selector, int wake_sig, sel_lock_t *(*sel_lock_alloc)(void *cb_data), @@ -69,7 +74,6 @@ int sel_alloc_selector_nothread(struct selector_s **new_selector); /* Used to destroy a selector. */ int sel_free_selector(struct selector_s *new_selector); - /* A function to call when select sees something on a file descriptor. */ typedef void (*sel_fd_handler_t)(int fd, void *data); @@ -176,6 +180,17 @@ int sel_select_intr(struct selector_s *sel, void *cb_data, struct timeval *timeout); +/* + * Like the above call, but allows the user to install their own sigmask + * while waiting. + */ +int sel_select_intr_sigmask(struct selector_s *sel, + sel_send_sig_cb send_sig, + long thread_id, + void *cb_data, + struct timeval *timeout, + sigset_t *sigmask); + /* This is the main loop for the program. If NULL is passed in to send_sig, then the signal sender is not used. If this encounters an unrecoverable problem with select(), it will return the errno. @@ -212,6 +227,13 @@ void ipmi_sel_set_read_fds_handler(struct selector_s *sel, int sel_alloc_selector(os_handler_t *os_hnd, struct selector_s **new_selector) IPMI_FUNC_DEPRECATED; +/* + * If you fork and expect to use the selector in the forked process, + * you *must* call this function in the forked process or you may + * get strange results. + */ +int sel_setup_forked_process(struct selector_s *sel); + #ifdef __cplusplus } #endif diff --git a/ipmi.init b/ipmi.init index ea10b11..1bc0769 100644 --- a/ipmi.init +++ b/ipmi.init @@ -114,7 +114,7 @@ LOCKFILE=/var/lock/subsys/ipmi DEV_IPMI_TIMEOUT=15 UDEV_EXISTS=0 -if [ -e /sbin/udev -o -e /sbin/udevd ]; then +if [ -e /sbin/udev -o -e /sbin/udevd -o -e /bin/udevadm ]; then UDEV_EXISTS=1 fi diff --git a/lanserv/Makefile.am b/lanserv/Makefile.am index 265b74a..659e957 100644 --- a/lanserv/Makefile.am +++ b/lanserv/Makefile.am @@ -34,7 +34,7 @@ noinst_HEADERS = emu.h bmc.h libIPMIlanserv_la_SOURCES = lanserv_ipmi.c lanserv_asf.c priv_table.c \ lanserv_oem_force.c lanserv_config.c config.c serv.c serial_ipmi.c \ - persist.c extcmd.c + persist.c extcmd.c ipmb_ipmi.c libIPMIlanserv_la_LIBADD = $(OPENSSLLIBS) -ldl $(RT_LIB) libIPMIlanserv_la_LDFLAGS = -version-info $(LD_VERSION) \ ../utils/libOpenIPMIutils.la diff --git a/lanserv/Makefile.in b/lanserv/Makefile.in index a909695..992f5b7 100644 --- a/lanserv/Makefile.in +++ b/lanserv/Makefile.in @@ -150,7 +150,7 @@ libIPMIlanserv_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) am_libIPMIlanserv_la_OBJECTS = lanserv_ipmi.lo lanserv_asf.lo \ priv_table.lo lanserv_oem_force.lo lanserv_config.lo config.lo \ - serv.lo serial_ipmi.lo persist.lo extcmd.lo + serv.lo serial_ipmi.lo persist.lo extcmd.lo ipmb_ipmi.lo libIPMIlanserv_la_OBJECTS = $(am_libIPMIlanserv_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -499,7 +499,7 @@ lib_LTLIBRARIES = libIPMIlanserv.la noinst_HEADERS = emu.h bmc.h libIPMIlanserv_la_SOURCES = lanserv_ipmi.c lanserv_asf.c priv_table.c \ lanserv_oem_force.c lanserv_config.c config.c serv.c serial_ipmi.c \ - persist.c extcmd.c + persist.c extcmd.c ipmb_ipmi.c libIPMIlanserv_la_LIBADD = $(OPENSSLLIBS) -ldl $(RT_LIB) libIPMIlanserv_la_LDFLAGS = -version-info $(LD_VERSION) \ @@ -685,6 +685,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emu_cmd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/extcmd.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmb_ipmi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_checksum.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipmi_sim.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lanserv.Po@am__quote@ diff --git a/lanserv/OpenIPMI/Makefile.am b/lanserv/OpenIPMI/Makefile.am index ab1e514..7c17c10 100644 --- a/lanserv/OpenIPMI/Makefile.am +++ b/lanserv/OpenIPMI/Makefile.am @@ -1,3 +1,3 @@ pkginclude_HEADERS = lanserv.h serserv.h serv.h extcmd.h persist.h msg.h \ - mcserv.h + mcserv.h ipmbserv.h diff --git a/lanserv/OpenIPMI/Makefile.in b/lanserv/OpenIPMI/Makefile.in index fda5cb2..5612f12 100644 --- a/lanserv/OpenIPMI/Makefile.in +++ b/lanserv/OpenIPMI/Makefile.in @@ -362,7 +362,7 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ pkginclude_HEADERS = lanserv.h serserv.h serv.h extcmd.h persist.h msg.h \ - mcserv.h + mcserv.h ipmbserv.h all: all-am diff --git a/lanserv/OpenIPMI/ipmbserv.h b/lanserv/OpenIPMI/ipmbserv.h new file mode 100644 index 0000000..06ee342 --- /dev/null +++ b/lanserv/OpenIPMI/ipmbserv.h @@ -0,0 +1,83 @@ +/* + * ipmbserv.h + * + * IPMB server include file + * + * Copyright 2019 Mellanox + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * Lesser General Public License (GPL) Version 2 or the modified BSD + * license below. The following disclamer applies to both licenses: + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * GNU Lesser General Public Licence + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Modified BSD Licence + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + */ + +#ifndef __IPMBSERV_H +#define __IPMBSERV_H + +#include +#include + +typedef struct ipmbserv_data_s ipmbserv_data_t; + +struct ipmbserv_data_s { + lan_addr_t addr; + + channel_t channel; + + os_handler_t *os_hnd; + + sys_data_t *sysinfo; + + void *user_info; + + int fd; + + void (*send_out)(ipmbserv_data_t *si, unsigned char *data, + unsigned int data_len); + + char *ipmbdev; +}; + +int ipmbserv_read_config(char **tokptr, sys_data_t *sys, const char **errstr); +int ipmbserv_init(ipmbserv_data_t *ipmb); +void ipmbserv_handle_data(ipmbserv_data_t *ipmb, uint8_t *imsg, unsigned int len); + +#endif /* __IPMBSERV_H */ diff --git a/lanserv/OpenIPMI/mcserv.h b/lanserv/OpenIPMI/mcserv.h index cc07d50..7030ba8 100644 --- a/lanserv/OpenIPMI/mcserv.h +++ b/lanserv/OpenIPMI/mcserv.h @@ -345,6 +345,13 @@ int ipmi_emu_register_oi_iana_handler(uint8_t cmd, cmd_handler_f handler, #define OPENIPMI_IANA_CMD_GET_HISTORY_RETURN_SIZE 2 /* + * Registration for group extensions + */ +void ipmi_emu_register_group_extension_handler(uint8_t group_extension, + cmd_handler_f handler, + void *cb_data); + +/* * SOL handling */ diff --git a/lanserv/OpenIPMI/serv.h b/lanserv/OpenIPMI/serv.h index d2087f5..6bcfc35 100644 --- a/lanserv/OpenIPMI/serv.h +++ b/lanserv/OpenIPMI/serv.h @@ -219,6 +219,9 @@ struct channel_s */ int (*oem_intf_recv_handler)(channel_t *chan, msg_t *msg, unsigned char *rdata, unsigned int *rdata_len); + + /* Set to 1 if ipmb channel 0 is listed in the config file, 0 otherwise */ + int prim_ipmb_in_cfg_file; }; struct user_s @@ -408,6 +411,7 @@ struct sys_data_s { void (*cfree)(channel_t *chan, void *data); int (*lan_channel_init)(void *info, channel_t *chan); int (*ser_channel_init)(void *info, channel_t *chan); + int (*ipmb_channel_init)(void *info, channel_t *chan); }; static inline void diff --git a/lanserv/README.design b/lanserv/README.design index 2f897c4..5260a6c 100644 --- a/lanserv/README.design +++ b/lanserv/README.design @@ -31,6 +31,8 @@ msg.h - This defines an IPMI message that is passed around, and a few serserv.h - The configuration of a serial interface. +ipmbserv.h - The configuration of an IPMB interface. + serv.h - This defines data structures used by the whole system. @@ -77,6 +79,8 @@ emu.h - Defines the interface between bmc_xxx.c and emu_cmd.c extcmd.c - Code for running the external command for dealing with LAN configuration. +ipmb_ipmi.c - An implementation of the IPMB protocol. + ipmi_sim.c - The main file for the ipmi_sim program. lanserv_asf.c - Handles LAN ASF commands. @@ -179,4 +183,4 @@ It is called from config.c to handle sol-specific configuration. It installs a hook into lanserv_ipmi.c to receive the SOL payload and send the SOL payload. -It ties into bmc.c to handle SOL-specific commands. \ No newline at end of file +It ties into bmc.c to handle SOL-specific commands. diff --git a/lanserv/bmc.c b/lanserv/bmc.c index 93d0b3f..ea5ec45 100644 --- a/lanserv/bmc.c +++ b/lanserv/bmc.c @@ -71,6 +71,19 @@ get_lanserv_version(void) return PVERSION; } +struct { + cmd_handler_f handler; + void *cb_data; +} group_extension_handlers[256]; + +void +ipmi_emu_register_group_extension_handler(uint8_t group_extension, + cmd_handler_f handler, void *cb_data) +{ + group_extension_handlers[group_extension].handler = handler; + group_extension_handlers[group_extension].cb_data = cb_data; +} + static void handle_group_extension_netfn(lmc_data_t *mc, msg_t *msg, @@ -78,21 +91,17 @@ handle_group_extension_netfn(lmc_data_t *mc, unsigned int *rdata_len, void *cb_data) { + uint8_t ge; + if (check_msg_length(msg, 1, rdata, rdata_len)) return; - switch (msg->data[0]) { - case IPMI_PICMG_GRP_EXT: - if (mc->emu->atca_mode) - handle_picmg_msg(mc, msg, rdata, rdata_len); - else - handle_invalid_cmd(mc, rdata, rdata_len); - break; - - default: + ge = msg->data[0]; + if (group_extension_handlers[ge].handler) + group_extension_handlers[ge].handler(mc, msg, rdata, rdata_len, + group_extension_handlers[ge].cb_data); + else handle_invalid_cmd(mc, rdata, rdata_len); - break; - } } static struct iana_handler_elem { @@ -261,7 +270,7 @@ ipmi_emu_register_cmd_handler(unsigned char netfn, unsigned char cmd, { unsigned int ni = netfn >> 1; - if (netfn >= 32) + if (ni >= 32) return EINVAL; if (!netfn_handlers[ni].handlers) { @@ -613,6 +622,9 @@ ipmi_mc_enable(lmc_data_t *mc) err = sys->lan_channel_init(sys->info, chan); else if (chan->medium_type == IPMI_CHANNEL_MEDIUM_RS232) err = sys->ser_channel_init(sys->info, chan); + else if ((chan->medium_type == IPMI_CHANNEL_MEDIUM_IPMB) && + ((chan->channel_num != 0) || (chan->prim_ipmb_in_cfg_file))) + err = sys->ipmb_channel_init(sys->info, chan); else chan_init(chan); if (err) { @@ -802,6 +814,7 @@ ipmi_mc_alloc_unconfigured(sys_data_t *sys, unsigned char ipmb, mc->ipmb_channel.protocol_type = IPMI_CHANNEL_PROTOCOL_IPMB; mc->ipmb_channel.session_support = IPMI_CHANNEL_SESSION_LESS; mc->ipmb_channel.active_sessions = 0; + mc->ipmb_channel.prim_ipmb_in_cfg_file = 0; mc->channels[0] = &mc->ipmb_channel; mc->channels[0]->log = sys->clog; diff --git a/lanserv/bmc.h b/lanserv/bmc.h index bf77174..9690d2c 100644 --- a/lanserv/bmc.h +++ b/lanserv/bmc.h @@ -425,11 +425,6 @@ extern cmd_handler_f transport_netfn_handlers[256]; extern cmd_handler_f sensor_event_netfn_handlers[256]; extern cmd_handler_f oem0_netfn_handlers[256]; -void handle_picmg_msg(lmc_data_t *mc, - msg_t *msg, - unsigned char *rdata, - unsigned int *rdata_len); - #define set_bit(m, b, v) (m) = (v) ? ((m) | (1 << (b))) : ((m) & ~(1 << (b))) #define bit_set(m, b) (!!((m) & (1 << (b)))) diff --git a/lanserv/bmc_picmg.c b/lanserv/bmc_picmg.c index a4a31b4..7c9605d 100644 --- a/lanserv/bmc_picmg.c +++ b/lanserv/bmc_picmg.c @@ -1062,10 +1062,18 @@ handle_picmg_cmd_get_shelf_manager_ip_addresses(lmc_data_t *mc, *rdata_len = 10 + ap->addr_len; } +void handle_picmg_msg(lmc_data_t *mc, + msg_t *msg, + unsigned char *rdata, + unsigned int *rdata_len, + void *cb_data); + int ipmi_emu_atca_enable(emu_data_t *emu) { emu->atca_mode = 1; + ipmi_emu_register_group_extension_handler(IPMI_PICMG_GRP_EXT, + handle_picmg_msg, NULL); return 0; } @@ -1089,7 +1097,8 @@ void handle_picmg_msg(lmc_data_t *mc, msg_t *msg, unsigned char *rdata, - unsigned int *rdata_len) + unsigned int *rdata_len, + void *cb_data) { switch(msg->cmd) { case IPMI_PICMG_CMD_GET_PROPERTIES: diff --git a/lanserv/config.c b/lanserv/config.c index f0dda9f..783ccf3 100644 --- a/lanserv/config.c +++ b/lanserv/config.c @@ -64,6 +64,7 @@ #include #include #include +#include #include void @@ -829,6 +830,8 @@ read_config(sys_data_t *sys, } } else if (strcmp(tok, "user") == 0) { err = get_user(&tokptr, sys, &errstr); + } else if (strcmp(tok, "ipmb") == 0) { + err = ipmbserv_read_config(&tokptr, sys, &errstr); } else if (strcmp(tok, "serial") == 0) { err = serserv_read_config(&tokptr, sys, &errstr); } else if (strcmp(tok, "sol") == 0) { diff --git a/lanserv/ipmb_ipmi.c b/lanserv/ipmb_ipmi.c new file mode 100644 index 0000000..d98b3cc --- /dev/null +++ b/lanserv/ipmb_ipmi.c @@ -0,0 +1,219 @@ +/* + * ipmb_ipmi.c + * + * IPMB server interface. + * + * Copyright 2019 Mellanox + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * Lesser General Public License (GPL) Version 2 or the modified BSD + * license below. The following disclamer applies to both licenses: + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * GNU Lesser General Public Licence + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Modified BSD Licence + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions 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. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + */ + +#include +#include +#include +#include +#include + +#define IPMIDEV_MAX_SIZE 15 + +static void +ipmb_send(msg_t *imsg, ipmbserv_data_t *ipmb) +{ + unsigned char msg[(IPMI_SIM_MAX_MSG_LENGTH + 7) * 3]; + unsigned int msg_len; + + msg[0] = imsg->len + 7; + msg[1] = imsg->rs_addr; + msg[2] = (imsg->netfn << 2) | imsg->rs_lun; + msg[3] = -ipmb_checksum(msg + 1, 2, 0); + msg[4] = imsg->rq_addr; + msg[5] = (imsg->rq_seq << 2) | imsg->rq_lun; + msg[6] = imsg->cmd; + memcpy(msg + 7, imsg->data, imsg->len); + msg_len = imsg->len + 7; + msg[msg_len] = -ipmb_checksum(msg + 4, msg_len - 4, 0); + msg_len++; + + if (ipmb->sysinfo->debug & DEBUG_RAW_MSG) + debug_log_raw_msg(ipmb->sysinfo, msg, msg_len, "Raw ipmb send:"); + ipmb->send_out(ipmb, msg, msg_len); +} + +static void +ipmb_return_rsp(channel_t *chan, msg_t *imsg, rsp_msg_t *rsp) +{ + ipmbserv_data_t *ipmb = chan->chan_info; + msg_t msg; + + msg.netfn = rsp->netfn; + msg.cmd = rsp->cmd; + msg.data = rsp->data; + msg.len = rsp->data_len; + msg.rq_lun = imsg->rs_lun; + msg.rq_addr = imsg->rs_addr; + msg.rs_lun = imsg->rq_lun; + msg.rs_addr = imsg->rq_addr; + msg.rq_seq = imsg->rq_seq; + + ipmb_send(&msg, ipmb); +} + +int +ipmbserv_init(ipmbserv_data_t *ipmb) +{ + ipmb->channel.return_rsp = ipmb_return_rsp; + chan_init(&ipmb->channel); + + return 0; +} + +void +ipmbserv_handle_data(ipmbserv_data_t *ipmb, uint8_t *imsg, unsigned int len) +{ + msg_t msg; + + if (len < 8) { + fprintf(stderr, "Message too short\n"); + return; + } + /* subtract len field and checksum */ + len--; + imsg++; + + if (ipmb_checksum(imsg, len, 0) != 0) { + fprintf(stderr, "Message checksum failure\n"); + return; + } + len--; + + memset(&msg, 0, sizeof(msg)); + + msg.rs_addr = imsg[0]; + msg.netfn = imsg[1] >> 2; + msg.rs_lun = imsg[1] & 3; + /* imsg[2] is first checksum */ + msg.rq_addr = imsg[3]; + msg.rq_seq = imsg[4] >> 2; + msg.rq_lun = imsg[4] & 3; + msg.cmd = imsg[5]; + + msg.len = len - 6; + msg.data = imsg + 6; + + msg.src_addr = NULL; + msg.src_len = 0; + + channel_smi_send(&ipmb->channel, &msg); +} + +int +ipmbserv_read_config(char **tokptr, sys_data_t *sys, const char **errstr) +{ + ipmbserv_data_t *ipmb; + unsigned int chan_num; + int err; + const char *tok; + char *ipmbdev; + + err = get_uint(tokptr, &chan_num, errstr); + if (err) + return -1; + + if (chan_num >= IPMI_MAX_CHANNELS) { + *errstr = "Invalid channel number, must be 0-15"; + return -1; + } + + /* + * Allow an IPMB channel to override the default channel 0. + */ + if (chan_num != 0 && sys->chan_set[chan_num]) { + *errstr = "Channel already in use"; + return -1; + } + + tok = mystrtok(NULL, " \t\n", tokptr); + if (!tok || strcmp(tok, "ipmb_dev_int")) { + *errstr = "Config file missing "; + return -1; + } + + tok = mystrtok(NULL, " \t\n", tokptr); + if (strlen(tok) > IPMIDEV_MAX_SIZE) { + *errstr = "Length of device file name %s > 15"; + return -1; + } + ipmbdev = strdup(tok); + if (!ipmbdev) { + *errstr = "Unable to alloc device file name"; + return -1; + } + + ipmb = malloc(sizeof(*ipmb)); + if (!ipmb) { + free(ipmbdev); + *errstr = "Out of memory"; + return -1; + } + memset(ipmb, 0, sizeof(*ipmb)); + ipmb->ipmbdev = ipmbdev; + + ipmb->channel.session_support = IPMI_CHANNEL_SESSION_LESS; + ipmb->channel.medium_type = IPMI_CHANNEL_MEDIUM_IPMB; + ipmb->channel.protocol_type = IPMI_CHANNEL_PROTOCOL_IPMB; + + ipmb->channel.channel_num = chan_num; + + ipmb->sysinfo = sys; + ipmb->channel.chan_info = ipmb; + + if (chan_num == 0) + ipmb->channel.prim_ipmb_in_cfg_file = 1; + else + ipmb->channel.prim_ipmb_in_cfg_file = 0; + + sys->chan_set[chan_num] = &ipmb->channel; + + return 0; +} diff --git a/lanserv/ipmi_sim.c b/lanserv/ipmi_sim.c index c4e5b18..0462235 100644 --- a/lanserv/ipmi_sim.c +++ b/lanserv/ipmi_sim.c @@ -90,6 +90,7 @@ #include #include #include +#include #include "emu.h" #include @@ -567,6 +568,100 @@ ser_channel_init(void *info, channel_t *chan) return err; } +static int +ipmb_open(char *ipmi_dev) +{ + int ipmi_fd; + + if (!ipmi_dev) { + fprintf(stderr, "ipmi_dev is not specified\n"); + return -1; + } + + ipmi_fd = open(ipmi_dev, O_RDWR); + if (ipmi_fd == -1) + fprintf(stderr, "Could not open ipmi device\n"); + + return ipmi_fd; +} + +static void +ipmb_data_ready(int fd, void *cb_data, os_hnd_fd_id_t *id) +{ + ipmbserv_data_t *ipmb = cb_data; + unsigned int len; + unsigned char msgd[256]; + + len = read(fd, msgd, sizeof(msgd)); + + if (ipmb->sysinfo->debug & DEBUG_MSG) + printf(">ipmb_data_ready size %d\n", len); + if (len <= 0) { + if ((len < 0) && (errno == EINTR)) + return; + + ipmb->os_hnd->remove_fd_to_wait_for(ipmb->os_hnd, id); + close(fd); + ipmb->fd = -1; + return; + } + + ipmbserv_handle_data(ipmb, msgd, len); +} + +static void +ipmb_send(ipmbserv_data_t *ipmb, unsigned char *data, unsigned int data_len) +{ + int rv; + + if (ipmb->fd == -1) + /* Not connected */ + return; + + rv = write(ipmb->fd, data, data_len); + if (rv) { + /* FIXME - log an error. */ + } +} + +static int +ipmb_channel_init(void *info, channel_t *chan) +{ + misc_data_t *data = info; + ipmbserv_data_t *ipmb = chan->chan_info; + int err; + os_hnd_fd_id_t *fd_id; + + ipmb->os_hnd = data->os_hnd; + ipmb->user_info = data; + ipmb->send_out = ipmb_send; + + err = ipmbserv_init(ipmb); + if (err) { + fprintf(stderr, "Unable to init ipmb: 0x%x\n", err); + exit(1); + } + + ipmb->fd = ipmb_open(ipmb->ipmbdev); + if (ipmb->fd == -1){ + fprintf(stderr, "Unable to open ipmi device file: 0x%x\n", err); + exit(1); + } + + err = data->os_hnd->add_fd_to_wait_for(data->os_hnd, ipmb->fd, + ipmb_data_ready, ipmb, + NULL, &fd_id); + if (err) { + close(ipmb->fd); + ipmb->fd = -1; + fprintf(stderr, "Unable to open ipmi device file: 0x%x\n", err); + exit(1); + } + + isim_add_fd(ipmb->fd); + return 0; +} + static void isim_log(sys_data_t *sys, int logtype, msg_t *msg, const char *format, va_list ap, int len) @@ -583,7 +678,7 @@ isim_log(sys_data_t *sys, int logtype, msg_t *msg, const char *format, #define mformat " channel=%d netfn=0x%x cmd=0x%x rs_addr=0x%x rs_lun=0x%x" \ " rq_addr=0x%x\n rq_lun=0x%x rq_seq=0x%x\n" - len += snprintf(&dummy, 1, mformat, msg->channel, msg->netfn, + len += snprintf(&dummy, 0, mformat, msg->channel, msg->netfn, msg->cmd, msg->rs_addr, msg->rs_lun, msg->rq_addr, msg->rq_lun, msg->rq_seq); len += 3 * msg->len + 3; @@ -1438,6 +1533,7 @@ main(int argc, const char *argv[]) sysinfo.cfree = ifree; sysinfo.lan_channel_init = lan_channel_init; sysinfo.ser_channel_init = ser_channel_init; + sysinfo.ipmb_channel_init = ipmb_channel_init; data.sys = &sysinfo; err = pipe(sigpipeh); diff --git a/lanserv/lan.conf b/lanserv/lan.conf index 37732d4..cec7ebb 100644 --- a/lanserv/lan.conf +++ b/lanserv/lan.conf @@ -126,3 +126,16 @@ set_working_mc 0x30 # where initstr is the init string passed on the module load line. # It should return 0 on success or an errno no failure. #loadlib "/opt/lib/ipmi_sim_extend.so" "Initialization String" + +# "ipmb" should be added to the config file of a device +# that needs to handle an IPMB request and generate a response +# back to the requester. +# In the case of an IPMB bridge request for example, the +# BMC is considered the requester and the responder device +# should have a config file defining the IPMB channel number. +# For example: +# ipmb +# ipmb 2 ipmb_dev_int /dev/ipmb-2 +# +# At the moment, this OpenIPMI ipmb interface works with the +# linux driver ipmb_dev_int.c. diff --git a/lanserv/lanserv.c b/lanserv/lanserv.c index b022916..5e64e35 100644 --- a/lanserv/lanserv.c +++ b/lanserv/lanserv.c @@ -441,7 +441,7 @@ ilanserv_log(sys_data_t *sys, int logtype, msg_t *msg, const char *format, #define mformat " channel=%d netfn=0x%x cmd=0x%x rs_addr=0x%x rs_lun=0x%x" \ " rq_addr=0x%x\n rq_lun=0x%x rq_seq=0x%x\n" - len += snprintf(&dummy, 1, mformat, msg->channel, msg->netfn, + len += snprintf(&dummy, 0, mformat, msg->channel, msg->netfn, msg->cmd, msg->rs_addr, msg->rs_lun, msg->rq_addr, msg->rq_lun, msg->rq_seq); len += 3 * msg->len + 3; diff --git a/lanserv/lanserv_ipmi.c b/lanserv/lanserv_ipmi.c index dc77eb4..8cedb35 100644 --- a/lanserv/lanserv_ipmi.c +++ b/lanserv/lanserv_ipmi.c @@ -248,7 +248,7 @@ raw_send(lanserv_data_t *lan, "Raw LAN send to:"); for (i = 0; i < vecs; i++) len += vec[i].iov_len; - slen = snprintf(&dummy, 1, format); + slen = snprintf(&dummy, 0, format); slen += len * 3 + 3; str = malloc(slen); if (!str) @@ -2638,7 +2638,7 @@ handle_rakp1_payload(lanserv_data_t *lan, msg_t *msg) if (!user) { lan->sysinfo->log(lan->sysinfo, NEW_SESSION_FAILED, msg, "RAKP msg: invalid user: %s", username); - err = IPMI_RMCPP_ILLEGAL_PARAMETER; + err = IPMI_RMCPP_UNAUTHORIZED_NAME; goto out_err; } diff --git a/lanserv/sdrcomp/sdrcomp.c b/lanserv/sdrcomp/sdrcomp.c index 5596cf5..ecce115 100644 --- a/lanserv/sdrcomp/sdrcomp.c +++ b/lanserv/sdrcomp/sdrcomp.c @@ -637,7 +637,7 @@ static struct sdr_field type2[] = .strvals = modifier_unit_fields }, { "percentage", SDR_BOOLBIT, 21, 0, 1 }, { "base_unit", SDR_BITS, 22, 0, 8 }, - { "modifier_unit", SDR_BITS, 23, 0, 8 }, + { "modifier_unit_code", SDR_BITS, 23, 0, 8 }, { "sensor_direction", SDR_BITS, 24, 6, 2, .strvals = sensor_direction_fields }, { "id_string_modifier", SDR_BITS, 24, 4, 2, diff --git a/lanserv/serv.c b/lanserv/serv.c index 0aa6b9b..b198199 100644 --- a/lanserv/serv.c +++ b/lanserv/serv.c @@ -251,8 +251,8 @@ debug_log_raw_msg(sys_data_t *sys, gettimeofday(&tv, NULL); va_start(ap, format); - slen = vsnprintf(&dummy, 1, format, ap); - slen += snprintf(&dummy, 1, " %ld.%6.6ld", tv.tv_sec, tv.tv_usec); + slen = vsnprintf(&dummy, 0, format, ap); + slen += snprintf(&dummy, 0, " %ld.%6.6ld", tv.tv_sec, tv.tv_usec); va_end(ap); slen += len * 3 + 2; str = malloc(slen); diff --git a/lib/conn.c b/lib/conn.c index 3be8312..a6e7c0d 100644 --- a/lib/conn.c +++ b/lib/conn.c @@ -269,7 +269,6 @@ ipmi_conn_check_oem_handlers(ipmi_con_t *conn, { conn_check_oem_t *check; int rv; - unsigned int count = 0; check = ipmi_mem_alloc(sizeof(*check)); if (!check) @@ -285,10 +284,6 @@ ipmi_conn_check_oem_handlers(ipmi_con_t *conn, locked_list_iterate(oem_handlers, conn_handler_call, check); - ipmi_lock(check->lock); - count = check->count; - ipmi_unlock(check->lock); - /* Say that this function is done with the check. */ conn_oem_check_done(conn, check); diff --git a/lib/control.c b/lib/control.c index 64df4da..346a602 100644 --- a/lib/control.c +++ b/lib/control.c @@ -431,8 +431,7 @@ control_final_destroy(ipmi_control_t *control) if (control->waitq) opq_destroy(control->waitq); - if (control->entity) - ipmi_entity_remove_control(control->entity, control); + ipmi_entity_remove_control(control->entity, control); if (control->oem_info_cleanup_handler) control->oem_info_cleanup_handler(control, control->oem_info); diff --git a/lib/ipmi.c b/lib/ipmi.c index 4ae14ec..45c6cb8 100644 --- a/lib/ipmi.c +++ b/lib/ipmi.c @@ -429,6 +429,7 @@ int init_oem_test(void); int i_ipmi_smi_init(os_handler_t *os_hnd); int i_ipmi_lan_init(os_handler_t *os_hnd); int ipmi_malloc_init(os_handler_t *os_hnd); +void ipmi_malloc_shutdown(void); int i_ipmi_rakp_init(void); int i_ipmi_aes_cbc_init(void); int i_ipmi_hmac_init(void); diff --git a/lib/ipmi_smi.c b/lib/ipmi_smi.c index bce526f..9e4a651 100644 --- a/lib/ipmi_smi.c +++ b/lib/ipmi_smi.c @@ -1644,7 +1644,7 @@ smi_args_get_val(ipmi_args_t *args, " would be 0. This is an integer value."; if (*value) { int len; - len = snprintf(dummy, 1, "%d", sargs->ifnum); + len = snprintf(dummy, 0, "%d", sargs->ifnum); sval = ipmi_mem_alloc(len+1); if (! sval) return ENOMEM; diff --git a/lib/sensor.c b/lib/sensor.c index 96aef5f..865fbb3 100644 --- a/lib/sensor.c +++ b/lib/sensor.c @@ -1040,8 +1040,7 @@ sensor_final_destroy(ipmi_sensor_t *sensor) if (sensor->handler_list_cl) locked_list_destroy(sensor->handler_list_cl); - if (sensor->entity) - ipmi_entity_remove_sensor(sensor->entity, sensor); + ipmi_entity_remove_sensor(sensor->entity, sensor); if (sensor->oem_info_cleanup_handler) sensor->oem_info_cleanup_handler(sensor, sensor->oem_info); diff --git a/lib/strings.c b/lib/strings.c index 0532880..fbe8c19 100644 --- a/lib/strings.c +++ b/lib/strings.c @@ -1291,7 +1291,7 @@ ipmi_get_error_string(unsigned int err, err_type = "IPMI: "; } else if (IPMI_IS_RMCPP_ERR(err)) { int rmcpp_err = IPMI_GET_RMCPP_ERR(err); - if ((rmcpp_err <= 0) && (rmcpp_err > 0x12)) + if ((rmcpp_err <= 0) || (rmcpp_err > 0x12)) rmcpp_err = 0x13; snprintf(buffer+7, buf_len-7, "%s (0x%02x)", rmcpp_error_codes[rmcpp_err - 1], @@ -1329,7 +1329,7 @@ int ipmi_get_error_string_len(unsigned int err) return ipmi_get_cc_string_len(IPMI_GET_IPMI_ERR(err)) + 7; } else if (IPMI_IS_RMCPP_ERR(err)) { int rmcpp_err = IPMI_GET_RMCPP_ERR(err); - if ((rmcpp_err <= 0) && (rmcpp_err > 0x12)) + if ((rmcpp_err <= 0) || (rmcpp_err > 0x12)) rmcpp_err = 0x13; return strlen(rmcpp_error_codes[rmcpp_err - 1]) + 15; } else if (IPMI_IS_SOL_ERR(err)) { diff --git a/sample/eventd.c b/sample/eventd.c index e99c37d..f5e0f4f 100644 --- a/sample/eventd.c +++ b/sample/eventd.c @@ -732,8 +732,12 @@ main(int argc, char *argv[]) /* * We have to daemonize before we fork or sigchld won't work. */ - if (daemonize) - daemon(0, 0); + if (daemonize) { + if (daemon(0, 0) == -1) { + perror("Call to daemonize failed"); + exit(1); + } + } if (execnow) { int infd = newpipe(&outfile); diff --git a/sample/ipmi_serial_bmc_emu.c b/sample/ipmi_serial_bmc_emu.c index e0ae019..184745e 100644 --- a/sample/ipmi_serial_bmc_emu.c +++ b/sample/ipmi_serial_bmc_emu.c @@ -42,7 +42,8 @@ #include #include #include -#include +#include +#include #define _GNU_SOURCE #include diff --git a/unix/selector.c b/unix/selector.c index d6cea65..b36bab3 100644 --- a/unix/selector.c +++ b/unix/selector.c @@ -173,10 +173,6 @@ typedef struct sel_wait_list_s sel_send_sig_cb send_sig; void *send_sig_cb_data; - /* This is the memory used to hold the timeout for select - operation. */ - volatile struct timeval *timeout; - struct sel_wait_list_s *next, *prev; } sel_wait_list_t; @@ -196,6 +192,11 @@ struct selector_s volatile int maxfd; /* The largest file descriptor registered with this code. */ + /* If something is deleted, we increment this count. This way when + a select/epoll returns a non-timeout, we know that we need to ignore + it as it may be from the just deleted fd. */ + unsigned long fd_del_count; + void *fd_lock; /* The timer heap. */ @@ -268,8 +269,6 @@ i_wake_sel_thread(struct selector_s *sel) item = sel->wait_list.next; while (item != &sel->wait_list) { - item->timeout->tv_sec = 0; - item->timeout->tv_usec = 0; if (item->send_sig) item->send_sig(item->thread_id, item->send_sig_cb_data); item = item->next; @@ -285,13 +284,6 @@ sel_wake_all(struct selector_s *sel) } static void -wake_fd_sel_thread(struct selector_s *sel) -{ - sel_wake_all(sel); - sel_fd_unlock(sel); -} - -static void wake_timer_sel_thread(struct selector_s *sel, volatile sel_timer_t *old_top) { if (old_top != theap_get_top(&sel->timer_heap)) @@ -306,10 +298,9 @@ static void add_sel_wait_list(struct selector_s *sel, sel_wait_list_t *item, sel_send_sig_cb send_sig, void *cb_data, - long thread_id, volatile struct timeval *timeout) + long thread_id) { item->thread_id = thread_id; - item->timeout = timeout; item->send_sig = send_sig; item->send_sig_cb_data = cb_data; item->next = sel->wait_list.next; @@ -337,10 +328,11 @@ init_fd(fd_control_t *fd) #ifdef HAVE_EPOLL_PWAIT static int -sel_update_epoll(struct selector_s *sel, int fd, int op, int read_enable) +sel_update_fd(struct selector_s *sel, int fd, int op) { fd_control_t *fdc = (fd_control_t *) &sel->fds[fd]; struct epoll_event event; + int rv; if (sel->epollfd < 0) return 1; @@ -349,11 +341,17 @@ sel_update_epoll(struct selector_s *sel, int fd, int op, int read_enable) event.events = EPOLLONESHOT; event.data.fd = fd; if (fdc->saved_events) { - if (!read_enable) + if (op == EPOLL_CTL_DEL) return 0; + if (!FD_ISSET(fd, &sel->read_set) && !FD_ISSET(fd, &sel->except_set)) + return 0; + fdc->saved_events = 0; op = EPOLL_CTL_ADD; - event.events = EPOLLIN | EPOLLHUP; - } else { + if (FD_ISSET(fd, &sel->read_set)) + event.events |= EPOLLIN | EPOLLHUP; + if (FD_ISSET(fd, &sel->except_set)) + event.events |= EPOLLERR | EPOLLPRI; + } else if (op != EPOLL_CTL_DEL) { if (FD_ISSET(fd, &sel->read_set)) event.events |= EPOLLIN | EPOLLHUP; if (FD_ISSET(fd, &sel->write_set)) @@ -361,12 +359,18 @@ sel_update_epoll(struct selector_s *sel, int fd, int op, int read_enable) if (FD_ISSET(fd, &sel->except_set)) event.events |= EPOLLERR | EPOLLPRI; } - epoll_ctl(sel->epollfd, op, fd, &event); + /* This should only fail due to system problems, and if that's the case, + well, we should probably terminate. */ + rv = epoll_ctl(sel->epollfd, op, fd, &event); + if (rv) { + perror("epoll_ctl"); + assert(0); + } return 0; } #else static int -sel_update_epoll(struct selector_s *sel, int fd, int op, int dummy) +sel_update_fd(struct selector_s *sel, int fd, int op) { return 1; } @@ -411,6 +415,10 @@ sel_set_fd_handlers(struct selector_s *sel, oldstate = fdc->state; olddata = fdc->data; added = 0; +#ifdef HAVE_EPOLL_PWAIT + fdc->saved_events = 0; +#endif + sel->fd_del_count++; } fdc->state = state; fdc->data = data; @@ -424,14 +432,14 @@ sel_set_fd_handlers(struct selector_s *sel, sel->maxfd = fd; } - if (sel_update_epoll(sel, fd, EPOLL_CTL_ADD, 0)) { - wake_fd_sel_thread(sel); - goto out; - } + if (sel_update_fd(sel, fd, EPOLL_CTL_ADD)) + sel_wake_all(sel); + } else { + if (sel_update_fd(sel, fd, EPOLL_CTL_MOD)) + sel_wake_all(sel); } sel_fd_unlock(sel); - out: if (oldstate) { oldstate->deleted = 1; if (oldstate->use_count == 0) { @@ -458,8 +466,11 @@ i_sel_clear_fd_handler(struct selector_s *sel, int fd, int imm) olddata = fdc->data; fdc->state = NULL; - sel_update_epoll(sel, fd, EPOLL_CTL_DEL, 0); + sel_update_fd(sel, fd, EPOLL_CTL_DEL); +#ifdef HAVE_EPOLL_PWAIT fdc->saved_events = 0; +#endif + sel->fd_del_count++; } init_fd(fdc); @@ -526,11 +537,8 @@ sel_set_fd_read_handler(struct selector_s *sel, int fd, int state) goto out; FD_CLR(fd, &sel->read_set); } - if (sel_update_epoll(sel, fd, EPOLL_CTL_MOD, - state == SEL_FD_HANDLER_ENABLED)) { - wake_fd_sel_thread(sel); - return; - } + if (sel_update_fd(sel, fd, EPOLL_CTL_MOD)) + sel_wake_all(sel); out: sel_fd_unlock(sel); @@ -556,10 +564,8 @@ sel_set_fd_write_handler(struct selector_s *sel, int fd, int state) goto out; FD_CLR(fd, &sel->write_set); } - if (sel_update_epoll(sel, fd, EPOLL_CTL_MOD, 0)) { - wake_fd_sel_thread(sel); - return; - } + if (sel_update_fd(sel, fd, EPOLL_CTL_MOD)) + sel_wake_all(sel); out: sel_fd_unlock(sel); @@ -585,10 +591,8 @@ sel_set_fd_except_handler(struct selector_s *sel, int fd, int state) goto out; FD_CLR(fd, &sel->except_set); } - if (sel_update_epoll(sel, fd, EPOLL_CTL_MOD, 0)) { - wake_fd_sel_thread(sel); - return; - } + if (sel_update_fd(sel, fd, EPOLL_CTL_MOD)) + sel_wake_all(sel); out: sel_fd_unlock(sel); @@ -653,6 +657,24 @@ sel_alloc_timer(struct selector_s *sel, return 0; } +static int +sel_stop_timer_i(struct selector_s *sel, sel_timer_t *timer) +{ + if (timer->val.stopped) + return ETIMEDOUT; + + if (timer->val.in_heap) { + volatile sel_timer_t *old_top = theap_get_top(&sel->timer_heap); + + theap_remove(&sel->timer_heap, timer); + timer->val.in_heap = 0; + wake_timer_sel_thread(sel, old_top); + } + timer->val.stopped = 1; + + return 0; +} + int sel_free_timer(sel_timer_t *timer) { @@ -660,9 +682,8 @@ sel_free_timer(sel_timer_t *timer) int in_handler; sel_timer_lock(sel); - if (timer->val.in_heap) { - sel_stop_timer(timer); - } + if (timer->val.in_heap) + sel_stop_timer_i(sel, timer); timer->val.freed = 1; in_handler = timer->val.in_handler; sel_timer_unlock(sel); @@ -708,25 +729,13 @@ int sel_stop_timer(sel_timer_t *timer) { struct selector_s *sel = timer->val.sel; + int rv; sel_timer_lock(sel); - if (timer->val.stopped) { - sel_timer_unlock(sel); - return ETIMEDOUT; - } - - if (timer->val.in_heap) { - volatile sel_timer_t *old_top = theap_get_top(&sel->timer_heap); - - theap_remove(&sel->timer_heap, timer); - timer->val.in_heap = 0; - wake_timer_sel_thread(sel, old_top); - } - timer->val.stopped = 1; - + rv = sel_stop_timer_i(sel, timer); sel_timer_unlock(sel); - return 0; + return rv; } int @@ -737,7 +746,11 @@ sel_stop_timer_with_done(sel_timer_t *timer, struct selector_s *sel = timer->val.sel; sel_timer_lock(sel); - if (timer->val.stopped || timer->val.done_handler) { + if (timer->val.done_handler) { + sel_timer_unlock(sel); + return EBUSY; + } + if (timer->val.stopped) { sel_timer_unlock(sel); return ETIMEDOUT; } @@ -966,6 +979,20 @@ handle_selector_call(struct selector_s *sel, int i, volatile fd_set *fdset, } } +static void +setup_my_sigmask(sigset_t *sigmask, sigset_t *isigmask) +{ + if (isigmask) { + *sigmask = *isigmask; + } else { +#ifdef USE_PTHREADS + pthread_sigmask(SIG_SETMASK, NULL, sigmask); +#else + sigprocmask(SIG_SETMASK, NULL, sigmask); +#endif + } +} + /* * return == 0 when timeout * > 0 when successful @@ -973,7 +1000,8 @@ handle_selector_call(struct selector_s *sel, int i, volatile fd_set *fdset, */ static int process_fds(struct selector_s *sel, - volatile struct timeval *timeout) + volatile struct timeval *timeout, + sigset_t *isigmask) { fd_set tmp_read_set; fd_set tmp_write_set; @@ -981,7 +1009,13 @@ process_fds(struct selector_s *sel, int i; int err; int num_fds; + sigset_t sigmask; + struct timespec ts = { .tv_sec = timeout->tv_sec, + .tv_nsec = timeout->tv_usec * 1000 }; + unsigned long entry_fd_del_count = sel->fd_del_count; + setup_my_sigmask(&sigmask, isigmask); + retry: sel_fd_lock(sel); memcpy(&tmp_read_set, (void *) &sel->read_set, sizeof(tmp_read_set)); memcpy(&tmp_write_set, (void *) &sel->write_set, sizeof(tmp_write_set)); @@ -989,16 +1023,25 @@ process_fds(struct selector_s *sel, num_fds = sel->maxfd+1; sel_fd_unlock(sel); - err = select(num_fds, - &tmp_read_set, - &tmp_write_set, - &tmp_except_set, - (struct timeval *) timeout); - if (err <= 0) + sigdelset(&sigmask, sel->wake_sig); + err = pselect(num_fds, + &tmp_read_set, + &tmp_write_set, + &tmp_except_set, + &ts, &sigmask); + if (err < 0) { + if (errno == EBADF || errno == EBADFD) + /* We raced, just retry it. */ + goto retry; goto out; + } /* We got some I/O. */ sel_fd_lock(sel); + if (entry_fd_del_count != sel->fd_del_count) + /* Something was deleted from the FD set, don't process this as it + may be from the old fd wakeup. */ + goto out_unlock; for (i = 0; i <= sel->maxfd; i++) { if (FD_ISSET(i, &tmp_read_set)) handle_selector_call(sel, i, &sel->read_set, @@ -1010,6 +1053,7 @@ process_fds(struct selector_s *sel, handle_selector_call(sel, i, &sel->except_set, sel->fds[i].handle_except); } + out_unlock: sel_fd_unlock(sel); out: return err; @@ -1017,13 +1061,17 @@ out: #ifdef HAVE_EPOLL_PWAIT static int -process_fds_epoll(struct selector_s *sel, struct timeval *tvtimeout) +process_fds_epoll(struct selector_s *sel, struct timeval *tvtimeout, + sigset_t *isigmask) { int rv, fd; struct epoll_event event; int timeout; sigset_t sigmask; fd_control_t *fdc; + unsigned long entry_fd_del_count = sel->fd_del_count; + + setup_my_sigmask(&sigmask, isigmask); if (tvtimeout->tv_sec > 600) /* Don't wait over 10 minutes, to work around an old epoll bug @@ -1034,20 +1082,18 @@ process_fds_epoll(struct selector_s *sel, struct timeval *tvtimeout) timeout = ((tvtimeout->tv_sec * 1000) + (tvtimeout->tv_usec + 999) / 1000); -#ifdef USE_PTHREADS - pthread_sigmask(SIG_SETMASK, NULL, &sigmask); -#else - sigprocmask(SIG_SETMASK, NULL, &sigmask); -#endif sigdelset(&sigmask, sel->wake_sig); rv = epoll_pwait(sel->epollfd, &event, 1, timeout, &sigmask); - if (rv <= 0) return rv; sel_fd_lock(sel); fd = event.data.fd; fdc = (fd_control_t *) &sel->fds[fd]; + if (entry_fd_del_count != sel->fd_del_count) + /* Something was deleted from the FD set, don't process this as it + may be from the old fd wakeup. */ + goto rearm; if (event.events & (EPOLLHUP | EPOLLERR)) { /* * The crazy people that designed epoll made it so that EPOLLHUP @@ -1059,8 +1105,13 @@ process_fds_epoll(struct selector_s *sel, struct timeval *tvtimeout) * EPOLLHUP or EPOLLERR, anyway, and then doing the callback * by hand. */ - sel_update_epoll(sel, fd, EPOLL_CTL_DEL, 0); + sel_update_fd(sel, fd, EPOLL_CTL_DEL); fdc->saved_events = event.events & (EPOLLHUP | EPOLLERR); + /* + * Have it handle read data, too, so if there is a pending + * error it will get handled. + */ + event.events |= EPOLLIN; } if (event.events & (EPOLLIN | EPOLLHUP)) handle_selector_call(sel, fd, &sel->read_set, fdc->handle_read); @@ -1069,27 +1120,62 @@ process_fds_epoll(struct selector_s *sel, struct timeval *tvtimeout) if (event.events & (EPOLLPRI | EPOLLERR)) handle_selector_call(sel, fd, &sel->except_set, fdc->handle_except); + rearm: /* Rearm the event. Remember it could have been deleted in the handler. */ if (fdc->state) - sel_update_epoll(sel, fd, EPOLL_CTL_MOD, 0); + sel_update_fd(sel, fd, EPOLL_CTL_MOD); sel_fd_unlock(sel); return rv; } + +int +sel_setup_forked_process(struct selector_s *sel) +{ + int i; + + /* + * More epoll stupidity. In a forked process we must create a new + * epoll because the epoll state is shared between a parent and a + * child. If it worked like it should, each epoll instance would + * be independent. If you don't do this, disabling an fd in the + * child disables the parent, too, and vice versa. + */ + close(sel->epollfd); + sel->epollfd = epoll_create(32768); + if (sel->epollfd == -1) { + return errno; + } + + for (i = 0; i <= sel->maxfd; i++) { + volatile fd_control_t *fdc = &sel->fds[i]; + if (fdc->state) + sel_update_fd(sel, i, EPOLL_CTL_ADD); + } + return 0; +} +#else +int +sel_setup_forked_process(struct selector_s *sel) +{ + /* Nothing to do. */ + return 0; +} #endif int -sel_select_intr(struct selector_s *sel, - sel_send_sig_cb send_sig, - long thread_id, - void *cb_data, - struct timeval *timeout) +sel_select_intr_sigmask(struct selector_s *sel, + sel_send_sig_cb send_sig, + long thread_id, + void *cb_data, + struct timeval *timeout, + sigset_t *sigmask) { int err, old_errno; struct timeval loc_timeout = { 0, 0 }; sel_wait_list_t wait_entry; unsigned int count; - struct timeval end, now; + struct timeval end = { 0, 0 }, now; int user_timeout = 0; if (timeout) { @@ -1099,24 +1185,22 @@ sel_select_intr(struct selector_s *sel, sel_timer_lock(sel); count = process_runners(sel); - /* If count is non-zero or any timers are processed, timeout is set to 0. */ - process_timers(sel, &count, (struct timeval *)(&loc_timeout)); + process_timers(sel, &count, &loc_timeout); if (timeout) { - if (cmp_timeval((struct timeval *)(&loc_timeout), timeout) >= 0) { + if (cmp_timeval(&loc_timeout, timeout) >= 0) { loc_timeout = *timeout; user_timeout = 1; } } - add_sel_wait_list(sel, &wait_entry, send_sig, cb_data, thread_id, - &loc_timeout); + add_sel_wait_list(sel, &wait_entry, send_sig, cb_data, thread_id); sel_timer_unlock(sel); #ifdef HAVE_EPOLL_PWAIT if (sel->epollfd >= 0) - err = process_fds_epoll(sel, &loc_timeout); + err = process_fds_epoll(sel, &loc_timeout, sigmask); else #endif - err = process_fds(sel, &loc_timeout); + err = process_fds(sel, &loc_timeout, sigmask); old_errno = errno; if (!user_timeout && !err) { @@ -1146,6 +1230,17 @@ sel_select_intr(struct selector_s *sel, } int +sel_select_intr(struct selector_s *sel, + sel_send_sig_cb send_sig, + long thread_id, + void *cb_data, + struct timeval *timeout) +{ + return sel_select_intr_sigmask(sel, send_sig, thread_id, cb_data, timeout, + NULL); +} + +int sel_select(struct selector_s *sel, sel_send_sig_cb send_sig, long thread_id, @@ -1154,7 +1249,8 @@ sel_select(struct selector_s *sel, { int err; - err = sel_select_intr(sel, send_sig, thread_id, cb_data, timeout); + err = sel_select_intr_sigmask(sel, send_sig, thread_id, cb_data, timeout, + NULL); if (err < 0 && errno == EINTR) /* * If we get an EINTR, we don't want to report a timeout. Just @@ -1198,6 +1294,8 @@ sel_alloc_selector_thread(struct selector_s **new_selector, int wake_sig, { struct selector_s *sel; unsigned int i; + int rv; + sigset_t sigset; sel = malloc(sizeof(*sel)); if (!sel) @@ -1239,28 +1337,23 @@ sel_alloc_selector_thread(struct selector_s **new_selector, int wake_sig, } } -#ifdef HAVE_EPOLL_PWAIT - sel->epollfd = epoll_create(32768); - if (sel->epollfd == -1) { - syslog(LOG_ERR, "Unable to set up epoll, falling back to select: %m"); - } else { - int rv; - sigset_t sigset; - - sigemptyset(&sigset); - sigaddset(&sigset, wake_sig); - rv = sigprocmask(SIG_BLOCK, &sigset, NULL); - if (rv == -1) { - rv = errno; - close(sel->epollfd); - if (sel->sel_lock_alloc) { - sel->sel_lock_free(sel->fd_lock); + sigemptyset(&sigset); + sigaddset(&sigset, wake_sig); + rv = sigprocmask(SIG_BLOCK, &sigset, NULL); + if (rv == -1) { + rv = errno; + if (sel->sel_lock_alloc) { + sel->sel_lock_free(sel->fd_lock); sel->sel_lock_free(sel->timer_lock); - } - free(sel); - return rv; } + free(sel); + return rv; } + +#ifdef HAVE_EPOLL_PWAIT + sel->epollfd = epoll_create(32768); + if (sel->epollfd == -1) + syslog(LOG_ERR, "Unable to set up epoll, falling back to select: %m"); #endif *new_selector = sel; diff --git a/unix/test_handlers.c b/unix/test_handlers.c index b434202..800dd63 100644 --- a/unix/test_handlers.c +++ b/unix/test_handlers.c @@ -135,7 +135,6 @@ timeout_handler(void *cb_data, os_hnd_timer_id_t *id) struct timeval *then = cb_data; struct timeval now; struct timeval diff; - int rv; fprintf(stderr, "Timeout!\n"); test_os_hnd->get_monotonic_time(test_os_hnd, &now); @@ -148,8 +147,8 @@ timeout_handler(void *cb_data, os_hnd_timer_id_t *id) diff.tv_sec = 0; diff.tv_usec = 500000; *then = now; - rv = test_os_hnd->start_timer(test_os_hnd, id, &diff, - timeout_handler, then); + test_os_hnd->start_timer(test_os_hnd, id, &diff, + timeout_handler, then); } else if (expect_timeout == 1) { expect_timeout++; if (diff.tv_sec != 0)