1    	/*
2    	 * Copyright 2012 Red Hat, Inc.
3    	 *
4    	 * This copyrighted material is made available to anyone wishing to use,
5    	 * modify, copy, or redistribute it subject to the terms and conditions
6    	 * of the GNU General Public License v2 or (at your option) any later version.
7    	 */
8    	
9    	
10   	#include <stdio.h>
11   	#include <stdlib.h>
12   	#include <unistd.h>
13   	#include <string.h>
14   	#include <errno.h>
15   	
16   	#include "fence_config.h"
17   	
18   	#if 0
19   	
20   	Empty new line separates the config for each fence device.
21   	
22   	-
23   	
24   	fence_all fence_foo key=val ...
25   	
26   	Special fence config format that applies to all nodes and allows
27   	no per node config parameters.  Multiple fence devices (parallel
28   	or priority) cannot be used with fence_all.
29   	
30   	fence_all fence_foo ...
31   	unfence_all
32   	
33   	Apply unfencing to all nodes.
34   	
35   	-
36   	
37   	device  <dev_name> <agent> <dev_args>
38   	connect <dev_name> node=<nodeid> <con_args>
39   	
40   	General fence config format, allowing per node config
41   	parameters.
42   	
43   	device  <dev_name> <agent> <dev_args>
44   	connect <dev_name> node=<nodeid> <con_args>
45   	unfence <dev_name>
46   	
47   	Apply unfencing to all nodes connected to this device.
48   	
49   	-
50   	
51   	device  foo fence_foo ipaddr=1.1.1.1 login=x password=y
52   	connect foo node=1 port=1
53   	connect foo node=2 port=2
54   	connect foo node=3 port=3
55   	
56   	Simple example of nodes connected to switch ports.
57   	If fencing with the device fails, the next device
58   	listed for the node, if any, will be tried.
59   	
60   	-
61   	
62   	device  foo:1 fence_foo ipaddr=1.1.1.1 login=x password=y
63   	connect foo:1 node=1 port=1
64   	connect foo:1 node=2 port=2
65   	connect foo:1 node=3 port=3
66   	
67   	device  foo:2 fence_foo ipaddr=2.2.2.2 login=x password=y
68   	connect foo:2 node=1 port=1
69   	connect foo:2 node=2 port=2
70   	connect foo:2 node=3 port=3
71   	
72   	Associate two parallel path/power devices that must both
73   	succeed for fencing to succeed.  Devices have same base
74   	name with :1 :2 suffix.
75   	
76   	-
77   	
78   	device  foo fence_foo ipaddr=1.1.1.1 login=x password=y
79   	connect foo node=1 port=1
80   	connect foo node=2 port=2
81   	connect foo node=3 port=3
82   	unfence foo
83   	
84   	Add unfence line to indicate nodes connected to the device
85   	should be unfenced.
86   	
87   	#endif
88   	
89   	#define MAX_LINE (FENCE_CONFIG_ARGS_MAX + (3 * FENCE_CONFIG_NAME_MAX))
90   	
91   	static unsigned int con_args_nodeid(char *args)
92   	{
93   		char *k;
94   		unsigned int v;
95   		int rv;
96   	
(1) Event returned_null: "strstr" returns "NULL" (checked 12 out of 14 times).
(10) Event var_assigned: Assigning: "k" = "NULL" return value from "strstr".
Also see events: [example_assign][example_checked][example_assign][example_checked][example_checked][example_assign][example_checked][example_checked][dereference]
97   		k = strstr(args, "node=");
98   	
(11) Event dereference: Passing null pointer "k" to "sscanf", which dereferences it.
Also see events: [returned_null][example_assign][example_checked][example_assign][example_checked][example_checked][example_assign][example_checked][example_checked][var_assigned]
99   		rv = sscanf(k, "node=%u", &v);
100  		if (rv != 1)
101  			return 0;
102  		return v;
103  	}
104  	
105  	static int read_config_section(unsigned int nodeid, FILE *file, char *dev_line,
106  				       struct fence_device **dev_out,
107  				       struct fence_connect **con_out)
108  	{
109  		struct fence_device *dev = NULL;
110  		struct fence_connect *con;
111  		char line[MAX_LINE];
112  		char unused[FENCE_CONFIG_NAME_MAX];
113  		char agent[FENCE_CONFIG_NAME_MAX];
114  		char dev_name[FENCE_CONFIG_NAME_MAX];
115  		char con_name[FENCE_CONFIG_NAME_MAX];
116  		char dev_args[FENCE_CONFIG_ARGS_MAX];
117  		char con_args[FENCE_CONFIG_ARGS_MAX];
118  		int rv, unfence = 0;
119  	
120  		if (strlen(dev_line) > MAX_LINE)
121  			return -1;
122  	
123  		memset(dev_name, 0, sizeof(dev_name));
124  		memset(agent, 0, sizeof(agent));
125  		memset(dev_args, 0, sizeof(dev_args));
126  	
127  		rv = sscanf(dev_line, "%s %s %s %[^\n]s\n", unused, dev_name, agent, dev_args);
128  		if (rv < 3)
129  			return -1;
130  	
131  		while (fgets(line, MAX_LINE, file)) {
132  			if (line[0] == '\n')
133  				break;
134  			if (line[0] == ' ')
135  				break;
136  			if (line[0] == '#')
137  				continue;
138  	
139  			if (!strncmp(line, "unfence", strlen("unfence"))) {
(6) Event example_checked: Example 3: "strstr(line, dev_name)" has its value checked in "strstr(line, dev_name)".
Also see events: [returned_null][example_assign][example_checked][example_assign][example_checked][example_assign][example_checked][example_checked][var_assigned][dereference]
140  				if (!strstr(line, dev_name))
141  					return -EINVAL;
142  				unfence = 1;
143  				continue;
144  			}
145  	
146  			/* invalid config */
147  			if (strncmp(line, "connect", strlen("connect")))
148  				return -EINVAL;
149  	
150  			/* once we've found the connect line we want, continue
151  			   scanning lines until end of section so we pick up an
152  			   unfence line at the end */
153  	
154  			if (dev)
155  				continue;
156  	
157  			memset(con_name, 0, sizeof(con_name));
158  			memset(con_args, 0, sizeof(con_args));
159  	
160  			sscanf(line, "%s %s %[^\n]s", unused, con_name, con_args);
161  	
162  			/* invalid config */
163  			if (strncmp(dev_name, con_name, FENCE_CONFIG_NAME_MAX))
164  				return -EINVAL;
165  	
166  			/* skip connection for another node */
167  			if (con_args_nodeid(con_args) != nodeid)
168  				continue;
169  	
170  			dev = malloc(sizeof(struct fence_device));
171  			if (!dev)
172  				return -ENOMEM;
173  	
174  			con = malloc(sizeof(struct fence_connect));
175  			if (!con) {
176  				free(dev);
177  				return -ENOMEM;
178  			}
179  	
180  			memset(dev, 0, sizeof(struct fence_device));
181  			memset(con, 0, sizeof(struct fence_connect));
182  	
183  			strncpy(dev->name, dev_name, FENCE_CONFIG_NAME_MAX);
184  			dev->name[FENCE_CONFIG_NAME_MAX - 1] = '\0';
185  	
186  			strncpy(dev->agent, agent, FENCE_CONFIG_NAME_MAX);
187  			dev->agent[FENCE_CONFIG_NAME_MAX - 1] = '\0';
188  	
189  			strncpy(dev->args, dev_args, FENCE_CONFIG_ARGS_MAX);
190  			dev->args[FENCE_CONFIG_ARGS_MAX - 1] = '\0';
191  	
192  			strncpy(con->name, con_name, FENCE_CONFIG_NAME_MAX);
193  			con->name[FENCE_CONFIG_NAME_MAX - 1] = '\0';
194  	
195  			strncpy(con->args, con_args, FENCE_CONFIG_ARGS_MAX);
196  			con->args[FENCE_CONFIG_ARGS_MAX - 1] = '\0';
197  	
198  			dev->unfence = unfence;
199  	
200  			*dev_out = dev;
201  			*con_out = con;
202  		}
203  	
204  		if (dev && unfence)
205  			dev->unfence = 1;
206  	
207  		if (dev)
208  			return 0;
209  		else
210  			return -ENOENT;
211  	}
212  	
213  	void fence_config_free(struct fence_config *fc)
214  	{
215  		struct fence_device *dev;
216  		struct fence_connect *con;
217  		int i;
218  	
219  		for (i = 0; i < FENCE_CONFIG_DEVS_MAX; i++) {
220  			dev = fc->dev[i];
221  			con = fc->con[i];
222  			if (dev)
223  				free(dev);
224  			if (con)
225  				free(con);
226  		}
227  	
228  		memset(fc, 0, sizeof(struct fence_config));
229  	}
230  	
231  	int fence_config_init(struct fence_config *fc, unsigned int nodeid, char *path)
232  	{
233  		char line[MAX_LINE];
234  		struct fence_device *dev;
235  		struct fence_connect *con;
236  		FILE *file;
237  		int pos = 0;
238  		int rv;
239  	
240  		fc->nodeid = nodeid;
241  	
242  		file = fopen(path, "r");
243  		if (!file)
244  			return -ENOENT;
245  	
246  		while (fgets(line, MAX_LINE, file)) {
247  			if (line[0] == '#')
248  				continue;
249  			if (line[0] == '\n')
250  				continue;
251  	
252  			if (!strncmp(line, "fence_all", strlen("fence_all"))) {
253  				/* fence_all cannot be used with other fence devices */
254  				if (pos) {
255  					rv = -EINVAL;
256  					goto out;
257  				}
258  	
259  				dev = malloc(sizeof(struct fence_device));
260  				if (!dev) {
261  					rv = -ENOMEM;
262  					goto out;
263  				}
264  				memset(dev, 0, sizeof(struct fence_device));
265  	
266  				rv = sscanf(line, "%s %s %[^\n]s\n", dev->name, dev->agent, dev->args);
267  				if (rv < 2) {
268  					rv = -EINVAL;
269  					goto out;
270  				}
271  	
272  				if (fgets(line, MAX_LINE, file) &&
273  				    !strncmp(line, "unfence_all", strlen("unfence_all")))
274  					dev->unfence = 1;
275  	
276  				fc->dev[0] = dev;
277  				fc->pos = 0;
278  				rv = 0;
279  				goto out;
280  			}
281  	
282  			if (strncmp(line, "device", strlen("device")))
283  				continue;
284  	
285  			dev = NULL;
286  			con = NULL;
287  	
288  			/* read connect and unfence lines following a device line */
289  			rv = read_config_section(nodeid, file, line, &dev, &con);
290  	
291  			/* nodeid not listed in this section */
292  			if (rv == -ENOENT)
293  				continue;
294  	
295  			/* an error parsing the section, may be config to free */
296  			if (rv < 0) {
297  				if (dev)
298  					free(dev);
299  				if (con)
300  					free(con);
301  				goto out;
302  			}
303  	
304  			fc->dev[pos] = dev;
305  			fc->con[pos] = con;
306  			pos++;
307  		}
308  	
309  		if (!pos)
310  			rv = -ENOENT;
311  		else
312  			rv = 0;
313  	 out:
314  		fclose(file);
315  		return rv;
316  	}
317  	
318  	static int same_base_name(struct fence_device *a,
319  				  struct fence_device *b)
320  	{
321  		int len, i;
322  	
323  		len = strlen(a->name);
324  		if (len > strlen(b->name))
325  			len = strlen(b->name);
326  	
327  		for (i = 0; i < len; i++) {
328  			if (a->name[i] == ':' && b->name[i] == ':')
329  				return 1;
330  			if (a->name[i] == b->name[i])
331  				continue;
332  			return 0;
333  		}
334  		return 0;
335  	}
336  	
337  	/*
338  	 * if next dev is in parallel with last one,
339  	 * set d,c return 0, else -1
340  	 *
341  	 * two consecutive devs with same basename are parallel
342  	 */
343  	
344  	int fence_config_next_parallel(struct fence_config *fc)
345  	{
346  		struct fence_device *prev, *next;
347  		int d = fc->pos;
348  	
349  		if (d >= FENCE_CONFIG_DEVS_MAX)
350  			return -1;
351  	
352  		prev = fc->dev[d];
353  		next = fc->dev[d+1];
354  	
355  		if (!next)
356  			return -1;
357  	
358  		if (same_base_name(prev, next)) {
359  			fc->pos = d+1;
360  			return 0;
361  		}
362  		return -1;
363  	}
364  	
365  	/*
366  	 * if there's a dev with the next priority,
367  	 * set d,c return 0, else -1
368  	 *
369  	 * look for another dev with a non-matching basename
370  	 */
371  	
372  	int fence_config_next_priority(struct fence_config *fc)
373  	{
374  		struct fence_device *prev, *next;
375  		int d = fc->pos;
376  		int i;
377  	
378  		if (d >= FENCE_CONFIG_DEVS_MAX)
379  			return -1;
380  	
381  		prev = fc->dev[d];
382  	
383  		for (i = d+1; i < FENCE_CONFIG_DEVS_MAX; i++) {
384  			next = fc->dev[i];
385  	
386  			if (!next)
387  				return -1;
388  	
389  			if (same_base_name(prev, next))
390  				continue;
391  	
392  			fc->pos = d+1;
393  			return 0;
394  		}
395  		return -1;
396  	}
397  	
398  	int fence_config_agent_args(struct fence_config *fc, char *extra, char *args)
399  	{
400  		struct fence_device *dev;
401  		struct fence_connect *con;
402  		char node[FENCE_CONFIG_NAME_MAX];
403  		char *p;
404  		int n = 0;
405  		int i, len;
406  	
407  		dev = fc->dev[fc->pos];
408  		con = fc->con[fc->pos];
409  	
410  		memset(node, 0, sizeof(node));
411  		snprintf(node, FENCE_CONFIG_NAME_MAX-1, "node=%u\n", fc->nodeid);
412  		len = strlen(node);
413  	
414  		if (dev)
415  			len += strlen(dev->args) + 1; /* +1 for \n */
416  		if (con)
417  			len += strlen(con->args) + 1;
418  		if (extra)
419  			len += strlen(extra) + 1;
420  	
421  		if (len > FENCE_CONFIG_ARGS_MAX - 1)
422  			return -1;
423  	
424  		if (dev && dev->args[0]) {
425  			p = dev->args;
426  	
427  			for (i = 0; i < strlen(dev->args); i++) {
428  				if (*p == ' ')
429  					args[n++] = '\n';
430  				else
431  					args[n++] = *p;
432  				p++;
433  			}
434  			args[n++] = '\n';
435  		}
436  	
437  		if (con && con->args[0]) {
438  			p = con->args;
439  	
440  			for (i = 0; i < strlen(con->args); i++) {
441  				if (*p == ' ')
442  					args[n++] = '\n';
443  				else
444  					args[n++] = *p;
445  				p++;
446  			}
447  			args[n++] = '\n';
448  		}
449  	
450  		if (!strstr(args, "node="))
451  			strcat(args, node);
452  		if (extra)
453  			strcat(args, extra);
454  	
455  		return 0;
456  	}
457  	
458