summaryrefslogtreecommitdiffstats
path: root/loader2
diff options
context:
space:
mode:
authorJeremy Katz <katzj@redhat.com>2002-12-09 23:09:52 +0000
committerJeremy Katz <katzj@redhat.com>2002-12-09 23:09:52 +0000
commit0923a61ba4a031a2e23128ca6475c3693676d940 (patch)
tree7c935dd1122335a0ac1374cbe7a45abc271c49d6 /loader2
parent4026940b2d7ba91ccc934d2dcf3fa6c674586c9a (diff)
downloadanaconda-0923a61ba4a031a2e23128ca6475c3693676d940.tar.gz
anaconda-0923a61ba4a031a2e23128ca6475c3693676d940.tar.xz
anaconda-0923a61ba4a031a2e23128ca6475c3693676d940.zip
merge telnet mode back in
Diffstat (limited to 'loader2')
-rw-r--r--loader2/Makefile3
-rw-r--r--loader2/loader.c7
-rw-r--r--loader2/telnet.c269
-rw-r--r--loader2/telnet.h40
-rw-r--r--loader2/telnetd.c236
-rw-r--r--loader2/telnetd.h8
6 files changed, 560 insertions, 3 deletions
diff --git a/loader2/Makefile b/loader2/Makefile
index 05c9bcf98..a83f7d252 100644
--- a/loader2/Makefile
+++ b/loader2/Makefile
@@ -32,8 +32,7 @@ OBJS = log.o moduleinfo.o loadermisc.o modules.o moduledeps.o windows.o \
md5.o mediacheck.o kickstart.o \
$(HWOBJS) $(METHOBJS)
LOADEROBJS = loader.o loader-pcmcia.o
-NETOBJS = net.o urls.o telnet.o telnetd.o
-NETOBJS = net.o urls.o ftp.o
+NETOBJS = net.o urls.o ftp.o telnet.o telnetd.o
PCMCIAOBJS = pcmcia.o $(NETOBJS)
SOURCES = $(subst .o,.c,$(OBJS)) loader.c
diff --git a/loader2/loader.c b/loader2/loader.c
index 1a9857a5f..6e52c3004 100644
--- a/loader2/loader.c
+++ b/loader2/loader.c
@@ -69,6 +69,8 @@
#include "hdinstall.h"
#include "urlinstall.h"
+#include "telnetd.h"
+
#include "../isys/imount.h"
#include "../isys/isys.h"
#include "../isys/probe.h"
@@ -352,6 +354,8 @@ static int parseCmdLineFlags(int flags, struct loaderData_s * loaderData,
flags |= LOADER_FLAGS_NOUSBSTORAGE;
else if (!strcasecmp(argv[i], "nousb"))
flags |= LOADER_FLAGS_NOUSB;
+ else if (!strcasecmp(argv[i], "telnet"))
+ flags |= LOADER_FLAGS_TELNETD;
else if (!strcasecmp(argv[i], "nofirewire"))
flags |= LOADER_FLAGS_NOIEEE1394;
else if (!strcasecmp(argv[i], "noprobe"))
@@ -865,7 +869,8 @@ int main(int argc, char ** argv) {
}
}
- /* JKFIXME: telnetd */
+ if (FL_TELNETD(flags))
+ startTelnetd(&kd, &loaderData, modInfo, modLoaded, modDeps, flags);
url = doLoaderMain("/mnt/source", &loaderData, &kd, modInfo, modLoaded, &modDeps, flags);
diff --git a/loader2/telnet.c b/loader2/telnet.c
new file mode 100644
index 000000000..fda836639
--- /dev/null
+++ b/loader2/telnet.c
@@ -0,0 +1,269 @@
+/* telnet.c -- basic telnet protocol handling for ttywatch
+ *
+ * Copyright © 2001 Michael K. Johnson <johnsonm@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* Shamelessly stolen from ttywatch -- oot */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "telnet.h"
+#include "log.h"
+
+#define IAC "\xff"
+#define DONT "\xfe"
+#define WONT "\xfc"
+#define WILL "\xfb"
+#define DO "\xfd"
+#define SB "\xfa"
+#define SE "\xf0"
+#define ECHO "\x01"
+#define SUPPRESS_GO_AHEAD "\x03"
+#define TERMINAL_TYPE "\x18"
+#define NAWS "\x1f"
+#define LINEMODE "\x22"
+#define NEWENVIRON "\x27"
+#define MODE "\x01"
+
+/* Make a request. Not intended to be RFC-compatible, just enough
+ * to convince telnet clients to do what we want... To do this
+ * right, we would have to honestly negotiate, not speak blind.
+ *
+ * For now, assume all responses will be favorable and stripped
+ * out in telnet_process_input()... Sending it all in a single
+ * write makes it more efficient because it will all go out in a
+ * single packet, and the responses are more likely to all come
+ * back in a single packet (and thus, practically, a single read)
+ * too.
+ */
+void
+telnet_negotiate(int socket, char ** term_type_ptr, int * heightPtr,
+ int * widthPtr) {
+ char ch;
+ int done = 0;
+ char * termType = NULL;
+ int termLength = 0, termAlloced = 0;
+ enum { ST_NONE, ST_TERMTYPE, ST_WINDOWSIZE } state;
+ char sizeBuf[4];
+ int height = -1, width = -1;
+ char * sizePtr = sizeBuf;
+ char request[]=
+ IAC DONT ECHO
+ IAC WILL ECHO
+ IAC WILL NAWS
+ IAC WILL SUPPRESS_GO_AHEAD
+ IAC DO SUPPRESS_GO_AHEAD
+ IAC DONT NEWENVIRON
+ IAC WONT NEWENVIRON
+ IAC WONT LINEMODE
+ IAC DO NAWS
+ IAC SB TERMINAL_TYPE "\x01" IAC SE
+ ;
+
+ write(socket, request, sizeof(request)-1);
+
+ /* Read from the terminal until we get the terminal type. This will
+ do bad things if the client doesn't send the terminal type, but
+ those clients have existed for aeons (right?) */
+
+ do {
+ read(socket, &ch, 1);
+ if (ch != '\xff') {
+ abort();
+ }
+
+ read(socket, &ch, 1); /* command */
+
+ if (ch != '\xfa') {
+ read(socket, &ch, 1); /* verb */
+ continue;
+ }
+
+ read(socket, &ch, 1); /* suboption */
+ if (ch == '\x18') {
+ state = ST_TERMTYPE;
+ read(socket, &ch, 1); /* should be 0x0! */
+ done = 1;
+ } else if (ch == '\x1f') {
+ state = ST_WINDOWSIZE;
+ } else {
+ state = ST_NONE;;
+ }
+
+ read(socket, &ch, 1); /* data */
+ while (ch != '\xff') {
+ if (state == ST_TERMTYPE) {
+ if (termAlloced == termLength) {
+ termAlloced += 10;
+ termType = realloc(termType, termAlloced + 1);
+ }
+
+ termType[termLength++] = tolower(ch);
+ } else if (state == ST_WINDOWSIZE) {
+ if ((sizePtr - sizeBuf) < sizeof(sizeBuf))
+ *sizePtr++ = ch;
+ }
+
+ read(socket, &ch, 1); /* data */
+ }
+
+ read(socket, &ch, 1); /* should be a SE */
+
+ } while (!done);
+
+ termType[termLength] = '\0';
+
+ if (sizePtr - sizeBuf == sizeof(sizeBuf)) {
+ width = (sizeBuf[0] << 8) + sizeBuf[1];
+ height = (sizeBuf[2] << 8) + sizeBuf[3];
+ }
+
+ if (heightPtr) *heightPtr = height;
+ if (widthPtr) *widthPtr = width;
+
+ if (term_type_ptr) *term_type_ptr = termType;
+}
+
+int
+telnet_process_input(telnet_state * ts, char *data, int len) {
+ char *s, *d; /* source, destination */
+
+# if DEBUG_TELNET
+ printf("\nprinting packet:");
+ for (s=data; s<data+len; s++) {
+ if (!((s-data)%10))
+ printf("\n %03d: ", s-data);
+ printf("%02x ", *s & 0x000000FF);
+ }
+ printf("\n");
+# endif /* DEBUG_TELNET */
+
+ for (s=data, d=data; s<data+len; s++) {
+ switch (*ts) {
+ case TS_DATA:
+ if (*s == '\xff') { /* IAC */
+ *ts = TS_IAC;
+ continue;
+ }
+#if DEBUG_TELNET
+ printf("copying data element '%c'\n", *s);
+#endif /* DEBUG_TELNET */
+ if (s>d) {
+ *(d++) = *s;
+ } else {
+ d++;
+ }
+ break;
+
+ case TS_IAC:
+ if (*s == '\xfa') { /* SB */
+ *ts = TS_SB;
+ continue;
+ }
+ /* if not SB, skip IAC verb object */
+# if DEBUG_TELNET
+ printf("skipping verb/object (offset %d)...\n", s-data-1);
+# endif /* DEBUG_TELNET */
+ s += 1;
+ *ts = TS_DATA;
+ break;
+
+ case TS_SB:
+# if DEBUG_TELNET
+ printf("skipping SB (offset %d)...\n", s-data-1);
+# endif /* DEBUG_TELNET */
+ while (s < (data+(len-1))) {
+ if (*s == '\xff') {
+ break; /* fall through to TS_SB_IAC setting below */
+ } else {
+ s++;
+ }
+ }
+ if (*s == '\xff') {
+ *ts = TS_SB_IAC;
+ }
+ break;
+
+ case TS_SB_IAC:
+ if (*s == '\xf0') { /* SE */
+# if DEBUG_TELNET
+ printf("SE ends SB (offset %d)...\n", s-data-1);
+# endif /* DEBUG_TELNET */
+ *ts = TS_DATA;
+ } else {
+# if DEBUG_TELNET
+ printf("IAC without SE in SB (offset %d)\n", s-data-1);
+# endif /* DEBUG_TELNET */
+ *ts = TS_SB;
+ }
+ break;
+
+ default:
+ logMessage("unknown telnet state %d for data element %c", *ts, *s);
+ *ts = TS_DATA;
+ break;
+ }
+ }
+
+ /* calculate new length after copying data around */
+ len = d - data;
+#if DEBUG_TELNET
+ printf("returning len: %d of packet:", len);
+ for (s=data; s<data+len; s++) {
+ if (!((s-data)%10))
+ printf("\n %03d: ", s-data);
+ printf("%02x ", *s & 0x000000FF);
+ }
+ printf("\n");
+#endif /* DEBUG_TELNET */
+
+ return len;
+}
+
+/* The telnet protocol requires CR/NL instead of just NL
+ * We normally deal with Unix, which just uses NL, so we need to translate.
+ *
+ * It would be easy to go through line-by-line and write each line, but
+ * that would create more packet overhead by sending out one packet
+ * per line, and over things like slow PPP connections, that is painful.
+ * Therefore, instead, we create a modified copy of the data and write
+ * the whole modified copy at once.
+ */
+void
+telnet_send_output(int sock, char *data, int len) {
+ char *s, *d; /* source, destination */
+ char *buf;
+
+ buf = alloca((len*2)+1); /* max necessary size */
+
+ /* just may need to add CR before NL (but do not double existing CRs) */
+ for (s=data, d=buf; d-buf<len; s++, d++) {
+ if ((*s == '\n') && (s == data || (*(s-1) != '\r'))) {
+ /* NL without preceding CR */
+ *(d++) = '\r';
+ len++;
+ }
+ *d = *s;
+ }
+
+ /* now send it... */
+ write(sock, buf, len);
+}
diff --git a/loader2/telnet.h b/loader2/telnet.h
new file mode 100644
index 000000000..58ea5ba0a
--- /dev/null
+++ b/loader2/telnet.h
@@ -0,0 +1,40 @@
+/* telnet.h -- basic telnet protocol handling for ttywatch
+ *
+ * Copyright © 2001 Michael K. Johnson <johnsonm@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+
+#ifndef __TELNET_H__
+#define __TELNET_H__
+
+typedef enum {
+ TS_DATA = 0,
+ TS_IAC,
+ TS_SB,
+ TS_SB_IAC,
+} telnet_state;
+
+void
+telnet_negotiate(int socket, char ** term_type_ptr, int * heightPtr,
+ int * widthPtr);
+int
+telnet_process_input(telnet_state * ts, char *data, int len);
+void
+telnet_send_output(int sock, char *data, int len);
+
+#endif /* __TELNET_H__ */
diff --git a/loader2/telnetd.c b/loader2/telnetd.c
new file mode 100644
index 000000000..796322523
--- /dev/null
+++ b/loader2/telnetd.c
@@ -0,0 +1,236 @@
+/*
+ * telnetd.c - glue to tie telnet.c from ttywatch to the loader
+ *
+ * Erik Troan <ewt@redhat.com>
+ * Jeremy Katz <katzj@redhat.com>
+ *
+ * Copyright 2002 Red Hat, Inc.
+ *
+ * This software may be freely redistributed under the terms of the GNU
+ * General Public License.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <newt.h>
+#include <pty.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "lang.h"
+#include "loader.h"
+#include "log.h"
+#include "modules.h"
+#include "net.h"
+#include "telnet.h"
+#include "windows.h"
+
+#include "../isys/probe.h"
+
+#ifndef IPPORT_TELNET
+#define IPPORT_TELNET 23
+#endif
+
+/* Forks, keeping the loader as our child (so we know when it dies). */
+int beTelnet(int flags) {
+ int sock;
+ int conn;
+ int addrLength;
+ pid_t child;
+ int i;
+ int masterFd;
+ struct sockaddr_in address;
+ char buf[4096];
+ struct pollfd fds[3];
+ telnet_state ts = TS_DATA;
+ char * termType;
+ int height, width;
+ struct winsize ws;
+
+ if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+ logMessage("socket: %s", strerror(errno));
+ return -1;
+ }
+
+ address.sin_family = AF_INET;
+ address.sin_port = htons(IPPORT_TELNET);
+ memset(&address.sin_addr, 0, sizeof(address.sin_addr));
+ addrLength = sizeof(address);
+
+ /* Let the kernel reuse the socket address. This lets us run
+ twice in a row, without waiting for the (ip, port) tuple
+ to time out. Makes testing much easier*/
+ conn = 1;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &conn, sizeof(conn));
+
+ bind(sock, (struct sockaddr *) &address, sizeof(address));
+ listen(sock, 5);
+
+ winStatus(45, 3, _("Telnet"), _("Waiting for telnet connection..."));
+
+ if ((conn = accept(sock, (struct sockaddr *) &address,
+ &addrLength)) < 0) {
+ newtWinMessage(_("Error"), _("OK"), "accept failed: %s",
+ strerror(errno));
+ close(sock);
+ return -1;
+ }
+
+ stopNewt();
+
+ close(sock);
+
+ telnet_negotiate(conn, &termType, &height, &width);
+
+#ifdef DEBUG
+ printf("got term type %s\n", termType);
+#endif
+
+ masterFd = open("/dev/ptyp0", O_RDWR);
+ if (masterFd < 0) {
+ logMessage("cannot open /dev/ttyp0");
+ close(conn);
+ return -1;
+ }
+
+ if (height != -1 && width != -1) {
+#ifdef DEBUF
+ printf("setting window size to %d x %d\n", width, height);
+#endif
+ ws.ws_row = height;
+ ws.ws_col = width;
+ ioctl(masterFd, TIOCSWINSZ, &ws);
+ }
+
+
+ child = fork();
+
+ if (child) {
+#ifndef DEBUG
+ startNewt(flags);
+ winStatus(45, 3, _("Telnet"), _("Running anaconda via telnet..."));
+#endif
+
+ fds[0].events = POLLIN;
+ fds[0].fd = masterFd;
+
+ fds[1].events = POLLIN;
+ fds[1].fd = conn;
+
+ while ((i = poll(fds, 2, -1)) > 0) {
+ if (fds[0].revents) {
+ i = read(masterFd, buf, sizeof(buf));
+
+#ifdef DEBUG
+ {
+ int j;
+ int row;
+
+ for (row = 0; row < (i / 12) + 1; row++) {
+ printf("wrote:");
+ for (j = (row * 12); j < i && j < ((row + 1) * 12); j++)
+ printf(" 0x%2x", (unsigned char) buf[j]);
+ printf("\n");
+ printf("wrote:");
+ for (j = (row * 12); j < i && j < ((row + 1) * 12); j++)
+ {
+ if (isprint(buf[j]))
+ printf(" %c ", buf[j]);
+ else
+ printf(" ");
+ }
+ printf("\n");
+ }
+ }
+#endif
+ /* child died */
+ if (i < 0)
+ break;
+
+ telnet_send_output(conn, buf, i);
+ }
+
+ if (fds[1].revents) {
+ i = read(conn, buf, sizeof(buf));
+
+ /* connection went away */
+ if (!i)
+ break;
+
+ i = telnet_process_input(&ts, buf, i);
+ write(masterFd, buf, i);
+
+#ifdef DEBUG
+ {
+ int j;
+
+ printf("got:");
+ for (j = 0; j < i; j++)
+ printf(" 0x%x", (unsigned char) buf[j]);
+ printf("\n");
+ }
+#endif
+
+ }
+ }
+
+
+ if (i < 0) {
+ logMessage("poll: %s", strerror(errno));
+ }
+
+#ifndef DEBUG
+ stopNewt();
+#endif
+
+ kill(child, SIGTERM);
+ close(conn);
+
+ exit(0);
+ }
+
+ close(masterFd);
+ setsid();
+ close(0);
+ close(1);
+ close(2);
+
+ open("/dev/ttyp0", O_RDWR);
+ dup(0);
+ dup(0);
+
+ /* brand new tty! */
+ setenv("TERM", termType, 1);
+
+ startNewt(flags);
+
+ return 0;
+}
+
+void startTelnetd(struct knownDevices * kd, struct loaderData_s * loaderData,
+ moduleInfoSet modInfo, moduleList modLoaded,
+ moduleDeps modDeps, int flags) {
+ if (kickstartNetworkUp(kd, loaderData, flags)) {
+ logMessage("unable to bring up network");
+ return;
+ }
+
+ logMessage("going to beTelnet");
+ if (!beTelnet(flags))
+ flags |= LOADER_FLAGS_TEXT | LOADER_FLAGS_NOSHELL;
+
+ return;
+}
diff --git a/loader2/telnetd.h b/loader2/telnetd.h
new file mode 100644
index 000000000..d9a2a6cd4
--- /dev/null
+++ b/loader2/telnetd.h
@@ -0,0 +1,8 @@
+#ifndef TELNETD_H
+#define TELNETD_H
+
+void startTelnetd(struct knownDevices * kd, struct loaderData_s * loaderData,
+ moduleInfoSet modInfo, moduleList modLoaded,
+ moduleDeps modDeps, int flags);
+
+#endif