/src/pdsh/wcoll.c

https://code.google.com/ · C · 358 lines · 208 code · 58 blank · 92 comment · 66 complexity · 7eb52c57fc44970335a6f708b1936f9f MD5 · raw file

  1. /*****************************************************************************\
  2. * $Id$
  3. *****************************************************************************
  4. * Copyright (C) 2001-2006 The Regents of the University of California.
  5. * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  6. * Written by Jim Garlick <garlick@llnl.gov>.
  7. * UCRL-CODE-2003-005.
  8. *
  9. * This file is part of Pdsh, a parallel remote shell program.
  10. * For details, see <http://www.llnl.gov/linux/pdsh/>.
  11. *
  12. * Pdsh is free software; you can redistribute it and/or modify it under
  13. * the terms of the GNU General Public License as published by the Free
  14. * Software Foundation; either version 2 of the License, or (at your option)
  15. * any later version.
  16. *
  17. * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY
  18. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  19. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  20. * details.
  21. *
  22. * You should have received a copy of the GNU General Public License along
  23. * with Pdsh; if not, write to the Free Software Foundation, Inc.,
  24. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  25. \*****************************************************************************/
  26. #if HAVE_CONFIG_H
  27. #include "config.h"
  28. #endif
  29. #include <string.h>
  30. #include <assert.h>
  31. #if HAVE_UNISTD_H
  32. #include <unistd.h> /* for R_OK, access() */
  33. #endif
  34. #include <stdlib.h> /* atoi */
  35. #include <errno.h>
  36. #include <ctype.h>
  37. #include <libgen.h>
  38. #include "src/common/err.h"
  39. #include "src/common/list.h"
  40. #include "src/common/xmalloc.h"
  41. #include "src/common/xstring.h"
  42. #include "src/common/hostlist.h"
  43. #include "src/common/split.h"
  44. #include "src/common/xstring.h"
  45. #include "xpopen.h"
  46. #include "dsh.h"
  47. #include "wcoll.h"
  48. struct wcoll_ctx {
  49. hostlist_t hl;
  50. List path_list;
  51. List include_cache;
  52. };
  53. static void free_f (void *x)
  54. {
  55. Free (&x);
  56. }
  57. static int strcmp_f (char *a, char *b)
  58. {
  59. return (strcmp (a, b) == 0);
  60. }
  61. static struct wcoll_ctx * wcoll_ctx_create (const char *path)
  62. {
  63. char *copy = Strdup (path);
  64. struct wcoll_ctx *ctx = Malloc (sizeof (*ctx));
  65. if (!copy || !ctx)
  66. errx ("%p: wcoll_ctx_create: Out of memory\n");
  67. ctx->hl = hostlist_create ("");
  68. ctx->path_list = list_split (":", copy);
  69. ctx->include_cache = list_create ((ListDelF) free_f);
  70. Free ((void **) &copy);
  71. return (ctx);
  72. }
  73. static void wcoll_ctx_destroy (struct wcoll_ctx *ctx)
  74. {
  75. list_destroy (ctx->path_list);
  76. list_destroy (ctx->include_cache);
  77. /*
  78. * Do not destroy hostlist, it is pulled out of ctx and returned
  79. * to caller of read_wcoll()
  80. */
  81. ctx->hl = NULL;
  82. Free ((void **)&ctx);
  83. }
  84. static int wcoll_ctx_file_is_cached (struct wcoll_ctx *ctx, char *file)
  85. {
  86. if (list_find_first (ctx->include_cache, (ListFindF) strcmp_f, file))
  87. return 1;
  88. list_push (ctx->include_cache, Strdup (file));
  89. return 0;
  90. }
  91. /*
  92. * Find first file named [name] in colon-separated path [path]
  93. * Returns file path in supplied buffer [buf] of length [n].
  94. * Returns -1 on failure.
  95. */
  96. static int wcoll_ctx_path_lookup (struct wcoll_ctx *ctx,
  97. const char *name, char *buf, int len)
  98. {
  99. int rc = -1;
  100. ListIterator i;
  101. const char *s;
  102. if (name == NULL)
  103. return -1;
  104. i = list_iterator_create (ctx->path_list);
  105. while ((s = list_next (i))) {
  106. int n = snprintf (buf, len, "%s/%s", s, name);
  107. if (n < 0 || n >= len) {
  108. errno = ENOSPC;
  109. break;
  110. }
  111. if (access (buf, R_OK) >= 0) {
  112. rc = 0;
  113. break;
  114. }
  115. }
  116. list_iterator_destroy (i);
  117. return (rc);
  118. }
  119. static char * include_file (char *line)
  120. {
  121. const char *sep = "\n\r\t ";
  122. char *p = line;
  123. char *orig;
  124. char *included = NULL;
  125. /*
  126. * Check for "#include" starting in first column. If it exists,
  127. * then attempt to return the included filename or path.
  128. */
  129. if (strncmp (p, "#include", 8) != 0)
  130. return (NULL);
  131. p += 8;
  132. /*
  133. * Skip whitespace
  134. */
  135. while (isblank (*p)) p++;
  136. /*
  137. * Copy original line in case we need to print an error.
  138. */
  139. orig = Strdup (line);
  140. /*
  141. * Get next token, which should be the included filename or path.
  142. *
  143. * If there are more tokens following the filename, then ignore
  144. * this line and print an error.
  145. */
  146. if ((p = strtok (p, sep)) == NULL || strtok (NULL, "\n\r\t "))
  147. err ("%p: warning: Ignoring invalid line: %s", orig);
  148. else
  149. included = p;
  150. Free ((void **) &orig);
  151. return (included);
  152. }
  153. /*
  154. * Return absolute path to [file] in buffer [buf] of max length [len].
  155. *
  156. * If [file] argument is an absolute path or relative to '.' or '..'
  157. * then [file] is copied to [buf], otherwise, colon-separated [path]
  158. * is searched for a file named [file].
  159. *
  160. * If no file is found, then NULL is returned, otherwise a pointer
  161. * to the passed [buf] is returned.
  162. */
  163. static char * wcoll_ctx_resolve_path (struct wcoll_ctx *ctx,
  164. const char *file, char *buf, int len)
  165. {
  166. if (file[0] == '/')
  167. strncpy (buf, file, len);
  168. else if ( file[0] == '.'
  169. && (file[1] == '/' || (file[1] == '.' && file[2] == '/')))
  170. strncpy (buf, file, len);
  171. else {
  172. if (wcoll_ctx_path_lookup (ctx, file, buf, len) < 0)
  173. return NULL;
  174. }
  175. return buf;
  176. }
  177. static int wcoll_ctx_read_file (struct wcoll_ctx *ctx, const char *f);
  178. static int wcoll_ctx_read_line (struct wcoll_ctx *ctx, char *line)
  179. {
  180. char *p;
  181. char *included;
  182. /*
  183. * Check for comment. Zap the comment and the rest of the line
  184. * unless this is an include statement '#include foo'
  185. */
  186. if ((p = strchr(line, '#')) != NULL) {
  187. if (p == line && (included = include_file (p)) != NULL)
  188. wcoll_ctx_read_file (ctx, included);
  189. *p = '\0';
  190. }
  191. xstrcln(line, NULL);
  192. if ((line[0] != '\0') && (hostlist_push(ctx->hl, line) == 0))
  193. err("%p: warning: target '%s' not parsed\n", line);
  194. return 0;
  195. }
  196. static int wcoll_ctx_read_stream (struct wcoll_ctx *ctx, FILE *fp)
  197. {
  198. char buf [LINEBUFSIZE];
  199. assert (ctx != NULL);
  200. assert (fp != NULL);
  201. while (fgets(buf, LINEBUFSIZE, fp) != NULL)
  202. wcoll_ctx_read_line (ctx, buf);
  203. return 0;
  204. }
  205. /*
  206. * Append the contents of file [f], optionally searching [path], to
  207. * the supplied hostlist [hl], returning the result. If [hl] is NULL,
  208. * then a new hostlist is allocated.
  209. *
  210. * This function supports
  211. *
  212. * #include file
  213. *
  214. * to include other working collective files.
  215. */
  216. static int wcoll_ctx_read_file (struct wcoll_ctx *ctx, const char *f)
  217. {
  218. char fq_path [4096];
  219. FILE *fp = NULL;
  220. assert (ctx != NULL);
  221. assert (f != NULL);
  222. if (wcoll_ctx_resolve_path (ctx, f, fq_path, sizeof (fq_path)) == NULL)
  223. errx("%p: %s: %m\n", f);
  224. /*
  225. * Detect recursive #include:
  226. */
  227. if (wcoll_ctx_file_is_cached (ctx, fq_path)) {
  228. err("%p: warning: file '%s' included multiple times\n", f);
  229. return -1;
  230. }
  231. if (access(fq_path, R_OK) == -1 || !(fp = fopen(fq_path, "r")))
  232. errx("%p: %s: %m\n", f);
  233. wcoll_ctx_read_stream (ctx, fp);
  234. fclose(fp);
  235. return 0;
  236. }
  237. hostlist_t read_wcoll_path (const char *path, const char *file)
  238. {
  239. struct wcoll_ctx *ctx;
  240. hostlist_t hl;
  241. ctx = wcoll_ctx_create (path);
  242. wcoll_ctx_read_file (ctx, file);
  243. hl = ctx->hl;
  244. wcoll_ctx_destroy (ctx);
  245. return hl;
  246. }
  247. /*
  248. * Get the dirname for the file path [file] and copy into the buffer
  249. * [dir] of length [len]. If [file] is NULL then return ".".
  250. */
  251. static char * get_file_path (const char *file, char *dir, int len)
  252. {
  253. char *str;
  254. char *dname;
  255. memset (dir, 0, len);
  256. dir[0] = '.';
  257. if (file == NULL)
  258. return (dir);
  259. str = Strdup (file);
  260. dname = dirname (str);
  261. if (dname && strlen (dname) < len - 1)
  262. strcpy (dir, dname);
  263. else
  264. err ("%p: %s: Error reading file path\n");
  265. Free ((void **) &str);
  266. return (dir);
  267. }
  268. /*
  269. * Read wcoll from specified file or from the specified FILE pointer.
  270. * (one of the arguments must be NULL).
  271. * file (IN) name of wcoll file (or NULL)
  272. * f (IN) FILE pointer to wcoll file (or NULL)
  273. * RETURN new list containing hostnames
  274. */
  275. hostlist_t read_wcoll(char *file, FILE * f)
  276. {
  277. char path[4096];
  278. hostlist_t new;
  279. struct wcoll_ctx *ctx;
  280. FILE *fp = NULL;
  281. assert(f != NULL || file != NULL);
  282. if (strcmp (file, "-") == 0) {
  283. f = stdin;
  284. file = NULL;
  285. }
  286. if (f == NULL) { /* read_wcoll("file", NULL) */
  287. if (access(file, R_OK) == -1 || !(fp = fopen(file, "r")))
  288. errx("%p: %s: %m\n", file);
  289. } else /* read_wcoll(NULL, fp) */
  290. fp = f;
  291. get_file_path (file, path, sizeof (path));
  292. ctx = wcoll_ctx_create (path);
  293. wcoll_ctx_read_stream (ctx, fp);
  294. new = ctx->hl;
  295. wcoll_ctx_destroy (ctx);
  296. return new;
  297. }
  298. /*
  299. * vi:tabstop=4 shiftwidth=4 expandtab
  300. */