xref: /freebsd/contrib/ofed/infiniband-diags/src/rdma-ndd.c (revision 87181516ef48be852d5e5fee53c6e0dbfc62f21e)
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  
newline_to_null(char * str)73  static void newline_to_null(char *str)
74  {
75  	char *term = index(str, '\n');
76  	if (term)
77  		*term = '\0';
78  }
79  
strip_domain(char * str)80  static void strip_domain(char *str)
81  {
82  	char *term = index(str, '.');
83  	if (term)
84  		*term = '\0';
85  }
86  
build_node_desc(char * dest,size_t len,const char * device,const char * hostname)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  
update_node_desc(const char * device,const char * hostname,int force)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  
set_rdma_node_desc(const char * hostname,int force)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  
read_hostname(int fd,char * name,size_t len)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  
process_opts(void * context,int ch,char * optarg)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
udev_log_fn(struct udev * ud,int priority,const char * file,int line,const char * fn,const char * format,va_list args)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  
setup_udev(void)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  
get_udev_fd(void)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  
process_udev_event(int ud_fd,const char * hostname)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  
monitor(void)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  
remove_pidfile(void)359  static void remove_pidfile(void)
360  {
361          if (pidfile)
362  		unlink(pidfile);
363  }
364  
write_pidfile(void)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  
main(int argc,char * argv[])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