common: prevent buffer overflow
[supercollider.git] / server / scsynth / scsynth_main.cpp
blob16fbcb4fd0abcb990017894a46585657ad75baa0
1 /*
2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "SC_WorldOptions.h"
24 #include <cstring>
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <math.h>
29 #include "clz.h"
30 #include <stdexcept>
31 #ifdef _WIN32
32 # include <pthread.h>
33 # include <winsock2.h>
34 #else
35 # include <sys/wait.h>
36 #endif
38 #ifdef _WIN32
40 // according to this page: http://www.mkssoftware.com/docs/man3/setlinebuf.3.asp
41 // setlinebuf is equivalent to the setvbuf call below.
42 inline int setlinebuf(FILE *stream)
44 return setvbuf( stream, (char*)0, _IONBF, 0 );
47 #endif
50 void Usage();
51 void Usage()
53 scprintf(
54 "supercollider_synth options:\n"
55 " -u <udp-port-number> a port number 0-65535\n"
56 " -t <tcp-port-number> a port number 0-65535\n"
57 " -c <number-of-control-bus-channels> (default %d)\n"
58 " -a <number-of-audio-bus-channels> (default %d)\n"
59 " -i <number-of-input-bus-channels> (default %d)\n"
60 " -o <number-of-output-bus-channels> (default %d)\n"
61 " -z <block-size> (default %d)\n"
62 " -Z <hardware-buffer-size> (default %d)\n"
63 " -S <hardware-sample-rate> (default %d)\n"
64 " -b <number-of-sample-buffers> (default %d)\n"
65 " -n <max-number-of-nodes> (default %d)\n"
66 " -d <max-number-of-synth-defs> (default %d)\n"
67 " -m <real-time-memory-size> (default %d)\n"
68 " -w <number-of-wire-buffers> (default %d)\n"
69 " -r <number-of-random-seeds> (default %d)\n"
70 " -D <load synthdefs? 1 or 0> (default %d)\n"
71 " -R <publish to Rendezvous? 1 or 0> (default %d)\n"
72 " -l <max-logins> (default %d)\n"
73 " maximum number of named return addresses stored\n"
74 " also maximum number of tcp connections accepted\n"
75 " -p <session-password>\n"
76 " When using TCP, the session password must be the first command sent.\n"
77 " The default is no password.\n"
78 " UDP ports never require passwords, so for security use TCP.\n"
79 " -N <cmd-filename> <input-filename> <output-filename> <sample-rate> <header-format> <sample-format>\n"
80 #ifdef __APPLE__
81 " -I <input-streams-enabled>\n"
82 " -O <output-streams-enabled>\n"
83 #endif
84 #if (_POSIX_MEMLOCK - 0) >= 200112L
85 " -L enable memory locking\n"
86 #endif
87 " -H <hardware-device-name>\n"
88 " -v <verbosity>\n"
89 " 0 is normal behaviour\n"
90 " -1 suppresses informational messages\n"
91 " -2 suppresses informational and many error messages\n"
92 " -U <ugen-plugins-path> a colon-separated list of paths\n"
93 " if -U is specified, the standard paths are NOT searched for plugins.\n"
94 " -P <restricted-path> \n"
95 " if specified, prevents file-accessing OSC commands from\n"
96 " accessing files outside <restricted-path>.\n"
97 "\nTo quit, send a 'quit' command via UDP or TCP, or press ctrl-C.\n\n",
98 kDefaultWorldOptions.mNumControlBusChannels,
99 kDefaultWorldOptions.mNumAudioBusChannels,
100 kDefaultWorldOptions.mNumInputBusChannels,
101 kDefaultWorldOptions.mNumOutputBusChannels,
102 kDefaultWorldOptions.mBufLength,
103 kDefaultWorldOptions.mPreferredHardwareBufferFrameSize,
104 kDefaultWorldOptions.mPreferredSampleRate,
105 kDefaultWorldOptions.mNumBuffers,
106 kDefaultWorldOptions.mMaxNodes,
107 kDefaultWorldOptions.mMaxGraphDefs,
108 kDefaultWorldOptions.mRealTimeMemorySize,
109 kDefaultWorldOptions.mMaxWireBufs,
110 kDefaultWorldOptions.mNumRGens,
111 kDefaultWorldOptions.mRendezvous,
112 kDefaultWorldOptions.mLoadGraphDefs,
113 kDefaultWorldOptions.mMaxLogins
115 exit(1);
118 #define checkNumArgs(n) \
119 if (i+n > argc) { \
120 if (n == 2) scprintf("ERROR: Argument expected after option %s\n", argv[j]); \
121 else scprintf("ERROR: More arguments expected after option %s\n", argv[j]); \
122 Usage(); \
124 i += n;
127 int main(int argc, char* argv[]);
128 int main(int argc, char* argv[])
130 setlinebuf(stdout);
132 #ifdef _WIN32
133 #ifdef SC_WIN32_STATIC_PTHREADS
134 // initialize statically linked pthreads library
135 pthread_win32_process_attach_np();
136 #endif
138 // initialize winsock
139 WSAData wsaData;
140 int nCode;
141 if ((nCode = WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0) {
142 scprintf( "WSAStartup() failed with error code %d.\n", nCode );
143 return 1;
145 #endif
147 int udpPortNum = -1;
148 int tcpPortNum = -1;
150 WorldOptions options = kDefaultWorldOptions;
152 for (int i=1; i<argc;) {
153 if (argv[i][0] != '-' || argv[i][1] == 0 || strchr("utaioczblndpmwZrNSDIOMHvRUhPL", argv[i][1]) == 0) {
154 scprintf("ERROR: Invalid option %s\n", argv[i]);
155 Usage();
157 int j = i;
158 switch (argv[j][1]) {
159 case 'u' :
160 checkNumArgs(2);
161 udpPortNum = atoi(argv[j+1]);
162 break;
163 case 't' :
164 checkNumArgs(2);
165 tcpPortNum = atoi(argv[j+1]);
166 break;
167 case 'a' :
168 checkNumArgs(2);
169 options.mNumAudioBusChannels = atoi(argv[j+1]);
170 break;
171 case 'i' :
172 checkNumArgs(2);
173 options.mNumInputBusChannels = atoi(argv[j+1]);
174 break;
175 case 'o' :
176 checkNumArgs(2);
177 options.mNumOutputBusChannels = atoi(argv[j+1]);
178 break;
179 case 'c' :
180 checkNumArgs(2);
181 options.mNumControlBusChannels = atoi(argv[j+1]);
182 break;
183 case 'z' :
184 checkNumArgs(2);
185 options.mBufLength = NEXTPOWEROFTWO(atoi(argv[j+1]));
186 break;
187 case 'Z' :
188 checkNumArgs(2);
189 options.mPreferredHardwareBufferFrameSize = NEXTPOWEROFTWO(atoi(argv[j+1]));
190 break;
191 case 'b' :
192 checkNumArgs(2);
193 options.mNumBuffers = NEXTPOWEROFTWO(atoi(argv[j+1]));
194 break;
195 case 'l' :
196 checkNumArgs(2);
197 options.mMaxLogins = NEXTPOWEROFTWO(atoi(argv[j+1]));
198 break;
199 case 'n' :
200 checkNumArgs(2);
201 options.mMaxNodes = NEXTPOWEROFTWO(atoi(argv[j+1]));
202 break;
203 case 'd' :
204 checkNumArgs(2);
205 options.mMaxGraphDefs = NEXTPOWEROFTWO(atoi(argv[j+1]));
206 break;
207 case 'p' :
208 checkNumArgs(2);
209 options.mPassword = argv[j+1];
210 break;
211 case 'm' :
212 checkNumArgs(2);
213 options.mRealTimeMemorySize = atoi(argv[j+1]);
214 break;
215 case 'w' :
216 checkNumArgs(2);
217 options.mMaxWireBufs = atoi(argv[j+1]);
218 break;
219 case 'r' :
220 checkNumArgs(2);
221 options.mNumRGens = atoi(argv[j+1]);
222 break;
223 case 'S' :
224 checkNumArgs(2);
225 options.mPreferredSampleRate = (uint32)atof(argv[j+1]);
226 break;
227 case 'D' :
228 checkNumArgs(2);
229 options.mLoadGraphDefs = atoi(argv[j+1]);
230 break;
231 case 'N' :
232 #ifdef NO_LIBSNDFILE
233 scprintf("NRT mode not supported: scsynth compiled without libsndfile\n");
234 exit(0);
235 #endif
236 // -N cmd-filename input-filename output-filename sample-rate header-format sample-format
237 checkNumArgs(7);
238 options.mRealTime = false;
239 options.mNonRealTimeCmdFilename = strcmp(argv[j+1], "_") ? argv[j+1] : 0;
240 options.mNonRealTimeInputFilename = strcmp(argv[j+2], "_") ? argv[j+2] : 0;
241 options.mNonRealTimeOutputFilename = argv[j+3];
242 options.mPreferredSampleRate = (uint32)atof(argv[j+4]);
243 options.mNonRealTimeOutputHeaderFormat = argv[j+5];
244 options.mNonRealTimeOutputSampleFormat = argv[j+6];
245 break;
246 #ifdef __APPLE__
247 case 'I' :
248 checkNumArgs(2);
249 options.mInputStreamsEnabled = argv[j+1];
250 break;
251 case 'O' :
252 checkNumArgs(2);
253 options.mOutputStreamsEnabled = argv[j+1];
254 break;
255 case 'M':
256 #endif
257 case 'H' :
258 checkNumArgs(2);
259 options.mInDeviceName = argv[j+1];
260 #ifdef __APPLE__
261 if (i+1>argc || argv[j+2][0]=='-')
263 options.mOutDeviceName = options.mInDeviceName;
265 else
267 // If there's a second argument then the user wants separate I/O devices
268 options.mOutDeviceName = argv[j+2];
269 ++i;
271 #else
272 options.mOutDeviceName = options.mInDeviceName; // Non-Mac platforms always use same device
273 #endif
274 break;
275 case 'L' :
276 checkNumArgs(1);
277 #if (_POSIX_MEMLOCK - 0) >= 200112L
278 options.mMemoryLocking = true;
279 #else
280 options.mMemoryLocking = false;
281 #endif
282 break;
283 case 'v' :
284 checkNumArgs(2);
285 options.mVerbosity = atoi(argv[j+1]);
286 break;
287 case 'R' :
288 checkNumArgs(2);
289 options.mRendezvous = atoi(argv[j+1]) > 0;
290 break;
291 case 'U' :
292 checkNumArgs(2);
293 options.mUGensPluginPath = argv[j+1];
294 break;
295 case 'P' :
296 checkNumArgs(2);
297 options.mRestrictedPath = argv[j+1];
298 break;
299 case 'h':
300 default: Usage();
303 if (udpPortNum == -1 && tcpPortNum == -1 && options.mRealTime) {
304 scprintf("ERROR: There must be a -u and/or a -t options, or -N for nonrealtime.\n");
305 Usage();
307 if (options.mNumInputBusChannels + options.mNumOutputBusChannels > options.mNumAudioBusChannels) {
308 scprintf("ERROR: number of audio bus channels < inputs + outputs.\n");
309 Usage();
312 if (options.mRealTime) {
313 int port = (udpPortNum > 0) ? udpPortNum
314 : tcpPortNum;
316 options.mSharedMemoryID = port;
317 } else
318 options.mSharedMemoryID = 0;
321 struct World *world = World_New(&options);
322 if (!world) return 1;
324 if (!options.mRealTime) {
325 #ifdef NO_LIBSNDFILE
326 return 1;
327 #else
328 int exitCode = 0;
329 try {
330 World_NonRealTimeSynthesis(world, &options);
331 } catch (std::exception& exc) {
332 scprintf("%s\n", exc.what());
333 exitCode = 1;
335 return exitCode;
336 #endif
339 if (udpPortNum >= 0) {
340 if (!World_OpenUDP(world, udpPortNum)) {
341 World_Cleanup(world);
342 return 1;
345 if (tcpPortNum >= 0) {
346 if (!World_OpenTCP(world, tcpPortNum, options.mMaxLogins, 8)) {
347 World_Cleanup(world);
348 return 1;
352 if(options.mVerbosity >=0){
353 #ifdef NDEBUG
354 scprintf("SuperCollider 3 server ready.\n");
355 #else
356 scprintf("SuperCollider 3 server ready (debug build).\n");
357 #endif
359 fflush(stdout);
361 World_WaitForQuit(world);
364 #ifdef _WIN32
365 // clean up winsock
366 WSACleanup();
368 #ifdef SC_WIN32_STATIC_PTHREADS
369 // clean up statically linked pthreads
370 pthread_win32_process_detach_np();
371 #endif // SC_WIN32_STATIC_PTHREADS
372 #endif // _WIN32
374 return 0;