PageRenderTime 44ms CodeModel.GetById 2ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 0ms

/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
 27#if     HAVE_CONFIG_H
 28#include "config.h"
 29#endif
 30
 31#include <string.h>
 32#include <assert.h>
 33#if     HAVE_UNISTD_H
 34#include <unistd.h>             /* for R_OK, access() */
 35#endif
 36#include <stdlib.h>             /* atoi */
 37#include <errno.h>
 38#include <ctype.h>
 39#include <libgen.h>
 40
 41#include "src/common/err.h"
 42#include "src/common/list.h"
 43#include "src/common/xmalloc.h"
 44#include "src/common/xstring.h"
 45#include "src/common/hostlist.h"
 46#include "src/common/split.h"
 47#include "src/common/xstring.h"
 48#include "xpopen.h"
 49#include "dsh.h"
 50#include "wcoll.h"
 51
 52struct wcoll_ctx {
 53    hostlist_t hl;
 54    List path_list;
 55    List include_cache;
 56};
 57
 58static void free_f (void *x)
 59{
 60    Free (&x);
 61}
 62
 63static int strcmp_f (char *a, char *b)
 64{
 65    return (strcmp (a, b) == 0);
 66}
 67
 68static struct wcoll_ctx * wcoll_ctx_create (const char *path)
 69{
 70    char *copy = Strdup (path);
 71    struct wcoll_ctx *ctx = Malloc (sizeof (*ctx));
 72
 73    if (!copy || !ctx)
 74        errx ("%p: wcoll_ctx_create: Out of memory\n");
 75
 76    ctx->hl = hostlist_create ("");
 77    ctx->path_list = list_split (":", copy);
 78    ctx->include_cache = list_create ((ListDelF) free_f);
 79
 80    Free ((void **) &copy);
 81    return (ctx);
 82}
 83
 84static void wcoll_ctx_destroy (struct wcoll_ctx *ctx)
 85{
 86    list_destroy (ctx->path_list);
 87    list_destroy (ctx->include_cache);
 88    /*
 89     * Do not destroy hostlist, it is pulled out of ctx and returned
 90     *  to caller of read_wcoll()
 91     */
 92    ctx->hl = NULL;
 93    Free ((void **)&ctx);
 94}
 95
 96
 97static int wcoll_ctx_file_is_cached (struct wcoll_ctx *ctx, char *file)
 98{
 99    if (list_find_first (ctx->include_cache, (ListFindF) strcmp_f, file))
100        return 1;
101    list_push (ctx->include_cache, Strdup (file));
102    return 0;
103}
104
105/*
106 *  Find first file named [name] in colon-separated path [path]
107 *   Returns file path in supplied buffer [buf] of length [n].
108 *   Returns -1 on failure.
109 */
110static int wcoll_ctx_path_lookup (struct wcoll_ctx *ctx,
111        const char *name, char *buf, int len)
112{
113    int rc = -1;
114    ListIterator i;
115    const char *s;
116
117    if (name == NULL)
118        return -1;
119
120    i = list_iterator_create (ctx->path_list);
121    while ((s = list_next (i))) {
122        int n = snprintf (buf, len, "%s/%s", s, name);
123        if (n < 0 || n >= len) {
124            errno = ENOSPC;
125            break;
126        }
127        if (access (buf, R_OK) >= 0) {
128            rc = 0;
129            break;
130        }
131    }
132    list_iterator_destroy (i);
133
134    return (rc);
135}
136
137static char * include_file (char *line)
138{
139    const char *sep = "\n\r\t ";
140    char *p = line;
141    char *orig;
142    char *included = NULL;
143
144    /*
145     *  Check for "#include" starting in first column. If it exists,
146     *   then attempt to return the included filename or path.
147     */
148    if (strncmp (p, "#include", 8) != 0)
149        return (NULL);
150    p += 8;
151
152    /*
153     *  Skip whitespace
154     */
155    while (isblank (*p)) p++;
156
157    /*
158     *  Copy original line in case we need to print an error.
159     */
160    orig = Strdup (line);
161
162    /*
163     *  Get next token, which should be the included filename or path.
164     *
165     *  If there are more tokens following the filename, then ignore
166     *   this line and print an error.
167     */
168    if ((p = strtok (p, sep)) == NULL || strtok (NULL, "\n\r\t "))
169        err ("%p: warning: Ignoring invalid line: %s", orig);
170    else
171        included = p;
172
173    Free ((void **) &orig);
174    return (included);
175}
176
177/*
178 *   Return absolute path to [file] in buffer [buf] of max length [len].
179 *
180 *   If [file] argument is an absolute path or relative to '.' or '..'
181 *    then [file] is copied to [buf], otherwise, colon-separated [path]
182 *    is searched for a file named [file].
183 *
184 *   If no file is found, then NULL is returned, otherwise a pointer
185 *    to the passed [buf] is returned.
186 */
187static char * wcoll_ctx_resolve_path (struct wcoll_ctx *ctx,
188        const char *file, char *buf, int len)
189{
190    if (file[0] == '/')
191        strncpy (buf, file, len);
192    else if ( file[0] == '.'
193            && (file[1] == '/' || (file[1] == '.' && file[2] == '/')))
194       strncpy (buf, file, len);
195    else {
196        if (wcoll_ctx_path_lookup (ctx, file, buf, len) < 0)
197            return NULL;
198    }
199    return buf;
200}
201
202static int wcoll_ctx_read_file (struct wcoll_ctx *ctx, const char *f);
203
204static int wcoll_ctx_read_line (struct wcoll_ctx *ctx, char *line)
205{
206    char *p;
207    char *included;
208
209    /*
210     *  Check for comment. Zap the comment and the rest of the line
211     *   unless this is an include statement '#include foo'
212     */
213    if ((p = strchr(line, '#')) != NULL) {
214        if (p == line && (included = include_file (p)) != NULL)
215            wcoll_ctx_read_file (ctx, included);
216        *p = '\0';
217    }
218    xstrcln(line, NULL);
219
220    if ((line[0] != '\0') && (hostlist_push(ctx->hl, line) == 0))
221        err("%p: warning: target '%s' not parsed\n", line);
222
223    return 0;
224}
225
226static int wcoll_ctx_read_stream (struct wcoll_ctx *ctx, FILE *fp)
227{
228    char buf [LINEBUFSIZE];
229
230    assert (ctx != NULL);
231    assert (fp != NULL);
232
233    while (fgets(buf, LINEBUFSIZE, fp) != NULL)
234        wcoll_ctx_read_line (ctx, buf);
235    return 0;
236}
237
238
239/*
240 *  Append the contents of file [f], optionally searching [path], to
241 *   the supplied hostlist [hl], returning the result. If [hl] is NULL,
242 *   then a new hostlist is allocated.
243 *
244 *  This function supports
245 *
246 *    #include file
247 *
248 *  to include other working collective files.
249 */
250static int wcoll_ctx_read_file (struct wcoll_ctx *ctx, const char *f)
251{
252    char fq_path [4096];
253    FILE *fp = NULL;
254
255    assert (ctx != NULL);
256    assert (f != NULL);
257
258    if (wcoll_ctx_resolve_path (ctx, f, fq_path, sizeof (fq_path)) == NULL)
259        errx("%p: %s: %m\n", f);
260
261    /*
262     *  Detect recursive #include:
263     */
264    if (wcoll_ctx_file_is_cached (ctx, fq_path)) {
265        err("%p: warning: file '%s' included multiple times\n", f);
266        return -1;
267    }
268
269    if (access(fq_path, R_OK) == -1 || !(fp = fopen(fq_path, "r")))
270        errx("%p: %s: %m\n", f);
271
272    wcoll_ctx_read_stream (ctx, fp);
273
274    fclose(fp);
275
276    return 0;
277}
278
279hostlist_t read_wcoll_path (const char *path, const char *file)
280{
281    struct wcoll_ctx *ctx;
282    hostlist_t hl;
283
284    ctx = wcoll_ctx_create (path);
285    wcoll_ctx_read_file (ctx, file);
286    hl = ctx->hl;
287    wcoll_ctx_destroy (ctx);
288
289    return hl;
290}
291
292/*
293 *  Get the dirname for the file path [file] and copy into the buffer
294 *   [dir] of length [len]. If [file] is NULL then return ".".
295 */
296static char * get_file_path (const char *file, char *dir, int len)
297{
298    char *str;
299    char *dname;
300
301    memset (dir, 0, len);
302    dir[0] = '.';
303
304    if (file == NULL)
305        return (dir);
306
307    str = Strdup (file);
308    dname = dirname (str);
309
310    if (dname && strlen (dname) < len - 1)
311        strcpy (dir, dname);
312    else
313        err ("%p: %s: Error reading file path\n");
314
315    Free ((void **) &str);
316    return (dir);
317}
318
319/*
320 * Read wcoll from specified file or from the specified FILE pointer.
321 * (one of the arguments must be NULL).  
322 *      file (IN)       name of wcoll file (or NULL)
323 *      f (IN)          FILE pointer to wcoll file (or NULL)    
324 *      RETURN          new list containing hostnames
325 */
326hostlist_t read_wcoll(char *file, FILE * f)
327{
328    char path[4096];
329    hostlist_t new;
330    struct wcoll_ctx *ctx;
331    FILE *fp = NULL;
332
333    assert(f != NULL || file != NULL);
334
335    if (strcmp (file, "-") == 0) {
336        f = stdin;
337        file = NULL;
338    }
339
340    if (f == NULL) {            /* read_wcoll("file", NULL) */
341        if (access(file, R_OK) == -1 || !(fp = fopen(file, "r")))
342            errx("%p: %s: %m\n", file);
343    } else                      /* read_wcoll(NULL, fp) */
344        fp = f;
345
346    get_file_path (file, path, sizeof (path));
347
348    ctx = wcoll_ctx_create (path);
349    wcoll_ctx_read_stream (ctx, fp);
350    new = ctx->hl;
351    wcoll_ctx_destroy (ctx);
352
353    return new;
354}
355
356/*
357 * vi:tabstop=4 shiftwidth=4 expandtab
358 */