Skip to content
Snippets Groups Projects
getnode.cpp 15.1 KiB
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)
	int		rd=sizeof(node_t);
	int		count;
	if(node==NULL || number<1)

	if(number>cfg.sys_nodes) {
		errormsg(WHERE,ERR_CHK,"node number",number);
	if(node!=&thisnode)
		memset(node,0,sizeof(node_t));
	pthread_mutex_lock(&nodefile_mutex);
		if((nodefile=opennodedat(&cfg))==-1) {
			pthread_mutex_unlock(&nodefile_mutex);
			errormsg(WHERE,ERR_OPEN,"nodefile",number);
#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));
		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) {
		pthread_mutex_unlock(&nodefile_mutex);
		errormsg(WHERE,rd==sizeof(node_t) ? ERR_LOCK : ERR_READ,"node.dab",number+1);
	pthread_mutex_unlock(&nodefile_mutex);
		SAFEPRINTF2(str,"NODE.DAB (node %d) COLLISION - Count: %d"
			,number+1, count);
		logline(LOG_WARNING,"!!",str); 
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];
	int		atr=curatr;
	if(nodesync_inside || !online) 
	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)) {
				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);
		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)) {
				getsmsg(useron.number, clearline); 	/* getsmsg clears MSGW flag */
				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;
Rob Swindell's avatar
Rob Swindell committed
		return;

	if(thisnode.misc&NODE_LCHAT) { // pulled into local chat with sysop
		privchat(true);

	if(thisnode.misc&NODE_FCHAT) { // forced into private chat
		int n = getpagingnode(&cfg);
		if(n) {
		if(getnodedat(cfg.node_num, &thisnode, true)) {
Rob Swindell's avatar
Rob Swindell committed
			putnodedat(cfg.node_num, &thisnode);
	if(sys_status&SS_USERON && memcmp(&nodesync_user,&useron,sizeof(user_t))) {
		getusrdirs();
		getusrsubs();
Rob Swindell's avatar
Rob Swindell committed
		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]
Rob Swindell's avatar
Rob Swindell committed
				,((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;
	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)
	if((file=nopen(str,O_RDWR))==-1) {
		/**
			errormsg(WHERE,ERR_OPEN,str,O_RDWR);
		**/
	if((buf=(char *)malloc(length+1))==NULL) {
		close(file);
		errormsg(WHERE,ERR_ALLOC,str,length+1);
	if(read(file,buf,length)!=length) {
		free(buf);
		errormsg(WHERE,ERR_READ,str,length);
	int retval = chsize(file,0L);
	close(file);
	buf[length]=0;

Rob Swindell's avatar
Rob Swindell committed
		CRLF;
	putmsg(buf,P_NOATCODES);
	free(buf);
}

/****************************************************************************/
/* 'ext' must be at least 128 bytes!                                        */
/****************************************************************************/
bool sbbs_t::getnodeext(uint number, char *ext)
	if(number < 1 || number>cfg.sys_nodes) {
		errormsg(WHERE,ERR_CHK,"node number",number);
	if((node_ext=opennodeext(&cfg))==-1) {
		errormsg(WHERE,ERR_OPEN,"node.exb",O_RDONLY|O_DENYNONE);
	number--;   /* make zero based */
	for(count=0;count<LOOP_NODEDAB;count++) {
		if(count > 0)
			FILE_RETRY_DELAY(count + 1);
Rob Swindell's avatar
Rob Swindell committed
		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)

	if(count==LOOP_NODEDAB) {
		errormsg(WHERE,ERR_READ,"node.exb",number+1);
		SAFEPRINTF2(str,"NODE.EXB (node %d) COLLISION - Count: %d"
			,number+1, count);
Rob Swindell's avatar
Rob Swindell committed
		logline("!!",str);
}

/****************************************************************************/
/* Prints short messages waiting for 'usernumber', if any...                */
/****************************************************************************/
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)
			if(node.useron==usernumber
					&& (node.status==NODE_INUSE || node.status==NODE_QUIET)
					&& node.misc&NODE_MSGW)
				node.misc&=~NODE_MSGW;
Rob Swindell's avatar
Rob Swindell committed
			putnodedat(i,&node);
		}
	if((buf = readsmsg(&cfg, usernumber)) == NULL)
		return false;
	getnodedat(cfg.node_num,&thisnode);
	putmsg(buf,P_NOATCODES);
	free(buf);
}

/****************************************************************************/
/* 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)
{
	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++) {
		if(i==cfg.node_num) {
			if(listself)
				printnodedat(i,&node);
Rob Swindell's avatar
Rob Swindell committed
			continue;
		if(node.status==NODE_INUSE || (SYSOP && node.status==NODE_QUIET)) {
			printnodedat(i,&node);
			if(!lastnodemsg)
				lastnodemsg=i;
Rob Swindell's avatar
Rob Swindell committed
			j++;
		}
	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++) {
Rob Swindell's avatar
Rob Swindell committed
		printnodedat(i,&node);
static char* node_connection_desc(sbbs_t* sbbs, ushort conn, char* str)
{
	switch(conn) {
		case NODE_CONNECTION_LOCAL:
rswindell's avatar
rswindell committed
			return (char*)" Locally";	/* obsolete */
			return sbbs->text[NodeConnectionTelnet];
			return sbbs->text[NodeConnectionRLogin];
			return sbbs->text[NodeConnectionSSH];
		case NODE_CONNECTION_SFTP:
			return sbbs->text[NodeConnectionSFTP];
		case NODE_CONNECTION_RAW:
			return sbbs->text[NodeConnectionRaw];
			snprintf(str,16,sbbs->text[NodeConnectionModem],conn);
/****************************************************************************/
/* Displays the information for node number 'number' contained in 'node'    */
/****************************************************************************/
void sbbs_t::printnodedat(uint number, node_t* node)
{
    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]);
			break;
		case NODE_OFFLINE:
			bputs(text[NodeStatusOffline]);
			break;
		case NODE_NETTING:
			bputs("Networking");	/* obsolete */
			break;
		case NODE_LOGON:
			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;
		case NODE_EVENT_WAITING:
			bputs(text[NodeStatusEventWaiting]);
			break;
		case NODE_EVENT_LIMBO:
			bprintf(text[NodeStatusEventLimbo],node->aux);
			break;
		case NODE_EVENT_RUNNING:
			bputs(text[NodeStatusEventRunning]);
			break;
		case NODE_NEWUSER:
			bputs(text[NodeStatusNewUser]);
			bputs(node_connection_desc(this, node->connection, tmp));
			break;
		case NODE_QUIET:
			if(!SYSOP) {
				bputs(text[NodeStatusWaitingForCall]);
Rob Swindell's avatar
Rob Swindell committed
				break;
		case NODE_INUSE:
			attr(cfg.color[clr_nodeuser]);
			if(node->misc&NODE_ANON && !SYSOP)
			else
				bputs(username(&cfg,node->useron,tmp));
			attr(cfg.color[clr_nodestatus]);
			bputs(" ");
			bputs(node_activity(&cfg, node, tmp, sizeof tmp, number));
			bputs(node_connection_desc(this, node->connection, tmp));
			if(node->action==NODE_DLNG) {
				if(cfg.sys_misc&SM_MILITARY) {
					hour=node->aux/60;
Rob Swindell's avatar
Rob Swindell committed
					mer[0]=0;
				else if((node->aux/60)>=12) {
					if(node->aux/60==12)
						hour=12;
					else
						hour=(node->aux/60)-12;
Rob Swindell's avatar
Rob Swindell committed
					strcpy(mer,"pm");
				else {
					if((node->aux/60)==0)    /* 12 midnite */
						hour=12;
					else hour=node->aux/60;
				bprintf(" ETA %02d:%02d %s"
	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');
	if(SYSOP && ((node->misc
		&(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');
		if(node->status==NODE_QUIET)
			outchar('Q');
	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;
			continue;
		if(!self && i==cfg.node_num)
			continue;
		if(node.status != NODE_INUSE)
			continue;
		count++;
	}
	return count;
}