summaryrefslogtreecommitdiffstats
path: root/loader2
diff options
context:
space:
mode:
authorDavid Cantrell <dcantrell@redhat.com>2007-05-29 18:22:55 +0000
committerDavid Cantrell <dcantrell@redhat.com>2007-05-29 18:22:55 +0000
commit15c1d5c26b0f85e5dccea4f1534b817d7d0f0c06 (patch)
tree83674ee223d0002d3459eca6a188d9121be2d860 /loader2
parent07647fb5b545d3dcc7c1bc1436f1f1e8c6237937 (diff)
downloadanaconda-15c1d5c26b0f85e5dccea4f1534b817d7d0f0c06.tar.gz
anaconda-15c1d5c26b0f85e5dccea4f1534b817d7d0f0c06.tar.xz
anaconda-15c1d5c26b0f85e5dccea4f1534b817d7d0f0c06.zip
piece of shit
Diffstat (limited to 'loader2')
-rw-r--r--loader2/Makefile2
-rw-r--r--loader2/loader.c6
-rw-r--r--loader2/loader.h2
-rw-r--r--loader2/telnet.c272
-rw-r--r--loader2/telnet.h40
-rw-r--r--loader2/telnetd.c250
-rw-r--r--loader2/telnetd.h8
7 files changed, 579 insertions, 1 deletions
diff --git a/loader2/Makefile b/loader2/Makefile
index 3eeec8762..0b59a2e91 100644
--- a/loader2/Makefile
+++ b/loader2/Makefile
@@ -44,7 +44,7 @@ OBJS = log.o moduleinfo.o loadermisc.o modules.o moduledeps.o windows.o \
getparts.o dirbrowser.o \
$(HWOBJS) $(METHOBJS)
LOADEROBJS = loader.o loader-pcmcia.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 8e59c4369..99841c489 100644
--- a/loader2/loader.c
+++ b/loader2/loader.c
@@ -82,6 +82,7 @@
#include "urlinstall.h"
#include "net.h"
+#include "telnetd.h"
#include <selinux/selinux.h>
#include "selinux.h"
@@ -622,6 +623,8 @@ static void parseCmdLineFlags(struct loaderData_s * loaderData,
flags |= LOADER_FLAGS_NOUSB;
else if (!strcasecmp(argv[i], "ub"))
flags |= LOADER_FLAGS_UB;
+ 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], "nonet"))
@@ -1620,6 +1623,9 @@ int main(int argc, char ** argv) {
}
}
+ if (FL_TELNETD(flags))
+ startTelnetd(&loaderData, modInfo, modLoaded, modDeps);
+
url = doLoaderMain("/mnt/source", &loaderData, modInfo, modLoaded, &modDeps);
if (!FL_TESTING(flags)) {
diff --git a/loader2/loader.h b/loader2/loader.h
index 5775eea92..9b9b012b4 100644
--- a/loader2/loader.h
+++ b/loader2/loader.h
@@ -27,6 +27,7 @@
#define LOADER_FLAGS_NOUSB (1 << 16)
#define LOADER_FLAGS_NOSHELL (1 << 17)
#define LOADER_FLAGS_NOPCMCIA (1 << 18)
+#define LOADER_FLAGS_TELNETD (1 << 19)
#define LOADER_FLAGS_NOPASS (1 << 20)
#define LOADER_FLAGS_UB (1 << 21)
#define LOADER_FLAGS_MEDIACHECK (1 << 22)
@@ -58,6 +59,7 @@
#define FL_NOFB(a) ((a) & LOADER_FLAGS_NOFB)
#define FL_NOPCMCIA(a) ((a) & LOADER_FLAGS_NOPCMCIA)
#define FL_RESCUE_NOMOUNT(a) ((a) & LOADER_FLAGS_RESCUE_NOMOUNT)
+#define FL_TELNETD(a) ((a) & LOADER_FLAGS_TELNETD)
#define FL_NOPASS(a) ((a) & LOADER_FLAGS_NOPASS)
#define FL_MEDIACHECK(a) ((a) & LOADER_FLAGS_MEDIACHECK)
#define FL_NOUSBSTORAGE(a) ((a) & LOADER_FLAGS_NOUSBSTORAGE)
diff --git a/loader2/telnet.c b/loader2/telnet.c
new file mode 100644
index 000000000..4b8cf09aa
--- /dev/null
+++ b/loader2/telnet.c
@@ -0,0 +1,272 @@
+/* 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
+ ;
+ int ret;
+
+ ret = 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 {
+ ret = read(socket, &ch, 1);
+ if (ch != '\xff') {
+ abort();
+ }
+
+ ret = read(socket, &ch, 1); /* command */
+
+ if (ch != '\xfa') {
+ ret = read(socket, &ch, 1); /* verb */
+ continue;
+ }
+
+ ret = read(socket, &ch, 1); /* suboption */
+ if (ch == '\x18') {
+ state = ST_TERMTYPE;
+ ret = read(socket, &ch, 1); /* should be 0x0! */
+ done = 1;
+ } else if (ch == '\x1f') {
+ state = ST_WINDOWSIZE;
+ } else {
+ state = ST_NONE;;
+ }
+
+ ret = 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) < (int)sizeof(sizeBuf))
+ *sizePtr++ = ch;
+ }
+
+ ret = read(socket, &ch, 1); /* data */
+ }
+
+ ret = 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(WARNING, "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;
+ int ret;
+
+ 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... */
+ ret = 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..75bce5222
--- /dev/null
+++ b/loader2/telnetd.c
@@ -0,0 +1,250 @@
+/*
+ * 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"
+
+#ifndef IPPORT_TELNET
+#define IPPORT_TELNET 23
+#endif
+
+/* boot flags */
+extern int flags;
+
+/* Forks, keeping the loader as our child (so we know when it dies). */
+int beTelnet(void) {
+ int sock;
+ int conn;
+ socklen_t addrLength;
+ pid_t child;
+ int i;
+ int masterFd, ttyFd;
+ 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(ERROR, "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_TELNET
+ printf("got term type %s\n", termType);
+#endif
+
+ masterFd = open("/dev/ptmx", O_RDWR);
+ if (masterFd < 0) {
+ logMessage(CRITICAL, "cannot open /dev/ptmx");
+ close(conn);
+ return -1;
+ }
+
+ if (height != -1 && width != -1) {
+#ifdef DEBUG_TELNET
+ 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_TELNET
+ startNewt();
+ 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_TELNET
+ {
+ 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) {
+ int ret;
+ i = read(conn, buf, sizeof(buf));
+
+ /* connection went away */
+ if (!i)
+ break;
+
+ i = telnet_process_input(&ts, buf, i);
+ ret = write(masterFd, buf, i);
+
+#ifdef DEBUG_TELNET
+ {
+ int j;
+
+ printf("got:");
+ for (j = 0; j < i; j++)
+ printf(" 0x%x", (unsigned char) buf[j]);
+ printf("\n");
+ }
+#endif
+
+ }
+ }
+
+
+ if (i < 0) {
+ logMessage(ERROR, "poll: %s", strerror(errno));
+ }
+
+#ifndef DEBUG_TELNET
+ stopNewt();
+#endif
+
+ kill(child, SIGTERM);
+ close(conn);
+
+ exit(0);
+ }
+
+ unlockpt(masterFd);
+ grantpt(masterFd);
+ ttyFd = open(ptsname(masterFd), O_RDWR);
+ close(masterFd);
+ setsid();
+ close(0);
+ close(1);
+ close(2);
+
+ if (ttyFd != 0) {
+ dup2(ttyFd, 0);
+ close(ttyFd);
+ }
+ dup2(0, 1);
+ dup2(0, 2);
+
+ /* brand new tty! */
+ setenv("TERM", termType, 1);
+
+ startNewt();
+
+ return 0;
+}
+
+void startTelnetd(struct loaderData_s * loaderData,
+ moduleInfoSet modInfo, moduleList modLoaded,
+ moduleDeps modDeps) {
+ char ret[47];
+ struct networkDeviceConfig netCfg;
+ ip_addr_t *tip;
+
+ if (kickstartNetworkUp(loaderData, &netCfg)) {
+ logMessage(ERROR, "unable to bring up network");
+ return;
+ }
+
+ tip = &(netCfg.dev.ip);
+ inet_ntop(tip->sa_family, IP_ADDR(tip), ret, IP_STRLEN(tip));
+ logMessage(INFO, "going to beTelnet for %s", ret);
+ if (!beTelnet())
+ flags |= LOADER_FLAGS_TEXT | LOADER_FLAGS_NOSHELL;
+
+ return;
+}
diff --git a/loader2/telnetd.h b/loader2/telnetd.h
new file mode 100644
index 000000000..145c55f21
--- /dev/null
+++ b/loader2/telnetd.h
@@ -0,0 +1,8 @@
+#ifndef TELNETD_H
+#define TELNETD_H
+
+void startTelnetd(struct loaderData_s * loaderData,
+ moduleInfoSet modInfo, moduleList modLoaded,
+ moduleDeps modDeps);
+
+#endif