]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/ofed/infiniband-diags/src/rdma-ndd.c
Import libxo-0.9.0:
[FreeBSD/FreeBSD.git] / contrib / ofed / infiniband-diags / src / rdma-ndd.c
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 }