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