/* * Copyright (c) 2014,2016 Intel Corporation. All Rights Reserved * * 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 * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - 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. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct udev *g_udev; static struct udev_monitor *g_mon; #define SYS_HOSTNAME "/proc/sys/kernel/hostname" #define SYS_INFINIBAND "/sys/class/infiniband" #define DEFAULT_ND_FORMAT "%h %d" static char *g_nd_format = NULL; static bool debugging; static void newline_to_null(char *str) { char *term = index(str, '\n'); if (term) *term = '\0'; } static void strip_domain(char *str) { char *term = index(str, '.'); if (term) *term = '\0'; } static __attribute__((format(printf, 1, 2))) void dbg_log(const char *fmt, ...) { va_list ap; if (!debugging) return; va_start(ap, fmt); vsyslog(LOG_DEBUG, fmt, ap); va_end(ap); } static void build_node_desc(char *dest, size_t len, const char *device, const char *hostname) { char *end = dest + len-1; const char *field; char *src = g_nd_format; while (*src && (dest < end)) { if (*src != '%') { *dest++ = *src++; } else { src++; switch (*src) { case 'h': field = hostname; while (*field && (*field != '.') && (dest < end)) *dest++ = *field++; break; case 'd': field = device; while (*field && (dest < end)) *dest++ = *field++; break; } src++; } } *dest = 0; } static int update_node_desc(const char *device, const char *hostname, int force) { int rc; char nd[128]; char new_nd[64]; char nd_file[PATH_MAX]; FILE *f; snprintf(nd_file, sizeof(nd_file), SYS_INFINIBAND "/%s/node_desc", device); nd_file[sizeof(nd_file)-1] = '\0'; f = fopen(nd_file, "r+"); if (!f) return -EIO; if (!fgets(nd, sizeof(nd), f)) { syslog(LOG_ERR, "Failed to read %s\n", nd_file); rc = -EIO; goto error; } newline_to_null(nd); build_node_desc(new_nd, sizeof(new_nd), device, hostname); if (!force && strncmp(new_nd, nd, sizeof(new_nd)) == 0) { dbg_log("%s: no change (%s)\n", device, new_nd); } else { dbg_log("%s: change (%s) -> (%s)\n", device, nd, new_nd); rewind(f); fprintf(f, "%s", new_nd); } rc = 0; error: fclose(f); return rc; } static void set_rdma_node_desc(const char *hostname, int force) { DIR *class_dir; struct dirent *dent; class_dir = opendir(SYS_INFINIBAND); if (!class_dir) { syslog(LOG_ERR, "Failed to open " SYS_INFINIBAND); return; } while ((dent = readdir(class_dir))) { if (dent->d_name[0] == '.') continue; if (update_node_desc(dent->d_name, hostname, force)) syslog(LOG_DEBUG, "set Node Description failed on %s\n", dent->d_name); } closedir(class_dir); } static void read_hostname(int fd, char *name, size_t len) { memset(name, 0, len); if (read(fd, name, len-1) >= 0) { newline_to_null(name); strip_domain(name); } else { syslog(LOG_ERR, "Read %s Failed\n", SYS_HOSTNAME); } lseek(fd, 0, SEEK_SET); } static void setup_udev(void) { g_udev = udev_new(); if (!g_udev) { syslog(LOG_ERR, "udev_new failed\n"); return; } } static int get_udev_fd(void) { g_mon = udev_monitor_new_from_netlink(g_udev, "udev"); if (!g_mon) { syslog(LOG_ERR, "udev monitoring failed\n"); return -1; } udev_monitor_filter_add_match_subsystem_devtype(g_mon, "infiniband", NULL); udev_monitor_enable_receiving(g_mon); return udev_monitor_get_fd(g_mon); } static void process_udev_event(int ud_fd, const char *hostname) { struct udev_device *dev; dev = udev_monitor_receive_device(g_mon); if (dev) { const char *device = udev_device_get_sysname(dev); const char *action = udev_device_get_action(dev); dbg_log("Device event: %s, %s, %s\n", udev_device_get_subsystem(dev), device, action); if (device && action && (!strncmp(action, "add", sizeof("add")) || !strncmp(action, "move", sizeof("add")))) if (update_node_desc(device, hostname, 1)) syslog(LOG_DEBUG, "set Node Description failed on %s\n", device); udev_device_unref(dev); } } static void monitor(bool systemd) { char hostname[128]; int hn_fd; struct pollfd fds[2]; int numfds = 1; int ud_fd; hn_fd = open(SYS_HOSTNAME, O_RDONLY); if (hn_fd < 0) { syslog(LOG_ERR, "Open %s Failed exiting\n", SYS_HOSTNAME); exit(EXIT_FAILURE); } read_hostname(hn_fd, hostname, sizeof(hostname)); fds[0].fd = hn_fd; fds[0].events = 0; ud_fd = get_udev_fd(); if (ud_fd >= 0) numfds = 2; fds[1].fd = ud_fd; fds[1].events = POLLIN; if (systemd) sd_notify(0, "READY=1"); set_rdma_node_desc((const char *)hostname, 1); while (1) { if (poll(fds, numfds, -1) <= 0) { syslog(LOG_ERR, "Poll %s failed; exiting\n", SYS_HOSTNAME); exit(EXIT_FAILURE); } if (fds[0].revents != 0) { read_hostname(hn_fd, hostname, sizeof(hostname)); dbg_log("Hostname event: %s\n", hostname); set_rdma_node_desc((const char *)hostname, 0); } if (fds[1].revents != 0) process_udev_event(ud_fd, hostname); } } int main(int argc, char *argv[]) { bool foreground = false; bool systemd = false; openlog(NULL, LOG_NDELAY | LOG_CONS | LOG_PID, LOG_DAEMON); while (1) { static const struct option long_opts[] = { { "foreground", 0, NULL, 'f' }, { "systemd", 0, NULL, 's' }, { "help", 0, NULL, 'h' }, { "debug", 0, NULL, 'd' }, { } }; int c = getopt_long(argc, argv, "fh", long_opts, NULL); if (c == -1) break; switch (c) { case 'f': foreground = true; break; case 's': systemd = true; break; case 'd': debugging = true; break; case 'h': printf("rdma-ndd [options]\n"); printf(" See 'man rdma-ndd' for details\n"); return 0; default: break; } } if (!foreground && !systemd) { if (daemon(0, 0) != 0) { syslog(LOG_ERR, "Failed to daemonize\n"); return EXIT_FAILURE; } } setup_udev(); g_nd_format = getenv("RDMA_NDD_ND_FORMAT"); if (g_nd_format && strncmp("", g_nd_format, strlen(g_nd_format)) != 0) g_nd_format = strdup(g_nd_format); else g_nd_format = strdup(DEFAULT_ND_FORMAT); dbg_log("Node Descriptor format (%s)\n", g_nd_format); monitor(systemd); return 0; }