22 |
|
|
23 |
#include "server.h" |
#include "server.h" |
24 |
|
|
25 |
#define LSCP_SERVER_RETRY 3 // Maximum number of unanswered PING retries. |
#define LSCP_SERVER_SLEEP 30 // Period in seconds for watchdog wakeup (idle loop). |
|
#define LSCP_SERVER_SLEEP 30 // Period in seconds for watchdog wakeup. |
|
26 |
|
|
27 |
|
|
28 |
// Local prototypes. |
// Local prototypes. |
30 |
static lscp_connect_t *_lscp_connect_create (lscp_server_t *pServer, lscp_socket_t sock, struct sockaddr_in *pAddr, int cAddr); |
static lscp_connect_t *_lscp_connect_create (lscp_server_t *pServer, lscp_socket_t sock, struct sockaddr_in *pAddr, int cAddr); |
31 |
static lscp_status_t _lscp_connect_destroy (lscp_connect_t *pConnect); |
static lscp_status_t _lscp_connect_destroy (lscp_connect_t *pConnect); |
32 |
static lscp_status_t _lscp_connect_recv (lscp_connect_t *pConnect); |
static lscp_status_t _lscp_connect_recv (lscp_connect_t *pConnect); |
|
static lscp_status_t _lscp_connect_send (lscp_connect_t *pConnect, const char *pchBuffer, int cchBuffer); |
|
|
static lscp_status_t _lscp_connect_ping (lscp_connect_t *pConnect); |
|
33 |
|
|
34 |
static void _lscp_connect_list_append (lscp_connect_list_t *pList, lscp_connect_t *pItem); |
static void _lscp_connect_list_append (lscp_connect_list_t *pList, lscp_connect_t *pItem); |
35 |
static void _lscp_connect_list_remove (lscp_connect_list_t *pList, lscp_connect_t *pItem); |
static void _lscp_connect_list_remove (lscp_connect_list_t *pList, lscp_connect_t *pItem); |
36 |
static void _lscp_connect_list_remove_safe (lscp_connect_list_t *pList, lscp_connect_t *pItem); |
static void _lscp_connect_list_remove_safe (lscp_connect_list_t *pList, lscp_connect_t *pItem); |
37 |
static void _lscp_connect_list_free (lscp_connect_list_t *pList); |
static void _lscp_connect_list_free (lscp_connect_list_t *pList); |
|
static lscp_connect_t *_lscp_connect_list_find_addr (lscp_connect_list_t *pList, struct sockaddr_in *pAddr); |
|
38 |
static lscp_connect_t *_lscp_connect_list_find_sock (lscp_connect_list_t *pList, lscp_socket_t sock); |
static lscp_connect_t *_lscp_connect_list_find_sock (lscp_connect_list_t *pList, lscp_socket_t sock); |
39 |
|
|
|
static lscp_status_t _lscp_server_evt_recv (lscp_server_t *pServer); |
|
|
|
|
40 |
static void _lscp_server_thread_proc (lscp_server_t *pServer); |
static void _lscp_server_thread_proc (lscp_server_t *pServer); |
41 |
static void _lscp_server_select_proc (lscp_server_t *pServer); |
static void _lscp_server_select_proc (lscp_server_t *pServer); |
42 |
|
|
43 |
static void _lscp_server_cmd_proc (void *pvServer); |
static void _lscp_server_agent_proc (void *pvServer); |
|
static void _lscp_server_evt_proc (void *pvServer); |
|
|
|
|
|
static void _lscp_watchdog_scan (lscp_server_t *pServer); |
|
|
|
|
|
static void _lscp_watchdog_proc (void *pvServer); |
|
44 |
|
|
|
#if defined(WIN32) |
|
|
#include <time.h> |
|
|
#undef gettimeofday |
|
|
#define gettimeofday(p,n) {(p)->tv_sec = (long) (clock() / CLOCKS_PER_SEC); (p)->tv_usec = 0;} |
|
|
#else |
|
|
#include <sys/time.h> |
|
|
#endif |
|
45 |
|
|
46 |
//------------------------------------------------------------------------- |
//------------------------------------------------------------------------- |
47 |
// Server-side client connection list methods. |
// Server-side client connection list methods. |
140 |
} |
} |
141 |
|
|
142 |
|
|
|
static lscp_connect_t *_lscp_connect_list_find_addr ( lscp_connect_list_t *pList, struct sockaddr_in *pAddr ) |
|
|
{ |
|
|
int iPort = ntohs(pAddr->sin_port); |
|
|
const char *pszAddr = inet_ntoa(pAddr->sin_addr); |
|
|
lscp_connect_t *p; |
|
|
|
|
|
// fprintf(stderr, "_lscp_connect_list_find_addr: pList=%p addr=%s port=%d.\n", pList, inet_ntoa(pAddr->sin_addr), ntohs(pAddr->sin_port)); |
|
|
|
|
|
for (p = pList->first; p; p = p->next) { |
|
|
if (iPort == p->port && strcmp(pszAddr, inet_ntoa(p->client.addr.sin_addr)) == 0) |
|
|
return p; |
|
|
} |
|
|
|
|
|
return NULL; |
|
|
} |
|
|
|
|
|
|
|
143 |
static lscp_connect_t *_lscp_connect_list_find_sock ( lscp_connect_list_t *pList, lscp_socket_t sock ) |
static lscp_connect_t *_lscp_connect_list_find_sock ( lscp_connect_list_t *pList, lscp_socket_t sock ) |
144 |
{ |
{ |
145 |
lscp_connect_t *p; |
lscp_connect_t *p; |
162 |
lscp_connect_t *pConnect = (lscp_connect_t *) pvConnect; |
lscp_connect_t *pConnect = (lscp_connect_t *) pvConnect; |
163 |
lscp_server_t *pServer = pConnect->server; |
lscp_server_t *pServer = pConnect->server; |
164 |
|
|
165 |
while (pServer->cmd.iState && pConnect->client.iState) { |
while (pServer->agent.iState && pConnect->client.iState) { |
166 |
if (_lscp_connect_recv(pConnect) != LSCP_OK) |
if (_lscp_connect_recv(pConnect) != LSCP_OK) |
167 |
pConnect->client.iState = 0; |
pConnect->client.iState = 0; |
168 |
} |
} |
190 |
memset(pConnect, 0, sizeof(lscp_connect_t)); |
memset(pConnect, 0, sizeof(lscp_connect_t)); |
191 |
|
|
192 |
pConnect->server = pServer; |
pConnect->server = pServer; |
193 |
|
pConnect->events = LSCP_EVENT_NONE; |
194 |
|
|
195 |
#ifdef DEBUG |
#ifdef DEBUG |
196 |
fprintf(stderr, "_lscp_connect_create: pConnect=%p: sock=%d addr=%s port=%d.\n", pConnect, sock, inet_ntoa(pAddr->sin_addr), ntohs(pAddr->sin_port)); |
fprintf(stderr, "_lscp_connect_create: pConnect=%p: sock=%d addr=%s port=%d.\n", pConnect, sock, inet_ntoa(pAddr->sin_addr), ntohs(pAddr->sin_port)); |
219 |
|
|
220 |
#ifdef DEBUG |
#ifdef DEBUG |
221 |
fprintf(stderr, "_lscp_connect_destroy: pConnect=%p.\n", pConnect); |
fprintf(stderr, "_lscp_connect_destroy: pConnect=%p.\n", pConnect); |
222 |
|
fprintf(stderr, "<%p> sock=%d addr=%s port=%d events=0x%04x ...\n", pConnect, |
223 |
|
pConnect->client.sock, |
224 |
|
inet_ntoa(pConnect->client.addr.sin_addr), |
225 |
|
ntohs(pConnect->client.addr.sin_port), |
226 |
|
(int) pConnect->events |
227 |
|
); |
228 |
#endif |
#endif |
229 |
|
|
230 |
lscp_socket_agent_free(&(pConnect->client)); |
lscp_socket_agent_free(&(pConnect->client)); |
231 |
|
|
232 |
if (pConnect->sessid) |
#ifdef DEBUG |
233 |
free(pConnect->sessid); |
fprintf(stderr, "<%p> Done.\n", pConnect); |
234 |
|
#endif |
235 |
|
|
236 |
free(pConnect); |
free(pConnect); |
237 |
|
|
263 |
} |
} |
264 |
|
|
265 |
|
|
|
static lscp_status_t _lscp_connect_send ( lscp_connect_t *pConnect, const char *pchBuffer, int cchBuffer ) |
|
|
{ |
|
|
lscp_status_t ret = LSCP_FAILED; |
|
|
struct sockaddr_in addr; |
|
|
int cAddr; |
|
|
|
|
|
if (pConnect == NULL) |
|
|
return ret; |
|
|
if (pchBuffer == NULL || cchBuffer < 1) |
|
|
return ret; |
|
|
if (pConnect->port == 0) |
|
|
return ret; |
|
|
|
|
|
cAddr = sizeof(struct sockaddr_in); |
|
|
memcpy((char *) &addr, (char *) &(pConnect->client.addr), cAddr); |
|
|
addr.sin_port = htons((short) pConnect->port); |
|
|
|
|
|
if (sendto((pConnect->server)->evt.sock, pchBuffer, cchBuffer, 0, (struct sockaddr *) &addr, cAddr) == cchBuffer) |
|
|
ret = LSCP_OK; |
|
|
else |
|
|
lscp_socket_perror("_lscp_connect_send: sendto"); |
|
|
|
|
|
return ret; |
|
|
} |
|
|
|
|
|
|
|
|
static lscp_status_t _lscp_connect_ping ( lscp_connect_t *pConnect ) |
|
|
{ |
|
|
char szBuffer[LSCP_BUFSIZ]; |
|
|
|
|
|
if (pConnect == NULL) |
|
|
return LSCP_FAILED; |
|
|
if (pConnect->sessid == NULL) |
|
|
return LSCP_FAILED; |
|
|
|
|
|
#ifdef DEBUG |
|
|
fprintf(stderr, "_lscp_connect_ping: pConnect=%p: addr=%s port=%d sessid=%s.\n", pConnect, inet_ntoa(pConnect->client.addr.sin_addr), pConnect->port, pConnect->sessid); |
|
|
#endif |
|
|
|
|
|
sprintf(szBuffer, "PING %d %s\r\n", ntohs((pConnect->server)->evt.addr.sin_port), pConnect->sessid); |
|
|
|
|
|
return _lscp_connect_send(pConnect, szBuffer, strlen(szBuffer)); |
|
|
} |
|
|
|
|
|
|
|
266 |
//------------------------------------------------------------------------- |
//------------------------------------------------------------------------- |
267 |
// Command service (stream oriented). |
// Command service (stream oriented). |
268 |
|
|
|
static lscp_status_t _lscp_server_evt_recv ( lscp_server_t *pServer ) |
|
|
{ |
|
|
lscp_status_t ret = LSCP_FAILED; |
|
|
struct sockaddr_in addr; |
|
|
int cAddr; |
|
|
char achBuffer[LSCP_BUFSIZ]; |
|
|
int cchBuffer; |
|
|
lscp_connect_t *pConnect; |
|
|
|
|
|
cAddr = sizeof(addr); |
|
|
cchBuffer = recvfrom(pServer->evt.sock, achBuffer, sizeof(achBuffer), 0, (struct sockaddr *) &addr, &cAddr); |
|
|
if (cchBuffer > 0) { |
|
|
#ifdef DEBUG |
|
|
lscp_socket_trace("_lscp_server_evt_recv: recvfrom", &addr, achBuffer, cchBuffer); |
|
|
#endif |
|
|
// Just do a simple check for PONG events (ignore sessid). |
|
|
if (strncmp(achBuffer, "PONG ", 5) == 0) { |
|
|
pConnect = _lscp_connect_list_find_addr(&(pServer->connects), &addr); |
|
|
if (pConnect) |
|
|
pConnect->ping = 0; |
|
|
} |
|
|
ret = LSCP_OK; |
|
|
} |
|
|
else lscp_socket_perror("_lscp_server_evt_recv: recvfrom"); |
|
|
|
|
|
return ret; |
|
|
} |
|
|
|
|
|
|
|
269 |
static void _lscp_server_thread_proc ( lscp_server_t *pServer ) |
static void _lscp_server_thread_proc ( lscp_server_t *pServer ) |
270 |
{ |
{ |
271 |
lscp_socket_t sock; |
lscp_socket_t sock; |
277 |
fprintf(stderr, "_lscp_server_thread_proc: Server listening for connections.\n"); |
fprintf(stderr, "_lscp_server_thread_proc: Server listening for connections.\n"); |
278 |
#endif |
#endif |
279 |
|
|
280 |
while (pServer->cmd.iState) { |
while (pServer->agent.iState) { |
281 |
cAddr = sizeof(struct sockaddr_in); |
cAddr = sizeof(struct sockaddr_in); |
282 |
sock = accept(pServer->cmd.sock, (struct sockaddr *) &addr, &cAddr); |
sock = accept(pServer->agent.sock, (struct sockaddr *) &addr, &cAddr); |
283 |
if (sock == INVALID_SOCKET) { |
if (sock == INVALID_SOCKET) { |
284 |
lscp_socket_perror("_lscp_server_thread_proc: accept"); |
lscp_socket_perror("_lscp_server_thread_proc: accept"); |
285 |
pServer->cmd.iState = 0; |
pServer->agent.iState = 0; |
286 |
} else { |
} else { |
287 |
pConnect = _lscp_connect_create(pServer, sock, &addr, cAddr); |
pConnect = _lscp_connect_create(pServer, sock, &addr, cAddr); |
288 |
if (pConnect) { |
if (pConnect) { |
306 |
struct timeval tv; // For specifying a timeout value. |
struct timeval tv; // For specifying a timeout value. |
307 |
int iSelect; // Holds select return status. |
int iSelect; // Holds select return status. |
308 |
|
|
|
struct timeval tv1, tv2; // To compute delta timeouts. |
|
|
|
|
309 |
lscp_socket_t sock; |
lscp_socket_t sock; |
310 |
struct sockaddr_in addr; |
struct sockaddr_in addr; |
311 |
int cAddr; |
int cAddr; |
317 |
FD_ZERO(&master_fds); |
FD_ZERO(&master_fds); |
318 |
FD_ZERO(&select_fds); |
FD_ZERO(&select_fds); |
319 |
|
|
320 |
// Add the listeners to the master set |
// Add the listener to the master set |
321 |
FD_SET((unsigned int) pServer->cmd.sock, &master_fds); |
FD_SET((unsigned int) pServer->agent.sock, &master_fds); |
|
FD_SET((unsigned int) pServer->evt.sock, &master_fds); |
|
322 |
|
|
323 |
// Keep track of the biggest file descriptor; |
// Keep track of the biggest file descriptor; |
324 |
// So far, it's ourself, the listener. |
// So far, it's ourself, the listener. |
325 |
if ((int) pServer->evt.sock > (int) pServer->cmd.sock) |
fdmax = (int) pServer->agent.sock; |
|
fdmax = (int) pServer->evt.sock; |
|
|
else |
|
|
fdmax = (int) pServer->cmd.sock; |
|
|
|
|
|
// To start counting for regular watchdog simulation. |
|
|
gettimeofday(&tv1, NULL); |
|
|
gettimeofday(&tv2, NULL); |
|
326 |
|
|
327 |
// Main loop... |
// Main loop... |
328 |
while (pServer->cmd.iState) { |
while (pServer->agent.iState) { |
329 |
|
|
330 |
// Use a copy of the master. |
// Use a copy of the master. |
331 |
select_fds = master_fds; |
select_fds = master_fds; |
332 |
// Use the timeout feature for watchdoggin. |
// Use the timeout feature for watchdoggin. |
333 |
tv.tv_sec = LSCP_SERVER_SLEEP - (tv2.tv_sec - tv1.tv_sec); |
tv.tv_sec = LSCP_SERVER_SLEEP; |
334 |
tv.tv_usec = 0; |
tv.tv_usec = 0; |
335 |
// Wait for events... |
// Wait for events... |
336 |
iSelect = select(fdmax + 1, &select_fds, NULL, NULL, &tv); |
iSelect = select(fdmax + 1, &select_fds, NULL, NULL, &tv); |
337 |
|
|
|
// Check later id it's time for ping time... |
|
|
gettimeofday(&tv2, NULL); |
|
|
|
|
338 |
if (iSelect < 0) { |
if (iSelect < 0) { |
339 |
lscp_socket_perror("_lscp_server_select_proc: select"); |
lscp_socket_perror("_lscp_server_select_proc: select"); |
340 |
pServer->cmd.iState = 0; |
pServer->agent.iState = 0; |
341 |
} |
} |
342 |
else if (iSelect > 0) { |
else if (iSelect > 0) { |
343 |
// Run through the existing connections looking for data to read... |
// Run through the existing connections looking for data to read... |
344 |
for (fd = 0; fd < fdmax + 1; fd++) { |
for (fd = 0; fd < fdmax + 1; fd++) { |
345 |
if (FD_ISSET(fd, &select_fds)) { // We got one!! |
if (FD_ISSET(fd, &select_fds)) { // We got one!! |
346 |
// Is it ourselves, the command listener? |
// Is it ourselves, the command listener? |
347 |
if (fd == (int) pServer->cmd.sock) { |
if (fd == (int) pServer->agent.sock) { |
348 |
// Accept the connection... |
// Accept the connection... |
349 |
cAddr = sizeof(struct sockaddr_in); |
cAddr = sizeof(struct sockaddr_in); |
350 |
sock = accept(pServer->cmd.sock, (struct sockaddr *) &addr, &cAddr); |
sock = accept(pServer->agent.sock, (struct sockaddr *) &addr, &cAddr); |
351 |
if (sock == INVALID_SOCKET) { |
if (sock == INVALID_SOCKET) { |
352 |
lscp_socket_perror("_lscp_server_select_proc: accept"); |
lscp_socket_perror("_lscp_server_select_proc: accept"); |
353 |
pServer->cmd.iState = 0; |
pServer->agent.iState = 0; |
354 |
} else { |
} else { |
355 |
// Add to master set. |
// Add to master set. |
356 |
FD_SET((unsigned int) sock, &master_fds); |
FD_SET((unsigned int) sock, &master_fds); |
365 |
} |
} |
366 |
} |
} |
367 |
// Done with one new connection. |
// Done with one new connection. |
|
} else if (fd == (int) pServer->evt.sock) { |
|
|
// Or from the event listener? |
|
|
if (_lscp_server_evt_recv(pServer) != LSCP_OK) |
|
|
pServer->cmd.iState = 0; |
|
|
// Done with event transaction. |
|
368 |
} else { |
} else { |
369 |
// Otherwise it's trivial transaction... |
// Otherwise it's trivial transaction... |
370 |
lscp_mutex_lock(pServer->connects.mutex); |
lscp_mutex_lock(pServer->connects.mutex); |
387 |
} |
} |
388 |
// Done (iSelect > 0) |
// Done (iSelect > 0) |
389 |
} |
} |
|
|
|
|
// Maybe select has timed out? |
|
|
if (iSelect == 0 || (tv2.tv_sec - tv1.tv_sec > LSCP_SERVER_SLEEP)) { |
|
|
// Let the pseudo-watchdog do it's stuff... |
|
|
_lscp_watchdog_scan(pServer); |
|
|
// Make it a new start... |
|
|
gettimeofday(&tv1, NULL); |
|
|
} |
|
390 |
} |
} |
391 |
|
|
392 |
#ifdef DEBUG |
#ifdef DEBUG |
395 |
} |
} |
396 |
|
|
397 |
|
|
398 |
static void _lscp_server_cmd_proc ( void *pvServer ) |
static void _lscp_server_agent_proc ( void *pvServer ) |
399 |
{ |
{ |
400 |
lscp_server_t *pServer = (lscp_server_t *) pvServer; |
lscp_server_t *pServer = (lscp_server_t *) pvServer; |
401 |
|
|
407 |
|
|
408 |
|
|
409 |
//------------------------------------------------------------------------- |
//------------------------------------------------------------------------- |
|
// Event service (datagram oriented). |
|
|
|
|
|
static void _lscp_server_evt_proc ( void *pvServer ) |
|
|
{ |
|
|
lscp_server_t *pServer = (lscp_server_t *) pvServer; |
|
|
|
|
|
#ifdef DEBUG |
|
|
fprintf(stderr, "_lscp_server_evt_proc: Server waiting for events.\n"); |
|
|
#endif |
|
|
|
|
|
while (pServer->evt.iState) { |
|
|
if (_lscp_server_evt_recv(pServer) != LSCP_OK) |
|
|
pServer->evt.iState = 0; |
|
|
} |
|
|
|
|
|
#ifdef DEBUG |
|
|
fprintf(stderr, "_lscp_server_evt_proc: Server closing.\n"); |
|
|
#endif |
|
|
} |
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------- |
|
|
// Watchdog thread procedure loop. |
|
|
|
|
|
static void _lscp_watchdog_scan ( lscp_server_t *pServer ) |
|
|
{ |
|
|
lscp_connect_t *p, *pNext; |
|
|
|
|
|
lscp_mutex_lock(pServer->connects.mutex); |
|
|
|
|
|
for (p = pServer->connects.first; p; p = pNext) { |
|
|
pNext = p->next; |
|
|
if (p->port > 0) { |
|
|
if (p->ping >= LSCP_SERVER_RETRY) { |
|
|
fprintf(stderr, "_lscp_watchdog_scan: addr=%s port=%d: Zombie connection about to close.\n", inet_ntoa(p->client.addr.sin_addr), ntohs(p->client.addr.sin_port)); |
|
|
_lscp_connect_list_remove(&(pServer->connects), p); |
|
|
_lscp_connect_destroy(p); |
|
|
} else { |
|
|
p->ping++; |
|
|
_lscp_connect_ping(p); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
lscp_mutex_unlock(pServer->connects.mutex); |
|
|
} |
|
|
|
|
|
|
|
|
static void _lscp_watchdog_proc ( void *pvServer ) |
|
|
{ |
|
|
lscp_server_t *pServer = (lscp_server_t *) pvServer; |
|
|
|
|
|
#ifdef DEBUG |
|
|
fprintf(stderr, "_lscp_watchdog_proc: Watchdog thread started.\n"); |
|
|
#endif |
|
|
|
|
|
while (pServer->iWatchdog) { |
|
|
|
|
|
#if defined(WIN32) |
|
|
Sleep(pServer->iSleep * 1000); |
|
|
#else |
|
|
sleep(pServer->iSleep); |
|
|
#endif |
|
|
_lscp_watchdog_scan(pServer); |
|
|
} |
|
|
|
|
|
#ifdef DEBUG |
|
|
fprintf(stderr, "_lscp_watchdog_proc: Watchdog thread terminated.\n"); |
|
|
#endif |
|
|
} |
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------- |
|
410 |
// Server versioning teller fuunction. |
// Server versioning teller fuunction. |
411 |
|
|
412 |
/** Retrieve the current server library version string. */ |
/** Retrieve the current server library version string. */ |
496 |
|
|
497 |
sock = socket(AF_INET, SOCK_STREAM, 0); |
sock = socket(AF_INET, SOCK_STREAM, 0); |
498 |
if (sock == INVALID_SOCKET) { |
if (sock == INVALID_SOCKET) { |
499 |
lscp_socket_perror("lscp_server_create: cmd: socket"); |
lscp_socket_perror("lscp_server_create: socket"); |
500 |
free(pServer); |
free(pServer); |
501 |
return NULL; |
return NULL; |
502 |
} |
} |
503 |
|
|
504 |
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) |
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) |
505 |
lscp_socket_perror("lscp_server_create: cmd: setsockopt(SO_REUSEADDR)"); |
lscp_socket_perror("lscp_server_create: setsockopt(SO_REUSEADDR)"); |
506 |
#if defined(WIN32) |
#if defined(WIN32) |
507 |
if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) |
if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) |
508 |
lscp_socket_perror("lscp_server_create: cmd: setsockopt(SO_DONTLINGER)"); |
lscp_socket_perror("lscp_server_create: setsockopt(SO_DONTLINGER)"); |
509 |
#endif |
#endif |
510 |
|
|
511 |
#ifdef DEBUG |
#ifdef DEBUG |
512 |
lscp_socket_getopts("lscp_server_create: cmd", sock); |
lscp_socket_getopts("lscp_server_create", sock); |
513 |
#endif |
#endif |
514 |
|
|
515 |
cAddr = sizeof(struct sockaddr_in); |
cAddr = sizeof(struct sockaddr_in); |
519 |
addr.sin_port = htons((short) iPort); |
addr.sin_port = htons((short) iPort); |
520 |
|
|
521 |
if (bind(sock, (const struct sockaddr *) &addr, cAddr) == SOCKET_ERROR) { |
if (bind(sock, (const struct sockaddr *) &addr, cAddr) == SOCKET_ERROR) { |
522 |
lscp_socket_perror("lscp_server_create: cmd: bind"); |
lscp_socket_perror("lscp_server_create: bind"); |
523 |
closesocket(sock); |
closesocket(sock); |
524 |
free(pServer); |
free(pServer); |
525 |
return NULL; |
return NULL; |
526 |
} |
} |
527 |
|
|
528 |
if (listen(sock, 10) == SOCKET_ERROR) { |
if (listen(sock, 10) == SOCKET_ERROR) { |
529 |
lscp_socket_perror("lscp_server_create: cmd: listen"); |
lscp_socket_perror("lscp_server_create: listen"); |
530 |
closesocket(sock); |
closesocket(sock); |
531 |
free(pServer); |
free(pServer); |
532 |
return NULL; |
return NULL; |
534 |
|
|
535 |
if (iPort == 0) { |
if (iPort == 0) { |
536 |
if (getsockname(sock, (struct sockaddr *) &addr, &cAddr) == SOCKET_ERROR) { |
if (getsockname(sock, (struct sockaddr *) &addr, &cAddr) == SOCKET_ERROR) { |
537 |
lscp_socket_perror("lscp_server_create: cmd: getsockname"); |
lscp_socket_perror("lscp_server_create: getsockname"); |
538 |
closesocket(sock); |
closesocket(sock); |
539 |
free(pServer); |
free(pServer); |
540 |
} |
} |
|
// Make command and event ports equal? |
|
|
iPort = ntohs(addr.sin_port); |
|
|
} |
|
|
|
|
|
lscp_socket_agent_init(&(pServer->cmd), sock, &addr, cAddr); |
|
|
|
|
|
#ifdef DEBUG |
|
|
fprintf(stderr, "lscp_server_create: cmd: sock=%d addr=%s port=%d.\n", pServer->cmd.sock, inet_ntoa(pServer->cmd.addr.sin_addr), ntohs(pServer->cmd.addr.sin_port)); |
|
|
#endif |
|
|
|
|
|
// Prepare the event datagram server socket... |
|
|
|
|
|
sock = socket(AF_INET, SOCK_DGRAM, 0); |
|
|
if (sock == INVALID_SOCKET) { |
|
|
lscp_socket_perror("lscp_server_create: evt: socket"); |
|
|
lscp_socket_agent_free(&(pServer->cmd)); |
|
|
free(pServer); |
|
|
return NULL; |
|
541 |
} |
} |
542 |
|
|
543 |
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) |
lscp_socket_agent_init(&(pServer->agent), sock, &addr, cAddr); |
|
lscp_socket_perror("lscp_server_create: evt: setsockopt(SO_REUSEADDR)"); |
|
544 |
|
|
545 |
#ifdef DEBUG |
#ifdef DEBUG |
546 |
lscp_socket_getopts("lscp_server_create: evt", sock); |
fprintf(stderr, "lscp_server_create: sock=%d addr=%s port=%d.\n", pServer->agent.sock, inet_ntoa(pServer->agent.addr.sin_addr), ntohs(pServer->agent.addr.sin_port)); |
|
#endif |
|
|
|
|
|
cAddr = sizeof(struct sockaddr_in); |
|
|
memset((char *) &addr, 0, cAddr); |
|
|
addr.sin_family = AF_INET; |
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY); |
|
|
addr.sin_port = htons((short) iPort); |
|
|
|
|
|
if (bind(sock, (const struct sockaddr *) &addr, cAddr) == SOCKET_ERROR) { |
|
|
lscp_socket_perror("lscp_server_create: evt: bind"); |
|
|
lscp_socket_agent_free(&(pServer->cmd)); |
|
|
closesocket(sock); |
|
|
free(pServer); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
if (iPort == 0) { |
|
|
if (getsockname(sock, (struct sockaddr *) &addr, &cAddr) == SOCKET_ERROR) { |
|
|
lscp_socket_perror("lscp_server_create: evt: getsockname"); |
|
|
lscp_socket_agent_free(&(pServer->cmd)); |
|
|
closesocket(sock); |
|
|
free(pServer); |
|
|
return NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
lscp_socket_agent_init(&(pServer->evt), sock, &addr, cAddr); |
|
|
|
|
|
#ifdef DEBUG |
|
|
fprintf(stderr, "lscp_server_create: evt: sock=%d addr=%s port=%d.\n", pServer->evt.sock, inet_ntoa(pServer->evt.addr.sin_addr), ntohs(pServer->evt.addr.sin_port)); |
|
547 |
#endif |
#endif |
548 |
|
|
549 |
// Now's finally time to startup threads... |
// Now's finally time to startup threads... |
550 |
|
|
551 |
// Command service thread... |
// Command service thread... |
552 |
if (lscp_socket_agent_start(&(pServer->cmd), _lscp_server_cmd_proc, pServer, 0) != LSCP_OK) { |
if (lscp_socket_agent_start(&(pServer->agent), _lscp_server_agent_proc, pServer, 0) != LSCP_OK) { |
553 |
lscp_socket_agent_free(&(pServer->cmd)); |
lscp_socket_agent_free(&(pServer->agent)); |
|
lscp_socket_agent_free(&(pServer->evt)); |
|
554 |
free(pServer); |
free(pServer); |
555 |
return NULL; |
return NULL; |
556 |
} |
} |
557 |
|
|
|
if (pServer->mode == LSCP_SERVER_THREAD) { |
|
|
// Event service thread... |
|
|
if (lscp_socket_agent_start(&(pServer->evt), _lscp_server_evt_proc, pServer, 0) != LSCP_OK) { |
|
|
lscp_socket_agent_free(&(pServer->cmd)); |
|
|
lscp_socket_agent_free(&(pServer->evt)); |
|
|
free(pServer); |
|
|
return NULL; |
|
|
} |
|
|
// Watchdog thread... |
|
|
pServer->iWatchdog = 1; |
|
|
pServer->iSleep = LSCP_SERVER_SLEEP; |
|
|
pServer->pWatchdog = lscp_thread_create(_lscp_watchdog_proc, pServer, 0); |
|
|
} |
|
|
|
|
558 |
// Finally we've some success... |
// Finally we've some success... |
559 |
return pServer; |
return pServer; |
560 |
} |
} |
574 |
fprintf(stderr, "lscp_server_join: pServer=%p.\n", pServer); |
fprintf(stderr, "lscp_server_join: pServer=%p.\n", pServer); |
575 |
#endif |
#endif |
576 |
|
|
577 |
// if (pServer->mode == LSCP_SERVER_THREAD) { |
lscp_socket_agent_join(&(pServer->agent)); |
|
// lscp_thread_join(pServer->pWatchdog); |
|
|
// lscp_socket_agent_join(&(pServer->evt)); |
|
|
// } |
|
|
lscp_socket_agent_join(&(pServer->cmd)); |
|
578 |
|
|
579 |
return LSCP_OK; |
return LSCP_OK; |
580 |
} |
} |
594 |
fprintf(stderr, "lscp_server_destroy: pServer=%p.\n", pServer); |
fprintf(stderr, "lscp_server_destroy: pServer=%p.\n", pServer); |
595 |
#endif |
#endif |
596 |
|
|
|
if (pServer->mode == LSCP_SERVER_THREAD) { |
|
|
pServer->iWatchdog = 0; |
|
|
lscp_thread_destroy(pServer->pWatchdog); |
|
|
} |
|
|
lscp_socket_agent_free(&(pServer->evt)); |
|
|
lscp_socket_agent_free(&(pServer->cmd)); |
|
597 |
_lscp_connect_list_free(&(pServer->connects)); |
_lscp_connect_list_free(&(pServer->connects)); |
598 |
|
lscp_socket_agent_free(&(pServer->agent)); |
599 |
|
|
600 |
free(pServer); |
free(pServer); |
601 |
|
|
604 |
|
|
605 |
|
|
606 |
/** |
/** |
607 |
* Send an event message to all subscribed clients. |
* Send an event notification message to all subscribed clients. |
608 |
* |
* |
609 |
* @param pServer Pointer to server instance structure. |
* @param pServer Pointer to server instance structure. |
610 |
* @param pchBuffer Pointer to data to be sent to all clients. |
* @param event Event type flag to send to all subscribed clients. |
611 |
* @param cchBuffer Length of the data to be sent in bytes. |
* @param pchBuffer Pointer to event data to be sent to all clients. |
612 |
|
* @param cchBuffer Length of the event data to be sent in bytes. |
613 |
* |
* |
614 |
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
615 |
*/ |
*/ |
616 |
lscp_status_t lscp_server_broadcast ( lscp_server_t *pServer, const char *pchBuffer, int cchBuffer ) |
lscp_status_t lscp_server_broadcast ( lscp_server_t *pServer, lscp_event_t event, const char *pchData, int cchData ) |
617 |
{ |
{ |
618 |
lscp_connect_t *p; |
lscp_connect_t *p; |
619 |
|
const char *pszEvent; |
620 |
|
char achBuffer[LSCP_BUFSIZ]; |
621 |
|
int cchBuffer; |
622 |
|
|
623 |
if (pServer == NULL) |
if (pServer == NULL) |
624 |
return LSCP_FAILED; |
return LSCP_FAILED; |
625 |
if (pchBuffer == NULL || cchBuffer < 1) |
if (pchData == NULL || cchData < 1) |
626 |
|
return LSCP_FAILED; |
627 |
|
|
628 |
|
// Which (single) event? |
629 |
|
pszEvent = lscp_event_to_text(event); |
630 |
|
if (pszEvent == NULL) |
631 |
return LSCP_FAILED; |
return LSCP_FAILED; |
632 |
|
|
633 |
|
// Build the event message string... |
634 |
|
cchBuffer = sprintf(achBuffer, "NOTIFY:%s:", pszEvent); |
635 |
|
if (pchData) { |
636 |
|
if (cchData > LSCP_BUFSIZ - cchBuffer - 2) |
637 |
|
cchData = LSCP_BUFSIZ - cchBuffer - 2; |
638 |
|
strncpy(&achBuffer[cchBuffer], pchData, cchData); |
639 |
|
cchBuffer += cchData; |
640 |
|
} |
641 |
|
achBuffer[cchBuffer++] = '\r'; |
642 |
|
achBuffer[cchBuffer++] = '\n'; |
643 |
|
|
644 |
|
// And do the direct broadcasting... |
645 |
|
|
646 |
lscp_mutex_lock(pServer->connects.mutex); |
lscp_mutex_lock(pServer->connects.mutex); |
647 |
|
|
648 |
for (p = pServer->connects.first; p; p = p->next) { |
for (p = pServer->connects.first; p; p = p->next) { |
649 |
if (p->port > 0 && p->ping == 0) |
if (p->events & event) |
650 |
_lscp_connect_send(p, pchBuffer, cchBuffer); |
send(p->client.sock, achBuffer, cchBuffer, 0); |
651 |
} |
} |
652 |
|
|
653 |
lscp_mutex_unlock(pServer->connects.mutex); |
lscp_mutex_unlock(pServer->connects.mutex); |
687 |
* Register client as a subscriber of event broadcast messages. |
* Register client as a subscriber of event broadcast messages. |
688 |
* |
* |
689 |
* @param pConnect Pointer to client connection instance structure. |
* @param pConnect Pointer to client connection instance structure. |
690 |
* @param iPort UDP port number of the requesting client connection. |
* @param event Event type flag of the requesting client subscription. |
691 |
* |
* |
692 |
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
693 |
*/ |
*/ |
694 |
lscp_status_t lscp_server_subscribe ( lscp_connect_t *pConnect, int iPort ) |
lscp_status_t lscp_server_subscribe ( lscp_connect_t *pConnect, lscp_event_t event ) |
695 |
{ |
{ |
|
char szSessID[32]; |
|
|
|
|
696 |
if (pConnect == NULL) |
if (pConnect == NULL) |
697 |
return LSCP_FAILED; |
return LSCP_FAILED; |
698 |
if (iPort == 0 || pConnect->port > 0 || pConnect->sessid) |
if (event == LSCP_EVENT_NONE) |
699 |
return LSCP_FAILED; |
return LSCP_FAILED; |
700 |
|
|
701 |
// Generate a psudo-unique session-id. |
pConnect->events |= event; |
|
sprintf(szSessID, "%08x", ((unsigned int) pConnect->server << 8) ^ (unsigned int) pConnect); |
|
|
|
|
|
pConnect->port = iPort; |
|
|
pConnect->ping = 0; |
|
|
pConnect->sessid = strdup(szSessID); |
|
702 |
|
|
703 |
return _lscp_connect_ping(pConnect); |
return LSCP_OK; |
704 |
} |
} |
705 |
|
|
706 |
|
|
707 |
/** |
/** |
708 |
* Deregister client as subscriber of event broadcast messages. |
* Deregister client as subscriber of event broadcast messages. |
709 |
* |
* |
710 |
* @param pConnect Pointer to client connection instance structure. |
* @param pConnect Pointer to client connection instance structure. |
711 |
* @param pszSessID Session identifier of the requesting client connection. |
* @param event Event type flag of the requesting client unsubscription. |
712 |
* |
* |
713 |
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
714 |
*/ |
*/ |
715 |
lscp_status_t lscp_server_unsubscribe ( lscp_connect_t *pConnect, const char *pszSessID ) |
lscp_status_t lscp_server_unsubscribe ( lscp_connect_t *pConnect, lscp_event_t event ) |
716 |
{ |
{ |
717 |
if (pConnect == NULL) |
if (pConnect == NULL) |
718 |
return LSCP_FAILED; |
return LSCP_FAILED; |
719 |
if (pConnect->port == 0 || pConnect->sessid == NULL) |
if (event == LSCP_EVENT_NONE) |
|
return LSCP_FAILED; |
|
|
|
|
|
// Session ids must match. |
|
|
if (strcmp(pszSessID, pConnect->sessid) != 0) |
|
720 |
return LSCP_FAILED; |
return LSCP_FAILED; |
721 |
|
|
722 |
free(pConnect->sessid); |
pConnect->events &= ~event; |
|
pConnect->sessid = NULL; |
|
|
pConnect->ping = 0; |
|
|
pConnect->port = 0; |
|
723 |
|
|
724 |
return LSCP_OK; |
return LSCP_OK; |
725 |
} |
} |