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;
if (number > cfg.sys_nodes) {
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
utime(str, NULL); /* NFS fix... utime() forces a cache refresh */
for (count = 0; count < LOOP_NODEDAB; count++) {
if (count > 0)
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);
}
if (count == LOOP_NODEDAB) {
if (nodefile != -1)
close(nodefile);
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"
, number + 1, count);
logline(LOG_WARNING, "!!", str);
}
return true;

Rob Swindell
committed
static int getpagingnode(scfg_t* cfg)
{
for (int i = 1; i <= cfg->sys_nodes; i++) {

Rob Swindell
committed
node_t node;
if (i == cfg->node_num)

Rob Swindell
committed
continue;
if (getnodedat(cfg, i, &node, FALSE, NULL) == 0 && node.action == NODE_PAGE && node.aux == cfg->node_num)

Rob Swindell
committed
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];
int atr = curatr;
if (nodesync_inside || !online)
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_DATE_CHANGED)) {
now = time(NULL);
unixtodstr(logontime, str);
unixtodstr(now, today);
if (strcmp(str, today)) { /* New day, clear "today" user vars */
sys_status |= SS_DATE_CHANGED; // 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) {
logline(LOG_NOTICE, nulstr, "Interrupted");
if (thisnode.misc & NODE_LCHAT) { // pulled into local chat with sysop
restoreline();

Rob Swindell
committed
if (thisnode.misc & NODE_FCHAT) { // forced into private chat

Rob Swindell
committed
int n = getpagingnode(&cfg);
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)) {
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)
char str[MAX_PATH + 1], *buf;
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);
if (flength(str) < 1L)
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;
if ((buf = (char *)malloc(length + 1)) == NULL) {
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);
this->clearline();
putmsg(buf, P_NOATCODES);
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)
if (lock(node_ext, (long)number * 128L, 128) != 0)
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);
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"
, number + 1, count);
logline("!!", str);
}
return true;
}
/****************************************************************************/
/* Prints short messages waiting for 'usernumber', if any... */
/* then truncates the file. */
/****************************************************************************/
bool sbbs_t::getsmsg(int usernumber, bool clearline)
char * buf;
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;
putnodedat(i, &node);
if ((buf = readsmsg(&cfg, usernumber)) == NULL)
return false;
getnodedat(cfg.node_num, &thisnode);
if (clearline)
this->clearline();
else
if (column)
CRLF;
putmsg(buf, P_NOATCODES);
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;
bputs(text[NoOtherActiveNodes]);
void sbbs_t::nodelist(void)
{
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);
printnodedat(i, &node);
static char* node_connection_desc(sbbs_t* sbbs, ushort conn, char* str)
{
case NODE_CONNECTION_LOCAL:
return (char*)" Locally"; /* obsolete */
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)
{
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) {
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));
bputs(text[NodeStatusWaitingForCall]);
case NODE_INUSE:
attr(cfg.color[clr_nodeuser]);
if (node->misc & NODE_ANON && !SYSOP)
bputs(text[UNKNOWN_USER]);
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;
mer[0] = 0;
else if ((node->aux / 60) >= 12) {
if (node->aux / 60 == 12)
hour = 12;
hour = (node->aux / 60) - 12;
strcpy(mer, "pm");
if ((node->aux / 60) == 0) /* 12 midnite */
hour = 12;
else
hour = node->aux / 60;
, hour, node->aux % 60, mer);
i = NODE_LOCK;
if (node->status == NODE_INUSE || SYSOP)
i |= NODE_POFF | NODE_AOFF | NODE_MSGW | NODE_NMSG;
if (node->misc & i) {
if (node->misc & (i & NODE_AOFF))
if (node->misc & NODE_LOCK)
if (node->misc & (i & (NODE_MSGW)))
if (node->misc & (i & (NODE_NMSG)))
if (node->misc & (i & NODE_POFF))
if (SYSOP && ((node->misc
& (NODE_ANON | NODE_UDAT | NODE_INTR | NODE_RRUN | NODE_EVENT | NODE_DOWN | NODE_LCHAT | NODE_FCHAT))
|| node->status == NODE_QUIET)) {
if (node->misc & NODE_ANON)
if (node->misc & NODE_INTR)
if (node->misc & NODE_RRUN)
if (node->misc & NODE_UDAT)
if (node->misc & NODE_EVENT)
if (node->misc & NODE_DOWN)
if (node->misc & NODE_LCHAT)
if (node->misc & NODE_FCHAT)

Rob Swindell
committed
outchar('F');
if (node->status == NODE_QUIET)
if (node->errors && SYSOP) {
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))
continue;
if (!self && i == cfg.node_num)
continue;
if (node.status != NODE_INUSE)