Newer
Older
/* Synchronet node information retrieval functions */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include "sbbs.h"
#include "cmdshell.h"
/****************************************************************************/
/* Reads the data for node number 'number' into the structure 'node' */
/* from NODE.DAB */
/* if lockit is non-zero, locks this node's record. putnodedat() unlocks it */
/****************************************************************************/
bool sbbs_t::getnodedat(uint number, node_t *node, bool lockit)
char str[MAX_PATH+1];
int rd=sizeof(node_t);
int count;
if(node==NULL || number<1)
return false;
errormsg(WHERE,ERR_CHK,"node number",number);
return false;
if(node!=&thisnode)
memset(node,0,sizeof(node_t));
pthread_mutex_lock(&nodefile_mutex);
if(nodefile==-1) {
if((nodefile=opennodedat(&cfg))==-1) {
pthread_mutex_unlock(&nodefile_mutex);
errormsg(WHERE,ERR_OPEN,"nodefile",number);
return false;
}
}
#if 0 // This leads to high disk (or Samba/file server) utilization as we call getnodedat() a lot
else
utime(str,NULL); /* NFS fix... utime() forces a cache refresh */
for(count=0;count<LOOP_NODEDAB;count++) {
if(count > 0)
FILE_RETRY_DELAY(count + 1);
if(lockit && lock(nodefile, nodedatoffset(number), sizeof(node_t))!=0) {
unlock(nodefile, nodedatoffset(number), sizeof(node_t));
continue;
if(seeknodedat(nodefile, number)) {
rd=read(nodefile,node,sizeof(node_t));
if(rd!=sizeof(node_t))
unlock(nodefile, nodedatoffset(number), sizeof(node_t));
if(rd==sizeof(node_t))
break;
}
}
if(!lockit && cfg.node_misc&NM_CLOSENODEDAB) {
close(nodefile);
nodefile=-1;
}
if(count==LOOP_NODEDAB) {
if(nodefile!=-1)
close(nodefile);
nodefile=-1;
pthread_mutex_unlock(&nodefile_mutex);
errormsg(WHERE,rd==sizeof(node_t) ? ERR_LOCK : ERR_READ,"node.dab",number+1);
return false;
pthread_mutex_unlock(&nodefile_mutex);
if(count>(LOOP_NODEDAB/2)) {
SAFEPRINTF2(str,"NODE.DAB (node %d) COLLISION - Count: %d"
logline(LOG_WARNING,"!!",str);
}
return true;

Rob Swindell
committed
static int getpagingnode(scfg_t* cfg)
{
for(int i = 1; i <= cfg->sys_nodes; i++) {
node_t node;
if(i == cfg->node_num)
continue;
if(getnodedat(cfg, i, &node, FALSE, NULL) == 0 && node.action == NODE_PAGE && node.aux == cfg->node_num)
return i;
}
return 0;
}
/****************************************************************************/
/* Synchronizes all the nodes knowledge of the other nodes' actions, mode, */
/* status and other flags. */
/* Assumes that getnodedat(node_num,&thisnode,0) was called right before it */
/* is called. */
/****************************************************************************/
void sbbs_t::nodesync(bool clearline)
{
char str[256],today[32];
nodesync_inside=1;
if(thisnode.action!=action) {
if(getnodedat(cfg.node_num,&thisnode, true)) {
thisnode.action=action;
putnodedat(cfg.node_num,&thisnode);
}
criterrs=thisnode.errors;
if(sys_status&SS_USERON) {
if(thisnode.status==NODE_WFC) {
lprintf(LOG_ERR, "Node %d NODE STATUS FIXUP", cfg.node_num);
if(getnodedat(cfg.node_num,&thisnode, true)) {
thisnode.status=NODE_INUSE;
thisnode.useron=useron.number;
putnodedat(cfg.node_num,&thisnode);
}
if(!(sys_status&SS_NEWDAY)) {
now=time(NULL);
unixtodstr(logontime,str);
unixtodstr(now,today);
if(strcmp(str,today)) { /* New day, clear "today" user vars */
sys_status|=SS_NEWDAY; // So we don't keep doing this over&over
resetdailyuserdat(&cfg, &useron,/* write: */true);

rswindell
committed
}
}
if(thisnode.misc&NODE_UDAT && !(useron.rest&FLAG('G'))) { /* not guest */
if(getuseron(WHERE) && getnodedat(cfg.node_num,&thisnode, true)) {
thisnode.misc&=~NODE_UDAT;
putnodedat(cfg.node_num,&thisnode);
}
if(!(sys_status&SS_MOFF)) {
if(thisnode.misc&NODE_MSGW)
getsmsg(useron.number, clearline); /* getsmsg clears MSGW flag */
if(thisnode.misc&NODE_NMSG)
getnmsg(clearline); /* getnmsg clears NMSG flag */
if(cfg.sync_mod[0])
exec_bin(cfg.sync_mod,&main_csi);
if(thisnode.misc&NODE_INTR) {
bputs(text[NodeLocked]);
logline(LOG_NOTICE,nulstr,"Interrupted");
hangup();
nodesync_inside=0;
if(thisnode.misc&NODE_LCHAT) { // pulled into local chat with sysop
restoreline();

Rob Swindell
committed
if(thisnode.misc&NODE_FCHAT) { // forced into private chat
int n = getpagingnode(&cfg);
if(n) {
uint save_action = action;

Rob Swindell
committed
privchat(true, n);
action = save_action;
restoreline();

Rob Swindell
committed
}
if(getnodedat(cfg.node_num, &thisnode, true)) {
thisnode.action = action;

Rob Swindell
committed
thisnode.misc &= ~NODE_FCHAT;

Rob Swindell
committed
}
}
if(sys_status&SS_USERON && memcmp(&nodesync_user,&useron,sizeof(user_t))) {
getusrdirs();
getusrsubs();
memcpy(&nodesync_user,&useron,sizeof(nodesync_user));
if(sys_status&SS_USERON && online && (timeleft/60)<(5-timeleft_warn)
&& !SYSOP) {
timeleft_warn=5-(timeleft/60);
if(!(sys_status&SS_MOFF)) {
attr(LIGHTGRAY);
bprintf(text[OnlyXminutesLeft]
,((ushort)timeleft/60)+1,(timeleft/60) ? "s" : nulstr);
attr(atr); /* replace original attributes */
nodesync_inside=0;
}
/****************************************************************************/
/* Prints short messages waiting for this node, if any... */
/****************************************************************************/
bool sbbs_t::getnmsg(bool clearline)
int file;
long length;
if(getnodedat(cfg.node_num,&thisnode, true)) {
thisnode.misc&=~NODE_NMSG; /* clear the NMSG flag */
putnodedat(cfg.node_num,&thisnode);
}
SAFEPRINTF2(str,"%smsgs/n%3.3u.msg",cfg.data_dir,cfg.node_num);
return true;
if((file=nopen(str,O_RDWR))==-1) {
/**
errormsg(WHERE,ERR_OPEN,str,O_RDWR);
**/
return false;
length=(long)filelength(file);
if(length <= 0) {
return true;
close(file);
errormsg(WHERE,ERR_ALLOC,str,length+1);
return false;
if(read(file,buf,length)!=length) {
errormsg(WHERE,ERR_READ,str,length);
return false;
int retval = chsize(file,0L);
close(file);
buf[length]=0;
if(clearline)
this->clearline();
else if(column)
return retval == 0;
}
/****************************************************************************/
/* 'ext' must be at least 128 bytes! */
/****************************************************************************/
bool sbbs_t::getnodeext(uint number, char *ext)
char str[MAX_PATH+1];
int rd,count;
if(number < 1 || number>cfg.sys_nodes) {
errormsg(WHERE,ERR_CHK,"node number",number);
return false;
if((node_ext=opennodeext(&cfg))==-1) {
memset(ext,0,128);
errormsg(WHERE,ERR_OPEN,"node.exb",O_RDONLY|O_DENYNONE);
return false;
}
number--; /* make zero based */
for(count=0;count<LOOP_NODEDAB;count++) {
if(count > 0)
FILE_RETRY_DELAY(count + 1);
if(lock(node_ext,(long)number*128L,128)!=0)
continue;
lseek(node_ext,(long)number*128L,SEEK_SET);
rd=read(node_ext,ext,128);
unlock(node_ext,(long)number*128L,128);
if(rd==128)
close(node_ext);
node_ext=-1;
if(count==LOOP_NODEDAB) {
errormsg(WHERE,ERR_READ,"node.exb",number+1);
return false;
}
if(count>(LOOP_NODEDAB/2)) {
SAFEPRINTF2(str,"NODE.EXB (node %d) COLLISION - Count: %d"
}
return true;
}
/****************************************************************************/
/* Prints short messages waiting for 'usernumber', if any... */
/* then truncates the file. */
/****************************************************************************/
bool sbbs_t::getsmsg(int usernumber, bool clearline)
node_t node;
int i;
for(i=1;i<=cfg.sys_nodes;i++) { /* clear msg waiting flag */
if(!getnodedat(i,&node,false) || node.useron != usernumber)
continue;
if(getnodedat(i,&node, true)) {
if(node.useron==usernumber
&& (node.status==NODE_INUSE || node.status==NODE_QUIET)
&& node.misc&NODE_MSGW)
node.misc&=~NODE_MSGW;
if((buf = readsmsg(&cfg, usernumber)) == NULL)
return false;
getnodedat(cfg.node_num,&thisnode);
if(clearline)
this->clearline();
else
if(column)
return true;
}
/****************************************************************************/
/* This function lists users that are online. */
/* If listself is true, it will list the current node. */
/* Returns number of active nodes (not including current node). */
/****************************************************************************/
int sbbs_t::whos_online(bool listself)
{
int i,j;
node_t node;
if(cfg.whosonline_mod[0] != '\0') {
return exec_bin(cfg.whosonline_mod, &main_csi);
}
CRLF;
bputs(text[NodeLstHdr]);
for(j=0,i=1;i<=cfg.sys_nodes && i<=cfg.sys_lastnode;i++) {
getnodedat(i,&node);
if(i==cfg.node_num) {
if(listself)
printnodedat(i,&node);
if(node.status==NODE_INUSE || (SYSOP && node.status==NODE_QUIET)) {
printnodedat(i,&node);
if(!lastnodemsg)
lastnodemsg=i;
if(!j)
bputs(text[NoOtherActiveNodes]);
return(j);
}
void sbbs_t::nodelist(void)
{
node_t node;
if(cfg.nodelist_mod[0] != '\0') {
exec_bin(cfg.nodelist_mod, &main_csi);
return;
}
CRLF;
bputs(text[NodeLstHdr]);
for(int i=1;i<=cfg.sys_nodes && i<=cfg.sys_lastnode;i++) {
getnodedat(i,&node);
static char* node_connection_desc(sbbs_t* sbbs, ushort conn, char* str)
{
switch(conn) {
case NODE_CONNECTION_LOCAL:
case NODE_CONNECTION_TELNET:
return sbbs->text[NodeConnectionTelnet];
case NODE_CONNECTION_RLOGIN:
return sbbs->text[NodeConnectionRLogin];
case NODE_CONNECTION_SSH:
return sbbs->text[NodeConnectionSSH];

Rob Swindell
committed
case NODE_CONNECTION_SFTP:
return sbbs->text[NodeConnectionSFTP];
case NODE_CONNECTION_RAW:
return sbbs->text[NodeConnectionRaw];
default:
snprintf(str,16,sbbs->text[NodeConnectionModem],conn);
break;
}
return str;
}
/****************************************************************************/
/* Displays the information for node number 'number' contained in 'node' */
/****************************************************************************/
void sbbs_t::printnodedat(uint number, node_t* node)
{

Rob Swindell
committed
int i;
char hour,mer[3];
char tmp[512];
attr(cfg.color[clr_nodenum]);
bprintf("%3d ",number);
attr(cfg.color[clr_nodestatus]);
switch(node->status) {
case NODE_WFC:
bputs(text[NodeStatusWaitingForCall]);
bputs(text[NodeStatusOffline]);
bputs("Networking"); /* obsolete */
bputs(text[NodeStatusLogon]);
bputs(node_connection_desc(this, node->connection, tmp));
case NODE_LOGOUT:
bprintf(text[NodeStatusLogout]
,(node->misc&NODE_ANON) && !SYSOP ? text[UNKNOWN_USER] : username(&cfg,node->useron,tmp));
break;
bputs(text[NodeStatusEventWaiting]);
break;
case NODE_EVENT_LIMBO:
bprintf(text[NodeStatusEventLimbo],node->aux);
break;
case NODE_EVENT_RUNNING:
bputs(text[NodeStatusEventRunning]);
bputs(text[NodeStatusNewUser]);
bputs(node_connection_desc(this, node->connection, tmp));
break;
case NODE_QUIET:
if(!SYSOP) {
bputs(text[NodeStatusWaitingForCall]);
case NODE_INUSE:
attr(cfg.color[clr_nodeuser]);
if(node->misc&NODE_ANON && !SYSOP)
bputs(text[UNKNOWN_USER]);
else
bputs(username(&cfg,node->useron,tmp));
attr(cfg.color[clr_nodestatus]);
bputs(" ");
bputs(node_activity(&cfg, node, tmp, sizeof tmp, number));
attr(cfg.color[clr_nodestatus]);
bputs(node_connection_desc(this, node->connection, tmp));
if(node->action==NODE_DLNG) {
if(cfg.sys_misc&SM_MILITARY) {
hour=node->aux/60;
else if((node->aux/60)>=12) {
if(node->aux/60==12)
hour=12;
else
hour=(node->aux/60)-12;
else {
if((node->aux/60)==0) /* 12 midnite */
hour=12;
else hour=node->aux/60;
strcpy(mer,"am");
}
,hour,node->aux%60,mer);
}
break;
}
i=NODE_LOCK;
if(node->status==NODE_INUSE || SYSOP)
i|=NODE_POFF|NODE_AOFF|NODE_MSGW|NODE_NMSG;
if(node->misc&i) {
bputs(" (");
if(node->misc&(i&NODE_AOFF))
outchar('A');
if(node->misc&NODE_LOCK)
outchar('L');
if(node->misc&(i&(NODE_MSGW)))
if(node->misc&(i&(NODE_NMSG)))
outchar('N');
if(node->misc&(i&NODE_POFF))
outchar('P');
outchar(')');
}

Rob Swindell
committed
&(NODE_ANON|NODE_UDAT|NODE_INTR|NODE_RRUN|NODE_EVENT|NODE_DOWN|NODE_LCHAT|NODE_FCHAT))
|| node->status==NODE_QUIET)) {
bputs(" [");
if(node->misc&NODE_ANON)
outchar('A');
if(node->misc&NODE_INTR)
outchar('I');
if(node->misc&NODE_RRUN)
outchar('R');
if(node->misc&NODE_UDAT)
outchar('U');
if(node->misc&NODE_EVENT)
outchar('E');
if(node->misc&NODE_DOWN)
outchar('D');
if(node->misc&NODE_LCHAT)
outchar('C');

Rob Swindell
committed
if(node->misc&NODE_FCHAT)
outchar('F');
if(node->status==NODE_QUIET)
outchar('Q');
outchar(']');
}
if(node->errors && SYSOP) {
attr(cfg.color[clr_err]);
bprintf(" %d error%c",node->errors, node->errors>1 ? 's' : '\0' );
}
attr(LIGHTGRAY);
CRLF;
}
uint sbbs_t::count_nodes(bool self)
{
uint count = 0;
for(int i=1; i<=cfg.sys_nodes && i<=cfg.sys_lastnode; i++) {
node_t node;
if(!getnodedat(i, &node))