PageRenderTime 56ms CodeModel.GetById 26ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/src/pdsh/pcp_client.c

https://code.google.com/
C | 404 lines | 239 code | 48 blank | 117 comment | 53 complexity | 1aa703376dc01110de728c4e95666c80 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/*
 28 * Copyright (c) 1983, 1990 The Regents of the University of California.
 29 * All rights reserved.
 30 *
 31 * Redistribution and use in source and binary forms, with or without
 32 * modification, are permitted provided that the following conditions
 33 * are met:
 34 * 1. Redistributions of source code must retain the above copyright
 35 *    notice, this list of conditions and the following disclaimer.
 36 * 2. Redistributions in binary form must reproduce the above copyright
 37 *    notice, this list of conditions and the following disclaimer in the
 38 *    documentation and/or other materials provided with the distribution.
 39 * 3. Advertising clause removed per the following letter:
 40 *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
 41 * 4. Neither the name of the University nor the names of its contributors
 42 *    may be used to endorse or promote products derived from this software
 43 *    without specific prior written permission.
 44 *
 45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 48 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 55 * SUCH DAMAGE.
 56 */
 57
 58#if HAVE_CONFIG_H
 59# include "config.h"
 60#endif
 61
 62#include <sys/param.h>     /* roundup() */
 63#if HAVE_SYS_SYSMACROS_H
 64# include <sys/sysmacros.h>
 65#endif
 66#include <sys/stat.h>
 67#include <sys/param.h>
 68#include <sys/types.h>
 69#include <errno.h>
 70#include <unistd.h>
 71#include <fcntl.h>
 72#include <dirent.h>
 73#include <stdio.h>
 74#include <stdarg.h>
 75#include <stdlib.h>
 76#include <string.h>
 77#include <stdio.h>
 78#include <assert.h>
 79
 80#include "src/common/err.h"
 81#include "src/common/fd.h"
 82#include "src/common/list.h"
 83#include "src/common/xstring.h"
 84#include "src/common/err.h"
 85#include "src/common/xmalloc.h"
 86#include "pcp_client.h"
 87#include "wcoll.h"
 88
 89#ifndef MAXPATHNAMELEN
 90#define MAXPATHNAMELEN MAXPATHLEN
 91#endif
 92
 93
 94static void _rexpand_dir(List list, char *name)
 95{
 96    DIR *dir;
 97    struct dirent *dp;
 98    struct stat sb;
 99    char file[MAXPATHNAMELEN];
100    struct pcp_filename *pf = NULL;
101
102    dir = opendir(name);
103    if (dir == NULL)
104        errx("%p: opendir: %s: %m\n", name);
105    while ((dp = readdir(dir))) {
106        if (dp->d_ino == 0)
107            continue;
108        if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
109            continue;
110        snprintf(file, sizeof(file), "%s/%s", name, dp->d_name);
111        if (stat(file, &sb) < 0)
112            errx("%p: can't stat %s: %m\n", file);
113        if (access(name, R_OK) < 0)
114            errx("%p: access: %s: %m\n", name);
115        if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode))
116            errx("%p: not a regular file or directory: %s\n", file);
117
118        /* XXX: This memleaks */
119        pf = Malloc(sizeof(struct pcp_filename));
120        pf->filename = Strdup(file);
121        pf->file_specified_by_user = 0;
122
123        list_append(list, pf);
124        if (S_ISDIR(sb.st_mode))
125            _rexpand_dir(list, file);
126    }
127    closedir(dir);
128
129    /* Since pdcp reads file names and directories only once for
130     * efficiency, we must specify a special flag so we know when
131     * to tell the server to "move up" the directory tree.
132     */
133    
134    /* XXX: This memleaks */
135    pf = Malloc(sizeof(struct pcp_filename));
136    pf->filename = Strdup(EXIT_SUBDIR_FILENAME);
137    pf->file_specified_by_user = 0;
138    list_append(list, pf);
139}
140
141List pcp_expand_dirs(List infiles)
142{
143    List new = list_create(NULL);
144    struct stat sb;
145    char *name;
146    ListIterator i;
147
148    i = list_iterator_create(infiles);
149    while ((name = list_next(i))) {
150        struct pcp_filename *pf = NULL;
151
152        if (access(name, R_OK) < 0)
153            errx("%p: access: %s: %m\n", name);
154        if (stat(name, &sb) < 0)
155            errx("%p: stat: %s: %m\n", name);
156        
157        /* XXX: This memleaks */
158        pf = Malloc(sizeof(struct pcp_filename));
159        pf->filename = name;
160        pf->file_specified_by_user = 1;
161
162        list_append(new, pf);
163
164        /* -r option checked during command line argument checks */
165        if (S_ISDIR(sb.st_mode))
166            _rexpand_dir(new, name);
167    }
168    
169    return new;
170}
171
172/*
173 * Wrapper for the write system call that handles short writes.
174 * Not sure if write ever returns short in practice but we have to be sure.
175 *	fd (IN)		file descriptor to write to 
176 *	buf (IN)	data to write
177 *	size (IN)	size of buf
178 *	RETURN		-1 on failure, size on success
179 */
180static int _pcp_write(int fd, char *buf, int size)
181{
182    char *bufp = buf;
183    int towrite = size;
184    int outbytes;
185
186    while (towrite > 0) {
187        outbytes = write(fd, bufp, towrite);
188        if (outbytes <= 0) {
189            assert(outbytes != 0);
190            return -1;
191        }
192        towrite -= outbytes;
193        bufp += outbytes;
194    }
195    return size;
196}
197
198/*
199 * Write the contents of the named file to the specified file descriptor.
200 *	outfd (IN)	file descriptor to write to 
201 *	filename (IN)	name of file
202 *	host (IN)	name of remote host for error messages
203 *	RETURN		-1 on failure, 0 on success.
204 */
205static int _pcp_send_file_data(int outfd, char *filename, char *host)
206{
207    int filefd, inbytes, total = 0;
208    char tmpbuf[BUFSIZ];
209
210    filefd = open(filename, O_RDONLY);
211    /* checked ahead of time - shouldn't happen */
212    if (filefd < 0) {
213        err("%S: _pcp_send_file_data: open %s: %m\n", host, filename);
214        return -1;
215    }
216    do {
217        inbytes = read(filefd, tmpbuf, BUFSIZ);
218        if (inbytes < 0) {
219            err("%S: _pcp_send_file_data: read %s: %m\n", host, filename);
220            return -1;
221        }
222        if (inbytes > 0) {
223            total += inbytes;
224            if (_pcp_write(outfd, tmpbuf, inbytes) < 0) {
225                err("%S: _pcp_send_file_data: write: %m\n", host);
226                return -1;
227            }
228        }
229    } while (inbytes > 0);      /* until EOF */
230    close(filefd);
231    return 0;
232}
233
234/*
235 * Send string to the specified file descriptor.  Do not send trailing '\0'
236 * as RCP terminates strings with newlines.
237 *	fd (IN)		file descriptor to write to
238 *	str (IN)	string to write
239 *	host (IN)	name of remote host for error messages
240 *	RETURN 		-1 on failure, 0 on success
241 */
242static int pcp_sendstr(int outfd, char *str, char *host)
243{
244    int n;
245    assert(strlen(str) > 0);
246    assert(str[strlen(str) - 1] == '\n');
247
248    if ((n = _pcp_write(outfd, str, strlen(str))) < 0) 
249        return -1;
250
251    assert(n == strlen(str));
252    return 0;
253}
254
255/*
256 * Receive an RCP response code and possibly error message.
257 *	fd (IN)		file desciptor to read from
258 *	host (IN)	hostname for error messages
259 *	RETURN		-1 on fatal error, 0 otherwise
260 */
261static int pcp_response(int infd, char *host)
262{
263    char resp;
264    int i = 0, result = -1;
265    int n;
266    char errstr[BUFSIZ];
267
268    if ((n = read(infd, &resp, sizeof(resp))) != sizeof(resp)) 
269        return (-1);
270
271    switch (resp) {
272        case 0:                /* ok */
273            result = 0;
274            break;
275        default:               /* just error string */
276            errstr[i++] = resp;
277            result = 0;
278        case 1:                /* fatal error + string */
279            fd_read_line (infd, &errstr[i], BUFSIZ - i);
280            err("%p: %S: %s: %s", host, result ? "fatal" : "error", errstr);
281            break;
282    }
283    return result;
284}
285
286#define RCP_MODEMASK (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
287
288int pcp_sendfile(struct pcp_client *pcp, char *file, char *output_file)
289{
290    int result = 0;
291    char tmpstr[BUFSIZ], *template;
292    struct stat sb;
293
294	if (output_file == NULL)
295		output_file = file;
296
297    /*err("%S: %s\n", host, file); */
298
299    if (stat(file, &sb) < 0) {
300        err("%S: %s: %m\n", pcp->host, file);
301        goto fail;
302    }
303
304    if (pcp->preserve) {
305        /* 
306         * 1: SEND stat time: "T%ld %ld %ld %ld\n" 
307         *    (st_mtime, st_mtime_usec, st_atime, st_atime_usec)
308         */
309        snprintf(tmpstr, sizeof(tmpstr), "T%ld %ld %ld %ld\n",
310                 (long) sb.st_mtime, 0L, sb.st_atime, 0L);
311        if (pcp_sendstr(pcp->outfd, tmpstr, pcp->host) < 0)
312            goto fail;
313
314        /* 2: RECV response code */
315        if (pcp_response(pcp->infd, pcp->host) < 0)
316            goto fail;
317    }
318
319    if (S_ISDIR(sb.st_mode)) {
320        /* 
321         * 3a: SEND directory mode: "D%04o %d %s\n"
322         *     (st_mode & RCP_MODEMASK, 0, name)
323         */
324        snprintf(tmpstr, sizeof(tmpstr), "D%04o %d %s\n",
325                 sb.st_mode & RCP_MODEMASK, 0, xbasename(output_file));
326        if (pcp_sendstr(pcp->outfd, tmpstr, pcp->host) < 0)
327            goto fail;
328    } else {
329        /* 
330         * 3b: SEND file mode: "C%04o %lld %s\n" or "C%04o %ld %s\n"
331         *    (st_mode & MODE_MASK, st_size, basename(filename))
332         *    Use second template if sizeof(st_size) > sizeof(long).
333         */
334        template = (sizeof(sb.st_size) > sizeof(long)
335                    ? "C%04o %lld %s\n" : "C%04o %ld %s\n");
336        snprintf(tmpstr, sizeof(tmpstr), template,
337                 sb.st_mode & RCP_MODEMASK, sb.st_size, xbasename(output_file));
338        if (pcp_sendstr(pcp->outfd, tmpstr, pcp->host) < 0)
339            goto fail;
340    }
341
342    /* 4: RECV response code */
343    if (pcp_response(pcp->infd, pcp->host) < 0)
344        goto fail;
345
346    if (S_ISREG(sb.st_mode)) {
347        /* 5: SEND data */
348        if (_pcp_send_file_data(pcp->outfd, file, pcp->host) < 0)
349            goto fail;
350
351        /* 6: SEND NULL byte */
352        if (_pcp_write(pcp->outfd, "", 1) < 0)
353            goto fail;
354
355        /* 7: RECV response code */
356        if (pcp_response(pcp->infd, pcp->host) < 0)
357            goto fail;
358    }
359
360    result = 1;                 /* indicate success */
361  fail:
362    return result;
363}
364
365static int _pcp_sendfile (struct pcp_filename *pf, struct pcp_client *pcp)
366{
367	char *output_filename = NULL;
368
369	if (strcmp(pf->filename, EXIT_SUBDIR_FILENAME) == 0) {
370		if (pcp_sendstr(pcp->outfd, EXIT_SUBDIR_FLAG, pcp->host) < 0)
371			errx("%p: failed to send exit subdir flag\n");
372		if (pcp_response(pcp->infd, pcp->host) < 0)
373			errx("%p: failed to exit subdir properly\n");
374		return (0);
375	}
376
377	/* during a reverse copy, the hostname has to be attached
378	 * to the end of the output filename for files specified
379	 * by the user.
380	 */
381	if (pcp->pcp_client && pf->file_specified_by_user) {
382		output_filename = Strdup(pf->filename);
383		xstrcat(&output_filename, ".");
384		xstrcat(&output_filename, pcp->host);
385	}
386
387	pcp_sendfile (pcp, pf->filename, output_filename);
388
389	return (0);
390}
391
392int pcp_client(struct pcp_client *pcp)
393{
394    /* 0: RECV response code */
395    if (pcp_response(pcp->infd, pcp->host) >= 0) {
396        struct pcp_filename *pf;
397        ListIterator i = list_iterator_create (pcp->infiles);
398        while ((pf = list_next (i)))
399            _pcp_sendfile (pf, pcp);
400        list_iterator_destroy (i);
401        return 0;
402    }
403    return -1;
404}