PageRenderTime 64ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/common/hostlist.c

https://github.com/cfenoy/slurm
C | 3862 lines | 2680 code | 644 blank | 538 comment | 726 complexity | 7b88cc68a24d92279e53c853d9951f8f MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0

Large files files are truncated, but you can click here to view the full file

  1. /*****************************************************************************\
  2. * $Id$
  3. *****************************************************************************
  4. * $LSDId: hostlist.c,v 1.14 2003/10/14 20:11:54 grondo Exp $
  5. *****************************************************************************
  6. * Copyright (C) 2002 The Regents of the University of California.
  7. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  8. * Written by Mark Grondona <mgrondona@llnl.gov>
  9. * CODE-OCEC-09-009. All rights reserved.
  10. *
  11. * This file is part of SLURM, a resource management program.
  12. * For details, see <http://www.schedmd.com/slurmdocs/>.
  13. * Please also read the included file: DISCLAIMER.
  14. *
  15. * SLURM is free software; you can redistribute it and/or modify it under
  16. * the terms of the GNU General Public License as published by the Free
  17. * Software Foundation; either version 2 of the License, or (at your option)
  18. * any later version.
  19. *
  20. * In addition, as a special exception, the copyright holders give permission
  21. * to link the code of portions of this program with the OpenSSL library under
  22. * certain conditions as described in each individual source file, and
  23. * distribute linked combinations including the two. You must obey the GNU
  24. * General Public License in all respects for all of the code used other than
  25. * OpenSSL. If you modify file(s) with this exception, you may extend this
  26. * exception to your version of the file(s), but you are not obligated to do
  27. * so. If you do not wish to do so, delete this exception statement from your
  28. * version. If you delete this exception statement from all source files in
  29. * the program, then also delete it here.
  30. *
  31. * SLURM is distributed in the hope that it will be useful, but WITHOUT ANY
  32. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  33. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  34. * details.
  35. *
  36. * You should have received a copy of the GNU General Public License along
  37. * with SLURM; if not, write to the Free Software Foundation, Inc.,
  38. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  39. \*****************************************************************************/
  40. #ifdef HAVE_CONFIG_H
  41. # include "config.h"
  42. # if HAVE_INTTYPES_H
  43. # include <inttypes.h>
  44. # else
  45. # if HAVE_STDINT_H
  46. # include <stdint.h>
  47. # endif
  48. # endif /* HAVE_INTTYPES_H */
  49. # if HAVE_STRING_H
  50. # include <string.h>
  51. # endif
  52. # if HAVE_PTHREAD_H
  53. # include <pthread.h>
  54. # endif
  55. #else /* !HAVE_CONFIG_H */
  56. # include <inttypes.h>
  57. # include <string.h>
  58. # include <pthread.h>
  59. #endif /* HAVE_CONFIG_H */
  60. #include <stdio.h>
  61. #include <stdlib.h>
  62. #include <stdarg.h>
  63. #include <assert.h>
  64. #include <errno.h>
  65. #include <ctype.h>
  66. #include <sys/param.h>
  67. #include <unistd.h>
  68. #include <slurm/slurmdb.h>
  69. #include "src/common/hostlist.h"
  70. #include "src/common/log.h"
  71. #include "src/common/macros.h"
  72. #include "src/common/xmalloc.h"
  73. #include "src/common/timers.h"
  74. #include "src/common/xassert.h"
  75. #include "src/common/working_cluster.h"
  76. /*
  77. * Define slurm-specific aliases for use by plugins, see slurm_xlator.h
  78. * for details.
  79. */
  80. strong_alias(hostlist_create_dims, slurm_hostlist_create_dims);
  81. strong_alias(hostlist_create, slurm_hostlist_create);
  82. strong_alias(hostlist_copy, slurm_hostlist_copy);
  83. strong_alias(hostlist_count, slurm_hostlist_count);
  84. strong_alias(hostlist_delete, slurm_hostlist_delete);
  85. strong_alias(hostlist_delete_host, slurm_hostlist_delete_host);
  86. strong_alias(hostlist_delete_nth, slurm_hostlist_delete_nth);
  87. strong_alias(hostlist_deranged_string_dims,
  88. slurm_hostlist_deranged_string_dims);
  89. strong_alias(hostlist_deranged_string, slurm_hostlist_deranged_string);
  90. strong_alias(hostlist_deranged_string_malloc,
  91. slurm_hostlist_deranged_string_malloc);
  92. strong_alias(hostlist_deranged_string_xmalloc_dims,
  93. slurm_hostlist_deranged_string_xmalloc_dims);
  94. strong_alias(hostlist_deranged_string_xmalloc,
  95. slurm_hostlist_deranged_string_xmalloc);
  96. strong_alias(hostlist_destroy, slurm_hostlist_destroy);
  97. strong_alias(hostlist_find, slurm_hostlist_find);
  98. strong_alias(hostlist_iterator_create, slurm_hostlist_iterator_create);
  99. strong_alias(hostlist_iterator_destroy, slurm_hostlist_iterator_destroy);
  100. strong_alias(hostlist_iterator_reset, slurm_hostlist_iterator_reset);
  101. strong_alias(hostlist_next, slurm_hostlist_next);
  102. strong_alias(hostlist_next_range, slurm_hostlist_next_range);
  103. strong_alias(hostlist_nth, slurm_hostlist_nth);
  104. strong_alias(hostlist_pop, slurm_hostlist_pop);
  105. strong_alias(hostlist_pop_range, slurm_hostlist_pop_range);
  106. strong_alias(hostlist_push, slurm_hostlist_push);
  107. strong_alias(hostlist_push_host_dims, slurm_hostlist_push_host_dims);
  108. strong_alias(hostlist_push_host, slurm_hostlist_push_host);
  109. strong_alias(hostlist_push_list, slurm_hostlist_push_list);
  110. strong_alias(hostlist_ranged_string_dims,
  111. slurm_hostlist_ranged_string_dims);
  112. strong_alias(hostlist_ranged_string, slurm_hostlist_ranged_string);
  113. strong_alias(hostlist_ranged_string_malloc,
  114. slurm_hostlist_ranged_string_malloc);
  115. strong_alias(hostlist_ranged_string_xmalloc_dims,
  116. slurm_hostlist_ranged_string_xmalloc_dims);
  117. strong_alias(hostlist_ranged_string_xmalloc,
  118. slurm_hostlist_ranged_string_xmalloc);
  119. strong_alias(hostlist_remove, slurm_hostlist_remove);
  120. strong_alias(hostlist_shift, slurm_hostlist_shift);
  121. strong_alias(hostlist_shift_range, slurm_hostlist_shift_range);
  122. strong_alias(hostlist_sort, slurm_hostlist_soft);
  123. strong_alias(hostlist_uniq, slurm_hostlist_uniq);
  124. strong_alias(hostset_copy, slurm_hostset_copy);
  125. strong_alias(hostset_count, slurm_hostset_count);
  126. strong_alias(hostset_create, slurm_hostset_create);
  127. strong_alias(hostset_delete, slurm_hostset_delete);
  128. strong_alias(hostset_destroy, slurm_hostset_destroy);
  129. strong_alias(hostset_find, slurm_hostset_find);
  130. strong_alias(hostset_insert, slurm_hostset_insert);
  131. strong_alias(hostset_shift, slurm_hostset_shift);
  132. strong_alias(hostset_shift_range, slurm_hostset_shift_range);
  133. strong_alias(hostset_within, slurm_hostset_within);
  134. strong_alias(hostset_nth, slurm_hostset_nth);
  135. /*
  136. * lsd_fatal_error : fatal error macro
  137. */
  138. #ifdef WITH_LSD_FATAL_ERROR_FUNC
  139. # undef lsd_fatal_error
  140. extern void lsd_fatal_error(char *file, int line, char *mesg);
  141. #else /* !WITH_LSD_FATAL_ERROR_FUNC */
  142. # ifndef lsd_fatal_error
  143. # define lsd_fatal_error(file, line, mesg) \
  144. do { \
  145. fprintf(stderr, "ERROR: [%s:%d] %s: %s\n", \
  146. file, line, mesg, strerror(errno)); \
  147. } while (0)
  148. # endif /* !lsd_fatal_error */
  149. #endif /* !WITH_LSD_FATAL_ERROR_FUNC */
  150. /*
  151. * lsd_nonmem_error
  152. */
  153. #ifdef WITH_LSD_NOMEM_ERROR_FUNC
  154. # undef lsd_nomem_error
  155. extern void * lsd_nomem_error(char *file, int line, char *mesg);
  156. #else /* !WITH_LSD_NOMEM_ERROR_FUNC */
  157. # ifndef lsd_nomem_error
  158. # define lsd_nomem_error(file, line, mesg) (NULL)
  159. # endif /* !lsd_nomem_error */
  160. #endif /* !WITH_LSD_NOMEM_ERROR_FUNC */
  161. /*
  162. * OOM helper function
  163. * Automatically call lsd_nomem_error with appropriate args
  164. * and set errno to ENOMEM
  165. */
  166. #define out_of_memory(mesg) \
  167. do { \
  168. fatal("malloc failure"); \
  169. errno = ENOMEM; \
  170. return(lsd_nomem_error(__FILE__, __LINE__, mesg)); \
  171. } while (0)
  172. /*
  173. * Some constants and tunables:
  174. */
  175. /* number of elements to allocate when extending the hostlist array */
  176. #define HOSTLIST_CHUNK 16
  177. /* max host range: anything larger will be assumed to be an error */
  178. #define MAX_RANGE (64*1024) /* 64K Hosts */
  179. /* max number of ranges that will be processed between brackets */
  180. #define MAX_RANGES (12*1024) /* 12K Ranges */
  181. /* size of internal hostname buffer (+ some slop), hostnames will probably
  182. * be truncated if longer than MAXHOSTNAMELEN */
  183. #ifndef MAXHOSTNAMELEN
  184. #define MAXHOSTNAMELEN 64
  185. #endif
  186. /* ----[ Internal Data Structures ]---- */
  187. /* hostname type: A convenience structure used in parsing single hostnames */
  188. struct hostname_components {
  189. char *hostname; /* cache of initialized hostname */
  190. char *prefix; /* hostname prefix */
  191. unsigned long num; /* numeric suffix */
  192. /* string representation of numeric suffix
  193. * points into `hostname' */
  194. char *suffix;
  195. };
  196. typedef struct hostname_components *hostname_t;
  197. /* hostrange type: A single prefix with `hi' and `lo' numeric suffix values */
  198. struct hostrange_components {
  199. char *prefix; /* alphanumeric prefix: */
  200. /* beginning (lo) and end (hi) of suffix range */
  201. unsigned long lo, hi;
  202. /* width of numeric output format
  203. * (pad with zeros up to this width) */
  204. int width;
  205. /* If singlehost is 1, `lo' and `hi' are invalid */
  206. unsigned singlehost:1;
  207. };
  208. typedef struct hostrange_components *hostrange_t;
  209. /* The hostlist type: An array based list of hostrange_t's */
  210. struct hostlist {
  211. #ifndef NDEBUG
  212. #define HOSTLIST_MAGIC 57005
  213. int magic;
  214. #endif
  215. #if WITH_PTHREADS
  216. pthread_mutex_t mutex;
  217. #endif /* WITH_PTHREADS */
  218. /* current number of elements available in array */
  219. int size;
  220. /* current number of ranges stored in array */
  221. int nranges;
  222. /* current number of hosts stored in hostlist */
  223. int nhosts;
  224. /* pointer to hostrange array */
  225. hostrange_t *hr;
  226. /* list of iterators */
  227. struct hostlist_iterator *ilist;
  228. };
  229. /* a hostset is a wrapper around a hostlist */
  230. struct hostset {
  231. hostlist_t hl;
  232. };
  233. struct hostlist_iterator {
  234. #ifndef NDEBUG
  235. int magic;
  236. #endif
  237. /* hostlist we are traversing */
  238. hostlist_t hl;
  239. /* current index of iterator in hl->hr[] */
  240. int idx;
  241. /* current hostrange object in list hl, i.e. hl->hr[idx] */
  242. hostrange_t hr;
  243. /* current depth we've traversed into range hr */
  244. int depth;
  245. /* next ptr for lists of iterators */
  246. struct hostlist_iterator *next;
  247. };
  248. struct _range {
  249. unsigned long lo, hi;
  250. int width;
  251. };
  252. /* ---- ---- */
  253. /* Multi-dimension system stuff here */
  254. char *alpha_num = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  255. /* logic for block node description */
  256. /* to speed things up we will do some calculations once to avoid
  257. * having to do it multiple times. We need to calculate the size of
  258. * the maximum sized array for each dimension. This way we can be
  259. * prepared for any size coming in.
  260. */
  261. static bool grid[HIGHEST_BASE*HIGHEST_BASE*HIGHEST_BASE*HIGHEST_BASE*HIGHEST_BASE];
  262. static int grid_start[HIGHEST_DIMENSIONS];
  263. static int grid_end[HIGHEST_DIMENSIONS];
  264. static int offset[HIGHEST_DIMENSIONS];
  265. static int dim_grid_size = -1;
  266. /* used to protect the above grid, grid_start, and grid_end. */
  267. static pthread_mutex_t multi_dim_lock = PTHREAD_MUTEX_INITIALIZER;
  268. static int _tell_if_used(int dim, int curr,
  269. int *start,
  270. int *end,
  271. int *last,
  272. int *found, int dims);
  273. static int _get_next_box(int *start, int *end, int dims);
  274. static int _get_boxes(char *buf, int max_len, int dims, int brackets);
  275. static void _set_box_in_grid(int dim, int curr,
  276. int *start,
  277. int *end,
  278. bool value, int dims);
  279. static int _add_box_ranges(int dim, int curr,
  280. int *start,
  281. int *end,
  282. int *pos,
  283. struct _range *ranges,
  284. int len, int *count, int dims);
  285. static void _set_min_max_of_grid(int dim, int curr,
  286. int *start,
  287. int *end,
  288. int *min,
  289. int *max,
  290. int *pos, int dims);
  291. static void _set_grid(unsigned long start, unsigned long end, int dims);
  292. static bool _test_box_in_grid(int dim, int curr,
  293. int *start,
  294. int *end, int dims);
  295. static bool _test_box(int *start, int *end, int dims);
  296. /* ------[ static function prototypes ]------ */
  297. static void _error(char *file, int line, char *mesg, ...)
  298. __attribute__ ((format (printf, 3, 4)));
  299. static char * _next_tok(char *, char **);
  300. static int _zero_padded(unsigned long, int);
  301. static int _width_equiv(unsigned long, int *, unsigned long, int *);
  302. static int host_prefix_end(const char *, int dims);
  303. static hostname_t hostname_create(const char *);
  304. static void hostname_destroy(hostname_t);
  305. static int hostname_suffix_is_valid(hostname_t);
  306. static int hostname_suffix_width(hostname_t);
  307. static hostrange_t hostrange_new(void);
  308. static hostrange_t hostrange_create_single(const char *);
  309. static hostrange_t hostrange_create(char *, unsigned long, unsigned long,
  310. int);
  311. static unsigned long hostrange_count(hostrange_t);
  312. static hostrange_t hostrange_copy(hostrange_t);
  313. static void hostrange_destroy(hostrange_t);
  314. static hostrange_t hostrange_delete_host(hostrange_t, unsigned long);
  315. static int hostrange_cmp(hostrange_t, hostrange_t);
  316. static int hostrange_prefix_cmp(hostrange_t, hostrange_t);
  317. static int hostrange_within_range(hostrange_t, hostrange_t);
  318. static int hostrange_width_combine(hostrange_t, hostrange_t);
  319. static int hostrange_empty(hostrange_t);
  320. static char * hostrange_pop(hostrange_t);
  321. static char * hostrange_shift(hostrange_t);
  322. static int hostrange_join(hostrange_t, hostrange_t);
  323. static hostrange_t hostrange_intersect(hostrange_t, hostrange_t);
  324. static int hostrange_hn_within(hostrange_t, hostname_t);
  325. static size_t hostrange_to_string(hostrange_t hr, size_t, char *,
  326. char *, int);
  327. static size_t hostrange_numstr(hostrange_t, size_t, char *, int);
  328. static hostlist_t hostlist_new(void);
  329. static hostlist_t _hostlist_create_bracketed(const char *, char *,
  330. char *, int);
  331. static int hostlist_resize(hostlist_t, size_t);
  332. static int hostlist_expand(hostlist_t);
  333. static int hostlist_push_range(hostlist_t, hostrange_t);
  334. static int hostlist_push_hr(hostlist_t, char *, unsigned long,
  335. unsigned long, int);
  336. static int hostlist_insert_range(hostlist_t, hostrange_t, int);
  337. static void hostlist_delete_range(hostlist_t, int n);
  338. static void hostlist_coalesce(hostlist_t hl);
  339. static void hostlist_collapse(hostlist_t hl);
  340. static hostlist_t _hostlist_create(const char *, char *, char *, int);
  341. static void hostlist_shift_iterators(hostlist_t, int, int, int);
  342. static int _attempt_range_join(hostlist_t, int);
  343. static int _is_bracket_needed(hostlist_t, int);
  344. static hostlist_iterator_t hostlist_iterator_new(void);
  345. static void _iterator_advance(hostlist_iterator_t);
  346. static void _iterator_advance_range(hostlist_iterator_t);
  347. static int hostset_find_host(hostset_t, const char *);
  348. /* ------[ macros ]------ */
  349. #ifdef WITH_PTHREADS
  350. # define mutex_init(mutex) \
  351. do { \
  352. int e = pthread_mutex_init(mutex, NULL); \
  353. if (e) { \
  354. errno = e; \
  355. lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex init:"); \
  356. abort(); \
  357. } \
  358. } while (0)
  359. # define mutex_lock(mutex) \
  360. do { \
  361. int e = pthread_mutex_lock(mutex); \
  362. if (e) { \
  363. errno = e; \
  364. lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex lock:"); \
  365. abort(); \
  366. } \
  367. } while (0)
  368. # define mutex_unlock(mutex) \
  369. do { \
  370. int e = pthread_mutex_unlock(mutex); \
  371. if (e) { \
  372. errno = e; \
  373. lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex unlock:"); \
  374. abort(); \
  375. } \
  376. } while (0)
  377. # define mutex_destroy(mutex) \
  378. do { \
  379. int e = pthread_mutex_destroy(mutex); \
  380. if (e) { \
  381. errno = e; \
  382. lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex destroy:"); \
  383. abort(); \
  384. } \
  385. } while (0)
  386. #else /* !WITH_PTHREADS */
  387. # define mutex_init(mutex)
  388. # define mutex_lock(mutex)
  389. # define mutex_unlock(mutex)
  390. # define mutex_destroy(mutex)
  391. #endif /* WITH_PTHREADS */
  392. #define LOCK_HOSTLIST(_hl) \
  393. do { \
  394. assert(_hl != NULL); \
  395. mutex_lock(&(_hl)->mutex); \
  396. assert((_hl)->magic == HOSTLIST_MAGIC); \
  397. } while (0)
  398. #define UNLOCK_HOSTLIST(_hl) \
  399. do { \
  400. mutex_unlock(&(_hl)->mutex); \
  401. } while (0)
  402. #define seterrno_ret(_errno, _rc) \
  403. do { \
  404. errno = _errno; \
  405. return _rc; \
  406. } while (0)
  407. /* ------[ Function Definitions ]------ */
  408. /* ----[ general utility functions ]---- */
  409. /*
  410. * Varargs capable error reporting via lsd_fatal_error()
  411. */
  412. static void _error(char *file, int line, char *msg, ...)
  413. {
  414. va_list ap;
  415. char buf[1024];
  416. int len = 0;
  417. va_start(ap, msg);
  418. len = vsnprintf(buf, 1024, msg, ap);
  419. if ((len < 0) || (len > 1024))
  420. buf[1023] = '\0';
  421. lsd_fatal_error(file, line, buf);
  422. va_end(ap);
  423. return;
  424. }
  425. /*
  426. * Helper function for host list string parsing routines
  427. * Returns a pointer to the next token; additionally advance *str
  428. * to the next separator.
  429. *
  430. * next_tok was taken directly from pdsh courtesy of Jim Garlick.
  431. * (with modifications to support bracketed hostlists, i.e.:
  432. * xxx[xx,xx,xx] is a single token)
  433. *
  434. */
  435. static char * _next_tok(char *sep, char **str)
  436. {
  437. char *tok;
  438. /* push str past any leading separators */
  439. while ((**str != '\0') && (strchr(sep, **str) != NULL))
  440. (*str)++;
  441. if (**str == '\0')
  442. return NULL;
  443. /* assign token ptr */
  444. tok = *str;
  445. /* push str past token and leave pointing to first separator */
  446. while ((**str != '\0') && (strchr(sep, **str) == NULL))
  447. (*str)++;
  448. /* if _single_ opening bracket exists b/w tok and str,
  449. * push str past first closing bracket */
  450. if ((memchr(tok, '[', *str - tok) != NULL) &&
  451. (memchr(tok, ']', *str - tok) == NULL)) {
  452. char *q = strchr(*str, ']');
  453. if (q && (memchr(*str, '[', q - *str) == NULL))
  454. *str = ++q;
  455. /* push str past token and leave pointing to next separator */
  456. while ((**str != '\0') && (strchr(sep, **str) == NULL))
  457. (*str)++;
  458. /* if _second_ opening bracket exists b/w tok and str,
  459. * push str past second closing bracket */
  460. if ((**str != '\0') && q &&
  461. (memchr(tok, '[', *str - q) != NULL) &&
  462. (memchr(tok, ']', *str - q) == NULL)) {
  463. q = strchr(*str, ']');
  464. if (q && (memchr(*str, '[', q - *str) == NULL))
  465. *str = q + 1;
  466. }
  467. }
  468. /* nullify consecutive separators and push str beyond them */
  469. while ((**str != '\0') && (strchr(sep, **str) != '\0'))
  470. *(*str)++ = '\0';
  471. return tok;
  472. }
  473. /*
  474. * return the number of zeros needed to pad "num" to "width"
  475. */
  476. static int _zero_padded(unsigned long num, int width)
  477. {
  478. int n = 1;
  479. while (num /= 10L)
  480. n++;
  481. return (width > n) ? (width - n) : 0;
  482. }
  483. /*
  484. * test whether two format `width' parameters are "equivalent"
  485. * The width arguments "wn" and "wm" for integers "n" and "m"
  486. * are equivalent if:
  487. *
  488. * o wn == wm OR
  489. *
  490. * o applying the same format width (either wn or wm) to both of
  491. * 'n' and 'm' will not change the zero padding of *either* 'm' nor 'n'.
  492. *
  493. * If this function returns 1 (or true), the appropriate width value
  494. * (either 'wm' or 'wn') will have been adjusted such that both format
  495. * widths are equivalent.
  496. */
  497. static int _width_equiv(unsigned long n, int *wn, unsigned long m, int *wm)
  498. {
  499. int npad, nmpad, mpad, mnpad;
  500. if (*wn == *wm)
  501. return 1;
  502. npad = _zero_padded(n, *wn);
  503. nmpad = _zero_padded(n, *wm);
  504. mpad = _zero_padded(m, *wm);
  505. mnpad = _zero_padded(m, *wn);
  506. if ((npad != nmpad) && (mpad != mnpad))
  507. return 0;
  508. else if (npad != nmpad) /* mpad == mnpad: adjust wm */
  509. *wm = *wn;
  510. else /* npad == nmpad: adjust wn */
  511. *wn = *wm;
  512. return 1;
  513. }
  514. /* ----[ hostname_t functions ]---- */
  515. /*
  516. * return the location of the last char in the hostname prefix
  517. */
  518. static int host_prefix_end(const char *hostname, int dims)
  519. {
  520. int idx;
  521. assert(hostname != NULL);
  522. if (!dims)
  523. dims = slurmdb_setup_cluster_name_dims();
  524. idx = strlen(hostname) - 1;
  525. if (dims > 1) {
  526. while ((idx >= 0) &&
  527. (isdigit((int)hostname[idx]) ||
  528. isupper((int)hostname[idx])))
  529. idx--;
  530. } else {
  531. while ((idx >= 0) && isdigit((int)hostname[idx]))
  532. idx--;
  533. }
  534. return idx;
  535. }
  536. static hostname_t hostname_create_dims(const char *hostname, int dims)
  537. {
  538. hostname_t hn = NULL;
  539. char *p = '\0';
  540. int idx = 0;
  541. int hostlist_base;
  542. assert(hostname != NULL);
  543. if (!dims)
  544. dims = slurmdb_setup_cluster_name_dims();
  545. hostlist_base = hostlist_get_base(dims);
  546. if (!(hn = (hostname_t) malloc(sizeof(*hn))))
  547. out_of_memory("hostname create");
  548. idx = host_prefix_end(hostname, dims);
  549. if (!(hn->hostname = strdup(hostname))) {
  550. free(hn);
  551. out_of_memory("hostname create");
  552. }
  553. hn->num = 0;
  554. hn->prefix = NULL;
  555. hn->suffix = NULL;
  556. if (idx == (strlen(hostname) - 1)) {
  557. if ((hn->prefix = strdup(hostname)) == NULL) {
  558. hostname_destroy(hn);
  559. out_of_memory("hostname prefix create");
  560. }
  561. return hn;
  562. }
  563. hn->suffix = hn->hostname + idx + 1;
  564. if((dims > 1) && (strlen(hn->suffix) != dims))
  565. hostlist_base = 10;
  566. hn->num = strtoul(hn->suffix, &p, hostlist_base);
  567. if (*p == '\0') {
  568. if (!(hn->prefix = malloc((idx + 2) * sizeof(char)))) {
  569. hostname_destroy(hn);
  570. out_of_memory("hostname prefix create");
  571. }
  572. memcpy(hn->prefix, hostname, idx + 1);
  573. hn->prefix[idx + 1] = '\0';
  574. } else {
  575. if (!(hn->prefix = strdup(hostname))) {
  576. hostname_destroy(hn);
  577. out_of_memory("hostname prefix create");
  578. }
  579. hn->suffix = NULL;
  580. }
  581. return hn;
  582. }
  583. /*
  584. * create a hostname_t object from a string hostname
  585. */
  586. static hostname_t hostname_create(const char *hostname)
  587. {
  588. int dims = slurmdb_setup_cluster_name_dims();
  589. return hostname_create_dims(hostname, dims);
  590. }
  591. /* free a hostname object
  592. */
  593. static void hostname_destroy(hostname_t hn)
  594. {
  595. if (hn == NULL)
  596. return;
  597. hn->suffix = NULL;
  598. if (hn->hostname)
  599. free(hn->hostname);
  600. if (hn->prefix)
  601. free(hn->prefix);
  602. free(hn);
  603. }
  604. /* return true if the hostname has a valid numeric suffix
  605. */
  606. static int hostname_suffix_is_valid(hostname_t hn)
  607. {
  608. if (!hn)
  609. return false;
  610. return hn->suffix != NULL;
  611. }
  612. /* return the width (in characters) of the numeric part of the hostname
  613. */
  614. static int hostname_suffix_width(hostname_t hn)
  615. {
  616. if (!hn)
  617. return -1;
  618. assert(hn->suffix != NULL);
  619. return (int) strlen(hn->suffix);
  620. }
  621. /* ----[ hostrange_t functions ]---- */
  622. /* allocate a new hostrange object
  623. */
  624. static hostrange_t hostrange_new(void)
  625. {
  626. hostrange_t new = (hostrange_t) malloc(sizeof(*new));
  627. if (!new)
  628. out_of_memory("hostrange create");
  629. return new;
  630. }
  631. /* Create a hostrange_t containing a single host without a valid suffix
  632. * hr->prefix will represent the entire hostname.
  633. */
  634. static hostrange_t hostrange_create_single(const char *prefix)
  635. {
  636. hostrange_t new;
  637. assert(prefix != NULL);
  638. if ((new = hostrange_new()) == NULL)
  639. goto error1;
  640. if ((new->prefix = strdup(prefix)) == NULL)
  641. goto error2;
  642. new->singlehost = 1;
  643. new->lo = 0L;
  644. new->hi = 0L;
  645. new->width = 0;
  646. return new;
  647. error2:
  648. free(new);
  649. error1:
  650. out_of_memory("hostrange create single");
  651. }
  652. /* Create a hostrange object with a prefix, hi, lo, and format width
  653. */
  654. static hostrange_t
  655. hostrange_create(char *prefix, unsigned long lo, unsigned long hi, int width)
  656. {
  657. hostrange_t new;
  658. assert(prefix != NULL);
  659. if ((new = hostrange_new()) == NULL)
  660. goto error1;
  661. if ((new->prefix = strdup(prefix)) == NULL)
  662. goto error2;
  663. new->lo = lo;
  664. new->hi = hi;
  665. new->width = width;
  666. new->singlehost = 0;
  667. return new;
  668. error2:
  669. free(new);
  670. error1:
  671. out_of_memory("hostrange create");
  672. }
  673. /* Return the number of hosts stored in the hostrange object
  674. */
  675. static unsigned long hostrange_count(hostrange_t hr)
  676. {
  677. assert(hr != NULL);
  678. if (hr->singlehost)
  679. return 1;
  680. else
  681. return hr->hi - hr->lo + 1;
  682. }
  683. /* Copy a hostrange object
  684. */
  685. static hostrange_t hostrange_copy(hostrange_t hr)
  686. {
  687. assert(hr != NULL);
  688. if (hr->singlehost)
  689. return hostrange_create_single(hr->prefix);
  690. else
  691. return hostrange_create(hr->prefix, hr->lo, hr->hi,
  692. hr->width);
  693. }
  694. /* free memory allocated by the hostrange object
  695. */
  696. static void hostrange_destroy(hostrange_t hr)
  697. {
  698. if (hr == NULL)
  699. return;
  700. if (hr->prefix)
  701. free(hr->prefix);
  702. free(hr);
  703. }
  704. /* hostrange_delete_host() deletes a specific host from the range.
  705. * If the range is split into two, the greater range is returned,
  706. * and `hi' of the lesser range is adjusted accordingly. If the
  707. * highest or lowest host is deleted from a range, NULL is returned
  708. * and the hostrange hr is adjusted properly.
  709. */
  710. static hostrange_t hostrange_delete_host(hostrange_t hr, unsigned long n)
  711. {
  712. hostrange_t new = NULL;
  713. assert(hr != NULL);
  714. assert(n >= hr->lo && n <= hr->hi);
  715. if (n == hr->lo)
  716. hr->lo++;
  717. else if (n == hr->hi)
  718. hr->hi--;
  719. else {
  720. if (!(new = hostrange_copy(hr)))
  721. out_of_memory("hostrange copy");
  722. hr->hi = n - 1;
  723. new->lo = n + 1;
  724. }
  725. return new;
  726. }
  727. /* hostrange_cmp() is used to sort hostrange objects. It will
  728. * sort based on the following (in order):
  729. * o result of strcmp on prefixes
  730. * o if widths are compatible, then:
  731. * sort based on lowest suffix in range
  732. * else
  733. * sort based on width */
  734. static int hostrange_cmp(hostrange_t h1, hostrange_t h2)
  735. {
  736. int retval;
  737. assert(h1 != NULL);
  738. assert(h2 != NULL);
  739. if ((retval = hostrange_prefix_cmp(h1, h2)) == 0)
  740. retval = hostrange_width_combine(h1, h2) ?
  741. h1->lo - h2->lo : h1->width - h2->width;
  742. return retval;
  743. }
  744. /* compare the prefixes of two hostrange objects.
  745. * returns:
  746. * < 0 if h1 prefix is less than h2 OR h2 == NULL.
  747. *
  748. * 0 if h1's prefix and h2's prefix match,
  749. * UNLESS, either h1 or h2 (NOT both) do not have a valid suffix.
  750. *
  751. * > 0 if h1's prefix is greater than h2's OR h1 == NULL. */
  752. static int hostrange_prefix_cmp(hostrange_t h1, hostrange_t h2)
  753. {
  754. int retval;
  755. if (h1 == NULL)
  756. return 1;
  757. if (h2 == NULL)
  758. return -1;
  759. retval = strcmp(h1->prefix, h2->prefix);
  760. return retval == 0 ? h2->singlehost - h1->singlehost : retval;
  761. }
  762. /* returns true if h1 and h2 would be included in the same bracketed hostlist.
  763. * h1 and h2 will be in the same bracketed list iff:
  764. *
  765. * 1. h1 and h2 have same prefix
  766. * 2. neither h1 nor h2 are singlet hosts (i.e. invalid suffix)
  767. *
  768. * (XXX: Should incompatible widths be placed in the same bracketed list?
  769. * There's no good reason not to, except maybe aesthetics)
  770. */
  771. static int hostrange_within_range(hostrange_t h1, hostrange_t h2)
  772. {
  773. if (hostrange_prefix_cmp(h1, h2) == 0)
  774. return h1->singlehost || h2->singlehost ? 0 : 1;
  775. else
  776. return 0;
  777. }
  778. /* compare two hostrange objects to determine if they are width
  779. * compatible, returns:
  780. * 1 if widths can safely be combined
  781. * 0 if widths cannot be safely combined
  782. */
  783. static int hostrange_width_combine(hostrange_t h0, hostrange_t h1)
  784. {
  785. assert(h0 != NULL);
  786. assert(h1 != NULL);
  787. return _width_equiv(h0->lo, &h0->width, h1->lo, &h1->width);
  788. }
  789. /* Return true if hostrange hr contains no hosts, i.e. hi < lo
  790. */
  791. static int hostrange_empty(hostrange_t hr)
  792. {
  793. assert(hr != NULL);
  794. return ((hr->hi < hr->lo) || (hr->hi == (unsigned long) -1));
  795. }
  796. /* return the string representation of the last host in hostrange hr
  797. * and remove that host from the range (i.e. decrement hi if possible)
  798. *
  799. * Returns NULL if malloc fails OR there are no more hosts left
  800. */
  801. static char *hostrange_pop(hostrange_t hr)
  802. {
  803. size_t size = 0;
  804. char *host = NULL;
  805. int dims = slurmdb_setup_cluster_name_dims();
  806. assert(hr != NULL);
  807. if (hr->singlehost) {
  808. hr->lo++; /* effectively set count == 0 */
  809. host = strdup(hr->prefix);
  810. if (host == NULL)
  811. out_of_memory("hostrange pop");
  812. } else if (hostrange_count(hr) > 0) {
  813. size = strlen(hr->prefix) + hr->width + 16;
  814. if (!(host = (char *) malloc(size * sizeof(char))))
  815. out_of_memory("hostrange pop");
  816. if ((dims > 1) && (hr->width == dims)) {
  817. int len = 0;
  818. int i2 = 0;
  819. int coord[dims];
  820. hostlist_parse_int_to_array(hr->hi, coord, dims, 0);
  821. len = snprintf(host, size, "%s", hr->prefix);
  822. if (len >= 0 && len + dims < size) {
  823. while (i2 < dims)
  824. host[len++] = alpha_num[coord[i2++]];
  825. host[len] = '\0';
  826. }
  827. hr->hi--;
  828. } else {
  829. snprintf(host, size, "%s%0*lu", hr->prefix,
  830. hr->width, hr->hi--);
  831. }
  832. }
  833. return host;
  834. }
  835. /* Same as hostrange_pop(), but remove host from start of range */
  836. static char *hostrange_shift(hostrange_t hr)
  837. {
  838. size_t size = 0;
  839. char *host = NULL;
  840. int dims = slurmdb_setup_cluster_name_dims();
  841. assert(hr != NULL);
  842. if (hr->singlehost) {
  843. hr->lo++;
  844. if (!(host = strdup(hr->prefix)))
  845. out_of_memory("hostrange shift");
  846. } else if (hostrange_count(hr) > 0) {
  847. size = strlen(hr->prefix) + hr->width + 16;
  848. if (!(host = (char *) malloc(size * sizeof(char))))
  849. out_of_memory("hostrange shift");
  850. if ((dims > 1) && (hr->width == dims)) {
  851. int len = 0;
  852. int i2 = 0;
  853. int coord[dims];
  854. hostlist_parse_int_to_array(hr->lo, coord, dims, 0);
  855. len = snprintf(host, size, "%s", hr->prefix);
  856. if (len >= 0 && len + dims < size) {
  857. while (i2 < dims)
  858. host[len++] = alpha_num[coord[i2++]];
  859. host[len] = '\0';
  860. }
  861. hr->lo++;
  862. } else {
  863. snprintf(host, size, "%s%0*lu", hr->prefix,
  864. hr->width, hr->lo++);
  865. }
  866. }
  867. return host;
  868. }
  869. /* join two hostrange objects.
  870. *
  871. * returns:
  872. *
  873. * -1 if ranges do not overlap (including incompatible zero padding)
  874. * 0 if ranges join perfectly
  875. * >0 number of hosts that were duplicated in h1 and h2
  876. *
  877. * h2 will be coalesced into h1 if rc >= 0
  878. *
  879. * it is assumed that h1->lo <= h2->lo, i.e. hr1 <= hr2
  880. *
  881. */
  882. static int hostrange_join(hostrange_t h1, hostrange_t h2)
  883. {
  884. int duplicated = -1;
  885. assert(h1 != NULL);
  886. assert(h2 != NULL);
  887. assert(hostrange_cmp(h1, h2) <= 0);
  888. if (hostrange_prefix_cmp(h1, h2) == 0 &&
  889. hostrange_width_combine(h1, h2)) {
  890. if (h1->singlehost && h2->singlehost) { /* matching singlets */
  891. duplicated = 1;
  892. } else if (h1->hi == h2->lo - 1) { /* perfect join */
  893. h1->hi = h2->hi;
  894. duplicated = 0;
  895. } else if (h1->hi >= h2->lo) { /* some duplication */
  896. if (h1->hi < h2->hi) {
  897. duplicated = h1->hi - h2->lo + 1;
  898. h1->hi = h2->hi;
  899. } else
  900. duplicated = hostrange_count(h2);
  901. }
  902. }
  903. return duplicated;
  904. }
  905. /* hostrange intersect returns the intersection (common hosts)
  906. * of hostrange objects h1 and h2. If there is no intersection,
  907. * NULL is returned.
  908. *
  909. * It is assumed that h1 <= h2 (i.e. h1->lo <= h2->lo)
  910. */
  911. static hostrange_t hostrange_intersect(hostrange_t h1, hostrange_t h2)
  912. {
  913. hostrange_t new = NULL;
  914. assert(h1 != NULL);
  915. assert(h2 != NULL);
  916. if (h1->singlehost || h2->singlehost)
  917. return NULL;
  918. assert(hostrange_cmp(h1, h2) <= 0);
  919. if ((hostrange_prefix_cmp(h1, h2) == 0)
  920. && (h1->hi > h2->lo)
  921. && (hostrange_width_combine(h1, h2))) {
  922. if (!(new = hostrange_copy(h1)))
  923. return NULL;
  924. new->lo = h2->lo;
  925. new->hi = h2->hi < h1->hi ? h2->hi : h1->hi;
  926. }
  927. return new;
  928. }
  929. /* return 1 if hostname hn is within the hostrange hr
  930. * 0 if not.
  931. */
  932. static int hostrange_hn_within(hostrange_t hr, hostname_t hn)
  933. {
  934. if (hr->singlehost) {
  935. /*
  936. * If the current hostrange [hr] is a `singlehost' (no valid
  937. * numeric suffix (lo and hi)), then the hostrange [hr]
  938. * stores just one host with name == hr->prefix.
  939. *
  940. * Thus the full hostname in [hn] must match hr->prefix, in
  941. * which case we return true. Otherwise, there is no
  942. * possibility that [hn] matches [hr].
  943. */
  944. if (strcmp (hn->hostname, hr->prefix) == 0)
  945. return 1;
  946. else
  947. return 0;
  948. }
  949. /*
  950. * Now we know [hr] is not a "singlehost", so hostname
  951. * better have a valid numeric suffix, or there is no
  952. * way we can match
  953. */
  954. if (!hostname_suffix_is_valid (hn))
  955. return 0;
  956. /*
  957. * If hostrange and hostname prefixes don't match, then
  958. * there is way the hostname falls within the range [hr].
  959. */
  960. if (strcmp(hr->prefix, hn->prefix) != 0) {
  961. int len1, len2, ldiff;
  962. int dims = slurmdb_setup_cluster_name_dims();
  963. if (dims != 1)
  964. return 0;
  965. /* Below logic was added since primarily for a cray
  966. * where people typically drop
  967. * leading zeros into the prefix so you can do
  968. * something like nid0000[2-7]. But doing this messes
  969. * up the hostlist_find since when someone queries
  970. * against nid00002 the prefixes don't match. The
  971. * below code is there to make sure get the best
  972. * chance for comparison.
  973. */
  974. /* First see if by taking some of the leading digits of the
  975. * suffix of hn and moving it to the end of the prefix if it
  976. * would be a match.
  977. */
  978. len1 = strlen(hr->prefix);
  979. len2 = strlen(hn->prefix);
  980. ldiff = len1 - len2;
  981. if (ldiff > 0 && isdigit(hr->prefix[len1-1])
  982. && (strlen(hn->suffix) >= ldiff)) {
  983. char *p = '\0';
  984. /* Tack on ldiff of the hostname's suffix to that of
  985. * it's prefix */
  986. hn->prefix = realloc(hn->prefix, len2+ldiff+1);
  987. strncat(hn->prefix, hn->suffix, ldiff);
  988. /* Now adjust the suffix of the hostname object. */
  989. hn->suffix += ldiff;
  990. /* And the numeric representation just incase
  991. * whatever we just tacked on to the prefix
  992. * had something other than 0 in it.
  993. *
  994. * Since we are only going through this logic for
  995. * single dimension systems we will always use
  996. * the base 10.
  997. */
  998. hn->num = strtoul(hn->suffix, &p, 10);
  999. /* Now compare them and see if they match */
  1000. if (strcmp(hr->prefix, hn->prefix) != 0)
  1001. return 0;
  1002. } else
  1003. return 0;
  1004. }
  1005. /*
  1006. * Finally, check whether [hn], with a valid numeric suffix,
  1007. * falls within the range of [hr].
  1008. */
  1009. if (hn->num <= hr->hi && hn->num >= hr->lo) {
  1010. int width = hostname_suffix_width(hn);
  1011. int num = hn->num;
  1012. return (_width_equiv(hr->lo, &hr->width, num, &width));
  1013. }
  1014. return 0;
  1015. }
  1016. /* copy a string representation of the hostrange hr into buffer buf,
  1017. * writing at most n chars including NUL termination
  1018. */
  1019. static size_t
  1020. hostrange_to_string(hostrange_t hr, size_t n, char *buf,
  1021. char *separator, int dims)
  1022. {
  1023. unsigned long i;
  1024. int ret, len = 0;
  1025. char sep = separator == NULL ? ',' : separator[0];
  1026. if (!dims)
  1027. dims = slurmdb_setup_cluster_name_dims();
  1028. if (n == 0)
  1029. return 0;
  1030. assert(hr != NULL);
  1031. if (hr->singlehost) {
  1032. ret = snprintf(buf, n, "%s", hr->prefix);
  1033. if (ret < 0 || ret >= n)
  1034. goto truncated;
  1035. return ret;
  1036. }
  1037. for (i = hr->lo; i <= hr->hi; i++) {
  1038. if (i > hr->lo)
  1039. buf[len++] = sep;
  1040. if (len >= n)
  1041. goto truncated;
  1042. if ((dims > 1) && (hr->width == dims)) {
  1043. int i2 = 0;
  1044. int coord[dims];
  1045. hostlist_parse_int_to_array(i, coord, dims, 0);
  1046. ret = snprintf(buf + len, n - len, "%s", hr->prefix);
  1047. if (ret < 0 || (len += ret) >= n || len + dims >= n)
  1048. goto truncated;
  1049. while (i2 < dims)
  1050. buf[len++] = alpha_num[coord[i2++]];
  1051. } else {
  1052. ret = snprintf(buf + len, n - len, "%s%0*lu",
  1053. hr->prefix, hr->width, i);
  1054. if (ret < 0 || (len += ret) >= n)
  1055. goto truncated;
  1056. }
  1057. }
  1058. buf[len] = '\0';
  1059. return len;
  1060. truncated:
  1061. buf[n-1] = '\0';
  1062. return -1;
  1063. }
  1064. /* Place the string representation of the numeric part of hostrange into buf
  1065. * writing at most n chars including NUL termination. The width argument
  1066. * controls the number of leading zeroes.
  1067. */
  1068. static size_t hostrange_numstr(hostrange_t hr, size_t n, char *buf, int width)
  1069. {
  1070. int len = 0;
  1071. int dims = slurmdb_setup_cluster_name_dims();
  1072. assert(buf != NULL);
  1073. assert(hr != NULL);
  1074. if (hr->singlehost || n == 0)
  1075. return 0;
  1076. if (n <= dims)
  1077. return -1;
  1078. if (width < 0 || width > hr->width)
  1079. width = hr->width;
  1080. if ((dims > 1) && (hr->width == dims)) {
  1081. int i2 = 0;
  1082. int coord[dims];
  1083. hostlist_parse_int_to_array(hr->lo, coord, dims, 0);
  1084. while (i2 < dims)
  1085. buf[len++] = alpha_num[coord[i2++]];
  1086. buf[len] = '\0';
  1087. } else {
  1088. len = snprintf(buf, n, "%0*lu", hr->width - width, hr->lo);
  1089. if (len < 0 || len >= n)
  1090. return -1;
  1091. }
  1092. if (hr->lo < hr->hi) {
  1093. if (n < len + dims + 2) /* '-' plus 'dims' digits, plus '\0' */
  1094. return -1;
  1095. if ((dims > 1) && (hr->width == dims)) {
  1096. int i2 = 0;
  1097. int coord[dims];
  1098. hostlist_parse_int_to_array(hr->hi, coord, dims, 0);
  1099. buf[len++] = '-';
  1100. while (i2 < dims)
  1101. buf[len++] = alpha_num[coord[i2++]];
  1102. buf[len] = '\0';
  1103. } else {
  1104. int len2 = snprintf(buf + len, n - len, "-%0*lu",
  1105. hr->width - width, hr->hi);
  1106. if (len2 < 0 || (len += len2) >= n)
  1107. return -1;
  1108. }
  1109. }
  1110. return len;
  1111. }
  1112. /* ----[ hostlist functions ]---- */
  1113. /* Create a new hostlist object.
  1114. * Returns an empty hostlist, or NULL if memory allocation fails.
  1115. */
  1116. static hostlist_t hostlist_new(void)
  1117. {
  1118. int i;
  1119. hostlist_t new = (hostlist_t) malloc(sizeof(*new));
  1120. if (!new)
  1121. goto fail1;
  1122. assert(new->magic = HOSTLIST_MAGIC);
  1123. mutex_init(&new->mutex);
  1124. new->hr = (hostrange_t *) malloc(HOSTLIST_CHUNK * sizeof(hostrange_t));
  1125. if (!new->hr)
  1126. goto fail2;
  1127. /* set entries in hostrange array to NULL */
  1128. for (i = 0; i < HOSTLIST_CHUNK; i++)
  1129. new->hr[i] = NULL;
  1130. new->size = HOSTLIST_CHUNK;
  1131. new->nranges = 0;
  1132. new->nhosts = 0;
  1133. new->ilist = NULL;
  1134. return new;
  1135. fail2:
  1136. free(new);
  1137. fail1:
  1138. out_of_memory("hostlist_new");
  1139. }
  1140. /* Resize the internal array used to store the list of hostrange objects.
  1141. *
  1142. * returns 1 for a successful resize,
  1143. * 0 if call to _realloc fails
  1144. *
  1145. * It is assumed that the caller has the hostlist hl locked
  1146. */
  1147. static int hostlist_resize(hostlist_t hl, size_t newsize)
  1148. {
  1149. int i;
  1150. size_t oldsize;
  1151. assert(hl != NULL);
  1152. assert(hl->magic == HOSTLIST_MAGIC);
  1153. oldsize = hl->size;
  1154. hl->size = newsize;
  1155. hl->hr = realloc((void *) hl->hr, hl->size*sizeof(hostrange_t));
  1156. if (!(hl->hr))
  1157. return 0;
  1158. for (i = oldsize; i < newsize; i++)
  1159. hl->hr[i] = NULL;
  1160. return 1;
  1161. }
  1162. /* Resize hostlist by one HOSTLIST_CHUNK
  1163. * Assumes that hostlist hl is locked by caller
  1164. */
  1165. static int hostlist_expand(hostlist_t hl)
  1166. {
  1167. if (!hostlist_resize(hl, hl->size + HOSTLIST_CHUNK))
  1168. return 0;
  1169. else
  1170. return 1;
  1171. }
  1172. /* Push a hostrange object onto hostlist hl
  1173. * Returns the number of hosts successfully pushed onto hl
  1174. * or -1 if there was an error allocating memory
  1175. */
  1176. static int hostlist_push_range(hostlist_t hl, hostrange_t hr)
  1177. {
  1178. hostrange_t tail;
  1179. int retval;
  1180. assert(hr != NULL);
  1181. LOCK_HOSTLIST(hl);
  1182. tail = (hl->nranges > 0) ? hl->hr[hl->nranges-1] : hl->hr[0];
  1183. if (hl->size == hl->nranges && !hostlist_expand(hl))
  1184. goto error;
  1185. if (hl->nranges > 0
  1186. && hostrange_prefix_cmp(tail, hr) == 0
  1187. && tail->hi == hr->lo - 1
  1188. && hostrange_width_combine(tail, hr)) {
  1189. tail->hi = hr->hi;
  1190. } else {
  1191. hostrange_t new = hostrange_copy(hr);
  1192. if (new == NULL)
  1193. goto error;
  1194. hl->hr[hl->nranges++] = new;
  1195. }
  1196. retval = hl->nhosts += hostrange_count(hr);
  1197. UNLOCK_HOSTLIST(hl);
  1198. return retval;
  1199. error:
  1200. UNLOCK_HOSTLIST(hl);
  1201. return -1;
  1202. }
  1203. /* Same as hostlist_push_range() above, but prefix, lo, hi, and width
  1204. * are passed as args
  1205. */
  1206. static int
  1207. hostlist_push_hr(hostlist_t hl, char *prefix, unsigned long lo,
  1208. unsigned long hi, int width)
  1209. {
  1210. hostrange_t hr = hostrange_create(prefix, lo, hi, width);
  1211. int retval = hostlist_push_range(hl, hr);
  1212. hostrange_destroy(hr);
  1213. return retval;
  1214. }
  1215. /* Insert a range object hr into position n of the hostlist hl
  1216. * Assumes that hl->mutex is already held by calling process
  1217. */
  1218. static int hostlist_insert_range(hostlist_t hl, hostrange_t hr, int n)
  1219. {
  1220. int i;
  1221. hostrange_t tmp;
  1222. hostlist_iterator_t hli;
  1223. assert(hl != NULL);
  1224. assert(hl->magic == HOSTLIST_MAGIC);
  1225. assert(hr != NULL);
  1226. if (n > hl->nranges)
  1227. return 0;
  1228. if (hl->size == hl->nranges && !hostlist_expand(hl))
  1229. return 0;
  1230. /* copy new hostrange into slot "n" in array */
  1231. tmp = hl->hr[n];
  1232. hl->hr[n] = hostrange_copy(hr);
  1233. /* push remaining hostrange entries up */
  1234. for (i = n + 1; i < hl->nranges + 1; i++) {
  1235. hostrange_t last = hl->hr[i];
  1236. hl->hr[i] = tmp;
  1237. tmp = last;
  1238. }
  1239. hl->nranges++;
  1240. /* adjust hostlist iterators if needed */
  1241. for (hli = hl->ilist; hli; hli = hli->next) {
  1242. if (hli->idx >= n)
  1243. hli->hr = hli->hl->hr[++hli->idx];
  1244. }
  1245. return 1;
  1246. }
  1247. /* Delete the range at position n in the range array
  1248. * Assumes the hostlist lock is already held.
  1249. */
  1250. static void hostlist_delete_range(hostlist_t hl, int n)
  1251. {
  1252. int i;
  1253. hostrange_t old;
  1254. assert(hl != NULL);
  1255. assert(hl->magic == HOSTLIST_MAGIC);
  1256. assert(n < hl->nranges && n >= 0);
  1257. old = hl->hr[n];
  1258. for (i = n; i < hl->nranges - 1; i++)
  1259. hl->hr[i] = hl->hr[i + 1];
  1260. hl->nranges--;
  1261. hl->hr[hl->nranges] = NULL;
  1262. hostlist_shift_iterators(hl, n, 0, 1);
  1263. /* XXX caller responsible for adjusting nhosts */
  1264. /* hl->nhosts -= hostrange_count(old) */
  1265. hostrange_destroy(old);
  1266. }
  1267. #if WANT_RECKLESS_HOSTRANGE_EXPANSION
  1268. /* The reckless hostrange expansion function.
  1269. * See comment in hostlist.h:hostlist_create() for more info on
  1270. * the different choices for hostlist notation.
  1271. */
  1272. hostlist_t _hostlist_create(const char *hostlist, char *sep, char *r_op,
  1273. int dims)
  1274. {
  1275. char *str, *orig;
  1276. char *tok, *cur;
  1277. int high, low, fmt = 0;
  1278. char prefix[256] = "";
  1279. int pos = 0;
  1280. int error = 0;
  1281. int hostlist_base;
  1282. char range_op = r_op[0];/* XXX support > 1 char range ops in future? */
  1283. hostlist_t new = hostlist_new();
  1284. if (hostlist == NULL)
  1285. return new;
  1286. if (dims > 1)
  1287. fatal("WANT_RECKLESS_HOSTRANGE_EXPANSION does not "
  1288. "work on multi-dimensional systems!!!!");
  1289. hostlist_base = hostlist_get_base(1);
  1290. orig = str = strdup(hostlist);
  1291. /* return an empty list if an empty string was passed in */
  1292. if (str == NULL || strlen(str) == 0)
  1293. goto done;
  1294. /* Use hostlist_create_bracketed if we see "[" */
  1295. if (strchr(str, '[') != NULL)
  1296. return _hostlist_create_bracketed(hostlist, sep, r_op, dims);
  1297. while ((tok = _next_tok(sep, &str)) != NULL) {
  1298. /* save the current string for error messages */
  1299. cur = tok;
  1300. high = low = 0;
  1301. /* find end of alpha part
  1302. * do this by finding last occurence of range_op in str */
  1303. pos = strlen(tok) - 1;
  1304. if (strstr(tok, r_op) != '\0') {
  1305. while (pos >= 0 && (char) tok[pos] != range_op)
  1306. pos--;
  1307. }
  1308. /* now back up past any digits */
  1309. while (pos >= 0 && isdigit((char) tok[--pos])) {;}
  1310. /* Check for valid x-y range (x must be a digit)
  1311. * Reset pos if the range is not valid */
  1312. if (!isdigit((char) tok[++pos]))
  1313. pos = strlen(tok) - 1;
  1314. /* create prefix string
  1315. * if prefix will be zero length, but prefix already exists
  1316. * use the previous prefix and fmt
  1317. */
  1318. if ((pos > 0) || (prefix[0] == '\0')) {
  1319. memcpy(prefix, tok, (size_t) pos * sizeof(char));
  1320. prefix[pos] = '\0';
  1321. /* push pointer past prefix */
  1322. tok += pos;
  1323. /* count number of digits for ouput fmt */
  1324. for (fmt = 0; isdigit(tok[fmt]); ++fmt) {;}
  1325. if (fmt == 0)
  1326. error = 1;
  1327. } else
  1328. tok += pos;
  1329. /* get lower bound */
  1330. low = strtoul(tok, (char **) &tok, hostlist_base);
  1331. if (*tok == range_op) { /* now get range upper bound */
  1332. /* push pointer past range op */
  1333. ++tok;
  1334. /* find length of alpha part */
  1335. for (pos = 0; tok[pos] && !isdigit(tok[pos]); ++pos) {;}
  1336. /* alpha part must match prefix or error
  1337. * this could mean we've got something like "rtr1-a2"
  1338. * so just record an error
  1339. */
  1340. if (pos > 0) {
  1341. if (pos != strlen(prefix) ||
  1342. strncmp(prefix, tok, pos) != 0)
  1343. error = 1;
  1344. }
  1345. if (*tok != '\0')
  1346. tok += pos;
  1347. /* make sure we have digits to the end */
  1348. for (pos = 0;
  1349. tok[pos] && isdigit((char) tok[pos]);
  1350. ++pos) {;}
  1351. if (pos > 0) { /* we have digits to process */
  1352. high = strtoul(tok, (char **) &tok,
  1353. hostlist_base);
  1354. } else { /* bad boy, no digits */
  1355. error = 1;
  1356. }
  1357. if ((low > high) || (high - low > MAX_RANGE))
  1358. error = 1;
  1359. } else { /* single value */
  1360. high = 0; /* special case, ugh. */
  1361. }
  1362. /* error if:
  1363. * 1. we are not at end of string
  1364. * 2. upper bound equals lower bound
  1365. */
  1366. if (*tok != '\0' || high == low)
  1367. error = 1;
  1368. if (error) { /* assume this is not a range on any error */
  1369. hostlist_push_host_dims(new, cur, dims);
  1370. } else {
  1371. if (high < low)
  1372. high = low;
  1373. hostlist_push_hr(new, prefix, low, high, fmt);
  1374. }
  1375. error = 0;
  1376. }
  1377. done:
  1378. if(orig)
  1379. free(orig);
  1380. return new;
  1381. }
  1382. #else /* !WANT_RECKLESS_HOSTRANGE_EXPANSION */
  1383. hostlist_t _hostlist_create(const char *hostlist, char *sep,
  1384. char *r_op, int dims)
  1385. {
  1386. return _hostlist_create_bracketed(hostlist, sep, r_op, dims);
  1387. }
  1388. #endif /* WANT_RECKLESS_HOSTRANGE_EXPANSION */
  1389. static int _parse_box_range(char *str, struct _range *ranges,
  1390. int len, int *count, int dims)
  1391. {
  1392. int start[dims], end[dims],
  1393. pos[dims];
  1394. char coord[dims+1];
  1395. char coord2[dims+1];
  1396. int i, a;
  1397. if(dims <= 1)
  1398. fatal("Unsupported dimensions count %d", dims);
  1399. if ((str[dims] != 'x') ||
  1400. (str[(dims * 2) + 1] != '\0'))
  1401. return 0;
  1402. for(i = 0; i<dims; i++) {
  1403. if ((str[i] >= '0') && (str[i] <= '9'))
  1404. start[i] = str[i] - '0';
  1405. else if ((str[i] >= 'A') && (str[i] <= 'Z'))
  1406. start[i] = str[i] - 'A' + 10;
  1407. else
  1408. return 0;
  1409. a = i + dims + 1;
  1410. if ((str[a] >= '0') && (str[a] <= '9'))
  1411. end[i] = str[a] - '0';
  1412. else if ((str[a] >= 'A') && (str[a] <= 'Z'))
  1413. end[i] = str[a] - 'A' + 10;
  1414. else
  1415. return 0;
  1416. }
  1417. memset(coord, 0, sizeof(coord));
  1418. memset(coord2, 0, sizeof(coord2));
  1419. for(i = 0; i<dims; i++) {
  1420. coord[i] = alpha_num[start[i]];
  1421. coord2[i] = alpha_num[end[i]];
  1422. }
  1423. /* info("adding ranges in %sx%s", coord, coord2); */
  1424. return _add_box_ranges(0, 0, start, end, pos, ranges, len, count, dims);
  1425. }
  1426. /* Grab a single range from str
  1427. * returns 1 if str contained a valid number or range,
  1428. * 0 if conversion of str to a range failed.
  1429. */
  1430. static int _parse_single_range(const char *str, struct _range *range, int dims)
  1431. {
  1432. char *p, *q;
  1433. char *orig = strdup(str);
  1434. int hostlist_base = hostlist_get_base(dims);
  1435. if (!orig)
  1436. seterrno_ret(ENOMEM, 0);
  1437. if ((p = strchr(str, 'x')))
  1438. goto error; /* do NOT allow boxes here */
  1439. if ((p = strchr(str, '-'))) {
  1440. *p++ = '\0';
  1441. if (*p == '-') /* do NOT allow negative numbers */
  1442. goto error;
  1443. }
  1444. range->width = strlen(str);
  1445. if(dims > 1) {
  1446. /* If we get something here where the width is not
  1447. SYSTEM_DIMENSIONS we need to treat it as a regular number
  1448. since that is how it will be treated in the future.
  1449. */
  1450. if(range->width != dims)
  1451. hostlist_base = 10;
  1452. }
  1453. range->lo = strtoul(str, &q, hostlist_base);
  1454. if (q == str)
  1455. goto error;
  1456. range->hi = (p && *p) ? strtoul(p, &q, hostlist_base) : range->lo;
  1457. if (q == p || *q != '\0')
  1458. goto error;
  1459. if (range->lo > range->hi)
  1460. goto error;
  1461. if (range->hi - range->lo + 1 > MAX_RANGE ) {
  1462. _error(__FILE__, __LINE__, "Too many hosts in range `%s'\n",
  1463. orig);
  1464. free(orig);
  1465. seterrno_ret(ERANGE, 0);
  1466. }
  1467. free(orig);
  1468. return 1;
  1469. error:
  1470. errno = EINVAL;
  1471. _error(__FILE__, __LINE__, "Invalid range: `%s'", orig);
  1472. free(orig);
  1473. return 0;
  1474. }
  1475. /*
  1476. * Convert 'str' containing comma separated digits and ranges into an array
  1477. * of struct _range types (max 'len' elements).
  1478. *
  1479. * Return number of ranges created, or -1 on error.
  1480. */
  1481. static int _parse_range_list(char *str, struct _range *ranges,
  1482. int len, int dims)
  1483. {
  1484. char *p;
  1485. int count = 0;
  1486. while (str) {
  1487. if (count == len) {
  1488. errno = EINVAL;
  1489. _error(__FILE__, __LINE__,
  1490. "Too many ranges, can't process "
  1491. "entire list");
  1492. return -1;
  1493. }
  1494. if ((p = strchr(str, ',')))
  1495. *p++ = '\0';
  1496. /* info("looking at %s", str); */
  1497. if ((dims > 1) &&
  1498. (str[dims] == 'x') &&
  1499. (strlen(str) == (dims * 2 + 1))) {
  1500. if (!_parse_box_range(str, ranges, len, &count, dims))
  1501. return -1;
  1502. } else {
  1503. if (!_parse_single_range(str, &ranges[count++], dims))
  1504. return -1;
  1505. }
  1506. str = p;
  1507. }
  1508. return count;
  1509. }
  1510. /* Validate prefix and push with the numeric suffix onto the hostlist
  1511. * The prefix can contain a up to one range expresseion (e.g. "rack[1-4]_").
  1512. * RET 0 on success, -1 on failure (invalid prefix) */
  1513. static int
  1514. _push_range_list(hostlist_t hl, char *prefix, struct _range *range,
  1515. int n, int dims)
  1516. {
  1517. int i, k, nr;
  1518. char *p, *q;
  1519. char new_prefix[1024], tmp_prefix[1024];
  1520. strncpy(tmp_prefix, prefix, sizeof(tmp_prefix));
  1521. if (((p = strrchr(tmp_prefix, '[')) != NULL) &&
  1522. ((q = strrchr(p, ']')) != NULL)) {
  1523. struct _range prefix_range[MAX_RANGES];
  1524. struct _range *saved_range = range, *pre_range = prefix_range;
  1525. unsigned long j, prefix_cnt = 0;
  1526. *p++ = '\0';
  1527. *q++ = '\0';
  1528. if (strrchr(tmp_prefix, '[') != NULL)
  1529. return -1; /* third range is illegal */
  1530. nr = _parse_range_list(p, prefix_range, MAX_RANGES, dims);
  1531. if (nr < 0)
  1532. return -1; /* bad numeric expression */
  1533. for (i = 0; i < nr; i++) {
  1534. prefix_cnt += pre_range->hi - pre_range->lo + 1;
  1535. if (prefix_cnt > MAX_PREFIX_CNT) {
  1536. /* Prevent overflow of memory with user input
  1537. * of something like "a[0-999999999].b[0-9]" */
  1538. return -1;
  1539. }
  1540. for (j = pre_range->lo; j <= pre_range->hi; j++) {
  1541. snprintf(new_prefix, sizeof(new_prefix),
  1542. "%s%0*lu%s", tmp_prefix,
  1543. pre_range->width, j, q);
  1544. range = saved_range;
  1545. for (k = 0; k < n; k++) {
  1546. hostlist_push_hr(hl, new_prefix,
  1547. range->lo, range->hi,
  1548. range->width);
  1549. range++;
  1550. }
  1551. }
  1552. pre_range++;
  1553. }
  1554. return 0;
  1555. }
  1556. for (k = 0; k < n; k++) {
  1557. hostlist_push_hr(hl, prefix,
  1558. range->lo, range->hi, range->width);
  1559. range++;
  1560. }
  1561. return 0;
  1562. }
  1563. /*
  1564. * Create a hostlist from a string with brackets '[' ']' to aid
  1565. * detection of ranges and compressed lists
  1566. */
  1567. static hostlist_t
  1568. _hostlist_create_bracketed(const char *hostlist, char *sep,
  1569. char *r_op, int dims)
  1570. {
  1571. hostlist_t new = hostlist_new();
  1572. struct _range ranges[MAX_RANGES];
  1573. int nr, err;
  1574. char *p, *tok, *str, *orig;
  1575. char cur_tok[1024];
  1576. if (hostlist == NULL)
  1577. return new;
  1578. if (!(orig = str = strdup(hostlist))) {
  1579. hostlist_destroy(new);
  1580. return NULL;
  1581. }
  1582. while ((tok = _next_tok(sep, &str)) != NULL) {
  1583. strncpy(cur_tok, tok, 1024);
  1584. if ((p = strrchr(tok, '[')) != NULL) {
  1585. char *q, *prefix = tok;
  1586. *p++ = '\0';
  1587. if ((q = strchr(p, ']'))) {
  1588. if ((q[1] != ',') && (q[1] != '\0'))
  1589. goto error;
  1590. *q = '\0';
  1591. nr = _parse_range_list(p, ranges,
  1592. MAX_RANGES, dims);
  1593. if (nr < 0)
  1594. goto error;
  1595. if (_push_range_list(
  1596. new, prefix, ranges, nr, dims))
  1597. goto error;
  1598. } else {
  1599. /* The hostname itself contains a '['
  1600. * (no ']' found).
  1601. * Not likely what the user
  1602. * wanted. We will just tack one on
  1603. * the end. */
  1604. strcat(cur_tok, "]");
  1605. if(prefix && pre

Large files files are truncated, but you can click here to view the full file