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