| /*****************************************************************************\ |
| * $Id$ |
| ***************************************************************************** |
| * $LSDId: hostlist.c,v 1.14 2003/10/14 20:11:54 grondo Exp $ |
| ***************************************************************************** |
| * Copyright (C) 2002 The Regents of the University of California. |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). |
| * Written by Mark Grondona <mgrondona@llnl.gov> |
| * UCRL-CODE-226842. |
| * |
| * This file is part of SLURM, a resource management program. |
| * For details, see <http://www.llnl.gov/linux/slurm/>. |
| * |
| * SLURM is free software; you can redistribute it and/or modify it under |
| * the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * In addition, as a special exception, the copyright holders give permission |
| * to link the code of portions of this program with the OpenSSL library under |
| * certain conditions as described in each individual source file, and |
| * distribute linked combinations including the two. You must obey the GNU |
| * General Public License in all respects for all of the code used other than |
| * OpenSSL. If you modify file(s) with this exception, you may extend this |
| * exception to your version of the file(s), but you are not obligated to do |
| * so. If you do not wish to do so, delete this exception statement from your |
| * version. If you delete this exception statement from all source files in |
| * the program, then also delete it here. |
| * |
| * SLURM is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with SLURM; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| \*****************************************************************************/ |
| |
| #ifdef HAVE_CONFIG_H |
| # include "config.h" |
| # if HAVE_STRING_H |
| # include <string.h> |
| # endif |
| # if HAVE_PTHREAD_H |
| # include <pthread.h> |
| # endif |
| #else /* !HAVE_CONFIG_H */ |
| # include <string.h> |
| # include <pthread.h> |
| #endif /* HAVE_CONFIG_H */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <assert.h> |
| #include <errno.h> |
| #include <ctype.h> |
| #include <sys/param.h> |
| #include <unistd.h> |
| |
| #include "src/common/hostlist.h" |
| #include "src/common/log.h" |
| #include "src/common/macros.h" |
| #include "src/common/xmalloc.h" |
| |
| /* |
| * Define slurm-specific aliases for use by plugins, see slurm_xlator.h |
| * for details. |
| */ |
| strong_alias(hostlist_create, slurm_hostlist_create); |
| strong_alias(hostlist_copy, slurm_hostlist_copy); |
| strong_alias(hostlist_count, slurm_hostlist_count); |
| strong_alias(hostlist_delete, slurm_hostlist_delete); |
| strong_alias(hostlist_delete_host, slurm_hostlist_delete_host); |
| strong_alias(hostlist_delete_nth, slurm_hostlist_delete_nth); |
| strong_alias(hostlist_deranged_string, slurm_hostlist_deranged_string); |
| strong_alias(hostlist_destroy, slurm_hostlist_destroy); |
| strong_alias(hostlist_find, slurm_hostlist_find); |
| strong_alias(hostlist_iterator_create, slurm_hostlist_iterator_create); |
| strong_alias(hostlist_iterator_destroy, slurm_hostlist_iterator_destroy); |
| strong_alias(hostlist_iterator_reset, slurm_hostlist_iterator_reset); |
| strong_alias(hostlist_next, slurm_hostlist_next); |
| strong_alias(hostlist_next_range, slurm_hostlist_next_range); |
| strong_alias(hostlist_nth, slurm_hostlist_nth); |
| strong_alias(hostlist_pop, slurm_hostlist_pop); |
| strong_alias(hostlist_pop_range, slurm_hostlist_pop_range); |
| strong_alias(hostlist_push, slurm_hostlist_push); |
| strong_alias(hostlist_push_host, slurm_hostlist_push_host); |
| strong_alias(hostlist_push_list, slurm_hostlist_push_list); |
| strong_alias(hostlist_ranged_string, slurm_hostlist_ranged_string); |
| strong_alias(hostlist_remove, slurm_hostlist_remove); |
| strong_alias(hostlist_shift, slurm_hostlist_shift); |
| strong_alias(hostlist_shift_range, slurm_hostlist_shift_range); |
| strong_alias(hostlist_sort, slurm_hostlist_soft); |
| strong_alias(hostlist_uniq, slurm_hostlist_uniq); |
| strong_alias(hostset_copy, slurm_hostset_copy); |
| strong_alias(hostset_count, slurm_hostset_count); |
| strong_alias(hostset_create, slurm_hostset_create); |
| strong_alias(hostset_delete, slurm_hostset_delete); |
| strong_alias(hostset_destroy, slurm_hostset_destroy); |
| strong_alias(hostset_find, slurm_hostset_find); |
| strong_alias(hostset_insert, slurm_hostset_insert); |
| strong_alias(hostset_shift, slurm_hostset_shift); |
| strong_alias(hostset_shift_range, slurm_hostset_shift_range); |
| strong_alias(hostset_within, slurm_hostset_within); |
| strong_alias(hostset_nth, slurm_hostset_nth); |
| |
| /* |
| * lsd_fatal_error : fatal error macro |
| */ |
| #ifdef WITH_LSD_FATAL_ERROR_FUNC |
| # undef lsd_fatal_error |
| extern void lsd_fatal_error(char *file, int line, char *mesg); |
| #else /* !WITH_LSD_FATAL_ERROR_FUNC */ |
| # ifndef lsd_fatal_error |
| # define lsd_fatal_error(file, line, mesg) \ |
| do { \ |
| fprintf(stderr, "ERROR: [%s:%d] %s: %s\n", \ |
| file, line, mesg, strerror(errno)); \ |
| } while (0) |
| # endif /* !lsd_fatal_error */ |
| #endif /* !WITH_LSD_FATAL_ERROR_FUNC */ |
| |
| /* |
| * lsd_nonmem_error |
| */ |
| #ifdef WITH_LSD_NOMEM_ERROR_FUNC |
| # undef lsd_nomem_error |
| extern void * lsd_nomem_error(char *file, int line, char *mesg); |
| #else /* !WITH_LSD_NOMEM_ERROR_FUNC */ |
| # ifndef lsd_nomem_error |
| # define lsd_nomem_error(file, line, mesg) (NULL) |
| # endif /* !lsd_nomem_error */ |
| #endif /* !WITH_LSD_NOMEM_ERROR_FUNC */ |
| |
| /* |
| * OOM helper function |
| * Automatically call lsd_nomem_error with appropriate args |
| * and set errno to ENOMEM |
| */ |
| #define out_of_memory(mesg) \ |
| do { \ |
| fatal("malloc failure"); \ |
| errno = ENOMEM; \ |
| return(lsd_nomem_error(__FILE__, __LINE__, mesg)); \ |
| } while (0) |
| |
| /* |
| * Some constants and tunables: |
| */ |
| |
| /* number of elements to allocate when extending the hostlist array */ |
| #define HOSTLIST_CHUNK 16 |
| |
| /* max host range: anything larger will be assumed to be an error */ |
| #define MAX_RANGE 16384 /* 16K Hosts */ |
| |
| /* max number of ranges that will be processed between brackets */ |
| #define MAX_RANGES 12288 /* 12K Ranges */ |
| |
| /* size of internal hostname buffer (+ some slop), hostnames will probably |
| * be truncated if longer than MAXHOSTNAMELEN */ |
| #ifndef MAXHOSTNAMELEN |
| #define MAXHOSTNAMELEN 64 |
| #endif |
| |
| /* ----[ Internal Data Structures ]---- */ |
| |
| |
| char *alpha_num = "0123456789ABCDEFGHIJKLMNOPQRSTUZWXYZ"; |
| |
| #ifdef HAVE_BG |
| /* logic for block node description */ |
| /* We allocate space for three digits, |
| * each with values 0 to Z even if they are not all used */ |
| bool axis[HOSTLIST_BASE][HOSTLIST_BASE][HOSTLIST_BASE]; |
| int axis_min_x, axis_min_y, axis_min_z; |
| int axis_max_x, axis_max_y, axis_max_z; |
| |
| static int _get_boxes(char *buf, int max_len); |
| static void _clear_grid(void); |
| static void _set_grid(unsigned long start, unsigned long end); |
| static bool _test_box(void); |
| #endif |
| |
| /* hostname type: A convenience structure used in parsing single hostnames */ |
| struct hostname_components { |
| char *hostname; /* cache of initialized hostname */ |
| char *prefix; /* hostname prefix */ |
| unsigned long num; /* numeric suffix */ |
| |
| /* string representation of numeric suffix |
| * points into `hostname' */ |
| char *suffix; |
| }; |
| |
| typedef struct hostname_components *hostname_t; |
| |
| /* hostrange type: A single prefix with `hi' and `lo' numeric suffix values */ |
| struct hostrange_components { |
| char *prefix; /* alphanumeric prefix: */ |
| |
| /* beginning (lo) and end (hi) of suffix range */ |
| unsigned long lo, hi; |
| |
| /* width of numeric output format |
| * (pad with zeros up to this width) */ |
| int width; |
| |
| /* If singlehost is 1, `lo' and `hi' are invalid */ |
| unsigned singlehost:1; |
| }; |
| |
| typedef struct hostrange_components *hostrange_t; |
| |
| /* The hostlist type: An array based list of hostrange_t's */ |
| struct hostlist { |
| #ifndef NDEBUG |
| #define HOSTLIST_MAGIC 57005 |
| int magic; |
| #endif |
| #if WITH_PTHREADS |
| pthread_mutex_t mutex; |
| #endif /* WITH_PTHREADS */ |
| |
| /* current number of elements available in array */ |
| int size; |
| |
| /* current number of ranges stored in array */ |
| int nranges; |
| |
| /* current number of hosts stored in hostlist */ |
| int nhosts; |
| |
| /* pointer to hostrange array */ |
| hostrange_t *hr; |
| |
| /* list of iterators */ |
| struct hostlist_iterator *ilist; |
| |
| }; |
| |
| |
| /* a hostset is a wrapper around a hostlist */ |
| struct hostset { |
| hostlist_t hl; |
| }; |
| |
| struct hostlist_iterator { |
| #ifndef NDEBUG |
| int magic; |
| #endif |
| /* hostlist we are traversing */ |
| hostlist_t hl; |
| |
| /* current index of iterator in hl->hr[] */ |
| int idx; |
| |
| /* current hostrange object in list hl, i.e. hl->hr[idx] */ |
| hostrange_t hr; |
| |
| /* current depth we've traversed into range hr */ |
| int depth; |
| |
| /* next ptr for lists of iterators */ |
| struct hostlist_iterator *next; |
| }; |
| |
| |
| /* ---- ---- */ |
| |
| /* ------[ static function prototypes ]------ */ |
| |
| static void _error(char *file, int line, char *mesg, ...); |
| static char * _next_tok(char *, char **); |
| static int _zero_padded(unsigned long, int); |
| static int _width_equiv(unsigned long, int *, unsigned long, int *); |
| |
| static int host_prefix_end(const char *); |
| static hostname_t hostname_create(const char *); |
| static void hostname_destroy(hostname_t); |
| static int hostname_suffix_is_valid(hostname_t); |
| static int hostname_suffix_width(hostname_t); |
| |
| static hostrange_t hostrange_new(void); |
| static hostrange_t hostrange_create_single(const char *); |
| static hostrange_t hostrange_create(char *, unsigned long, unsigned long, |
| int); |
| static unsigned long hostrange_count(hostrange_t); |
| static hostrange_t hostrange_copy(hostrange_t); |
| static void hostrange_destroy(hostrange_t); |
| static hostrange_t hostrange_delete_host(hostrange_t, unsigned long); |
| static int hostrange_cmp(hostrange_t, hostrange_t); |
| static int hostrange_prefix_cmp(hostrange_t, hostrange_t); |
| static int hostrange_within_range(hostrange_t, hostrange_t); |
| static int hostrange_width_combine(hostrange_t, hostrange_t); |
| static int hostrange_empty(hostrange_t); |
| static char * hostrange_pop(hostrange_t); |
| static char * hostrange_shift(hostrange_t); |
| static int hostrange_join(hostrange_t, hostrange_t); |
| static hostrange_t hostrange_intersect(hostrange_t, hostrange_t); |
| static int hostrange_hn_within(hostrange_t, hostname_t); |
| static size_t hostrange_to_string(hostrange_t hr, size_t, char *, |
| char *); |
| static size_t hostrange_numstr(hostrange_t, size_t, char *); |
| |
| static hostlist_t hostlist_new(void); |
| static hostlist_t _hostlist_create_bracketed(const char *, char *, char *); |
| static int hostlist_resize(hostlist_t, size_t); |
| static int hostlist_expand(hostlist_t); |
| static int hostlist_push_range(hostlist_t, hostrange_t); |
| static int hostlist_push_hr(hostlist_t, char *, unsigned long, |
| unsigned long, int); |
| static int hostlist_insert_range(hostlist_t, hostrange_t, int); |
| static void hostlist_delete_range(hostlist_t, int n); |
| static void hostlist_coalesce(hostlist_t hl); |
| static void hostlist_collapse(hostlist_t hl); |
| static hostlist_t _hostlist_create(const char *, char *, char *); |
| static void hostlist_shift_iterators(hostlist_t, int, int, int); |
| static int _attempt_range_join(hostlist_t, int); |
| static int _is_bracket_needed(hostlist_t, int); |
| |
| static hostlist_iterator_t hostlist_iterator_new(void); |
| static void _iterator_advance(hostlist_iterator_t); |
| static void _iterator_advance_range(hostlist_iterator_t); |
| |
| static int hostset_find_host(hostset_t, const char *); |
| |
| /* ------[ macros ]------ */ |
| |
| #ifdef WITH_PTHREADS |
| # define mutex_init(mutex) \ |
| do { \ |
| int e = pthread_mutex_init(mutex, NULL); \ |
| if (e) { \ |
| errno = e; \ |
| lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex init:"); \ |
| abort(); \ |
| } \ |
| } while (0) |
| |
| # define mutex_lock(mutex) \ |
| do { \ |
| int e = pthread_mutex_lock(mutex); \ |
| if (e) { \ |
| errno = e; \ |
| lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex lock:"); \ |
| abort(); \ |
| } \ |
| } while (0) |
| |
| # define mutex_unlock(mutex) \ |
| do { \ |
| int e = pthread_mutex_unlock(mutex); \ |
| if (e) { \ |
| errno = e; \ |
| lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex unlock:"); \ |
| abort(); \ |
| } \ |
| } while (0) |
| |
| # define mutex_destroy(mutex) \ |
| do { \ |
| int e = pthread_mutex_destroy(mutex); \ |
| if (e) { \ |
| errno = e; \ |
| lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex destroy:"); \ |
| abort(); \ |
| } \ |
| } while (0) |
| |
| #else /* !WITH_PTHREADS */ |
| |
| # define mutex_init(mutex) |
| # define mutex_lock(mutex) |
| # define mutex_unlock(mutex) |
| # define mutex_destroy(mutex) |
| |
| #endif /* WITH_PTHREADS */ |
| |
| #define LOCK_HOSTLIST(_hl) \ |
| do { \ |
| assert(_hl != NULL); \ |
| mutex_lock(&(_hl)->mutex); \ |
| assert((_hl)->magic == HOSTLIST_MAGIC); \ |
| } while (0) |
| |
| #define UNLOCK_HOSTLIST(_hl) \ |
| do { \ |
| mutex_unlock(&(_hl)->mutex); \ |
| } while (0) |
| |
| #define seterrno_ret(_errno, _rc) \ |
| do { \ |
| errno = _errno; \ |
| return _rc; \ |
| } while (0) |
| |
| /* ------[ Function Definitions ]------ */ |
| |
| /* ----[ general utility functions ]---- */ |
| |
| |
| /* |
| * Varargs capable error reporting via lsd_fatal_error() |
| */ |
| static void _error(char *file, int line, char *msg, ...) |
| { |
| va_list ap; |
| char buf[1024]; |
| int len = 0; |
| va_start(ap, msg); |
| |
| len = vsnprintf(buf, 1024, msg, ap); |
| if ((len < 0) || (len > 1024)) |
| buf[1023] = '\0'; |
| |
| lsd_fatal_error(file, line, buf); |
| |
| va_end(ap); |
| return; |
| } |
| |
| /* |
| * Helper function for host list string parsing routines |
| * Returns a pointer to the next token; additionally advance *str |
| * to the next separator. |
| * |
| * next_tok was taken directly from pdsh courtesy of Jim Garlick. |
| * (with modifications to support bracketed hostlists, i.e.: |
| * xxx[xx,xx,xx] is a single token) |
| * |
| */ |
| static char * _next_tok(char *sep, char **str) |
| { |
| char *tok; |
| |
| /* push str past any leading separators */ |
| while (**str != '\0' && strchr(sep, **str) != '\0') |
| (*str)++; |
| |
| if (**str == '\0') |
| return NULL; |
| |
| /* assign token ptr */ |
| tok = *str; |
| |
| /* push str past token and leave pointing to first separator */ |
| while (**str != '\0' && strchr(sep, **str) == '\0') |
| (*str)++; |
| |
| /* if _single_ opening bracket exists b/w tok and str, push str |
| * past first closing bracket */ |
| if ( memchr(tok, '[', *str - tok) != NULL |
| && memchr(tok, ']', *str - tok) == NULL ) { |
| char *q = strchr(*str, ']'); |
| if (q && memchr(*str, '[', q - *str) == NULL) |
| *str = q + 1; |
| } |
| |
| /* nullify consecutive separators and push str beyond them */ |
| while (**str != '\0' && strchr(sep, **str) != '\0') |
| *(*str)++ = '\0'; |
| |
| return tok; |
| } |
| |
| |
| /* return the number of zeros needed to pad "num" to "width" |
| */ |
| static int _zero_padded(unsigned long num, int width) |
| { |
| int n = 1; |
| while (num /= 10L) |
| n++; |
| return width > n ? width - n : 0; |
| } |
| |
| /* test whether two format `width' parameters are "equivalent" |
| * The width arguments "wn" and "wm" for integers "n" and "m" |
| * are equivalent if: |
| * |
| * o wn == wm OR |
| * |
| * o applying the same format width (either wn or wm) to both of |
| * 'n' and 'm' will not change the zero padding of *either* 'm' nor 'n'. |
| * |
| * If this function returns 1 (or true), the appropriate width value |
| * (either 'wm' or 'wn') will have been adjusted such that both format |
| * widths are equivalent. |
| */ |
| static int _width_equiv(unsigned long n, int *wn, unsigned long m, int *wm) |
| { |
| int npad, nmpad, mpad, mnpad; |
| |
| if (wn == wm) |
| return 1; |
| |
| npad = _zero_padded(n, *wn); |
| nmpad = _zero_padded(n, *wm); |
| mpad = _zero_padded(m, *wm); |
| mnpad = _zero_padded(m, *wn); |
| |
| if (npad != nmpad && mpad != mnpad) |
| return 0; |
| |
| if (npad != nmpad) { |
| if (mpad == mnpad) { |
| *wm = *wn; |
| return 1; |
| } else |
| return 0; |
| } else { /* mpad != mnpad */ |
| if (npad == nmpad) { |
| *wn = *wm; |
| return 1; |
| } else |
| return 0; |
| } |
| |
| /* not reached */ |
| } |
| |
| |
| /* ----[ hostname_t functions ]---- */ |
| |
| /* |
| * return the location of the last char in the hostname prefix |
| */ |
| static int host_prefix_end(const char *hostname) |
| { |
| int idx, len; |
| |
| assert(hostname != NULL); |
| |
| len = strlen(hostname); |
| #ifdef HAVE_BG |
| if (len < 4) |
| return -1; |
| idx = len - 4; |
| #else |
| if (len < 1) |
| return -1; |
| idx = len - 1; |
| |
| while (idx >= 0 && isdigit((char) hostname[idx])) |
| idx--; |
| #endif |
| return idx; |
| } |
| |
| /* |
| * create a hostname_t object from a string hostname |
| */ |
| static hostname_t hostname_create(const char *hostname) |
| { |
| hostname_t hn = NULL; |
| char *p = '\0'; |
| int idx = 0; |
| |
| assert(hostname != NULL); |
| |
| if (!(hn = (hostname_t) malloc(sizeof(*hn)))) |
| out_of_memory("hostname create"); |
| |
| idx = host_prefix_end(hostname); |
| |
| if (!(hn->hostname = strdup(hostname))) { |
| free(hn); |
| out_of_memory("hostname create"); |
| } |
| |
| hn->num = 0; |
| hn->prefix = NULL; |
| hn->suffix = NULL; |
| |
| if (idx == (strlen(hostname) - 1)) { |
| if ((hn->prefix = strdup(hostname)) == NULL) { |
| hostname_destroy(hn); |
| out_of_memory("hostname prefix create"); |
| } |
| return hn; |
| } |
| |
| hn->suffix = hn->hostname + idx + 1; |
| |
| hn->num = strtoul(hn->suffix, &p, HOSTLIST_BASE); |
| |
| if (*p == '\0') { |
| if (!(hn->prefix = malloc((idx + 2) * sizeof(char)))) { |
| hostname_destroy(hn); |
| out_of_memory("hostname prefix create"); |
| } |
| memcpy(hn->prefix, hostname, idx + 1); |
| hn->prefix[idx + 1] = '\0'; |
| } else { |
| if (!(hn->prefix = strdup(hostname))) { |
| hostname_destroy(hn); |
| out_of_memory("hostname prefix create"); |
| } |
| hn->suffix = NULL; |
| } |
| |
| return hn; |
| } |
| |
| /* free a hostname object |
| */ |
| static void hostname_destroy(hostname_t hn) |
| { |
| if (hn == NULL) |
| return; |
| hn->suffix = NULL; |
| if (hn->hostname) |
| free(hn->hostname); |
| if (hn->prefix) |
| free(hn->prefix); |
| free(hn); |
| } |
| |
| /* return true if the hostname has a valid numeric suffix |
| */ |
| static int hostname_suffix_is_valid(hostname_t hn) |
| { |
| if (!hn) |
| return false; |
| return hn->suffix != NULL; |
| } |
| |
| /* return the width (in characters) of the numeric part of the hostname |
| */ |
| static int hostname_suffix_width(hostname_t hn) |
| { |
| if (!hn) |
| return -1; |
| assert(hn->suffix != NULL); |
| return (int) strlen(hn->suffix); |
| } |
| |
| |
| /* ----[ hostrange_t functions ]---- */ |
| |
| /* allocate a new hostrange object |
| */ |
| static hostrange_t hostrange_new(void) |
| { |
| hostrange_t new = (hostrange_t) malloc(sizeof(*new)); |
| if (!new) |
| out_of_memory("hostrange create"); |
| return new; |
| } |
| |
| /* Create a hostrange_t containing a single host without a valid suffix |
| * hr->prefix will represent the entire hostname. |
| */ |
| static hostrange_t hostrange_create_single(const char *prefix) |
| { |
| hostrange_t new; |
| |
| assert(prefix != NULL); |
| |
| if ((new = hostrange_new()) == NULL) |
| goto error1; |
| |
| if ((new->prefix = strdup(prefix)) == NULL) |
| goto error2; |
| |
| new->singlehost = 1; |
| new->lo = 0L; |
| new->hi = 0L; |
| new->width = 0; |
| |
| return new; |
| |
| error2: |
| free(new); |
| error1: |
| out_of_memory("hostrange create single"); |
| } |
| |
| |
| /* Create a hostrange object with a prefix, hi, lo, and format width |
| */ |
| static hostrange_t |
| hostrange_create(char *prefix, unsigned long lo, unsigned long hi, int width) |
| { |
| hostrange_t new; |
| |
| assert(prefix != NULL); |
| |
| if ((new = hostrange_new()) == NULL) |
| goto error1; |
| |
| if ((new->prefix = strdup(prefix)) == NULL) |
| goto error2; |
| |
| new->lo = lo; |
| new->hi = hi; |
| new->width = width; |
| |
| new->singlehost = 0; |
| |
| return new; |
| |
| error2: |
| free(new); |
| error1: |
| out_of_memory("hostrange create"); |
| } |
| |
| |
| /* Return the number of hosts stored in the hostrange object |
| */ |
| static unsigned long hostrange_count(hostrange_t hr) |
| { |
| assert(hr != NULL); |
| if (hr->singlehost) |
| return 1; |
| else |
| return hr->hi - hr->lo + 1; |
| } |
| |
| /* Copy a hostrange object |
| */ |
| static hostrange_t hostrange_copy(hostrange_t hr) |
| { |
| assert(hr != NULL); |
| |
| if (hr->singlehost) |
| return hostrange_create_single(hr->prefix); |
| else |
| return hostrange_create(hr->prefix, hr->lo, hr->hi, |
| hr->width); |
| } |
| |
| |
| /* free memory allocated by the hostrange object |
| */ |
| static void hostrange_destroy(hostrange_t hr) |
| { |
| if (hr == NULL) |
| return; |
| if (hr->prefix) |
| free(hr->prefix); |
| free(hr); |
| } |
| |
| /* hostrange_delete_host() deletes a specific host from the range. |
| * If the range is split into two, the greater range is returned, |
| * and `hi' of the lesser range is adjusted accordingly. If the |
| * highest or lowest host is deleted from a range, NULL is returned |
| * and the hostrange hr is adjusted properly. |
| */ |
| static hostrange_t hostrange_delete_host(hostrange_t hr, unsigned long n) |
| { |
| hostrange_t new = NULL; |
| |
| assert(hr != NULL); |
| assert(n >= hr->lo && n <= hr->hi); |
| |
| if (n == hr->lo) |
| hr->lo++; |
| else if (n == hr->hi) |
| hr->hi--; |
| else { |
| if (!(new = hostrange_copy(hr))) |
| out_of_memory("hostrange copy"); |
| hr->hi = n - 1; |
| new->lo = n + 1; |
| } |
| |
| return new; |
| } |
| |
| /* hostrange_cmp() is used to sort hostrange objects. It will |
| * sort based on the following (in order): |
| * o result of strcmp on prefixes |
| * o if widths are compatible, then: |
| * sort based on lowest suffix in range |
| * else |
| * sort based on width */ |
| static int hostrange_cmp(hostrange_t h1, hostrange_t h2) |
| { |
| int retval; |
| |
| assert(h1 != NULL); |
| assert(h2 != NULL); |
| |
| if ((retval = hostrange_prefix_cmp(h1, h2)) == 0) |
| retval = hostrange_width_combine(h1, h2) ? |
| h1->lo - h2->lo : h1->width - h2->width; |
| |
| return retval; |
| } |
| |
| |
| /* compare the prefixes of two hostrange objects. |
| * returns: |
| * < 0 if h1 prefix is less than h2 OR h2 == NULL. |
| * |
| * 0 if h1's prefix and h2's prefix match, |
| * UNLESS, either h1 or h2 (NOT both) do not have a valid suffix. |
| * |
| * > 0 if h1's prefix is greater than h2's OR h1 == NULL. */ |
| static int hostrange_prefix_cmp(hostrange_t h1, hostrange_t h2) |
| { |
| int retval; |
| if (h1 == NULL) |
| return 1; |
| if (h2 == NULL) |
| return -1; |
| |
| retval = strcmp(h1->prefix, h2->prefix); |
| return retval == 0 ? h2->singlehost - h1->singlehost : retval; |
| } |
| |
| /* returns true if h1 and h2 would be included in the same bracketed hostlist. |
| * h1 and h2 will be in the same bracketed list iff: |
| * |
| * 1. h1 and h2 have same prefix |
| * 2. neither h1 nor h2 are singlet hosts (i.e. invalid suffix) |
| * |
| * (XXX: Should incompatible widths be placed in the same bracketed list? |
| * There's no good reason not to, except maybe aesthetics) |
| */ |
| static int hostrange_within_range(hostrange_t h1, hostrange_t h2) |
| { |
| if (hostrange_prefix_cmp(h1, h2) == 0) |
| return h1->singlehost || h2->singlehost ? 0 : 1; |
| else |
| return 0; |
| } |
| |
| |
| /* compare two hostrange objects to determine if they are width |
| * compatible, returns: |
| * 1 if widths can safely be combined |
| * 0 if widths cannot be safely combined |
| */ |
| static int hostrange_width_combine(hostrange_t h0, hostrange_t h1) |
| { |
| assert(h0 != NULL); |
| assert(h1 != NULL); |
| |
| return _width_equiv(h0->lo, &h0->width, h1->lo, &h1->width); |
| } |
| |
| |
| /* Return true if hostrange hr contains no hosts, i.e. hi < lo |
| */ |
| static int hostrange_empty(hostrange_t hr) |
| { |
| assert(hr != NULL); |
| return ((hr->hi < hr->lo) || (hr->hi == (unsigned long) -1)); |
| } |
| |
| /* return the string representation of the last host in hostrange hr |
| * and remove that host from the range (i.e. decrement hi if possible) |
| * |
| * Returns NULL if malloc fails OR there are no more hosts left |
| */ |
| static char *hostrange_pop(hostrange_t hr) |
| { |
| size_t size = 0; |
| char *host = NULL; |
| assert(hr != NULL); |
| |
| if (hr->singlehost) { |
| hr->lo++; /* effectively set count == 0 */ |
| host = strdup(hr->prefix); |
| } else if (hostrange_count(hr) > 0) { |
| size = strlen(hr->prefix) + hr->width + 16; |
| if (!(host = (char *) malloc(size * sizeof(char)))) |
| out_of_memory("hostrange pop"); |
| #ifdef HAVE_BG |
| if (hr->width == 3) { |
| int coord[3]; |
| coord[0] = hr->hi / (HOSTLIST_BASE * HOSTLIST_BASE); |
| coord[1] = (hr->hi % (HOSTLIST_BASE * HOSTLIST_BASE)) |
| / HOSTLIST_BASE; |
| coord[2] = (hr->hi % HOSTLIST_BASE); |
| |
| snprintf(host, size, "%s%c%c%c", hr->prefix, |
| alpha_num[coord[0]], alpha_num[coord[1]], |
| alpha_num[coord[2]]); |
| hr->hi--; |
| } else { |
| snprintf(host, size, "%s%0*lu", hr->prefix, |
| hr->width, hr->hi--); |
| } |
| #else |
| snprintf(host, size, "%s%0*lu", hr->prefix, |
| hr->width, hr->hi--); |
| #endif |
| } |
| |
| return host; |
| } |
| |
| /* Same as hostrange_pop(), but remove host from start of range */ |
| static char *hostrange_shift(hostrange_t hr) |
| { |
| size_t size = 0; |
| char *host = NULL; |
| |
| assert(hr != NULL); |
| |
| if (hr->singlehost) { |
| hr->lo++; |
| if (!(host = strdup(hr->prefix))) |
| out_of_memory("hostrange shift"); |
| } else if (hostrange_count(hr) > 0) { |
| size = strlen(hr->prefix) + hr->width + 16; |
| if (!(host = (char *) malloc(size * sizeof(char)))) |
| out_of_memory("hostrange shift"); |
| #ifdef HAVE_BG |
| if (hr->width == 3) { |
| int coord[3]; |
| coord[0] = hr->lo / (HOSTLIST_BASE * HOSTLIST_BASE); |
| coord[1] = (hr->lo % (HOSTLIST_BASE * HOSTLIST_BASE)) |
| / HOSTLIST_BASE; |
| coord[2] = (hr->lo % HOSTLIST_BASE); |
| snprintf(host, size, "%s%c%c%c", hr->prefix, |
| alpha_num[coord[0]], alpha_num[coord[1]], |
| alpha_num[coord[2]]); |
| hr->lo++; |
| } else { |
| snprintf(host, size, "%s%0*lu", hr->prefix, |
| hr->width, hr->lo++); |
| } |
| #else |
| snprintf(host, size, "%s%0*lu", hr->prefix, |
| hr->width, hr->lo++); |
| #endif |
| } |
| |
| return host; |
| } |
| |
| |
| /* join two hostrange objects. |
| * |
| * returns: |
| * |
| * -1 if ranges do not overlap (including incompatible zero padding) |
| * 0 if ranges join perfectly |
| * >0 number of hosts that were duplicated in h1 and h2 |
| * |
| * h2 will be coalesced into h1 if rc >= 0 |
| * |
| * it is assumed that h1->lo <= h2->lo, i.e. hr1 <= hr2 |
| * |
| */ |
| static int hostrange_join(hostrange_t h1, hostrange_t h2) |
| { |
| int duplicated = -1; |
| |
| assert(h1 != NULL); |
| assert(h2 != NULL); |
| assert(hostrange_cmp(h1, h2) <= 0); |
| |
| if (hostrange_prefix_cmp(h1, h2) == 0 && |
| hostrange_width_combine(h1, h2)) { |
| |
| if (h1->singlehost && h2->singlehost) { /* matching singlets */ |
| duplicated = 1; |
| } else if (h1->hi == h2->lo - 1) { /* perfect join */ |
| h1->hi = h2->hi; |
| duplicated = 0; |
| } else if (h1->hi >= h2->lo) { /* some duplication */ |
| if (h1->hi < h2->hi) { |
| duplicated = h1->hi - h2->lo + 1; |
| h1->hi = h2->hi; |
| } else |
| duplicated = hostrange_count(h2); |
| } |
| } |
| |
| return duplicated; |
| } |
| |
| /* hostrange intersect returns the intersection (common hosts) |
| * of hostrange objects h1 and h2. If there is no intersection, |
| * NULL is returned. |
| * |
| * It is assumed that h1 <= h2 (i.e. h1->lo <= h2->lo) |
| */ |
| static hostrange_t hostrange_intersect(hostrange_t h1, hostrange_t h2) |
| { |
| hostrange_t new = NULL; |
| |
| assert(h1 != NULL); |
| assert(h2 != NULL); |
| |
| if (h1->singlehost || h2->singlehost) |
| return NULL; |
| |
| assert(hostrange_cmp(h1, h2) <= 0); |
| |
| if ((hostrange_prefix_cmp(h1, h2) == 0) |
| && (h1->hi > h2->lo) |
| && (hostrange_width_combine(h1, h2))) { |
| |
| if (!(new = hostrange_copy(h1))) |
| return NULL; |
| new->lo = h2->lo; |
| new->hi = h2->hi < h1->hi ? h2->hi : h1->hi; |
| } |
| |
| return new; |
| } |
| |
| /* return 1 if hostname hn is within the hostrange hr |
| * 0 if not. |
| */ |
| static int hostrange_hn_within(hostrange_t hr, hostname_t hn) |
| { |
| int retval = 0; |
| |
| if (strcmp(hr->prefix, hn->prefix) == 0) { |
| if (!hostname_suffix_is_valid(hn)) { |
| if (hr->singlehost) |
| retval = 1; |
| } else if (hn->num <= hr->hi && hn->num >= hr->lo) { |
| int width = hostname_suffix_width(hn); |
| int num = hn->num; |
| retval = _width_equiv(hr->lo, &hr->width, num, &width); |
| } |
| } |
| return retval; |
| } |
| |
| |
| /* copy a string representation of the hostrange hr into buffer buf, |
| * writing at most n chars including NUL termination |
| */ |
| static size_t |
| hostrange_to_string(hostrange_t hr, size_t n, char *buf, char *separator) |
| { |
| unsigned long i; |
| int truncated = 0; |
| int len = 0; |
| char sep = separator == NULL ? ',' : separator[0]; |
| |
| if (n == 0) |
| return 0; |
| |
| assert(hr != NULL); |
| |
| if (hr->singlehost) |
| return snprintf(buf, n, "%s", hr->prefix); |
| |
| for (i = hr->lo; i <= hr->hi; i++) { |
| size_t m = (n - len) <= n ? n - len : 0; /* check for < 0 */ |
| int ret = 0; |
| #ifdef HAVE_BG |
| if (hr->width == 3) { |
| int coord[3]; |
| coord[0] = i / (HOSTLIST_BASE * HOSTLIST_BASE); |
| coord[1] = (i % (HOSTLIST_BASE * HOSTLIST_BASE)) |
| / HOSTLIST_BASE; |
| coord[2] = (i % HOSTLIST_BASE); |
| ret = snprintf(buf + len, m, "%s%c%c%c", hr->prefix, |
| alpha_num[coord[0]], alpha_num[coord[1]], |
| alpha_num[coord[2]]); |
| } else { |
| ret = snprintf(buf + len, m, "%s%0*lu", |
| hr->prefix, hr->width, i); |
| } |
| #else |
| ret = snprintf(buf + len, m, "%s%0*lu", |
| hr->prefix, hr->width, i); |
| #endif |
| if (ret < 0 || ret >= m) { |
| len = n; |
| truncated = 1; |
| break; |
| } |
| len+=ret; |
| buf[len++] = sep; |
| } |
| |
| if (truncated) { |
| buf[n-1] = '\0'; |
| return -1; |
| } else { |
| /* back up over final separator */ |
| buf[--len] = '\0'; |
| return len; |
| } |
| } |
| |
| /* Place the string representation of the numeric part of hostrange into buf |
| * writing at most n chars including NUL termination. |
| */ |
| static size_t hostrange_numstr(hostrange_t hr, size_t n, char *buf) |
| { |
| int len = 0; |
| assert(buf != NULL); |
| assert(hr != NULL); |
| |
| if (hr->singlehost || n == 0) |
| return 0; |
| |
| #ifdef HAVE_BG |
| if (hr->width == 3) { |
| int coord[3]; |
| coord[0] = hr->lo / (HOSTLIST_BASE * HOSTLIST_BASE); |
| coord[1] = (hr->lo % (HOSTLIST_BASE * HOSTLIST_BASE)) / HOSTLIST_BASE; |
| coord[2] = (hr->lo % HOSTLIST_BASE); |
| len = snprintf(buf, n, "%c%c%c", |
| alpha_num[coord[0]], alpha_num[coord[1]], |
| alpha_num[coord[2]]); |
| } else { |
| len = snprintf(buf, n, "%0*lu", hr->width, hr->lo); |
| } |
| #else |
| len = snprintf(buf, n, "%0*lu", hr->width, hr->lo); |
| #endif |
| |
| if ((len >= 0) && (len < n) && (hr->lo < hr->hi)) { |
| int len2 = 0; |
| #ifdef HAVE_BG |
| if (hr->width == 3) { |
| int coord[3]; |
| coord[0] = hr->hi / (HOSTLIST_BASE * HOSTLIST_BASE); |
| coord[1] = (hr->hi % (HOSTLIST_BASE * HOSTLIST_BASE)) |
| / HOSTLIST_BASE; |
| coord[2] = (hr->hi % HOSTLIST_BASE); |
| len2 = snprintf(buf+len, n-len, "-%c%c%c", |
| alpha_num[coord[0]], alpha_num[coord[1]], |
| alpha_num[coord[2]]); |
| } else { |
| len2 = snprintf(buf+len, n-len, "-%0*lu", |
| hr->width, hr->hi); |
| } |
| #else |
| len2 = snprintf(buf+len, n-len, "-%0*lu", hr->width, hr->hi); |
| #endif |
| if (len2 < 0) |
| len = -1; |
| else |
| len += len2; |
| } |
| |
| return len; |
| } |
| |
| |
| /* ----[ hostlist functions ]---- */ |
| |
| /* Create a new hostlist object. |
| * Returns an empty hostlist, or NULL if memory allocation fails. |
| */ |
| static hostlist_t hostlist_new(void) |
| { |
| int i; |
| hostlist_t new = (hostlist_t) malloc(sizeof(*new)); |
| if (!new) |
| goto fail1; |
| |
| assert(new->magic = HOSTLIST_MAGIC); |
| mutex_init(&new->mutex); |
| |
| new->hr = (hostrange_t *) malloc(HOSTLIST_CHUNK * sizeof(hostrange_t)); |
| if (!new->hr) |
| goto fail2; |
| |
| /* set entries in hostrange array to NULL */ |
| for (i = 0; i < HOSTLIST_CHUNK; i++) |
| new->hr[i] = NULL; |
| |
| new->size = HOSTLIST_CHUNK; |
| new->nranges = 0; |
| new->nhosts = 0; |
| new->ilist = NULL; |
| return new; |
| |
| fail2: |
| free(new); |
| fail1: |
| out_of_memory("hostlist_create"); |
| } |
| |
| |
| /* Resize the internal array used to store the list of hostrange objects. |
| * |
| * returns 1 for a successful resize, |
| * 0 if call to _realloc fails |
| * |
| * It is assumed that the caller has the hostlist hl locked |
| */ |
| static int hostlist_resize(hostlist_t hl, size_t newsize) |
| { |
| int i; |
| size_t oldsize; |
| assert(hl != NULL); |
| assert(hl->magic == HOSTLIST_MAGIC); |
| oldsize = hl->size; |
| hl->size = newsize; |
| hl->hr = realloc((void *) hl->hr, hl->size*sizeof(hostrange_t)); |
| if (!(hl->hr)) |
| return 0; |
| |
| for (i = oldsize; i < newsize; i++) |
| hl->hr[i] = NULL; |
| |
| return 1; |
| } |
| |
| /* Resize hostlist by one HOSTLIST_CHUNK |
| * Assumes that hostlist hl is locked by caller |
| */ |
| static int hostlist_expand(hostlist_t hl) |
| { |
| if (!hostlist_resize(hl, hl->size + HOSTLIST_CHUNK)) |
| return 0; |
| else |
| return 1; |
| } |
| |
| /* Push a hostrange object onto hostlist hl |
| * Returns the number of hosts successfully pushed onto hl |
| * or -1 if there was an error allocating memory |
| */ |
| static int hostlist_push_range(hostlist_t hl, hostrange_t hr) |
| { |
| hostrange_t tail; |
| int retval; |
| |
| assert(hr != NULL); |
| LOCK_HOSTLIST(hl); |
| |
| tail = (hl->nranges > 0) ? hl->hr[hl->nranges-1] : hl->hr[0]; |
| |
| if (hl->size == hl->nranges && !hostlist_expand(hl)) |
| goto error; |
| |
| if (hl->nranges > 0 |
| && hostrange_prefix_cmp(tail, hr) == 0 |
| && tail->hi == hr->lo - 1 |
| && hostrange_width_combine(tail, hr)) { |
| tail->hi = hr->hi; |
| } else { |
| hostrange_t new = hostrange_copy(hr); |
| if (new == NULL) |
| goto error; |
| hl->hr[hl->nranges++] = new; |
| } |
| |
| retval = hl->nhosts += hostrange_count(hr); |
| |
| UNLOCK_HOSTLIST(hl); |
| |
| return retval; |
| |
| error: |
| UNLOCK_HOSTLIST(hl); |
| return -1; |
| } |
| |
| |
| |
| /* Same as hostlist_push_range() above, but prefix, lo, hi, and width |
| * are passed as args |
| */ |
| static int |
| hostlist_push_hr(hostlist_t hl, char *prefix, unsigned long lo, |
| unsigned long hi, int width) |
| { |
| hostrange_t hr = hostrange_create(prefix, lo, hi, width); |
| int retval = hostlist_push_range(hl, hr); |
| hostrange_destroy(hr); |
| return retval; |
| } |
| |
| /* Insert a range object hr into position n of the hostlist hl |
| * Assumes that hl->mutex is already held by calling process |
| */ |
| static int hostlist_insert_range(hostlist_t hl, hostrange_t hr, int n) |
| { |
| int i; |
| hostrange_t tmp; |
| hostlist_iterator_t hli; |
| |
| assert(hl != NULL); |
| assert(hl->magic == HOSTLIST_MAGIC); |
| assert(hr != NULL); |
| |
| if (n > hl->nranges) |
| return 0; |
| |
| if (hl->size == hl->nranges && !hostlist_expand(hl)) |
| return 0; |
| |
| /* copy new hostrange into slot "n" in array */ |
| tmp = hl->hr[n]; |
| hl->hr[n] = hostrange_copy(hr); |
| |
| /* push remaining hostrange entries up */ |
| for (i = n + 1; i < hl->nranges + 1; i++) { |
| hostrange_t last = hl->hr[i]; |
| hl->hr[i] = tmp; |
| tmp = last; |
| } |
| hl->nranges++; |
| |
| /* adjust hostlist iterators if needed */ |
| for (hli = hl->ilist; hli; hli = hli->next) { |
| if (hli->idx >= n) |
| hli->hr = hli->hl->hr[++hli->idx]; |
| } |
| |
| return 1; |
| } |
| |
| /* Delete the range at position n in the range array |
| * Assumes the hostlist lock is already held. |
| */ |
| static void hostlist_delete_range(hostlist_t hl, int n) |
| { |
| int i; |
| hostrange_t old; |
| |
| assert(hl != NULL); |
| assert(hl->magic == HOSTLIST_MAGIC); |
| assert(n < hl->nranges && n >= 0); |
| |
| old = hl->hr[n]; |
| for (i = n; i < hl->nranges - 1; i++) |
| hl->hr[i] = hl->hr[i + 1]; |
| hl->nranges--; |
| hl->hr[hl->nranges] = NULL; |
| hostlist_shift_iterators(hl, n, 0, 1); |
| |
| /* XXX caller responsible for adjusting nhosts */ |
| /* hl->nhosts -= hostrange_count(old) */ |
| |
| hostrange_destroy(old); |
| } |
| |
| #if WANT_RECKLESS_HOSTRANGE_EXPANSION |
| |
| /* The reckless hostrange expansion function. |
| * See comment in hostlist.h:hostlist_create() for more info on |
| * the different choices for hostlist notation. |
| */ |
| hostlist_t _hostlist_create(const char *hostlist, char *sep, char *r_op) |
| { |
| char *str, *orig; |
| char *tok, *cur; |
| int high, low, fmt = 0; |
| char prefix[256] = ""; |
| int pos = 0; |
| int error = 0; |
| char range_op = r_op[0];/* XXX support > 1 char range ops in future? */ |
| |
| hostlist_t new = hostlist_new(); |
| |
| if (hostlist == NULL) |
| return new; |
| #ifdef HAVE_BG |
| fatal("WANT_RECKLESS_HOSTRANGE_EXPANSION does not work on " |
| "bluegene systems!!!!"); |
| #endif |
| orig = str = strdup(hostlist); |
| |
| /* return an empty list if an empty string was passed in */ |
| if (str == NULL || strlen(str) == 0) |
| goto done; |
| |
| /* Use hostlist_create_bracketed if we see "[" */ |
| if (strchr(str, '[') != NULL) |
| return _hostlist_create_bracketed(hostlist, sep, r_op); |
| |
| while ((tok = _next_tok(sep, &str)) != NULL) { |
| |
| /* save the current string for error messages */ |
| cur = tok; |
| |
| high = low = 0; |
| |
| /* find end of alpha part |
| * do this by finding last occurence of range_op in str */ |
| pos = strlen(tok) - 1; |
| if (strstr(tok, r_op) != '\0') { |
| while (pos >= 0 && (char) tok[pos] != range_op) |
| pos--; |
| } |
| |
| /* now back up past any digits */ |
| while (pos >= 0 && isdigit((char) tok[--pos])) {;} |
| |
| /* Check for valid x-y range (x must be a digit) |
| * Reset pos if the range is not valid */ |
| if (!isdigit((char) tok[++pos])) |
| pos = strlen(tok) - 1; |
| |
| /* create prefix string |
| * if prefix will be zero length, but prefix already exists |
| * use the previous prefix and fmt |
| */ |
| if ((pos > 0) || (prefix[0] == '\0')) { |
| memcpy(prefix, tok, (size_t) pos * sizeof(char)); |
| prefix[pos] = '\0'; |
| |
| /* push pointer past prefix */ |
| tok += pos; |
| |
| /* count number of digits for ouput fmt */ |
| for (fmt = 0; isdigit(tok[fmt]); ++fmt) {;} |
| |
| if (fmt == 0) |
| error = 1; |
| |
| } else |
| tok += pos; |
| |
| /* get lower bound */ |
| low = strtoul(tok, (char **) &tok, HOSTLIST_BASE); |
| |
| if (*tok == range_op) { /* now get range upper bound */ |
| /* push pointer past range op */ |
| ++tok; |
| |
| /* find length of alpha part */ |
| for (pos = 0; tok[pos] && !isdigit(tok[pos]); ++pos) {;} |
| |
| /* alpha part must match prefix or error |
| * this could mean we've got something like "rtr1-a2" |
| * so just record an error |
| */ |
| if (pos > 0) { |
| if (pos != strlen(prefix) || |
| strncmp(prefix, tok, pos) != 0) |
| error = 1; |
| } |
| |
| if (*tok != '\0') |
| tok += pos; |
| |
| /* make sure we have digits to the end */ |
| for (pos = 0; tok[pos] && isdigit((char) tok[pos]); ++pos) {;} |
| |
| if (pos > 0) { /* we have digits to process */ |
| high = strtoul(tok, (char **) &tok, |
| HOSTLIST_BASE); |
| } else { /* bad boy, no digits */ |
| error = 1; |
| } |
| |
| if ((low > high) || (high - low > MAX_RANGE)) |
| error = 1; |
| |
| } else { /* single value */ |
| high = 0; /* special case, ugh. */ |
| } |
| |
| /* error if: |
| * 1. we are not at end of string |
| * 2. upper bound equals lower bound |
| */ |
| if (*tok != '\0' || high == low) |
| error = 1; |
| |
| if (error) { /* assume this is not a range on any error */ |
| hostlist_push_host(new, cur); |
| } else { |
| if (high < low) |
| high = low; |
| hostlist_push_hr(new, prefix, low, high, fmt); |
| } |
| |
| error = 0; |
| } |
| |
| done: |
| if(orig) |
| free(orig); |
| |
| return new; |
| } |
| |
| #else /* !WANT_RECKLESS_HOSTRANGE_EXPANSION */ |
| |
| hostlist_t _hostlist_create(const char *hostlist, char *sep, char *r_op) |
| { |
| return _hostlist_create_bracketed(hostlist, sep, r_op); |
| } |
| |
| #endif /* WANT_RECKLESS_HOSTRANGE_EXPANSION */ |
| |
| struct _range { |
| unsigned long lo, hi; |
| int width; |
| }; |
| |
| /* Grab a single range from str |
| * returns 1 if str contained a valid number or range, |
| * 0 if conversion of str to a range failed. |
| */ |
| static int _parse_single_range(const char *str, struct _range *range) |
| { |
| char *p, *q; |
| char *orig = strdup(str); |
| if (!orig) |
| seterrno_ret(ENOMEM, 0); |
| |
| if ((p = strchr(str, 'x'))) { |
| goto error; /* do NOT allow boxes in here */ |
| } |
| |
| if ((p = strchr(str, '-'))) { |
| *p++ = '\0'; |
| if (*p == '-') /* do NOT allow negative numbers */ |
| goto error; |
| } |
| |
| range->lo = strtoul(str, &q, HOSTLIST_BASE); |
| |
| if (q == str) |
| goto error; |
| |
| range->hi = (p && *p) ? strtoul(p, &q, HOSTLIST_BASE) : range->lo; |
| |
| if (q == p || *q != '\0') |
| goto error; |
| |
| if (range->lo > range->hi) |
| goto error; |
| |
| if (range->hi - range->lo + 1 > MAX_RANGE ) { |
| _error(__FILE__, __LINE__, "Too many hosts in range `%s'\n", orig); |
| free(orig); |
| seterrno_ret(ERANGE, 0); |
| } |
| |
| free(orig); |
| range->width = strlen(str); |
| return 1; |
| |
| error: |
| errno = EINVAL; |
| _error(__FILE__, __LINE__, "Invalid range: `%s'", orig); |
| free(orig); |
| return 0; |
| } |
| |
| /* |
| * Convert description of a rectangular prism in 3-D node space into a set of |
| * sequential node ranges. |
| * str IN - contains "<number>x<number>" in which the two number describe the |
| * XYZ boundaries of the nodes, each must contain three-digits |
| * ranges IN/OUT - set of high/low numeric ranges based upon sequential ordering |
| * len IN - number of entries in ranges structure |
| * count OUT - location in ranges of first unused entry |
| * RET 1 if str contained a valid number or range, |
| * 0 if conversion of str to a range failed. |
| */ |
| static int _parse_box_range(char *str, struct _range *ranges, |
| int len, int *count) |
| { |
| int a[3], b[3], i1, i2, i; |
| char new_str[8]; |
| |
| if((str[3] != 'x') || (str[7] != '\0')) |
| return 0; |
| |
| for(i = 0; i<3; i++) { |
| if ((str[i] >= '0') && (str[i] <= '9')) |
| a[i] = str[i] - '0'; |
| else if ((str[i] >= 'A') && (str[i] <= 'Z')) |
| a[i] = str[i] - 'A' + 10; |
| else |
| return 0; |
| |
| if ((str[i+4] >= '0') && (str[i+4] <= '9')) |
| b[i] = str[i+4] - '0'; |
| else if ((str[i+4] >= 'A') && (str[i+4] <= 'Z')) |
| b[i] = str[i+4] - 'A' + 10; |
| else |
| return 0; |
| } |
| |
| for (i1=a[0]; i1 <= b[0]; i1++) { |
| for (i2=a[1]; i2 <=b[1]; i2++) { |
| if (*count == len) |
| return -1; |
| snprintf(new_str, 8, "%c%c%c-%c%c%c", |
| alpha_num[i1], alpha_num[i2], alpha_num[a[2]], |
| alpha_num[i1], alpha_num[i2], |
| alpha_num[b[2]]); |
| if (!_parse_single_range(new_str,&ranges[*count])) |
| return -1; |
| (*count)++; |
| } |
| } |
| return 1; |
| } |
| |
| /* |
| * Convert 'str' containing comma separated digits and ranges into an array |
| * of struct _range types (max 'len' elements). |
| * |
| * Return number of ranges created, or -1 on error. |
| */ |
| static int _parse_range_list(char *str, struct _range *ranges, int len) |
| { |
| char *p; |
| int count = 0; |
| |
| while (str) { |
| if (count == len) |
| return -1; |
| if ((p = strchr(str, ','))) |
| *p++ = '\0'; |
| |
| if ((str[3] == 'x') && (strlen(str) == 7)) { |
| if (!_parse_box_range(str, ranges, len, &count)) |
| return -1; |
| } else { |
| if (!_parse_single_range(str, &ranges[count++])) |
| return -1; |
| } |
| str = p; |
| } |
| return count; |
| } |
| |
| static void |
| _push_range_list(hostlist_t hl, char *pfx, struct _range *rng, |
| int n) |
| { |
| int i; |
| |
| for (i = 0; i < n; i++) { |
| hostlist_push_hr(hl, pfx, rng->lo, rng->hi, rng->width); |
| rng++; |
| } |
| } |
| |
| /* |
| * Create a hostlist from a string with brackets '[' ']' to aid |
| * detection of ranges and compressed lists |
| */ |
| static hostlist_t |
| _hostlist_create_bracketed(const char *hostlist, char *sep, char *r_op) |
| { |
| hostlist_t new = hostlist_new(); |
| struct _range ranges[MAX_RANGES]; |
| int nr, err; |
| char *p, *tok, *str, *orig; |
| char cur_tok[1024]; |
| |
| if (hostlist == NULL) |
| return new; |
| |
| if (!(orig = str = strdup(hostlist))) { |
| hostlist_destroy(new); |
| return NULL; |
| } |
| |
| while ((tok = _next_tok(sep, &str)) != NULL) { |
| strncpy(cur_tok, tok, 1024); |
| |
| if ((p = strchr(tok, '[')) != NULL) { |
| char *q, *prefix = tok; |
| *p++ = '\0'; |
| |
| if ((q = strchr(p, ']'))) { |
| if ((q[1] != ',') && (q[1] != '\0')) { |
| errno = EINVAL; /* Invalid suffix */ |
| goto error; |
| } |
| *q = '\0'; |
| nr = _parse_range_list(p, ranges, MAX_RANGES); |
| if (nr < 0) |
| goto error; |
| _push_range_list(new, prefix, ranges, nr); |
| |
| |
| } else { |
| /* The hostname itself contains a '[' |
| * (no ']' found). |
| * Not likely what the user wanted. */ |
| hostlist_push_host(new, cur_tok); |
| } |
| |
| } else |
| hostlist_push_host(new, cur_tok); |
| } |
| |
| free(orig); |
| return new; |
| |
| error: |
| err = errno; |
| hostlist_destroy(new); |
| free(orig); |
| seterrno_ret(err, NULL); |
| } |
| |
| |
| |
| hostlist_t hostlist_create(const char *str) |
| { |
| return _hostlist_create(str, "\t, ", "-"); |
| } |
| |
| |
| hostlist_t hostlist_copy(const hostlist_t hl) |
| { |
| int i; |
| hostlist_t new; |
| |
| if (!hl) |
| return NULL; |
| |
| LOCK_HOSTLIST(hl); |
| if (!(new = hostlist_new())) |
| goto done; |
| |
| new->nranges = hl->nranges; |
| new->nhosts = hl->nhosts; |
| if (new->nranges > new->size) |
| hostlist_resize(new, new->nranges); |
| |
| for (i = 0; i < hl->nranges; i++) |
| new->hr[i] = hostrange_copy(hl->hr[i]); |
| |
| done: |
| UNLOCK_HOSTLIST(hl); |
| return new; |
| } |
| |
| |
| void hostlist_destroy(hostlist_t hl) |
| { |
| int i; |
| if (!hl) |
| return; |
| LOCK_HOSTLIST(hl); |
| while (hl->ilist) { |
| mutex_unlock(&hl->mutex); |
| hostlist_iterator_destroy(hl->ilist); |
| mutex_lock(&hl->mutex); |
| } |
| for (i = 0; i < hl->nranges; i++) |
| hostrange_destroy(hl->hr[i]); |
| free(hl->hr); |
| assert(hl->magic = 0x1); |
| UNLOCK_HOSTLIST(hl); |
| mutex_destroy(&hl->mutex); |
| free(hl); |
| } |
| |
| |
| int hostlist_push(hostlist_t hl, const char *hosts) |
| { |
| hostlist_t new; |
| int retval; |
| if (!hosts || !hl) |
| return 0; |
| new = hostlist_create(hosts); |
| if (!new) |
| return 0; |
| mutex_lock(&new->mutex); |
| retval = new->nhosts; |
| mutex_unlock(&new->mutex); |
| hostlist_push_list(hl, new); |
| hostlist_destroy(new); |
| return retval; |
| } |
| |
| int hostlist_push_host(hostlist_t hl, const char *str) |
| { |
| hostrange_t hr; |
| hostname_t hn; |
| |
| if (!str || !hl) |
| return 0; |
| |
| hn = hostname_create(str); |
| |
| if (hostname_suffix_is_valid(hn)) |
| hr = hostrange_create(hn->prefix, hn->num, hn->num, |
| hostname_suffix_width(hn)); |
| else |
| hr = hostrange_create_single(str); |
| |
| hostlist_push_range(hl, hr); |
| |
| hostrange_destroy(hr); |
| hostname_destroy(hn); |
| |
| return 1; |
| } |
| |
| int hostlist_push_list(hostlist_t h1, hostlist_t h2) |
| { |
| int i, n = 0; |
| |
| if (!h2 || !h1) |
| return 0; |
| |
| LOCK_HOSTLIST(h2); |
| |
| for (i = 0; i < h2->nranges; i++) |
| n += hostlist_push_range(h1, h2->hr[i]); |
| |
| UNLOCK_HOSTLIST(h2); |
| |
| return n; |
| } |
| |
| |
| char *hostlist_pop(hostlist_t hl) |
| { |
| char *host = NULL; |
| if(!hl) { |
| error("hostlist_pop: no hostlist given"); |
| return NULL; |
| } |
| |
| LOCK_HOSTLIST(hl); |
| if (hl->nhosts > 0) { |
| hostrange_t hr = hl->hr[hl->nranges - 1]; |
| host = hostrange_pop(hr); |
| hl->nhosts--; |
| if (hostrange_empty(hr)) { |
| hostrange_destroy(hl->hr[--hl->nranges]); |
| hl->hr[hl->nranges] = NULL; |
| } |
| } |
| UNLOCK_HOSTLIST(hl); |
| return host; |
| } |
| |
| /* find all iterators affected by a shift (or deletion) at |
| * hl->hr[idx], depth, with the deletion of n ranges */ |
| static void |
| hostlist_shift_iterators(hostlist_t hl, int idx, int depth, int n) |
| { |
| hostlist_iterator_t i; |
| if(!hl) { |
| error("hostlist_shift_iterators: no hoslist given"); |
| return; |
| } |
| for (i = hl->ilist; i; i = i->next) { |
| if (n == 0) { |
| if (i->idx == idx && i->depth >= depth) |
| i->depth = i->depth > -1 ? i->depth - 1 : -1; |
| } else { |
| if (i->idx >= idx) { |
| if ((i->idx -= n) >= 0) |
| i->hr = i->hl->hr[i->idx]; |
| else |
| hostlist_iterator_reset(i); |
| } |
| } |
| } |
| } |
| |
| char *hostlist_shift(hostlist_t hl) |
| { |
| char *host = NULL; |
| |
| if(!hl){ |
| error("hostlist_shift: no hoslist given"); |
| return NULL; |
| } |
| LOCK_HOSTLIST(hl); |
| |
| if (hl->nhosts > 0) { |
| hostrange_t hr = hl->hr[0]; |
| |
| host = hostrange_shift(hr); |
| hl->nhosts--; |
| |
| if (hostrange_empty(hr)) { |
| hostlist_delete_range(hl, 0); |
| /* hl->nranges--; */ |
| } else |
| hostlist_shift_iterators(hl, 0, 0, 0); |
| } |
| |
| UNLOCK_HOSTLIST(hl); |
| |
| return host; |
| } |
| |
| |
| char *hostlist_pop_range(hostlist_t hl) |
| { |
| int i; |
| char buf[MAXHOSTRANGELEN + 1]; |
| hostlist_t hltmp; |
| hostrange_t tail; |
| |
| if(!hl) |
| return NULL; |
| LOCK_HOSTLIST(hl); |
| if (hl->nranges < 1 || !(hltmp = hostlist_new())) { |
| UNLOCK_HOSTLIST(hl); |
| return NULL; |
| } |
| |
| i = hl->nranges - 2; |
| tail = hl->hr[hl->nranges - 1]; |
| while (i >= 0 && hostrange_within_range(tail, hl->hr[i])) |
| i--; |
| |
| for (i++; i < hl->nranges; i++) { |
| hostlist_push_range(hltmp, hl->hr[i]); |
| hostrange_destroy(hl->hr[i]); |
| hl->hr[i] = NULL; |
| } |
| hl->nhosts -= hltmp->nhosts; |
| hl->nranges -= hltmp->nranges; |
| |
| UNLOCK_HOSTLIST(hl); |
| hostlist_ranged_string(hltmp, MAXHOSTRANGELEN, buf); |
| hostlist_destroy(hltmp); |
| return strdup(buf); |
| } |
| |
| |
| char *hostlist_shift_range(hostlist_t hl) |
| { |
| int i; |
| char buf[MAXHOSTRANGELEN+1]; |
| hostlist_t hltmp = hostlist_new(); |
| if (!hltmp || !hl) |
| return NULL; |
| |
| LOCK_HOSTLIST(hl); |
| |
| if (hl->nranges == 0) { |
| hostlist_destroy(hltmp); |
| UNLOCK_HOSTLIST(hl); |
| return NULL; |
| } |
| |
| i = 0; |
| do { |
| hostlist_push_range(hltmp, hl->hr[i]); |
| hostrange_destroy(hl->hr[i]); |
| } while ( (++i < hl->nranges) |
| && hostrange_within_range(hltmp->hr[0], hl->hr[i]) ); |
| |
| hostlist_shift_iterators(hl, i, 0, hltmp->nranges); |
| |
| /* shift rest of ranges back in hl */ |
| for (; i < hl->nranges; i++) { |
| hl->hr[i - hltmp->nranges] = hl->hr[i]; |
| hl->hr[i] = NULL; |
| } |
| hl->nhosts -= hltmp->nhosts; |
| hl->nranges -= hltmp->nranges; |
| |
| UNLOCK_HOSTLIST(hl); |
| |
| hostlist_ranged_string(hltmp, MAXHOSTRANGELEN, buf); |
| hostlist_destroy(hltmp); |
| |
| return strdup(buf); |
| } |
| |
| /* XXX: Note: efficiency improvements needed */ |
| int hostlist_delete(hostlist_t hl, const char *hosts) |
| { |
| int n = 0; |
| char *hostname = NULL; |
| hostlist_t hltmp; |
| if(!hl) |
| return -1; |
| |
| if (!(hltmp = hostlist_create(hosts))) |
| seterrno_ret(EINVAL, 0); |
| |
| while ((hostname = hostlist_pop(hltmp)) != NULL) { |
| n += hostlist_delete_host(hl, hostname); |
| free(hostname); |
| } |
| hostlist_destroy(hltmp); |
| |
| return n; |
| } |
| |
| |
| /* XXX watch out! poor implementation follows! (fix it at some point) */ |
| int hostlist_delete_host(hostlist_t hl, const char *hostname) |
| { |
| int n; |
| |
| if(!hl) |
| return -1; |
| n = hostlist_find(hl, hostname); |
| |
| if (n >= 0) |
| hostlist_delete_nth(hl, n); |
| return n >= 0 ? 1 : 0; |
| } |
| |
| |
| static char * |
| _hostrange_string(hostrange_t hr, int depth) |
| { |
| char buf[MAXHOSTNAMELEN + 16]; |
| int len = snprintf(buf, MAXHOSTNAMELEN + 15, "%s", hr->prefix); |
| |
| if (!hr->singlehost) { |
| #ifdef HAVE_BG |
| if (hr->width == 3) { |
| int coord[3]; |
| int temp = hr->lo+depth; |
| coord[0] = temp / (HOSTLIST_BASE * HOSTLIST_BASE); |
| coord[1] = (temp % (HOSTLIST_BASE * HOSTLIST_BASE)) |
| / HOSTLIST_BASE; |
| coord[2] = (temp % HOSTLIST_BASE); |
| snprintf(buf+len, MAXHOSTNAMELEN+15 - len, "%c%c%c", |
| alpha_num[coord[0]], alpha_num[coord[1]], |
| alpha_num[coord[2]]); |
| } else { |
| snprintf(buf+len, MAXHOSTNAMELEN+15 - len, "%0*lu", |
| hr->width, hr->lo + depth); |
| } |
| #else |
| snprintf(buf+len, MAXHOSTNAMELEN+15 - len, "%0*lu", |
| hr->width, hr->lo + depth); |
| #endif |
| } |
| return strdup(buf); |
| } |
| |
| char * hostlist_nth(hostlist_t hl, int n) |
| { |
| char *host = NULL; |
| int i, count; |
| |
| if(!hl) |
| return NULL; |
| LOCK_HOSTLIST(hl); |
| count = 0; |
| for (i = 0; i < hl->nranges; i++) { |
| int num_in_range = hostrange_count(hl->hr[i]); |
| |
| if (n <= (num_in_range - 1 + count)) { |
| host = _hostrange_string(hl->hr[i], n - count); |
| break; |
| } else |
| count += num_in_range; |
| } |
| |
| UNLOCK_HOSTLIST(hl); |
| |
| return host; |
| } |
| |
| |
| int hostlist_delete_nth(hostlist_t hl, int n) |
| { |
| int i, count; |
| |
| if(!hl) |
| return -1; |
| LOCK_HOSTLIST(hl); |
| assert(n >= 0 && n <= hl->nhosts); |
| |
| count = 0; |
| |
| for (i = 0; i < hl->nranges; i++) { |
| int num_in_range = hostrange_count(hl->hr[i]); |
| hostrange_t hr = hl->hr[i]; |
| |
| if (n <= (num_in_range - 1 + count)) { |
| unsigned long num = hr->lo + n - count; |
| hostrange_t new; |
| |
| if (hr->singlehost) { /* this wasn't a range */ |
| hostlist_delete_range(hl, i); |
| } else if ((new = hostrange_delete_host(hr, num))) { |
| hostlist_insert_range(hl, new, i + 1); |
| hostrange_destroy(new); |
| } else if (hostrange_empty(hr)) |
| hostlist_delete_range(hl, i); |
| |
| goto done; |
| } else |
| count += num_in_range; |
| |
| } |
| |
| done: |
| UNLOCK_HOSTLIST(hl); |
| hl->nhosts--; |
| return 1; |
| } |
| |
| int hostlist_count(hostlist_t hl) |
| { |
| int retval; |
| if(!hl) |
| return -1; |
| |
| LOCK_HOSTLIST(hl); |
| retval = hl->nhosts; |
| UNLOCK_HOSTLIST(hl); |
| return retval; |
| } |
| |
| int hostlist_find(hostlist_t hl, const char *hostname) |
| { |
| int i, count, ret = -1; |
| hostname_t hn; |
| |
| if (!hostname || !hl) |
| return -1; |
| |
| hn = hostname_create(hostname); |
| |
| LOCK_HOSTLIST(hl); |
| |
| for (i = 0, count = 0; i < hl->nranges; i++) { |
| if (hostrange_hn_within(hl->hr[i], hn)) { |
| if (hostname_suffix_is_valid(hn)) |
| ret = count + hn->num - hl->hr[i]->lo; |
| else |
| ret = count; |
| goto done; |
| } else |
| count += hostrange_count(hl->hr[i]); |
| } |
| |
| done: |
| UNLOCK_HOSTLIST(hl); |
| hostname_destroy(hn); |
| return ret; |
| } |
| |
| /* hostrange compare with void * arguments to allow use with |
| * libc qsort() |
| */ |
| int _cmp(const void *hr1, const void *hr2) |
| { |
| hostrange_t *h1 = (hostrange_t *) hr1; |
| hostrange_t *h2 = (hostrange_t *) hr2; |
| return hostrange_cmp((hostrange_t) * h1, (hostrange_t) * h2); |
| } |
| |
| |
| void hostlist_sort(hostlist_t hl) |
| { |
| hostlist_iterator_t i; |
| LOCK_HOSTLIST(hl); |
| |
| if (hl->nranges <= 1) { |
| UNLOCK_HOSTLIST(hl); |
| return; |
| } |
| |
| qsort(hl->hr, hl->nranges, sizeof(hostrange_t), &_cmp); |
| |
| /* reset all iterators */ |
| for (i = hl->ilist; i; i = i->next) |
| hostlist_iterator_reset(i); |
| |
| UNLOCK_HOSTLIST(hl); |
| |
| hostlist_coalesce(hl); |
| |
| } |
| |
| |
| /* search through hostlist for ranges that can be collapsed |
| * does =not= delete any hosts |
| */ |
| static void hostlist_collapse(hostlist_t hl) |
| { |
| int i; |
| |
| LOCK_HOSTLIST(hl); |
| for (i = hl->nranges - 1; i > 0; i--) { |
| hostrange_t hprev = hl->hr[i - 1]; |
| hostrange_t hnext = hl->hr[i]; |
| |
| if (hostrange_prefix_cmp(hprev, hnext) == 0 && |
| hprev->hi == hnext->lo - 1 && |
| hostrange_width_combine(hprev, hnext)) { |
| hprev->hi = hnext->hi; |
| hostlist_delete_range(hl, i); |
| } |
| } |
| UNLOCK_HOSTLIST(hl); |
| } |
| |
| /* search through hostlist (hl) for intersecting ranges |
| * split up duplicates and coalesce ranges where possible |
| */ |
| static void hostlist_coalesce(hostlist_t hl) |
| { |
| int i, j; |
| hostrange_t new; |
| |
| LOCK_HOSTLIST(hl); |
| |
| for (i = hl->nranges - 1; i > 0; i--) { |
| |
| new = hostrange_intersect(hl->hr[i - 1], hl->hr[i]); |
| |
| if (new) { |
| hostrange_t hprev = hl->hr[i - 1]; |
| hostrange_t hnext = hl->hr[i]; |
| j = i; |
| |
| if (new->hi < hprev->hi) |
| hnext->hi = hprev->hi; |
| |
| hprev->hi = new->lo; |
| hnext->lo = new->hi; |
| |
| if (hostrange_empty(hprev)) |
| hostlist_delete_range(hl, i); |
| |
| while (new->lo <= new->hi) { |
| hostrange_t hr = hostrange_create( new->prefix, |
| new->lo, new->lo, |
| new->width ); |
| |
| if (new->lo > hprev->hi) |
| hostlist_insert_range(hl, hr, j++); |
| |
| if (new->lo < hnext->lo) |
| hostlist_insert_range(hl, hr, j++); |
| |
| hostrange_destroy(hr); |
| |
| new->lo++; |
| } |
| i = hl->nranges; |
| hostrange_destroy(new); |
| } |
| } |
| UNLOCK_HOSTLIST(hl); |
| |
| hostlist_collapse(hl); |
| |
| } |
| |
| /* attempt to join ranges at loc and loc-1 in a hostlist */ |
| /* delete duplicates, return the number of hosts deleted */ |
| /* assumes that the hostlist hl has been locked by caller */ |
| static int _attempt_range_join(hostlist_t hl, int loc) |
| { |
| int ndup; |
| assert(hl != NULL); |
| assert(hl->magic == HOSTLIST_MAGIC); |
| assert(loc > 0); |
| assert(loc < hl->nranges); |
| ndup = hostrange_join(hl->hr[loc - 1], hl->hr[loc]); |
| if (ndup >= 0) { |
| hostlist_delete_range(hl, loc); |
| hl->nhosts -= ndup; |
| } |
| return ndup; |
| } |
| |
| void hostlist_uniq(hostlist_t hl) |
| { |
| int i = 1; |
| hostlist_iterator_t hli; |
| LOCK_HOSTLIST(hl); |
| if (hl->nranges <= 1) { |
| UNLOCK_HOSTLIST(hl); |
| return; |
| } |
| qsort(hl->hr, hl->nranges, sizeof(hostrange_t), &_cmp); |
| |
| while (i < hl->nranges) { |
| if (_attempt_range_join(hl, i) < 0) /* No range join occurred */ |
| i++; |
| } |
| |
| /* reset all iterators */ |
| for (hli = hl->ilist; hli; hli = hli->next) |
| hostlist_iterator_reset(hli); |
| |
| UNLOCK_HOSTLIST(hl); |
| } |
| |
| |
| size_t hostlist_deranged_string(hostlist_t hl, size_t n, char *buf) |
| { |
| int i; |
| int len = 0; |
| int truncated = 0; |
| |
| LOCK_HOSTLIST(hl); |
| for (i = 0; i < hl->nranges; i++) { |
| size_t m = (n - len) <= n ? n - len : 0; |
| int ret = hostrange_to_string(hl->hr[i], m, buf + len, ","); |
| if (ret < 0 || ret > m) { |
| len = n; |
| truncated = 1; |
| break; |
| } |
| len+=ret; |
| buf[len++] = ','; |
| } |
| UNLOCK_HOSTLIST(hl); |
| |
| buf[len > 0 ? --len : 0] = '\0'; |
| if (len == n) |
| truncated = 1; |
| |
| return truncated ? -1 : len; |
| } |
| |
| /* return true if a bracket is needed for the range at i in hostlist hl */ |
| static int _is_bracket_needed(hostlist_t hl, int i) |
| { |
| hostrange_t h1 = hl->hr[i]; |
| hostrange_t h2 = i < hl->nranges - 1 ? hl->hr[i + 1] : NULL; |
| return hostrange_count(h1) > 1 || hostrange_within_range(h1, h2); |
| } |
| |
| /* write the next bracketed hostlist, i.e. prefix[n-m,k,...] |
| * into buf, writing at most n chars including the terminating '\0' |
| * |
| * leaves start pointing to one past last range object in bracketed list, |
| * and returns the number of bytes written into buf. |
| * |
| * Assumes hostlist is locked. |
| */ |
| static int |
| _get_bracketed_list(hostlist_t hl, int *start, const size_t n, char *buf) |
| { |
| hostrange_t *hr = hl->hr; |
| int i = *start; |
| int m, len = 0; |
| int bracket_needed = _is_bracket_needed(hl, i); |
| |
| len = snprintf(buf, n, "%s", hr[i]->prefix); |
| |
| if ((len < 0) || (len > n)) |
| return n; /* truncated, buffer filled */ |
| |
| if (bracket_needed && len < n && len >= 0) |
| buf[len++] = '['; |
| |
| do { |
| m = (n - len) <= n ? n - len : 0; |
| len += hostrange_numstr(hr[i], m, buf + len); |
| if (len >= n) |
| break; |
| if (bracket_needed) /* Only need commas inside brackets */ |
| buf[len++] = ','; |
| } while (++i < hl->nranges && hostrange_within_range(hr[i], hr[i-1])); |
| |
| if (bracket_needed && len < n && len > 0) { |
| |
| /* Add trailing bracket (change trailing "," from above to "]" */ |
| buf[len - 1] = ']'; |
| |
| /* NUL terminate for safety, but do not add terminator to len */ |
| buf[len] = '\0'; |
| |
| } else if (len >= n) { |
| if (n > 0) |
| buf[n-1] = '\0'; |
| |
| } else { |
| /* If len is > 0, NUL terminate (but do not add to len) */ |
| buf[len > 0 ? len : 0] = '\0'; |
| } |
| |
| *start = i; |
| return len; |
| } |
| |
| #ifdef HAVE_BG |
| |
| /* logic for block node description */ |
| /* write the next bracketed hostlist, i.e. prefix[n-m,k,...] |
| * into buf, writing at most n chars including the terminating '\0' |
| * |
| * leaves start pointing to one past last range object in bracketed list, |
| * and returns the number of bytes written into buf. |
| * |
| * Assumes hostlist is locked. |
| */ |
| static int |
| _get_boxes(char *buf, int max_len) |
| { |
| int i, j, k, len = 0; |
| int is_box, start_box=-1, end_box=-1; |
| |
| /* scan each X-plane for a box then match across X values */ |
| for (i=axis_min_x; i<=axis_max_x; i++) { |
| is_box = 1; |
| for (j=axis_min_y; j<=axis_max_y; j++) { |
| for (k=axis_min_z; k<=axis_max_z; k++) { |
| if (!axis[i][j][k]) { |
| is_box = 0; |
| break; |
| } |
| } |
| } |
| if (is_box) { |
| if (start_box == -1) |
| start_box = i; |
| end_box = i; |
| } |
| |
| |
| if (((len+8) < max_len) && (start_box != -1) |
| && ((is_box == 0) || (i == axis_max_x))) { |
| if(start_box == end_box |
| && axis_min_y == axis_max_y |
| && axis_min_z == axis_max_z) { |
| sprintf(buf+len,"%c%c%c,", |
| alpha_num[start_box], |
| alpha_num[axis_min_y], |
| alpha_num[axis_min_z]); |
| len += 4; |
| } else { |
| sprintf(buf+len,"%c%c%cx%c%c%c,", |
| alpha_num[start_box], |
| alpha_num[axis_min_y], |
| alpha_num[axis_min_z], |
| alpha_num[end_box], |
| alpha_num[axis_max_y], |
| alpha_num[axis_max_z]); |
| len += 8; |
| } |
| start_box = -1; |
| end_box = -1; |
| } |
| if (is_box == 0) { |
| for (j=axis_min_y; j<=axis_max_y; j++) { |
| for (k=axis_min_z; k<=axis_max_z; k++) { |
| if (!axis[i][j][k]) |
| continue; |
| if ((len+4) >= max_len) |
| break; |
| sprintf(buf+len,"%c%c%c,", |
| alpha_num[i], alpha_num[j], |
| alpha_num[k]); |
| len += 4; |
| } |
| } |
| } |
| } |
| |
| buf[len - 1] = ']'; |
| |
| /* NUL terminate for safety, but do not add terminator to len */ |
| buf[len] = '\0'; |
| |
| return len; |
| } |
| |
| static void |
| _clear_grid(void) |
| { |
| bzero(axis, sizeof(axis)); |
| |
| axis_min_x = HOSTLIST_BASE; |
| axis_min_y = HOSTLIST_BASE; |
| axis_min_z = HOSTLIST_BASE; |
| |
| axis_max_x = -1; |
| axis_max_y = -1; |
| axis_max_z = -1; |
| } |
| |
| static void |
| _set_grid(unsigned long start, unsigned long end) |
| { |
| int pt1 = (int) start; |
| int pt2 = (int) end; |
| int x1, y1, z1, x2, y2, z2; |
| int temp, temp1, temp2; |
| |
| x1 = pt1 / (HOSTLIST_BASE * HOSTLIST_BASE); |
| y1 = (pt1 % (HOSTLIST_BASE * HOSTLIST_BASE)) / HOSTLIST_BASE; |
| z1 = pt1 % HOSTLIST_BASE; |
| |
| x2 = pt2 / (HOSTLIST_BASE * HOSTLIST_BASE); |
| y2 = (pt2 % (HOSTLIST_BASE * HOSTLIST_BASE)) / HOSTLIST_BASE; |
| z2 = pt2 % HOSTLIST_BASE; |
| /* printf("new %c%c%c %c%c%c\n", */ |
| /* alpha_num[x1],alpha_num[y1],alpha_num[z1], */ |
| /* alpha_num[x2],alpha_num[y2],alpha_num[z2]); */ |
| |
| axis_min_x = MIN(axis_min_x, x1); |
| axis_min_y = MIN(axis_min_y, y1); |
| axis_min_z = MIN(axis_min_z, z1); |
| |
| axis_max_x = MAX(axis_max_x, x2); |
| axis_max_y = MAX(axis_max_y, y2); |
| axis_max_z = MAX(axis_max_z, z2); |
| /* printf("max %c%c%c %c%c%c\n", */ |
| /* alpha_num[axis_min_x],alpha_num[axis_min_y], */ |
| /* alpha_num[axis_min_z], */ |
| /* alpha_num[axis_max_x],alpha_num[axis_max_y], */ |
| /* alpha_num[axis_max_z]); */ |
| |
| for (temp=x1; temp<=x2; temp++) { |
| for (temp1=y1; temp1<=y2; temp1++) { |
| for (temp2=z1; temp2<=z2; temp2++) { |
| axis[temp][temp1][temp2] = true; |
| } |
| } |
| } |
| } |
| |
| static bool |
| _test_box(void) |
| { |
| int temp, temp1, temp2; |
| |
| if ((axis_min_x > axis_max_x) |
| || (axis_min_y > axis_max_y) |
| || (axis_min_z > axis_max_z)) |
| return false; |
| |
| if ((axis_min_x == axis_max_x) |
| && (axis_min_y == axis_max_y) |
| && (axis_min_z == axis_max_z)) |
| return false; /* single node */ |
| |
| for (temp = axis_min_x; temp<=axis_max_x; temp++) { |
| for (temp1 = axis_min_y; temp1<=axis_max_y; temp1++) { |
| for (temp2 = axis_min_z; temp2<=axis_max_z; temp2++) { |
| if (!axis[temp][temp1][temp2]) |
| return false; /* gap in box */ |
| } |
| } |
| } |
| |
| return true; |
| } |
| #endif |
| |
| size_t hostlist_ranged_string(hostlist_t hl, size_t n, char *buf) |
| { |
| int i = 0; |
| int len = 0; |
| int truncated = 0; |
| bool box = false; |
| |
| LOCK_HOSTLIST(hl); |
| |
| #ifdef HAVE_BG /* logic for block node description */ |
| if (hl->nranges < 1) |
| goto notbox; /* no data */ |
| if (hl->hr[0]->width != 3) { |
| /* We use this logic to build task list ranges, so |
| * this does not necessarily contain a BlueGene |
| * host list. It could just be numeric values */ |
| if (hl->hr[0]->prefix[0]) { |
| error("This node is not in bluegene format. " |
| "The suffix was %d chars in length", |
| hl->hr[0]->width); |
| } |
| goto notbox; |
| } |
| _clear_grid(); |
| for (i=0;i<hl->nranges;i++) |
| _set_grid(hl->hr[i]->lo, hl->hr[i]->hi); |
| if ((axis_min_x == axis_max_x) |
| && (axis_min_y == axis_max_y) |
| && (axis_min_z == axis_max_z)) { |
| len += snprintf(buf, n, "%s%c%c%c", |
| hl->hr[0]->prefix, |
| alpha_num[axis_min_x], alpha_num[axis_min_y], |
| alpha_num[axis_min_z]); |
| if ((len < 0) || (len > n)) |
| len = n; /* truncated */ |
| } else if (!_test_box()) { |
| sprintf(buf, "%s[", hl->hr[0]->prefix); |
| len = strlen(hl->hr[0]->prefix) + 1; |
| len += _get_boxes(buf + len, (n-len)); |
| } else { |
| len += snprintf(buf, n, "%s[%c%c%cx%c%c%c]", |
| hl->hr[0]->prefix, |
| alpha_num[axis_min_x], alpha_num[axis_min_y], |
| alpha_num[axis_min_z], |
| alpha_num[axis_max_x], alpha_num[axis_max_y], |
| alpha_num[axis_max_z]); |
| if ((len < 0) || (len > n)) |
| len = n; /* truncated */ |
| } |
| box = true; |
| |
| notbox: |
| #endif |
| if (!box) { |
| i=0; |
| while (i < hl->nranges && len < n) { |
| len += _get_bracketed_list(hl, &i, n - len, buf + len); |
| if ((len > 0) && (len < n) && (i < hl->nranges)) |
| buf[len++] = ','; |
| } |
| } |
| |
| UNLOCK_HOSTLIST(hl); |
| |
| /* NUL terminate */ |
| if (len >= n) { |
| truncated = 1; |
| if (n > 0) |
| buf[n-1] = '\0'; |
| } else |
| buf[len > 0 ? len : 0] = '\0'; |
| |
| return truncated ? -1 : len; |
| } |
| /* ----[ hostlist iterator functions ]---- */ |
| |
| static hostlist_iterator_t hostlist_iterator_new(void) |
| { |
| hostlist_iterator_t i = (hostlist_iterator_t) malloc(sizeof(*i)); |
| if (!i) |
| return NULL; |
| i->hl = NULL; |
| i->hr = NULL; |
| i->idx = 0; |
| i->depth = -1; |
| i->next = i; |
| assert(i->magic = HOSTLIST_MAGIC); |
| return i; |
| } |
| |
| hostlist_iterator_t hostlist_iterator_create(hostlist_t hl) |
| { |
| hostlist_iterator_t i; |
| |
| if (!(i = hostlist_iterator_new())) |
| out_of_memory("hostlist_iterator_create"); |
| |
| LOCK_HOSTLIST(hl); |
| i->hl = hl; |
| i->hr = hl->hr[0]; |
| i->next = hl->ilist; |
| hl->ilist = i; |
| UNLOCK_HOSTLIST(hl); |
| return i; |
| } |
| |
| hostlist_iterator_t hostset_iterator_create(hostset_t set) |
| { |
| return hostlist_iterator_create(set->hl); |
| } |
| |
| void hostlist_iterator_reset(hostlist_iterator_t i) |
| { |
| assert(i != NULL); |
| assert(i->magic == HOSTLIST_MAGIC); |
| i->idx = 0; |
| i->hr = i->hl->hr[0]; |
| i->depth = -1; |
| return; |
| } |
| |
| void hostlist_iterator_destroy(hostlist_iterator_t i) |
| { |
| hostlist_iterator_t *pi; |
| if (i == NULL) |
| return; |
| assert(i != NULL); |
| assert(i->magic == HOSTLIST_MAGIC); |
| LOCK_HOSTLIST(i->hl); |
| for (pi = &i->hl->ilist; *pi; pi = &(*pi)->next) { |
| assert((*pi)->magic == HOSTLIST_MAGIC); |
| if (*pi == i) { |
| *pi = (*pi)->next; |
| break; |
| } |
| } |
| UNLOCK_HOSTLIST(i->hl); |
| assert(i->magic = 0x1); |
| free(i); |
| } |
| |
| static void _iterator_advance(hostlist_iterator_t i) |
| { |
| assert(i != NULL); |
| assert(i->magic == HOSTLIST_MAGIC); |
| |
| if (i->idx > i->hl->nranges - 1) |
| return; |
| |
| if (++(i->depth) > (i->hr->hi - i->hr->lo)) { |
| i->depth = 0; |
| i->hr = i->hl->hr[++i->idx]; |
| } |
| } |
| |
| /* advance iterator to end of current range (meaning within "[" "]") |
| * i.e. advance iterator past all range objects that could be represented |
| * in on bracketed hostlist. |
| */ |
| static void _iterator_advance_range(hostlist_iterator_t i) |
| { |
| int nr, j; |
| hostrange_t *hr; |
| assert(i != NULL); |
| assert(i->magic == HOSTLIST_MAGIC); |
| |
| nr = i->hl->nranges; |
| hr = i->hl->hr; |
| j = i->idx; |
| if (++i->depth > 0) { |
| while (++j < nr && hostrange_within_range(i->hr, hr[j])) {;} |
| i->idx = j; |
| i->hr = i->hl->hr[i->idx]; |
| i->depth = 0; |
| } |
| } |
| |
| char *hostlist_next(hostlist_iterator_t i) |
| { |
| char buf[MAXHOSTNAMELEN + 16]; |
| int len = 0; |
| assert(i != NULL); |
| assert(i->magic == HOSTLIST_MAGIC); |
| LOCK_HOSTLIST(i->hl); |
| _iterator_advance(i); |
| |
| if (i->idx > i->hl->nranges - 1) { |
| UNLOCK_HOSTLIST(i->hl); |
| return NULL; |
| } |
| |
| len = snprintf(buf, MAXHOSTNAMELEN + 15, "%s", i->hr->prefix); |
| if (!i->hr->singlehost) { |
| #ifdef HAVE_BG |
| if (i->hr->width == 3) { |
| int coord[3]; |
| int temp = i->hr->lo + i->depth; |
| coord[0] = temp / (HOSTLIST_BASE * HOSTLIST_BASE); |
| coord[1] = (temp % (HOSTLIST_BASE * HOSTLIST_BASE)) |
| / HOSTLIST_BASE; |
| coord[2] = (temp % HOSTLIST_BASE); |
| snprintf(buf + len, MAXHOSTNAMELEN + 15 - len, "%c%c%c", |
| alpha_num[coord[0]], alpha_num[coord[1]], |
| alpha_num[coord[2]]); |
| } else { |
| snprintf(buf + len, MAXHOSTNAMELEN + 15 - len, "%0*lu", |
| i->hr->width, i->hr->lo + i->depth); |
| } |
| #else |
| snprintf(buf + len, MAXHOSTNAMELEN + 15 - len, "%0*lu", |
| i->hr->width, i->hr->lo + i->depth); |
| #endif |
| } |
| UNLOCK_HOSTLIST(i->hl); |
| return strdup(buf); |
| } |
| |
| char *hostlist_next_range(hostlist_iterator_t i) |
| { |
| char buf[MAXHOSTRANGELEN + 1]; |
| int j; |
| |
| assert(i != NULL); |
| assert(i->magic == HOSTLIST_MAGIC); |
| LOCK_HOSTLIST(i->hl); |
| |
| _iterator_advance_range(i); |
| |
| if (i->idx > i->hl->nranges - 1) { |
| UNLOCK_HOSTLIST(i->hl); |
| return NULL; |
| } |
| |
| j = i->idx; |
| _get_bracketed_list(i->hl, &j, MAXHOSTRANGELEN, buf); |
| |
| UNLOCK_HOSTLIST(i->hl); |
| |
| return strdup(buf); |
| } |
| |
| int hostlist_remove(hostlist_iterator_t i) |
| { |
| hostrange_t new; |
| assert(i != NULL); |
| assert(i->magic == HOSTLIST_MAGIC); |
| LOCK_HOSTLIST(i->hl); |
| new = hostrange_delete_host(i->hr, i->hr->lo + i->depth); |
| if (new) { |
| hostlist_insert_range(i->hl, new, i->idx + 1); |
| hostrange_destroy(new); |
| i->hr = i->hl->hr[++i->idx]; |
| i->depth = -1; |
| } else if (hostrange_empty(i->hr)) { |
| hostlist_delete_range(i->hl, i->idx); |
| /* i->hr = i->hl->hr[i->idx]; |
| i->depth = -1; */ |
| } else |
| i->depth--; |
| |
| i->hl->nhosts--; |
| UNLOCK_HOSTLIST(i->hl); |
| |
| return 1; |
| } |
| |
| /* ----[ hostset functions ]---- */ |
| |
| hostset_t hostset_create(const char *hostlist) |
| { |
| hostset_t new; |
| |
| if (!(new = (hostset_t) malloc(sizeof(*new)))) |
| goto error1; |
| |
| if (!(new->hl = hostlist_create(hostlist))) |
| goto error2; |
| |
| hostlist_uniq(new->hl); |
| return new; |
| |
| error2: |
| free(new); |
| error1: |
| return NULL; |
| } |
| |
| hostset_t hostset_copy(const hostset_t set) |
| { |
| hostset_t new; |
| if (!(new = (hostset_t) malloc(sizeof(*new)))) |
| goto error1; |
| |
| if (!(new->hl = hostlist_copy(set->hl))) |
| goto error2; |
| |
| return new; |
| error2: |
| free(new); |
| error1: |
| return NULL; |
| } |
| |
| void hostset_destroy(hostset_t set) |
| { |
| if (set == NULL) |
| return; |
| hostlist_destroy(set->hl); |
| free(set); |
| } |
| |
| /* inserts a single range object into a hostset |
| * Assumes that the set->hl lock is already held |
| */ |
| static int hostset_insert_range(hostset_t set, hostrange_t hr) |
| { |
| int i, n = 0; |
| int inserted = 0; |
| int retval = 0; |
| hostlist_t hl; |
| |
| hl = set->hl; |
| |
| if (hl->size == hl->nranges && !hostlist_expand(hl)) |
| return 0; |
| |
| retval = hostrange_count(hr); |
| |
| for (i = 0; i < hl->nranges; i++) { |
| if (hostrange_cmp(hr, hl->hr[i]) <= 0) { |
| n = hostrange_join(hr, hl->hr[i]); |
| |
| if (n >= 0) { |
| hostlist_delete_range(hl, i); |
| hl->nhosts -= n; |
| } |
| |
| hostlist_insert_range(hl, hr, i); |
| |
| /* now attempt to join hr[i] and hr[i-1] */ |
| if (i > 0) { |
| int m = _attempt_range_join(hl, i); |
| n += m; |
| } |
| inserted = 1; |
| break; |
| } |
| } |
| |
| if (inserted == 0) { |
| hl->hr[hl->nranges++] = hostrange_copy(hr); |
| n = _attempt_range_join(hl, hl->nranges - 1); |
| } |
| |
| return retval - n; |
| } |
| |
| int hostset_insert(hostset_t set, const char *hosts) |
| { |
| int i, n = 0; |
| hostlist_t hl = hostlist_create(hosts); |
| if (!hl) |
| return 0; |
| |
| hostlist_uniq(hl); |
| LOCK_HOSTLIST(set->hl); |
| for (i = 0; i < hl->nranges; i++) |
| n += hostset_insert_range(set, hl->hr[i]); |
| UNLOCK_HOSTLIST(set->hl); |
| hostlist_destroy(hl); |
| return n; |
| } |
| |
| |
| /* linear search through N ranges for hostname "host" |
| * */ |
| static int hostset_find_host(hostset_t set, const char *host) |
| { |
| int i; |
| int retval = 0; |
| hostname_t hn; |
| LOCK_HOSTLIST(set->hl); |
| hn = hostname_create(host); |
| for (i = 0; i < set->hl->nranges; i++) { |
| if (hostrange_hn_within(set->hl->hr[i], hn)) { |
| retval = 1; |
| goto done; |
| } |
| } |
| done: |
| UNLOCK_HOSTLIST(set->hl); |
| hostname_destroy(hn); |
| return retval; |
| } |
| |
| int hostset_within(hostset_t set, const char *hosts) |
| { |
| int nhosts, nfound; |
| hostlist_t hl; |
| char *hostname; |
| |
| assert(set->hl->magic == HOSTLIST_MAGIC); |
| |
| hl = hostlist_create(hosts); |
| nhosts = hostlist_count(hl); |
| nfound = 0; |
| |
| while ((hostname = hostlist_pop(hl)) != NULL) { |
| nfound += hostset_find_host(set, hostname); |
| free(hostname); |
| } |
| |
| hostlist_destroy(hl); |
| |
| return (nhosts == nfound); |
| } |
| |
| int hostset_delete(hostset_t set, const char *hosts) |
| { |
| return hostlist_delete(set->hl, hosts); |
| } |
| |
| int hostset_delete_host(hostset_t set, const char *hostname) |
| { |
| return hostlist_delete_host(set->hl, hostname); |
| } |
| |
| char *hostset_shift(hostset_t set) |
| { |
| return hostlist_shift(set->hl); |
| } |
| |
| char *hostset_pop(hostset_t set) |
| { |
| return hostlist_pop(set->hl); |
| } |
| |
| char *hostset_shift_range(hostset_t set) |
| { |
| return hostlist_shift_range(set->hl); |
| } |
| |
| char *hostset_pop_range(hostset_t set) |
| { |
| return hostlist_pop_range(set->hl); |
| } |
| |
| int hostset_count(hostset_t set) |
| { |
| return hostlist_count(set->hl); |
| } |
| |
| size_t hostset_ranged_string(hostset_t set, size_t n, char *buf) |
| { |
| return hostlist_ranged_string(set->hl, n, buf); |
| } |
| |
| size_t hostset_deranged_string(hostset_t set, size_t n, char *buf) |
| { |
| return hostlist_deranged_string(set->hl, n, buf); |
| } |
| |
| char * hostset_nth(hostset_t set, int n) |
| { |
| return hostlist_nth(set->hl, n); |
| } |
| |
| int hostset_find(hostset_t set, const char *hostname) |
| { |
| return hostlist_find(set->hl, hostname); |
| } |
| |
| #if TEST_MAIN |
| |
| int hostlist_nranges(hostlist_t hl) |
| { |
| return hl->nranges; |
| } |
| |
| int hostset_nranges(hostset_t set) |
| { |
| return set->hl->nranges; |
| } |
| |
| /* test iterator functionality on the list of hosts represented |
| * by list |
| */ |
| int iterator_test(char *list) |
| { |
| int j; |
| char buf[MAXHOSTRANGELEN+1]; |
| hostlist_t hl = hostlist_create(list); |
| hostset_t set = hostset_create(list); |
| |
| hostlist_iterator_t i = hostlist_iterator_create(hl); |
| hostlist_iterator_t seti = hostset_iterator_create(set); |
| hostlist_iterator_t i2 = hostlist_iterator_create(hl); |
| char *host; |
| |
| hostlist_ranged_string(hl, MAXHOSTRANGELEN, buf); |
| printf("iterator_test: hl = `%s' passed in `%s'\n", buf, list); |
| host = hostlist_next(i); |
| printf("first host in list hl = `%s'\n", host); |
| free(host); |
| |
| /* forge ahead three hosts with i2 */ |
| for (j = 0; j < 4; j++) { |
| host = hostlist_next(i2); |
| free(host); |
| } |
| |
| host = hostlist_shift(hl); |
| printf("result of shift(hl) = `%s'\n", host); |
| free(host); |
| host = hostlist_next(i); |
| printf("next host in list hl = `%s'\n", host); |
| free(host); |
| host = hostlist_next(i2); |
| printf("next host for i2 = `%s'\n", host); |
| free(host); |
| |
| hostlist_iterator_destroy(i); |
| |
| hostlist_destroy(hl); |
| hostset_destroy(set); |
| return 1; |
| } |
| |
| int main(int ac, char **av) |
| { |
| char buf[1024000]; |
| int i; |
| char *str; |
| |
| hostlist_t hl1, hl2, hl3; |
| hostset_t set, set1; |
| hostlist_iterator_t iter, iter2; |
| |
| if (ac < 2) |
| printf("Recommended usage: %s [hostlist]\n\n", av[0]); |
| |
| if (!(hl1 = hostlist_create(ac > 1 ? av[1] : NULL))) { |
| perror("hostlist_create"); |
| exit(1); |
| } |
| |
| /* build a temporary hostlist, remove duplicates, |
| * use it to make the hostset */ |
| if (!(hl2 = hostlist_create(ac > 1 ? av[1] : NULL))) { |
| perror("hostlist_create"); |
| exit(1); |
| } |
| hostlist_uniq(hl2); |
| hostlist_ranged_string(hl2, 102400, buf); |
| if (!(set = hostset_create(buf))) { |
| perror("hostset_create"); |
| exit(1); |
| } |
| hostlist_destroy(hl2); |
| |
| hl3 = hostlist_create("f[0-5]"); |
| hostlist_delete(hl3, "f[1-3]"); |
| hostlist_ranged_string(hl3, 102400, buf); |
| printf("after delete = `%s'\n", buf); |
| hostlist_destroy(hl3); |
| |
| hl3 = hostlist_create("bg[012x123]"); |
| hostlist_ranged_string(hl3, 102400, buf); |
| printf("bg[012x123] == `%s'\n", buf); |
| i = hostlist_count(hl3); |
| assert(i == 8); |
| hostlist_ranged_string(hl3, 102400, buf); |
| hostlist_destroy(hl3); |
| |
| for (i = 2; i < ac; i++) { |
| hostlist_push(hl1, av[i]); |
| hostset_insert(set, av[i]); |
| } |
| |
| hostlist_ranged_string(hl1, 102400, buf); |
| printf("ranged = `%s'\n", buf); |
| |
| iterator_test(buf); |
| |
| hostlist_deranged_string(hl1, 10240, buf); |
| printf("deranged = `%s'\n", buf); |
| |
| hostset_ranged_string(set, 1024, buf); |
| printf("hostset = `%s'\n", buf); |
| |
| hostlist_sort(hl1); |
| hostlist_ranged_string(hl1, 10240, buf); |
| printf("sorted = `%s'\n", buf); |
| |
| hostlist_uniq(hl1); |
| hostlist_ranged_string(hl1, 10240, buf); |
| printf("uniqed = `%s'\n", buf); |
| |
| hl2 = hostlist_copy(hl1); |
| printf("pop_range: "); |
| while ((str = hostlist_pop_range(hl2))) { |
| printf("`%s' ", str); |
| free(str); |
| } |
| hostlist_destroy(hl2); |
| printf("\n"); |
| |
| hl2 = hostlist_copy(hl1); |
| printf("shift_range: "); |
| while ((str = hostlist_shift_range(hl2))) { |
| printf("`%s' ", str); |
| free(str); |
| } |
| hostlist_destroy(hl2); |
| printf("\n"); |
| |
| iter = hostset_iterator_create(set); |
| iter2 = hostset_iterator_create(set); |
| hostlist_iterator_destroy(iter2); |
| |
| printf("next: "); |
| while ((str = hostlist_next(iter))) { |
| printf("`%s' ", str); |
| free(str); |
| } |
| printf("\n"); |
| |
| hostlist_iterator_reset(iter); |
| printf("next_range: "); |
| while ((str = hostlist_next_range(iter))) { |
| printf("`%s' ", str); |
| free(str); |
| } |
| printf("\n"); |
| |
| printf("nranges = %d\n", hostset_nranges(set)); |
| |
| hostset_ranged_string(set, 1024, buf); |
| printf("set = %s\n", buf); |
| |
| hostset_destroy(set); |
| hostlist_destroy(hl1); |
| return 0; |
| } |
| |
| #endif /* TEST_MAIN */ |
| |
| /* |
| * vi: tabstop=4 shiftwidth=4 expandtab |
| */ |