This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
deb-mbse/mbfido/mbindex.c
2002-09-28 22:22:50 +00:00

993 lines
19 KiB
C

/*****************************************************************************
*
* $Id$
* Purpose ...............: Nodelist Compiler
*
*****************************************************************************
* Copyright (C) 1997-2002
*
* Michiel Broek FIDO: 2:280/2802
* Beekmansbos 10
* 1971 BV IJmuiden
* the Netherlands
*
* This file is part of MBSE BBS.
*
* This BBS is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* MBSE BBS is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MBSE BBS; see the file COPYING. If not, write to the Free
* Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*****************************************************************************/
#include "../config.h"
#include "../lib/libs.h"
#include "../lib/memwatch.h"
#include "../lib/structs.h"
#include "../lib/users.h"
#include "../lib/records.h"
#include "../lib/common.h"
#include "../lib/clcomm.h"
#include "../lib/dbcfg.h"
#include "../lib/dbftn.h"
#define TMPNAME "TMP."
#define LCKNAME "LOCKFILE"
typedef struct _nl_list {
struct _nl_list *next;
struct _nlidx idx;
} nl_list;
#include "mbindex.h"
FILE *ifp, *ufp, *ffp;
long total = 0, entries = 0;
int filenr = 0;
unsigned short regio;
nl_list *nll = NULL;
static char lockfile[81];
extern int do_quiet; /* Quiet flag */
extern int show_log; /* Show logging on screen */
time_t t_start; /* Start time */
time_t t_end; /* End time */
/*
* Put a lock on this program.
*/
int lockindex(void)
{
char Tmpfile[81];
FILE *fp;
pid_t oldpid;
sprintf(Tmpfile, "%s/", CFG.nodelists);
strcpy(lockfile, Tmpfile);
sprintf(Tmpfile + strlen(Tmpfile), "%s%u", TMPNAME, getpid());
sprintf(lockfile + strlen(lockfile), "%s", LCKNAME);
if ((fp = fopen(Tmpfile, "w")) == NULL) {
WriteError("$Can't create lockfile \"%s\"", Tmpfile);
return 1;
}
fprintf(fp, "%10u\n", getpid());
fclose(fp);
while (TRUE) {
if (link(Tmpfile, lockfile) == 0) {
unlink(Tmpfile);
return 0;
}
if ((fp = fopen(lockfile, "r")) == NULL) {
WriteError("$Can't open lockfile \"%s\"", Tmpfile);
unlink(Tmpfile);
return 1;
}
if (fscanf(fp, "%u", &oldpid) != 1) {
WriteError("$Can't read old pid from \"%s\"", Tmpfile);
fclose(fp);
unlink(Tmpfile);
return 1;
}
fclose(fp);
if (kill(oldpid,0) == -1) {
if (errno == ESRCH) {
Syslog('+', "Stale lock found for pid %u", oldpid);
unlink(lockfile);
/* no return, try lock again */
} else {
WriteError("$Kill for %u failed",oldpid);
unlink(Tmpfile);
return 1;
}
} else {
Syslog('+', "mbindex already running, pid=%u", oldpid);
if (!do_quiet)
printf("Another mbindex is already running.\n");
unlink(Tmpfile);
return 1;
}
}
}
void ulockindex(void)
{
if (lockfile)
(void)unlink(lockfile);
}
/*
* If we don't know what to type
*/
void Help(void)
{
do_quiet = FALSE;
ProgName();
colour(11, 0);
printf("\nUsage: mbindex <options>\n\n");
colour(9, 0);
printf(" Options are:\n\n");
colour(3, 0);
printf(" -quiet Quiet mode\n");
colour(7, 0);
printf("\n");
die(0);
}
/*
* Header, only if not quiet.
*/
void ProgName(void)
{
if (do_quiet)
return;
colour(15, 0);
printf("\nMBINDEX: MBSE BBS %s Nodelist Index Compiler\n", VERSION);
colour(14, 0);
printf(" %s\n", COPYRIGHT);
colour(3, 0);
}
void die(int onsig)
{
if (onsig && (onsig < NSIG))
signal(onsig, SIG_IGN);
ulockindex();
if (!do_quiet) {
colour(3, 0);
show_log = TRUE;
}
if (IsSema((char *)"mbindex"))
RemoveSema((char *)"mbindex");
if (onsig) {
if (onsig <= NSIG)
WriteError("Terminated on signal %d (%s)", onsig, SigName[onsig]);
else
WriteError("Terminated with error %d", onsig);
}
t_end = time(NULL);
Syslog(' ', "MBINDEX finished in %s", t_elapsed(t_start, t_end));
if (!do_quiet)
colour(7, 0);
ExitClient(onsig);
}
int main(int argc,char *argv[])
{
int i, rc;
char *cmd;
struct passwd *pw;
#ifdef MEMWATCH
mwInit();
#endif
InitConfig();
InitFidonet();
TermInit(1);
t_start = time(NULL);
umask(002);
/*
* Catch all the signals we can, and ignore the rest.
* Don't listen to SIGTERM.
*/
for(i = 0; i < NSIG; i++) {
if ((i == SIGHUP) || (i == SIGINT) || (i == SIGBUS) || (i == SIGILL) || (i == SIGSEGV))
signal(i, (void (*))die);
else
signal(i, SIG_IGN);
}
cmd = xstrcpy((char *)"Command line: mbindex");
if (argc > 2)
Help();
if (argc == 2) {
cmd = xstrcat(cmd, (char *)" ");
cmd = xstrcat(cmd, argv[1]);
if (strncasecmp(argv[1], "-q", 2) == 0)
do_quiet = TRUE;
else
Help();
}
ProgName();
pw = getpwuid(getuid());
InitClient(pw->pw_name, (char *)"mbindex", CFG.location, CFG.logfile, CFG.util_loglevel, CFG.error_log, CFG.mgrlog);
Syslog(' ', " ");
Syslog(' ', "MBINDEX v%s", VERSION);
Syslog(' ', cmd);
free(cmd);
if (!diskfree(CFG.freespace))
die(101);
if (lockindex()) {
if (!do_quiet)
printf("Can't lock mbindex, abort.\n");
die(104);
}
rc = nodebld();
die(rc);
return 0;
}
void tidy_nllist(nl_list **fap)
{
nl_list *tmp, *old;
Syslog('S', "tidy_nllist");
for (tmp = *fap; tmp; tmp = old) {
old = tmp->next;
free(tmp);
}
*fap = NULL;
}
int in_nllist(struct _nlidx idx, nl_list **fap, int replace)
{
nl_list *tmp;
Syslog('S', "Seeking nlidx match for %u:%u/%u.%u", idx.zone, idx.net, idx.node, idx.point);
for (tmp = *fap; tmp; tmp = tmp->next)
if ((tmp->idx.zone == idx.zone) && (tmp->idx.net == idx.net) &&
(tmp->idx.node == idx.node) && (tmp->idx.point == idx.point)) {
Syslog('S', "Match found");
if (replace) {
tmp->idx = idx;
entries++;
}
regio = tmp->idx.region;
return TRUE;
}
return FALSE;
}
void fill_nllist(struct _nlidx idx, nl_list **fap)
{
nl_list *tmp;
Syslog('S', "fill_nllist %u:%u/%u.%u", idx.zone, idx.net, idx.node, idx.point);
tmp = (nl_list *)malloc(sizeof(nl_list));
tmp->next = *fap;
tmp->idx = idx;
*fap = tmp;
total++;
entries++;
}
char *fullpath(char *fname)
{
static char path[128];
sprintf(path, "%s/%s", CFG.nodelists, fname);
return path;
}
void sort_nllist(nl_list **fap)
{
nl_list *ta, **vector;
size_t n = 0, i;
if (*fap == NULL)
return;
for (ta = *fap; ta; ta = ta->next)
n++;
vector = (nl_list **)malloc(n * sizeof(nl_list *));
Syslog('s', "Sorting %d nodelist entries", n);
i = 0;
for (ta = *fap; ta; ta = ta->next) {
vector[i++] = ta;
Syslog('S', "Before %u:%u/%u.%u", ta->idx.zone, ta->idx.net, ta->idx.node, ta->idx.point);
}
qsort(vector, n, sizeof(nl_list *),
(int(*)(const void*, const void *))comp_node);
(*fap) = vector[0];
i = 1;
for (ta = *fap; ta; ta = ta->next) {
if (i < n)
ta->next = vector[i++];
else
ta->next = NULL;
Syslog('S', "After %u:%u/%u.%u", ta->idx.zone, ta->idx.net, ta->idx.node, ta->idx.point);
}
free(vector);
return;
}
int comp_node(nl_list **fap1, nl_list **fap2)
{
if ((*fap1)->idx.zone != (*fap2)->idx.zone)
return ((*fap1)->idx.zone - (*fap2)->idx.zone);
else if ((*fap1)->idx.net != (*fap2)->idx.net)
return ((*fap1)->idx.net - (*fap2)->idx.net);
else if ((*fap1)->idx.node != (*fap2)->idx.node)
return ((*fap1)->idx.node - (*fap2)->idx.node);
else
return ((*fap1)->idx.point - (*fap2)->idx.point);
}
int compile(char *nlname, unsigned short zo, unsigned short ne, unsigned short no)
{
int num, i, rc = 0;
int lineno, boss = FALSE, bossvalid = FALSE;
unsigned short upnet, upnode;
char buf[256], *p, *q;
faddr *tmpa;
FILE *nl;
struct _nlidx ndx;
struct _nlusr udx;
rc = 0;
if ((nl = fopen(fullpath(nlname), "r")) == NULL) {
WriteError("$Can't open %s", fullpath(nlname));
return 102;
}
Syslog('+', "Compiling \"%s\" (%d)", nlname, filenr);
IsDoing("Compile NL %d", filenr +1);
memset(&ndx, 0, sizeof(ndx));
ndx.type = NL_NODE;
ndx.fileno = filenr;
upnet = 0;
upnode = 0;
/*
* If zone is set, it is a overlay segment
*/
if (zo) {
ndx.zone = zo;
ndx.net = ne;
ndx.node = no;
ndx.point = 0;
}
entries = 0;
lineno = 0;
while (!feof(nl)) {
Nopper();
ndx.offset = ftell(nl);
lineno++;
if (fgets(buf, sizeof(buf)-1, nl) == NULL)
continue;
/*
* Next check at <lf> and <eof> characters
*/
if ((*(buf+strlen(buf) -1) != '\n') && (*(buf + strlen(buf) -1) != '\012')) {
while (fgets(buf, sizeof(buf) -1, nl) &&
(*(buf + strlen(buf) -1) != '\n')) /*void*/;
if (strlen(buf) > 1) /* Suppress EOF character */
Syslog('s', "Nodelist: too long line junked (%d)", lineno);
continue;
}
if (*(p=buf+strlen(buf) -1) == '\n')
*p-- = '\0';
if (*p == '\r')
*p = '\0';
if ((buf[0] == ';') || (buf[0] == '\032') || (buf[0] == '\0'))
continue;
if (CFG.slow_util && do_quiet) {
if (zo) {
usleep(1);
} else {
if ((lineno % 40) == 0)
usleep(1);
}
}
if ((p = strchr(buf, ',')))
*p++ = '\0';
if ((q = strchr(p, ',')))
*q++ = '\0';
ndx.type = NL_NONE;
ndx.pflag = 0;
if (buf[0] == '\0') {
if (boss)
ndx.type = NL_POINT;
else
ndx.type = NL_NODE;
} else
if (strcasecmp(buf,"Boss") == 0) {
ndx.type = NL_POINT;
bossvalid = FALSE;
if ((tmpa=parsefnode(p)) == NULL) {
WriteError("%s(%u): unparsable Boss addr \"%s\"",
nlname,lineno,p);
continue;
}
boss = TRUE;
if (tmpa->zone)
ndx.zone = tmpa->zone;
ndx.net = tmpa->net;
ndx.node = tmpa->node;
ndx.point = 0;
tidy_faddr(tmpa);
Syslog('S', "Boss %u:%u/%u", ndx.zone, ndx.net, ndx.node);
ndx.type = NL_NONE;
if (in_nllist(ndx, &nll, FALSE)) {
Syslog('S', "Boss exists");
bossvalid = TRUE;
}
else
Syslog('S', "Boss not found");
continue; /* no further processing */
} else {
boss = FALSE;
ndx.type = NL_NONE;
if (!strcasecmp(buf, "Down")) {
ndx.pflag |= NL_DOWN;
ndx.type = NL_NODE;
}
if (!strcasecmp(buf, "Hold")) {
ndx.pflag |= NL_HOLD;
ndx.type = NL_NODE;
}
if (!strcasecmp(buf, "Pvt")) {
ndx.pflag |= NL_PVT;
ndx.type = NL_NODE;
}
if (!strcasecmp(buf, "Zone"))
ndx.type = NL_ZONE;
if (!strcasecmp(buf, "Region"))
ndx.type = NL_REGION;
if (!strcasecmp(buf, "Host"))
ndx.type = NL_HOST;
if (!strcasecmp(buf, "Hub"))
ndx.type = NL_HUB;
if (!strcasecmp(buf, "Point")) {
ndx.type = NL_POINT;
bossvalid = TRUE;
}
}
if (ndx.type == NL_NONE) {
for (q = buf; *q; q++)
if (*q < ' ')
*q='.';
WriteError("%s(%u): unidentified entry \"%s\"",
nlname, lineno, buf);
continue;
}
Syslog('S',"Got \"%s\" as \"%s\" typ %d", buf, p, ndx.type);
if ((num=atoi(p)) == 0) {
WriteError("%s(%u): bad numeric \"%s\"",
nlname,lineno,p);
continue;
}
/*
* now update the current address
*/
switch (ndx.type) {
case NL_REGION: ndx.net = num;
ndx.node = 0;
ndx.point = 0;
ndx.upnet = ndx.zone;
ndx.upnode= 0;
ndx.region= num;
upnet = num;
upnode = 0;
break;
case NL_ZONE: ndx.zone = num;
ndx.net = num;
ndx.node = 0;
ndx.point = 0;
ndx.upnet = 0;
ndx.upnode= 0;
ndx.region= 0;
upnet = num;
upnode = 0;
break;
case NL_HOST: ndx.net = num;
ndx.node = 0;
ndx.point = 0;
ndx.upnet = ndx.region;
ndx.upnode= 0;
upnet = num;
upnode = 0;
break;
case NL_HUB: ndx.node = num;
ndx.point = 0;
ndx.upnet = ndx.net;
ndx.upnode= 0;
upnet = ndx.net;
upnode = num;
break;
case NL_NODE: ndx.node = num;
ndx.point = 0;
ndx.upnet = upnet;
ndx.upnode= upnode;
break;
case NL_POINT: ndx.point = num;
ndx.upnet = ndx.net;
ndx.upnode= ndx.node;
if ((!ndx.region) && bossvalid)
ndx.region = regio;
break;
}
if (!do_quiet) {
printf("\rZone %-6uRegion %-6uNet %-6uNode %-6uPoint %-6u",
ndx.zone, ndx.region, ndx.net, ndx.node, ndx.point);
fflush(stdout);
}
memset(&udx, 0, sizeof(udx));
udx.record = total;
/*
* Read nodelist line and extract username.
*/
for (i = 0; i < 3; i++) {
p = q;
if (p == NULL)
continue;
if ((q = strchr(p, ',')))
*q++ = '\0';
if (q == NULL)
q = p;
}
if (strlen(p) > 35)
p[35] = '\0';
sprintf(udx.user, "%s", p);
/*
* Now search for the baudrate field, 300 means it's
* and ISDN or TCP/IP only node which is a special case.
*/
for (i = 0; i < 2; i++) {
p = q;
if (p == NULL)
continue;
if ((q = strchr(p, ',')))
*q++ = '\0';
if (q == NULL)
q = p;
}
if ((strlen(p) == 3) && (!strcmp(p, "300")) && (q != NULL)) {
if ((strstr(q, (char *)"X75")) ||
(strstr(q, (char *)"V110L")) ||
(strstr(q, (char *)"V110H")) ||
(strstr(q, (char *)"V120L")) ||
(strstr(q, (char *)"V120H")) ||
(strstr(q, (char *)"ISDN")))
ndx.pflag |= NL_ISDN;
if ((strstr(q, (char *)"IFC")) ||
(strstr(q, (char *)"IBN")) ||
(strstr(q, (char *)"ITN")) ||
(strstr(q, (char *)"IVM")) ||
(strstr(q, (char *)"IFT")) ||
(strstr(q, (char *)"IP")))
ndx.pflag |= NL_TCPIP;
}
Syslog('S',"put: %u:%u/%u.%u reg %u upl %u/%u typ %u flg %02X as (%u,%lu)",
ndx.zone,ndx.net,ndx.node,
ndx.point,ndx.region,ndx.upnet,ndx.upnode,
ndx.type,ndx.pflag,ndx.fileno,ndx.offset);
/*
* If zone, net and node given, then this list is an
* overlay so we will call in_list() to replace the
* existing records, or append them if they don't exist.
* Also, only points with a valid boss will be added.
*/
if (zo) {
if (!(in_nllist(ndx, &nll, TRUE))) {
if (ndx.point && bossvalid) {
fill_nllist(ndx, &nll);
Syslog('S', "Add point %u:%u/%u.%u", ndx.zone, ndx.net, ndx.node, ndx.point);
}
if (!ndx.point)
fill_nllist(ndx, &nll);
}
} else
fill_nllist(ndx, &nll);
}
Syslog('+', "%d entries", entries);
if (!do_quiet) {
printf(" %ld entries\n", entries);
fflush(stdout);
}
return rc;
}
/*
* Tidy the filearray
*/
void tidy_fdlist(fd_list **fdp)
{
fd_list *tmp, *old;
for (tmp = *fdp; tmp; tmp = old) {
old = tmp->next;
free(tmp);
}
*fdp = NULL;
}
/*
* Add a file on the array.
*/
void fill_fdlist(fd_list **fdp, char *filename, time_t filedate)
{
fd_list *tmp;
tmp = (fd_list *)malloc(sizeof(fd_list));
tmp->next = *fdp;
sprintf(tmp->fname, "%s", filename);
tmp->fdate = filedate;
*fdp = tmp;
}
int compfdate(fd_list **, fd_list **);
/*
* Sort the array of files by filedate
*/
void sort_fdlist(fd_list **fdp)
{
fd_list *ta, **vector;
size_t n = 0, i;
if (*fdp == NULL) {
return;
}
for (ta = *fdp; ta; ta = ta->next)
n++;
vector = (fd_list **)malloc(n * sizeof(fd_list *));
i = 0;
for (ta = *fdp; ta; ta = ta->next) {
vector[i++] = ta;
}
qsort(vector, n, sizeof(fd_list*), (int(*)(const void*, const void*))compfdate);
(*fdp) = vector[0];
i = 1;
for (ta = *fdp; ta; ta = ta->next) {
if (i < n)
ta->next = vector[i++];
else
ta->next = NULL;
}
free(vector);
return;
}
int compfdate(fd_list **fdp1, fd_list **fdp2)
{
return ((*fdp1)->fdate - (*fdp2)->fdate);
}
/*
* Return the name of the oldest file in the array
*/
char *pull_fdlist(fd_list **fdp)
{
static char buf[65];
fd_list *ta;
if (*fdp == NULL)
return NULL;
ta = *fdp;
memset(&buf, 0, sizeof(buf));
sprintf(buf, "%s", ta->fname);
if (ta->next != NULL)
*fdp = ta->next;
else
*fdp = NULL;
free(ta);
return buf;
}
int makelist(char *base, unsigned short zo, unsigned short ne, unsigned short no)
{
int rc = 0, files = 0;
struct dirent *de;
DIR *dp;
char *p = NULL, *q;
struct _nlfil fdx;
fd_list *fdl = NULL;
if (!strlen(base)) {
WriteError("Error, no nodelist defined for %d:%d/%d", zo, ne, no);
return 0;
}
if ((dp = opendir(CFG.nodelists)) == NULL) {
WriteError("$Can't open dir %s", CFG.nodelists);
rc = 103;
} else {
while ((de = readdir(dp)))
if (strncmp(de->d_name, base, strlen(base)) == 0) {
/*
* Extension must be at least 2 digits
*/
q = (de->d_name) + strlen(base);
if ((*q == '.') && (strlen(q) > 2) &&
(strspn(q+1,"0123456789") == (strlen(q)-1))) {
/*
* Add matched filenames to the array
*/
fill_fdlist(&fdl, de->d_name, file_time(fullpath(de->d_name)));
files++;
}
}
closedir(dp);
if (files == 0) {
Syslog('+', "No nodelist found for %s", base);
return 103;
}
/*
* Sort found nodelists by age and kill all but the newest 2.
*/
sort_fdlist(&fdl);
while (files) {
p = pull_fdlist(&fdl);
if (files > 2) {
Syslog('+', "Remove old \"%s\"", p);
unlink(fullpath(p));
}
files--;
}
tidy_fdlist(&fdl);
memset(&fdx, 0, sizeof(fdx));
sprintf(fdx.filename, "%s", p);
sprintf(fdx.domain, "%s", fidonet.domain);
fdx.number = filenr;
fwrite(&fdx, sizeof(fdx), 1, ffp);
rc = compile(p, zo, ne, no);
filenr++;
}
return rc;
}
int nodebld(void)
{
int rc = 0, i;
char *im, *fm, *um, *old, *new;
struct _nlfil fdx;
FILE *fp;
nl_list *tmp;
memset(&fdx, 0, sizeof(fdx));
im = xstrcpy(fullpath((char *)"temp.index"));
fm = xstrcpy(fullpath((char *)"temp.files"));
um = xstrcpy(fullpath((char *)"temp.users"));
if ((ifp = fopen(im, "w+")) == NULL) {
WriteError("$Can't create %s",MBSE_SS(im));
return 101;
}
if ((ufp = fopen(um, "w+")) == NULL) {
WriteError("$Can't create %s", MBSE_SS(um));
fclose(ifp);
unlink(im);
return 101;
}
if ((ffp = fopen(fm, "w+")) == NULL) {
WriteError("$Can't create %s", MBSE_SS(fm));
fclose(ifp);
unlink(im);
fclose(ufp);
unlink(um);
return 101;
}
if (!do_quiet) {
colour(3, 0);
printf("\n");
}
if ((fp = fopen(fidonet_fil, "r")) == 0)
rc = 102;
else {
fread(&fidonethdr, sizeof(fidonethdr), 1, fp);
while (fread(&fidonet, fidonethdr.recsize, 1, fp) == 1) {
if (fidonet.available) {
rc = makelist(fidonet.nodelist, 0, 0, 0);
if (rc)
break;
for (i = 0; i < 6; i++) {
if (fidonet.seclist[i].zone) {
rc = makelist(fidonet.seclist[i].nodelist,
fidonet.seclist[i].zone,
fidonet.seclist[i].net,
fidonet.seclist[i].node);
if (rc)
break;
}
}
if (rc)
break;
}
}
fclose(fp);
}
fclose(ufp);
fclose(ffp);
if (rc == 0) {
IsDoing("Sorting nodes");
sort_nllist(&nll);
IsDoing("Writing files");
for (tmp = nll; tmp; tmp = tmp->next)
fwrite(&tmp->idx, sizeof(struct _nlidx), 1, ifp);
fclose(ifp);
Syslog('+', "Compiled %d entries", total);
/*
* Rename existing files to old.*, they stay on disk in
* case they are open by some program. The temp.* files
* are then renamed to node.*
*/
old = xstrcpy(fullpath((char *)"old.index"));
new = xstrcpy(fullpath((char *)"node.index"));
unlink(old);
rename(new, old);
rename(im, new);
free(old);
free(new);
old = xstrcpy(fullpath((char *)"old.users"));
new = xstrcpy(fullpath((char *)"node.users"));
unlink(old);
rename(new, old);
rename(um, new);
free(old);
free(new);
old = xstrcpy(fullpath((char *)"old.files"));
new = xstrcpy(fullpath((char *)"node.files"));
unlink(old);
rename(new, old);
rename(fm, new);
free(old);
free(new);
} else {
fclose(ifp);
Syslog('+', "Compile failed, rc=%d", rc);
unlink(im);
unlink(fm);
unlink(um);
}
free(im);
free(fm);
free(um);
tidy_nllist(&nll);
return rc;
}