diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c
index ee361201010a865d1b33e0cfaae9edc3d9d73066..7507d28f447a828a960b3bac1241149726540d26 100644
--- a/src/sbbs3/services.c
+++ b/src/sbbs3/services.c
@@ -152,6 +152,25 @@ static int lprintf(int level, const char *fmt, ...)
     return lputs(level, sbuf);
 }
 
+#if defined(__GNUC__)   // Catch printf-format errors with errprintf
+static int errprintf(int level, int line, const char* function, const char* file, const char *fmt, ...) __attribute__ ((format (printf, 5, 6)));
+#endif
+static int errprintf(int level, int line, const char* function, const char* file, 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(repeated_error(line, function)) {
+		if(level < LOG_WARNING)
+			level = LOG_WARNING;
+	}
+	return lputs(level, sbuf);
+}
+
 #ifdef _WINSOCKAPI_
 
 static WSADATA WSAData;
@@ -477,14 +496,14 @@ js_login(JSContext *cx, uintN argc, jsval *arglist)
 
 	int result = putuserdat(&scfg,&client->user);
 	if(result != 0) {
-		lprintf(LOG_ERR, "%04d %s !Error %d writing user data for user #%d"
+		errprintf(LOG_ERR, WHERE, "%04d %s !Error %d writing user data for user #%d"
 			,client->socket,client->service->protocol
 			,result, client->user.number);
 	}
 	if(client->subscan==NULL) {
 		client->subscan=(subscan_t*)calloc(scfg.total_subs, sizeof(subscan_t));
 		if(client->subscan==NULL)
-			lprintf(LOG_CRIT,"!MALLOC FAILURE");
+			errprintf(LOG_CRIT, WHERE, "!MALLOC FAILURE");
 	}
 	if(client->subscan!=NULL) {
 		getmsgptrs(&scfg,&client->user,client->subscan,NULL,NULL);
@@ -493,7 +512,7 @@ js_login(JSContext *cx, uintN argc, jsval *arglist)
 	JS_RESUMEREQUEST(cx, rc);
 
 	if(!js_CreateUserObjects(cx, obj, &scfg, &client->user, client->client, NULL, client->subscan, &mqtt))
-		lprintf(LOG_ERR,"%04d %s !JavaScript ERROR creating user objects"
+		errprintf(LOG_ERR, WHERE, "%04d %s !JavaScript ERROR creating user objects"
 			,client->socket,client->service->protocol);
 
 	if(client->client!=NULL) {
@@ -510,7 +529,7 @@ js_login(JSContext *cx, uintN argc, jsval *arglist)
 
 	val = BOOLEAN_TO_JSVAL(JS_TRUE);
 	if(!JS_SetProperty(cx, obj, "logged_in", &val))
-		lprintf(LOG_ERR, "%04d %s Error setting logged in property for %s"
+		errprintf(LOG_ERR, WHERE, "%04d %s Error setting logged_in property for %s"
 			,client->socket, client->service->protocol, client->user.alias);
 
 	if(client->user.pass[0])
@@ -547,7 +566,7 @@ js_logout(JSContext *cx, uintN argc, jsval *arglist)
 
 	rc=JS_SUSPENDREQUEST(cx);
 	if(!logoutuserdat(&scfg,&client->user,time(NULL),client->logintime))
-		lprintf(LOG_ERR,"%04d !ERROR in logoutuserdat",client->socket);
+		errprintf(LOG_ERR, WHERE, "%04d !ERROR in logoutuserdat",client->socket);
 
 	if(client->service->log_level >= LOG_INFO)
 		lprintf(LOG_INFO,"%04d %s Logging out %s"
@@ -832,7 +851,7 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client,
 	bool		rooted=false;
 
     if((js_cx = JS_NewContext(js_runtime, JAVASCRIPT_CONTEXT_STACK))==NULL) {
-		lprintf(LOG_CRIT, "%04d %s JavaScript: Failed to create new context", sock, service_client->service->protocol);
+		errprintf(LOG_CRIT, WHERE, "%04d %s JavaScript: Failed to create new context", sock, service_client->service->protocol);
 		return(NULL);
 	}
 	JS_SetOptions(js_cx, startup->js.options);
@@ -941,7 +960,7 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client,
 
 
 	if(!success) {
-		lprintf(LOG_CRIT, "%04d %s JavaScript: Failed to create global objects and classes"
+		errprintf(LOG_CRIT, WHERE, "%04d %s JavaScript: Failed to create global objects and classes"
 			,sock, service_client->service->protocol);
 		if(rooted)
 			JS_RemoveObjectRoot(js_cx, glob);
@@ -1136,7 +1155,7 @@ static void js_service_thread(void* arg)
 		}
 #endif
 		if (!ssl_sync(&scfg, lprintf)) {
-			lprintf(LOG_CRIT, "!ssl_sync() failure trying to enable TLS support");
+			errprintf(LOG_CRIT, WHERE, "!ssl_sync() failure trying to enable TLS support");
 		} else {
 			HANDLE_CRYPT_CALL(add_private_key(&scfg, lprintf, service_client.tls_sess), &service_client, "setting private key");
 		}
@@ -1238,7 +1257,7 @@ static void js_service_thread(void* arg)
 
 	if(js_script==NULL) {
 		if(service->log_level >= LOG_ERR)
-			lprintf(LOG_ERR,"%04d !JavaScript FAILED to compile script (%s)",socket,spath);
+			errprintf(LOG_ERR, WHERE, "%04d !JavaScript FAILED to compile script (%s)",socket,spath);
 	} else {
 		service_client.callback.events_supported = true;
 		js_PrepareToExecute(js_cx, js_glob, spath, /* startup_dir */NULL, js_glob);
@@ -1327,7 +1346,7 @@ static void js_static_service_thread(void* arg)
 
 	if((js_runtime=jsrt_GetNew(service->js.max_bytes, 5000, __FILE__, __LINE__))==NULL) {
 		if(service->log_level >= LOG_ERR)
-			lprintf(LOG_ERR,"%s !JavaScript ERROR creating runtime"
+			errprintf(LOG_ERR, WHERE, "%s !JavaScript ERROR creating runtime"
 				,service->protocol);
 		xpms_destroy(service->set, close_socket_cb, service);
 		service->set = NULL;
@@ -1358,7 +1377,7 @@ static void js_static_service_thread(void* arg)
 
 		if((js_script=JS_CompileFile(js_cx, js_glob, spath))==NULL)  {
 			if(service->log_level >= LOG_ERR)
-				lprintf(LOG_ERR,"!JavaScript FAILED to compile script (%s)",spath);
+				errprintf(LOG_ERR, WHERE, "!JavaScript FAILED to compile script (%s)",spath);
 			break;
 		}
 
@@ -1431,7 +1450,7 @@ static void native_static_service_thread(void* arg)
 			0,
 			true, /* Inheritable */
 			DUPLICATE_SAME_ACCESS)) {
-		lprintf(LOG_ERR,"%04d %s !ERROR %d duplicating socket descriptor"
+		errprintf(LOG_ERR, WHERE, "%04d %s !ERROR %d duplicating socket descriptor"
 			,inst.socket,inst.service->protocol,GetLastError());
 		close_socket(inst.socket);
 		thread_down();
@@ -1441,7 +1460,7 @@ static void native_static_service_thread(void* arg)
 #else
 	socket_dup = dup(inst.socket);
 	if(socket_dup == -1) {
-		lprintf(LOG_ERR,"%04d %s !ERROR %d duplicating socket descriptor"
+		errprintf(LOG_ERR, WHERE, "%04d %s !ERROR %d duplicating socket descriptor"
 			,inst.socket, inst.service->protocol, errno);
 		close_socket(inst.socket);
 		thread_down();
@@ -1460,7 +1479,7 @@ static void native_static_service_thread(void* arg)
 	do {
 		int result = system(fullcmd);
 		if(result != 0)
-			lprintf(LOG_ERR, "%04d %s '%s' returned %d"
+			errprintf(LOG_ERR, WHERE, "%04d %s '%s' returned %d"
 				,inst.socket, inst.service->protocol, fullcmd, result);
 	} while(!inst.service->terminated && inst.service->options&SERVICE_OPT_STATIC_LOOP);
 
@@ -1560,7 +1579,7 @@ static void native_service_thread(void* arg)
 		0,
 		true, /* Inheritable */
 		DUPLICATE_SAME_ACCESS)) {
-		lprintf(LOG_ERR,"%04d %s !ERROR %d duplicating socket descriptor"
+		errprintf(LOG_ERR, WHERE, "%04d %s !ERROR %d duplicating socket descriptor"
 			,socket,service->protocol,GetLastError());
 		close_socket(socket);
 		thread_down();
@@ -1569,7 +1588,7 @@ static void native_service_thread(void* arg)
 #else
 	socket_dup = dup(socket);
 	if(socket_dup == -1) {
-		lprintf(LOG_ERR,"%04d %s !ERROR %d duplicating socket descriptor"
+		errprintf(LOG_ERR, WHERE, "%04d %s !ERROR %d duplicating socket descriptor"
 			,socket, service->protocol, errno);
 		close_socket(socket);
 		thread_down();
@@ -1598,7 +1617,7 @@ static void native_service_thread(void* arg)
 
 	int result = system(fullcmd);
 	if(result != 0)
-		lprintf(LOG_ERR, "%04d %s '%s' returned %d"
+		errprintf(LOG_ERR, WHERE, "%04d %s '%s' returned %d"
 			,socket, service->protocol, fullcmd, result);
 
 	ulong remain = protected_uint32_adjust(&service->clients, -1);
@@ -1655,7 +1674,7 @@ static service_t* read_services_ini(const char* services_ini, service_t* service
 	char		*default_interfaces;
 
 	if((fp=fopen(services_ini,"r"))==NULL) {
-		lprintf(LOG_CRIT,"!ERROR %d (%s) opening %s", errno, strerror(errno), services_ini);
+		errprintf(LOG_CRIT, WHERE, "!ERROR %d (%s) opening %s", errno, strerror(errno), services_ini);
 		return(NULL);
 	}
 
@@ -1737,7 +1756,7 @@ static service_t* read_services_ini(const char* services_ini, service_t* service
 		}
 
 		if((np=(service_t*)realloc(service,sizeof(service_t)*((*services)+1)))==NULL) {
-			lprintf(LOG_CRIT,"!MALLOC FAILURE");
+			errprintf(LOG_CRIT, WHERE, "!MALLOC FAILURE");
 			free(default_interfaces);
 			iniFreeStringList(sec_list);
 			return(service);
@@ -2031,23 +2050,23 @@ void services_thread(void* arg)
 		for(i=0;i<(int)services && !startup->shutdown_now;i++) {
 			if (service[i].options & SERVICE_OPT_TLS) {
 				if (service[i].options & SERVICE_OPT_UDP) {
-					lprintf(LOG_ERR, "Option error, TLS and UDP specified for %s", service[i].protocol);
+					errprintf(LOG_ERR, WHERE, "Option error, TLS and UDP specified for %s", service[i].protocol);
 					continue;
 				}
 				if (service[i].options & SERVICE_OPT_NATIVE) {
-					lprintf(LOG_ERR, "Option error, TLS not yet supported for native services (%s)", service[i].protocol);
+					errprintf(LOG_ERR, WHERE, "Option error, TLS not yet supported for native services (%s)", service[i].protocol);
 					continue;
 				}
 				if (service[i].options & SERVICE_OPT_STATIC) {
-					lprintf(LOG_ERR, "Option error, TLS not yet supported for static services (%s)", service[i].protocol);
+					errprintf(LOG_ERR, WHERE, "Option error, TLS not yet supported for static services (%s)", service[i].protocol);
 					continue;
 				}
 				if(!ssl_sync(&scfg, lprintf))
-					lprintf(LOG_CRIT, "!ssl_sync() failure trying to enable TLS support");
+					errprintf(LOG_CRIT, WHERE, "!ssl_sync() failure trying to enable TLS support");
 			}
 			service[i].set=xpms_create(startup->bind_retry_count, startup->bind_retry_delay, lprintf);
 			if(service[i].set == NULL) {
-				lprintf(LOG_CRIT,"!ERROR creating %s socket set", service[i].protocol);
+				errprintf(LOG_CRIT, WHERE, "!ERROR creating %s socket set", service[i].protocol);
 				cleanup(1);
 				return;
 			}
@@ -2108,7 +2127,7 @@ void services_thread(void* arg)
 #ifdef PREFER_POLL
 		nfds = setup_poll(&fds);
 		if (nfds == 0) {
-			lprintf(LOG_CRIT, "!ERROR setting up poll() data");
+			errprintf(LOG_CRIT, WHERE, "!ERROR setting up poll() data");
 			cleanup(1);
 			return;
 		}
@@ -2242,7 +2261,7 @@ void services_thread(void* arg)
 					if(service[i].options&SERVICE_OPT_UDP) {
 						/* UDP */
 						if((udp_buf = (BYTE*)calloc(1, MAX_UDP_BUF_LEN)) == NULL) {
-							lprintf(LOG_CRIT,"%04d %s !ERROR %d allocating UDP buffer"
+							errprintf(LOG_CRIT, WHERE, "%04d %s !ERROR %d allocating UDP buffer"
 								,service[i].set->socks[j].sock, service[i].protocol, errno);
 							continue;
 						}
@@ -2260,7 +2279,7 @@ void services_thread(void* arg)
 						if((client_socket = open_socket(service[i].set->socks[j].domain, SOCK_DGRAM, &service[i]))
 							==INVALID_SOCKET) {
 							FREE_AND_NULL(udp_buf);
-							lprintf(LOG_ERR,"%04d %s !ERROR %d opening socket: %s"
+							errprintf(LOG_ERR, WHERE, "%04d %s !ERROR %d opening socket: %s"
 								,service[i].set->socks[j].sock, service[i].protocol, SOCKET_ERRNO, SOCKET_STRERROR(error,sizeof(error)));
 							continue;
 						}
@@ -2273,7 +2292,7 @@ void services_thread(void* arg)
 						if(setsockopt(client_socket,SOL_SOCKET,SO_REUSEADDR
 							,(char*)&optval,sizeof(optval))!=0) {
 							FREE_AND_NULL(udp_buf);
-							lprintf(LOG_ERR,"%04d %s !ERROR %d setting socket option: %s"
+							errprintf(LOG_ERR, WHERE, "%04d %s !ERROR %d setting socket option: %s"
 								,client_socket, service[i].protocol, SOCKET_ERRNO, SOCKET_STRERROR(error,sizeof(error)));
 							close_socket(client_socket);
 							continue;
@@ -2282,7 +2301,7 @@ void services_thread(void* arg)
 						if(setsockopt(client_socket,SOL_SOCKET,SO_REUSEPORT
 							,(char*)&optval,sizeof(optval))!=0) {
 							FREE_AND_NULL(udp_buf);
-							lprintf(LOG_ERR,"%04d %s !ERROR %d setting socket option: %s"
+							errprintf(LOG_ERR, WHERE, "%04d %s !ERROR %d setting socket option: %s"
 								,client_socket, service[i].protocol, SOCKET_ERRNO, SOCKET_STRERROR(error,sizeof(error)));
 							close_socket(client_socket);
 							continue;
@@ -2302,7 +2321,7 @@ void services_thread(void* arg)
 						}
 						if(result!=0) {
 							FREE_AND_NULL(udp_buf);
-							lprintf(LOG_ERR,"%04d %s !ERROR %d re-binding socket to port %u: %s"
+							errprintf(LOG_ERR, WHERE, "%04d %s !ERROR %d re-binding socket to port %u: %s"
 								,client_socket, service[i].protocol, SOCKET_ERRNO, service[i].port, SOCKET_STRERROR(error,sizeof(error)));
 							close_socket(client_socket);
 							continue;
@@ -2312,7 +2331,7 @@ void services_thread(void* arg)
 						if(connect(client_socket
 							,(struct sockaddr *)&client_addr, client_addr_len)!=0) {
 							FREE_AND_NULL(udp_buf);
-							lprintf(LOG_ERR,"%04d %s !ERROR %d connect failed: %s"
+							errprintf(LOG_ERR, WHERE, "%04d %s !ERROR %d connect failed: %s"
 								,client_socket, service[i].protocol, SOCKET_ERRNO, SOCKET_STRERROR(error,sizeof(error)));
 							close_socket(client_socket);
 							continue;
@@ -2349,7 +2368,7 @@ void services_thread(void* arg)
 					memset(&local_addr, 0, sizeof(local_addr));
 					socklen_t addr_len = sizeof(local_addr);
 					if(getsockname(client_socket, (struct sockaddr *)&local_addr, &addr_len) != 0) {
-						lprintf(LOG_CRIT,"%04d %s [%s] !ERROR %d getting local address/port of socket"
+						errprintf(LOG_CRIT, WHERE, "%04d %s [%s] !ERROR %d getting local address/port of socket"
 							,client_socket, service[i].protocol, host_ip, SOCKET_ERRNO);
 						FREE_AND_NULL(udp_buf);
 						close_socket(client_socket);
@@ -2404,7 +2423,7 @@ void services_thread(void* arg)
 
 					if((client=malloc(sizeof(service_client_t)))==NULL) {
 						FREE_AND_NULL(udp_buf);
-						lprintf(LOG_CRIT,"%04d %s !ERROR allocating %lu bytes of memory for service_client"
+						errprintf(LOG_CRIT, WHERE, "%04d %s !ERROR allocating %lu bytes of memory for service_client"
 							,client_socket, service[i].protocol, (ulong)sizeof(service_client_t));
 						close_socket(client_socket);
 						continue;