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