sbbscon.c 63.6 KB
Newer Older
rswindell's avatar
rswindell committed
1 2
/* Synchronet vanilla/console-mode "front-end" */

Rob Swindell's avatar
Rob Swindell committed
3
/* $Id: sbbscon.c,v 1.282 2020/08/17 00:48:28 rswindell Exp $ */
4
// vi: tabstop=4
rswindell's avatar
rswindell committed
5 6 7 8 9

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
rswindell's avatar
rswindell committed
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 *																			*
 * 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										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

37
#if defined USE_LINUX_CAPS && !defined _GNU_SOURCE
sbbs's avatar
sbbs committed
38 39 40
#define _GNU_SOURCE
#endif

41
/* ANSI headers */
42
#include <stdio.h>
43
#include <stdbool.h>
44 45 46
#include <string.h>
#include <signal.h>
#include <ctype.h>
deuce's avatar
deuce committed
47 48 49
#ifdef __QNX__
#include <locale.h>
#endif
50

rswindell's avatar
rswindell committed
51
/* Synchronet-specific headers */
52
#undef SBBS	/* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
53 54
#include "sbbs.h"		/* load_cfg() */
#include "sbbs_ini.h"	/* sbbs_read_ini() */
rswindell's avatar
rswindell committed
55 56
#include "ftpsrvr.h"	/* ftp_startup_t, ftp_server */
#include "mailsrvr.h"	/* mail_startup_t, mail_server */
57
#include "services.h"	/* services_startup_t, services_thread */
rswindell's avatar
rswindell committed
58

59 60 61 62
/* XPDEV headers */
#include "conwrap.h"	/* kbhit/getch */
#include "threadwrap.h"	/* pthread_mutex_t */

63
#ifdef __unix__
64
#include "sbbs_status.h"
65

66 67
#ifdef USE_LINUX_CAPS
#include <sys/capability.h>
sbbs's avatar
sbbs committed
68
#include <sys/prctl.h>
69 70
#endif

71 72 73
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
74
#include <grp.h>
75 76
#include <stdarg.h>
#include <stdlib.h>  /* Is this included from somewhere else? */
77
#include <syslog.h>
78

79 80
#endif

rswindell's avatar
rswindell committed
81
/* Global variables */
82 83
BOOL				terminated=FALSE;

84
BOOL				run_bbs=FALSE;
85
BOOL				bbs_running=FALSE;
86
BOOL				bbs_stopped=FALSE;
87
BOOL				has_bbs=FALSE;
88
bbs_startup_t		bbs_startup;
89
BOOL				run_ftp=FALSE;
90
BOOL				ftp_running=FALSE;
91
BOOL				ftp_stopped=FALSE;
92
BOOL				has_ftp=FALSE;
93
ftp_startup_t		ftp_startup;
94
BOOL				run_mail=FALSE;
95
BOOL				mail_running=FALSE;
96
BOOL				mail_stopped=FALSE;
97
BOOL				has_mail=FALSE;
98
mail_startup_t		mail_startup;
99
BOOL				run_services=FALSE;
100
BOOL				services_running=FALSE;
101
BOOL				services_stopped=FALSE;
102
BOOL				has_services=FALSE;
103
services_startup_t	services_startup;
104
BOOL				run_web=FALSE;
rswindell's avatar
rswindell committed
105 106
BOOL				web_running=FALSE;
BOOL				web_stopped=FALSE;
107
BOOL				has_web=FALSE;
rswindell's avatar
rswindell committed
108
web_startup_t		web_startup;
109 110 111
ulong				thread_count=1;
ulong				socket_count=0;
ulong				error_count=0;
112
int					prompt_len=0;
113 114
static scfg_t		scfg;					/* To allow rerun */
static ulong		served=0;
115
char				ini_file[MAX_PATH+1];
116
link_list_t			login_attempt_list;
117
link_list_t			client_list;
118

119
BOOL				status_running=FALSE;
120
#ifdef __unix__
121 122
sbbs_status_startup_t	status_startup;
BOOL				status_stopped=FALSE;
123 124
char				new_uid_name[32];
char				new_gid_name[32];
125 126 127 128
uid_t				new_uid;
uid_t				old_uid;
gid_t				new_gid;
gid_t				old_gid;
129
BOOL				is_daemon=FALSE;
130 131
char				log_facility[2];
char				log_ident[128];
132
BOOL				std_facilities=FALSE;
133
FILE *				pidf;
134
char				pid_fname[MAX_PATH+1];
135
BOOL                capabilities_set=FALSE;
rswindell's avatar
rswindell committed
136
BOOL				syslog_always=FALSE;
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156

#ifdef USE_LINUX_CAPS
/*
 * If the value of PR_SET_KEEPCAPS is not in <sys/prctl.h>, define it
 * here.  This allows setuid() to work on systems running a new enough
 * kernel but with /usr/include/linux pointing to "standard" kernel
 * headers.
 */
#ifndef PR_SET_KEEPCAPS
#define PR_SET_KEEPCAPS 8
#endif

#ifndef SYS_capset
#ifndef __NR_capset
#include <asm/unistd.h> /* Slackware 4.0 needs this. */
#endif
#define SYS_capset __NR_capset
#endif
#endif /* USE_LINUX_CAPS */

157
#endif
158

159
static const char* prompt;
160

161 162 163 164 165
static const char* usage  = "\nusage: %s [[cmd | setting] [...]] [path/ini_file]\n"
							"\n"
							"Commands:\n"
							"\n"
							"\tversion    show version/revision details and exit\n"
166
							"\n"
167 168 169 170 171 172 173 174 175 176 177
							"Global settings:\n"
							"\n"
							"\thn[host]   set hostname for this instance\n"
							"\t           if host not specified, uses gethostname\n"
#ifdef __unix__
							"\tun<user>   set username for BBS to run as\n"
							"\tug<group>  set group for BBS to run as\n"
							"\td[x]       run as daemon, log using syslog\n"
							"\t           x is the optional LOCALx facility to use\n"
							"\t           if none is specified, uses USER\n"
							"\t           if 'S' is specified, uses standard facilities\n"
rswindell's avatar
rswindell committed
178
							"\tsyslog     log to syslog (even when not daemonized)\n"
179 180 181 182 183 184 185 186 187 188 189 190
#endif
							"\tgi         get user identity (using IDENT protocol)\n"
							"\tnh         disable hostname lookups\n"
							"\tnj         disable JavaScript support\n"
							"\tne         disable event thread\n"
							"\tni         do not read settings from .ini file\n"
#ifdef __unix__
							"\tnd         do not read run as daemon - overrides .ini file\n"
#endif
							"\tdefaults   show default settings and options\n"
							"\n"
							;
191 192 193
static const char* telnet_usage  = "Terminal server settings:\n\n"
							"\ttf<node>   set first node number\n"
							"\ttl<node>   set last node number\n"
194 195
							"\ttp<port>   set Telnet server port\n"
							"\trp<port>   set RLogin server port (and enable RLogin server)\n"
196
							"\tto<value>  set Terminal server options value (advanced)\n"
197 198 199
							"\tta         enable auto-logon via IP address\n"
							"\ttd         enable Telnet command debug output\n"
							"\ttq         disable QWK events\n"
200
							"\tt-         disable Terminal server\n"
201 202
							;
static const char* ftp_usage  = "FTP server settings:\n"
203 204
							"\n"
							"\tfp<port>   set FTP server port\n"
205
							"\tfo<value>  set FTP server options value (advanced)\n"
206
							"\tf-         disable FTP server\n"
207 208
							;
static const char* mail_usage  = "Mail server settings:\n"
209
							"\n"
210 211
							"\tms<port>   set SMTP server port\n"
							"\tmp<port>   set POP3 server port\n"
212 213
							"\tmr<addr>   set SMTP relay server (and enable SMTP relay)\n"
							"\tmd<addr>   set DNS server address for MX-record lookups\n"
214
							"\tmo<value>  set Mail server options value (advanced)\n"
215
							"\tma         allow SMTP relays from authenticated users\n"
216 217 218
							"\tm-         disable Mail server (entirely)\n"
							"\tmp-        disable POP3 server\n"
							"\tms-        disable SendMail thread\n"
219 220
							;
static const char* services_usage  = "Services settings:\n"
221 222 223
							"\n"
							"\tso<value>  set Services option value (advanced)\n"
							"\ts-         disable Services (no services module)\n"
224
							;
deuce's avatar
deuce committed
225 226 227 228
static const char* web_usage  = "Web server settings:\n"
							"\n"
							"\twp<port>   set HTTP server port\n"
							"\two<value>  set Web server option value (advanced)\n"
229
							"\tw-         disable Web server\n"
deuce's avatar
deuce committed
230
							;
231

232
static int lputs(int level, char *str)
233 234
{
	static pthread_mutex_t mutex;
235
	static BOOL mutex_initialized;
236
	char	*p;
237

238
#ifdef __unix__
239

240
	if (is_daemon)  {
241
		if(str!=NULL) {
242
			if (std_facilities)
243
				syslog(level|LOG_AUTH,"%s",str);
244
			else
245
				syslog(level,"%s",str);
246
		}
247
		return(0);
248
	}
249
#endif
250 251 252 253 254 255 256
	if(!mutex_initialized) {
		pthread_mutex_init(&mutex,NULL);
		mutex_initialized=TRUE;
	}
	pthread_mutex_lock(&mutex);
	/* erase prompt */
	printf("\r%*s\r",prompt_len,"");
257 258
	if(str!=NULL) {
		for(p=str; *p; p++) {
259
			if(iscntrl((unsigned char)*p))
260 261 262 263 264 265
				printf("^%c",'@'+*p);
			else
				printf("%c",*p);
		}
		puts("");
	}
266
	/* re-display prompt with current stats */
267
	if(prompt!=NULL)
268
		prompt_len = printf(prompt, thread_count, socket_count, client_list.count, served, error_count);
269 270
	fflush(stdout);
	pthread_mutex_unlock(&mutex);
271 272

    return(prompt_len);
273 274
}

275
static void errormsg(void *cbdata, int level, const char *msg)
276
{
277 278 279 280 281 282 283 284 285 286 287 288 289 290
#ifdef __unix__
	if(cbdata==&bbs_startup)
		status_errormsg(SERVICE_TERM, level, msg);
	else if(cbdata==&ftp_startup)
		status_errormsg(SERVICE_FTP, level, msg);
	else if(cbdata==&web_startup)
		status_errormsg(SERVICE_WEB, level, msg);
	else if(cbdata==&mail_startup)
		status_errormsg(SERVICE_MAIL, level, msg);
	else if(cbdata==&services_startup)
		status_errormsg(SERVICE_SERVICES, level, msg);
	else if(cbdata==&status_startup)
		status_errormsg(SERVICE_STATUS, level, msg);
#endif
291 292 293
	error_count++;
}

294
static int lprintf(int level, const char *fmt, ...)
295 296 297 298 299 300 301 302 303 304 305
{
	va_list argptr;
	char sbuf[1024];

    va_start(argptr,fmt);
    vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
	sbuf[sizeof(sbuf)-1]=0;
    va_end(argptr);
    return(lputs(level,sbuf));
}

306
#ifdef __unix__
307
static pthread_mutex_t setid_mutex;
308
static BOOL setid_mutex_initialized=0;
309 310 311
/**********************************************************
* Change uid of the calling process to the user if specified
* **********************************************************/
312
static BOOL do_seteuid(BOOL to_new)
313
{
314
	BOOL	result=FALSE;
315

316 317
    if(capabilities_set)
	    return(TRUE);		/* do nothing */
318

319 320
    if(new_uid_name[0]==0)	/* not set? */
	    return(TRUE);		/* do nothing */
321

322 323 324
	if(old_uid==new_uid && old_gid==new_gid)
		return(TRUE);		/* do nothing */

325 326 327
	if(!setid_mutex_initialized) {
		pthread_mutex_init(&setid_mutex,NULL);
		setid_mutex_initialized=TRUE;
328 329
	}

330
	pthread_mutex_lock(&setid_mutex);
331

332
	if(to_new) {
333 334
		if((new_gid==getegid() || setregid(-1,new_gid)==0)
				&& (new_uid==geteuid() || setreuid(-1,new_uid)==0))
335 336 337
			result=TRUE;
		else
			result=FALSE;
338 339
	}
	else {
340 341
		if((old_gid==getegid() || setregid(-1,old_gid)==0)
				&& (old_uid==geteuid() || setreuid(-1,old_uid)==0))
342
			result=TRUE;
343 344
		else
			result=FALSE;
345
	}
346

347
	pthread_mutex_unlock(&setid_mutex);
348

349
	if(!result) {
350 351
		lputs(LOG_ERR,"!seteuid FAILED");
		lputs(LOG_ERR,strerror(errno));
352
	}
353
	return result;
354
}
355 356 357 358

/**********************************************************
* Change uid of the calling process to the user if specified
* **********************************************************/
359
BOOL do_setuid(BOOL force)
360
{
361
	BOOL result=TRUE;
362
#if defined(DONT_BLAME_SYNCHRONET)
363 364 365
	if(!force)
		return(do_seteuid(TRUE));
#endif
366

367 368 369 370 371
#if defined(_THREAD_SUID_BROKEN)
	if(thread_suid_broken && (!force))
		return(do_seteuid(TRUE));
#endif

372 373 374
	if(old_uid==new_uid && old_gid==new_gid)
		return(TRUE);		/* do nothing */

375 376 377 378 379 380 381
	if(!setid_mutex_initialized) {
		pthread_mutex_init(&setid_mutex,NULL);
		setid_mutex_initialized=TRUE;
	}

	pthread_mutex_lock(&setid_mutex);

382 383 384 385 386 387 388 389 390 391 392
	if(getegid()!=old_gid)
		setregid(-1,old_gid);
	if(geteuid()!=old_gid)
		setreuid(-1,old_uid);
	if(getgid() != new_gid || getegid() != new_gid) {
		if(setregid(new_gid,new_gid))
		{
			lputs(LOG_ERR,"!setgid FAILED");
			lputs(LOG_ERR,strerror(errno));
			result=FALSE;
		}
393
	}
394

395
	if(getuid() != new_uid || geteuid() != new_uid) {
396 397 398 399
		if(initgroups(new_uid_name, new_gid)) {
			lputs(LOG_ERR,"!initgroups FAILED");
			lputs(LOG_ERR,strerror(errno));
			result=FALSE;
400
		}
401 402 403 404 405 406
		if(setreuid(new_uid,new_uid))
		{
			lputs(LOG_ERR,"!setuid FAILED");
			lputs(LOG_ERR,strerror(errno));
			result=FALSE;
		}
407
	}
408 409 410

	pthread_mutex_unlock(&setid_mutex);

411 412 413 414
	if(force && (!result))
		exit(1);

	return(result);
415
}
416

417
bool change_user(void)
418 419 420
{
    if(!do_setuid(FALSE)) {
        /* actually try to change the uid of this process */
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
        lputs(LOG_ERR,"!Setting new user-id failed!  (Does the user exist?)");
        return false;
	}
	struct passwd *pwent;

	pwent=getpwnam(new_uid_name);
	if(pwent == NULL)
		lprintf(LOG_WARNING, "No password name for: %s", new_uid_name);
	else {
		static char	uenv[128];
		static char	henv[MAX_PATH+6];
		sprintf(uenv,"USER=%s",pwent->pw_name);
		putenv(uenv);
		sprintf(henv,"HOME=%s",pwent->pw_dir);
		putenv(henv);
	}
	if(new_gid_name[0]) {
		static char	genv[128];
		sprintf(genv,"GROUP=%s",new_gid_name);
		putenv(genv);
	}
	lprintf(LOG_INFO,"Successfully changed user-id to %s", new_uid_name);
	return true;
444 445 446 447 448 449 450 451 452 453 454
}

#ifdef USE_LINUX_CAPS
/**********************************************************
* Set system capabilities on Linux.  Allows non root user
* to make calls to bind
* **********************************************************/
void whoami(void)
{
    uid_t a, b, c;
    getresuid(&a, &b, &c);
455
    lprintf(LOG_DEBUG,"Current usr ids: ruid - %d, euid - %d, suid - %d", a, b, c);
456
    getresgid(&a, &b, &c);
457
    lprintf(LOG_DEBUG,"Current grp ids: rgid - %d, egid - %d, sgid - %d", a, b, c);
458 459
}

460
bool list_caps(void)
461 462
{
    cap_t caps = cap_get_proc();
463 464
    if(caps == NULL)
	    return false;
465
    ssize_t y = 0;
rswindell's avatar
rswindell committed
466
    lprintf(LOG_DEBUG, "The process %d was given capabilities %s", (int) getpid(), cap_to_text(caps, &y));
467
    cap_free(caps);
468
    return true;
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486
}

static int linux_keepcaps(void)
{
	/*
	 * Ask the kernel to allow us to keep our capabilities after we
	 * setuid().
	 */
	if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
		if (errno != EINVAL) {
			lputs(LOG_ERR,"linux_keepcaps FAILED");
			lputs(LOG_ERR,strerror(errno));
		}
        return(-1);
	}
    return(0);
}

487
static bool linux_setcaps(unsigned int caps)
488 489 490
{
    struct __user_cap_header_struct caphead;
    struct __user_cap_data_struct cap;
491

492 493 494 495 496 497 498
    memset(&caphead, 0, sizeof(caphead));
    caphead.version = _LINUX_CAPABILITY_VERSION;
    caphead.pid = 0;
    memset(&cap, 0, sizeof(cap));
    cap.effective = caps;
    cap.permitted = caps;
    cap.inheritable = 0;
499 500 501 502 503 504 505
    int ret = syscall(SYS_capset, &caphead, &cap);
    if (ret == 0)
	    return true;
    lprintf(LOG_ERR, "linux_setcaps(0x%x) failed (errno %d: %s)"
		, caps, errno, strerror(errno));

    return false;
506 507
}

508
static bool linux_initialprivs(void)
509 510 511 512 513 514 515 516 517 518
{
    unsigned int caps;

    caps = 0;
    caps |= (1 << CAP_NET_BIND_SERVICE);
    caps |= (1 << CAP_SETUID);
    caps |= (1 << CAP_SETGID);
    caps |= (1 << CAP_DAC_READ_SEARCH);
    caps |= (1 << CAP_SYS_RESOURCE);
    printf("Setting initial privileges\n");
519
    return linux_setcaps(caps);
520 521
}

522
static bool linux_minprivs(void)
523 524 525 526 527 528 529
{
    unsigned int caps;

    caps = 0;
    caps |= (1 << CAP_NET_BIND_SERVICE);
    caps |= (1 << CAP_SYS_RESOURCE);
    printf("Setting minimum privileges\n");
530
    return linux_setcaps(caps);
531 532 533 534 535 536
}
/**********************************************************
* End capabilities section
* **********************************************************/
#endif /* USE_LINUX_CAPS */

537 538
#endif   /* __unix__ */

539 540
#ifdef _WINSOCKAPI_

541
static WSADATA WSAData;
542 543 544 545 546 547 548 549

static BOOL winsock_startup(void)
{
	int		status;             /* Status Code */

    if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0)
		return(TRUE);

550
    lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
551 552 553
	return(FALSE);
}

554
static BOOL winsock_cleanup(void)
555 556 557 558
{
	if(WSACleanup()==0)
		return(TRUE);

559
	lprintf(LOG_ERR,"!WinSock cleanup ERROR %d",ERROR_VALUE);
560 561 562 563 564
	return(FALSE);
}

#else /* No WINSOCK */

565
#define winsock_startup()	(TRUE)
566 567 568 569
#define winsock_cleanup()	(TRUE)

#endif

570
static void thread_up(void* p, BOOL up, BOOL setuid)
571
{
572 573 574
   	static pthread_mutex_t mutex;
	static BOOL mutex_initialized;

575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
#ifdef __unix__
	if(p==&bbs_startup)
		status_thread_up(SERVICE_TERM, up, setuid);
	else if(p==&ftp_startup)
		status_thread_up(SERVICE_FTP, up, setuid);
	else if(p==&web_startup)
		status_thread_up(SERVICE_WEB, up, setuid);
	else if(p==&mail_startup)
		status_thread_up(SERVICE_MAIL, up, setuid);
	else if(p==&services_startup)
		status_thread_up(SERVICE_SERVICES, up, setuid);
	else if(p==&status_startup)
		status_thread_up(SERVICE_STATUS, up, setuid);
#endif

590 591 592 593 594 595 596
#ifdef _THREAD_SUID_BROKEN
	if(thread_suid_broken && up && setuid) {
		do_seteuid(FALSE);
		do_setuid(FALSE);
	}
#endif

597 598 599 600 601 602 603
	if(!mutex_initialized) {
		pthread_mutex_init(&mutex,NULL);
		mutex_initialized=TRUE;
	}

	pthread_mutex_lock(&mutex);

604
	if(up)
605 606
	    thread_count++;
    else if(thread_count>0)
607
		thread_count--;
608
	pthread_mutex_unlock(&mutex);
609
	lputs(LOG_INFO,NULL); /* update displayed stats */
610 611
}

612
static void socket_open(void* p, BOOL open)
613 614 615 616 617 618 619
{
   	static pthread_mutex_t mutex;
	static BOOL mutex_initialized;

	if(!mutex_initialized) {
		pthread_mutex_init(&mutex,NULL);
		mutex_initialized=TRUE;
620
	}
621

622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
#ifdef __unix__
	if(p==&bbs_startup)
		status_socket_open(SERVICE_TERM, open);
	else if(p==&ftp_startup)
		status_socket_open(SERVICE_FTP, open);
	else if(p==&web_startup)
		status_socket_open(SERVICE_WEB, open);
	else if(p==&mail_startup)
		status_socket_open(SERVICE_MAIL, open);
	else if(p==&services_startup)
		status_socket_open(SERVICE_SERVICES, open);
	else if(p==&status_startup)
		status_socket_open(SERVICE_STATUS, open);
#endif

637 638 639
	pthread_mutex_lock(&mutex);
	if(open)
	    socket_count++;
640
	else if(socket_count>0)
641 642
    	socket_count--;
	pthread_mutex_unlock(&mutex);
643
	lputs(LOG_INFO,NULL); /* update displayed stats */
644 645
}

646
static void client_on(void* p, BOOL on, int sock, client_t* client, BOOL update)
647
{
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
#ifdef __unix__
	if(p==&bbs_startup)
		status_client_on(SERVICE_TERM, on, sock, client, update);
	else if(p==&ftp_startup)
		status_client_on(SERVICE_FTP, on, sock, client, update);
	else if(p==&web_startup)
		status_client_on(SERVICE_WEB, on, sock, client, update);
	else if(p==&mail_startup)
		status_client_on(SERVICE_MAIL, on, sock, client, update);
	else if(p==&services_startup)
		status_client_on(SERVICE_SERVICES, on, sock, client, update);
	else if(p==&status_startup)
		status_client_on(SERVICE_STATUS, on, sock, client, update);
#endif

663 664 665 666 667 668 669 670 671 672 673 674 675 676
	if(on) {
		if(update) {
			list_node_t*	node;

			listLock(&client_list);
			if((node=listFindTaggedNode(&client_list, sock)) != NULL)
				memcpy(node->data, client, sizeof(client_t));
			listUnlock(&client_list);
		} else {
			served++;
			listAddNodeData(&client_list, client, sizeof(client_t), sock, LAST_NODE);
		}
	} else
		listRemoveTaggedNode(&client_list, sock, /* free_data: */TRUE);
677

678
	lputs(LOG_INFO,NULL); /* update displayed stats */
679 680
}

rswindell's avatar
rswindell committed
681 682 683
/****************************************************************************/
/* BBS local/log print routine												*/
/****************************************************************************/
684
static int bbs_lputs(void* p, int level, const char *str)
rswindell's avatar
rswindell committed
685 686 687 688
{
	char		logline[512];
	char		tstr[64];
	time_t		t;
689
	struct tm	tm;
rswindell's avatar
rswindell committed
690

691
	if(level > bbs_startup.log_level)
692 693
		return(0);

694
#ifdef __unix__
695
	status_lputs(SERVICE_TERM, level, str);
rswindell's avatar
rswindell committed
696
	if (is_daemon || syslog_always)  {
697 698
		if(str==NULL)
			return(0);
699
		if (std_facilities)
700
			syslog(level|LOG_AUTH,"%s",str);
701
		else
702
			syslog(level,"term %s",str);
rswindell's avatar
rswindell committed
703 704
		if(is_daemon)
			return(strlen(str));
705 706 707
	}
#endif

708
	t=time(NULL);
709
	if(localtime_r(&t,&tm)==NULL)
710 711 712
		tstr[0]=0;
	else
		sprintf(tstr,"%d/%d %02d:%02d:%02d "
713 714
			,tm.tm_mon+1,tm.tm_mday
			,tm.tm_hour,tm.tm_min,tm.tm_sec);
rswindell's avatar
rswindell committed
715

716
	sprintf(logline,"%sterm %.*s",tstr,(int)sizeof(logline)-70,str);
rswindell's avatar
rswindell committed
717
	truncsp(logline);
718
	lputs(level,logline);
719

rswindell's avatar
rswindell committed
720 721 722
    return(strlen(logline)+1);
}

723
static void bbs_started(void* p)
rswindell's avatar
rswindell committed
724
{
725 726 727
#ifdef __unix__
	status_started(SERVICE_TERM);
#endif
rswindell's avatar
rswindell committed
728
	bbs_running=TRUE;
729
	bbs_stopped=FALSE;
730
#ifdef _THREAD_SUID_BROKEN
731 732 733 734
        if(thread_suid_broken) {
            do_seteuid(FALSE);
            do_setuid(FALSE);
        }
735
#endif
rswindell's avatar
rswindell committed
736 737
}

738
static void bbs_terminated(void* p, int code)
rswindell's avatar
rswindell committed
739
{
740 741 742
#ifdef __unix__
	status_terminated(SERVICE_TERM, code);
#endif
rswindell's avatar
rswindell committed
743
	bbs_running=FALSE;
744
	bbs_stopped=TRUE;
rswindell's avatar
rswindell committed
745 746
}

747 748 749 750 751 752 753 754 755 756 757 758 759 760
/****************************************************************************/
/* Status local/log print routine										*/
/****************************************************************************/
#ifdef __unix__
static int stat_lputs(void* p, int level, const char *str)
{
	char		logline[512];
	char		tstr[64];
	time_t		t;
	struct tm	tm;

	if(level > bbs_startup.log_level)
		return(0);

761
	status_lputs(SERVICE_STATUS, level, str);
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
	if (is_daemon || syslog_always)  {
		if(str==NULL)
			return(0);
		if (std_facilities)
			syslog(level|LOG_AUTH,"%s",str);
		else
			syslog(level,"stat %s",str);
		if(is_daemon)
			return(strlen(str));
	}

	t=time(NULL);
	if(localtime_r(&t,&tm)==NULL)
		tstr[0]=0;
	else
		sprintf(tstr,"%d/%d %02d:%02d:%02d "
			,tm.tm_mon+1,tm.tm_mday
			,tm.tm_hour,tm.tm_min,tm.tm_sec);

781
	sprintf(logline,"%sstat %.*s",tstr,(int)sizeof(logline)-70,str);
782 783
	truncsp(logline);
	lputs(level,logline);
784

785 786 787 788 789
	return(strlen(logline)+1);
}

static void stat_started(void* p)
{
790
	status_started(SERVICE_STATUS);
791 792 793 794 795 796 797 798 799 800 801 802
	status_running=TRUE;
	status_stopped=FALSE;
#ifdef _THREAD_SUID_BROKEN
        if(thread_suid_broken) {
            do_seteuid(FALSE);
            do_setuid(FALSE);
        }
#endif
}

static void stat_terminated(void* p, int code)
{
803
	status_terminated(SERVICE_STATUS, code);
804 805 806 807 808
	status_running=FALSE;
	status_stopped=TRUE;
}
#endif

rswindell's avatar
rswindell committed
809 810 811
/****************************************************************************/
/* FTP local/log print routine												*/
/****************************************************************************/
812
static int ftp_lputs(void* p, int level, const char *str)
rswindell's avatar
rswindell committed
813 814 815 816
{
	char		logline[512];
	char		tstr[64];
	time_t		t;
817
	struct tm	tm;
rswindell's avatar
rswindell committed
818

819
	if(level > ftp_startup.log_level)
820 821
		return(0);

822
#ifdef __unix__
823
	status_lputs(SERVICE_FTP, level, str);
rswindell's avatar
rswindell committed
824
	if (is_daemon || syslog_always)  {
825 826
		if(str==NULL)
			return(0);
827
		if (std_facilities)
828
#ifdef __solaris__
829
			syslog(level|LOG_DAEMON,"%s",str);
830
#else
831
			syslog(level|LOG_FTP,"%s",str);
832
#endif
833
		else
834
			syslog(level,"ftp  %s",str);
rswindell's avatar
rswindell committed
835 836
		if(is_daemon)
			return(strlen(str));
837 838 839
	}
#endif

840
	t=time(NULL);
841
	if(localtime_r(&t,&tm)==NULL)
842 843 844
		tstr[0]=0;
	else
		sprintf(tstr,"%d/%d %02d:%02d:%02d "
845 846
			,tm.tm_mon+1,tm.tm_mday
			,tm.tm_hour,tm.tm_min,tm.tm_sec);
rswindell's avatar
rswindell committed
847

848
	sprintf(logline,"%sftp  %.*s",tstr,(int)sizeof(logline)-70,str);
rswindell's avatar
rswindell committed
849
	truncsp(logline);
850
	lputs(level,logline);
851

rswindell's avatar
rswindell committed
852 853 854
    return(strlen(logline)+1);
}

855
static void ftp_started(void* p)
rswindell's avatar
rswindell committed
856
{
857 858 859
#ifdef __unix__
	status_started(SERVICE_FTP);
#endif
rswindell's avatar
rswindell committed
860
	ftp_running=TRUE;
861
	ftp_stopped=FALSE;
862 863 864 865 866 867
#ifdef _THREAD_SUID_BROKEN
	if(thread_suid_broken) {
		do_seteuid(FALSE);
		do_setuid(FALSE);
	}
#endif
rswindell's avatar
rswindell committed
868 869
}

870
static void ftp_terminated(void* p, int code)
rswindell's avatar
rswindell committed
871
{
872 873 874
#ifdef __unix__
	status_terminated(SERVICE_FTP, code);
#endif
rswindell's avatar
rswindell committed
875
	ftp_running=FALSE;
876
	ftp_stopped=TRUE;
rswindell's avatar
rswindell committed
877 878 879 880 881
}

/****************************************************************************/
/* Mail Server local/log print routine										*/
/****************************************************************************/
882
static int mail_lputs(void* p, int level, const char *str)
rswindell's avatar
rswindell committed
883 884 885 886
{
	char		logline[512];
	char		tstr[64];
	time_t		t;
887
	struct tm	tm;
rswindell's avatar
rswindell committed
888

889
	if(level > mail_startup.log_level)
890 891
		return(0);

892
#ifdef __unix__
893
	status_lputs(SERVICE_MAIL, level, str);
rswindell's avatar
rswindell committed
894
	if (is_daemon || syslog_always)  {
895 896
		if(str==NULL)
			return(0);
897
		if (std_facilities)
898
			syslog(level|LOG_MAIL,"%s",str);
899
		else
900
			syslog(level,"mail %s",str);
rswindell's avatar
rswindell committed
901 902
		if(is_daemon)
			return(strlen(str));
903 904 905
	}
#endif

906
	t=time(NULL);
907
	if(localtime_r(&t,&tm)==NULL)
908 909 910
		tstr[0]=0;
	else
		sprintf(tstr,"%d/%d %02d:%02d:%02d "
911 912
			,tm.tm_mon+1,tm.tm_mday
			,tm.tm_hour,tm.tm_min,tm.tm_sec);
rswindell's avatar
rswindell committed
913

914
	sprintf(logline,"%smail %.*s",tstr,(int)sizeof(logline)-70,str);
rswindell's avatar
rswindell committed
915
	truncsp(logline);
916
	lputs(level,logline);
917

rswindell's avatar
rswindell committed
918 919 920
    return(strlen(logline)+1);
}

921
static void mail_started(void* p)
rswindell's avatar
rswindell committed
922
{
923 924 925
#ifdef __unix__
	status_started(SERVICE_MAIL);
#endif
rswindell's avatar
rswindell committed
926
	mail_running=TRUE;
927
	mail_stopped=FALSE;
928 929 930 931 932 933
#ifdef _THREAD_SUID_BROKEN
	if(thread_suid_broken) {
		do_seteuid(FALSE);
		do_setuid(FALSE);
	}
#endif
rswindell's avatar
rswindell committed
934 935
}

936
static void mail_terminated(void* p, int code)
rswindell's avatar
rswindell committed
937
{
938 939 940
#ifdef __unix__
	status_terminated(SERVICE_MAIL, code);
#endif
rswindell's avatar
rswindell committed
941
	mail_running=FALSE;
942
	mail_stopped=TRUE;
rswindell's avatar
rswindell committed
943 944
}

945 946 947
/****************************************************************************/
/* Services local/log print routine											*/
/****************************************************************************/
948
static int services_lputs(void* p, int level, const char *str)
949 950 951 952
{
	char		logline[512];
	char		tstr[64];
	time_t		t;
953
	struct tm	tm;
954

955
	if(level > services_startup.log_level)
956 957
		return(0);

958
#ifdef __unix__
959
	status_lputs(SERVICE_SERVICES, level, str);
rswindell's avatar
rswindell committed
960
	if (is_daemon || syslog_always)  {
961 962
		if(str==NULL)
			return(0);
963
		if (std_facilities)
964
			syslog(level|LOG_DAEMON,"%s",str);
965
		else
966
			syslog(level,"srvc %s",str);
rswindell's avatar
rswindell committed
967 968
		if(is_daemon)
			return(strlen(str));
969 970 971
	}
#endif

972
	t=time(NULL);