1226031Sstas/* 2226031Sstas * Copyright (c) 2008 Kungliga Tekniska H��gskolan 3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4226031Sstas * All rights reserved. 5226031Sstas * 6226031Sstas * Redistribution and use in source and binary forms, with or without 7226031Sstas * modification, are permitted provided that the following conditions 8226031Sstas * are met: 9226031Sstas * 10226031Sstas * 1. Redistributions of source code must retain the above copyright 11226031Sstas * notice, this list of conditions and the following disclaimer. 12226031Sstas * 13226031Sstas * 2. Redistributions in binary form must reproduce the above copyright 14226031Sstas * notice, this list of conditions and the following disclaimer in the 15226031Sstas * documentation and/or other materials provided with the distribution. 16226031Sstas * 17226031Sstas * 3. Neither the name of the Institute nor the names of its contributors 18226031Sstas * may be used to endorse or promote products derived from this software 19226031Sstas * without specific prior written permission. 20226031Sstas * 21226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24226031Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31226031Sstas * SUCH DAMAGE. 32226031Sstas */ 33226031Sstas 34226031Sstas#include "config.h" 35226031Sstas 36226031Sstas#ifndef HAVE_SYS_TYPES_H 37226031Sstas#include <sys/types.h> 38226031Sstas#endif 39226031Sstas#ifdef HAVE_SYS_WAIT_H 40226031Sstas#include <sys/wait.h> 41226031Sstas#endif 42226031Sstas#include <stdio.h> 43226031Sstas#include <stdlib.h> 44226031Sstas#ifdef HAVE_UNISTD_H 45226031Sstas#include <unistd.h> 46226031Sstas#endif 47226031Sstas#ifdef HAVE_PTY_H 48226031Sstas#include <pty.h> 49226031Sstas#endif 50226031Sstas#ifdef HAVE_UTIL_H 51226031Sstas#include <util.h> 52226031Sstas#endif 53226031Sstas#ifdef HAVE_LIBUTIL_H 54226031Sstas#include <libutil.h> 55226031Sstas#endif 56226031Sstas 57226031Sstas#ifdef STREAMSPTY 58226031Sstas#include <stropts.h> 59226031Sstas#endif /* STREAMPTY */ 60226031Sstas 61226031Sstas#include "roken.h" 62226031Sstas#include <getarg.h> 63226031Sstas 64226031Sstasstruct command { 65226031Sstas enum { CMD_EXPECT = 0, CMD_SEND, CMD_PASSWORD } type; 66226031Sstas unsigned int lineno; 67226031Sstas char *str; 68226031Sstas struct command *next; 69226031Sstas}; 70226031Sstas 71226031Sstas/* 72226031Sstas * 73226031Sstas */ 74226031Sstas 75226031Sstasstatic struct command *commands, **next = &commands; 76226031Sstas 77226031Sstasstatic sig_atomic_t alarmset = 0; 78226031Sstas 79226031Sstasstatic int timeout = 10; 80226031Sstasstatic int verbose; 81226031Sstasstatic int help_flag; 82226031Sstasstatic int version_flag; 83226031Sstas 84226031Sstasstatic int master; 85226031Sstasstatic int slave; 86226031Sstasstatic char line[256] = { 0 }; 87226031Sstas 88226031Sstasstatic void 89226031Sstascaught_signal(int signo) 90226031Sstas{ 91226031Sstas alarmset = signo; 92226031Sstas} 93226031Sstas 94226031Sstas 95226031Sstasstatic void 96226031Sstasopen_pty(void) 97226031Sstas{ 98226031Sstas#ifdef _AIX 99226031Sstas printf("implement open_pty\n"); 100226031Sstas exit(77); 101226031Sstas#endif 102226031Sstas#if defined(HAVE_OPENPTY) || defined(__linux) || defined(__osf__) /* XXX */ 103226031Sstas if(openpty(&master, &slave, line, 0, 0) == 0) 104226031Sstas return; 105226031Sstas#endif /* HAVE_OPENPTY .... */ 106226031Sstas#ifdef STREAMSPTY 107226031Sstas { 108226031Sstas char *clone[] = { 109226031Sstas "/dev/ptc", 110226031Sstas "/dev/ptmx", 111226031Sstas "/dev/ptm", 112226031Sstas "/dev/ptym/clone", 113226031Sstas NULL 114226031Sstas }; 115226031Sstas char **q; 116226031Sstas 117226031Sstas for(q = clone; *q; q++){ 118226031Sstas master = open(*q, O_RDWR); 119226031Sstas if(master >= 0){ 120226031Sstas#ifdef HAVE_GRANTPT 121226031Sstas grantpt(master); 122226031Sstas#endif 123226031Sstas#ifdef HAVE_UNLOCKPT 124226031Sstas unlockpt(master); 125226031Sstas#endif 126226031Sstas strlcpy(line, ptsname(master), sizeof(line)); 127226031Sstas slave = open(line, O_RDWR); 128226031Sstas if (slave < 0) 129226031Sstas errx(1, "failed to open slave when using %s", *q); 130226031Sstas ioctl(slave, I_PUSH, "ptem"); 131226031Sstas ioctl(slave, I_PUSH, "ldterm"); 132226031Sstas 133226031Sstas return; 134226031Sstas } 135226031Sstas } 136226031Sstas } 137226031Sstas#endif /* STREAMSPTY */ 138226031Sstas 139226031Sstas /* more cases, like open /dev/ptmx, etc */ 140226031Sstas 141226031Sstas exit(77); 142226031Sstas} 143226031Sstas 144226031Sstas/* 145226031Sstas * 146226031Sstas */ 147226031Sstas 148226031Sstasstatic char * 149226031Sstasiscmd(const char *buf, const char *s) 150226031Sstas{ 151226031Sstas size_t len = strlen(s); 152226031Sstas if (strncmp(buf, s, len) != 0) 153226031Sstas return NULL; 154226031Sstas return estrdup(buf + len); 155226031Sstas} 156226031Sstas 157226031Sstasstatic void 158226031Sstasparse_configuration(const char *fn) 159226031Sstas{ 160226031Sstas struct command *c; 161226031Sstas char s[1024]; 162226031Sstas char *str; 163226031Sstas unsigned int lineno = 0; 164226031Sstas FILE *cmd; 165226031Sstas 166226031Sstas cmd = fopen(fn, "r"); 167226031Sstas if (cmd == NULL) 168226031Sstas err(1, "open: %s", fn); 169226031Sstas 170226031Sstas while (fgets(s, sizeof(s), cmd) != NULL) { 171226031Sstas 172226031Sstas s[strcspn(s, "#\n")] = '\0'; 173226031Sstas lineno++; 174226031Sstas 175226031Sstas c = calloc(1, sizeof(*c)); 176226031Sstas if (c == NULL) 177226031Sstas errx(1, "malloc"); 178226031Sstas 179226031Sstas c->lineno = lineno; 180226031Sstas (*next) = c; 181226031Sstas next = &(c->next); 182226031Sstas 183226031Sstas if ((str = iscmd(s, "expect ")) != NULL) { 184226031Sstas c->type = CMD_EXPECT; 185226031Sstas c->str = str; 186226031Sstas } else if ((str = iscmd(s, "send ")) != NULL) { 187226031Sstas c->type = CMD_SEND; 188226031Sstas c->str = str; 189226031Sstas } else if ((str = iscmd(s, "password ")) != NULL) { 190226031Sstas c->type = CMD_PASSWORD; 191226031Sstas c->str = str; 192226031Sstas } else 193226031Sstas errx(1, "Invalid command on line %d: %s", lineno, s); 194226031Sstas } 195226031Sstas 196226031Sstas fclose(cmd); 197226031Sstas} 198226031Sstas 199226031Sstas 200226031Sstas/* 201226031Sstas * 202226031Sstas */ 203226031Sstas 204226031Sstasstatic int 205226031Sstaseval_parent(pid_t pid) 206226031Sstas{ 207226031Sstas struct command *c; 208226031Sstas char in; 209226031Sstas size_t len = 0; 210226031Sstas ssize_t sret; 211226031Sstas 212226031Sstas for (c = commands; c != NULL; c = c->next) { 213226031Sstas switch(c->type) { 214226031Sstas case CMD_EXPECT: 215226031Sstas if (verbose) 216226031Sstas printf("[expecting %s]", c->str); 217226031Sstas len = 0; 218226031Sstas alarm(timeout); 219226031Sstas while((sret = read(master, &in, sizeof(in))) > 0) { 220226031Sstas alarm(timeout); 221226031Sstas printf("%c", in); 222226031Sstas if (c->str[len] != in) { 223226031Sstas len = 0; 224226031Sstas continue; 225226031Sstas } 226226031Sstas len++; 227226031Sstas if (c->str[len] == '\0') 228226031Sstas break; 229226031Sstas } 230226031Sstas alarm(0); 231226031Sstas if (alarmset == SIGALRM) 232226031Sstas errx(1, "timeout waiting for %s (line %u)", 233226031Sstas c->str, c->lineno); 234226031Sstas else if (alarmset) 235226031Sstas errx(1, "got a signal %d waiting for %s (line %u)", 236226031Sstas alarmset, c->str, c->lineno); 237226031Sstas if (sret <= 0) 238226031Sstas errx(1, "end command while waiting for %s (line %u)", 239226031Sstas c->str, c->lineno); 240226031Sstas break; 241226031Sstas case CMD_SEND: 242226031Sstas case CMD_PASSWORD: { 243226031Sstas size_t i = 0; 244226031Sstas const char *msg = (c->type == CMD_PASSWORD) ? "****" : c->str; 245226031Sstas 246226031Sstas if (verbose) 247226031Sstas printf("[send %s]", msg); 248226031Sstas 249226031Sstas len = strlen(c->str); 250226031Sstas 251226031Sstas while (i < len) { 252226031Sstas if (c->str[i] == '\\' && i < len - 1) { 253226031Sstas char ctrl; 254226031Sstas i++; 255226031Sstas switch(c->str[i]) { 256226031Sstas case 'n': ctrl = '\n'; break; 257226031Sstas case 'r': ctrl = '\r'; break; 258226031Sstas case 't': ctrl = '\t'; break; 259226031Sstas default: 260226031Sstas errx(1, "unknown control char %c (line %u)", 261226031Sstas c->str[i], c->lineno); 262226031Sstas } 263226031Sstas if (net_write(master, &ctrl, 1) != 1) 264226031Sstas errx(1, "command refused input (line %u)", c->lineno); 265226031Sstas } else { 266226031Sstas if (net_write(master, &c->str[i], 1) != 1) 267226031Sstas errx(1, "command refused input (line %u)", c->lineno); 268226031Sstas } 269226031Sstas i++; 270226031Sstas } 271226031Sstas break; 272226031Sstas } 273226031Sstas default: 274226031Sstas abort(); 275226031Sstas } 276226031Sstas } 277226031Sstas while(read(master, &in, sizeof(in)) > 0) 278226031Sstas printf("%c", in); 279226031Sstas 280226031Sstas if (verbose) 281226031Sstas printf("[end of program]\n"); 282226031Sstas 283226031Sstas /* 284226031Sstas * Fetch status from child 285226031Sstas */ 286226031Sstas { 287226031Sstas int ret, status; 288226031Sstas 289226031Sstas ret = waitpid(pid, &status, 0); 290226031Sstas if (ret == -1) 291226031Sstas err(1, "waitpid"); 292226031Sstas if (WIFEXITED(status) && WEXITSTATUS(status)) 293226031Sstas return WEXITSTATUS(status); 294226031Sstas else if (WIFSIGNALED(status)) { 295226031Sstas printf("killed by signal: %d\n", WTERMSIG(status)); 296226031Sstas return 1; 297226031Sstas } 298226031Sstas } 299226031Sstas return 0; 300226031Sstas} 301226031Sstas 302226031Sstas/* 303226031Sstas * 304226031Sstas */ 305226031Sstas 306226031Sstasstatic struct getargs args[] = { 307226031Sstas { "timeout", 't', arg_integer, &timeout, "timout", "seconds" }, 308226031Sstas { "verbose", 'v', arg_counter, &verbose, "verbose debugging" }, 309226031Sstas { "version", 0, arg_flag, &version_flag, "print version" }, 310226031Sstas { "help", 0, arg_flag, &help_flag, NULL } 311226031Sstas}; 312226031Sstas 313226031Sstasstatic void 314226031Sstasusage(int ret) 315226031Sstas{ 316226031Sstas arg_printusage (args, sizeof(args)/sizeof(*args), NULL, "infile command.."); 317226031Sstas exit (ret); 318226031Sstas} 319226031Sstas 320226031Sstasint 321226031Sstasmain(int argc, char **argv) 322226031Sstas{ 323226031Sstas int optidx = 0; 324226031Sstas pid_t pid; 325226031Sstas 326226031Sstas setprogname(argv[0]); 327226031Sstas 328226031Sstas if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) 329226031Sstas usage(1); 330226031Sstas 331226031Sstas if (help_flag) 332226031Sstas usage (0); 333226031Sstas 334226031Sstas if (version_flag) { 335226031Sstas fprintf (stderr, "%s from %s-%s\n", getprogname(), PACKAGE, VERSION); 336226031Sstas return 0; 337226031Sstas } 338226031Sstas 339226031Sstas argv += optidx; 340226031Sstas argc -= optidx; 341226031Sstas 342226031Sstas if (argc < 2) 343226031Sstas usage(1); 344226031Sstas 345226031Sstas parse_configuration(argv[0]); 346226031Sstas 347226031Sstas argv += 1; 348226031Sstas 349226031Sstas open_pty(); 350226031Sstas 351226031Sstas pid = fork(); 352226031Sstas switch (pid) { 353226031Sstas case -1: 354226031Sstas err(1, "Failed to fork"); 355226031Sstas case 0: 356226031Sstas 357226031Sstas if(setsid()<0) 358226031Sstas err(1, "setsid"); 359226031Sstas 360226031Sstas dup2(slave, STDIN_FILENO); 361226031Sstas dup2(slave, STDOUT_FILENO); 362226031Sstas dup2(slave, STDERR_FILENO); 363226031Sstas closefrom(STDERR_FILENO + 1); 364226031Sstas 365226031Sstas execvp(argv[0], argv); /* add NULL to end of array ? */ 366226031Sstas err(1, "Failed to exec: %s", argv[0]); 367226031Sstas default: 368226031Sstas close(slave); 369226031Sstas { 370226031Sstas struct sigaction sa; 371226031Sstas 372226031Sstas sa.sa_handler = caught_signal; 373226031Sstas sa.sa_flags = 0; 374226031Sstas sigemptyset (&sa.sa_mask); 375226031Sstas 376226031Sstas sigaction(SIGALRM, &sa, NULL); 377226031Sstas } 378226031Sstas 379226031Sstas return eval_parent(pid); 380226031Sstas } 381226031Sstas} 382