/src/pdsh/pcp_client.c
https://code.google.com/ · C · 404 lines · 239 code · 48 blank · 117 comment · 53 complexity · 1aa703376dc01110de728c4e95666c80 MD5 · raw file
- /*****************************************************************************\
- * $Id$
- *****************************************************************************
- * Copyright (C) 2001-2006 The Regents of the University of California.
- * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
- * Written by Jim Garlick <garlick@llnl.gov>.
- * UCRL-CODE-2003-005.
- *
- * This file is part of Pdsh, a parallel remote shell program.
- * For details, see <http://www.llnl.gov/linux/pdsh/>.
- *
- * Pdsh is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- *
- * Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along
- * with Pdsh; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- \*****************************************************************************/
- /*
- * Copyright (c) 1983, 1990 The Regents of the University of California.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Advertising clause removed per the following letter:
- * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
- #if HAVE_CONFIG_H
- # include "config.h"
- #endif
- #include <sys/param.h> /* roundup() */
- #if HAVE_SYS_SYSMACROS_H
- # include <sys/sysmacros.h>
- #endif
- #include <sys/stat.h>
- #include <sys/param.h>
- #include <sys/types.h>
- #include <errno.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <dirent.h>
- #include <stdio.h>
- #include <stdarg.h>
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #include <assert.h>
- #include "src/common/err.h"
- #include "src/common/fd.h"
- #include "src/common/list.h"
- #include "src/common/xstring.h"
- #include "src/common/err.h"
- #include "src/common/xmalloc.h"
- #include "pcp_client.h"
- #include "wcoll.h"
- #ifndef MAXPATHNAMELEN
- #define MAXPATHNAMELEN MAXPATHLEN
- #endif
- static void _rexpand_dir(List list, char *name)
- {
- DIR *dir;
- struct dirent *dp;
- struct stat sb;
- char file[MAXPATHNAMELEN];
- struct pcp_filename *pf = NULL;
- dir = opendir(name);
- if (dir == NULL)
- errx("%p: opendir: %s: %m\n", name);
- while ((dp = readdir(dir))) {
- if (dp->d_ino == 0)
- continue;
- if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
- continue;
- snprintf(file, sizeof(file), "%s/%s", name, dp->d_name);
- if (stat(file, &sb) < 0)
- errx("%p: can't stat %s: %m\n", file);
- if (access(name, R_OK) < 0)
- errx("%p: access: %s: %m\n", name);
- if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode))
- errx("%p: not a regular file or directory: %s\n", file);
- /* XXX: This memleaks */
- pf = Malloc(sizeof(struct pcp_filename));
- pf->filename = Strdup(file);
- pf->file_specified_by_user = 0;
- list_append(list, pf);
- if (S_ISDIR(sb.st_mode))
- _rexpand_dir(list, file);
- }
- closedir(dir);
- /* Since pdcp reads file names and directories only once for
- * efficiency, we must specify a special flag so we know when
- * to tell the server to "move up" the directory tree.
- */
-
- /* XXX: This memleaks */
- pf = Malloc(sizeof(struct pcp_filename));
- pf->filename = Strdup(EXIT_SUBDIR_FILENAME);
- pf->file_specified_by_user = 0;
- list_append(list, pf);
- }
- List pcp_expand_dirs(List infiles)
- {
- List new = list_create(NULL);
- struct stat sb;
- char *name;
- ListIterator i;
- i = list_iterator_create(infiles);
- while ((name = list_next(i))) {
- struct pcp_filename *pf = NULL;
- if (access(name, R_OK) < 0)
- errx("%p: access: %s: %m\n", name);
- if (stat(name, &sb) < 0)
- errx("%p: stat: %s: %m\n", name);
-
- /* XXX: This memleaks */
- pf = Malloc(sizeof(struct pcp_filename));
- pf->filename = name;
- pf->file_specified_by_user = 1;
- list_append(new, pf);
- /* -r option checked during command line argument checks */
- if (S_ISDIR(sb.st_mode))
- _rexpand_dir(new, name);
- }
-
- return new;
- }
- /*
- * Wrapper for the write system call that handles short writes.
- * Not sure if write ever returns short in practice but we have to be sure.
- * fd (IN) file descriptor to write to
- * buf (IN) data to write
- * size (IN) size of buf
- * RETURN -1 on failure, size on success
- */
- static int _pcp_write(int fd, char *buf, int size)
- {
- char *bufp = buf;
- int towrite = size;
- int outbytes;
- while (towrite > 0) {
- outbytes = write(fd, bufp, towrite);
- if (outbytes <= 0) {
- assert(outbytes != 0);
- return -1;
- }
- towrite -= outbytes;
- bufp += outbytes;
- }
- return size;
- }
- /*
- * Write the contents of the named file to the specified file descriptor.
- * outfd (IN) file descriptor to write to
- * filename (IN) name of file
- * host (IN) name of remote host for error messages
- * RETURN -1 on failure, 0 on success.
- */
- static int _pcp_send_file_data(int outfd, char *filename, char *host)
- {
- int filefd, inbytes, total = 0;
- char tmpbuf[BUFSIZ];
- filefd = open(filename, O_RDONLY);
- /* checked ahead of time - shouldn't happen */
- if (filefd < 0) {
- err("%S: _pcp_send_file_data: open %s: %m\n", host, filename);
- return -1;
- }
- do {
- inbytes = read(filefd, tmpbuf, BUFSIZ);
- if (inbytes < 0) {
- err("%S: _pcp_send_file_data: read %s: %m\n", host, filename);
- return -1;
- }
- if (inbytes > 0) {
- total += inbytes;
- if (_pcp_write(outfd, tmpbuf, inbytes) < 0) {
- err("%S: _pcp_send_file_data: write: %m\n", host);
- return -1;
- }
- }
- } while (inbytes > 0); /* until EOF */
- close(filefd);
- return 0;
- }
- /*
- * Send string to the specified file descriptor. Do not send trailing '\0'
- * as RCP terminates strings with newlines.
- * fd (IN) file descriptor to write to
- * str (IN) string to write
- * host (IN) name of remote host for error messages
- * RETURN -1 on failure, 0 on success
- */
- static int pcp_sendstr(int outfd, char *str, char *host)
- {
- int n;
- assert(strlen(str) > 0);
- assert(str[strlen(str) - 1] == '\n');
- if ((n = _pcp_write(outfd, str, strlen(str))) < 0)
- return -1;
- assert(n == strlen(str));
- return 0;
- }
- /*
- * Receive an RCP response code and possibly error message.
- * fd (IN) file desciptor to read from
- * host (IN) hostname for error messages
- * RETURN -1 on fatal error, 0 otherwise
- */
- static int pcp_response(int infd, char *host)
- {
- char resp;
- int i = 0, result = -1;
- int n;
- char errstr[BUFSIZ];
- if ((n = read(infd, &resp, sizeof(resp))) != sizeof(resp))
- return (-1);
- switch (resp) {
- case 0: /* ok */
- result = 0;
- break;
- default: /* just error string */
- errstr[i++] = resp;
- result = 0;
- case 1: /* fatal error + string */
- fd_read_line (infd, &errstr[i], BUFSIZ - i);
- err("%p: %S: %s: %s", host, result ? "fatal" : "error", errstr);
- break;
- }
- return result;
- }
- #define RCP_MODEMASK (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO)
- int pcp_sendfile(struct pcp_client *pcp, char *file, char *output_file)
- {
- int result = 0;
- char tmpstr[BUFSIZ], *template;
- struct stat sb;
- if (output_file == NULL)
- output_file = file;
- /*err("%S: %s\n", host, file); */
- if (stat(file, &sb) < 0) {
- err("%S: %s: %m\n", pcp->host, file);
- goto fail;
- }
- if (pcp->preserve) {
- /*
- * 1: SEND stat time: "T%ld %ld %ld %ld\n"
- * (st_mtime, st_mtime_usec, st_atime, st_atime_usec)
- */
- snprintf(tmpstr, sizeof(tmpstr), "T%ld %ld %ld %ld\n",
- (long) sb.st_mtime, 0L, sb.st_atime, 0L);
- if (pcp_sendstr(pcp->outfd, tmpstr, pcp->host) < 0)
- goto fail;
- /* 2: RECV response code */
- if (pcp_response(pcp->infd, pcp->host) < 0)
- goto fail;
- }
- if (S_ISDIR(sb.st_mode)) {
- /*
- * 3a: SEND directory mode: "D%04o %d %s\n"
- * (st_mode & RCP_MODEMASK, 0, name)
- */
- snprintf(tmpstr, sizeof(tmpstr), "D%04o %d %s\n",
- sb.st_mode & RCP_MODEMASK, 0, xbasename(output_file));
- if (pcp_sendstr(pcp->outfd, tmpstr, pcp->host) < 0)
- goto fail;
- } else {
- /*
- * 3b: SEND file mode: "C%04o %lld %s\n" or "C%04o %ld %s\n"
- * (st_mode & MODE_MASK, st_size, basename(filename))
- * Use second template if sizeof(st_size) > sizeof(long).
- */
- template = (sizeof(sb.st_size) > sizeof(long)
- ? "C%04o %lld %s\n" : "C%04o %ld %s\n");
- snprintf(tmpstr, sizeof(tmpstr), template,
- sb.st_mode & RCP_MODEMASK, sb.st_size, xbasename(output_file));
- if (pcp_sendstr(pcp->outfd, tmpstr, pcp->host) < 0)
- goto fail;
- }
- /* 4: RECV response code */
- if (pcp_response(pcp->infd, pcp->host) < 0)
- goto fail;
- if (S_ISREG(sb.st_mode)) {
- /* 5: SEND data */
- if (_pcp_send_file_data(pcp->outfd, file, pcp->host) < 0)
- goto fail;
- /* 6: SEND NULL byte */
- if (_pcp_write(pcp->outfd, "", 1) < 0)
- goto fail;
- /* 7: RECV response code */
- if (pcp_response(pcp->infd, pcp->host) < 0)
- goto fail;
- }
- result = 1; /* indicate success */
- fail:
- return result;
- }
- static int _pcp_sendfile (struct pcp_filename *pf, struct pcp_client *pcp)
- {
- char *output_filename = NULL;
- if (strcmp(pf->filename, EXIT_SUBDIR_FILENAME) == 0) {
- if (pcp_sendstr(pcp->outfd, EXIT_SUBDIR_FLAG, pcp->host) < 0)
- errx("%p: failed to send exit subdir flag\n");
- if (pcp_response(pcp->infd, pcp->host) < 0)
- errx("%p: failed to exit subdir properly\n");
- return (0);
- }
- /* during a reverse copy, the hostname has to be attached
- * to the end of the output filename for files specified
- * by the user.
- */
- if (pcp->pcp_client && pf->file_specified_by_user) {
- output_filename = Strdup(pf->filename);
- xstrcat(&output_filename, ".");
- xstrcat(&output_filename, pcp->host);
- }
- pcp_sendfile (pcp, pf->filename, output_filename);
- return (0);
- }
- int pcp_client(struct pcp_client *pcp)
- {
- /* 0: RECV response code */
- if (pcp_response(pcp->infd, pcp->host) >= 0) {
- struct pcp_filename *pf;
- ListIterator i = list_iterator_create (pcp->infiles);
- while ((pf = list_next (i)))
- _pcp_sendfile (pf, pcp);
- list_iterator_destroy (i);
- return 0;
- }
- return -1;
- }