From: Michael Tremer Date: Thu, 10 Sep 2009 15:13:04 +0000 (+0200) Subject: Serial console: Initial commit. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9d4fd3e4be03bfb23ad5b1ae10315bd0a5a217c8;p=ipfire-3.x.git Serial console: Initial commit. --- diff --git a/config/udev/10-console.rules b/config/udev/10-console.rules index a3459ab09..6e8c45876 100644 --- a/config/udev/10-console.rules +++ b/config/udev/10-console.rules @@ -1,2 +1,9 @@ # Console initialization - keyboard, font, etc. -KERNEL=="tty0", RUN+="/lib/udev/console_init %k" +KERNEL=="tty0", RUN+="/lib/udev/console_init %k" + +# Check and set up serial and serial-like consoles if necessary +KERNEL=="console", RUN+="/lib/udev/console_check %k" +KERNEL=="ttySG*", RUN+="/lib/udev/console_check %k" +KERNEL=="xvc*", RUN+="/lib/udev/console_check %k" +KERNEL=="hvsi*", RUN+="/lib/udev/console_check %k" +KERNEL=="hvc*", RUN+="/lib/udev/console_check %k" diff --git a/lfs/initscripts b/lfs/initscripts index f14083fd1..dd90c555c 100644 --- a/lfs/initscripts +++ b/lfs/initscripts @@ -78,6 +78,11 @@ $(objects): $(OBJECT): $(objects) @$(PREBUILD) + cd $(DIR_APP)/src && make clean + + cd $(DIR_APP)/src && make $(PARALLELISMFLAGS) + cd $(DIR_APP)/src && make install + install -d -m 755 /etc/init install -d -m 755 /etc/sysconfig @@ -92,4 +97,5 @@ $(OBJECT): $(objects) cp -vf $(DIR_CONFIG)/$(PKG_NAME)/sysctl.conf /etc + cd $(DIR_APP)/src && make clean @$(POSTBUILD) diff --git a/src/initscripts/core/serial.conf b/src/initscripts/core/serial.conf new file mode 100644 index 000000000..da1bd7e5b --- /dev/null +++ b/src/initscripts/core/serial.conf @@ -0,0 +1,24 @@ +description "Start a tty" +author "IPFire Team" + +# Automatically start a configured serial console +# +# How this works: +# +# On boot, a udev helper examines /dev/console. If a serial console is the +# primary console (last console on the commandline in grub), the event +# 'serial-console-available ' is emitted, which +# triggers this script. +# +# If your serial console is not the primary console, or you want a getty +# on serial even if it's not the console, create your own event by copying +# /etc/init/tty[2-6], and changing the getty line in that file. + +start on serial-console-available * +stop on starting shutdown or starting reboot + +pre-start script + /sbin/securetty $1 +end script + +exec /sbin/agetty /dev/$1 $2 vt100-nav diff --git a/src/initscripts/src/Makefile b/src/initscripts/src/Makefile new file mode 100644 index 000000000..452f45f9e --- /dev/null +++ b/src/initscripts/src/Makefile @@ -0,0 +1,21 @@ + +PROGS = console_check securetty + +CFLAGS += -D_GNU_SOURCE + +all: $(PROGS) + +clean: + rm -vf $(PROGS) *.o + +install: + -mkdir -pv $(DESTDIR)/lib/udev $(DESTDIR)/sbin + install -v -m 755 console_check $(DESTDIR)/lib/udev/ + install -v -m 755 securetty $(DESTDIR)/sbin + + +console_check: console_check.o + $(CC) $(LDFLAGS) -o $@ $< + +securetty: securetty.o + $(CC) $(LDFLAGS) -o $@ $< diff --git a/src/initscripts/src/console_check.c b/src/initscripts/src/console_check.c new file mode 100644 index 000000000..9ecbb036d --- /dev/null +++ b/src/initscripts/src/console_check.c @@ -0,0 +1,180 @@ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct speeds +{ + speed_t speed; + unsigned long value; +}; + +struct speeds speed_map[] = +{ + {B50, 50}, + {B75, 75}, + {B110, 110}, + {B134, 134}, + {B150, 150}, + {B200, 200}, + {B300, 300}, + {B600, 600}, + {B1200, 1200}, + {B1800, 1800}, + {B2400, 2400}, + {B4800, 4800}, + {B9600, 9600}, + {B19200, 19200}, + {B38400, 38400}, +#ifdef B57600 + {B57600, 57600}, +#endif +#ifdef B115200 + {B115200, 115200}, +#endif +#ifdef B230400 + {B230400, 230400}, +#endif +#ifdef B460800 + {B460800, 460800}, +#endif + {0, 0} +}; + +int termcmp(struct termios *a, struct termios *b) { + if (a->c_iflag != b->c_iflag || a->c_oflag != b->c_oflag || + a->c_cflag != b->c_cflag || a->c_lflag != b->c_lflag || + cfgetispeed(a) != cfgetispeed(b) || cfgetospeed(a) != cfgetospeed(b)) + return 1; + return memcmp(a->c_cc, b->c_cc, sizeof(a->c_cc)); +} + +int get_serial_speed(int fd) { + struct termios mode; + + if (!tcgetattr(fd, &mode)) { + int i; + speed_t speed; + + speed = cfgetospeed(&mode); + for (i = 0; speed_map[i].value != 0; i++) + if (speed_map[i].speed == speed) + return speed_map[i].value; + } + return 0; +} + +int compare_termios_to_console(char *dev, int *speed) { + struct termios cmode, mode; + int fd, cfd; + + cfd = open ("/dev/console", O_RDONLY); + tcgetattr(cfd, &cmode); + close(cfd); + + fd = open(dev, O_RDONLY|O_NONBLOCK); + tcgetattr(fd, &mode); + + if (!termcmp(&cmode, &mode)) { + *speed = get_serial_speed(fd); + close(fd); + return 1; + } + close(fd); + return 0; +} + +char *serial_tty_name(int type) { + switch (type) { + case PORT_8250...PORT_MAX_8250: + return "ttyS"; + case PORT_PMAC_ZILOG: + return "ttyPZ"; + case PORT_MPSC: + return "ttyMM"; + case PORT_CPM: + return "ttyCPM"; + case PORT_MPC52xx: + return "ttyPSC"; + default: + return NULL; + } +} + +char *check_serial_console(int *speed) { + int fd; + char *ret = NULL, *device; + char twelve = 12; + struct serial_struct si, si2; + char *tty_name; + + memset(&si, 0, sizeof(si)); + memset(&si2, 0, sizeof(si)); + + fd = open("/dev/console", O_RDWR); + if (ioctl (fd, TIOCLINUX, &twelve) >= 0) + goto out; + + if (ioctl(fd, TIOCGSERIAL, &si) < 0) + goto out; + close(fd); + + tty_name = serial_tty_name(si.type); + if (!tty_name) + goto out; + + asprintf(&device, "%s%d", tty_name, si.line); + fd = open(device, O_RDWR|O_NONBLOCK); + if (fd == -1) + goto out; + + if (ioctl(fd, TIOCGSERIAL, &si2) < 0) + goto out; + + if (memcmp(&si,&si2, sizeof(si))) + goto out; + + *speed = get_serial_speed(fd); + ret = device; +out: + close(fd); + return ret; +} + +int emit_console_event(char *dev, int speed) { + char *args[] = { "initctl", "emit", "--no-wait", "serial-console-available", NULL, NULL, NULL }; + + args[4] = dev; + if (speed) + asprintf(&args[5],"%d",speed); + execv("/sbin/initctl", args); + return 1; +} + +int main(int argc, char **argv) { + char *device; + int speed; + + if (argc < 2) { + printf("usage: console_check \n"); + exit(1); + } + chdir("/dev"); + device = argv[1]; + if (!strcmp(device, "console")) { + device = check_serial_console(&speed); + if (device) + return emit_console_event(device, speed); + } else if (compare_termios_to_console(device, &speed)) { + return emit_console_event(device, speed); + } + return 0; +} diff --git a/src/initscripts/src/securetty.c b/src/initscripts/src/securetty.c new file mode 100644 index 000000000..f1505076f --- /dev/null +++ b/src/initscripts/src/securetty.c @@ -0,0 +1,94 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +void alarm_handler(int num) { + return; +} + +int open_and_lock_securetty() { + int fd; + struct flock lock; + struct sigaction act, oldact; + + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + + fd = open("/etc/securetty", O_RDWR); + if (fd == -1) { + syslog(LOG_ERR, "Couldn't open /etc/securetty: %s",strerror(errno)); + return -1; + } + act.sa_handler = alarm_handler; + act.sa_flags = 0; + sigaction(SIGALRM, &act, &oldact); + alarm(2); + while (fcntl(fd, F_SETLKW, &lock) == -1) { + if (errno == EINTR) { + syslog(LOG_ERR, "Couldn't lock /etc/securetty: Timeout exceeded"); + } else { + syslog(LOG_ERR, "Couldn't lock /etc/securetty: %s",strerror(errno)); + } + return -1; + } + alarm(0); + sigaction(SIGALRM, &oldact, NULL); + return fd; +} + +int rewrite_securetty(char *terminal) { + int fd; + char *buf, *pos; + struct stat sbuf; + + fd = open_and_lock_securetty(); + if (fd == -1) + return 1; + if (fstat(fd, &sbuf) == -1) { + close(fd); + syslog(LOG_ERR, "Couldn't stat /etc/securetty: %s",strerror(errno)); + return 1; + } + buf = malloc(sbuf.st_size + 1); + if (read(fd, buf, sbuf.st_size) != sbuf.st_size) { + close(fd); + syslog(LOG_ERR, "Couldn't read /etc/securetty: %s",strerror(errno)); + return 1; + } + if (!strncmp(buf,terminal,strlen(terminal)) && buf[strlen(terminal)] == '\n') + goto out_ok; + if ((pos = strstr(buf, terminal))) { + if (pos[strlen(terminal)] == '\n' && *(pos-1) == '\n') + goto out_ok; + } + if (lseek(fd, 0, SEEK_END) == -1) { + close(fd); + syslog(LOG_ERR, "Couldn't seek to end of /etc/securetty: %s",strerror(errno)); + return 1; + } + write(fd, terminal, strlen(terminal)); + write(fd, "\n", 1); +out_ok: + close(fd); + return 0; +} + +int main(int argc, char **argv) { + if (argc < 2 ) { + fprintf(stderr, "Usage: securetty \n"); + exit(1); + } + openlog("securetty", LOG_CONS, LOG_DAEMON); + return rewrite_securetty(argv[1]); +} diff --git a/src/rootfiles/core/initscripts b/src/rootfiles/core/initscripts index 5d8938873..934350064 100644 --- a/src/rootfiles/core/initscripts +++ b/src/rootfiles/core/initscripts @@ -6,6 +6,7 @@ etc/init/load-modules.conf etc/init/loopback.conf etc/init/mount-kernel-filesystems.conf etc/init/mountfs.conf +etc/init/serial.conf etc/init/shutdown.conf etc/init/swap.conf etc/init/sysctl.conf @@ -24,3 +25,5 @@ etc/sysconfig/rc etc/sysconfig/rc.local etc/sysconfig/rc.site etc/sysctl.conf +lib/udev/console_check +sbin/securetty