1321936Shselasky/* 2321936Shselasky * Copyright (c) 2014 Intel Corporation. All Rights Reserved 3321936Shselasky * 4321936Shselasky * This software is available to you under a choice of one of two 5321936Shselasky * licenses. You may choose to be licensed under the terms of the GNU 6321936Shselasky * General Public License (GPL) Version 2, available from the file 7321936Shselasky * COPYING in the main directory of this source tree, or the 8321936Shselasky * OpenIB.org BSD license below: 9321936Shselasky * 10321936Shselasky * Redistribution and use in source and binary forms, with or 11321936Shselasky * without modification, are permitted provided that the following 12321936Shselasky * conditions are met: 13321936Shselasky * 14321936Shselasky * - Redistributions of source code must retain the above 15321936Shselasky * copyright notice, this list of conditions and the following 16321936Shselasky * disclaimer. 17321936Shselasky * 18321936Shselasky * - Redistributions in binary form must reproduce the above 19321936Shselasky * copyright notice, this list of conditions and the following 20321936Shselasky * disclaimer in the documentation and/or other materials 21321936Shselasky * provided with the distribution. 22321936Shselasky * 23321936Shselasky * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24321936Shselasky * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25321936Shselasky * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26321936Shselasky * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27321936Shselasky * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28321936Shselasky * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29321936Shselasky * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30321936Shselasky * SOFTWARE. 31321936Shselasky * 32321936Shselasky */ 33321936Shselasky 34321936Shselasky#if HAVE_CONFIG_H 35321936Shselasky# include <config.h> 36321936Shselasky#endif /* HAVE_CONFIG_H */ 37321936Shselasky 38321936Shselasky#include <poll.h> 39321936Shselasky#include <sys/types.h> 40321936Shselasky#include <sys/stat.h> 41321936Shselasky#include <fcntl.h> 42321936Shselasky#include <assert.h> 43321936Shselasky#include <string.h> 44321936Shselasky#include <limits.h> 45321936Shselasky#include <stdio.h> 46321936Shselasky#include <syslog.h> 47321936Shselasky#include <dirent.h> 48321936Shselasky#include <errno.h> 49321936Shselasky#include <unistd.h> 50321936Shselasky#include <getopt.h> 51321936Shselasky#include <stdlib.h> 52321936Shselasky 53321936Shselasky#include <libudev.h> 54321936Shselasky 55321936Shselaskystruct udev *udev; 56321936Shselaskystruct udev_monitor *mon; 57321936Shselasky 58321936Shselasky#include "ibdiag_common.h" 59321936Shselasky 60321936Shselasky#define SYS_HOSTNAME "/proc/sys/kernel/hostname" 61321936Shselasky#define DEF_SYS_DIR "/sys" 62321936Shselaskychar *sys_dir = DEF_SYS_DIR; 63321936Shselasky#define SYS_INFINIBAND "class/infiniband" 64321936Shselasky#define DEFAULT_RETRY_RATE 60 65321936Shselasky#define DEFAULT_RETRY_COUNT 0 66321936Shselasky#define DEFAULT_ND_FORMAT "%h %d" 67321936Shselasky 68321936Shselaskyint failure_retry_rate = DEFAULT_RETRY_RATE; 69321936Shselaskyint set_retry_cnt = DEFAULT_RETRY_COUNT; 70321936Shselaskyint foreground = 0; 71321936Shselaskychar *pidfile = NULL; 72321936Shselasky 73321936Shselaskystatic void newline_to_null(char *str) 74321936Shselasky{ 75321936Shselasky char *term = index(str, '\n'); 76321936Shselasky if (term) 77321936Shselasky *term = '\0'; 78321936Shselasky} 79321936Shselasky 80321936Shselaskystatic void strip_domain(char *str) 81321936Shselasky{ 82321936Shselasky char *term = index(str, '.'); 83321936Shselasky if (term) 84321936Shselasky *term = '\0'; 85321936Shselasky} 86321936Shselasky 87321936Shselaskystatic void build_node_desc(char *dest, size_t len, 88321936Shselasky const char *device, const char *hostname) 89321936Shselasky{ 90321936Shselasky char *end = dest + len-1; 91321936Shselasky const char *field; 92321936Shselasky char *src = ibd_nd_format; 93321936Shselasky 94321936Shselasky while (*src && (dest < end)) { 95321936Shselasky if (*src != '%') { 96321936Shselasky *dest++ = *src++; 97321936Shselasky } else { 98321936Shselasky src++; 99321936Shselasky switch (*src) { 100321936Shselasky case 'h': 101321936Shselasky field = hostname; 102321936Shselasky while (*field && (*field != '.') && (dest < end)) 103321936Shselasky *dest++ = *field++; 104321936Shselasky break; 105321936Shselasky case 'd': 106321936Shselasky field = device; 107321936Shselasky while (*field && (dest < end)) 108321936Shselasky *dest++ = *field++; 109321936Shselasky break; 110321936Shselasky } 111321936Shselasky src++; 112321936Shselasky } 113321936Shselasky } 114321936Shselasky *dest = 0; 115321936Shselasky} 116321936Shselasky 117321936Shselaskystatic int update_node_desc(const char *device, const char *hostname, int force) 118321936Shselasky{ 119321936Shselasky int rc; 120321936Shselasky char nd[128]; 121321936Shselasky char new_nd[64]; 122321936Shselasky char nd_file[PATH_MAX]; 123321936Shselasky FILE *f; 124321936Shselasky 125321936Shselasky snprintf(nd_file, sizeof(nd_file), "%s/%s/%s/node_desc", 126321936Shselasky sys_dir, SYS_INFINIBAND, device); 127321936Shselasky nd_file[sizeof(nd_file)-1] = '\0'; 128321936Shselasky 129321936Shselasky f = fopen(nd_file, "r+"); 130321936Shselasky if (!f) { 131321936Shselasky syslog(LOG_ERR, "Failed to open %s\n", nd_file); 132321936Shselasky return -EIO; 133321936Shselasky } 134321936Shselasky 135321936Shselasky if (!fgets(nd, sizeof(nd), f)) { 136321936Shselasky syslog(LOG_ERR, "Failed to read %s\n", nd_file); 137321936Shselasky rc = -EIO; 138321936Shselasky goto error; 139321936Shselasky } 140321936Shselasky newline_to_null(nd); 141321936Shselasky 142321936Shselasky build_node_desc(new_nd, sizeof(new_nd), device, hostname); 143321936Shselasky 144321936Shselasky if (!force && strncmp(new_nd, nd, sizeof(new_nd)) == 0) { 145321936Shselasky syslog(LOG_INFO, "%s: no change (%s)\n", device, new_nd); 146321936Shselasky } else { 147321936Shselasky syslog(LOG_INFO, "%s: change (%s) -> (%s)\n", 148321936Shselasky device, nd, new_nd); 149321936Shselasky rewind(f); 150321936Shselasky fprintf(f, new_nd); 151321936Shselasky } 152321936Shselasky 153321936Shselasky rc = 0; 154321936Shselaskyerror: 155321936Shselasky fclose(f); 156321936Shselasky return rc; 157321936Shselasky} 158321936Shselasky 159321936Shselaskystatic int set_rdma_node_desc(const char *hostname, int force) 160321936Shselasky{ 161321936Shselasky DIR *class_dir; 162321936Shselasky struct dirent *dent; 163321936Shselasky char dev_dir[PATH_MAX]; 164321936Shselasky 165321936Shselasky snprintf(dev_dir, sizeof(dev_dir), "%s/%s", sys_dir, SYS_INFINIBAND); 166321936Shselasky dev_dir[sizeof(dev_dir)-1] = '\0'; 167321936Shselasky 168321936Shselasky class_dir = opendir(dev_dir); 169321936Shselasky if (!class_dir) { 170321936Shselasky syslog(LOG_INFO, "Failed to open %s", dev_dir); 171321936Shselasky return -ENOSYS; 172321936Shselasky } 173321936Shselasky 174321936Shselasky while ((dent = readdir(class_dir))) { 175321936Shselasky int retry = set_retry_cnt; 176321936Shselasky if (dent->d_name[0] == '.') 177321936Shselasky continue; 178321936Shselasky 179321936Shselasky while (update_node_desc(dent->d_name, hostname, force) && retry > 0) { 180321936Shselasky syslog(LOG_ERR, "retrying set Node Description on %s\n", 181321936Shselasky dent->d_name); 182321936Shselasky retry--; 183321936Shselasky } 184321936Shselasky } 185321936Shselasky 186321936Shselasky closedir(class_dir); 187321936Shselasky return 0; 188321936Shselasky} 189321936Shselasky 190321936Shselaskystatic int read_hostname(int fd, char *name, size_t len) 191321936Shselasky{ 192321936Shselasky int rc; 193321936Shselasky memset(name, 0, len); 194321936Shselasky if (read(fd, name, len-1) >= 0) { 195321936Shselasky newline_to_null(name); 196321936Shselasky strip_domain(name); 197321936Shselasky rc = 0; 198321936Shselasky } else { 199321936Shselasky syslog(LOG_ERR, "Read %s Failed\n", SYS_HOSTNAME); 200321936Shselasky rc = -EIO; 201321936Shselasky } 202321936Shselasky return rc; 203321936Shselasky} 204321936Shselasky 205321936Shselaskystatic int process_opts(void *context, int ch, char *optarg) 206321936Shselasky{ 207321936Shselasky unsigned long tmp; 208321936Shselasky switch (ch) { 209321936Shselasky case 0: 210321936Shselasky pidfile = optarg; 211321936Shselasky break; 212321936Shselasky case 'f': 213321936Shselasky foreground = 1; 214321936Shselasky break; 215321936Shselasky case 't': 216321936Shselasky tmp = strtoul(optarg, NULL, 0); 217321936Shselasky if (tmp >= INT_MAX) { 218321936Shselasky syslog(LOG_ERR, 219321936Shselasky "Invalid retry rate specified: %lu s\n", 220321936Shselasky tmp); 221321936Shselasky } else { 222321936Shselasky failure_retry_rate = (int)tmp; 223321936Shselasky } 224321936Shselasky break; 225321936Shselasky case 'r': 226321936Shselasky tmp = strtoul(optarg, NULL, 0); 227321936Shselasky if (tmp >= INT_MAX) { 228321936Shselasky syslog(LOG_ERR, 229321936Shselasky "Invalid retry count specified: %lu\n", 230321936Shselasky tmp); 231321936Shselasky } else { 232321936Shselasky set_retry_cnt = (int)tmp; 233321936Shselasky } 234321936Shselasky break; 235321936Shselasky default: 236321936Shselasky return -1; 237321936Shselasky } 238321936Shselasky return 0; 239321936Shselasky} 240321936Shselasky 241321936Shselasky#define MSG_MAX 2048 242321936Shselaskystatic void udev_log_fn(struct udev *ud, int priority, const char *file, int line, 243321936Shselasky const char *fn, const char *format, va_list args) 244321936Shselasky{ 245321936Shselasky int off = 0; 246321936Shselasky char msg[MSG_MAX]; 247321936Shselasky off = snprintf(msg, MSG_MAX, "libudev: %s:%d %s", 248321936Shselasky file, line, fn); 249321936Shselasky if (off < MSG_MAX-1) 250321936Shselasky vsnprintf(msg+off, MSG_MAX-off, format, args); 251321936Shselasky syslog(LOG_ERR, msg); 252321936Shselasky} 253321936Shselasky 254321936Shselaskystatic void setup_udev(void) 255321936Shselasky{ 256321936Shselasky udev = udev_new(); 257321936Shselasky if (!udev) { 258321936Shselasky syslog(LOG_ERR, "udev_new failed\n"); 259321936Shselasky return; 260321936Shselasky } 261321936Shselasky 262321936Shselasky udev_set_log_fn(udev, udev_log_fn); 263321936Shselasky udev_set_log_priority(udev, LOG_INFO); 264321936Shselasky#if HAVE_UDEV_GET_SYS_PATH 265321936Shselasky sys_dir = (char *)udev_get_sys_path(udev); 266321936Shselasky#endif 267321936Shselasky} 268321936Shselasky 269321936Shselaskystatic int get_udev_fd(void) 270321936Shselasky{ 271321936Shselasky mon = udev_monitor_new_from_netlink(udev, "udev"); 272321936Shselasky if (!mon) { 273321936Shselasky syslog(LOG_ERR, "udev monitoring failed\n"); 274321936Shselasky return -1; 275321936Shselasky } 276321936Shselasky 277321936Shselasky udev_monitor_filter_add_match_subsystem_devtype(mon, "infiniband", NULL); 278321936Shselasky udev_monitor_enable_receiving(mon); 279321936Shselasky return udev_monitor_get_fd(mon); 280321936Shselasky} 281321936Shselasky 282321936Shselaskystatic void process_udev_event(int ud_fd, const char *hostname) 283321936Shselasky{ 284321936Shselasky struct udev_device *dev; 285321936Shselasky 286321936Shselasky dev = udev_monitor_receive_device(mon); 287321936Shselasky if (dev) { 288321936Shselasky const char *device = udev_device_get_sysname(dev); 289321936Shselasky const char *action = udev_device_get_action(dev); 290321936Shselasky 291321936Shselasky syslog(LOG_INFO, "Device event: %s, %s, %s\n", 292321936Shselasky udev_device_get_subsystem(dev), 293321936Shselasky device, action); 294321936Shselasky 295321936Shselasky if (device && action 296321936Shselasky && strncmp(action, "add", sizeof("add")) == 0) 297321936Shselasky update_node_desc(device, hostname, 1); 298321936Shselasky 299321936Shselasky udev_device_unref(dev); 300321936Shselasky } 301321936Shselasky} 302321936Shselasky 303321936Shselaskystatic void monitor(void) 304321936Shselasky{ 305321936Shselasky char hostname[128]; 306321936Shselasky int hn_fd; 307321936Shselasky int rc; 308321936Shselasky struct pollfd fds[2]; 309321936Shselasky int numfds = 1; 310321936Shselasky int ud_fd; 311321936Shselasky 312321936Shselasky ud_fd = get_udev_fd(); 313321936Shselasky if (ud_fd >= 0) 314321936Shselasky numfds = 2; 315321936Shselasky 316321936Shselasky while (1) { 317321936Shselasky hn_fd = open(SYS_HOSTNAME, O_RDONLY); 318321936Shselasky if (hn_fd < 0) { 319321936Shselasky syslog(LOG_ERR, 320321936Shselasky "Open %s Failed: retry in %d seconds\n", 321321936Shselasky SYS_HOSTNAME, failure_retry_rate); 322321936Shselasky sleep(failure_retry_rate); 323321936Shselasky continue; 324321936Shselasky } 325321936Shselasky 326321936Shselasky fds[0].fd = hn_fd; 327321936Shselasky fds[0].events = 0; 328321936Shselasky fds[0].revents = 0; 329321936Shselasky 330321936Shselasky fds[1].fd = ud_fd; 331321936Shselasky fds[1].events = POLLIN; 332321936Shselasky fds[1].revents = 0; 333321936Shselasky 334321936Shselasky rc = poll(fds, numfds, -1); 335321936Shselasky 336321936Shselasky if (rc > 0) { 337321936Shselasky if (read_hostname(hn_fd, hostname, sizeof(hostname)) != 0) 338321936Shselasky hostname[0] = '\0'; 339321936Shselasky 340321936Shselasky if (fds[0].revents != 0) 341321936Shselasky syslog(LOG_ERR, "Hostname change: %s\n", hostname); 342321936Shselasky 343321936Shselasky if (fds[1].revents != 0) 344321936Shselasky process_udev_event(ud_fd, hostname); 345321936Shselasky 346321936Shselasky rc = set_rdma_node_desc((const char *)hostname, 0); 347321936Shselasky } else { 348321936Shselasky syslog(LOG_ERR, "Poll %s Failed\n", SYS_HOSTNAME); 349321936Shselasky rc = -EIO; 350321936Shselasky } 351321936Shselasky 352321936Shselasky close(hn_fd); 353321936Shselasky 354321936Shselasky if (rc) 355321936Shselasky sleep(failure_retry_rate); 356321936Shselasky } 357321936Shselasky} 358321936Shselasky 359321936Shselaskystatic void remove_pidfile(void) 360321936Shselasky{ 361321936Shselasky if (pidfile) 362321936Shselasky unlink(pidfile); 363321936Shselasky} 364321936Shselasky 365321936Shselaskystatic void write_pidfile(void) 366321936Shselasky{ 367321936Shselasky FILE *f; 368321936Shselasky if (pidfile) { 369321936Shselasky remove_pidfile(); 370321936Shselasky f = fopen(pidfile, "w"); 371321936Shselasky if (f) { 372321936Shselasky fprintf(f, "%d\n", getpid()); 373321936Shselasky fclose(f); 374321936Shselasky } else { 375321936Shselasky syslog(LOG_ERR, "Failed to write pidfile : %s\n", 376321936Shselasky pidfile); 377321936Shselasky exit(errno); 378321936Shselasky } 379321936Shselasky } 380321936Shselasky} 381321936Shselasky 382321936Shselaskyint main(int argc, char *argv[]) 383321936Shselasky{ 384321936Shselasky int fd; 385321936Shselasky char hostname[128]; 386321936Shselasky 387321936Shselasky openlog("rdma-ndd", LOG_PID | LOG_PERROR, LOG_DAEMON); 388321936Shselasky 389321936Shselasky const struct ibdiag_opt opts[] = { 390321936Shselasky {"retry_timer", 't', 1, "<retry_timer>", 391321936Shselasky "Length of time to sleep when system errors occur " 392321936Shselasky "when attempting to poll and or read the hostname " 393321936Shselasky "from the system.\n"}, 394321936Shselasky {"retry_count", 'r', 1, "<retry_count>", 395321936Shselasky "Number of times to attempt to retry setting " 396321936Shselasky "of the node description on failure\n"}, 397321936Shselasky {"foreground", 'f', 0, NULL, "run in the foreground instead of as a daemon\n"}, 398321936Shselasky {"pidfile", 0, 1, "<pidfile>", "specify a pid file (daemon mode only)\n"}, 399321936Shselasky {0} 400321936Shselasky }; 401321936Shselasky 402321936Shselasky ibdiag_process_opts(argc, argv, NULL, "CPDLGtsKyevd", opts, 403321936Shselasky process_opts, "", NULL); 404321936Shselasky 405321936Shselasky if (!ibd_nd_format) 406321936Shselasky ibd_nd_format = DEFAULT_ND_FORMAT; 407321936Shselasky 408321936Shselasky if (!foreground) { 409321936Shselasky closelog(); 410321936Shselasky openlog("rdma-ndd", LOG_PID, LOG_DAEMON); 411321936Shselasky if (daemon(0, 0) != 0) { 412321936Shselasky syslog(LOG_ERR, "Failed to daemonize\n"); 413321936Shselasky exit(errno); 414321936Shselasky } 415321936Shselasky write_pidfile(); 416321936Shselasky } 417321936Shselasky 418321936Shselasky setup_udev(); 419321936Shselasky 420321936Shselasky syslog(LOG_INFO, "Node Descriptor format (%s)\n", ibd_nd_format); 421321936Shselasky 422321936Shselasky fd = open(SYS_HOSTNAME, O_RDONLY); 423321936Shselasky if (read_hostname(fd, hostname, sizeof(hostname)) != 0) 424321936Shselasky hostname[0] = '\0'; 425321936Shselasky set_rdma_node_desc((const char *)hostname, 1); 426321936Shselasky close(fd); 427321936Shselasky 428321936Shselasky monitor(); 429321936Shselasky 430321936Shselasky remove_pidfile(); 431321936Shselasky 432321936Shselasky return 0; 433321936Shselasky} 434