TheNetNode-CB/os/linux/linux.c

2266 lines
61 KiB
C

/************************************************************************/
/* */
/* ***** ***** */
/* ***** ***** */
/* ***** ***** */
/* ***** ***** */
/* *************** *************** */
/* ***************** ***************** */
/* *************** *************** */
/* ***** ***** TheNetNode */
/* ***** ***** Portable */
/* ***** ***** Network */
/* ***** ***** Software */
/* */
/* File os/linux/linux.c (maintained by: DF6LN) */
/* */
/* This file is part of "TheNetNode" - Software Package */
/* */
/* Copyright (C) 1998 - 2008 NORD><LINK e.V. Braunschweig */
/* */
/* This program is free software; you can redistribute it and/or modify */
/* it under the terms of the NORD><LINK ALAS (Allgemeine Lizenz fuer */
/* Amateurfunk Software) as published by Hans Georg Giese (DF2AU) */
/* on 13/Oct/1992; either version 1, or (at your option) any later */
/* version. */
/* */
/* This program is distributed WITHOUT ANY WARRANTY only for further */
/* development and learning purposes. See the ALAS (Allgemeine Lizenz */
/* fuer Amateurfunk Software). */
/* */
/* You should have received a copy of the NORD><LINK ALAS (Allgemeine */
/* Lizenz fuer Amateurfunk Software) along with this program; if not, */
/* write to NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig */
/* */
/* Dieses Programm ist PUBLIC DOMAIN, mit den Einschraenkungen durch */
/* die ALAS (Allgemeine Lizenz fuer Amateurfunk Software), entweder */
/* Version 1, veroeffentlicht von Hans Georg Giese (DF2AU), */
/* am 13.Oct.1992, oder (wenn gewuenscht) jede spaetere Version. */
/* */
/* Dieses Programm wird unter Haftungsausschluss vertrieben, aus- */
/* schliesslich fuer Weiterentwicklungs- und Lehrzwecke. Naeheres */
/* koennen Sie der ALAS (Allgemeine Lizenz fuer Amateurfunk Software) */
/* entnehmen. */
/* */
/* Sollte dieser Software keine ALAS (Allgemeine Lizenz fuer Amateur- */
/* funk Software) beigelegen haben, wenden Sie sich bitte an */
/* NORD><LINK e.V., Hinter dem Berge 5, D-38108 Braunschweig */
/* */
/************************************************************************/
#define _TNN_LINUX_C
#include "tnn.h"
static struct hostqueue *hostq_root;
static struct hostqueue *hostq_last;
static struct termios oconin_termios;
static struct termios nconin_termios;
static struct termios oconout_termios;
static struct termios nconout_termios;
static struct timeval tv;
static struct timezone tz;
static struct timeval tv_old;
static struct sockaddr_un serv_addr;
static int hostq_len;
static int servlen;
static int consockfd;
static int sockfd;
#ifndef NO_WATCHDOG
static int wdpid[2];
static int watch_dog[2];
#endif
UBYTE console_type = CONS_TERM_DO_SETUP;
#ifndef NO_WATCHDOG
static void watchdog_init(void);
#endif
#ifdef DEBUG_MODUS
static void sigterm(int);
#endif
static void host_to_queue(char *, int);
static void setfiletime(struct ffblk *);
static float load[3];
static float sys_load = 0;
static int tnn_load;
BOOLEAN unlock;
/* BOOLEAN use_socket; */
char tnn_socket[MAXPATH];
char start_name[MAXPATH];
char stop_name[MAXPATH];
UWORD maxrounds = 1; /* 1 = 100 Runden (default, wenn kein */
/* "rounds"-Eintrag in tnn.ini */
/*
String in Kleinbuchstaben umwandeln - aus DJGPP GNU-Paket fuer MS-DOS
von D.J. Delorie
*/
char *
strlwr(char *s)
{
char *p = s;
while (*s)
{
if ((*s >= 'A') && (*s <= 'Z'))
*s += 'a'-'A';
s++;
}
return(p);
}
/*
String in Grossbuchstaben umwandeln - aus DJGPP GNU-Paket fuer MS-DOS
von D.J. Delorie
*/
char *
strupr(char *s)
{
char *p = s;
while (*s)
{
if ((*s >= 'a') && (*s <= 'z'))
*s += 'A'-'a';
s++;
}
return(p);
}
/************************************************************************/
/* */
/* Als Ersatz fuer die Funktionen "findfirst" und "findnext" (bei DOS) */
/* werden die Funktionen "xfindfirst" und "xfindnext" verwendet. Hier */
/* wird nur der fuer TNN benoetigte Teil in die Struktur ffblk einge- */
/* tragen. Der zuletzt gefundene Filename muss in ffblk->ff_name erhal- */
/* ten bleiben, weil dieser beim Aufruf von "xfindnext" gesucht wird. */
/* */
/************************************************************************/
int
xfindfirst(const char *pathname, struct ffblk *ffblk, int attrib)
{
char *fnpoi;
DIR *dp;
struct dirent *dirp;
int retval;
strcpy(ffblk->ff_path, pathname); /* Filename incl. Pfad */
normfname(ffblk->ff_path); /* '\\' -> '/' */
fnpoi = strrchr(ffblk->ff_path, '/'); /* Pfad angegeben? */
if (fnpoi == NULL) /* - nein .. */
{
strcpy(ffblk->ff_find, ffblk->ff_path); /* Filename kopieren */
strcpy(ffblk->ff_path, textpath); /* default: textpath */
}
else /* mit Pfad */
{
if (fnpoi == ffblk->ff_path+strlen(ffblk->ff_path)) /* ohne Name */
return(-1); /* Unsinn */
strcpy(ffblk->ff_find, ++fnpoi); /* nur Filename */
*fnpoi = NUL; /* Filename im Pfad loeschen */
}
if ((dp = opendir(ffblk->ff_path)) == NULL) /* Directory vorhanden? */
return(-1);
retval = -1; /* default: nix gefunden */
while ((dirp = readdir(dp)) != NULL) /* Eintrag vorhanden? */
{
if ((fnmatch(ffblk->ff_find, dirp->d_name,
FNM_PATHNAME|FNM_PERIOD) != 0))
continue;
strcpy(ffblk->ff_name, dirp->d_name);
setfiletime(ffblk);
retval = 0;
break;
}
closedir(dp);
return(retval);
}
/************************************************************************/
/* */
/* Erst den zuletzt gefundenen Eintrag suchen (steht in ffblk->ff_name) */
/* und den darauffolgenden passenden Eintrag zurueckmelden. */
/* */
/************************************************************************/
int
xfindnext(struct ffblk *ffblk)
{
DIR *dp;
struct dirent *dirp;
int retval;
if ((dp = opendir(ffblk->ff_path)) == NULL) return(-1);
retval = -1; /* default: nix gefunden */
while ((dirp = readdir(dp)) != NULL)
{
if ((fnmatch(ffblk->ff_name, dirp->d_name,
FNM_PATHNAME|FNM_PERIOD) != 0))
continue;
retval = 1;
break;
}
if (retval == 1)
{
retval = -1; /* default: nix gefunden */
while ((dirp = readdir(dp)) != NULL)
{
if ((fnmatch(ffblk->ff_find, dirp->d_name,
FNM_PATHNAME|FNM_PERIOD) != 0))
continue;
strcpy(ffblk->ff_name, dirp->d_name);
setfiletime(ffblk);
retval = 0;
break;
}
}
closedir(dp);
return(retval);
}
/************************************************************************/
/* */
/* Bei xfindfirst und xfindnext Datum und Uhrzeit im Fileblock setzen */
/* */
/************************************************************************/
static void
setfiletime(struct ffblk *ffblk)
{
struct stat filestat;
struct tm *filetime;
char fn[MAXPATH];
sprintf(fn, "%s%s", ffblk->ff_path, ffblk->ff_name);
stat(fn, &filestat);
filetime = gmtime(&filestat.st_mtime);
ffblk->ff_ftime = ((filetime->tm_sec / 2) & 0x1f)
+ ((filetime->tm_min & 0x3f) << 5)
+ ((filetime->tm_hour & 0x1f) << 11);
ffblk->ff_fdate = (filetime->tm_mday & 0x1f)
+ (((filetime->tm_mon & 0x0f) + 1) << 5)
+ (((filetime->tm_year - 80) & 0x7f) << 9);
}
/************************************************************************/
/* */
/* Temporaeren Filenamen generieren und pruefen auf Verwendbarkeit, */
/* d.h. Grossbuchstaben sind nicht erlaubt, damit die Datei bei ccpread */
/* gefunden wird. */
/* */
/* 31.3.02 DG9OBU auf Verwendung von mkstemp() umgestellt */
/************************************************************************/
char *
xtempnam(const char *directory, const char *prefix)
{
char *fn = NULL;
size_t i;
register int x;
const char* sixx = "XXXXXX\000";
char namebuf[PATH_MAX];
char xe[128];
/* den Dateinamen erstellen, prefix und sechs X fuer mkstemp() */
memset(xe, 0, sizeof(xe));
strcpy(xe, prefix);
strcat(xe, sixx);
/* solange bis brauchbarer Filename gefunden */
while (fn == NULL)
{
/* Namen zusammenbauen */
memset(namebuf, 0, sizeof(namebuf));
strcpy(namebuf, directory);
strcat(namebuf, xe);
/* File generieren */
if ((x = mkstemp(&namebuf[0])) > 0)
{
/* und gleich wieder schliessen und loeschen */
close(x);
unlink(&namebuf[0]);
/* Namen pruefen */
for (i = 0; i < strlen(&namebuf[0]); ++i) /* Filenamen untersuchen */
if (namebuf[i] != tolower(namebuf[i])) /* Grossbuchstabe geht nicht */
break;
/* wenn Namenspruefung erfolgreich war, dann Namen melden */
if (i == strlen(namebuf))
{
fn = malloc(PATH_MAX);
strcpy(fn, &namebuf[0]);
return(fn);
}
}
else break;
}
/* nicht erfolgreich */
return(NULL);
}
/************************************************************************/
/* */
/* Ermitteln des freien Festplattenplatzes in Bytes. */
/* */
/* Damit es fuer grosse Festplatten keinen Ueberlauf gibt (Rueckgabe */
/* ist vom Typ LONG), wird ab 1GB freiem Speicher immer 1GB zurueck- */
/* gemeldet - sieht da jemand Probleme? */
/* */
/************************************************************************/
LONG
getdiskfree(char *path)
{
FILE *fp;
char str[100];
ULONG frei = 0L;
sprintf(str, "df %s", path);
if ((fp = popen(str, "r")) != NULL)
{
fgets(str, 100, fp);
fgets(str, 100, fp);
pclose(fp);
sscanf(str, "%*s %*s %*s %lu", &frei);
if (frei >= 1024 * 1024 * 1024)
frei = (1024 * 1024 * 1024);
else
frei = (frei * 1024);
}
return (frei);
}
/************************************************************************/
/* */
/* Fuer das erweiterte AUTOBIN-Protokoll wird die File-Zeit in einem */
/* Bitfeld gespeichert (s. Doku zu DPBOX), wie es BC++ bei dem Befehl */
/* getftime verwendet. Hier wird die Linux-Zeit der letzten Aenderung */
/* verwendet. */
/* */
/************************************************************************/
int getftime(int handle, struct ftime *ftimep)
{
struct stat filestat;
struct tm *filetime;
if (fstat(handle, &filestat) == -1)
return(-1);
filetime = gmtime(&filestat.st_mtime);
ftimep->ft_tsec = filetime->tm_sec / 2;
ftimep->ft_min = filetime->tm_min;
ftimep->ft_hour = filetime->tm_hour;
ftimep->ft_day = filetime->tm_mday;
ftimep->ft_month = filetime->tm_mon + 1;
ftimep->ft_year = filetime->tm_year - 80;
return(0);
}
/************************************************************************/
/* */
/* Fuer DOS-Kompatibilitaet - Programm starten und Exit-Code des */
/* Programms zurueckgeben */
/* */
/************************************************************************/
int
spawnl(int mode, const char *path, const char *arg0, const char *arg1,
const char *arg2, const char *arg3, const char *arg4, const char *arg5)
{
pid_t pid;
int status;
if (mode != P_WAIT)
{
errno = EINVAL;
return (-1);
}
if ((pid = fork()) < 0)
{
errno = ENOMEM;
return (-1);
}
else
{
if (pid == 0)
{
if (execl(path, arg0, arg1, arg2, arg3, arg4, arg5, NULL) == -1)
{
exit(-1);
}
}
}
waitpid(pid, &status, 0);
return(status);
}
/************************************************************************/
/* */
/* Groesse des freien RAM feststellen (wenig aussagekraeftig durch */
/* SWAP-Partition). Schlaegt der Aufruf fehl, wird 0 gemeldet. */
/* */
/************************************************************************/
ULONG
coreleft(void)
{
FILE *fp;
char str[100];
ULONG frei = 0L;
if ((fp = popen("free -b", "r")) != NULL)
{
fgets(str, 100, fp);
fgets(str, 100, fp);
pclose(fp);
sscanf(str, "%*s %*s %*s %lu", &frei);
}
return(frei);
}
/************************************************************************/
/* */
/* Befehlszeile auf Zeichen pruefen, die von der Shell ausgewertet */
/* werden. Diese werden entwertet durch ein vorangesetztes '\\'. */
/* */
/************************************************************************/
void
security_check(char *cmd)
{
char *cmdpoi = cmd;
char tmpcmd[MAXPATH];
register int i = 0;
char ch;
for (; i < MAXPATH - 2; ++i)
{
ch = *cmdpoi++;
if (ch == NUL)
break;
if (strchr("*?\\|&;()<>$'`{}[]^#\"", ch) != NULL)
tmpcmd[i++] = '\\';
tmpcmd[i] = ch;
}
tmpcmd[i] = NUL;
strcpy(cmd, tmpcmd);
}
/************************************************************************/
/* */
/* 10ms-Ticker updaten */
/* */
/************************************************************************/
void
update_timer(void)
{
fd_set rmask;
struct timeval timevalue;
int max_fd = 0;
int count;
register int i;
int len;
char buffer[1024];
socklen_t clilen;
struct sockaddr_un cli_addr;
static UWORD roundcounter = 1;
#ifndef NO_WATCHDOG
int lasttic10 = tic10;
#endif
#ifdef KERNELIF
int kif_fd = -1;
#endif
gettimeofday(&tv, &tz);
tic10 = (tv.tv_sec - tv_old.tv_sec) * 100 +
(tv.tv_usec - tv_old.tv_usec) / 10000;
#ifndef NO_WATCHDOG
if (tic10 - lasttic10)
if (write(watch_dog[1], "\0", 1) != 1)
{
exit(1);
}
#endif
FD_ZERO(&rmask);
/* Behandlung der Konsole bzw. Socket-Verbindung */
switch (console_type)
{
/* Ein/Ausgabe ueber normales Terminal */
case CONS_TERM_RUNNING:
FD_SET(0, &rmask); /* Eingabe Console */
max_fd = 1;
break;
/* Ein/Ausgabe ueber Socket, aber noch nicht verbunden */
case CONS_SOCKET_WAITING:
FD_SET(sockfd, &rmask);
if (sockfd > max_fd - 1)
max_fd = sockfd + 1;
break;
/* Ein/Ausgabe ueber verbundenen Socket */
case CONS_SOCKET_CONNECTED:
FD_SET(consockfd, &rmask);
if (consockfd > max_fd - 1)
max_fd = consockfd + 1;
break;
/* Alles andere ... */
default:
break;
}
/*
if (!use_socket)
{
FD_SET(0, &rmask);
max_fd = 1;
}
else
{
if (!soc_con)
{
FD_SET(sockfd, &rmask);
if (sockfd > max_fd - 1)
max_fd = sockfd + 1;
}
else
{
FD_SET(consockfd, &rmask);
if (consockfd > max_fd - 1)
max_fd = consockfd + 1;
}
}
*/
if (kiss_active)
{
for (i = 0; i < L1PNUM; ++i)
{
if (l1port[i].kisslink < 0)
continue;
#ifdef VANESSA
if (l1port[i].kisstype == KISS_VAN)
continue;
#endif
#ifdef AX_IPX
if (l1port[i].kisstype == KISS_IPX)
continue;
#endif
#ifdef AX25IP
if (l1port[i].kisstype == KISS_AXIP)
continue;
#endif
#ifdef KERNELIF
if ( (l1port[i].kisstype == KISS_KAX25)
|| (l1port[i].kisstype == KISS_KAX25KJD))
continue;
#endif
#ifdef SIXPACK
if (l1port[i].kisstype == KISS_6PACK)
continue;
#endif
if (l1port[i].port_active)
{
FD_SET(l1port[i].kisslink, &rmask);
if (l1port[i].kisslink > max_fd -1)
max_fd = l1port[i].kisslink + 1;
}
}
}
#ifdef KERNELIF
/* IP-Interface Filedescriptor ermitteln */
if ((kif_fd = ifip_active()) != -1) /* ein aktives Kernelinterface */
{
FD_SET(kif_fd, &rmask);
if (kif_fd > max_fd - 1)
max_fd = kif_fd + 1;
}
#endif
/* fuer alle Interfaces die benutzten Filedescriptoren fuer select() */
/* ermitteln und eintragen */
for (i = 0; i < L1PNUM; ++i)
{
/* nur aktive Ports bearbeiten */
if ( (l1port[i].kisslink < 0) || (l1port[i].port_active != TRUE)
#ifdef SIXPACK
|| (l1port[i].kisstype == KISS_6PACK) /* der hat hier nichts zu suchen */
#endif
)
continue;
/* Interfacetypen durchgehen */
if (
#ifdef KERNELIF
(l1port[i].kisstype == KISS_KAX25)
|| (l1port[i].kisstype == KISS_KAX25KJD)
#endif
#if defined(KERNELIF) && defined(AX_IPX)
||
#endif
#ifdef AX_IPX
(l1port[i].kisstype == KISS_IPX)
#endif
)
{
FD_SET(l1port[i].kisslink, &rmask);
if (l1port[i].kisslink > max_fd -1)
max_fd = l1port[i].kisslink + 1;
}
} /* for(...) */
timevalue.tv_usec = 10000;
timevalue.tv_sec = 0;
if (++roundcounter < maxrounds) /* schon genug Runden vergangen? */
timevalue.tv_usec = 0;
else
roundcounter = 0; /* Durchlaufzaehler ruecksetzen */
count = select(max_fd, &rmask, NULL, NULL, &timevalue);
if (count == -1)
return;
/* Konsolen/Socketverbindung bearbeiten */
switch (console_type)
{
/* Konsole ist ein Terminal */
case CONS_TERM_RUNNING:
/* Zeichen auf der Standardeingabe ? */
if (FD_ISSET(0, &rmask))
{
if ((len = read(0, buffer, 1024)) != -1)
host_to_queue(buffer, len);
else
{
/* Oh, oh, Fehler beim Lesen von der Konsole */
/* Es werden nun weitere Konsolenoperationen */
/* unterdrueckt. */
console_type = CONS_NO_CONSOLE;
}
}
break;
/* Socket wartet auf Verbindung */
case CONS_SOCKET_WAITING:
/* Wartende Verbindung annehmen */
if (FD_ISSET(sockfd, &rmask))
{
clilen = sizeof(cli_addr);
consockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (consockfd >= 0)
console_type = CONS_SOCKET_CONNECTED;
}
break;
/* Daten von verbundenem Socket */
case CONS_SOCKET_CONNECTED:
if (FD_ISSET(consockfd, &rmask))
{
len = read(consockfd, buffer, 1024);
if ((len == -1) || (len == 0))
{
close(consockfd);
consockfd = -1;
console_type = CONS_SOCKET_WAITING;
}
else
host_to_queue(buffer, len);
}
break;
default:
break;
}
/*
if (!use_socket)
{
if (FD_ISSET(0, &rmask))
if ((len = read(0, buffer, 1024)) != 0)
host_to_queue(buffer, len);
}
else
{
if (!soc_con)
{
if (FD_ISSET(sockfd, &rmask))
{
clilen = sizeof(cli_addr);
consockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (consockfd >= 0)
soc_con = TRUE;
}
}
else
if (FD_ISSET(consockfd, &rmask))
{
len = read(consockfd, buffer, 1024);
if ((len == -1) || (len == 0))
{
close(consockfd);
soc_con = FALSE;
}
else
host_to_queue(buffer, len);
}
}
*/
#ifdef KERNELIF
/* IP-Interface RX */
if ((kif_fd != -1) && (FD_ISSET(kif_fd, &rmask)))
ifip_frame_to_router();
#endif
/* alle Ports durchgehen */
for (i = 0; i < L1PNUM; ++i)
{
/* unbenutzte Ports nicht bearbeiten */
if ( (l1port[i].kisslink < 0) || (l1port[i].port_active != TRUE)
#ifdef SIXPACK
|| (l1port[i].kisstype == KISS_6PACK) /* der hat hier nichts zu suchen */
#endif
)
continue;
/* pruefen ob dieser Port einen lesbaren Filedescriptor hat */
if (FD_ISSET(l1port[i].kisslink, &rmask))
{
/* je nach Interfacetyp den richtigen RX aufrufen */
switch (l1port[i].kisstype)
{
#ifdef KERNELIF
/* Kernel-AX25-Port RX */
case KISS_KAX25 :
case KISS_KAX25KJD : ifax_rx(l1port[i].kisslink);
break;
#endif
#ifdef AX_IPX
/* AXIPX-RX */
case KISS_IPX : axipx_recv();
break;
#endif
default : continue;
}
}
}
if (kiss_active)
{
for (i = 0; i < L1PNUM; ++i)
{
if (l1port[i].kisslink == -1)
continue;
#ifdef VANESSA
if (l1port[i].kisstype == KISS_VAN)
continue;
#endif
#ifdef AX_IPX
if (l1port[i].kisstype == KISS_IPX)
continue;
#endif
#ifdef AX25IP
if (l1port[i].kisstype == KISS_AXIP)
continue;
#endif
#ifdef KERNELIF
if ( (l1port[i].kisstype == KISS_KAX25)
|| (l1port[i].kisstype == KISS_KAX25KJD))
continue;
#endif
#ifdef SIXPACK
if (l1port[i].kisstype == KISS_6PACK)
continue;
#endif
if (l1port[i].port_active)
if (FD_ISSET(l1port[i].kisslink, &rmask))
if ((len = read(l1port[i].kisslink, buffer, 1024)) != 0)
framedata_to_queue(i, buffer, len);
}
}
}
/************************************************************************/
/* */
/* Rechner neu starten */
/* */
/************************************************************************/
void
reboot_system(void)
{
exit_all();
sync();
/* CRASH(); */
/*system("/sbin/shutdown -r now");*/
/* Hierher wird man wohl nur kommen, wenn tnn nicht als root gestartet */
/* wurde. Nun gibt es 2 Moeglichkeiten: entweder wir starten von vorne, */
/* weil die Resourcen ja breits freigegeben wurden, oder aber wir */
/* beenden das Programm, weil das ja mehr oder weniger gefordert wurde. */
/* Sinnvoller ist es wohl, wenn wir das Programm verlassen, in der */
/* Hoffnung, dass es danach regulaer von einem Batch erneut gestartet */
/* wird. Das ist jedenfalls besser, als nix zu tun. */
exit(1);
}
/************************************************************************/
/* */
/* Shell-Befehl - Linux-spezifischer Teil: */
/* Das gewuenschte Programm wird als Hintergrundprozess gestartet. */
/* */
/************************************************************************/
BOOLEAN
tnnshell(char *cmdline)
{
char sysline[MAXPATH + 1];
char cFirstArg[MAXPATH + 1];
char *pFirstArg = NULL;
MBHEAD* mbp;
struct termios term;
/* In TNB-Files darf die interaktive Shell nicht verwendet werden */
if ((tnnb_aktiv == TRUE) && (strlen(cmdline) == 0))
{
putmsg("Interactive shell is not allowed in tnb-files !\r");
return (FALSE);
}
/* Haben wir eine Shell ? */
if (cShell[0] == 0)
{
putmsg("No shell set, use 'setshell' to set one !\r");
return (FALSE);
}
/* Initialisieren */
memset(sysline, 0, MAXPATH);
memset(cFirstArg, 0, MAXPATH);
memset(&term, 0, sizeof(struct termios));
term.c_iflag = ICRNL | IXOFF;
term.c_oflag = OPOST | ONOCR; /* ONLCR */
term.c_cflag = CS8 | CREAD | CLOCAL;
term.c_lflag = ISIG | ICANON;
/*
term.c_iflag = IGNCR | IXOFF;
term.c_oflag = OPOST | ONLCR | ONLRET | OCRNL;
term.c_cflag = CS8 | CREAD | CLOCAL;
term.c_lflag = ISIG | ICANON;
*/
term.c_cc[VINTR] = 3; /* ^C */
term.c_cc[VSUSP] = 26; /* ^Z */
term.c_cc[VQUIT] = 28;
term.c_cc[VERASE] = 8; /* ^H */
term.c_cc[VKILL] = 24; /* ^X */
term.c_cc[VEOF] = 4; /* ^D */
strncpy(sysline, cmdline, MAXPATH);
sysline[MAXPATH] = NUL; /* Sicher ist sicher ... */
/* Das erste Argument fuer exec bestimmen */
if ((pFirstArg = strrchr(cShell, '/')) == NULL)
{
pFirstArg = cShell;
}
else
{
++pFirstArg; /* wir wollen das erste Zeichen hinter dem "/" */
}
if (strlen(cmdline) == 0)
cFirstArg[0] = '-';
strcat(cFirstArg, pFirstArg);
/* neue Shell abforken */
if ((userpo->child_pid = forkpty(&userpo->child_fd, NULL, &term, NULL)) == -1)
{
/* forkpty() nicht moeglich */
userpo->child_pid = 0;
putmsg("Can't fork child process !\r");
return(FALSE);
}
else
{
if (userpo->child_pid == 0) /* Kind-Prozess (die Shell) */
{
if (strlen(cmdline) == 0)
{
execl(cShell, cFirstArg, NULL);
}
else
{
execl(cShell, cFirstArg, "-c", cmdline, NULL);
}
/* Wenn wir hier ankommen, dann ist wirklich was nicht ok ... */
exit(-1);
}
else /* Hauptprogramm */
{
/* Timeout und Interaktivitaet setzen je nach Shell-Typ */
/* Achtung, Timeout in 100ms-Schritten ! */
if (strlen(cmdline) == 0)
{
mbp = getmbp();
putprintf(mbp, "Invoking shell, type 'exit' to end your session.\r");
seteom(mbp);
userpo->child_timeout = 30000; /* 5 Min. Timeout */
userpo->child_iactive = TRUE; /* interaktive Shell */
}
else
{
userpo->child_timeout = 6000; /* 1 Min. Timeout */
userpo->child_iactive = FALSE; /* keine interaktive Shell */
}
userpo->status = US_EXTP; /* externes Programm laeuft */
return(TRUE);
}
}
return(TRUE);
}
/************************************************************************/
/* */
/* Pruefen, ob Kind-Prozesse beendet sind (vom Sysop gestartete externe */
/* Programme). Bei Zeitueberschreitung wird das Programm mit einer */
/* entsprechenden Fehlermeldung abgebrochen. */
/* */
/************************************************************************/
void
shellsrv(void)
{
pid_t c_pid;
MBHEAD* mbp;
for (userpo = (USRBLK *) usccpl.head;
userpo != (USRBLK *) &usccpl;
userpo = (USRBLK *) userpo->unext)
{
if (userpo->status != US_EXTP) /* externer Prozess aktiv? */
continue; /* nein .. */
if (userpo->child_pid == 0) /* externer Prozess aktiv? */
continue; /* nein .. */
c_pid = waitpid(userpo->child_pid, /* dieser Prozess beendet? */
NULL, WNOHANG);
/* Prozess noch nicht beendet */
if (c_pid == 0) /* noch nicht fertig? */
{
/* Timeout-Warnung fuer interaktive Shell */
if ((userpo->child_iactive == TRUE) && (userpo->child_timeout <= 6000))
{
MBHEAD* mbp;
switch (userpo->child_timeout)
{
case 1000 :
case 2000 :
case 3000 :
case 6000 : mbp = putals("WARNING: Your shell times out in ");
putprintf(mbp, "%u seconds !!!\r", (userpo->child_timeout / 100));
seteom(mbp);
break;
default : break;
}
}
/* Shell-Timeout */
if (--userpo->child_timeout == 0) /* zu lange inaktiv? */
{
kill(-(userpo->child_pid), SIGKILL); /* Abbruch (Watchdog) */
c_pid = waitpid(userpo->child_pid, /* Prozess-Status holen */
NULL, 0); /* sonst gips 'n Zombie */
userpo->child_pid = 0;
putmsg("SHELL SESSION ABORTED (TIMEOUT)\r");
break;
}
else
{
shell_to_user();
continue;
}
}
/* Shell ist geschlossen */
if (userpo->child_fd != -1)
{
if (c_pid > 0)
shell_to_user(); /* Letzte Daten abholen */
/* Shell nun schon geschlossen ? */
if (userpo->child_fd != -1)
{
close(userpo->child_fd);
userpo->child_fd = -1;
}
}
userpo->child_pid = 0; /* Prozess ist fertig */
userpo->child_timeout = 0;
userpo->child_iactive = FALSE;
userpo->status = US_CCP;
/* und ab die Post ... */
mbp = getmbp();
prompt(mbp);
seteom(mbp);
}
}
/************************************************************************/
/* */
/* Daten von der Shell abholen und zum User bringen */
/* */
/************************************************************************/
void
shell_to_user(void)
{
fd_set rset;
struct timeval tvalue;
int i;
UBYTE usrtype;
/* nur gueltige Descriptoren */
if (userpo->child_fd == -1)
return;
/* Pruefen, ob die Verbindung noch Daten aufnehmen kann, ist eine L2/L4- */
/* Verbindung zu voll, dann keine neuen Pakete generieren. An die Hostkonsole wird */
/* ungebremst ausgegeben. */
usrtype = g_utyp(userpo->uid);
if ( ((usrtype == L2_USER) || (usrtype == L4_USER))
&& (((LNKBLK *)g_ulink(userpo->uid))->tosend > 100)
)
return;
FD_ZERO(&rset);
FD_SET(userpo->child_fd, &rset);
tvalue.tv_usec = 0;
tvalue.tv_sec = 0;
/* Daten holen */
if ((i = select((userpo->child_fd + 1), &rset, NULL, NULL, &tvalue)) < 0)
{
putmsg("ERROR while select()-ing descriptor !!!\r");
close(userpo->child_fd);
userpo->child_fd = -1;
userpo->child_timeout = 1; /* Aufraeumen macht shellsrv() */
}
else
{
/* Daten Shell -> User */
if ((i > 0) && (FD_ISSET(userpo->child_fd, &rset)))
{
unsigned char data[8192];
unsigned int uZaehler;
ssize_t rd;
MBHEAD* mbp;
if ((rd = read(userpo->child_fd, data, 8192)) == -1)
{
/* Ist der abgearbeitete Befehl ohne jegliche Rueckgabe, dann wird der Fehler 5 */
/* erzeugt. Im nicht-interaktiven Modus unterdruecken wir die Fehlermeldung, im */
/* interaktiven Modus wird er normal angezeigt. */
if ((errno == EIO) && (userpo->child_iactive == TRUE))
{
mbp = putals("");
putprintf(mbp, "ERROR %u while read()-ing descriptor !!! Shell terminated !\r", errno);
seteom(mbp);
}
close(userpo->child_fd);
userpo->child_fd = -1;
userpo->child_timeout = 1; /* Aufraeumen macht shellsrv() */
}
if (rd > 0)
{
mbp = getmbp();
for (uZaehler = 0; uZaehler < rd; ++uZaehler)
{
if (data[uZaehler] == 0x0D)
continue;
if (data[uZaehler] == 0x0A)
{
putchr(0x0D, mbp);
continue;
}
putchr(data[uZaehler], mbp);
}
seteom(mbp);
/* Timeout ruecksetzen */
if (userpo->child_iactive == TRUE)
userpo->child_timeout = 30000;
}
}
}
}
/************************************************************************/
/* */
/* User-Eingabe in mhdp verarbeiten */
/* */
/************************************************************************/
BOOLEAN
l7tosh(MBHEAD *mhdp)
{
if (userpo->status == US_EXTP) /* externer Prozess aktiv? */
{
if (userpo->child_iactive == FALSE)
{
dealmb(mhdp); /* Abbruchzeile ignorieren */
userpo->mbhd = NULL;
kill(-(userpo->child_pid), SIGKILL); /* Abbruch (Watchdog) */
waitpid(userpo->child_pid, /* Prozess-Status holen */
NULL, 0); /* sonst gips 'n Zombie */
putmsg("SHELL ABORTED by User\r");
userpo->child_pid = 0; /* Prozess ist fertig */
userpo->child_fd = -1;
userpo->child_iactive = FALSE;
userpo->child_timeout = 0;
userpo->status = US_CCP;
return(TRUE);
}
else
{
struct timeval tvalue;
int i;
fd_set wset;
FD_ZERO(&wset);
FD_SET(userpo->child_fd, &wset);
tvalue.tv_usec = 0;
tvalue.tv_sec = 0;
if ((i = select((userpo->child_fd + 1), NULL, &wset, NULL, &tvalue)) < 0)
{
putmsg("ERROR while select()-ing descriptor !!!)\r");
userpo->child_fd = -1;
userpo->child_timeout = 1; /* Aufraeumen macht shellsrv() */
}
else
{
/* Daten User -> Shell */
if ((i > 0) && (FD_ISSET(userpo->child_fd, &wset) && (mhdp->mbgc < mhdp->mbpc)))
{
unsigned char data[1024];
unsigned int uBufSize = 0;
while (mhdp->mbgc < mhdp->mbpc)
data[uBufSize++] = getchr(mhdp);
/* Daten zum Shell-Child schicken */
if (write(userpo->child_fd, data, uBufSize) < 0)
{
/* Oh oh, das hat nicht geklappt */
userpo->child_fd = -1;
userpo->child_timeout = 1; /* Aufraeumen macht shellsrv() */
}
else
{
/* Timeout ruecksetzen */
if (userpo->child_iactive == TRUE)
userpo->child_timeout = 30000;
}
dealmb(mhdp);
userpo->mbhd = NULL;
}
}
}
return (TRUE);
}
return (FALSE);
}
/************************************************************************/
/* */
/* Filenamen pruefen */
/* */
/************************************************************************/
BOOLEAN
good_file_name(const char *file)
{
if ( (strncmp("/bin/", file, 5) == 0)
|| (strncmp("/dev/", file, 5) == 0)
|| (strncmp("/proc/", file, 6) == 0)
|| (strncmp("/sbin/", file, 6) == 0)
|| (strncmp("/usr/bin/", file, 9) == 0)
|| (strncmp("/usr/sbin/", file, 10) == 0)
|| (strncmp("/usr/local/bin/", file, 16) == 0)
|| (strncmp("/usr/local/sbin/", file, 17) == 0))
return(FALSE);
return(TRUE);
}
/************************************************************************/
/* */
/* */
/************************************************************************/
void
tnnexec(char *file_name)
{
if (access(file_name, X_OK) == 0) /* nur ausfuehrbares Programm */
{
exit_all(); /* Resourcen freigeben */
execl(file_name, NULL, NULL); /* Programm starten */
/* Hierher darf man eigentlich nicht kommen! Daher verlassen wir das */
/* Programm in der Hoffnung, dass es danach regulaer von einem Batch */
/* erneut gestartet wird. */
exit(1);
}
}
/************************************************************************/
/* */
/* */
/************************************************************************/
void
init_console(void)
{
#ifdef L1TCPIP
/* Interface Initialisieren. */
InitIFC();
#endif /* L1TCPIP */
if (console_type == CONS_TERM_DO_SETUP)
{
setvbuf(stdout, NULL, _IONBF, 0); /* Ausgabe ungepuffert */
tcgetattr(fileno(stdin), &oconin_termios);
tcgetattr(fileno(stdout), &oconout_termios);
nconin_termios = oconin_termios;
nconin_termios.c_cc[VTIME] = 0;
nconin_termios.c_cc[VMIN] = 1;
nconin_termios.c_cc[VSTART] = -1;
nconin_termios.c_cc[VSTOP] = -1;
nconin_termios.c_iflag = IGNBRK;
nconin_termios.c_oflag = OPOST|ONLCR;
nconin_termios.c_lflag = 0;
nconin_termios.c_cflag = (CS8|CREAD|CLOCAL);
tcsetattr(fileno(stdin), TCSADRAIN, &nconin_termios);
nconout_termios = oconout_termios;
nconout_termios.c_cc[VTIME] = 0;
nconout_termios.c_cc[VMIN] = 1;
nconout_termios.c_cc[VSTART] = -1;
nconout_termios.c_cc[VSTOP] = -1;
nconout_termios.c_iflag = IGNBRK|ICRNL;
nconout_termios.c_oflag = OPOST|ONLCR|ONLRET|OCRNL;
nconout_termios.c_lflag = 0;
nconout_termios.c_cflag = (CS8|CREAD|CLOCAL);
tcsetattr(fileno(stdout), TCSAFLUSH, &nconout_termios);
consfile = stdout;
console_type = CONS_TERM_RUNNING;
}
else
consfile = NULL;
hostq_root = NULL;
hostq_last = NULL;
hostq_len = 0;
}
/************************************************************************/
/* */
/* */
/************************************************************************/
void
exit_console(void)
{
struct hostqueue *hostq_ptr;
while (hostq_root != NULL)
{
hostq_ptr = hostq_root;
hostq_root = hostq_ptr->next;
free(hostq_ptr);
}
hostq_last = NULL;
hostq_len = 0;
exit_proc();
if (console_type != CONS_TERM_RUNNING)
return;
tcsetattr(1, TCSADRAIN, &oconout_termios);
tcsetattr(0, TCSADRAIN, &oconin_termios);
console_type = CONS_NO_CONSOLE;
}
/************************************************************************/
/* */
/* */
/************************************************************************/
static void
alloc_hostqbuf(void)
{
struct hostqueue *hostq_ptr;
hostq_ptr = (struct hostqueue *) malloc(sizeof(struct hostqueue));
if (hostq_ptr == NULL)
{
exit_all();
exit(1);
}
hostq_ptr->first = 0;
hostq_ptr->last = 0;
hostq_ptr->next = NULL;
if (hostq_root == NULL)
{
hostq_root = hostq_ptr;
}
else
{
hostq_last->next = hostq_ptr;
hostq_last = hostq_ptr;
}
hostq_last = hostq_ptr;
}
/* put one character in host-queue */
static void
puthostq(char ch)
{
struct hostqueue *hostq_ptr;
if (hostq_root == NULL)
alloc_hostqbuf();
hostq_ptr = hostq_last;
if (hostq_ptr->last >= HOSTQ_BUFLEN)
{
alloc_hostqbuf();
hostq_ptr = hostq_last;
}
hostq_ptr->buffer[hostq_ptr->last] = ch;
hostq_ptr->last++;
hostq_len++;
}
/* get one character out of host-queue */
static char
gethostq(void)
{
struct hostqueue *hostq_ptr;
char ch;
if ((hostq_len == 0) || (hostq_root == NULL)) /* sanity check */
return(0);
hostq_ptr = hostq_root;
ch = hostq_ptr->buffer[hostq_ptr->first];
hostq_ptr->first++;
hostq_len--;
if ( (hostq_ptr->first == hostq_ptr->last)
|| (hostq_ptr->first == HOSTQ_BUFLEN))
{
if (hostq_ptr->next == NULL)
{
hostq_root = NULL;
hostq_last = NULL;
}
else
hostq_root = hostq_ptr->next;
free(hostq_ptr);
}
return(ch);
}
/************************************************************************/
/* */
/* */
/************************************************************************/
void
hputc(char c)
{
int res;
if (tnb_ch)
{
if (consfile != NULL)
fputc(c, consfile);
return;
}
switch (console_type)
{
case CONS_SOCKET_CONNECTED:
res = write(consockfd, &c, 1);
if (res == -1)
{
close(consockfd);
consockfd = -1;
console_type = CONS_SOCKET_WAITING;
}
break;
case CONS_TERM_RUNNING:
if ((c == '\n') || (c == '\r'))
putchar(LF);
else
putchar(c);
break;
default:
break;
}
}
/* return if character available */
BOOLEAN
ishget(void)
{
return(hostq_len != 0);
}
/* return if output to console is stopped */
BOOLEAN
ishput(void)
{
return(FALSE);
}
/* get one character from console */
char
hgetc(void)
{
char ch = gethostq();
if (console_type == CONS_TERM_RUNNING)
if (ch == LF)
ch = CR;
return (ch);
}
/* put received characters in hostbuffer */
static void
host_to_queue(char *buffer, int len)
{
char *bufptr;
register int i = 0;
bufptr = buffer;
while (i < len)
{
puthostq(*bufptr);
++i;
bufptr++;
}
}
/************************************************************************/
/* */
/* */
/************************************************************************/
BOOLEAN
init_hardware(int argc, char *argv[])
{
char *pEnvShell = NULL;
int scanned = 1;
umask(0);
time(&sys_time);
/* Kommandozeilenparameter auswerten */
/* Den Verbose-Modus schon hier feststellen */
while (scanned < argc)
{
/* Parameter "-v" (ausfuehrlichere Meldungen) */
if (strcmp(argv[scanned], "-v") == 0)
{
bVerbose = TRUE;
}
++scanned;
}
VMSG("--- Verbose logging started !\n");
/* Shell loeschen */
memset(cShell, 0, sizeof(cShell));
VMSG("--- Determining shell-program ...\n");
/* Shell aus dem Environment holen */
if ((pEnvShell = getenv("SHELL")) == NULL)
fprintf(stderr, "Warning: SHELL not set, can't use external programs !!!\n");
else
{
/* Shell kopieren */
strncpy(cShell, pEnvShell, sizeof(cShell));
/* und sicherheitshalber abschliessen */
cShell[sizeof(cShell)] = 0;
VMSG("--- Shell is '%s'\n", cShell);
}
VMSG("--- Reading ini-file ...\n");
if (read_init_file(argc, argv))
return(TRUE);
VMSG("--- Entering pid-file checks ...\n");
if (!init_proc())
exit(1);
/*
if (tnn_errfile[0] != NUL)
{
}
*/
VMSG("--- Allocating mem for %ld buffers ...\n", tnn_buffers);
RAMBOT = (char *)malloc(tnn_buffers * sizeof(MAX_BUFFER));
RAMTOP = RAMBOT + tnn_buffers * sizeof(MAX_BUFFER);
if (RAMBOT == NULL)
{
VMSG("!!! Allocation mem for %ld buffers failed, trying to allocate for %u buffers now ...\n", tnn_buffers, TNN_BUFFERS);
RAMBOT = (char *)malloc(TNN_BUFFERS * sizeof(MAX_BUFFER));
RAMTOP = RAMBOT + TNN_BUFFERS * sizeof(MAX_BUFFER);
if (RAMBOT == NULL)
{
fprintf(stderr, "malloc for buffers failed\n");
exit(1);
}
else
tnn_buffers = TNN_BUFFERS;
}
if (kiss_active)
{
VMSG("--- Initializing KISS-interfaces ...\n");
if (init_kisslink())
{
free(RAMBOT);
exit(1);
}
}
if (console_type == CONS_SOCKET_DO_SETUP)
{
VMSG("--- Initializing UNIX-socket interface ...\n");
if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
{
fprintf(stderr, "Can't open socket !\n");
fprintf(stderr, "Error %u : %s\n", errno, strerror(errno));
fprintf(stderr, "Exiting.\n");
close(sockfd);
free(RAMBOT);
if (kiss_active)
exit_kisslink();
exit(1);
}
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strcpy(serv_addr.sun_path, tnn_socket);
servlen = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);
unlink(serv_addr.sun_path);
if (bind(sockfd, (struct sockaddr *) &serv_addr, servlen) < 0)
{
fprintf(stderr, "Can't bind socket !\n");
fprintf(stderr, "Error %u : %s\n", errno, strerror(errno));
fprintf(stderr, "Exiting.\n");
close(sockfd);
free(RAMBOT);
if (kiss_active)
exit_kisslink();
exit(1);
}
if (listen(sockfd, 5) < 0)
{
fprintf(stderr, "Can't listen to socket !\n");
fprintf(stderr, "Error %u : %s\n", errno, strerror(errno));
fprintf(stderr, "Exiting.\n");
free(RAMBOT);
if (kiss_active)
exit_kisslink();
exit(1);
}
console_type = CONS_SOCKET_WAITING;
if (getppid() != 1)
{
if (fork() != 0)
{
/* Parent */
exit_proc(); /* eigenes PID-File loeschen */
exit(0);
}
else
{
sleep(1); /* warten, dass Parent sein PID-File loescht */
/* Child */
if (!init_proc())
exit(1);
}
}
}
#ifndef NO_WATCHDOG
VMSG("--- Initializing watchdog ...\n");
watchdog_init();
#endif
VMSG("--- Installing signal-handlers ...\n");
#ifdef DEBUG_MODUS
signal(SIGSEGV, sigsegv);
signal(SIGTERM, sigterm);
#endif
signal(SIGHUP, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
gettimeofday(&tv, &tz);
tv_old = tv;
return(FALSE);
}
#ifndef NO_WATCHDOG
/************************************************************************/
/* */
/* Watchdog - ein vom TNN-Hauptprogramm unabhaengiger Prozess muss */
/* regelmaessig vom Hauptprogramm Daten erhalten. Bleiben diese Daten */
/* laenger als 60s aus, wird TNN beendet. */
/* */
/************************************************************************/
static void
watchdog_init(void)
{
pid_t pid;
time_t wdzeit;
time_t zeit;
int num;
int status;
char buf[1000];
VMSG("--- Initializing watchdog ...\n");
if (pipe(watch_dog) < 0)
{
fprintf(stderr, "Error %u while creating watchdog's pipe: %s\n", errno, strerror(errno));
fprintf(stderr, "Exiting.\n");
exit(1);
}
/* Descriptoren auf non-blocking einstellen */
num = fcntl(watch_dog[0], F_GETFL, 0);
num |= O_NONBLOCK;
fcntl(watch_dog[0], F_SETFL, num);
num = fcntl(watch_dog[1], F_GETFL, 0);
num |= O_NONBLOCK;
fcntl(watch_dog[1], F_SETFL, num);
/* Forken */
if ((pid = fork()) < 0)
{
fprintf(stderr, "Error %u while fork()-ing in watchdog: %s\n", errno, strerror(errno));
fprintf(stderr, "Exiting.\n");
exit(1);
}
else
{
/* Na wer sind wir denn ... */
if (pid == 0)
{
/* wir sind Child (das Hauptprogramm) */
wdpid[0] = getpid();
close(watch_dog[0]);
sleep(1);
return;
}
else
{
/* und wir sind Parent (der Watchdog) */
wdpid[0] = pid; /* Child */
wdpid[1] = getpid(); /* Parent */
close(watch_dog[1]);
VMSG(" +-> main loop has pid %u\n", wdpid[0]);
VMSG(" +-> watchdog has pid %u\n", wdpid[1]);
signal(SIGHUP, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
signal(SIGSEGV, sigsegv);
time(&zeit);
time(&wdzeit);
LOOP
{
/* Hat sich das Child beendet ? */
if (waitpid(wdpid[0], &status, WNOHANG))
{
/* Child hat sich beendet */
/* Es gibt nichts mehr zu killen, pid wegwerfen */
wdpid[0] = 0;
VMSG("!!! watchdog: child has exited !\n");
if (bVerbose == TRUE)
{
if (WIFEXITED(status))
VMSG(" +-> child exited with code %u\n", WEXITSTATUS(status));
if (WIFSIGNALED(status))
{
VMSG(" +-> child exited because of signal %u\n", WTERMSIG(status));
if (WCOREDUMP(status))
VMSG(" +-> child wrote a core dump !\n");
}
}
break;
}
/* Child sollte laufen, dann schauen, ob es was geschickt hat */
num = read(watch_dog[0], &buf, sizeof(buf));
if ( (num == 0) /* schreibendes Ende der Pipe geschlossen */
|| ((num < 0) && (errno != EWOULDBLOCK))) /* sonstige Fehler */
break;
if (num > 0)
{
time(&wdzeit);
continue;
}
time(&zeit);
if ((zeit - wdzeit) > 60)
break;
sleep(1);
}
time(&wdzeit);
close(watch_dog[0]);
if (wdpid[0] != 0)
{
VMSG("!!! watchdog: child has exited abnormally !\n");
kill(-wdpid[0], SIGSEGV);
/* Falls aus irgendeinem Grund SIGSEGV nicht zum Programmende fuehrt, */
/* kommt ein SIGKILL hinterher. Damit wird sichergestellt, dass der */
/* Childprozess beendet wird. */
sleep(1);
kill(-wdpid[0], SIGKILL);
}
/* Hauptprogramm beenden */
exit(0);
}
}
}
#endif
/************************************************************************/
/* */
/* */
/************************************************************************/
void
exit_hardware(void)
{
free(RAMBOT);
if (kiss_active)
exit_kisslink();
}
#ifdef DEBUG_MODUS
/************************************************************************/
/* Signalbehandlungsfunktion fuer das Signal SIGTERM */
/************************************************************************/
static void
sigterm(int signo)
{
signal(SIGTERM, SIG_IGN);
printf("sigterm\n");
quit_program(-1);
}
void
sigsegv(int signo)
{
signal(SIGSEGV, SIG_IGN);
#ifdef DEBUG_MODUS
printf("\nTheNetNode verursachte einen unbekannten Systemfehler!!!\n");
printf("Letzte Funktion war: %s\n", lastfunc);
#endif
quit_program(-1);
}
#endif
/************************************************************************/
/* */
/* */
/************************************************************************/
int
exit_all(void)
{
exit_mh(); /* MH-Liste sichern */
save_stat();
save_configuration();
personalmanager(SAVE, NULL, NULL); /* Pers. Daten fuer Convers */
/* noch laufende Shells killen */
for (userpo = (USRBLK *) usccpl.head;
userpo != (USRBLK *) &usccpl;
userpo = (USRBLK *) userpo->unext)
{
if (userpo->status != US_EXTP) /* externer Prozess aktiv? */
continue;
kill(-(userpo->child_pid), SIGKILL); /* Abbruch */
waitpid(userpo->child_pid, NULL, 0); /* Prozess-Status holen */
}
/* Buffer freigeben */
free(RAMBOT);
/* Ab hier stehen keine Buffer mehr zur Verfuegung, alles was mit Buffern */
/* zu tun hat, gibt nun nen Segfault ! */
if (console_type == CONS_SOCKET_CONNECTED)
{
close(consockfd);
consockfd = -1;
console_type = CONS_SOCKET_WAITING;
}
if (console_type == CONS_SOCKET_WAITING)
{
close(sockfd);
sockfd = -1;
}
else
exit_console();
if (kiss_active)
exit_kisslink();
exit_proc();
printf("\r\n");
return(0);
}
/************************************************************************/
/* */
/* Systembelastung durch TNN berechnen */
/* diese Routine muss alle 10 Sekunden aufgerufen werden */
/* */
/************************************************************************/
void
calculate_load(void)
{
static long clktck = 0;
static int oldtime;
static float oldjiffies[2];
static pid_t tnnpid;
FILE *fp;
int utime;
int stime;
register int gtime;
char proc_name[80];
/* im ersten Durchlauf alles aufraeumen */
if (clktck == 0)
{
clktck = sysconf(_SC_CLK_TCK);
tnnpid = getpid();
oldjiffies[0] = 0;
oldjiffies[1] = 0;
oldtime = 0;
load[0] = 0;
load[1] = 0;
load[2] = 0;
}
sprintf(proc_name , "/proc/%d/stat", tnnpid);
/* stat-Datei von TNN oeffnen */
if ((fp = fopen(proc_name, "r")))
{
fscanf(fp, "%*d %*s %*c %*d %*d %*d %*d "
"%*d %*u %*u %*u %*u %*u %d %d",
&utime, &stime);
fclose(fp);
/* User- & System-Time addieren und in Sekunden umrechnen */
gtime = (utime + stime) - oldtime;
/* die letzten Zeiten fuer neue Berechnung merken */
oldtime = utime + stime;
/* Prozente ausrechnen */
tnn_load = ((gtime * 100) / (10 * clktck));
}
else
tnn_load = -1; /* Fehler ! */
/* Systemload aus /proc/loadavg lesen */
if ((fp = fopen("/proc/loadavg", "r")))
{
fscanf(fp, "%f %f %f %*s %*s", &load[0], &load[1], &load[2]);
fclose(fp);
}
else
load[0] = -1;
/* gesamte Systembelastung im letzten Messintervall errechnen */
if ((fp = fopen("/proc/uptime", "r")))
{
float actjiffies[2];
fscanf(fp, "%f %f", &actjiffies[0], &actjiffies[1]);
fclose(fp);
if (oldjiffies[0] != 0 && oldjiffies[1] != 0)
sys_load = 100 * (1 - ((actjiffies[1] - oldjiffies[1]) / (actjiffies[0] - oldjiffies[0])));
oldjiffies[0] = actjiffies[0];
oldjiffies[1] = actjiffies[1];
}
else
sys_load = -1;
}
/************************************************************************/
/* */
/* Load-Werte ausgeben */
/* */
/************************************************************************/
void
print_load(MBHEAD *mbp)
{
ULONG user = 0;
ULONG sys = 0;
struct rusage sysusage;
if (tnn_load >= 0)
putprintf(mbp, "\r TNN-Load: %i%%", tnn_load);
else
putprintf(mbp, "\r TNN-Load: can't read from /proc !");
if (sys_load >= 0)
putprintf(mbp, "\r Sysload: %.2f%%", sys_load);
else
putprintf(mbp, "\r Sysload: can't read from /proc !");
/* IO-Last ausgeben (siehe auch man proc) */
if (load[0] >= 0)
putprintf(mbp, "\r Loadavg: %.2f %.2f %.2f",
load[0], load[1], load[2]);
else /* mhh...Kernel ohne /proc-System compiliert ? */
putprintf(mbp, "\r Loadavg: can't read from /proc/loadavg !");
/* verbrauchte Systemzeit ausgeben, wenn moeglich */
if (getrusage(RUSAGE_SELF, &sysusage) == 0)
{
user = (ULONG)sysusage.ru_utime.tv_sec * 1000L
+ sysusage.ru_utime.tv_usec / 1000L;
sys = (ULONG)sysusage.ru_stime.tv_sec * 1000L
+ sysusage.ru_stime.tv_usec / 1000L;
if (getrusage(RUSAGE_CHILDREN, &sysusage) == 0)
{
user += (ULONG) sysusage.ru_utime.tv_sec * 1000L
+ sysusage.ru_utime.tv_usec / 1000L;
sys += (ULONG) sysusage.ru_stime.tv_sec * 1000L
+ sysusage.ru_stime.tv_usec / 1000L;
}
if (user > 1000000L || sys > 1000000L)
putprintf(mbp, "\r CPU time used: user %lus, system %lus",
user / 1000L, sys / 1000L);
else
putprintf(mbp, "\r CPU time used: user %lums, system %lums",
user, sys);
}
}
/* ermoeglicht das Setzen einer anderen Shell */
void ccpsetshell(void)
{
MBHEAD *mbp;
char cBuf[512];
const size_t tBufSize = sizeof(cBuf);
register size_t tCmdlen = 0;
struct stat tFileStat;
/* Sysop will aendern und noch was in der Zeile da ? */
if (issyso())
{
/* Sind noch Usereingaben da ? */
if (skipsp(&clicnt, &clipoi))
{
/* Frischer Buffer */
memset(cBuf, 0, tBufSize);
/* Zeile lesen */
for (; tCmdlen < tBufSize; ++tCmdlen)
{
if ((!clicnt) || (*clipoi == ' '))
break;
clicnt--;
cBuf[tCmdlen] = (*clipoi++);
}
}
else /* Syntax anzeigen */
{
mbp = putals("usage:\r'setshell /path/to/shell' to set a new shell\r");
putprintf(mbp, "'setshell ?' to get the current setting\r");
prompt(mbp);
seteom(mbp);
return;
}
}
else /* kein Sysop */
{
invmsg();
return;
}
/* Aktuelle Einstellung anzeigen */
if ((tCmdlen == 1) && (cBuf[0] == '?'))
{
mbp = putals("");
putprintf(mbp, "current shell is : %s\r", cShell);
prompt(mbp);
seteom(mbp);
return;
}
/* Neue Shell grob ueberpruefen */
if (access(cBuf, F_OK) == -1) /* gibt es das File ? */
{
mbp = putals("");
putprintf(mbp, "error accessing file '%s': %s\r", cBuf, strerror(errno));
prompt(mbp);
seteom(mbp);
return;
}
/* Nur regulaere Dateien und Links erlaubt */
if (stat(cBuf, &tFileStat) == -1)
{
mbp = putals("");
putprintf(mbp, "can't get stats for '%s': %s\r", cBuf, strerror(errno));
prompt(mbp);
seteom(mbp);
return;
}
else
{
/* Pruefung */
if ( ((tFileStat.st_mode & S_IFDIR) == S_IFDIR) /* directory */
|| ((tFileStat.st_mode & S_IFSOCK) == S_IFSOCK) /* socket */
|| ((tFileStat.st_mode & S_IFBLK) == S_IFBLK) /* block device */
|| ((tFileStat.st_mode & S_IFCHR) == S_IFCHR) /* character device */
|| ((tFileStat.st_mode & S_IFIFO) == S_IFIFO) /* fifo */
)
{
mbp = putals("");
putprintf(mbp, "'%s' is not a file, can't use !\r", cBuf);
prompt(mbp);
seteom(mbp);
return;
}
}
/* Ausfuehrbar muss es auch noch sein */
if (access(cBuf, X_OK) == -1) /* nur ausfuehrbares Programm */
{
mbp = putals("");
putprintf(mbp, "file '%s' is NOT marked executable, not accepted !!!\r", cBuf);
prompt(mbp);
seteom(mbp);
return;
}
/* Uebernehmen */
strncpy(cShell, cBuf, min(sizeof(cShell), tBufSize));
mbp = putals("");
putprintf(mbp, "shell changed to : %s\r", cShell);
prompt(mbp);
seteom(mbp);
return;
}
#ifdef L1TCPIP
/* Pruefe auf TCPIP-Port's */
int CheckPortTCP(int port)
{
switch(kissmode(port))
{
#ifdef L1TELNET
case KISS_TELNET :
return(TRUE);
#endif /* L1TELNET */
#ifdef L1HTTPD
case KISS_HTTPD :
return(TRUE);
#endif /* L1HTTPD */
#ifdef L1IPCONV
case KISS_IPCONV :
return(TRUE);
#endif /* L1IPCONV */
#ifdef L1IRC
case KISS_IRC :
return(TRUE);
#endif /* L1IRC */
default :
return(FALSE);
}
return(FALSE);
}
#endif /* L1TCPIP */
/*
Dummy-Funktionen
*/
void
init_timer(void) {}
void
DIinc(void) {}
void
decEI(void) {}
void
exit_timer(void) {}
void
init_rs232(void) {}
/* End of os/linux/linux.c */