1 |
capela |
98 |
// server.c |
2 |
|
|
// |
3 |
|
|
/**************************************************************************** |
4 |
|
|
liblscp - LinuxSampler Control Protocol API |
5 |
capela |
1019 |
Copyright (C) 2004-2007, rncbc aka Rui Nuno Capela. All rights reserved. |
6 |
capela |
98 |
|
7 |
|
|
This library is free software; you can redistribute it and/or |
8 |
|
|
modify it under the terms of the GNU Lesser General Public |
9 |
|
|
License as published by the Free Software Foundation; either |
10 |
|
|
version 2.1 of the License, or (at your option) any later version. |
11 |
|
|
|
12 |
|
|
This library is distributed in the hope that it will be useful, |
13 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 |
|
|
Lesser General Public License for more details. |
16 |
|
|
|
17 |
capela |
921 |
You should have received a copy of the GNU General Public License along |
18 |
|
|
with this program; if not, write to the Free Software Foundation, Inc., |
19 |
|
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
20 |
capela |
98 |
|
21 |
|
|
*****************************************************************************/ |
22 |
|
|
|
23 |
|
|
#include "server.h" |
24 |
|
|
|
25 |
capela |
146 |
#define LSCP_SERVER_SLEEP 30 // Period in seconds for watchdog wakeup (idle loop). |
26 |
capela |
98 |
|
27 |
|
|
|
28 |
|
|
// Local prototypes. |
29 |
|
|
|
30 |
|
|
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); |
32 |
|
|
static lscp_status_t _lscp_connect_recv (lscp_connect_t *pConnect); |
33 |
|
|
|
34 |
|
|
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); |
36 |
|
|
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); |
38 |
|
|
static lscp_connect_t *_lscp_connect_list_find_sock (lscp_connect_list_t *pList, lscp_socket_t sock); |
39 |
|
|
|
40 |
|
|
static void _lscp_server_thread_proc (lscp_server_t *pServer); |
41 |
|
|
static void _lscp_server_select_proc (lscp_server_t *pServer); |
42 |
|
|
|
43 |
capela |
146 |
static void _lscp_server_agent_proc (void *pvServer); |
44 |
capela |
98 |
|
45 |
|
|
|
46 |
|
|
//------------------------------------------------------------------------- |
47 |
|
|
// Server-side client connection list methods. |
48 |
|
|
|
49 |
|
|
static void _lscp_connect_list_init ( lscp_connect_list_t *pList ) |
50 |
|
|
{ |
51 |
|
|
// fprintf(stderr, "_lscp_connect_list_init: pList=%p.\n", pList); |
52 |
|
|
|
53 |
capela |
963 |
pList->first = NULL; |
54 |
|
|
pList->last = NULL; |
55 |
|
|
pList->count = 0; |
56 |
capela |
98 |
|
57 |
capela |
963 |
lscp_mutex_init(pList->mutex); |
58 |
capela |
98 |
} |
59 |
|
|
|
60 |
|
|
|
61 |
|
|
static void _lscp_connect_list_append ( lscp_connect_list_t *pList, lscp_connect_t *pItem ) |
62 |
|
|
{ |
63 |
|
|
// fprintf(stderr, "_lscp_connect_list_append: pList=%p pItem=%p.\n", pList, pItem); |
64 |
|
|
|
65 |
capela |
963 |
lscp_mutex_lock(pList->mutex); |
66 |
capela |
98 |
|
67 |
capela |
963 |
pItem->prev = pList->last; |
68 |
|
|
pItem->next = NULL; |
69 |
capela |
98 |
|
70 |
capela |
963 |
if (pList->last) |
71 |
|
|
(pList->last)->next = pItem; |
72 |
|
|
else |
73 |
|
|
pList->first = pItem; |
74 |
capela |
98 |
|
75 |
capela |
963 |
pList->last = pItem; |
76 |
capela |
98 |
|
77 |
capela |
963 |
pList->count++; |
78 |
capela |
98 |
|
79 |
capela |
963 |
lscp_mutex_unlock(pList->mutex); |
80 |
capela |
98 |
} |
81 |
|
|
|
82 |
|
|
|
83 |
|
|
static void _lscp_connect_list_remove ( lscp_connect_list_t *pList, lscp_connect_t *pItem ) |
84 |
|
|
{ |
85 |
|
|
// fprintf(stderr, "_lscp_connect_list_remove: pList=%p pItem=%p.\n", pList, pItem); |
86 |
|
|
|
87 |
capela |
963 |
if (pItem->next) |
88 |
|
|
(pItem->next)->prev = pItem->prev; |
89 |
|
|
else |
90 |
|
|
pList->last = pItem->prev; |
91 |
capela |
98 |
|
92 |
capela |
963 |
if (pItem->prev) |
93 |
|
|
(pItem->prev)->next = pItem->next; |
94 |
|
|
else |
95 |
|
|
pList->first = pItem->next; |
96 |
capela |
98 |
|
97 |
capela |
963 |
pItem->next = NULL; |
98 |
|
|
pItem->prev = NULL; |
99 |
capela |
98 |
|
100 |
capela |
963 |
pList->count--; |
101 |
capela |
98 |
} |
102 |
|
|
|
103 |
|
|
|
104 |
|
|
static void _lscp_connect_list_remove_safe ( lscp_connect_list_t *pList, lscp_connect_t *pItem ) |
105 |
|
|
{ |
106 |
capela |
963 |
lscp_connect_t *p; |
107 |
capela |
98 |
|
108 |
|
|
// fprintf(stderr, "_lscp_connect_list_remove_safe: pList=%p pItem=%p.\n", pList, pItem); |
109 |
|
|
|
110 |
capela |
963 |
lscp_mutex_lock(pList->mutex); |
111 |
capela |
98 |
|
112 |
capela |
963 |
for (p = pList->first; p; p = p->next) { |
113 |
|
|
if (p == pItem) { |
114 |
|
|
_lscp_connect_list_remove(pList, pItem); |
115 |
|
|
break; |
116 |
|
|
} |
117 |
|
|
} |
118 |
capela |
98 |
|
119 |
capela |
963 |
lscp_mutex_unlock(pList->mutex); |
120 |
capela |
98 |
} |
121 |
|
|
|
122 |
|
|
|
123 |
|
|
static void _lscp_connect_list_free ( lscp_connect_list_t *pList ) |
124 |
|
|
{ |
125 |
capela |
963 |
lscp_connect_t *p, *pNext; |
126 |
capela |
98 |
|
127 |
|
|
// fprintf(stderr, "_lscp_connect_list_free: pList=%p.\n", pList); |
128 |
|
|
|
129 |
capela |
963 |
lscp_mutex_lock(pList->mutex); |
130 |
capela |
98 |
|
131 |
capela |
963 |
for (p = pList->first; p; p = pNext) { |
132 |
|
|
pNext = p->next; |
133 |
|
|
_lscp_connect_list_remove(pList, p); |
134 |
|
|
_lscp_connect_destroy(p); |
135 |
|
|
} |
136 |
capela |
98 |
|
137 |
capela |
963 |
lscp_mutex_unlock(pList->mutex); |
138 |
|
|
|
139 |
|
|
lscp_mutex_destroy(pList->mutex); |
140 |
capela |
98 |
} |
141 |
|
|
|
142 |
|
|
|
143 |
|
|
static lscp_connect_t *_lscp_connect_list_find_sock ( lscp_connect_list_t *pList, lscp_socket_t sock ) |
144 |
|
|
{ |
145 |
capela |
963 |
lscp_connect_t *p; |
146 |
capela |
98 |
|
147 |
|
|
// fprintf(stderr, "_lscp_connect_list_find_sock: pList=%p sock=%d.\n", pList, sock); |
148 |
|
|
|
149 |
capela |
963 |
for (p = pList->first; p; p = p->next) { |
150 |
|
|
if (sock == p->client.sock) |
151 |
|
|
return p; |
152 |
|
|
} |
153 |
capela |
98 |
|
154 |
capela |
963 |
return NULL; |
155 |
capela |
98 |
} |
156 |
|
|
|
157 |
|
|
//------------------------------------------------------------------------- |
158 |
|
|
// Server-side threaded client connections. |
159 |
|
|
|
160 |
|
|
static void _lscp_connect_proc ( void *pvConnect ) |
161 |
|
|
{ |
162 |
capela |
963 |
lscp_connect_t *pConnect = (lscp_connect_t *) pvConnect; |
163 |
|
|
lscp_server_t *pServer = pConnect->server; |
164 |
capela |
98 |
|
165 |
capela |
963 |
while (pServer->agent.iState && pConnect->client.iState) { |
166 |
|
|
if (_lscp_connect_recv(pConnect) != LSCP_OK) |
167 |
|
|
pConnect->client.iState = 0; |
168 |
|
|
} |
169 |
capela |
98 |
|
170 |
capela |
963 |
(*pServer->pfnCallback)(pConnect, NULL, LSCP_CONNECT_CLOSE, pServer->pvData); |
171 |
|
|
_lscp_connect_list_remove_safe(&(pServer->connects), pConnect); |
172 |
|
|
closesocket(pConnect->client.sock); |
173 |
capela |
98 |
} |
174 |
|
|
|
175 |
|
|
static lscp_connect_t *_lscp_connect_create ( lscp_server_t *pServer, lscp_socket_t sock, struct sockaddr_in *pAddr, int cAddr ) |
176 |
|
|
{ |
177 |
capela |
963 |
lscp_connect_t *pConnect; |
178 |
capela |
98 |
|
179 |
capela |
963 |
if (pServer == NULL || sock == INVALID_SOCKET || pAddr == NULL) { |
180 |
|
|
fprintf(stderr, "_lscp_connect_create: Invalid connection arguments.\n"); |
181 |
|
|
return NULL; |
182 |
|
|
} |
183 |
capela |
98 |
|
184 |
capela |
963 |
pConnect = (lscp_connect_t *) malloc(sizeof(lscp_connect_t)); |
185 |
|
|
if (pConnect == NULL) { |
186 |
|
|
fprintf(stderr, "_lscp_connect_create: Out of memory.\n"); |
187 |
|
|
closesocket(sock); |
188 |
|
|
return NULL; |
189 |
|
|
} |
190 |
|
|
memset(pConnect, 0, sizeof(lscp_connect_t)); |
191 |
capela |
98 |
|
192 |
capela |
963 |
pConnect->server = pServer; |
193 |
|
|
pConnect->events = LSCP_EVENT_NONE; |
194 |
capela |
98 |
|
195 |
|
|
#ifdef DEBUG |
196 |
capela |
963 |
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)); |
197 |
capela |
98 |
#endif |
198 |
|
|
|
199 |
capela |
963 |
lscp_socket_agent_init(&(pConnect->client), sock, pAddr, cAddr); |
200 |
capela |
98 |
|
201 |
capela |
963 |
if (pServer->mode == LSCP_SERVER_THREAD) { |
202 |
|
|
if (lscp_socket_agent_start(&(pConnect->client), _lscp_connect_proc, pConnect, 0) != LSCP_OK) { |
203 |
|
|
closesocket(sock); |
204 |
|
|
free(pConnect); |
205 |
|
|
return NULL; |
206 |
|
|
} |
207 |
|
|
} |
208 |
capela |
98 |
|
209 |
capela |
963 |
return pConnect; |
210 |
capela |
98 |
} |
211 |
|
|
|
212 |
|
|
|
213 |
|
|
static lscp_status_t _lscp_connect_destroy ( lscp_connect_t *pConnect ) |
214 |
|
|
{ |
215 |
capela |
963 |
lscp_status_t ret = LSCP_FAILED; |
216 |
capela |
98 |
|
217 |
capela |
963 |
if (pConnect == NULL) |
218 |
|
|
return ret; |
219 |
capela |
98 |
|
220 |
|
|
#ifdef DEBUG |
221 |
capela |
963 |
fprintf(stderr, "_lscp_connect_destroy: pConnect=%p.\n", pConnect); |
222 |
capela |
98 |
#endif |
223 |
|
|
|
224 |
capela |
963 |
lscp_socket_agent_free(&(pConnect->client)); |
225 |
capela |
98 |
|
226 |
capela |
963 |
free(pConnect); |
227 |
capela |
98 |
|
228 |
capela |
963 |
return ret; |
229 |
capela |
98 |
} |
230 |
|
|
|
231 |
|
|
|
232 |
|
|
lscp_status_t _lscp_connect_recv ( lscp_connect_t *pConnect ) |
233 |
|
|
{ |
234 |
capela |
963 |
lscp_status_t ret = LSCP_FAILED; |
235 |
|
|
lscp_server_t *pServer; |
236 |
|
|
char achBuffer[LSCP_BUFSIZ]; |
237 |
|
|
int cchBuffer; |
238 |
capela |
98 |
|
239 |
capela |
963 |
if (pConnect == NULL) |
240 |
|
|
return ret; |
241 |
capela |
98 |
|
242 |
capela |
963 |
pServer = pConnect->server; |
243 |
|
|
if (pServer == NULL) |
244 |
|
|
return ret; |
245 |
capela |
98 |
|
246 |
capela |
963 |
cchBuffer = recv(pConnect->client.sock, achBuffer, sizeof(achBuffer), 0); |
247 |
|
|
if (cchBuffer > 0) |
248 |
|
|
ret = (*pServer->pfnCallback)(pConnect, achBuffer, cchBuffer, pServer->pvData); |
249 |
|
|
else if (cchBuffer < 0) |
250 |
|
|
lscp_socket_perror("_lscp_connect_recv: recv"); |
251 |
capela |
98 |
|
252 |
capela |
963 |
return ret; |
253 |
capela |
98 |
} |
254 |
|
|
|
255 |
|
|
|
256 |
|
|
//------------------------------------------------------------------------- |
257 |
capela |
132 |
// Command service (stream oriented). |
258 |
capela |
98 |
|
259 |
|
|
static void _lscp_server_thread_proc ( lscp_server_t *pServer ) |
260 |
|
|
{ |
261 |
capela |
963 |
lscp_socket_t sock; |
262 |
|
|
struct sockaddr_in addr; |
263 |
|
|
socklen_t cAddr; |
264 |
|
|
lscp_connect_t *pConnect; |
265 |
capela |
98 |
|
266 |
|
|
#ifdef DEBUG |
267 |
capela |
963 |
fprintf(stderr, "_lscp_server_thread_proc: Server listening for connections.\n"); |
268 |
capela |
98 |
#endif |
269 |
|
|
|
270 |
capela |
963 |
while (pServer->agent.iState) { |
271 |
|
|
cAddr = sizeof(struct sockaddr_in); |
272 |
|
|
sock = accept(pServer->agent.sock, (struct sockaddr *) &addr, &cAddr); |
273 |
|
|
if (sock == INVALID_SOCKET) { |
274 |
|
|
lscp_socket_perror("_lscp_server_thread_proc: accept"); |
275 |
|
|
pServer->agent.iState = 0; |
276 |
|
|
} else { |
277 |
|
|
pConnect = _lscp_connect_create(pServer, sock, &addr, cAddr); |
278 |
|
|
if (pConnect) { |
279 |
|
|
_lscp_connect_list_append(&(pServer->connects), pConnect); |
280 |
|
|
(*pServer->pfnCallback)(pConnect, NULL, LSCP_CONNECT_OPEN, pServer->pvData); |
281 |
|
|
} |
282 |
|
|
} |
283 |
|
|
} |
284 |
capela |
98 |
|
285 |
|
|
#ifdef DEBUG |
286 |
capela |
963 |
fprintf(stderr, "_lscp_server_thread_proc: Server closing.\n"); |
287 |
capela |
98 |
#endif |
288 |
|
|
} |
289 |
|
|
|
290 |
|
|
|
291 |
|
|
static void _lscp_server_select_proc ( lscp_server_t *pServer ) |
292 |
|
|
{ |
293 |
capela |
963 |
fd_set master_fds; // Master file descriptor list. |
294 |
|
|
fd_set select_fds; // temp file descriptor list for select(). |
295 |
|
|
int fd, fdmax; // Maximum file descriptor number. |
296 |
|
|
struct timeval tv; // For specifying a timeout value. |
297 |
|
|
int iSelect; // Holds select return status. |
298 |
capela |
98 |
|
299 |
capela |
963 |
lscp_socket_t sock; |
300 |
|
|
struct sockaddr_in addr; |
301 |
|
|
socklen_t cAddr; |
302 |
|
|
lscp_connect_t *pConnect; |
303 |
capela |
98 |
|
304 |
|
|
#ifdef DEBUG |
305 |
capela |
963 |
fprintf(stderr, "_lscp_server_select_proc: Server listening for connections.\n"); |
306 |
capela |
98 |
#endif |
307 |
capela |
963 |
FD_ZERO(&master_fds); |
308 |
|
|
FD_ZERO(&select_fds); |
309 |
capela |
98 |
|
310 |
capela |
963 |
// Add the listener to the master set |
311 |
|
|
FD_SET((unsigned int) pServer->agent.sock, &master_fds); |
312 |
capela |
98 |
|
313 |
capela |
963 |
// Keep track of the biggest file descriptor; |
314 |
|
|
// So far, it's ourself, the listener. |
315 |
|
|
fdmax = (int) pServer->agent.sock; |
316 |
capela |
98 |
|
317 |
capela |
963 |
// Main loop... |
318 |
|
|
while (pServer->agent.iState) { |
319 |
capela |
98 |
|
320 |
capela |
963 |
// Use a copy of the master. |
321 |
|
|
select_fds = master_fds; |
322 |
|
|
// Use the timeout feature for watchdoggin. |
323 |
|
|
tv.tv_sec = LSCP_SERVER_SLEEP; |
324 |
|
|
tv.tv_usec = 0; |
325 |
|
|
// Wait for events... |
326 |
|
|
iSelect = select(fdmax + 1, &select_fds, NULL, NULL, &tv); |
327 |
capela |
98 |
|
328 |
capela |
963 |
if (iSelect < 0) { |
329 |
|
|
lscp_socket_perror("_lscp_server_select_proc: select"); |
330 |
|
|
pServer->agent.iState = 0; |
331 |
|
|
} |
332 |
|
|
else if (iSelect > 0) { |
333 |
|
|
// Run through the existing connections looking for data to read... |
334 |
|
|
for (fd = 0; fd < fdmax + 1; fd++) { |
335 |
|
|
if (FD_ISSET(fd, &select_fds)) { // We got one!! |
336 |
|
|
// Is it ourselves, the command listener? |
337 |
|
|
if (fd == (int) pServer->agent.sock) { |
338 |
|
|
// Accept the connection... |
339 |
|
|
cAddr = sizeof(struct sockaddr_in); |
340 |
|
|
sock = accept(pServer->agent.sock, (struct sockaddr *) &addr, &cAddr); |
341 |
|
|
if (sock == INVALID_SOCKET) { |
342 |
|
|
lscp_socket_perror("_lscp_server_select_proc: accept"); |
343 |
|
|
pServer->agent.iState = 0; |
344 |
|
|
} else { |
345 |
|
|
// Add to master set. |
346 |
|
|
FD_SET((unsigned int) sock, &master_fds); |
347 |
|
|
// Keep track of the maximum. |
348 |
|
|
if ((int) sock > fdmax) |
349 |
|
|
fdmax = (int) sock; |
350 |
|
|
// And do create the client connection entry. |
351 |
|
|
pConnect = _lscp_connect_create(pServer, sock, &addr, cAddr); |
352 |
|
|
if (pConnect) { |
353 |
|
|
_lscp_connect_list_append(&(pServer->connects), pConnect); |
354 |
|
|
(*pServer->pfnCallback)(pConnect, NULL, LSCP_CONNECT_OPEN, pServer->pvData); |
355 |
|
|
} |
356 |
|
|
} |
357 |
|
|
// Done with one new connection. |
358 |
|
|
} else { |
359 |
|
|
// Otherwise it's trivial transaction... |
360 |
|
|
lscp_mutex_lock(pServer->connects.mutex); |
361 |
|
|
// Find the connection on our cache... |
362 |
|
|
pConnect = _lscp_connect_list_find_sock(&(pServer->connects), (lscp_socket_t) fd); |
363 |
|
|
// Handle data from a client. |
364 |
|
|
if (_lscp_connect_recv(pConnect) != LSCP_OK) { |
365 |
|
|
// Say bye bye! |
366 |
|
|
if (pConnect) { |
367 |
|
|
(*pServer->pfnCallback)(pConnect, NULL, LSCP_CONNECT_CLOSE, pServer->pvData); |
368 |
|
|
_lscp_connect_list_remove(&(pServer->connects), pConnect); |
369 |
|
|
_lscp_connect_destroy(pConnect); |
370 |
|
|
} |
371 |
|
|
// Remove from master set. |
372 |
|
|
FD_CLR((unsigned int) fd, &master_fds); |
373 |
|
|
} |
374 |
|
|
lscp_mutex_unlock(pServer->connects.mutex); |
375 |
|
|
} |
376 |
|
|
} |
377 |
|
|
} |
378 |
|
|
// Done (iSelect > 0) |
379 |
|
|
} |
380 |
|
|
} |
381 |
capela |
98 |
|
382 |
|
|
#ifdef DEBUG |
383 |
capela |
963 |
fprintf(stderr, "_lscp_server_select_proc: Server closing.\n"); |
384 |
capela |
98 |
#endif |
385 |
|
|
} |
386 |
|
|
|
387 |
|
|
|
388 |
capela |
146 |
static void _lscp_server_agent_proc ( void *pvServer ) |
389 |
capela |
98 |
{ |
390 |
capela |
963 |
lscp_server_t *pServer = (lscp_server_t *) pvServer; |
391 |
capela |
98 |
|
392 |
capela |
963 |
if (pServer->mode == LSCP_SERVER_THREAD) |
393 |
|
|
_lscp_server_thread_proc(pServer); |
394 |
|
|
else |
395 |
|
|
_lscp_server_select_proc(pServer); |
396 |
capela |
98 |
} |
397 |
|
|
|
398 |
|
|
|
399 |
|
|
//------------------------------------------------------------------------- |
400 |
|
|
// Server versioning teller fuunction. |
401 |
|
|
|
402 |
|
|
/** Retrieve the current server library version string. */ |
403 |
|
|
const char* lscp_server_package (void) { return LSCP_PACKAGE; } |
404 |
|
|
|
405 |
|
|
/** Retrieve the current server library version string. */ |
406 |
|
|
const char* lscp_server_version (void) { return LSCP_VERSION; } |
407 |
|
|
|
408 |
|
|
/** Retrieve the current server library build timestamp string. */ |
409 |
|
|
const char* lscp_server_build (void) { return __DATE__ " " __TIME__; } |
410 |
|
|
|
411 |
|
|
|
412 |
|
|
//------------------------------------------------------------------------- |
413 |
|
|
// Server sockets. |
414 |
|
|
|
415 |
|
|
/** |
416 |
|
|
* Create a server instance, listening on the given port for client |
417 |
|
|
* connections. A server callback function must be suplied that will |
418 |
|
|
* handle every and each client request. |
419 |
|
|
* |
420 |
|
|
* @param iPort Port number where the server will bind for listening. |
421 |
|
|
* @param pfnCallback Callback function to receive and handle client requests. |
422 |
|
|
* @param pvData Server context opaque data, that will be passed |
423 |
|
|
* to the callback function without change. |
424 |
|
|
* |
425 |
|
|
* @returns The new server instance pointer @ref lscp_server_t if successfull, |
426 |
|
|
* which shall be used on all subsequent server calls, NULL otherwise. |
427 |
|
|
*/ |
428 |
|
|
lscp_server_t* lscp_server_create ( int iPort, lscp_server_proc_t pfnCallback, void *pvData ) |
429 |
|
|
{ |
430 |
capela |
963 |
return lscp_server_create_ex(iPort, pfnCallback, pvData, LSCP_SERVER_SELECT); |
431 |
capela |
98 |
} |
432 |
|
|
|
433 |
|
|
|
434 |
|
|
/** |
435 |
|
|
* Create a server instance, listening on the given port for client |
436 |
|
|
* connections. A server callback function must be suplied that will |
437 |
|
|
* handle every and each client request. A server threading model |
438 |
|
|
* maybe specified either as multi-threaded (one thread per client) |
439 |
|
|
* or single thread multiplex mode (one thread serves all clients). |
440 |
|
|
* |
441 |
|
|
* @param iPort Port number where the server will bind for listening. |
442 |
|
|
* @param pfnCallback Callback function to receive and handle client requests. |
443 |
|
|
* @param pvData Server context opaque data, that will be passed |
444 |
|
|
* to the callback function without change. |
445 |
|
|
* @param mode Server mode of operation, regarding the internal |
446 |
|
|
* threading model, either @ref LSCP_SERVER_THREAD for |
447 |
|
|
* a multi-threaded server, or @ref LSCP_SERVER_SELECT |
448 |
|
|
* for a single-threaded multiplexed server. |
449 |
|
|
* |
450 |
|
|
* @returns The new server instance pointer if successfull, which shall be |
451 |
|
|
* used on all subsequent server calls, NULL otherwise. |
452 |
|
|
*/ |
453 |
|
|
lscp_server_t* lscp_server_create_ex ( int iPort, lscp_server_proc_t pfnCallback, void *pvData, lscp_server_mode_t mode ) |
454 |
|
|
{ |
455 |
capela |
963 |
lscp_server_t *pServer; |
456 |
|
|
lscp_socket_t sock; |
457 |
|
|
struct sockaddr_in addr; |
458 |
|
|
socklen_t cAddr; |
459 |
|
|
int iSockOpt = (-1); |
460 |
capela |
98 |
|
461 |
capela |
963 |
if (pfnCallback == NULL) { |
462 |
|
|
fprintf(stderr, "lscp_server_create: Invalid server callback function.\n"); |
463 |
|
|
return NULL; |
464 |
|
|
} |
465 |
capela |
98 |
|
466 |
capela |
963 |
// Allocate server descriptor... |
467 |
capela |
98 |
|
468 |
capela |
963 |
pServer = (lscp_server_t *) malloc(sizeof(lscp_server_t)); |
469 |
|
|
if (pServer == NULL) { |
470 |
|
|
fprintf(stderr, "lscp_server_create: Out of memory.\n"); |
471 |
|
|
return NULL; |
472 |
|
|
} |
473 |
|
|
memset(pServer, 0, sizeof(lscp_server_t)); |
474 |
capela |
98 |
|
475 |
capela |
963 |
_lscp_connect_list_init(&(pServer->connects)); |
476 |
capela |
98 |
|
477 |
capela |
963 |
pServer->mode = mode; |
478 |
|
|
pServer->pfnCallback = pfnCallback; |
479 |
|
|
pServer->pvData = pvData; |
480 |
capela |
98 |
|
481 |
|
|
#ifdef DEBUG |
482 |
capela |
963 |
fprintf(stderr, "lscp_server_create: pServer=%p: iPort=%d.\n", pServer, iPort); |
483 |
capela |
98 |
#endif |
484 |
|
|
|
485 |
capela |
963 |
// Prepare the command stream server socket... |
486 |
capela |
98 |
|
487 |
capela |
963 |
sock = socket(AF_INET, SOCK_STREAM, 0); |
488 |
|
|
if (sock == INVALID_SOCKET) { |
489 |
|
|
lscp_socket_perror("lscp_server_create: socket"); |
490 |
|
|
free(pServer); |
491 |
|
|
return NULL; |
492 |
|
|
} |
493 |
capela |
98 |
|
494 |
capela |
963 |
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) |
495 |
|
|
lscp_socket_perror("lscp_server_create: setsockopt(SO_REUSEADDR)"); |
496 |
capela |
98 |
#if defined(WIN32) |
497 |
capela |
963 |
if (setsockopt(sock, SOL_SOCKET, SO_DONTLINGER, (char *) &iSockOpt, sizeof(int)) == SOCKET_ERROR) |
498 |
|
|
lscp_socket_perror("lscp_server_create: setsockopt(SO_DONTLINGER)"); |
499 |
capela |
98 |
#endif |
500 |
|
|
|
501 |
|
|
#ifdef DEBUG |
502 |
capela |
963 |
lscp_socket_getopts("lscp_server_create", sock); |
503 |
capela |
98 |
#endif |
504 |
|
|
|
505 |
capela |
963 |
cAddr = sizeof(struct sockaddr_in); |
506 |
|
|
memset((char *) &addr, 0, cAddr); |
507 |
|
|
addr.sin_family = AF_INET; |
508 |
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY); |
509 |
|
|
addr.sin_port = htons((short) iPort); |
510 |
capela |
98 |
|
511 |
capela |
963 |
if (bind(sock, (const struct sockaddr *) &addr, cAddr) == SOCKET_ERROR) { |
512 |
|
|
lscp_socket_perror("lscp_server_create: bind"); |
513 |
|
|
closesocket(sock); |
514 |
|
|
free(pServer); |
515 |
|
|
return NULL; |
516 |
|
|
} |
517 |
capela |
98 |
|
518 |
capela |
963 |
if (listen(sock, 10) == SOCKET_ERROR) { |
519 |
|
|
lscp_socket_perror("lscp_server_create: listen"); |
520 |
|
|
closesocket(sock); |
521 |
|
|
free(pServer); |
522 |
|
|
return NULL; |
523 |
|
|
} |
524 |
capela |
98 |
|
525 |
capela |
963 |
if (iPort == 0) { |
526 |
|
|
if (getsockname(sock, (struct sockaddr *) &addr, &cAddr) == SOCKET_ERROR) { |
527 |
|
|
lscp_socket_perror("lscp_server_create: getsockname"); |
528 |
|
|
closesocket(sock); |
529 |
|
|
free(pServer); |
530 |
|
|
} |
531 |
|
|
} |
532 |
capela |
98 |
|
533 |
capela |
963 |
lscp_socket_agent_init(&(pServer->agent), sock, &addr, cAddr); |
534 |
capela |
98 |
|
535 |
|
|
#ifdef DEBUG |
536 |
capela |
963 |
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)); |
537 |
capela |
98 |
#endif |
538 |
|
|
|
539 |
capela |
963 |
// Now's finally time to startup threads... |
540 |
capela |
98 |
|
541 |
capela |
963 |
// Command service thread... |
542 |
|
|
if (lscp_socket_agent_start(&(pServer->agent), _lscp_server_agent_proc, pServer, 0) != LSCP_OK) { |
543 |
|
|
lscp_socket_agent_free(&(pServer->agent)); |
544 |
|
|
free(pServer); |
545 |
|
|
return NULL; |
546 |
|
|
} |
547 |
capela |
98 |
|
548 |
capela |
963 |
// Finally we've some success... |
549 |
|
|
return pServer; |
550 |
capela |
98 |
} |
551 |
|
|
|
552 |
|
|
|
553 |
|
|
/** |
554 |
|
|
* Wait for a server instance to terminate graciously. |
555 |
|
|
* |
556 |
|
|
* @param pServer Pointer to server instance structure. |
557 |
|
|
*/ |
558 |
|
|
lscp_status_t lscp_server_join ( lscp_server_t *pServer ) |
559 |
|
|
{ |
560 |
capela |
963 |
if (pServer == NULL) |
561 |
|
|
return LSCP_FAILED; |
562 |
capela |
98 |
|
563 |
|
|
#ifdef DEBUG |
564 |
capela |
963 |
fprintf(stderr, "lscp_server_join: pServer=%p.\n", pServer); |
565 |
capela |
98 |
#endif |
566 |
|
|
|
567 |
capela |
963 |
lscp_socket_agent_join(&(pServer->agent)); |
568 |
capela |
98 |
|
569 |
capela |
963 |
return LSCP_OK; |
570 |
capela |
98 |
} |
571 |
|
|
|
572 |
|
|
|
573 |
|
|
/** |
574 |
|
|
* Terminate and destroy a server instance. |
575 |
|
|
* |
576 |
|
|
* @param pServer Pointer to server instance structure. |
577 |
|
|
*/ |
578 |
|
|
lscp_status_t lscp_server_destroy ( lscp_server_t *pServer ) |
579 |
|
|
{ |
580 |
capela |
963 |
if (pServer == NULL) |
581 |
|
|
return LSCP_FAILED; |
582 |
capela |
98 |
|
583 |
|
|
#ifdef DEBUG |
584 |
capela |
963 |
fprintf(stderr, "lscp_server_destroy: pServer=%p.\n", pServer); |
585 |
capela |
98 |
#endif |
586 |
|
|
|
587 |
capela |
963 |
_lscp_connect_list_free(&(pServer->connects)); |
588 |
|
|
lscp_socket_agent_free(&(pServer->agent)); |
589 |
capela |
98 |
|
590 |
capela |
963 |
free(pServer); |
591 |
capela |
98 |
|
592 |
capela |
963 |
return LSCP_OK; |
593 |
capela |
98 |
} |
594 |
|
|
|
595 |
|
|
|
596 |
|
|
/** |
597 |
capela |
146 |
* Send an event notification message to all subscribed clients. |
598 |
capela |
98 |
* |
599 |
capela |
147 |
* @param pServer Pointer to server instance structure. |
600 |
|
|
* @param event Event type flag to send to all subscribed clients. |
601 |
|
|
* @param pchData Pointer to event data to be sent to all clients. |
602 |
|
|
* @param cchData Length of the event data to be sent in bytes. |
603 |
capela |
98 |
* |
604 |
|
|
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
605 |
|
|
*/ |
606 |
capela |
146 |
lscp_status_t lscp_server_broadcast ( lscp_server_t *pServer, lscp_event_t event, const char *pchData, int cchData ) |
607 |
capela |
98 |
{ |
608 |
capela |
963 |
lscp_connect_t *p; |
609 |
|
|
const char *pszEvent; |
610 |
|
|
char achBuffer[LSCP_BUFSIZ]; |
611 |
|
|
int cchBuffer; |
612 |
capela |
98 |
|
613 |
capela |
963 |
if (pServer == NULL) |
614 |
|
|
return LSCP_FAILED; |
615 |
|
|
if (pchData == NULL || cchData < 1) |
616 |
|
|
return LSCP_FAILED; |
617 |
capela |
98 |
|
618 |
capela |
963 |
// Which (single) event? |
619 |
|
|
pszEvent = lscp_event_to_text(event); |
620 |
|
|
if (pszEvent == NULL) |
621 |
|
|
return LSCP_FAILED; |
622 |
capela |
146 |
|
623 |
capela |
963 |
// Build the event message string... |
624 |
|
|
cchBuffer = sprintf(achBuffer, "NOTIFY:%s:", pszEvent); |
625 |
|
|
if (pchData) { |
626 |
|
|
if (cchData > LSCP_BUFSIZ - cchBuffer - 2) |
627 |
|
|
cchData = LSCP_BUFSIZ - cchBuffer - 2; |
628 |
|
|
strncpy(&achBuffer[cchBuffer], pchData, cchData); |
629 |
|
|
cchBuffer += cchData; |
630 |
|
|
} |
631 |
|
|
achBuffer[cchBuffer++] = '\r'; |
632 |
|
|
achBuffer[cchBuffer++] = '\n'; |
633 |
capela |
146 |
|
634 |
capela |
963 |
// And do the direct broadcasting... |
635 |
|
|
|
636 |
|
|
lscp_mutex_lock(pServer->connects.mutex); |
637 |
capela |
98 |
|
638 |
capela |
963 |
for (p = pServer->connects.first; p; p = p->next) { |
639 |
|
|
if (p->events & event) |
640 |
|
|
send(p->client.sock, achBuffer, cchBuffer, 0); |
641 |
|
|
} |
642 |
capela |
98 |
|
643 |
capela |
963 |
lscp_mutex_unlock(pServer->connects.mutex); |
644 |
capela |
98 |
|
645 |
capela |
963 |
return LSCP_OK; |
646 |
capela |
98 |
} |
647 |
|
|
|
648 |
|
|
|
649 |
|
|
/** |
650 |
|
|
* Send response for the current client callback request. |
651 |
|
|
* |
652 |
|
|
* @param pConnect Pointer to client connection instance structure. |
653 |
|
|
* @param pchBuffer Pointer to data to be sent to the client as response. |
654 |
|
|
* @param cchBuffer Length of the response data to be sent in bytes. |
655 |
|
|
* |
656 |
|
|
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
657 |
|
|
*/ |
658 |
|
|
lscp_status_t lscp_server_result ( lscp_connect_t *pConnect, const char *pchBuffer, int cchBuffer ) |
659 |
|
|
{ |
660 |
capela |
963 |
lscp_status_t ret = LSCP_FAILED; |
661 |
capela |
98 |
|
662 |
capela |
963 |
if (pConnect == NULL) |
663 |
|
|
return ret; |
664 |
|
|
if (pchBuffer == NULL || cchBuffer < 1) |
665 |
|
|
return ret; |
666 |
capela |
98 |
|
667 |
capela |
963 |
if (send(pConnect->client.sock, pchBuffer, cchBuffer, 0) != cchBuffer) |
668 |
|
|
lscp_socket_perror("lscp_server_result"); |
669 |
|
|
else |
670 |
|
|
ret = LSCP_OK; |
671 |
capela |
98 |
|
672 |
capela |
963 |
return ret; |
673 |
capela |
98 |
} |
674 |
|
|
|
675 |
|
|
|
676 |
|
|
/** |
677 |
|
|
* Register client as a subscriber of event broadcast messages. |
678 |
|
|
* |
679 |
capela |
132 |
* @param pConnect Pointer to client connection instance structure. |
680 |
capela |
146 |
* @param event Event type flag of the requesting client subscription. |
681 |
capela |
98 |
* |
682 |
|
|
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
683 |
|
|
*/ |
684 |
capela |
146 |
lscp_status_t lscp_server_subscribe ( lscp_connect_t *pConnect, lscp_event_t event ) |
685 |
capela |
98 |
{ |
686 |
capela |
963 |
if (pConnect == NULL) |
687 |
|
|
return LSCP_FAILED; |
688 |
|
|
if (event == LSCP_EVENT_NONE) |
689 |
|
|
return LSCP_FAILED; |
690 |
capela |
98 |
|
691 |
capela |
963 |
pConnect->events |= event; |
692 |
capela |
98 |
|
693 |
capela |
963 |
return LSCP_OK; |
694 |
capela |
98 |
} |
695 |
|
|
|
696 |
|
|
|
697 |
|
|
/** |
698 |
|
|
* Deregister client as subscriber of event broadcast messages. |
699 |
|
|
* |
700 |
capela |
146 |
* @param pConnect Pointer to client connection instance structure. |
701 |
|
|
* @param event Event type flag of the requesting client unsubscription. |
702 |
capela |
98 |
* |
703 |
|
|
* @returns LSCP_OK on success, LSCP_FAILED otherwise. |
704 |
capela |
963 |
*/ |
705 |
capela |
146 |
lscp_status_t lscp_server_unsubscribe ( lscp_connect_t *pConnect, lscp_event_t event ) |
706 |
capela |
98 |
{ |
707 |
capela |
963 |
if (pConnect == NULL) |
708 |
|
|
return LSCP_FAILED; |
709 |
|
|
if (event == LSCP_EVENT_NONE) |
710 |
|
|
return LSCP_FAILED; |
711 |
capela |
98 |
|
712 |
capela |
963 |
pConnect->events &= ~event; |
713 |
capela |
98 |
|
714 |
capela |
963 |
return LSCP_OK; |
715 |
capela |
98 |
} |
716 |
|
|
|
717 |
|
|
|
718 |
|
|
// end of server.c |