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, &copy)))
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