1 /*
2 server.c:
3
4 Copyright (C) 2013 V Lazzarini, John ffitch
5
6 This file is part of Csound.
7
8 The Csound Library is free software; you can redistribute it
9 and/or modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
12
13 Csound is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with Csound; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21 02110-1301 USA
22 */
23 #ifdef NACL
24 typedef unsigned int u_int32_t;
25 #endif
26
27 /* Haiku 'int32' etc definitions in net headers conflict with sysdep.h */
28 #define __HAIKU_CONFLICT
29
30 #include "csoundCore.h"
31 #if defined(WIN32) && !defined(__CYGWIN__)
32 #include <winsock2.h>
33 #include <ws2tcpip.h>
34 #else
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #endif
39
40 typedef struct {
41 int port;
42 int sock;
43 CSOUND *cs;
44 void *thrid;
45 void *cb;
46 struct sockaddr_in server_addr;
47 unsigned char status;
48 } UDPCOM;
49
50 #define MAXSTR 1048576 /* 1MB */
51
udp_socksend(CSOUND * csound,int * sock,const char * addr,int port,const char * msg)52 static void udp_socksend(CSOUND *csound, int *sock, const char *addr,
53 int port, const char *msg) {
54 struct sockaddr_in server_addr;
55 if(*sock <= 0) {
56 #if defined(WIN32) && !defined(__CYGWIN__)
57 WSADATA wsaData = {0};
58 int err;
59 if (UNLIKELY((err=WSAStartup(MAKEWORD(2,2), &wsaData))!= 0))
60 csound->Warning(csound, Str("UDP: Winsock2 failed to start: %d"), err);
61 return;
62 #endif
63 *sock = socket(AF_INET, SOCK_DGRAM, 0);
64 if (UNLIKELY(*sock < 0)) {
65 csound->Warning(csound, Str("UDP: error creating socket"));
66 return;
67 }
68 #ifndef WIN32
69 if (UNLIKELY(fcntl(*sock, F_SETFL, O_NONBLOCK)<0)) {
70 csound->Warning(csound, Str("UDP Server: Cannot set nonblock"));
71 if (*sock>=0) close(*sock);
72 return;
73 }
74 #else
75 {
76 u_long argp = 1;
77 err = ioctlsocket(*sock, FIONBIO, &argp);
78 if (UNLIKELY(err != NO_ERROR)) {
79 csound->Warning(csound, Str("UDP Server: Cannot set nonblock"));
80 closesocket(*sock);
81 return;
82 }
83 }
84 #endif
85
86 }
87 server_addr.sin_family = AF_INET; /* it is an INET address */
88 #if defined(WIN32) && !defined(__CYGWIN__)
89 server_addr.sin_addr.S_un.S_addr = inet_addr(addr);
90 #else
91 inet_aton(addr, &server_addr.sin_addr); /* the server IP address */
92 #endif
93 server_addr.sin_port = htons((int) port); /* the port */
94
95 if (UNLIKELY(sendto(*sock, (void*) msg, strlen(msg)+1, 0,
96 (const struct sockaddr *) &server_addr,
97 sizeof(server_addr)) < 0)) {
98 csound->Warning(csound, Str("UDP: sock end failed"));
99 }
100 }
101
102
udp_recv(void * pdata)103 static uintptr_t udp_recv(void *pdata){
104 struct sockaddr from;
105 socklen_t clilen = sizeof(from);
106 UDPCOM *p = (UDPCOM *) pdata;
107 CSOUND *csound = p->cs;
108 int port = p->port;
109 char *orchestra = csound->Calloc(csound, MAXSTR);
110 int sock = 0;
111 int received, cont = 0;
112 char *start = orchestra;
113 size_t timout = (size_t) lround(1000/csound->GetKr(csound));
114
115 csound->Message(csound, Str("UDP server started on port %d\n"),port);
116 while (p->status) {
117 if ((received =
118 recvfrom(p->sock, (void *)orchestra, MAXSTR, 0, &from, &clilen)) <= 0) {
119 csoundSleep(timout ? timout : 1);
120 continue;
121 }
122 else {
123 orchestra[received] = '\0'; // terminate string
124 if(strlen(orchestra) < 2) continue;
125 if (csound->oparms->echo)
126 csound->Message(csound, "%s", orchestra);
127 if (strncmp("!!close!!",orchestra,9)==0 ||
128 strncmp("##close##",orchestra,9)==0) {
129 csoundInputMessageAsync(csound, "e 0 0");
130 break;
131 }
132 if(*orchestra == '&') {
133 csoundInputMessageAsync(csound, orchestra+1);
134 }
135 else if(*orchestra == '$') {
136 csoundReadScoreAsync(csound, orchestra+1);
137 }
138 else if(*orchestra == '@') {
139 char chn[128];
140 MYFLT val;
141 sscanf(orchestra+1, "%s", chn);
142 val = atof(orchestra+1+strlen(chn));
143 csoundSetControlChannel(csound, chn, val);
144 }
145 else if(*orchestra == '%') {
146 char chn[128];
147 char *str;
148 sscanf(orchestra+1, "%s", chn);
149 str = cs_strdup(csound, orchestra+1+strlen(chn));
150 csoundSetStringChannel(csound, chn, str);
151 csound->Free(csound, str);
152 }
153 else if(*orchestra == ':') {
154 char addr[128], chn[128], *msg;
155 int sport, err = 0;
156 MYFLT val;
157 sscanf(orchestra+2, "%s", chn);
158 sscanf(orchestra+2+strlen(chn), "%s", addr);
159 sport = atoi(orchestra+3+strlen(addr)+strlen(chn));
160 if(*(orchestra+1) == '@') {
161 val = csoundGetControlChannel(csound, chn, &err);
162 msg = (char *) csound->Calloc(csound, strlen(chn) + 32);
163 sprintf(msg, "%s::%f", chn, val);
164 }
165 else if (*(orchestra+1) == '%') {
166 MYFLT *pstring;
167 if (csoundGetChannelPtr(csound, &pstring, chn,
168 CSOUND_STRING_CHANNEL | CSOUND_OUTPUT_CHANNEL)
169 == CSOUND_SUCCESS) {
170 STRINGDAT* stringdat = (STRINGDAT*) pstring;
171 int size = stringdat->size;
172 spin_lock_t *lock =
173 (spin_lock_t *) csoundGetChannelLock(csound, (char*) chn);
174 msg = (char *) csound->Calloc(csound, strlen(chn) + size);
175 if (lock != NULL)
176 csoundSpinLock(lock);
177 sprintf(msg, "%s::%s", chn, stringdat->data);
178 if (lock != NULL)
179 csoundSpinUnLock(lock);
180 } else err = -1;
181 }
182 else err = -1;
183 if(!err) {
184 udp_socksend(csound, &sock, addr, sport,msg);
185 csound->Free(csound, msg);
186 }
187 else
188 csound->Warning(csound, Str("could not retrieve channel %s"), chn);
189 }
190 else if(*orchestra == '{' || cont) {
191 char *cp;
192 if((cp = strrchr(orchestra, '}')) != NULL) {
193 if(*(cp-1) != '}') {
194 *cp = '\0';
195 cont = 0;
196 } else {
197 orchestra += received;
198 cont = 1;
199 }
200 }
201 else {
202 orchestra += received;
203 cont = 1;
204 }
205 if(!cont) {
206 orchestra = start;
207 //csound->Message(csound, "%s\n", orchestra+1);
208 csoundCompileOrcAsync(csound, orchestra+1);
209 }
210 }
211 else {
212 //csound->Message(csound, "%s\n", orchestra);
213 csoundCompileOrcAsync(csound, orchestra);
214 }
215 }
216 }
217 csound->Message(csound, Str("UDP server on port %d stopped\n"),port);
218 csound->Free(csound, start);
219 // csound->Message(csound, "orchestra dealloc\n");
220 if(sock > 0)
221 #ifndef WIN32
222 close(sock);
223 #else
224 closesocket(sock);
225 #endif
226 return (uintptr_t) 0;
227
228 }
229
udp_start(CSOUND * csound,UDPCOM * p)230 static int udp_start(CSOUND *csound, UDPCOM *p)
231 {
232 #if defined(WIN32) && !defined(__CYGWIN__)
233 WSADATA wsaData = {0};
234 int err;
235 if (UNLIKELY((err=WSAStartup(MAKEWORD(2,2), &wsaData))!= 0)){
236 csound->Warning(csound, Str("Winsock2 failed to start: %d"), err);
237 return CSOUND_ERROR;
238 }
239 #endif
240 p->cs = csound;
241 p->sock = socket(AF_INET, SOCK_DGRAM, 0);
242 #ifndef WIN32
243 if (UNLIKELY(fcntl(p->sock, F_SETFL, O_NONBLOCK)<0)) {
244 csound->Warning(csound, Str("UDP Server: Cannot set nonblock"));
245 if (p->sock>=0) close(p->sock);
246 return CSOUND_ERROR;
247 }
248 #else
249 {
250 u_long argp = 1;
251 err = ioctlsocket(p->sock, FIONBIO, &argp);
252 if (UNLIKELY(err != NO_ERROR)) {
253 csound->Warning(csound, Str("UDP Server: Cannot set nonblock"));
254 closesocket(p->sock);
255 return CSOUND_ERROR;
256 }
257 }
258 #endif
259 if (UNLIKELY(p->sock < 0)) {
260 csound->Warning(csound, Str("error creating socket"));
261 return CSOUND_ERROR;
262 }
263 /* create server address: where we want to send to and clear it out */
264 memset(&p->server_addr, 0, sizeof(p->server_addr));
265 p->server_addr.sin_family = AF_INET; /* it is an INET address */
266 p->server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
267 p->server_addr.sin_port = htons((int) p->port); /* the port */
268 /* associate the socket with the address and port */
269 if (UNLIKELY(bind(p->sock, (struct sockaddr *) &p->server_addr,
270 sizeof(p->server_addr)) < 0)) {
271 csound->Warning(csound, Str("bind failed"));
272 p->thrid = NULL;
273 #ifndef WIN32
274 close(p->sock);
275 #else
276 closesocket(p->sock);
277 #endif
278 return CSOUND_ERROR;
279 }
280 /* set status flag */
281 p->status = 1;
282 /* create thread */
283 p->thrid = csoundCreateThread(udp_recv, (void *) p);
284 return CSOUND_SUCCESS;
285 }
286
csoundUDPServerClose(CSOUND * csound)287 int csoundUDPServerClose(CSOUND *csound)
288 {
289 UDPCOM *p = (UDPCOM *) csound->QueryGlobalVariable(csound,"::UDPCOM");
290 if (p != NULL) {
291 /* unset status flag */
292 p->status = 0;
293 /* wait for server thread to close */
294 csoundJoinThread(p->thrid);
295 /* close socket */
296 #ifndef WIN32
297 close(p->sock);
298 #else
299 closesocket(p->sock);
300 #endif
301 csound->DestroyGlobalVariable(csound,"::UDPCOM");
302 return CSOUND_SUCCESS;
303 }
304 else return CSOUND_ERROR;
305 }
306
csoundUDPServerStart(CSOUND * csound,unsigned int port)307 int csoundUDPServerStart(CSOUND *csound, unsigned int port){
308 UDPCOM *connection;
309 csound->CreateGlobalVariable(csound, "::UDPCOM", sizeof(UDPCOM));
310 connection = (UDPCOM *) csound->QueryGlobalVariable(csound, "::UDPCOM");
311 if (connection != NULL){
312 connection->port = port;
313 if(connection->status) {
314 csound->Warning(csound, Str("UDP Server: already running"));
315 return CSOUND_ERROR;
316 }
317 else {
318 int res = udp_start(csound, connection);
319 if (res != CSOUND_SUCCESS) {
320 csound->Warning(csound, Str("UDP Server: could not start"));
321 csound->DestroyGlobalVariable(csound,"::UDPCOM");
322 return CSOUND_ERROR;
323 }
324 else return CSOUND_SUCCESS;
325 }
326 }
327 else {
328 csound->Warning(csound, Str("UDP Server: failed to allocate memory"));
329 return CSOUND_ERROR;
330 }
331 }
332
csoundUDPServerStatus(CSOUND * csound)333 int csoundUDPServerStatus(CSOUND *csound) {
334 UDPCOM *p = (UDPCOM *) csound->QueryGlobalVariable(csound,"::UDPCOM");
335 if (p != NULL) {
336 return p->port;
337 }
338 else return CSOUND_ERROR;
339 }
340
341 #define UDPMSG 1024
342
343 typedef struct {
344 int port;
345 const char *addr;
346 int sock;
347 void (*cb)(CSOUND *csound,int attr, const char *format, va_list args);
348 } UDPCONS;
349
350
udp_msg_callback(CSOUND * csound,int attr,const char * format,va_list args)351 static void udp_msg_callback(CSOUND *csound, int attr, const char *format,
352 va_list args) {
353 UDPCONS *p;
354 p = (UDPCONS *) csound->QueryGlobalVariable(csound, "::UDPCONS");
355 if(p) {
356 char string[UDPMSG];
357 va_list nargs;
358 va_copy(nargs, args);
359 vsnprintf(string, UDPMSG, format, args);
360 udp_socksend(csound, &(p->sock), p->addr, p->port, string);
361 if(p->cb)
362 p->cb(csound, attr, format, nargs);
363 va_end(nargs);
364 }
365 }
366
udp_console_stop(CSOUND * csound,void * pp)367 static int udp_console_stop(CSOUND *csound, void *pp) {
368 UDPCONS *p = (UDPCONS *) pp;
369 if(p) {
370 csoundSetMessageCallback(csound, p->cb);
371 #ifndef WIN32
372 close(p->sock);
373 #else
374 closesocket(p->sock);
375 #endif
376 csound->DestroyGlobalVariable(csound,"::UDPCONS");
377 }
378 return CSOUND_SUCCESS;
379 }
380
381
csoundUDPConsole(CSOUND * csound,const char * addr,int port,int mirror)382 int csoundUDPConsole(CSOUND *csound, const char *addr, int port, int
383 mirror) {
384 UDPCONS *p = (UDPCONS *) csound->QueryGlobalVariable(csound, "::UDPCONS");
385 if(p == NULL) {
386 csound->CreateGlobalVariable(csound, "::UDPCONS", sizeof(UDPCONS));
387 p = (UDPCONS *) csound->QueryGlobalVariable(csound, "::UDPCONS");
388 if(p) {
389 p->port = port;
390 p->addr = cs_strdup(csound, (char *) addr);
391 p->sock = 0;
392 if(mirror)
393 p->cb = csound->csoundMessageCallback_;
394 csound->SetMessageCallback(csound, udp_msg_callback);
395 csound->RegisterResetCallback(csound, p, udp_console_stop);
396 } else {
397 csound->Warning(csound, "Could not set UDP console\n");
398 return CSOUND_ERROR;
399 }
400 return CSOUND_SUCCESS;
401 }
402 return CSOUND_ERROR;
403 }
404
csoundStopUDPConsole(CSOUND * csound)405 void csoundStopUDPConsole(CSOUND *csound) {
406 UDPCONS *p;
407 csound->CreateGlobalVariable(csound, "::UDPCONS", sizeof(UDPCONS));
408 p = (UDPCONS *) csound->QueryGlobalVariable(csound, "::UDPCONS");
409 udp_console_stop(csound, p);
410 }
411