b531496a14b577dde5628998986a6361c8538a63
[miniexpect.git] / example-sshpass.c
1 /* miniexpect example.
2  * Copyright (C) 2014 Red Hat Inc.
3  *
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.
8  *
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.
13  *
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
17  */
18
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.
22  *
23  * The first argument is the password to send at the password prompt.
24  * The remaining arguments are passed to the ssh subprocess.
25  *
26  * For example:
27  *   sshpass [-d] 123456 ssh remote.example.com
28  *   sshpass [-d] 123456 ssh -l root remote.example.com
29  *
30  * Use the -d flag to enable debugging to stderr.
31  */
32
33 #include <config.h>
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <assert.h>
40
41 #include <pcre.h>
42
43 #include "miniexpect.h"
44
45 static pcre *compile_re (const char *rex);
46
47 static void
48 usage (void)
49 {
50   fprintf (stderr, "usage: sshpass [-d] PASSWORD ssh [SSH-ARGS...] HOST\n");
51   exit (EXIT_FAILURE);
52 }
53
54 int
55 main (int argc, char *argv[])
56 {
57   int opt;
58   int debug = 0;
59   mexp_h *h;
60   const char *password;
61   int status;
62   pcre *password_re, *prompt_re, *hello_re;
63   const int ovecsize = 12;
64   int ovector[ovecsize];
65
66   while ((opt = getopt (argc, argv, "d")) != -1) {
67     switch (opt) {
68     case 'd':
69       debug = 1;
70       break;
71     default:
72       usage ();
73     }
74   }
75
76   if (argc-optind <= 2)
77     usage ();
78
79   password = argv[optind];
80   optind++;
81
82   printf ("starting ssh command ...\n");
83
84   h = mexp_spawnv (argv[optind], &argv[optind]);
85   if (h == NULL) {
86     perror ("mexp_spawnv: ssh");
87     exit (EXIT_FAILURE);
88   }
89   if (debug)
90     mexp_set_debug_file (h, stderr);
91
92   /* Wait for the password prompt. */
93   password_re = compile_re ("assword");
94   switch (mexp_expect (h,
95                        (mexp_regexp[]) {
96                          { 100, .re = password_re },
97                          { 0 }
98                        },
99                        ovector, ovecsize)) {
100   case 100:
101     break;
102   case MEXP_EOF:
103     fprintf (stderr, "error: ssh closed the connection unexpectedly\n");
104     goto error;
105   case MEXP_TIMEOUT:
106     fprintf (stderr, "error: timeout before reaching the password prompt\n");
107     goto error;
108   case MEXP_ERROR:
109     perror ("mexp_expect");
110     goto error;
111   case MEXP_PCRE_ERROR:
112     fprintf (stderr, "error: PCRE error: %d\n", mexp_get_pcre_error (h));
113     goto error;
114   }
115
116   /* Got the password prompt, so send a password.
117    *
118    * Note use of mexp_printf_password here which is identical to
119    * mexp_printf except that it hides the password in debugging
120    * output.
121    */
122   printf ("sending the password ...\n");
123
124   if (mexp_printf_password (h, "%s", password) == -1 ||
125       mexp_printf (h, "\n") == -1) {
126     perror ("mexp_printf");
127     goto error;
128   }
129
130   /* We have to wait for the prompt before we can send commands
131    * (because the ssh connection may not be fully established).  If we
132    * get "password" again, then probably the password was wrong.  This
133    * expect checks all these possibilities.  Unfortunately since all
134    * prompts are a little bit different, we have to guess here.
135    */
136   prompt_re = compile_re ("[#$]");
137   switch (mexp_expect (h,
138                        (mexp_regexp[]) {
139                          { 100, .re = password_re },
140                          { 101, .re = prompt_re },
141                          { 0 },
142                        },
143                        ovector, ovecsize)) {
144   case 100:                     /* Password. */
145     fprintf (stderr, "error: ssh asked for password again, probably the password supplied is wrong\n");
146     goto error;
147   case 101:                     /* Prompt. */
148     break;
149   case MEXP_EOF:
150     fprintf (stderr, "error: ssh closed the connection unexpectedly\n");
151     goto error;
152   case MEXP_TIMEOUT:
153     fprintf (stderr, "error: timeout before reaching the prompt\n");
154     goto error;
155   case MEXP_ERROR:
156     perror ("mexp_expect");
157     goto error;
158   case MEXP_PCRE_ERROR:
159     fprintf (stderr, "error: PCRE error: %d\n", mexp_get_pcre_error (h));
160     goto error;
161   }
162
163   /* Send a command which will have expected output. */
164   printf ("sending a test command ...\n");
165
166   if (mexp_printf (h, "echo h''ello\n") == -1) {
167     perror ("mexp_printf");
168     goto error;
169   }
170
171   /* Wait for expected output from echo hello command. */
172   hello_re = compile_re ("hello");
173   switch (mexp_expect (h,
174                        (mexp_regexp[]) {
175                          { 100, .re = hello_re },
176                          { 0 },
177                        },
178                        ovector, ovecsize)) {
179   case 100:
180     break;
181   case MEXP_EOF:
182     fprintf (stderr, "error: ssh closed the connection unexpectedly\n");
183     goto error;
184   case MEXP_TIMEOUT:
185     fprintf (stderr, "error: timeout before reading command output\n");
186     goto error;
187   case MEXP_ERROR:
188     perror ("mexp_expect");
189     goto error;
190   case MEXP_PCRE_ERROR:
191     fprintf (stderr, "error: PCRE error: %d\n", mexp_get_pcre_error (h));
192     goto error;
193   }
194
195   /* Send exit command and wait for ssh to exit. */
196   printf ("sending the exit command ...\n");
197
198   if (mexp_printf (h, "exit\n") == -1) {
199     perror ("mexp_printf");
200     goto error;
201   }
202
203   switch (mexp_expect (h, NULL, NULL, 0)) {
204   case MEXP_EOF:
205     /* This is what we're expecting: ssh will close the connection. */
206     break;
207   case MEXP_TIMEOUT:
208     fprintf (stderr, "error: timeout before ssh closed the connection\n");
209     goto error;
210   case MEXP_ERROR:
211     perror ("mexp_expect");
212     goto error;
213   case MEXP_PCRE_ERROR:
214     fprintf (stderr, "error: unexpected return value from mexp_expect\n");
215     goto error;
216   }
217
218   /* Close the ssh connection. */
219   status = mexp_close (h);
220   if (status != 0) {
221     fprintf (stderr, "error: bad exit status from ssh subprocess (status=%d)\n",
222              status);
223     exit (EXIT_FAILURE);
224   }
225
226   printf ("test was successful\n");
227
228   exit (EXIT_SUCCESS);
229
230  error:
231   mexp_close (h);
232   exit (EXIT_FAILURE);
233 }
234
235 /* Helper function to compile a PCRE regexp. */
236 static pcre *
237 compile_re (const char *rex)
238 {
239   const char *errptr;
240   int erroffset;
241   pcre *ret;
242
243   ret = pcre_compile (rex, 0, &errptr, &erroffset, NULL);
244   if (ret == NULL) {
245     fprintf (stderr, "error: failed to compile regular expression '%s': %s at offset %d\n",
246              rex, errptr, erroffset);
247     exit (EXIT_FAILURE);
248   }
249   return ret;
250 }