diff --git a/src/sbbs3/sbbs_status.c b/src/sbbs3/sbbs_status.c index 2dc7b6290b9e21a481dfc43bd376021186dfe0b6..bd2b335034b979b4c6f19c499d0018ea52647ea2 100644 --- a/src/sbbs3/sbbs_status.c +++ b/src/sbbs3/sbbs_status.c @@ -1,10 +1,16 @@ #include <sys/stat.h> +#include <stdarg.h> #include <stdbool.h> #include <stddef.h> #include <stdlib.h> #include <string.h> +#ifdef SBBS +#undef SBBS +#endif + +#include "sbbs.h" #include "sbbs_status.h" #include "genwrap.h" @@ -24,6 +30,39 @@ static link_list_t status_sock; static pthread_once_t init_once = PTHREAD_ONCE_INIT; static pthread_mutex_t status_mutex[SERVICE_COUNT]; static pthread_mutex_t status_thread_mutex; +static sbbs_status_startup_t* startup; +static scfg_t scfg; +static protected_uint32_t active_clients; +static protected_uint32_t thread_count; + +static int lprintf(int level, const char *fmt, ...) +{ + va_list argptr; + char sbuf[1024]; + + va_start(argptr,fmt); + vsnprintf(sbuf,sizeof(sbuf),fmt,argptr); + sbuf[sizeof(sbuf)-1]=0; + va_end(argptr); + + if(level <= LOG_ERR) { + char errmsg[sizeof(sbuf)+16]; + SAFEPRINTF(errmsg, "ftp %s", sbuf); + errorlog(&scfg, startup==NULL ? NULL:startup->host_name, errmsg); + if(startup!=NULL && startup->errormsg!=NULL) + startup->errormsg(startup->cbdata,level,errmsg); + } + + if(startup==NULL || startup->lputs==NULL || level > startup->log_level) + return(0); + +#if defined(_WIN32) + if(IsBadCodePtr((FARPROC)startup->lputs)) + return(0); +#endif + + return startup->lputs(startup->cbdata,level,sbuf); +} static void init_lists(void) { @@ -37,12 +76,34 @@ static void init_lists(void) } } +static void client_on(SOCKET sock, client_t* client, BOOL update) +{ +lprintf(LOG_DEBUG, "User: %s", client->user); +lprintf(LOG_DEBUG, "Prot: %s", client->protocol); + if(startup!=NULL && startup->client_on!=NULL) + startup->client_on(startup->cbdata,TRUE,sock,client,update); +} + +static void client_off(SOCKET sock) +{ + if(startup!=NULL && startup->client_on!=NULL) + startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE); +} + +static void update_clients(void) +{ + if (startup != NULL && startup->clients != NULL) + startup->clients(startup->cbdata, protected_uint32_value(active_clients)); +} + static void sendsmsg(struct sbbs_status_msg *msg) { int ret; list_node_t* node; list_node_t* next; SOCKET *sock; + static link_list_t off_socks; + bool os_init = false; listLock(&status_sock); for(node=status_sock.first; node!=NULL; node=next) { @@ -52,11 +113,26 @@ static void sendsmsg(struct sbbs_status_msg *msg) if (ret == SOCKET_ERROR) { if (ERROR_VALUE != EAGAIN) { closesocket(*sock); - listRemoveNode(&status_sock, node, TRUE); + if (!os_init) { + listInit(&off_socks, 0); + } + listPushNode(&off_socks, sock); + listRemoveNode(&status_sock, node, FALSE); } } } listUnlock(&status_sock); + + if (os_init) { + for (node = off_socks.first; node != NULL; node=next) { + next = node->next; + sock = node->data; + client_off(*sock); + protected_uint32_adjust(&active_clients, -1); + update_clients(); + } + listFree(&off_socks); + } } void status_lputs(enum sbbs_status_service svc, int level, const char *str) @@ -288,6 +364,7 @@ void status_client_on(enum sbbs_status_service svc, BOOL on, SOCKET sock, client listRemoveTaggedNode(&svc_client_list[svc], sock, /* free_data: */TRUE); pthread_mutex_unlock(&status_mutex[svc]); + if (status_sock.count == 0) return; if (client) @@ -329,7 +406,6 @@ void status_thread(void *arg) { SOCKET sock; struct sockaddr_un addr; - char *fname = strdup(arg); socklen_t addrlen; fd_set fd; struct timeval tv; @@ -341,20 +417,77 @@ void status_thread(void *arg) int i; int nb; list_node_t* node; + list_node_t* next; struct stat st; + char error[256]; + client_t client = {0}; + user_t user; + char *p; + int rc = 0; + + startup = arg; + SetThreadName("sbbs/status"); + +#ifdef _THREAD_SUID_BROKEN + if (thread_suid_broken) + startup->seteuid(TRUE); +#endif + + if (startup == NULL) { + sbbs_beep(100, 500); + fprintf(stderr, "No status startup structure passed!\n"); + return; + } + + if (startup->size != sizeof(sbbs_status_startup_t)) { /* verify size */ + sbbs_beep(100, 500); + sbbs_beep(300, 500); + sbbs_beep(100, 500); + fprintf(stderr, "Invalud status strartup structure!\n"); + return; + } + + protected_uint32_init(&thread_count, 0); pthread_once(&init_once, init_lists); - SetThreadName("status"); - //thread_up(TRUE); - if (stat(fname, &st) == 0) { + startup->status(startup->cbdata, "Initializing"); + strcpy(client.addr, startup->sock_fname); + strcpy(client.host, "<unix-domain>"); + client.protocol = "STATUS"; + client.size = sizeof(client); + + memset(&scfg, 0, sizeof(scfg)); + + if (chdir(startup->ctrl_dir) != 0) + lprintf(LOG_ERR, "!ERROR %d changing directory to: %s", errno, startup->ctrl_dir); + + /* Initial configuration and load from CNF files */ + SAFECOPY(scfg.ctrl_dir, startup->ctrl_dir); + lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir); + scfg.size=sizeof(scfg); + SAFECOPY(error,UNKNOWN_LOAD_ERROR); + if(!load_cfg(&scfg, NULL, TRUE, error)) { + lprintf(LOG_CRIT,"!ERROR %s",error); + lprintf(LOG_CRIT,"!Failed to load configuration files"); + return; + } + + if(startup->host_name[0]==0) + SAFECOPY(startup->host_name,scfg.sys_inetaddr); + prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir)); + protected_uint32_init(&active_clients, 0); + update_clients(); + + startup->thread_up(startup->cbdata, TRUE, TRUE); + if (stat(startup->sock_fname, &st) == 0) { if ((st.st_mode & S_IFMT) == S_IFSOCK) - unlink(fname); + unlink(startup->sock_fname); } sock = socket(PF_UNIX, SOCK_SEQPACKET, 0); if (sock == INVALID_SOCKET) return; memset(&addr, 0, sizeof(addr)); - SAFECOPY(addr.sun_path, fname); + SAFECOPY(addr.sun_path, startup->sock_fname); addr.sun_family = AF_UNIX; #ifdef SUN_LEN addrlen = SUN_LEN(&addr); @@ -362,16 +495,19 @@ void status_thread(void *arg) addrlen = offsetof(struct sockaddr_un, un_addr.sun_path) + strlen(addr.sun_path) + 1; #endif if (bind(sock, (void *)&addr, addrlen) != 0) { + lprintf(LOG_ERR, "Unable to bind to %s", addr.sun_path); closesocket(sock); - free(fname); return; } if (listen(sock, 2) != 0) { + lprintf(LOG_ERR, "Unable to listen on %s", addr.sun_path); closesocket(sock); - free(fname); return; } + startup->status(startup->cbdata, "Listening"); + startup->started(startup->cbdata); + pthread_mutex_lock(&status_thread_mutex); while (!status_thread_terminated) { pthread_mutex_unlock(&status_thread_mutex); @@ -381,9 +517,13 @@ void status_thread(void *arg) tv.tv_sec = 1; if (select(sock+1, &fd, NULL, NULL, &tv) == 1) { csock = malloc(sizeof(SOCKET)); - if (csock == NULL) + if (csock == NULL) { + lprintf(LOG_CRIT, "Error allocating memory!"); + rc = 1; break; + } *csock = accept(sock, (void *)&addr, &addrlen); + lprintf(LOG_DEBUG, "accept()ed new stat socket %d", *csock); if (*csock != INVALID_SOCKET) { nb = 1; ioctlsocket(*csock, FIONBIO, &nb); @@ -397,9 +537,83 @@ void status_thread(void *arg) closesocket(*csock); free(csock); pthread_mutex_lock(&status_thread_mutex); + lprintf(LOG_CRIT, "Error recv returned %d (%d)!", len, errno); continue; } // TODO: Check auth... "User\0Pass\0SysPass" + client.user = auth; + user.number = matchuser(&scfg, auth, TRUE); + if (user.number == 0) { + closesocket(*csock); + free(csock); + lprintf(LOG_WARNING, "Invalid username \"%s\"", auth); + pthread_mutex_lock(&status_thread_mutex); + continue; + } + if (getuserdat(&scfg, &user) != 0) { + closesocket(*csock); + free(csock); + lprintf(LOG_WARNING, "Unable to read data for \"%s\"", auth); + pthread_mutex_lock(&status_thread_mutex); + continue; + } + p = strchr(auth, 0); + p++; + if (p-auth >= len) { + closesocket(*csock); + free(csock); + lprintf(LOG_WARNING, "No password specified"); + pthread_mutex_lock(&status_thread_mutex); + continue; + } + if (stricmp(p, user.pass)) { + closesocket(*csock); + free(csock); + if (scfg.sys_misc & SM_ECHO_PW) + lprintf(LOG_WARNING, "Invalid password %s", p); + else + lprintf(LOG_WARNING, "Invalid password"); + pthread_mutex_lock(&status_thread_mutex); + continue; + } + if (user.misc & (DELETED|INACTIVE)) { + closesocket(*csock); + free(csock); + lprintf(LOG_WARNING, "!DELETED or INACTIVE user #%d (%s)", user.number, user.alias); + pthread_mutex_lock(&status_thread_mutex); + continue; + } + if (user.pass[0] == 0) { + closesocket(*csock); + free(csock); + lprintf(LOG_WARNING, "User with empty password #%d (%s)", user.number, user.alias); + pthread_mutex_lock(&status_thread_mutex); + continue; + } + if (user.level < SYSOP_LEVEL) { + closesocket(*csock); + free(csock); + lprintf(LOG_WARNING, "User not SysOp #%d (%s)", user.number, user.alias); + pthread_mutex_lock(&status_thread_mutex); + continue; + } + p = strchr(p, 0); + p++; + if (p-auth >= len) { + closesocket(*csock); + free(csock); + lprintf(LOG_WARNING, "No syspass specified", p); + pthread_mutex_lock(&status_thread_mutex); + continue; + } + if (stricmp(p, scfg.sys_pass)) { + closesocket(*csock); + free(csock); + lprintf(LOG_WARNING, "Invalid syspass %s", p); + pthread_mutex_lock(&status_thread_mutex); + continue; + } + client.time = time(NULL); listLock(&status_sock); listPushNode(&status_sock, csock); for (i=0; i<SERVICE_COUNT; i++) { @@ -448,24 +662,43 @@ void status_thread(void *arg) } // Send current status listUnlock(&status_sock); + client_on(*csock, &client, FALSE); + protected_uint32_adjust(&active_clients, 1); + update_clients(); } else { closesocket(*csock); free(csock); + lprintf(LOG_WARNING, "select() for recv() failed"); pthread_mutex_lock(&status_thread_mutex); continue; } } else { free(csock); + lprintf(LOG_WARNING, "accept() failed"); pthread_mutex_lock(&status_thread_mutex); continue; } } pthread_mutex_lock(&status_thread_mutex); } - unlink(fname); - free(fname); + if (sock != INVALID_SOCKET) + closesocket(sock); + unlink(startup->sock_fname); + listLock(&status_sock); + for(node=status_sock.first; node!=NULL; node=next) { + next = node->next; + csock = node->data; + closesocket(*csock); + listRemoveNode(&status_sock, node, FALSE); + } + listUnlock(&status_sock); + protected_uint32_destroy(thread_count); + protected_uint32_destroy(active_clients); + + startup->thread_up(startup->cbdata, FALSE, FALSE); + startup->terminated(startup->cbdata, rc); } #define makestubs(lower, UPPER) \ @@ -486,3 +719,4 @@ makestubs(term, TERM); makestubs(web, WEB); makestubs(services, SERVICES); makestubs(event, EVENT); +makestubs(status, STATUS); diff --git a/src/sbbs3/sbbs_status.h b/src/sbbs3/sbbs_status.h index acab708479fa51ffaa844f11359495cd8a879e96..9926ec74f4f46b70fa9a4392e475f5420f63d383 100644 --- a/src/sbbs3/sbbs_status.h +++ b/src/sbbs3/sbbs_status.h @@ -3,6 +3,7 @@ #include "client.h" #include "gen_defs.h" +#include "dirwrap.h" enum sbbs_status_service { SERVICE_FTP, @@ -11,6 +12,7 @@ enum sbbs_status_service { SERVICE_WEB, SERVICE_SERVICES, SERVICE_EVENT, + SERVICE_STATUS, SERVICE_COUNT }; @@ -25,7 +27,7 @@ enum sbbs_status_type { STATUS_THREAD_UP, STATUS_SOCKET_OPEN, STATUS_CLIENT_ON, - STATUS_STATS + STATUS_STATS, }; struct sbbs_status_msg_hdr { @@ -87,6 +89,26 @@ struct sbbs_status_msg { } msg; }; +typedef struct sbbs_status_startup { + size_t size; + void *cbdata; + char sock_fname[MAX_PATH+1]; + char host_name[128]; + char ctrl_dir[128]; + int log_level; + void (*thread_up)(void*, BOOL up, BOOL setuid); + void (*socket_open)(void*, BOOL open); + void (*errormsg)(void*, int level, const char* msg); + void (*client_on)(void*, BOOL on, int sock, client_t*, BOOL update); + void (*started)(void*); + void (*terminated)(void*, int code); + void (*clients)(void*, int active); + void (*status)(void*, const char*); + int (*lputs)(void*, int level, const char* msg); + BOOL (*seteuid)(BOOL user); + BOOL (*setuid)(BOOL force); +} sbbs_status_startup_t; + void status_lputs(enum sbbs_status_service svc, int level, const char *str); void status_errormsg(enum sbbs_status_service svc, int level, const char *str); void status_status(enum sbbs_status_service svc, const char *str); @@ -118,6 +140,7 @@ makestubs(term); makestubs(web); makestubs(services); makestubs(event); +makestubs(status); #undef makestubs #endif