1 /*****************************************************************************\
2 * $Id: hostlist.c,v 1.3 2009-12-16 17:49:39 chu11 Exp $
3 *****************************************************************************
4 * Copyright (C) 2007-2015 Lawrence Livermore National Security, LLC.
5 * Copyright (C) 2003-2007 The Regents of the University of California.
6 * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
7 * Written by Albert Chu <chu11@llnl.gov>
8 * UCRL-CODE-155698
9 *
10 * This file is part of Ipmipower, a remote power control utility.
11 * For details, see http://www.llnl.gov/linux/.
12 *
13 * Ipmipower is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by the
15 * Free Software Foundation; either version 3 of the License, or (at your
16 * option) any later version.
17 *
18 * Ipmipower is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 * for more details.
22 *
23 * You should have received a copy of the GNU General Public License along
24 * with Ipmipower. If not, see <http://www.gnu.org/licenses/>.
25 \*****************************************************************************/
26
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif /* HAVE_CONFIG_H */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35 #if STDC_HEADERS
36 #include <string.h>
37 #endif /* STDC_HEADERS */
38 #include <assert.h>
39 #include <errno.h>
40 #include <unistd.h>
41
42 #include "fi_hostlist.h"
43 #include "hostlist.h"
44 #include "network.h"
45
46 #define FI_HOSTLIST_MAGIC 57006
47
48 #define FI_HOSTLIST_SEPERATOR "\t, "
49
50 /* "impossible" legal hostnames to replace brackets with */
51 #define FI_LEFT_BRACKET "@l@"
52 #define FI_RIGHT_BRACKET "@r@"
53
54 struct fi_hostlist {
55 #ifndef NDEBUG
56 int magic;
57 #endif
58 hostlist_t hl;
59 };
60
61 struct fi_hostlist_iterator {
62 #ifndef NDEBUG
63 int magic;
64 #endif
65 hostlist_iterator_t itr;
66 };
67
68 /* achu: _advance_past_brackets and _next_tok ripped from hostlist.c
69 */
70
_advance_past_brackets(char * tok,char ** str)71 static int _advance_past_brackets (char *tok, char **str)
72 {
73 /* if _single_ opening bracket exists b/w tok and str, push str
74 * past first closing bracket to next seperator */
75 if ( memchr(tok, '[', *str - tok) != NULL
76 && memchr(tok, ']', *str - tok) == NULL ) {
77 char *q = strchr(*str, ']');
78 if (q && memchr(*str, '[', q - *str) == NULL) {
79 *str = q + 1;
80 return (1);
81 }
82 }
83
84 return 0;
85 }
86
87 /*
88 * Helper function for host list string parsing routines
89 * Returns a pointer to the next token; additionally advance *str
90 * to the next separator.
91 *
92 * next_tok was taken directly from pdsh courtesy of Jim Garlick.
93 * (with modifications to support bracketed hostlists, i.e.:
94 * xxx[xx,xx,xx] is a single token)
95 *
96 */
_next_tok(char * sep,char ** str)97 static char * _next_tok(char *sep, char **str)
98 {
99 char *tok;
100
101 /* push str past any leading separators */
102 while (**str != '\0' && strchr(sep, **str) != '\0')
103 (*str)++;
104
105 if (**str == '\0')
106 return NULL;
107
108 /* assign token ptr */
109 tok = *str;
110
111 /*
112 * Advance str past any separators, but if a separator occurs between
113 * brackets, e.g. foo[0-3,5], then advance str past closing brackets and
114 * try again.
115 */
116 do {
117 /* push str past token and leave pointing to first separator */
118 while (**str != '\0' && strchr(sep, **str) == '\0')
119 (*str)++;
120 } while (_advance_past_brackets (tok, str));
121
122 /* nullify consecutive separators and push str beyond them */
123 while (**str != '\0' && strchr(sep, **str) != '\0')
124 *(*str)++ = '\0';
125
126 return tok;
127 }
128
129 /* achu: Bulk from
130 *
131 * http://stackoverflow.com/questions/779875/what-is-the-function-to-replace-string-in-c
132 *
133 * as a starting point. Adjusting code style and library needs.
134 */
135 static char *
_str_replace(const char * orig,const char * replace,const char * with)136 _str_replace (const char *orig, const char *replace, const char *with) {
137 int len_replace;
138 int len_with;
139 int count = 0;
140 char *new = NULL;
141 char *tmp, *newp, *p;
142 char *rv = NULL;
143
144 assert (orig);
145 assert (replace);
146 assert (strlen (replace) > 0);
147 assert (with);
148 assert (strlen (with) > 0);
149
150 len_replace = strlen (replace);
151 len_with = strlen (with);
152
153 /* count number of replacements needed */
154 p = (char *)orig;
155 while ((tmp = strstr (p, replace)))
156 {
157 p = tmp + len_replace;
158 count++;
159 }
160
161 /* No substitutions special case */
162 if (!count)
163 {
164 if (!(new = strdup (orig)))
165 goto cleanup;
166 return (new);
167 }
168
169 new = newp = (char *) malloc (strlen(orig) + (len_with - len_replace) * count + 1);
170 if (!new)
171 goto cleanup;
172
173 /* p - points to remainder of orig */
174 /* newp - walks the new string buffer */
175 p = (char *)orig;
176 while (count)
177 {
178 char *ins;
179 int len_front;
180
181 ins = strstr (p, replace);
182 len_front = ins - p;
183 strncpy (newp, p, len_front);
184 newp += len_front;
185 strncpy (newp, with, len_with);
186 newp += len_with;
187 p += len_front + len_replace;
188 count--;
189 }
190 /* copy the rest over */
191 strcpy (newp, p);
192
193 rv = new;
194 cleanup:
195 if (!rv)
196 free (new);
197 return (rv);
198 }
199
200 static char *
_fi_unparse_string(const char * hosts)201 _fi_unparse_string (const char *hosts)
202 {
203 char *str1 = NULL;
204 char *str2 = NULL;
205 char *rv = NULL;
206
207 if (!(str1 = _str_replace (hosts, FI_LEFT_BRACKET, "[")))
208 goto cleanup;
209
210 if (!(str2 = _str_replace (str1, FI_RIGHT_BRACKET, "]")))
211 goto cleanup;
212
213 rv = strdup (str2);
214 cleanup:
215 free (str1);
216 free (str2);
217 return (rv);
218 }
219
220 static char *
_fi_preparse_host(const char * host)221 _fi_preparse_host (const char *host)
222 {
223 char *new = NULL;
224 char *rv = NULL;
225
226 /* Handle special IPv6 address with port format, i.e.
227 * "[Ipv6 address]:port"
228 */
229
230 if (host_is_ipv6_with_port (host, NULL, NULL))
231 {
232 unsigned int len = 0;
233 char *pl;
234 char *pr;
235
236 /* +4 for extra 2 chars on left & right bracket, +1 for NUL char */
237 if (!(new = (char *) malloc (strlen (host) + 5)))
238 goto cleanup;
239
240 pl = strchr (host, '[');
241 memcpy (new, host, pl - host);
242 len += (pl - host);
243
244 memcpy (new + len, FI_LEFT_BRACKET, strlen (FI_LEFT_BRACKET));
245 len += strlen (FI_LEFT_BRACKET);
246
247 /* move past left bracket */
248 pl++;
249
250 pr = strchr (pl, ']');
251 memcpy (new + len, pl, pr - pl);
252 len += (pr - pl);
253
254 memcpy (new + len, FI_RIGHT_BRACKET, strlen (FI_RIGHT_BRACKET));
255 len += strlen (FI_RIGHT_BRACKET);
256
257 pr++;
258 memcpy (new + len, pr, host + strlen (host) - pr);
259 len += (host + strlen (host) - pr);
260
261 new[len] = '\0';
262 }
263 else
264 {
265 if (!(new = strdup (host)))
266 goto cleanup;
267 }
268
269 rv = new;
270 cleanup:
271 if (!rv)
272 free (new);
273 return (rv);
274 }
275
276 typedef int (HostlistFn)(hostlist_t, const char *);
277
_fi_hosts(const char * hosts,fi_hostlist_t fihl,HostlistFn hfn)278 static int _fi_hosts (const char *hosts,
279 fi_hostlist_t fihl,
280 HostlistFn hfn)
281 {
282 char *orig = NULL;
283 char *copy = NULL;
284 char *tok;
285 int rv = -1;
286 int count = 0;
287
288 assert (hosts);
289 assert (fihl);
290 assert (hfn);
291
292 if (!(orig = copy = strdup (hosts)))
293 goto cleanup;
294
295 while ((tok = _next_tok (FI_HOSTLIST_SEPERATOR, ©)))
296 {
297 char *tok_parsed;
298 int ret;
299
300 tok_parsed = _fi_preparse_host (tok);
301
302 ret = hfn (fihl->hl, tok_parsed);
303
304 free (tok_parsed);
305
306 if (!ret)
307 goto cleanup;
308
309 count += ret;
310 }
311
312 rv = count;
313 cleanup:
314 free (orig);
315 return (rv);
316 }
317
318
319 static int
_fi_push_hosts(const char * hosts,fi_hostlist_t fihl)320 _fi_push_hosts (const char *hosts, fi_hostlist_t fihl)
321 {
322 return (_fi_hosts (hosts, fihl, hostlist_push));
323 }
324
325 static int
_fi_delete_hosts(const char * hosts,fi_hostlist_t fihl)326 _fi_delete_hosts (const char *hosts, fi_hostlist_t fihl)
327 {
328 return (_fi_hosts (hosts, fihl, hostlist_delete));
329 }
330
331 static fi_hostlist_t
_fi_hostlist_new(void)332 _fi_hostlist_new (void)
333 {
334 fi_hostlist_t fihl;
335
336 if (!(fihl = (fi_hostlist_t) malloc (sizeof (struct fi_hostlist))))
337 return (NULL);
338
339 #ifndef NDEBUG
340 fihl->magic = FI_HOSTLIST_MAGIC;
341 #endif
342 fihl->hl = NULL;
343 return (fihl);
344 }
345
346 fi_hostlist_t
fi_hostlist_create(const char * hostlist)347 fi_hostlist_create (const char *hostlist)
348 {
349 fi_hostlist_t fihl = NULL;
350 hostlist_t hl = NULL;
351 fi_hostlist_t rv = NULL;
352
353 if (!(hl = hostlist_create (NULL)))
354 goto cleanup;
355
356 if (!(fihl = _fi_hostlist_new ()))
357 goto cleanup;
358 fihl->hl = hl;
359
360 if (hostlist)
361 {
362 if (_fi_push_hosts (hostlist, fihl) < 0)
363 goto cleanup;
364 }
365
366 rv = fihl;
367 cleanup:
368 if (!rv)
369 {
370 if (fihl)
371 fi_hostlist_destroy (fihl);
372 else if (hl)
373 hostlist_destroy (hl);
374 }
375 return (rv);
376 }
377
378 fi_hostlist_t
fi_hostlist_copy(const fi_hostlist_t fihl)379 fi_hostlist_copy(const fi_hostlist_t fihl)
380 {
381 fi_hostlist_t newfihl;
382
383 assert (fihl);
384 assert (fihl->magic == FI_HOSTLIST_MAGIC);
385
386 if (!(newfihl = _fi_hostlist_new()))
387 return (NULL);
388
389 if (!(newfihl->hl = hostlist_copy (fihl->hl)))
390 {
391 fi_hostlist_destroy (newfihl);
392 return (NULL);
393 }
394
395 return (newfihl);
396 }
397
398 void
fi_hostlist_destroy(fi_hostlist_t fihl)399 fi_hostlist_destroy (fi_hostlist_t fihl)
400 {
401 if (fihl)
402 {
403 assert (fihl->magic == FI_HOSTLIST_MAGIC);
404 hostlist_destroy (fihl->hl);
405 free (fihl);
406 }
407 }
408
409 int
fi_hostlist_push(fi_hostlist_t fihl,const char * hosts)410 fi_hostlist_push (fi_hostlist_t fihl, const char *hosts)
411 {
412 int rv;
413
414 assert (fihl);
415 assert (fihl->magic == FI_HOSTLIST_MAGIC);
416
417 if ((rv = _fi_push_hosts (hosts, fihl)) < 0)
418 return (0);
419 return (rv);
420 }
421
422 int
fi_hostlist_push_host(fi_hostlist_t fihl,const char * host)423 fi_hostlist_push_host (fi_hostlist_t fihl, const char *host)
424 {
425 int rv;
426
427 assert (fihl);
428 assert (fihl->magic == FI_HOSTLIST_MAGIC);
429
430 if ((rv = _fi_push_hosts (host, fihl)) < 0)
431 return (0);
432 return (rv);
433 }
434
435 int
fi_hostlist_push_list(fi_hostlist_t fihl1,fi_hostlist_t fihl2)436 fi_hostlist_push_list (fi_hostlist_t fihl1, fi_hostlist_t fihl2)
437 {
438 assert (fihl1);
439 assert (fihl1->magic == FI_HOSTLIST_MAGIC);
440 assert (fihl2);
441 assert (fihl2->magic == FI_HOSTLIST_MAGIC);
442
443 return hostlist_push_list (fihl1->hl, fihl2->hl);
444 }
445
446 int
fi_hostlist_find(fi_hostlist_t fihl,const char * hostname)447 fi_hostlist_find (fi_hostlist_t fihl, const char *hostname)
448 {
449 char *p;
450 int rv;
451
452 assert (fihl);
453 assert (fihl->magic == FI_HOSTLIST_MAGIC);
454
455 p = _fi_preparse_host (hostname);
456
457 rv = hostlist_find (fihl->hl, p);
458
459 free (p);
460
461 return (rv);
462 }
463
464 int
fi_hostlist_delete(fi_hostlist_t fihl,const char * hosts)465 fi_hostlist_delete (fi_hostlist_t fihl, const char *hosts)
466 {
467 int rv;
468
469 assert (fihl);
470 assert (fihl->magic == FI_HOSTLIST_MAGIC);
471
472 if ((rv = _fi_delete_hosts (hosts, fihl)) < 0)
473 return (0);
474 return (rv);
475 }
476
477 int
fi_hostlist_delete_host(fi_hostlist_t fihl,const char * hostname)478 fi_hostlist_delete_host (fi_hostlist_t fihl, const char *hostname)
479 {
480 int rv;
481
482 assert (fihl);
483 assert (fihl->magic == FI_HOSTLIST_MAGIC);
484
485 if ((rv = _fi_delete_hosts (hostname, fihl)) < 0)
486 return (0);
487 return (rv);
488 }
489
490 int
fi_hostlist_count(fi_hostlist_t fihl)491 fi_hostlist_count (fi_hostlist_t fihl)
492 {
493 assert (fihl);
494 assert (fihl->magic == FI_HOSTLIST_MAGIC);
495
496 return hostlist_count (fihl->hl);
497 }
498
499 void
fi_hostlist_sort(fi_hostlist_t fihl)500 fi_hostlist_sort (fi_hostlist_t fihl)
501 {
502 assert (fihl);
503 assert (fihl->magic == FI_HOSTLIST_MAGIC);
504
505 return hostlist_sort (fihl->hl);
506 }
507
508 void
fi_hostlist_uniq(fi_hostlist_t fihl)509 fi_hostlist_uniq (fi_hostlist_t fihl)
510 {
511 assert (fihl);
512 assert (fihl->magic == FI_HOSTLIST_MAGIC);
513
514 return hostlist_uniq (fihl->hl);
515 }
516
517 typedef ssize_t (HostlistStrFn)(hostlist_t, size_t, char *);
518
_fi_hostlist_string(fi_hostlist_t fihl,size_t n,char * buf,HostlistStrFn hfn)519 static int _fi_hostlist_string (fi_hostlist_t fihl,
520 size_t n,
521 char *buf,
522 HostlistStrFn hfn)
523 {
524 char *tmpbuf = NULL;
525 char *str = NULL;
526 ssize_t rv = -1;
527
528 assert (fihl);
529 assert (fihl->magic == FI_HOSTLIST_MAGIC);
530 assert (hfn);
531
532 /* +1 to guarantee NUL byte */
533 if (!(tmpbuf = (char *) malloc (n + 1)))
534 return -1;
535 memset (tmpbuf, '\0', n + 1);
536
537 if (hfn (fihl->hl, n, tmpbuf) < 0)
538 goto cleanup;
539
540 if (!(str = _fi_unparse_string (tmpbuf)))
541 goto cleanup;
542
543 if (strlen (str) > n)
544 goto cleanup;
545
546 strncpy (buf, str, n);
547 rv = strlen (str);
548 cleanup:
549 free (tmpbuf);
550 free (str);
551 return (rv);
552 }
553
554 ssize_t
fi_hostlist_ranged_string(fi_hostlist_t fihl,size_t n,char * buf)555 fi_hostlist_ranged_string (fi_hostlist_t fihl, size_t n, char *buf)
556 {
557 return (_fi_hostlist_string (fihl, n, buf, hostlist_ranged_string));
558 }
559
560 ssize_t
fi_hostlist_deranged_string(fi_hostlist_t fihl,size_t n,char * buf)561 fi_hostlist_deranged_string (fi_hostlist_t fihl, size_t n, char *buf)
562 {
563 return (_fi_hostlist_string (fihl, n, buf, hostlist_deranged_string));
564 }
565
566 static fi_hostlist_iterator_t
_fi_hostlist_iterator_new(void)567 _fi_hostlist_iterator_new (void)
568 {
569 fi_hostlist_iterator_t fiitr;
570
571 if (!(fiitr = (fi_hostlist_iterator_t) malloc (sizeof (struct fi_hostlist_iterator))))
572 return (NULL);
573
574 #ifndef NDEBUG
575 fiitr->magic = FI_HOSTLIST_MAGIC;
576 #endif
577 fiitr->itr = NULL;
578 return (fiitr);
579 }
580
581 fi_hostlist_iterator_t
fi_hostlist_iterator_create(fi_hostlist_t fihl)582 fi_hostlist_iterator_create (fi_hostlist_t fihl)
583 {
584 hostlist_iterator_t itr;
585 fi_hostlist_iterator_t fiitr;
586
587 assert (fihl);
588 assert (fihl->magic == FI_HOSTLIST_MAGIC);
589
590 if (!(itr = hostlist_iterator_create (fihl->hl)))
591 return (NULL);
592
593 if (!(fiitr = _fi_hostlist_iterator_new ()))
594 {
595 hostlist_iterator_destroy (itr);
596 return (NULL);
597 }
598
599 fiitr->itr = itr;
600 return (fiitr);
601 }
602
603 void
fi_hostlist_iterator_destroy(fi_hostlist_iterator_t fiitr)604 fi_hostlist_iterator_destroy (fi_hostlist_iterator_t fiitr)
605 {
606 if (fiitr)
607 {
608 assert (fiitr->magic == FI_HOSTLIST_MAGIC);
609 hostlist_iterator_destroy (fiitr->itr);
610 free (fiitr);
611 }
612 }
613
614 void
fi_hostlist_iterator_reset(fi_hostlist_iterator_t fiitr)615 fi_hostlist_iterator_reset (fi_hostlist_iterator_t fiitr)
616 {
617 assert (fiitr);
618 assert (fiitr->magic == FI_HOSTLIST_MAGIC);
619
620 return hostlist_iterator_reset (fiitr->itr);
621 }
622
623 char *
fi_hostlist_next(fi_hostlist_iterator_t fiitr)624 fi_hostlist_next (fi_hostlist_iterator_t fiitr)
625 {
626 char *str = NULL;
627 char *rv = NULL;
628
629 assert (fiitr);
630 assert (fiitr->magic == FI_HOSTLIST_MAGIC);
631
632 if ((str = hostlist_next (fiitr->itr)))
633 rv = _fi_unparse_string (str);
634
635 free (str);
636 return (rv);
637 }
638
639 int
fi_hostlist_remove(fi_hostlist_iterator_t fiitr)640 fi_hostlist_remove (fi_hostlist_iterator_t fiitr)
641 {
642 assert (fiitr);
643 assert (fiitr->magic == FI_HOSTLIST_MAGIC);
644
645 return hostlist_remove (fiitr->itr);
646 }
647