diff --git a/CHANGES b/CHANGES index 8bfede5..8d79986 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,19 @@ CHANGES ======= +version 8.0.8 (Dec 2, 2003): + - added client ^Ec| sequence for running a command on the client + and having it's I/O directed to the console - inspired by + discussions with David Williamson + years ago ;-) + - touched up Makefiles to test against .h files + - fixed inability to replay the console log while the console is + down - reported by Matt Selsky + - added a console 'logfilemax' option for rotating the console + logfile once the file is greater than the specified size + - added sample configuration files to conserver.cf/samples + - tweaked some failure messages and initcmd notifications + version 8.0.7 (Nov 20, 2003): - renamed util.[ch] to cutil.[ch] to prevent name conflict with system util.h, reordered some #includes, and fixed a configure @@ -624,5 +637,5 @@ before version 6.05: and enhancements of various types were applied. # -# $Id: CHANGES,v 1.135 2003/11/20 14:48:29 bryan Exp $ +# $Id: CHANGES,v 1.140 2003/12/02 16:26:59 bryan Exp $ # diff --git a/TODO b/TODO index 661e75d..92c73a5 100644 --- a/TODO +++ b/TODO @@ -33,6 +33,7 @@ Bryan Stansell - pipe input/output (console <-> program) via 'console' - some apps (net-ups thing, gdb) might need to talk to user + - ^Ec| does this, but the interact with user bits might not work - autologout? setting per console? gack, would have to interpret data. @@ -59,8 +60,6 @@ Bryan Stansell - server -M flag should accept multiple addresses (comma separated) - should client as well? -- logfile rotation based on size - - automatic log rotation in general : Egan Ford - website docs on serial port configs @@ -83,9 +82,9 @@ Bryan Stansell - send a string when connected to a console, so you can set the xterm title, for example : Richard Threadgill -- allows for very long replays (hundres of lines) : John Stoffel +- allow for very long replays (hundres of lines) : John Stoffel # -# $Id: TODO,v 1.42 2003/11/15 17:08:15 bryan Exp $ +# $Id: TODO,v 1.44 2003/11/29 14:10:47 bryan Exp $ # diff --git a/autologin/Makefile.in b/autologin/Makefile.in index c5f4054..21faa8c 100644 --- a/autologin/Makefile.in +++ b/autologin/Makefile.in @@ -31,6 +31,8 @@ ALL = autologin all: $(ALL) +$(AUTOLOGIN_OBJS): $(AUTOLOGIN_HDRS) + autologin: $(AUTOLOGIN_OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o autologin $(AUTOLOGIN_OBJS) $(LIBS) diff --git a/compat.h b/compat.h index a25fb04..8b58b92 100644 --- a/compat.h +++ b/compat.h @@ -287,6 +287,10 @@ typedef int socklen_t; #define ONLRET 0 #endif +#ifndef SEEK_SET +#define SEEK_SET L_SET +#endif + #ifndef PARAMS # if PROTOTYPES # define PARAMS(protos) protos diff --git a/config.guess b/config.guess index 4c5bde8..61f2e4c 100755 --- a/config.guess +++ b/config.guess @@ -3,7 +3,7 @@ # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003 Free Software Foundation, Inc. -timestamp='2003-10-16' +timestamp='2003-10-20' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by @@ -997,6 +997,9 @@ EOF i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit 0 ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit 0 ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit 0 ;; diff --git a/config.sub b/config.sub index 5698174..fac5195 100755 --- a/config.sub +++ b/config.sub @@ -3,7 +3,7 @@ # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003 Free Software Foundation, Inc. -timestamp='2003-11-03' +timestamp='2003-11-20' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software @@ -1215,6 +1215,9 @@ case $os in -atheos*) os=-atheos ;; + -syllable*) + os=-syllable + ;; -386bsd) os=-bsd ;; diff --git a/conserver.cf/conserver.cf b/conserver.cf/conserver.cf index 2cd7892..4d6bcbb 100644 --- a/conserver.cf/conserver.cf +++ b/conserver.cf/conserver.cf @@ -14,39 +14,53 @@ default cisco { type host; portbase 2000; portinc 1; } default xyplex { type host; portbase 2000; portinc 100; } default iolan { type host; portbase 10000; portinc 1; } +### set up some custom break sequences +break 4 { string "+\d+\d+"; delay 300; } +break 5 { string "\033c"; } + ### set the defaults for all the consoles # these get applied before anything else default * { # The '&' character is substituted with the console name logfile /var/consoles/&; + # timestamps every hour with activity and break logging timestamp 1hab; + # include the 'full' default include full; + # master server is localhost master localhost; } ### define the first terminal server default ts1.conserver.com { + # use the xyplex defaults include xyplex; + # host to connect to is ts1.conserver.com host ts1.conserver.com; + # run login-xyplex when connecting to the term server initcmd /usr/local/sbin/login-xyplex; } # now define the consoles on ts1.conserver.com -console web1.conserver.com { include ts1.conserver.com; port 2; } +# bryan isn't allowed on web1.conserver.com +console web1.conserver.com { include ts1.conserver.com; port 2; rw !bryan; } console ns1.conserver.com { include ts1.conserver.com; port 10; } console ns2.conserver.com { include ts1.conserver.com; port 8; } ### define the second terminal server +# this one is a cisco, with simple socket connections default ts2.conserver.com { include cisco; host ts2.conserver.com; } -# and the consoles +# and the consoles on ts2.conserver.com console ldap1.conserver.com { include ts2.conserver.com; port 7; } ### and now some one-off consoles +# we still inherit the '*' default set # a simple ssh invocation console ssh { type exec; exec ssh localhost; + # provide a 'message-of-the-day' motd "just a simple ssh to localhost"; } @@ -59,6 +73,38 @@ console ttya { timestamp ""; # no timestamps on this console } +### define a group of users +group sysadmin { + users bryan, todd; + users dave; +} + +### reset the defaults for the next set of consoles +# again, these get applied before anything else +default * { + # The '&' character is substituted with the console name + logfile /var/consoles/&; + timestamp 5m; + rw sysadmin; # use the group defined above + master localhost; +} + +default cyclades { + # sets up /dev/ttyC0 through /dev/ttyC31, for a 32 port card + type device; + device /dev/ttyC.; + devicesubst ,.d; + portbase -1; + portinc 1; + host none; # not really used, since devicesubst doesn't use it + baud 9600; + parity none; +} + +console modem1.conserver.com { include cyclades; port 2; break 4; } +# todd isn't allowed on modem2.conserver.com +console modem2.conserver.com { include cyclades; port 6; rw !todd; } + ### list of clients we allow access * { allowed 10.0.0.0/8 192.168.0.0/16; diff --git a/conserver.cf/conserver.cf.man b/conserver.cf/conserver.cf.man index cd60196..1105603 100644 --- a/conserver.cf/conserver.cf.man +++ b/conserver.cf/conserver.cf.man @@ -1,5 +1,5 @@ -.\" $Id: conserver.cf.man,v 1.51 2003/11/15 20:00:11 bryan Exp $ -.TH CONSERVER.CF 5 "2003/11/15" "conserver-8.0.7" "conserver" +.\" $Id: conserver.cf.man,v 1.53 2003/12/01 02:15:39 bryan Exp $ +.TH CONSERVER.CF 5 "2003/12/01" "conserver-8.0.8" "conserver" .SH NAME conserver.cf \- console configuration file for .BR conserver (8) @@ -493,7 +493,12 @@ as the host to connect to for accessing the console. You must also set the .B port option as well. -Only consoles of type ``host'' will use this value. +Normally, only consoles of type ``host'' will use this value. +If the +.B devicesubst +or +.B execsubst +keywords are used in ``device'' and ``exec'' types, this value is used. .TP .B idlestring .RI [ " string " @@ -559,6 +564,36 @@ will be replaced with the name of the console. If the null string (``""'') is used, the logfile name is unset and no logging will occur. .TP +.B logfilemax +.BR \fInumber\fP [ k | m ] +.br +Enable automatic rotation of +.B logfile +once it's size exceeds +.I number +bytes. +Specifying +.B k +or +.B m +interpret +.I number +as kilobytes and megabytes. +.I number +must be at least 2048 bytes. +A value of zero will turn off automatic rotation of +.BR logfile . +The +.B logfile +.I filename +will be renamed +.IR filename -\s-1YYYYMMDD\s0-\s-1HHMMSS\s0, +where the extension is the current GMT year, month, day, hour, +minute, and second (to prevent issues with clock rollbacks). +File sizes are checked every 5 minutes with an additional initial +pseudo-random delay of up to one minute (to help prevent all processes +checking all consoles simultaneously). +.TP .B master .RI [ " hostname " | " ipaddr " ] .br @@ -730,12 +765,19 @@ terminal server by their physical numbering of 1 through You must also set the .B host option as well. -Only consoles of type ``host'' will use this value. +Normally, only consoles of type ``host'' will use this value. +If the +.B devicesubst +or +.B execsubst +keywords are used in ``device'' and ``exec'' types, this value is used. .TP .B portbase .I number .br Set the base value for the port calculation formula. +.I number +must be -1 or greater. The default is zero. See .B port @@ -745,6 +787,8 @@ for the details of the formula. .I number .br Set the increment value for the port calculation formula. +.I number +must be 1 or greater. The default is one. See .B port diff --git a/conserver.cf/conserver.passwd.man b/conserver.cf/conserver.passwd.man index 4998847..ea3cd56 100644 --- a/conserver.cf/conserver.passwd.man +++ b/conserver.cf/conserver.passwd.man @@ -1,5 +1,5 @@ .\" $Id: conserver.passwd.man,v 1.9 2003/07/04 20:20:52 bryan Exp $ -.TH CONSERVER.PASSWD 5 "2003/07/04" "conserver-8.0.7" "conserver" +.TH CONSERVER.PASSWD 5 "2003/07/04" "conserver-8.0.8" "conserver" .SH NAME conserver.passwd \- user access information for .BR conserver (8) diff --git a/conserver.cf/samples/README b/conserver.cf/samples/README new file mode 100644 index 0000000..7c18945 --- /dev/null +++ b/conserver.cf/samples/README @@ -0,0 +1,21 @@ +I put together the sample configuration files in this directory in hopes +that it would help folks see some of the possibilities of the +configuration file format. Each of the files are syntatically correct, +but have never actually been used. + +Each file is basically built upon the previous...theoretically, if not +actually. Hopefully they'll help show some of the cool things you can +do with the configuration file and help those trying to figure out how +they should even start. + + simple.cf - A very simple, one console config file + + basic.cf - A config with a couple consoles, mostly using defaults + + average.cf - A config for many consoles, using breaks, user lists, + etc...bascially customizing each area + + average-distributed.cf - Taking average.cf to multiple conserver + hosts with overrides on those hosts + +Bryan Stansell diff --git a/conserver.cf/samples/average-distributed.cf b/conserver.cf/samples/average-distributed.cf new file mode 100644 index 0000000..8d1b616 --- /dev/null +++ b/conserver.cf/samples/average-distributed.cf @@ -0,0 +1,108 @@ +# +# I took the average.cf file and expanded it to use a distributed +# conserver setup...two conserver hosts (conserver1 and conserver2), but +# the basic philosophy would hold for many more console and/or conserver +# hosts. +# + +# ------ define a user group ------ +group sysadmin { + users bryan, todd, dave; +} +# helpers is everyone but the sysadmin group +group helpers { + users *, !sysadmin; +} + + +# ------ make sure breaks are the way we want -------- +break 1 { string "\z"; } +break 2 { string "\r\d~\d^b"; delay 600; } +break 3 { string "#."; } + + +# ----- define some console types ------ +# yeah, just setting a break doesn't quite seem worth it, but perhaps, +# some day, there will be more host-specific stuff. +default sun-std { break 1; } +default sun-alt { break 2; } +default sun-lom { break 3; } + + +# ------ defaults ------ +# we set a 'global' default so we can reuse the bits below. we're going +# to set the '*' default, then define consoles, reset the '*' default, +# define more consoles, etc. +default global { + logfile /var/consoles/&; # '&' is replaced with console name + timestamp 1hab; # write timestamps + rw sysadmin; # allow sysadmins full access + ro helpers; # allow helpers to watch + include sun-std; +} + + +# --------- define our terminal attributes ---------- +# simple tcp connections are "easy" +default cisco { type host; portbase 2000; portinc 1; } +default xyplex { type host; portbase 2000; portinc 100; } + +# this is a cyclades card referenced with /dev/ttyC0 through /dev/ttyC31 +# we set the various port calculation bits and pattern substitution to +# come up with a generic definition +default cyclades { type device; device /dev/ttyC&; baud 9600; parity none; + devicesubst .&d; portbase -1; portinc 1; host unused; } + +## this is a term server accessed with an ssh command +# it too uses pattern substitution and such to get the job done +default ciscossh { type exec; portbase 2000; portinc 1; + exec /usr/local/bin/ssh -p P -l tsuser H; + execsubst HPd; } + + +# ------- set the global default for the first conserver host ------- +# the consoles below (until the default is reset) are managed +# by conserver1.conserver.com +default * { include global; master conserver1.conserver.com; } + +# ------- define the consoles on ts1.conserver.com -------- +default ts1.conserver.com { include cisco; host ts1.conserver.com; } +console web1.conserver.com { include ts1.conserver.com; port 2; } +console ns1.conserver.com { include ts1.conserver.com; port 10; } + +# ------- define the consoles on ts2.conserver.com -------- +default ts2.conserver.com { include xyplex; host ts2.conserver.com; } +console web2.conserver.com { include ts2.conserver.com; port 4; } +console ns2.conserver.com { include ts2.conserver.com; port 22; } + +# ------- set the global default for the second conserver host ------- +# the following consoles are managed by conserver2.conserver.com +default * { include global; master conserver2.conserver.com; } + +# ------- define the consoles on ts3.conserver.com -------- +default ts3.conserver.com { include ciscossh; host ts3.conserver.com; } +console ftp1.conserver.com { include ts3.conserver.com; include sun-lom; + port 7; } + +# ------- set up the an access list to avoid the default ------- +# anything *not* matched here will fallback to the default access mode +access * { + trusted 127.0.0.1; + allowed 10.0.0.0/8; +} + +# conserver2 has an extra leg that is trusted +access conserver2.conserver.com { trusted 192.168.0.0/16; } + +# ------- do some server configuration --------- +# both conserver1.conserver.com and conserver2.conserver.com use the same +# set of defaults +config * { + defaultaccess rejected; + daemonmode on; + logfile /var/log/conserver; +} + +# we're going to set the default access on conserver2 to allowed, because +# it's in a higher-trust network +config conserver2.conserver.com { defaultaccess allowed; } diff --git a/conserver.cf/samples/average.cf b/conserver.cf/samples/average.cf new file mode 100644 index 0000000..0f7019c --- /dev/null +++ b/conserver.cf/samples/average.cf @@ -0,0 +1,90 @@ +# +# This would be what i'd expect a more common configuration file would +# look like. There are consoles attached to multiple devices, simple +# access lists, etc. +# + +# ------ define a user group ------ +group sysadmin { + users bryan, todd, dave; +} +# helpers is everyone but the sysadmin group +group helpers { + users *, !sysadmin; +} + + +# ------ make sure breaks are the way we want -------- +break 1 { string "\z"; } +break 2 { string "\r\d~\d^b"; delay 600; } +break 3 { string "#."; } + + +# ----- define some console types ------ +# yeah, just setting a break doesn't quite seem worth it, but perhaps, +# some day, there will be more host-specific stuff. +default sun-std { break 1; } +default sun-alt { break 2; } +default sun-lom { break 3; } + + +# ------ defaults ------ +# now for some generic console defaults so that we don't have to +# duplicate them for each console. +default * { + logfile /var/consoles/&; # '&' is replaced with console name + timestamp 1hab; # write timestamps + rw sysadmin; # allow sysadmins full access + ro helpers; # allow helpers to watch + master localhost; + include sun-std; +} + + +# --------- define our terminal attributes ---------- +# simple tcp connections are "easy" +default cisco { type host; portbase 2000; portinc 1; } +default xyplex { type host; portbase 2000; portinc 100; } + +# this is a cyclades card referenced with /dev/ttyC0 through /dev/ttyC31 +# we set the various port calculation bits and pattern substitution to +# come up with a generic definition +default cyclades { type device; device /dev/ttyC&; baud 9600; parity none; + devicesubst .&d; portbase -1; portinc 1; host unused; } + +## this is a term server accessed with an ssh command +# it too uses pattern substitution and such to get the job done +default ciscossh { type exec; portbase 2000; portinc 1; + exec /usr/local/bin/ssh -p P -l tsuser H; + execsubst HPd; } + + +# ------- define the consoles on ts1.conserver.com -------- +default ts1.conserver.com { include cisco; host ts1.conserver.com; } +console web1.conserver.com { include ts1.conserver.com; port 2; } +console ns1.conserver.com { include ts1.conserver.com; port 10; } + +# ------- define the consoles on ts2.conserver.com -------- +default ts2.conserver.com { include xyplex; host ts2.conserver.com; } +console web2.conserver.com { include ts2.conserver.com; port 4; } +console ns2.conserver.com { include ts2.conserver.com; port 22; } + +# ------- define the consoles on ts3.conserver.com -------- +default ts3.conserver.com { include ciscossh; host ts3.conserver.com; } +console ftp1.conserver.com { include ts3.conserver.com; include sun-lom; + port 7; } + + +# ------- set up the an access list to avoid the default ------- +# anything *not* matched here will fallback to the default access mode +access * { + trusted 127.0.0.1; + allowed 10.0.0.0/8; +} + +# ------- do some server configuration --------- +config * { + defaultaccess rejected; + daemonmode on; + logfile /var/log/conserver; +} diff --git a/conserver.cf/samples/basic.cf b/conserver.cf/samples/basic.cf new file mode 100644 index 0000000..d64d583 --- /dev/null +++ b/conserver.cf/samples/basic.cf @@ -0,0 +1,28 @@ +# +# This is a fairly basic configuration file that interacts with one +# terminal server. +# + +# first, we're going to set some generic console defaults so that we +# don't have to duplicate them for each console. +default * { + logfile /var/consoles/&; # '&' is replaced with console name + timestamp 1hab; # write timestamps + rw *; # allow all users + master localhost; + type host; + host ts1.conserver.com; # consoles on ts1.conserver.co + portbase 2000; # port numbers start at 2001 and + portinc 1; # go up by 1 (port #1 == 2001, etc) +} + +# define two consoles on the terminal server +console web1.conserver.com { port 2; } # calculates to tcp port 2002 +console ns1.conserver.com { port 10; } # calculates to tcp port 2010 + +# set up the an access list to avoid the default +# anything *not* matched here will fallback to the default access (-a) +# mode +access * { + trusted 127.0.0.1; +} diff --git a/conserver.cf/samples/simple.cf b/conserver.cf/samples/simple.cf new file mode 100644 index 0000000..b066235 --- /dev/null +++ b/conserver.cf/samples/simple.cf @@ -0,0 +1,11 @@ +# +# I believe this is the smallest configuration file that is also fully +# functional. You have to be happy with the default access type (-a) +# as well as the default access list that gets used. +# + +console simple { + master localhost; + type exec; + rw *; +} diff --git a/conserver.html b/conserver.html index 415ad34..de8f1d5 100644 --- a/conserver.html +++ b/conserver.html @@ -183,11 +183,11 @@

Downloading

-

The current version, released on Nov 20, 2003, is 8.0.7.tar.gz. You can get it via +

The current version, released on Dec 2, 2003, is 8.0.8.tar.gz. You can get it via FTP - or HTTP. See the FTP + or HTTP. See the CHANGES file for information on the latest updates.

diff --git a/conserver/Makefile.in b/conserver/Makefile.in index 73ad2e8..c37b3aa 100644 --- a/conserver/Makefile.in +++ b/conserver/Makefile.in @@ -29,14 +29,17 @@ LIBS = @LIBS@ @CONSLIBS@ CONSERVER_OBJS = access.o client.o consent.o group.o main.o master.o \ readcfg.o fallback.o cutil.o CONSERVER_HDRS = ../config.h $(top_srcdir)/compat.h $(srcdir)/access.h \ - $(srcdir)/client.h $(srcdir)/consent.h $(srcdir)/group.h \ - $(srcdir)/main.h $(srcdir)/master.h $(srcdir)/readcfg.h \ - $(srcdir)/util.h + $(srcdir)/client.h $(srcdir)/consent.h $(srcdir)/cutil.h \ + $(srcdir)/group.h $(srcdir)/main.h $(srcdir)/master.h \ + $(srcdir)/readcfg.h $(srcdir)/version.h + ALL = conserver convert all: $(ALL) +$(CONSERVER_OBJS): $(CONSERVER_HDRS) + conserver: $(CONSERVER_OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o conserver $(CONSERVER_OBJS) $(LIBS) diff --git a/conserver/client.c b/conserver/client.c index aabb292..b8015d2 100644 --- a/conserver/client.c +++ b/conserver/client.c @@ -1,5 +1,5 @@ /* - * $Id: client.c,v 5.76 2003/11/20 13:56:38 bryan Exp $ + * $Id: client.c,v 5.79 2003/11/28 23:36:02 bryan Exp $ * * Copyright conserver.com, 2000 * @@ -97,15 +97,15 @@ FindWrite(pCE) */ void #if PROTOTYPES -Replay(CONSFILE *fdLog, CONSFILE *fdOut, int iBack) +Replay(CONSENT *pCE, CONSFILE *fdOut, int iBack) #else -Replay(fdLog, fdOut, iBack) - CONSFILE *fdLog; +Replay(pCE, fdOut, iBack) + CONSENT *pCE; CONSFILE *fdOut; int iBack; #endif { - + CONSFILE *fdLog = (CONSFILE *)0; off_t file_pos; off_t buf_pos; char *buf; @@ -130,7 +130,16 @@ Replay(fdLog, fdOut, iBack) unsigned long dmallocMarkReplay = 0; #endif - if ((CONSFILE *)0 == fdLog) { + if (pCE != (CONSENT *)0) { + fdLog = pCE->fdlog; + + /* no logfile and down and logfile defined? try and open it */ + if (fdLog == (CONSFILE *)0 && !pCE->fup && + pCE->logfile != (char *)0) + fdLog = FileOpen(pCE->logfile, O_RDONLY, 0644); + } + + if (fdLog == (CONSFILE *)0) { FileWrite(fdOut, FLAGFALSE, "[no log file on this console]\r\n", -1); return; @@ -179,17 +188,9 @@ Replay(fdLog, fdOut, iBack) * the rest (as we work our way back in the file) should be */ buf_pos = (file_pos / BUFSIZ) * BUFSIZ; -#if defined(SEEK_SET) - /* PTX and maybe other Posix systems - */ if (FileSeek(fdLog, buf_pos, SEEK_SET) < 0) { goto common_exit; } -#else - if (FileSeek(fdLog, buf_pos, L_SET) < 0) { - goto common_exit; - } -#endif if ((r = FileRead(fdLog, buf, BUFSIZ)) < 0) { goto common_exit; } @@ -339,6 +340,10 @@ Replay(fdLog, fdOut, iBack) common_exit: + /* if we opened the logfile, close it */ + if (fdLog != pCE->fdlog) + FileClose(&fdLog); + if ((struct lines *)0 != lines) { for (i = 0; i < n_lines; i++) { DestroyString(lines[i].mark_end); @@ -397,6 +402,7 @@ static HELP aHLTable[] = { {WHEN_ALWAYS, "w who is on this console"}, {WHEN_ALWAYS, "x show console baud info"}, {WHEN_ALWAYS, "z suspend the connection"}, + {WHEN_ATTACH, "| attach local command"}, {WHEN_ALWAYS, " ignore/abort command"}, {WHEN_ALWAYS, "? print this message"}, {WHEN_ALWAYS, "^R replay the last line"}, diff --git a/conserver/client.h b/conserver/client.h index c836bc4..820aa27 100644 --- a/conserver/client.h +++ b/conserver/client.h @@ -1,5 +1,5 @@ /* - * $Id: client.h,v 5.33 2003/10/10 10:28:49 bryan Exp $ + * $Id: client.h,v 5.35 2003/11/28 15:55:34 bryan Exp $ * * Copyright conserver.com, 2000 * @@ -46,7 +46,8 @@ typedef enum clientState { S_IDENT, /* probational connection (who is this) */ S_PASSWD, /* still needs a passwd to connect */ S_QUOTE, /* send any character we can spell */ - S_BCAST /* send a broadcast message to all clients */ + S_BCAST, /* send a broadcast message to all clients */ + S_CWAIT /* wait for client */ } CLIENTSTATE; typedef struct client { /* Connection Information: */ @@ -82,7 +83,7 @@ typedef struct client { /* Connection Information: */ cnct_port; /* where from */ } CONSCLIENT; -extern void Replay PARAMS((CONSFILE *, CONSFILE *, int)); +extern void Replay PARAMS((CONSENT *, CONSFILE *, int)); extern void HelpUser PARAMS((CONSCLIENT *)); extern void FindWrite PARAMS((CONSENT *)); extern int ClientAccessOk PARAMS((CONSCLIENT *)); diff --git a/conserver/consent.c b/conserver/consent.c index 711c558..f04e8a3 100644 --- a/conserver/consent.c +++ b/conserver/consent.c @@ -1,5 +1,5 @@ /* - * $Id: consent.c,v 5.131 2003/11/20 13:56:38 bryan Exp $ + * $Id: consent.c,v 5.132 2003/12/02 16:21:42 bryan Exp $ * * Copyright conserver.com, 2000 * @@ -261,12 +261,12 @@ StopInit(pCE) if (pCE->initpid != 0) { kill(pCE->initpid, SIGHUP); - pCE->initpid = 0; - Verbose("[%s] initcmd terminated: pid %lu", pCE->server, - (unsigned long)pCE->initpid); - TagLogfileAct(pCE, "initcmd terminated"); CONDDEBUG((1, "StopInit(): sending initcmd pid %lu signal %d", (unsigned long)pCE->initpid, SIGHUP)); + Msg("[%s] initcmd terminated: pid %lu", pCE->server, + (unsigned long)pCE->initpid); + TagLogfileAct(pCE, "initcmd terminated"); + pCE->initpid = 0; } if (pCE->initfile != (CONSFILE *)0) { @@ -359,8 +359,8 @@ StartInit(pCE) pCE->initpid = 0; return; } - Verbose("[%s] initcmd started: pid %lu", pCE->server, - (unsigned long)pCE->initpid); + Msg("[%s] initcmd started: pid %lu", pCE->server, + (unsigned long)pCE->initpid); TagLogfileAct(pCE, "initcmd started"); FD_SET(pin[0], &rinit); if (maxfd < pin[0] + 1) diff --git a/conserver/consent.h b/conserver/consent.h index 01f6cc2..33bdd3c 100644 --- a/conserver/consent.h +++ b/conserver/consent.h @@ -1,5 +1,5 @@ /* - * $Id: consent.h,v 5.52 2003/11/15 20:00:08 bryan Exp $ + * $Id: consent.h,v 5.53 2003/11/28 23:36:02 bryan Exp $ * * Copyright conserver.com, 2000 * @@ -102,6 +102,7 @@ typedef struct consent { /* console information */ char *master; /* master hostname */ unsigned short breakNum; /* break type [1-9] */ char *logfile; /* logfile */ + off_t logfilemax; /* size limit for rolling logfile */ char *initcmd; /* initcmd command */ char *motd; /* motd */ time_t idletimeout; /* idle timeout */ diff --git a/conserver/conserver.man b/conserver/conserver.man index d9139c1..981746c 100644 --- a/conserver/conserver.man +++ b/conserver/conserver.man @@ -1,6 +1,6 @@ .\" @(#)conserver.8 01/06/91 OSU CIS; Thomas A. Fine .\" $Id: conserver.man,v 1.40 2003/11/10 15:37:24 bryan Exp $ -.TH CONSERVER 8 "2003/11/10" "conserver-8.0.7" "conserver" +.TH CONSERVER 8 "2003/11/10" "conserver-8.0.8" "conserver" .SH NAME conserver \- console server daemon .SH SYNOPSIS diff --git a/conserver/cutil.c b/conserver/cutil.c index 21dc0e3..6ff34dc 100644 --- a/conserver/cutil.c +++ b/conserver/cutil.c @@ -1,5 +1,5 @@ /* - * $Id: cutil.c,v 1.107 2003/11/20 13:59:55 bryan Exp $ + * $Id: cutil.c,v 1.109 2003/12/02 16:21:43 bryan Exp $ * * Copyright conserver.com, 2000 * @@ -921,7 +921,12 @@ FileClose(pcfp) break; } - CONDDEBUG((2, "FileClose(): closed fd %d", cfp->fd)); + if (cfp->ftype == simplePipe) { + CONDDEBUG((2, "FileClose(): closed fd %d/%d", cfp->fd, + cfp->fdout)); + } else { + CONDDEBUG((2, "FileClose(): closed fd %d", cfp->fd)); + } DestroyString(cfp->wbuf); free(cfp); *pcfp = (CONSFILE *)0; @@ -1108,6 +1113,19 @@ FileWrite(cfp, bufferonly, buf, len) break; } retval = -1; + /* i believe, as of 8.0.8, we need to just ignore + * this and actually produce the error message + * below. perhaps we'll have a lot of extra + * FileWrite() errors, perhaps not. things shouldn't + * just close down and cause errors in normal cases, + * right?!? -bryan + * maybe not right now, actually. i'm going to check + * the return code of FileWrite() on the "important" + * things and let the others silently fail and have + * the FileRead() catch problems - like it has been + * doing. i really should be checking all the return + * codes...and i'm sure i'll get there eventually. + */ if (errno == EPIPE) break; Error("FileWrite(): fd %d: %s", fdout, diff --git a/conserver/cutil.h b/conserver/cutil.h index 779e421..45b002f 100644 --- a/conserver/cutil.h +++ b/conserver/cutil.h @@ -1,5 +1,5 @@ /* - * $Id: cutil.h,v 1.56 2003/11/10 20:38:25 bryan Exp $ + * $Id: cutil.h,v 1.57 2003/11/28 00:47:29 bryan Exp $ * * Copyright conserver.com, 2000 * @@ -18,6 +18,7 @@ /* communication constants */ +#define OB_EXEC 'E' /* exec a command on the client */ #define OB_SUSP 'Z' /* suspended by server */ #define OB_DROP '.' /* dropped by server */ diff --git a/conserver/group.c b/conserver/group.c index 0a67d1d..9c59d64 100644 --- a/conserver/group.c +++ b/conserver/group.c @@ -1,5 +1,5 @@ /* - * $Id: group.c,v 5.275 2003/11/20 13:56:38 bryan Exp $ + * $Id: group.c,v 5.279 2003/12/02 16:21:43 bryan Exp $ * * Copyright conserver.com, 2000 * @@ -860,7 +860,7 @@ ReOpen(pGE) FileOpen(pCE->logfile, O_RDWR | O_CREAT | O_APPEND, 0644))) { Error("[%s] FileOpen(%s): %s: forcing down", pCE->server, pCE->logfile, strerror(errno)); - ConsoleError(pCE); + ConsDown(pCE, FLAGTRUE, FLAGTRUE); continue; } } @@ -979,6 +979,116 @@ TagLogfileAct(pCE, fmt, va_alist) va_end(ap); } +static void +#if PROTOTYPES +RollLogs(GRPENT *pGE) +#else +RollLogs(pGE) + GRPENT *pGE; +#endif +{ + CONSENT *pCE; + struct stat stLog; + char *t = (char *)0; + char timestr[40]; + time_t tyme = (time_t)0; + short maxset = 0; + char buf[4096]; + int roll = 0; + int r = 0; + CONSFILE *old; + + if ((GRPENT *)0 == pGE) + return; + + for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { + if (pCE->logfilemax == 0) + continue; + maxset = 1; + if (pCE->fdlog == (CONSFILE *)0) + continue; + if (FileStat(pCE->fdlog, &stLog) != 0) { + CONDDEBUG((1, "RollLogs(): FileStat(%d) failed", + FileFDNum(pCE->fdlog))); + continue; + } + if (stLog.st_size < pCE->logfilemax) + continue; + if (pCE->logfilemax > 1024) { + if (pCE->logfilemax > 0x100000) + Msg("[%s] logfile exceeds %dMB: rolling", pCE->server, + pCE->logfilemax / 0x100000); + else + Msg("[%s] logfile exceeds %dKB: rolling", pCE->server, + pCE->logfilemax / 1024); + } + + if (pCE->logfilemax < 4000) + roll = 100; + else if (pCE->logfilemax > 160000) + roll = 4000; + else + roll = pCE->logfilemax * 0.025; + + r = 0; + + if (FileSeek(pCE->fdlog, stLog.st_size - roll, SEEK_SET) > 0) { + if ((r = FileRead(pCE->fdlog, buf, 4096)) > 0) { + if (r == roll) { + for (; r != 0 && buf[roll - r] != '\n'; r--); + r--; /* go beyond \n */ + } else + r = 0; + } + } + + if (tyme == (time_t)0) { + tyme = time((time_t *)0); + strftime(timestr, sizeof(timestr), "-%Y%m%d-%H%M%S", + gmtime(&tyme)); + } + BuildTmpString((char *)0); + t = BuildTmpStringPrint("%s%s", pCE->logfile, timestr); + + if (rename(pCE->logfile, t) != 0) { + Error("[%s] RollLogs(): rename(%s,%s) failed: %s", pCE->server, + pCE->logfile, t, strerror(errno)); + continue; + } + + old = pCE->fdlog; + + if ((pCE->fdlog = + FileOpen(pCE->logfile, O_RDWR | O_CREAT | O_APPEND, + 0644)) == (CONSFILE *)0) { + FileClose(&old); + Error("[%s] RollLogs(): open(%s): %s: forcing down", + pCE->server, pCE->logfile, strerror(errno)); + ConsDown(pCE, FLAGTRUE, FLAGTRUE); + continue; + } + if (r > 0) { + FileWrite(pCE->fdlog, FLAGFALSE, buf + roll - r, r); + ftruncate(FileFDNum(old), stLog.st_size - r); + } + + FileClose(&old); + } + + if (tyme != (time_t)0) + BuildTmpString((char *)0); + + if (maxset == 0) + timers[T_ROLL] = (time_t)0; + else { + if (timers[T_ROLL] == (time_t)0) + /* try and spread processes out a bit */ + timers[T_ROLL] = time((time_t *)0) + 300 + (pGE->id * 7) % 60; + else + timers[T_ROLL] = time((time_t *)0) + 300; + } +} + static void #if PROTOTYPES Mark(GRPENT *pGE) @@ -1191,8 +1301,12 @@ ReapVirt(pGE) for (pCE = pGE->pCElist; pCE != (CONSENT *)0; pCE = pCE->pCEnext) { if (pid == pCE->initpid) { - Verbose("[%s] initcmd terminated: pid %lu", pCE->server, - (unsigned long)pCE->initpid); + if (WIFEXITED(UWbuf)) + Msg("[%s] initcmd terminated: pid %lu: exit(%d)", + pCE->server, pid, WEXITSTATUS(UWbuf)); + if (WIFSIGNALED(UWbuf)) + Msg("[%s] initcmd terminated: pid %lu: signal(%d)", + pCE->server, pid, WTERMSIG(UWbuf)); TagLogfileAct(pCE, "initcmd terminated"); pCE->initpid = 0; StopInit(pCE); @@ -1240,6 +1354,10 @@ static char acStop[] = { /* buffer for OOB stop command */ OB_SUSP }; +static char acExec[] = { /* buffer for OOB exec command */ + OB_EXEC +}; + int #if PROTOTYPES CheckPasswd(CONSCLIENT *pCL, char *pw_string) @@ -1795,9 +1913,10 @@ CommandForce(pGE, pCLServing, pCEServing, tyme) FilePrint(pCLServing->fd, FLAGFALSE, "bumped %s]\r\n", pCL->acid->string); } - FilePrint(pCL->fd, FLAGFALSE, - "\r\n[forced to `spy' mode by %s]\r\n", - pCLServing->acid->string); + if (pCL->fcon) + FilePrint(pCL->fd, FLAGFALSE, + "\r\n[forced to `spy' mode by %s]\r\n", + pCLServing->acid->string); TagLogfileAct(pCEServing, "%s bumped %s", pCLServing->acid->string, pCL->acid->string); } else { @@ -2119,6 +2238,7 @@ DoConsoleRead(pCEServing) /* read terminal line */ if ((nr = FileRead(pCEServing->cofile, acInOrig, sizeof(acInOrig))) < 0) { + Error("[%s] read failure", pCEServing->server); ConsoleError(pCEServing); return; } @@ -2704,6 +2824,19 @@ DoClientRead(pGE, pCLServing) pCLServing->msg->string[pCLServing->msg->used - 2] = '\000'; pCLServing->msg->used--; + } else if ((acIn[i] == 0x15) && + pCLServing->msg->used > 1) { + while (pCLServing->msg->used > 1) { + if (pCLServing->msg-> + string[pCLServing->msg->used - 2] != + '\a' && pGE->pCEctl != pCEServing) { + FileWrite(pCLServing->fd, FLAGFALSE, + "\b \b", 3); + } + pCLServing->msg->string[pCLServing->msg-> + used - 2] = '\000'; + pCLServing->msg->used--; + } } continue; } @@ -2773,8 +2906,18 @@ DoClientRead(pGE, pCLServing) FileWrite(pCLServing->fd, FLAGFALSE, " -- spy mode]\r\n", -1); } + /* fall through */ + case S_CWAIT: pCLServing->fcon = 1; pCLServing->iState = S_NORMAL; + if (acInOrig[i] == OB_EXEC) { + if (pCEServing->pCLwr == pCLServing) + FileWrite(pCLServing->fd, FLAGFALSE, + "[rw]\r\n", -1); + else + FileWrite(pCLServing->fd, FLAGFALSE, + "[ro]\r\n", -1); + } continue; case S_NORMAL: @@ -3014,20 +3157,20 @@ DoClientRead(pGE, pCLServing) case '\022': /* ^R */ FileWrite(pCLServing->fd, FLAGFALSE, "^R]\r\n", -1); - Replay(pCEServing->fdlog, pCLServing->fd, 1); + Replay(pCEServing, pCLServing->fd, 1); break; case 'R': /* DEC vt100 pf3 */ case 'r': /* replay 20 lines */ FileWrite(pCLServing->fd, FLAGFALSE, "replay]\r\n", -1); - Replay(pCEServing->fdlog, pCLServing->fd, 20); + Replay(pCEServing, pCLServing->fd, 20); break; case 'p': /* replay 60 lines */ FileWrite(pCLServing->fd, FLAGFALSE, "long replay]\r\n", -1); - Replay(pCEServing->fdlog, pCLServing->fd, 60); + Replay(pCEServing, pCLServing->fd, 60); break; case 'S': /* DEC vt100 pf4 */ @@ -3076,6 +3219,18 @@ DoClientRead(pGE, pCLServing) tyme); break; + case '|': /* wait for client */ + if (!pCLServing->fwr) { + FileWrite(pCLServing->fd, FLAGFALSE, + "attach to run local command]\r\n", + -1); + continue; + } + FileSend(pCLServing->fd, acExec, 1, MSG_OOB); + pCLServing->fcon = 0; + pCLServing->iState = S_CWAIT; + break; + case 'z': /* suspend the client */ case 'Z': case '\032': @@ -3237,6 +3392,7 @@ FlushConsole(pCEServing) if (FileWrite (pCEServing->cofile, FLAGFALSE, pCEServing->wbuf->string, pCEServing->wbuf->used - 1) < 0) { + Error("[%s] write failure", pCEServing->server); ConsoleError(pCEServing); break; } @@ -3270,6 +3426,8 @@ FlushConsole(pCEServing) (pCEServing->cofile, FLAGFALSE, pCEServing->wbuf->string, pCEServing->wbufIAC - 2) < 0) { + Error("[%s] write failure", + pCEServing->server); ConsoleError(pCEServing); break; } @@ -3425,6 +3583,7 @@ FlushConsole(pCEServing) if (FileWrite (pCEServing->cofile, FLAGFALSE, buf->string, buf->used - 1) < 0) { + Error("[%s] write failure", pCEServing->server); ConsoleError(pCEServing); break; } @@ -3605,6 +3764,7 @@ Kiddie(pGE, sfd) SimpleSignal(SIGUSR1, FlagReUp); /* prime the pump */ + RollLogs(pGE); Mark(pGE); /* the MAIN loop a group server @@ -3727,6 +3887,11 @@ Kiddie(pGE, sfd) time((time_t *)0) >= timers[T_AUTOUP]) ReUp(pGE, 1); + /* do we need to check on log file sizes? */ + if (timers[T_ROLL] != (time_t)0 && + time((time_t *)0) >= timers[T_ROLL]) + RollLogs(pGE); + /* check on various timers and set the appropriate timeout */ /* all this so we don't have to use alarm() any more... */ @@ -3834,6 +3999,8 @@ Kiddie(pGE, sfd) if (FileWrite (pCEServing->cofile, FLAGFALSE, (char *)0, 0) < 0) { + Error("[%s] write failure", + pCEServing->server); ConsoleError(pCEServing); break; } @@ -3846,6 +4013,8 @@ Kiddie(pGE, sfd) if (FileWrite (pCEServing->fdlog, FLAGFALSE, (char *)0, 0) < 0) { + Error("[%s] write failure", + pCEServing->server); ConsoleError(pCEServing); break; } @@ -3859,6 +4028,8 @@ Kiddie(pGE, sfd) if (FileWrite (pCEServing->initfile, FLAGFALSE, (char *)0, 0) < 0) { + Error("[%s] write failure", + pCEServing->server); ConsoleError(pCEServing); break; } diff --git a/conserver/group.h b/conserver/group.h index 353b413..4b87a3e 100644 --- a/conserver/group.h +++ b/conserver/group.h @@ -1,5 +1,5 @@ /* - * $Id: group.h,v 5.41 2003/11/15 20:00:09 bryan Exp $ + * $Id: group.h,v 5.42 2003/11/28 23:36:02 bryan Exp $ * * Copyright conserver.com, 2000 * @@ -40,7 +40,8 @@ #define T_MARK 2 #define T_REINIT 3 #define T_AUTOUP 4 -#define T_MAX 5 /* T_MAX *must* be last */ +#define T_ROLL 5 +#define T_MAX 6 /* T_MAX *must* be last */ /* return values used by CheckPass() */ diff --git a/conserver/readcfg.c b/conserver/readcfg.c index f465b2b..1f4a8d7 100644 --- a/conserver/readcfg.c +++ b/conserver/readcfg.c @@ -1,5 +1,5 @@ /* - * $Id: readcfg.c,v 5.158 2003/11/20 13:56:39 bryan Exp $ + * $Id: readcfg.c,v 5.160 2003/12/01 02:15:18 bryan Exp $ * * Copyright conserver.com, 2000 * @@ -663,6 +663,8 @@ ApplyDefault(d, c) c->parity = d->parity; if (d->idletimeout != 0) c->idletimeout = d->idletimeout; + if (d->logfilemax != 0) + c->logfilemax = d->logfilemax; if (d->port != 0) c->port = d->port; if (d->portinc != 0) @@ -1299,6 +1301,66 @@ DefaultItemLogfile(id) ProcessLogfile(parserDefaultTemp, id); } +void +#if PROTOTYPES +ProcessLogfilemax(CONSENT *c, char *id) +#else +ProcessLogfilemax(c, id) + CONSENT *c; + char *id; +#endif +{ + char *p; + off_t v = 0; + + c->logfilemax = 0; + + if (id == (char *)0 || id[0] == '\000') + return; + + for (p = id; *p != '\000'; p++) { + if (!isdigit((int)(*p))) + break; + v = v * 10 + (*p - '0'); + } + + /* if it wasn't just numbers */ + if (*p != '\000') { + if ((*p == 'k' || *p == 'K') && *(p + 1) == '\000') { + v *= 1024; + } else if ((*p == 'm' || *p == 'M') && *(p + 1) == '\000') { + v *= 1024 * 1024; + } else { + if (isMaster) + Error("invalid `logfilemax' specification `%s' [%s:%d]", + id, file, line); + return; + } + } + + if (v < 2048) { + if (isMaster) + Error + ("invalid `logfilemax' specification `%s' (must be >= 2K) [%s:%d]", + id, file, line); + return; + } + + c->logfilemax = v; +} + +void +#if PROTOTYPES +DefaultItemLogfilemax(char *id) +#else +DefaultItemLogfilemax(id) + char *id; +#endif +{ + CONDDEBUG((1, "DefaultItemLogfilemax(%s) [%s:%d]", id, file, line)); + ProcessLogfilemax(parserDefaultTemp, id); +} + void #if PROTOTYPES DefaultItemInitcmd(char *id) @@ -1543,6 +1605,12 @@ ProcessPortinc(c, id) return; } c->portinc = (unsigned short)atoi(id) + 1; + /* make sure the value was >=1 */ + if (c->portinc <= 1) { + if (isMaster) + Error("invalid portinc number `%s' [%s:%d]", id, file, line); + c->portinc = 0; + } } void @@ -1560,16 +1628,24 @@ ProcessPortbase(c, id) c->portbase = 0; return; } + + /* if we have -1, allow it (we allow >= -1 now) */ + if (id[0] == '-' && id[1] == '1' && id[2] == '\000') { + c->portbase = 1; + return; + } + for (p = id; *p != '\000'; p++) if (!isdigit((int)(*p))) break; + /* if it wasn't a number */ if (*p != '\000') { if (isMaster) Error("invalid portbase number `%s' [%s:%d]", id, file, line); return; } - c->portbase = (unsigned short)atoi(id) + 1; + c->portbase = (unsigned short)atoi(id) + 2; } void @@ -2632,6 +2708,11 @@ ConsoleAdd(c) timers[T_IDLE] > pCEmatch->lastWrite + pCEmatch->idletimeout)) timers[T_IDLE] = pCEmatch->lastWrite + pCEmatch->idletimeout; + pCEmatch->logfilemax = c->logfilemax; + if (pCEmatch->logfilemax != (off_t) 0 && + timers[T_ROLL] == (time_t)0) + timers[T_ROLL] = time((time_t)0); + SwapStr(&pCEmatch->motd, &c->motd); SwapStr(&pCEmatch->idlestring, &c->idlestring); pCEmatch->portinc = c->portinc; @@ -2722,17 +2803,20 @@ ConsoleDestroy() if (c->breakNum == 0) c->breakNum = 1; - /* portbase and portinc values are +1 so a zero can show that - * no value was given. defaults: portbase=0, portinc=1 + /* portbase and portinc values are +2 and +1, so a zero can + * show that no value was given. defaults: portbase=0, portinc=1 */ if (c->portbase != 0) - c->portbase--; + c->portbase -= 2; if (c->portinc != 0) c->portinc--; else c->portinc = 1; /* now calculate the "real" port number */ + /* this formula should always give >= 0 because + * portbase >= -1, portinc >= 1, and port >= 1 + */ c->port = c->portbase + c->portinc * c->port; /* check for substitutions */ @@ -3074,6 +3158,18 @@ ConsoleItemLogfile(id) ProcessLogfile(parserConsoleTemp, id); } +void +#if PROTOTYPES +ConsoleItemLogfilemax(char *id) +#else +ConsoleItemLogfilemax(id) + char *id; +#endif +{ + CONDDEBUG((1, "ConsoleItemLogfilemax(%s) [%s:%d]", id, file, line)); + ProcessLogfilemax(parserConsoleTemp, id); +} + void #if PROTOTYPES ConsoleItemInitcmd(char *id) @@ -4159,6 +4255,7 @@ ITEM keyDefault[] = { {"include", DefaultItemInclude}, {"initcmd", DefaultItemInitcmd}, {"logfile", DefaultItemLogfile}, + {"logfilemax", DefaultItemLogfilemax}, {"master", DefaultItemMaster}, {"motd", DefaultItemMOTD}, {"options", DefaultItemOptions}, @@ -4188,6 +4285,7 @@ ITEM keyConsole[] = { {"include", ConsoleItemInclude}, {"initcmd", ConsoleItemInitcmd}, {"logfile", ConsoleItemLogfile}, + {"logfilemax", ConsoleItemLogfilemax}, {"master", ConsoleItemMaster}, {"motd", ConsoleItemMOTD}, {"options", ConsoleItemOptions}, diff --git a/conserver/version.h b/conserver/version.h index f0e16e8..e669a85 100644 --- a/conserver/version.h +++ b/conserver/version.h @@ -1,5 +1,5 @@ /* - * $Id: version.h,v 1.53 2003/11/20 14:15:39 bryan Exp $ + * $Id: version.h,v 1.54 2003/11/28 00:47:29 bryan Exp $ * * Copyright conserver.com, 2000 * @@ -14,4 +14,4 @@ @(#) Copyright 2000 conserver.com.\n\ All rights reserved.\n" -#define THIS_VERSION "conserver.com version 8.0.7" +#define THIS_VERSION "conserver.com version 8.0.8" diff --git a/console/Makefile.in b/console/Makefile.in index 3f3dbf8..093ce5c 100644 --- a/console/Makefile.in +++ b/console/Makefile.in @@ -15,7 +15,7 @@ MKDIR = @MKDIR@ ### Compiler and link options CC = @CC@ -CFLAGS = @CFLAGS@ # -DPUCC -DSUN5 +CFLAGS = @CFLAGS@ DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)\" CPPFLAGS = -I.. -I$(top_srcdir) -I$(srcdir) -I$(top_srcdir)/conserver $(DEFS) @CPPFLAGS@ LDFLAGS = @LDFLAGS@ @@ -26,17 +26,22 @@ LIBS = @LIBS@ ### Makefile rules - no user-servicable parts below CONSOLE_OBJS = console.o getpassword.o ../conserver/cutil.o -CONSOLE_HDRS = ../config.h $(top_srcdir)/compat.h $(top_srcdir)/conserver/port.h +CONSOLE_HDRS = ../config.h $(top_srcdir)/compat.h \ + $(top_srcdir)/conserver/cutil.h \ + $(top_srcdir)/conserver/version.h \ + $(srcdir)/getpassword.h ALL = console all: $(ALL) +$(CONSOLE_OBJS): $(CONSOLE_HDRS) + console: $(CONSOLE_OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -o console $(CONSOLE_OBJS) $(LIBS) -../conserver/util.o: - ( cd ../conserver && $(MAKE) $(MAKE_FLAGS) util.o ) || exit 1; +../conserver/cutil.o: + ( cd ../conserver && $(MAKE) $(MAKE_FLAGS) cutil.o ) || exit 1; .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< diff --git a/console/console.c b/console/console.c index e085d4c..926a060 100644 --- a/console/console.c +++ b/console/console.c @@ -1,5 +1,5 @@ /* - * $Id: console.c,v 5.151 2003/11/20 13:56:41 bryan Exp $ + * $Id: console.c,v 5.152 2003/11/28 00:47:30 bryan Exp $ * * Copyright conserver.com, 2000 * @@ -54,6 +54,9 @@ unsigned short bindPort; CONSFILE *cfstdout; char *pcUser = (char *)0; int disconnectCount = 0; +STRING *execCmd = (STRING *)0; +CONSFILE *execCmdFile = (CONSFILE *)0; +pid_t execCmdPid = 0; #if HAVE_OPENSSL SSL_CTX *ctx = (SSL_CTX *)0; @@ -625,15 +628,17 @@ OOB(sig) void #if PROTOTYPES -ProcessUrgentData(int s) +ProcessUrgentData(CONSFILE *pcf) #else -ProcessUrgentData(s) - int s; +ProcessUrgentData(pcf) + CONSFILE *pcf; #endif { static char acCmd; + int s; SawUrg = 0; + s = FileFDNum(pcf); /* get the pending urgent message */ @@ -642,7 +647,7 @@ ProcessUrgentData(s) case EWOULDBLOCK: /* clear any pending input to make room */ read(s, &acCmd, 1); - write(1, ".", 1); + FileWrite(cfstdout, FLAGFALSE, ".", 1); continue; case EINVAL: default: @@ -652,21 +657,57 @@ ProcessUrgentData(s) } } switch (acCmd) { + case OB_EXEC: + FileWrite(cfstdout, FLAGFALSE, "exec: ", 6); + BuildString((char *)0, execCmd); + for (;;) { + char c; + if (read(0, &c, 1) == 0) + break; + if (c == '\n' || c == '\r') { + FileWrite(cfstdout, FLAGFALSE, "]\r\n", 3); + if (execCmd->used <= 1) { + char s = OB_DROP; + FileWrite(pcf, FLAGFALSE, &s, 1); + } + break; + } + if (c == '\a' || (c >= ' ' && c <= '~')) { + BuildStringChar(c, execCmd); + FileWrite(cfstdout, FLAGFALSE, &c, 1); + } else if ((c == '\b' || c == 0x7f) && execCmd->used > 1) { + if (execCmd->string[execCmd->used - 2] != '\a') { + FileWrite(cfstdout, FLAGFALSE, "\b \b", 3); + } + execCmd->string[execCmd->used - 2] = '\000'; + execCmd->used--; + } else if ((c == 0x15) && execCmd->used > 1) { + while (execCmd->used > 1) { + if (execCmd->string[execCmd->used - 2] != '\a') { + FileWrite(cfstdout, FLAGFALSE, "\b \b", 3); + } + execCmd->string[execCmd->used - 2] = '\000'; + execCmd->used--; + } + } + } + break; case OB_SUSP: #if defined(SIGSTOP) - write(1, "stop]", 5); + FileWrite(cfstdout, FLAGFALSE, "stop]", 5); C2Cooked(); kill(getpid(), SIGSTOP); C2Raw(); - write(1, "[press any character to continue", 32); + FileWrite(cfstdout, FLAGFALSE, + "[press any character to continue", 32); #else - write(1, - "stop not supported -- press any character to continue", - 53); + FileWrite(cfstdout, FLAGFALSE, + "stop not supported -- press any character to continue", + 53); #endif break; case OB_DROP: - write(1, "dropped by server]\r\n", 20); + FileWrite(cfstdout, FLAGFALSE, "dropped by server]\r\n", 20); C2Cooked(); Bye(EX_UNAVAILABLE); /*NOTREACHED*/ default: @@ -676,6 +717,179 @@ ProcessUrgentData(s) } } +static void +#if PROTOTYPES +ReapVirt(void) +#else +ReapVirt() +#endif +{ + pid_t pid; + int UWbuf; + + while (-1 != (pid = waitpid(-1, &UWbuf, WNOHANG | WUNTRACED))) { + if (0 == pid) + break; + + /* stopped child is just continued + */ + if (WIFSTOPPED(UWbuf) && 0 == kill(pid, SIGCONT)) { + Msg("child pid %lu: stopped, sending SIGCONT", + (unsigned long)pid); + continue; + } + + if (WIFEXITED(UWbuf)) + Verbose("child process %lu: exit(%d)", pid, + WEXITSTATUS(UWbuf)); + if (WIFSIGNALED(UWbuf)) + Verbose("child process %lu: signal(%d)", pid, WTERMSIG(UWbuf)); + if (pid == execCmdPid) { + if (WIFEXITED(UWbuf)) + FilePrint(cfstdout, FLAGFALSE, + "[local command terminated - pid %lu: exit(%d)]\r\n", + pid, WEXITSTATUS(UWbuf)); + if (WIFSIGNALED(UWbuf)) + FilePrint(cfstdout, FLAGFALSE, + "[local command terminated - pid %lu: signal(%d)]\r\n", + pid, WTERMSIG(UWbuf)); + } + } +} + +static sig_atomic_t fSawReapVirt = 0; + +#if HAVE_SIGACTION +static +#endif + RETSIGTYPE +#if PROTOTYPES +FlagReapVirt(int sig) +#else +FlagReapVirt(sig) + int sig; +#endif +{ + fSawReapVirt = 1; +#if !HAVE_SIGACTION + SimpleSignal(SIGCHLD, FlagReapVirt); +#endif +} + +/* invoke the execcmd command */ +void +#if PROTOTYPES +ExecCmd(void) +#else +ExecCmd() +#endif +{ + int i; + pid_t iNewGrp; + extern char **environ; + int pin[2]; + int pout[2]; + static char *apcArgv[] = { + "/bin/sh", "-ce", (char *)0, (char *)0 + }; + + if (execCmd == (STRING *)0 || execCmd->used <= 1) + return; + + CONDDEBUG((1, "ExecCmd(): `%s'", execCmd->string)); + + /* pin[0] = parent read, pin[1] = child write */ + if (pipe(pin) != 0) { + Error("ExecCmd(): pipe(): %s", strerror(errno)); + return; + } + /* pout[0] = child read, pout[l] = parent write */ + if (pipe(pout) != 0) { + close(pin[0]); + close(pin[1]); + Error("ExecCmd(): pipe(): %s", strerror(errno)); + return; + } + + fflush(stdout); + fflush(stderr); + + switch (execCmdPid = fork()) { + case -1: + return; + case 0: + thepid = getpid(); + break; + default: + close(pout[0]); + close(pin[1]); + if ((execCmdFile = + FileOpenPipe(pin[0], pout[1])) == (CONSFILE *)0) { + Error("ExecCmd(): FileOpenPipe(%d,%d) failed", pin[0], + pout[1]); + close(pin[0]); + close(pout[1]); + kill(execCmdPid, SIGHUP); + return; + } + FilePrint(cfstdout, FLAGFALSE, + "[local command running - pid %lu]\r\n", execCmdPid); + FD_SET(pin[0], &rinit); + if (maxfd < pin[0] + 1) + maxfd = pin[0] + 1; + fflush(stderr); + return; + } + + close(pin[0]); + close(pout[1]); + + /* put the signals back that we ignore (trapped auto-reset to default) + */ +#if defined(SIGURG) + SimpleSignal(SIGURG, SIG_DFL); +#endif + SimpleSignal(SIGPIPE, SIG_DFL); + SimpleSignal(SIGCHLD, SIG_DFL); + + /* setup new process with clean file descriptors + * stderr still goes to stderr...so user sees it + */ + i = GetMaxFiles(); + for ( /* i above */ ; --i > 3;) { + if (i != pout[0] && i != pin[1]) + close(i); + } + close(1); + close(0); + +# if HAVE_SETSID + iNewGrp = setsid(); + if (-1 == iNewGrp) { + Error("ExecCmd(): setsid(): %s", strerror(errno)); + iNewGrp = getpid(); + } +# else + iNewGrp = getpid(); +# endif + + if (dup(pout[0]) != 0 || dup(pin[1]) != 1) { + Error("ExecCmd(): fd sync error"); + Bye(EX_OSERR); + } + close(pout[0]); + close(pin[1]); + + tcsetpgrp(0, iNewGrp); + + apcArgv[2] = execCmd->string; + + execve(apcArgv[0], apcArgv, environ); + Error("ExecCmd(): execve(%s): %s", apcArgv[2], strerror(errno)); + Bye(EX_OSERR); + return; +} + /* interact with a group server (ksb) */ static int @@ -690,7 +904,7 @@ CallUp(pcf, pcMaster, pcMach, pcHow, result) { int nc; int fIn = '-'; - fd_set rmask, rinit; + fd_set rmask, wmask; int i; int justProcessedUrg = 0; char *r = (char *)0; @@ -723,6 +937,7 @@ CallUp(pcf, pcMaster, pcMach, pcHow, result) #if defined(SIGURG) SimpleSignal(SIGURG, OOB); #endif + SimpleSignal(SIGCHLD, FlagReapVirt); /* if we are going for a particular console * send sign-on stuff, then wait for some indication of what mode @@ -818,12 +1033,16 @@ CallUp(pcf, pcMaster, pcMach, pcHow, result) C2Raw(); + /* set socket to non-blocking */ + SetFlags(FileFDNum(pcf), O_NONBLOCK, 0); + /* read from stdin and the socket (non-blocking!). * rmask indicates which descriptors to read from, * the others are not used, nor is the result from * select, read, or write. */ FD_ZERO(&rinit); + FD_ZERO(&winit); FD_SET(FileFDNum(pcf), &rinit); FD_SET(0, &rinit); if (maxfd < FileFDNum(pcf) + 1) @@ -831,14 +1050,46 @@ CallUp(pcf, pcMaster, pcMach, pcHow, result) for (;;) { justProcessedUrg = 0; if (SawUrg) { - ProcessUrgentData(FileFDNum(pcf)); + ProcessUrgentData(pcf); justProcessedUrg = 1; } + if (execCmd != (STRING *)0 && execCmd->used > 1) { + char *r; + char s = OB_EXEC; + ExecCmd(); + BuildString((char *)0, execCmd); + if (execCmdFile == (CONSFILE *)0) { /* exec failed */ + s = OB_DROP; + FileWrite(pcf, FLAGFALSE, &s, 1); /* say forget it */ + } else { + /* go back to blocking mode */ + SetFlags(FileFDNum(pcf), 0, O_NONBLOCK); + FileWrite(pcf, FLAGFALSE, &s, 1); /* say we're ready */ + r = ReadReply(pcf, 0); + /* now back to non-blocking now that we've got reply */ + SetFlags(FileFDNum(pcf), O_NONBLOCK, 0); + /* if we aren't still r/w, abort */ + if (strncmp(r, "[rw]", 4) != 0) { + FileWrite(cfstdout, FLAGFALSE, + "[no longer read-write - aborting command]\r\n", + -1); + FD_CLR(FileFDNum(execCmdFile), &rinit); + FD_CLR(FileFDOutNum(execCmdFile), &winit); + FileClose(&execCmdFile); + kill(execCmdPid, SIGHUP); + } + } + } + if (fSawReapVirt) { + fSawReapVirt = 0; + ReapVirt(); + } /* reset read mask and select on it */ rmask = rinit; + wmask = winit; if (-1 == - select(maxfd, &rmask, (fd_set *)0, (fd_set *)0, + select(maxfd, &rmask, &wmask, (fd_set *)0, (struct timeval *)0)) { if (errno != EINTR) { Error("Master(): select(): %s", strerror(errno)); @@ -847,8 +1098,34 @@ CallUp(pcf, pcMaster, pcMach, pcHow, result) continue; } + /* anything from execCmd */ + if (execCmdFile != (CONSFILE *)0) { + if (FileCanRead(execCmdFile, &rmask, &wmask)) { + if ((nc = + FileRead(execCmdFile, acMesg, sizeof(acMesg))) < 0) { + FD_CLR(FileFDNum(execCmdFile), &rinit); + FD_CLR(FileFDOutNum(execCmdFile), &winit); + FileClose(&execCmdFile); + } else { + if (fStrip) { + for (i = 0; i < nc; ++i) + acMesg[i] &= 127; + } + FileWrite(pcf, FLAGFALSE, acMesg, nc); + } + } else if (!FileBufEmpty(execCmdFile) && + FileCanWrite(execCmdFile, &rmask, &wmask)) { + CONDDEBUG((1, "CallUp(): flushing fd %d", + FileFDNum(execCmdFile))); + if (FileWrite(execCmdFile, FLAGFALSE, (char *)0, 0) < 0) { + /* -bryan */ + break; + } + } + } + /* anything from socket? */ - if (FD_ISSET(FileFDNum(pcf), &rmask)) { + if (FileCanRead(pcf, &rmask, &wmask)) { if ((nc = FileRead(pcf, acMesg, sizeof(acMesg))) < 0) { /* if we got an error/eof after returning from suspend */ if (justProcessedUrg) { @@ -862,6 +1139,15 @@ CallUp(pcf, pcMaster, pcMach, pcHow, result) acMesg[i] &= 127; } FileWrite(cfstdout, FLAGFALSE, acMesg, nc); + if (execCmdFile != (CONSFILE *)0) { + FileWrite(execCmdFile, FLAGFALSE, acMesg, nc); + } + } else if (!FileBufEmpty(pcf) && FileCanWrite(pcf, &rmask, &wmask)) { + CONDDEBUG((1, "CallUp(): flushing fd %d", FileFDNum(pcf))); + if (FileWrite(pcf, FLAGFALSE, (char *)0, 0) < 0) { + /* -bryan */ + break; + } } /* anything from stdin? */ @@ -874,11 +1160,31 @@ CallUp(pcf, pcMaster, pcMach, pcHow, result) continue; } } - if (fStrip) { - for (i = 0; i < nc; ++i) - acMesg[i] &= 127; + if (execCmdFile == (CONSFILE *)0) { + if (fStrip) { + for (i = 0; i < nc; ++i) + acMesg[i] &= 127; + } + FileWrite(pcf, FLAGFALSE, acMesg, nc); + } else { + for (i = 0; i < nc; ++i) { + if (acMesg[i] == '\n' || acMesg[i] == '\r') + FilePrint(cfstdout, FLAGFALSE, + "[local command running - pid %lu]\r\n", + execCmdPid); + else if (acMesg[i] == 0x03) { /* ctrl-c */ + kill(execCmdPid, SIGHUP); + FilePrint(cfstdout, FLAGFALSE, + "[local command sent SIGHUP - pid %lu]\r\n", + execCmdPid); + } else if (acMesg[i] == 0x1c) { /* ctrl-\ */ + kill(execCmdPid, SIGKILL); + FilePrint(cfstdout, FLAGFALSE, + "[local command sent SIGKILL - pid %lu]\r\n", + execCmdPid); + } + } } - FileWrite(pcf, FLAGFALSE, acMesg, nc); } } C2Cooked(); @@ -1387,6 +1693,8 @@ main(argc, argv) } } + if (execCmd == (STRING *)0) + execCmd = AllocString(); SimpleSignal(SIGPIPE, SIG_IGN); diff --git a/console/console.man b/console/console.man index c09d116..1e7ebb3 100644 --- a/console/console.man +++ b/console/console.man @@ -1,5 +1,5 @@ -.\" $Id: console.man,v 1.40 2003/11/16 15:35:45 bryan Exp $ -.TH CONSOLE 1 "2003/11/16" "conserver-8.0.7" "conserver" +.\" $Id: console.man,v 1.41 2003/11/28 00:47:30 bryan Exp $ +.TH CONSOLE 1 "2003/11/28" "conserver-8.0.8" "conserver" .SH NAME console \- console server client program .SH SYNOPSIS @@ -454,6 +454,9 @@ examine this group's devices and modes .B z suspend this connection .TP +.B | +attach a local command to the console +.TP .B ? display list of commands .TP diff --git a/contrib/redhat-rpm/conserver.spec b/contrib/redhat-rpm/conserver.spec index 1d1cd5b..80974d2 100644 --- a/contrib/redhat-rpm/conserver.spec +++ b/contrib/redhat-rpm/conserver.spec @@ -4,7 +4,7 @@ # %define pkg conserver -%define ver 8.0.7 +%define ver 8.0.8 # define the name of the machine on which the main conserver # daemon will be running if you don't want to use the default diff --git a/contrib/solaris-package/pkginfo b/contrib/solaris-package/pkginfo index 72ca78c..cbfc6ad 100644 --- a/contrib/solaris-package/pkginfo +++ b/contrib/solaris-package/pkginfo @@ -1,7 +1,7 @@ PKG="conserver" NAME="Console server and client" CATEGORY="system" -VERSION="8.0.7" +VERSION="8.0.8" DESC="Console server and client" CLASSES=none ARCH=sparc diff --git a/test/results/test2 b/test/results/test2 index 69a4b72..5e5823e 100644 --- a/test/results/test2 +++ b/test/results/test2 @@ -11,7 +11,7 @@ r replay the last 20 lines s spy read only u show host status v show version info w who is on this console x show console baud info - z suspend the connection ignore/abort command - ? print this message ^R replay the last line - \ooo send character by octal code + z suspend the connection | attach local command + ignore/abort command ? print this message + ^R replay the last line \ooo send character by octal code [disconnect] diff --git a/test/results/test7 b/test/results/test7 index 69a4b72..5e5823e 100644 --- a/test/results/test7 +++ b/test/results/test7 @@ -11,7 +11,7 @@ r replay the last 20 lines s spy read only u show host status v show version info w who is on this console x show console baud info - z suspend the connection ignore/abort command - ? print this message ^R replay the last line - \ooo send character by octal code + z suspend the connection | attach local command + ignore/abort command ? print this message + ^R replay the last line \ooo send character by octal code [disconnect]