/src/pdsh/main.c

https://code.google.com/ · C · 430 lines · 273 code · 70 blank · 87 comment · 72 complexity · d44bc9982c059faaaac145cdd0163674 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 <signal.h>
  30. #include <errno.h>
  31. #include <sys/types.h>
  32. #include <sys/param.h>
  33. #if HAVE_UNISTD_H
  34. #include <unistd.h> /* setuid */
  35. #endif
  36. #include <sys/wait.h> /* wait */
  37. #include <string.h> /* strcmp */
  38. #include <stdlib.h> /* exit */
  39. #include <stdio.h>
  40. #if HAVE_READLINE
  41. #include <readline/readline.h>
  42. #include <readline/history.h>
  43. #include <sys/types.h>
  44. #include <sys/stat.h>
  45. #include <fcntl.h>
  46. #endif
  47. #include "src/common/err.h"
  48. #include "src/common/xmalloc.h"
  49. #include "src/common/xstring.h"
  50. #include "dsh.h"
  51. #include "opt.h"
  52. #include "mod.h"
  53. #include "pcp_client.h"
  54. #include "pcp_server.h"
  55. #include "privsep.h"
  56. extern const char *pdsh_module_dir;
  57. static void _interactive_dsh(opt_t *);
  58. static int _pcp_remote_client (opt_t *);
  59. static int _pcp_remote_server (opt_t *);
  60. int main(int argc, char *argv[])
  61. {
  62. opt_t opt;
  63. int retval = 0;
  64. const char *m;
  65. /*
  66. * Initialize.
  67. */
  68. err_init(xbasename(argv[0])); /* init err package */
  69. /*
  70. * If running setuid, fork a child to handle
  71. * all privileged operations and drop privs in this process.
  72. */
  73. privsep_init();
  74. /*
  75. * Seed options with default values:
  76. */
  77. opt_default(&opt, argv[0]);
  78. /*
  79. * Override defaults with environment
  80. */
  81. opt_env(&opt);
  82. /*
  83. * Process any options that need to be handled early:
  84. */
  85. opt_args_early(&opt, argc, argv);
  86. /*
  87. * Load static or dynamic pdsh modules
  88. */
  89. mod_init();
  90. /*
  91. * Allow module directory to be overridden, but not when
  92. * running as root or setuid. (This is mainly for testing...)
  93. */
  94. if (!(m = getenv ("PDSH_MODULE_DIR")) ||
  95. getuid() == 0 ||
  96. getuid() != geteuid())
  97. m = pdsh_module_dir;
  98. if (mod_load_modules(m, &opt) < 0)
  99. errx("%p: Couldn't load any pdsh modules\n");
  100. /*
  101. * Handle options.
  102. */
  103. opt_args(&opt, argc, argv); /* override with command line */
  104. if (opt_verify(&opt)) { /* verify options, print errors */
  105. /*
  106. * Do the work.
  107. */
  108. if (opt.info_only) /* display info only */
  109. opt_list(&opt);
  110. else if (pdsh_personality() == PCP && opt.pcp_server)
  111. retval = (_pcp_remote_server (&opt) < 0);
  112. else if (pdsh_personality() == PCP && opt.pcp_client)
  113. retval = (_pcp_remote_client (&opt) < 0);
  114. else if (pdsh_personality() == PCP || opt.cmd != NULL)
  115. retval = dsh(&opt); /* single dsh/pcp command */
  116. else /* prompt loop */
  117. _interactive_dsh(&opt);
  118. } else {
  119. retval = 1;
  120. }
  121. mod_exit();
  122. /*
  123. * Clean up.
  124. */
  125. privsep_fini();
  126. opt_free(&opt); /* free heap storage in opt struct */
  127. err_cleanup();
  128. return retval;
  129. }
  130. #if HAVE_READLINE
  131. static int _history_file_create (char *path, size_t len)
  132. {
  133. char * home;
  134. struct stat sbuf;
  135. int fd;
  136. int rc;
  137. int n;
  138. memset (path, 0, len);
  139. if (!(home = getenv ("HOME"))) {
  140. err ("%p: Unable to read HOME env var\n");
  141. return (-1);
  142. }
  143. n = snprintf (path, len, "%s/.pdsh", home);
  144. if ((n < 0) || (n >= len)) {
  145. err ("%p: Unable to open %s/.pdsh: path too long\n", home);
  146. return (-1);
  147. }
  148. /* Check for ~/.pdsh directory
  149. * and create if it does not exist
  150. */
  151. if (lstat (path, &sbuf) < 0) {
  152. if (errno == ENOENT) {
  153. if (mkdir (path, 0700) < 0) {
  154. err ("%p: Unable to create ~/.pdsh: %m\n");
  155. return (-1);
  156. }
  157. } else {
  158. err ("%p: Couldn't find ~/.pdsh: %m\n");
  159. return (-1);
  160. }
  161. } else if (!(S_ISDIR (sbuf.st_mode))) {
  162. err ("%p: ~/.pdsh exists but is not a directory\n");
  163. return (-1);
  164. }
  165. /* Now should have ~/.pdsh/,
  166. * create ~/.pdsh/history if it does not exist.
  167. */
  168. n = snprintf (path, len, "%s/.pdsh/history", home);
  169. if ((n < 0) || (n >= len)) {
  170. err ("%p: Unable to open %s/.pdsh/history: path too long\n", home);
  171. return (-1);
  172. }
  173. if ((fd = open (path, O_CREAT, 00600)) < 0) {
  174. err ("%p: Error: Unable to create history file \"%s\": %m\n", path);
  175. return (-1);
  176. }
  177. close (fd);
  178. if ((rc = read_history (path))) {
  179. err ("%p: Warning: Unable to read history file \"%s\": %s\n",
  180. path, strerror (rc));
  181. return (-1);
  182. }
  183. return (0);
  184. }
  185. static void _history_list (void)
  186. {
  187. HIST_ENTRY **list;
  188. int i;
  189. if (!(list = history_list ()))
  190. return;
  191. for (i = 0; list[i]; i++) {
  192. out ("%p: %d: %s\n", i + history_base, list[i]->line);
  193. }
  194. return;
  195. }
  196. /* Warning: May be running setuid root! */
  197. static void _interactive_dsh(opt_t * opt)
  198. {
  199. pid_t pid;
  200. char prompt[64];
  201. char history_filename[MAXPATHLEN];
  202. char *cmd = NULL;
  203. int got_history_file = 1;
  204. int len;
  205. snprintf(prompt, sizeof(prompt), "%s> ", opt->progname);
  206. using_history ();
  207. len = sizeof (history_filename);
  208. if (_history_file_create (history_filename, len) < 0) {
  209. got_history_file = 0;
  210. }
  211. while ((cmd = readline(prompt)) != NULL) {
  212. int errnum;
  213. char *expansion;
  214. if ((errnum = history_expand (cmd, &expansion))) {
  215. err ("%p: %s\n", expansion);
  216. }
  217. free (cmd);
  218. if ((errnum < 0) || (errnum == 2)) {
  219. free (expansion);
  220. continue;
  221. }
  222. cmd = expansion;
  223. if (!strcmp(cmd, "history")) {
  224. _history_list ();
  225. continue;
  226. }
  227. add_history (cmd);
  228. if (strlen(cmd) == 0) { /* empty line */
  229. free(cmd);
  230. continue;
  231. }
  232. if (!strcmp(cmd, "quit") || !strcmp(cmd, "exit")) {
  233. free(cmd); /* quit or exit */
  234. break;
  235. }
  236. if ((strlen(cmd) != 0) && (got_history_file))
  237. append_history (1, history_filename);
  238. /*
  239. * fork dsh so we can ignore SIGINT in prompt loop
  240. */
  241. switch (pid = fork()) {
  242. case -1: /* error */
  243. errx("%p: fork: %m\n");
  244. case 0: /* child - run cmd */
  245. opt->cmd = Strdup(cmd);
  246. dsh(opt);
  247. Free((void **) &opt->cmd);
  248. exit(0);
  249. default: /* parent - wait */
  250. while (waitpid(pid, NULL, 0) < 0) {
  251. if (errno != EINTR)
  252. break;
  253. }
  254. break;
  255. }
  256. free (cmd);
  257. }
  258. }
  259. #else
  260. static char *_getcmd(char *);
  261. static void _shell(uid_t, char *);
  262. /*
  263. * Prompt for dsh commands, then execute them, prompt again, ...
  264. * "quit", "exit", or ^D to get out.
  265. * opt (IN) program options struct
  266. */
  267. static void _interactive_dsh(opt_t * opt)
  268. {
  269. pid_t pid;
  270. signal(SIGINT, SIG_IGN);
  271. while ((opt->cmd = _getcmd(opt->progname))) {
  272. if (*opt->cmd == '\0') { /* empty command */
  273. Free((void **) &opt->cmd);
  274. continue;
  275. }
  276. if (*opt->cmd == '!') { /* shell escape */
  277. _shell(opt->luid, opt->cmd + 1);
  278. Free((void **) &opt->cmd);
  279. continue;
  280. }
  281. if (strcmp(opt->cmd, "quit") == 0 /* user exit */
  282. || strcmp(opt->cmd, "exit") == 0) {
  283. Free((void **) &opt->cmd);
  284. break;
  285. }
  286. /* must fork dsh so we can ignore SIGINT in prompt loop */
  287. switch (pid = fork()) {
  288. case -1:
  289. errx("%p: fork: %m\n");
  290. case 0:
  291. dsh(opt); /* run command */
  292. exit(0);
  293. default:
  294. while (waitpid(pid, NULL, 0) < 0 && errno == EINTR);
  295. }
  296. Free((void **) &opt->cmd);
  297. }
  298. }
  299. /*
  300. * Run a command that was shell-escaped from the dsh> prompt. Run it as
  301. * the real uid of the invoking user, so we must fork to maintain root
  302. * effective uid in the parent.
  303. * uid (IN) uid used to execute command
  304. * cmd (IN) command and args
  305. */
  306. static void _shell(uid_t uid, char *cmd)
  307. {
  308. pid_t pid;
  309. switch (pid = fork()) {
  310. case -1:
  311. errx("%p: fork: %m\n");
  312. case 0:
  313. setuid(uid);
  314. system(cmd);
  315. exit(0);
  316. default:
  317. waitpid(pid, NULL, 0);
  318. }
  319. }
  320. /*
  321. * Prompt for a command and return it.
  322. * prompt (IN) string used to build prompt (e.g. program name)
  323. */
  324. static char *_getcmd(char *prompt)
  325. {
  326. char *cmd = NULL;
  327. char buf[LINEBUFSIZE];
  328. out("%s> ", prompt);
  329. if (fgets(buf, LINEBUFSIZE, stdin) != NULL) {
  330. buf[LINEBUFSIZE - 1] = '\0';
  331. xstrcln(buf, NULL);
  332. cmd = Strdup(buf);
  333. }
  334. return cmd;
  335. }
  336. #endif /* WITH_READLINE */
  337. static int _pcp_remote_server (opt_t *opt)
  338. {
  339. struct pcp_server svr[1];
  340. svr->infd = STDIN_FILENO;
  341. svr->outfd = STDOUT_FILENO;
  342. svr->preserve = opt->preserve;
  343. svr->target_is_dir = opt->target_is_directory;
  344. svr->outfile = opt->outfile_name;
  345. return (pcp_server (svr));
  346. }
  347. static int _pcp_remote_client (opt_t *opt)
  348. {
  349. struct pcp_client pcp[1];
  350. pcp->infd = STDIN_FILENO;
  351. pcp->outfd = STDOUT_FILENO;
  352. pcp->infiles = pcp_expand_dirs (opt->infile_names);
  353. pcp->host = opt->pcp_client_host;
  354. pcp->preserve = opt->preserve;
  355. pcp->pcp_client = opt->pcp_client;
  356. return (pcp_client (pcp));
  357. }
  358. /*
  359. * vi:tabstop=4 shiftwidth=4 expandtab
  360. */