Add samba4kpasswd and rkpty binaries
[tprouty/samba.git] / source4 / heimdal / lib / roken / rkpty.c
1 /*
2  * Copyright (c) 2008 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "config.h"
35
36 #ifndef HAVE_SYS_TYPES_H
37 #include <sys/types.h>
38 #endif
39 #ifdef HAVE_SYS_WAIT_H
40 #include <sys/wait.h>
41 #endif
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #ifdef HAVE_PTY_H
46 #include <pty.h>
47 #endif
48 #ifdef HAVE_UTIL_H
49 #include <util.h>
50 #endif
51
52 #include "roken.h"
53 #include <getarg.h>
54
55 struct command {
56     enum { CMD_EXPECT = 0, CMD_SEND, CMD_PASSWORD } type;
57     unsigned int lineno;
58     char *str;
59     struct command *next;
60 };
61
62 /*
63  *
64  */
65
66 static struct command *commands, **next = &commands;
67
68 static sig_atomic_t alarmset = 0;
69
70 static int timeout = 10;
71 static int verbose;
72 static int help_flag;
73 static int version_flag;
74
75 static int master;
76 static int slave;
77 static char line[256] = { 0 };
78
79 static void
80 caught_signal(int signo)
81 {
82     alarmset = signo;
83 }
84
85
86 static void
87 open_pty(void)
88 {
89 #if defined(HAVE_OPENPTY) || defined(__linux) || defined(__osf__) /* XXX */
90     if(openpty(&master, &slave, line, 0, 0) == 0)
91         return;
92 #endif /* HAVE_OPENPTY .... */
93     /* more cases, like open /dev/ptmx, etc */
94
95     exit(77);
96 }
97
98 /*
99  *
100  */
101
102 static char *
103 iscmd(const char *buf, const char *s)
104 {
105     size_t len = strlen(s);
106     if (strncmp(buf, s, len) != 0)
107         return NULL;
108     return estrdup(buf + len);
109 }
110
111 static void
112 parse_configuration(const char *fn)
113 {
114     struct command *c;
115     char s[1024];
116     char *str;
117     unsigned int lineno = 0;
118     FILE *cmd;
119
120     cmd = fopen(fn, "r");
121     if (cmd == NULL)
122         err(1, "open: %s", fn);
123
124     while (fgets(s, sizeof(s),  cmd) != NULL) {
125
126         s[strcspn(s, "#\n")] = '\0';
127         lineno++;
128
129         c = calloc(1, sizeof(*c));
130         if (c == NULL)
131             errx(1, "malloc");
132
133         c->lineno = lineno;
134         (*next) = c;
135         next = &(c->next);
136
137         if ((str = iscmd(s, "expect ")) != NULL) {
138             c->type = CMD_EXPECT;
139             c->str = str;
140         } else if ((str = iscmd(s, "send ")) != NULL) {
141             c->type = CMD_SEND;
142             c->str = str;
143         } else if ((str = iscmd(s, "password ")) != NULL) {
144             c->type = CMD_PASSWORD;
145             c->str = str;
146         } else
147             errx(1, "Invalid command on line %d: %s", lineno, s);
148     }
149
150     fclose(cmd);
151 }
152
153
154 /*
155  *
156  */
157
158 static int
159 eval_parent(pid_t pid)
160 {
161     struct command *c;
162     char in;
163     size_t len = 0;
164     ssize_t sret;
165
166     for (c = commands; c != NULL; c = c->next) {
167         switch(c->type) {
168         case CMD_EXPECT:
169             if (verbose)
170                 printf("[expecting %s]", c->str);
171             len = 0;
172             alarm(timeout);
173             while((sret = read(master, &in, sizeof(in))) > 0) {
174                 alarm(timeout);
175                 printf("%c", in);
176                 if (c->str[len] != in) {
177                     len = 0;
178                     continue;
179                 }
180                 len++;
181                 if (c->str[len] == '\0')
182                     break;
183             }
184             alarm(0);
185             if (alarmset == SIGALRM)
186                 errx(1, "timeout waiting for %s (line %u)", 
187                      c->str, c->lineno);
188             else if (alarmset)
189                 errx(1, "got a signal %d waiting for %s (line %u)", 
190                      alarmset, c->str, c->lineno);
191             if (sret <= 0)
192                 errx(1, "end command while waiting for %s (line %u)",
193                      c->str, c->lineno);
194             break;
195         case CMD_SEND:
196         case CMD_PASSWORD: {
197             size_t i = 0;
198             const char *msg = (c->type == CMD_PASSWORD) ? "****" : c->str;
199
200             if (verbose)
201                 printf("[send %s]", msg);
202
203             len = strlen(c->str);
204
205             while (i < len) {
206                 if (c->str[i] == '\\' && i < len - 1) {
207                     char ctrl;
208                     i++;
209                     switch(c->str[i]) {
210                     case 'n': ctrl = '\n'; break;
211                     case 'r': ctrl = '\r'; break;
212                     case 't': ctrl = '\t'; break;
213                     default:
214                         errx(1, "unknown control char %c (line %u)", 
215                              c->str[i], c->lineno);
216                     }
217                     if (net_write(master, &ctrl, 1) != 1)
218                         errx(1, "command refused input (line %u)", c->lineno);
219                 } else {
220                     if (net_write(master, &c->str[i], 1) != 1)
221                         errx(1, "command refused input (line %u)", c->lineno);
222                 }
223                 i++;
224             }
225             break;
226         }
227         default:
228             abort();
229         }
230     }
231     while(read(master, &in, sizeof(in)) > 0)
232         printf("%c", in);
233
234     if (verbose)
235         printf("[end of program]\n");
236
237     /*
238      * Fetch status from child
239      */
240     {
241         int ret, status;
242
243         ret = waitpid(pid, &status, 0);
244         if (ret == -1)
245             err(1, "waitpid");
246         if (WIFEXITED(status) && WEXITSTATUS(status))
247             return WEXITSTATUS(status);
248         else if (WIFSIGNALED(status)) {
249             printf("killed by signal: %d\n", WTERMSIG(status));
250             return 1;
251         }
252     }
253     return 0;
254 }
255
256 /*
257  *
258  */
259
260 static struct getargs args[] = {
261     { "timeout",        't', arg_integer, &timeout, "timout", "seconds" },
262     { "verbose",        'v', arg_counter, &verbose, "verbose debugging" },
263     { "version",        0, arg_flag,    &version_flag, "print version" },
264     { "help",           0, arg_flag,    &help_flag, NULL }
265 };
266
267 static void
268 usage(int ret)
269 {
270     arg_printusage (args, sizeof(args)/sizeof(*args), NULL, "infile command..");
271     exit (ret);
272 }
273
274 int
275 main(int argc, char **argv)
276 {
277     int optidx = 0;
278     pid_t pid;
279
280     setprogname(argv[0]);
281
282     if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
283         usage(1);
284
285     if (help_flag)
286         usage (0);
287
288     if (version_flag) {
289         fprintf (stderr, "%s from %s-%s\n", getprogname(), PACKAGE, VERSION);
290         return 0;
291     }
292
293     argv += optidx;
294     argc -= optidx;
295
296     if (argc < 2)
297         usage(1);
298
299     parse_configuration(argv[0]);
300
301     argv += 1;
302     argc -= 1;
303
304     open_pty();
305
306     pid = fork();
307     switch (pid) {
308     case -1:
309         err(1, "Failed to fork");
310     case 0:
311
312         if(setsid()<0)
313             err(1, "setsid");
314
315         dup2(slave, STDIN_FILENO);
316         dup2(slave, STDOUT_FILENO);
317         dup2(slave, STDERR_FILENO);
318         closefrom(STDERR_FILENO + 1);
319
320         execvp(argv[0], argv); /* add NULL to end of array ? */
321         err(1, "Failed to exec: %s", argv[0]);
322     default:
323         close(slave);
324         {
325             struct sigaction sa;
326
327             sa.sa_handler = caught_signal;
328             sa.sa_flags = 0;
329             sigemptyset (&sa.sa_mask);
330             
331             sigaction(SIGALRM, &sa, NULL);
332         }
333
334         return eval_parent(pid);
335     }
336 }