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