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