2 * Copyright (C) 2014-2022 Red Hat Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 /* There is a program called 'sshpass' which does roughly the same
20 * as this simplified example. We run ssh as a subprocess, and log
21 * in using the given password from the command line.
23 * The first argument is the password to send at the password prompt.
24 * The remaining arguments are passed to the ssh subprocess.
27 * sshpass [-d] 123456 ssh remote.example.com
28 * sshpass [-d] 123456 ssh -l root remote.example.com
30 * Use the -d flag to enable debugging to stderr.
41 #define PCRE2_CODE_UNIT_WIDTH 8
44 #include "miniexpect.h"
46 static pcre2_code *compile_re (const char *rex);
51 fprintf (stderr, "usage: sshpass [-d] PASSWORD ssh [SSH-ARGS...] HOST\n");
56 main (int argc, char *argv[])
63 pcre2_code *password_re, *prompt_re, *hello_re;
64 pcre2_match_data *match_data;
66 match_data = pcre2_match_data_create (4, NULL);
68 while ((opt = getopt (argc, argv, "d")) != -1) {
81 password = argv[optind];
84 printf ("starting ssh command ...\n");
86 h = mexp_spawnv (argv[optind], &argv[optind]);
88 perror ("mexp_spawnv: ssh");
92 mexp_set_debug_file (h, stderr);
94 /* Wait for the password prompt. */
95 password_re = compile_re ("assword");
96 switch (mexp_expect (h,
98 { 100, .re = password_re },
105 fprintf (stderr, "error: ssh closed the connection unexpectedly\n");
108 fprintf (stderr, "error: timeout before reaching the password prompt\n");
111 perror ("mexp_expect");
113 case MEXP_PCRE_ERROR:
114 fprintf (stderr, "error: PCRE error: %d\n", mexp_get_pcre_error (h));
118 /* Got the password prompt, so send a password.
120 * Note use of mexp_printf_password here which is identical to
121 * mexp_printf except that it hides the password in debugging
124 printf ("sending the password ...\n");
126 if (mexp_printf_password (h, "%s", password) == -1 ||
127 mexp_printf (h, "\n") == -1) {
128 perror ("mexp_printf");
132 /* We have to wait for the prompt before we can send commands
133 * (because the ssh connection may not be fully established). If we
134 * get "password" again, then probably the password was wrong. This
135 * expect checks all these possibilities. Unfortunately since all
136 * prompts are a little bit different, we have to guess here.
138 prompt_re = compile_re ("[#$]");
139 switch (mexp_expect (h,
141 { 100, .re = password_re },
142 { 101, .re = prompt_re },
146 case 100: /* Password. */
147 fprintf (stderr, "error: ssh asked for password again, probably the password supplied is wrong\n");
149 case 101: /* Prompt. */
152 fprintf (stderr, "error: ssh closed the connection unexpectedly\n");
155 fprintf (stderr, "error: timeout before reaching the prompt\n");
158 perror ("mexp_expect");
160 case MEXP_PCRE_ERROR:
161 fprintf (stderr, "error: PCRE error: %d\n", mexp_get_pcre_error (h));
165 /* Send a command which will have expected output. */
166 printf ("sending a test command ...\n");
168 if (mexp_printf (h, "echo h''ello\n") == -1) {
169 perror ("mexp_printf");
173 /* Wait for expected output from echo hello command. */
174 hello_re = compile_re ("hello");
175 switch (mexp_expect (h,
177 { 100, .re = hello_re },
184 fprintf (stderr, "error: ssh closed the connection unexpectedly\n");
187 fprintf (stderr, "error: timeout before reading command output\n");
190 perror ("mexp_expect");
192 case MEXP_PCRE_ERROR:
193 fprintf (stderr, "error: PCRE error: %d\n", mexp_get_pcre_error (h));
197 /* Send exit command and wait for ssh to exit. */
198 printf ("sending the exit command ...\n");
200 if (mexp_printf (h, "exit\n") == -1) {
201 perror ("mexp_printf");
205 switch (mexp_expect (h, NULL, NULL)) {
207 /* This is what we're expecting: ssh will close the connection. */
210 fprintf (stderr, "error: timeout before ssh closed the connection\n");
213 perror ("mexp_expect");
215 case MEXP_PCRE_ERROR:
216 fprintf (stderr, "error: unexpected return value from mexp_expect\n");
220 /* Close the ssh connection. */
221 status = mexp_close (h);
223 fprintf (stderr, "error: bad exit status from ssh subprocess (status=%d)\n",
228 printf ("test was successful\n");
230 pcre2_match_data_free (match_data);
238 /* Helper function to compile a PCRE regexp. */
240 compile_re (const char *rex)
243 PCRE2_SIZE erroroffset;
247 ret = pcre2_compile ((PCRE2_SPTR) rex, PCRE2_ZERO_TERMINATED,
248 0, &errorcode, &erroroffset, NULL);
250 pcre2_get_error_message (errorcode,
251 (PCRE2_UCHAR *) errormsg, sizeof errormsg);
252 fprintf (stderr, "error: "
253 "failed to compile regular expression '%s': "
254 "%s at offset %zu\n",
255 rex, errormsg, erroroffset);