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