1 /* 2 * Copyright (c) 2014 Intel Corporation. All Rights Reserved 3 * 4 * This software is available to you under a choice of one of two 5 * licenses. You may choose to be licensed under the terms of the GNU 6 * General Public License (GPL) Version 2, available from the file 7 * COPYING in the main directory of this source tree, or the 8 * OpenIB.org BSD license below: 9 * 10 * Redistribution and use in source and binary forms, with or 11 * without modification, are permitted provided that the following 12 * conditions are met: 13 * 14 * - Redistributions of source code must retain the above 15 * copyright notice, this list of conditions and the following 16 * disclaimer. 17 * 18 * - Redistributions in binary form must reproduce the above 19 * copyright notice, this list of conditions and the following 20 * disclaimer in the documentation and/or other materials 21 * provided with the distribution. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 * SOFTWARE. 31 * 32 */ 33 34 #if HAVE_CONFIG_H 35 # include <config.h> 36 #endif /* HAVE_CONFIG_H */ 37 38 #include <poll.h> 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <fcntl.h> 42 #include <assert.h> 43 #include <string.h> 44 #include <limits.h> 45 #include <stdio.h> 46 #include <syslog.h> 47 #include <dirent.h> 48 #include <errno.h> 49 #include <unistd.h> 50 #include <getopt.h> 51 #include <stdlib.h> 52 53 #include <libudev.h> 54 55 struct udev *udev; 56 struct udev_monitor *mon; 57 58 #include "ibdiag_common.h" 59 60 #define SYS_HOSTNAME "/proc/sys/kernel/hostname" 61 #define DEF_SYS_DIR "/sys" 62 char *sys_dir = DEF_SYS_DIR; 63 #define SYS_INFINIBAND "class/infiniband" 64 #define DEFAULT_RETRY_RATE 60 65 #define DEFAULT_RETRY_COUNT 0 66 #define DEFAULT_ND_FORMAT "%h %d" 67 68 int failure_retry_rate = DEFAULT_RETRY_RATE; 69 int set_retry_cnt = DEFAULT_RETRY_COUNT; 70 int foreground = 0; 71 char *pidfile = NULL; 72 73 static void newline_to_null(char *str) 74 { 75 char *term = index(str, '\n'); 76 if (term) 77 *term = '\0'; 78 } 79 80 static void strip_domain(char *str) 81 { 82 char *term = index(str, '.'); 83 if (term) 84 *term = '\0'; 85 } 86 87 static void build_node_desc(char *dest, size_t len, 88 const char *device, const char *hostname) 89 { 90 char *end = dest + len-1; 91 const char *field; 92 char *src = ibd_nd_format; 93 94 while (*src && (dest < end)) { 95 if (*src != '%') { 96 *dest++ = *src++; 97 } else { 98 src++; 99 switch (*src) { 100 case 'h': 101 field = hostname; 102 while (*field && (*field != '.') && (dest < end)) 103 *dest++ = *field++; 104 break; 105 case 'd': 106 field = device; 107 while (*field && (dest < end)) 108 *dest++ = *field++; 109 break; 110 } 111 src++; 112 } 113 } 114 *dest = 0; 115 } 116 117 static int update_node_desc(const char *device, const char *hostname, int force) 118 { 119 int rc; 120 char nd[128]; 121 char new_nd[64]; 122 char nd_file[PATH_MAX]; 123 FILE *f; 124 125 snprintf(nd_file, sizeof(nd_file), "%s/%s/%s/node_desc", 126 sys_dir, SYS_INFINIBAND, device); 127 nd_file[sizeof(nd_file)-1] = '\0'; 128 129 f = fopen(nd_file, "r+"); 130 if (!f) { 131 syslog(LOG_ERR, "Failed to open %s\n", nd_file); 132 return -EIO; 133 } 134 135 if (!fgets(nd, sizeof(nd), f)) { 136 syslog(LOG_ERR, "Failed to read %s\n", nd_file); 137 rc = -EIO; 138 goto error; 139 } 140 newline_to_null(nd); 141 142 build_node_desc(new_nd, sizeof(new_nd), device, hostname); 143 144 if (!force && strncmp(new_nd, nd, sizeof(new_nd)) == 0) { 145 syslog(LOG_INFO, "%s: no change (%s)\n", device, new_nd); 146 } else { 147 syslog(LOG_INFO, "%s: change (%s) -> (%s)\n", 148 device, nd, new_nd); 149 rewind(f); 150 fprintf(f, new_nd); 151 } 152 153 rc = 0; 154 error: 155 fclose(f); 156 return rc; 157 } 158 159 static int set_rdma_node_desc(const char *hostname, int force) 160 { 161 DIR *class_dir; 162 struct dirent *dent; 163 char dev_dir[PATH_MAX]; 164 165 snprintf(dev_dir, sizeof(dev_dir), "%s/%s", sys_dir, SYS_INFINIBAND); 166 dev_dir[sizeof(dev_dir)-1] = '\0'; 167 168 class_dir = opendir(dev_dir); 169 if (!class_dir) { 170 syslog(LOG_INFO, "Failed to open %s", dev_dir); 171 return -ENOSYS; 172 } 173 174 while ((dent = readdir(class_dir))) { 175 int retry = set_retry_cnt; 176 if (dent->d_name[0] == '.') 177 continue; 178 179 while (update_node_desc(dent->d_name, hostname, force) && retry > 0) { 180 syslog(LOG_ERR, "retrying set Node Description on %s\n", 181 dent->d_name); 182 retry--; 183 } 184 } 185 186 closedir(class_dir); 187 return 0; 188 } 189 190 static int read_hostname(int fd, char *name, size_t len) 191 { 192 int rc; 193 memset(name, 0, len); 194 if (read(fd, name, len-1) >= 0) { 195 newline_to_null(name); 196 strip_domain(name); 197 rc = 0; 198 } else { 199 syslog(LOG_ERR, "Read %s Failed\n", SYS_HOSTNAME); 200 rc = -EIO; 201 } 202 return rc; 203 } 204 205 static int process_opts(void *context, int ch, char *optarg) 206 { 207 unsigned long tmp; 208 switch (ch) { 209 case 0: 210 pidfile = optarg; 211 break; 212 case 'f': 213 foreground = 1; 214 break; 215 case 't': 216 tmp = strtoul(optarg, NULL, 0); 217 if (tmp >= INT_MAX) { 218 syslog(LOG_ERR, 219 "Invalid retry rate specified: %lu s\n", 220 tmp); 221 } else { 222 failure_retry_rate = (int)tmp; 223 } 224 break; 225 case 'r': 226 tmp = strtoul(optarg, NULL, 0); 227 if (tmp >= INT_MAX) { 228 syslog(LOG_ERR, 229 "Invalid retry count specified: %lu\n", 230 tmp); 231 } else { 232 set_retry_cnt = (int)tmp; 233 } 234 break; 235 default: 236 return -1; 237 } 238 return 0; 239 } 240 241 #define MSG_MAX 2048 242 static void udev_log_fn(struct udev *ud, int priority, const char *file, int line, 243 const char *fn, const char *format, va_list args) 244 { 245 int off = 0; 246 char msg[MSG_MAX]; 247 off = snprintf(msg, MSG_MAX, "libudev: %s:%d %s", 248 file, line, fn); 249 if (off < MSG_MAX-1) 250 vsnprintf(msg+off, MSG_MAX-off, format, args); 251 syslog(LOG_ERR, msg); 252 } 253 254 static void setup_udev(void) 255 { 256 udev = udev_new(); 257 if (!udev) { 258 syslog(LOG_ERR, "udev_new failed\n"); 259 return; 260 } 261 262 udev_set_log_fn(udev, udev_log_fn); 263 udev_set_log_priority(udev, LOG_INFO); 264 #if HAVE_UDEV_GET_SYS_PATH 265 sys_dir = (char *)udev_get_sys_path(udev); 266 #endif 267 } 268 269 static int get_udev_fd(void) 270 { 271 mon = udev_monitor_new_from_netlink(udev, "udev"); 272 if (!mon) { 273 syslog(LOG_ERR, "udev monitoring failed\n"); 274 return -1; 275 } 276 277 udev_monitor_filter_add_match_subsystem_devtype(mon, "infiniband", NULL); 278 udev_monitor_enable_receiving(mon); 279 return udev_monitor_get_fd(mon); 280 } 281 282 static void process_udev_event(int ud_fd, const char *hostname) 283 { 284 struct udev_device *dev; 285 286 dev = udev_monitor_receive_device(mon); 287 if (dev) { 288 const char *device = udev_device_get_sysname(dev); 289 const char *action = udev_device_get_action(dev); 290 291 syslog(LOG_INFO, "Device event: %s, %s, %s\n", 292 udev_device_get_subsystem(dev), 293 device, action); 294 295 if (device && action 296 && strncmp(action, "add", sizeof("add")) == 0) 297 update_node_desc(device, hostname, 1); 298 299 udev_device_unref(dev); 300 } 301 } 302 303 static void monitor(void) 304 { 305 char hostname[128]; 306 int hn_fd; 307 int rc; 308 struct pollfd fds[2]; 309 int numfds = 1; 310 int ud_fd; 311 312 ud_fd = get_udev_fd(); 313 if (ud_fd >= 0) 314 numfds = 2; 315 316 while (1) { 317 hn_fd = open(SYS_HOSTNAME, O_RDONLY); 318 if (hn_fd < 0) { 319 syslog(LOG_ERR, 320 "Open %s Failed: retry in %d seconds\n", 321 SYS_HOSTNAME, failure_retry_rate); 322 sleep(failure_retry_rate); 323 continue; 324 } 325 326 fds[0].fd = hn_fd; 327 fds[0].events = 0; 328 fds[0].revents = 0; 329 330 fds[1].fd = ud_fd; 331 fds[1].events = POLLIN; 332 fds[1].revents = 0; 333 334 rc = poll(fds, numfds, -1); 335 336 if (rc > 0) { 337 if (read_hostname(hn_fd, hostname, sizeof(hostname)) != 0) 338 hostname[0] = '\0'; 339 340 if (fds[0].revents != 0) 341 syslog(LOG_ERR, "Hostname change: %s\n", hostname); 342 343 if (fds[1].revents != 0) 344 process_udev_event(ud_fd, hostname); 345 346 rc = set_rdma_node_desc((const char *)hostname, 0); 347 } else { 348 syslog(LOG_ERR, "Poll %s Failed\n", SYS_HOSTNAME); 349 rc = -EIO; 350 } 351 352 close(hn_fd); 353 354 if (rc) 355 sleep(failure_retry_rate); 356 } 357 } 358 359 static void remove_pidfile(void) 360 { 361 if (pidfile) 362 unlink(pidfile); 363 } 364 365 static void write_pidfile(void) 366 { 367 FILE *f; 368 if (pidfile) { 369 remove_pidfile(); 370 f = fopen(pidfile, "w"); 371 if (f) { 372 fprintf(f, "%d\n", getpid()); 373 fclose(f); 374 } else { 375 syslog(LOG_ERR, "Failed to write pidfile : %s\n", 376 pidfile); 377 exit(errno); 378 } 379 } 380 } 381 382 int main(int argc, char *argv[]) 383 { 384 int fd; 385 char hostname[128]; 386 387 openlog("rdma-ndd", LOG_PID | LOG_PERROR, LOG_DAEMON); 388 389 const struct ibdiag_opt opts[] = { 390 {"retry_timer", 't', 1, "<retry_timer>", 391 "Length of time to sleep when system errors occur " 392 "when attempting to poll and or read the hostname " 393 "from the system.\n"}, 394 {"retry_count", 'r', 1, "<retry_count>", 395 "Number of times to attempt to retry setting " 396 "of the node description on failure\n"}, 397 {"foreground", 'f', 0, NULL, "run in the foreground instead of as a daemon\n"}, 398 {"pidfile", 0, 1, "<pidfile>", "specify a pid file (daemon mode only)\n"}, 399 {0} 400 }; 401 402 ibdiag_process_opts(argc, argv, NULL, "CPDLGtsKyevd", opts, 403 process_opts, "", NULL); 404 405 if (!ibd_nd_format) 406 ibd_nd_format = DEFAULT_ND_FORMAT; 407 408 if (!foreground) { 409 closelog(); 410 openlog("rdma-ndd", LOG_PID, LOG_DAEMON); 411 if (daemon(0, 0) != 0) { 412 syslog(LOG_ERR, "Failed to daemonize\n"); 413 exit(errno); 414 } 415 write_pidfile(); 416 } 417 418 setup_udev(); 419 420 syslog(LOG_INFO, "Node Descriptor format (%s)\n", ibd_nd_format); 421 422 fd = open(SYS_HOSTNAME, O_RDONLY); 423 if (read_hostname(fd, hostname, sizeof(hostname)) != 0) 424 hostname[0] = '\0'; 425 set_rdma_node_desc((const char *)hostname, 1); 426 close(fd); 427 428 monitor(); 429 430 remove_pidfile(); 431 432 return 0; 433 } 434