00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <pthread.h>
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027 #include <unistd.h>
00028 #include <signal.h>
00029 #include <sched.h>
00030 #include <sys/types.h>
00031 #include <sys/socket.h>
00032 #include <netinet/in.h>
00033 #include <arpa/inet.h>
00034 #include <glib.h>
00035
00036 #include "SoundProxy.hh"
00037
00038
00039 static bool bDaemon;
00040 static bool bDebug;
00041 clSoundProxy *SoundProxy;
00042
00043
00044 int main (int argc, char *argv[])
00045 {
00046 int iRetVal;
00047
00048 bDaemon = false;
00049 bDebug = false;
00050 signal(SIGPIPE, SIG_IGN);
00051 signal(SIGFPE, SIG_IGN);
00052 if (argc > 1)
00053 {
00054 if (strcmp(argv[1], "-D") == 0)
00055 bDaemon = true;
00056 if (strcmp(argv[1], "--debug") == 0)
00057 bDebug = true;
00058 if (strcmp(argv[1], "--version") == 0)
00059 {
00060 printf("SoundProxy v%i.%i.%i\n",
00061 SP_VERSMAJ, SP_VERSMIN, SP_VERSPL);
00062 printf("Copyright (C) 2000-2001 Jussi Laako\n\n");
00063 }
00064 if (strcmp(argv[1], "--help") == 0)
00065 {
00066 printf("%s [-D|--debug|--version|--help]\n", argv[0]);
00067 }
00068 }
00069 if (bDaemon)
00070 {
00071 if (fork() == 0)
00072 {
00073 setsid();
00074 freopen("/dev/null", "r", stdin);
00075 freopen("/dev/null", "a", stdout);
00076 freopen("/dev/null", "a", stderr);
00077 }
00078 else
00079 {
00080 return 0;
00081 }
00082 }
00083 SoundProxy = new clSoundProxy();
00084 iRetVal = SoundProxy->Exec();
00085 delete SoundProxy;
00086 return iRetVal;
00087 }
00088
00089
00090 void *WrapSoundInThread (void *vpData)
00091 {
00092 return SoundProxy->SoundInThread(vpData);
00093 }
00094
00095
00096 void *WrapWaitConnectThread (void *vpData)
00097 {
00098 return SoundProxy->WaitConnectThread(vpData);
00099 }
00100
00101
00102 void *WrapServeClientThread (void *vpData)
00103 {
00104 return SoundProxy->ServeClientThread(vpData);
00105 }
00106
00107
00108 inline void clSoundProxy::AddToLog (char cMark, const char *cpLogEntry)
00109 {
00110 MutexClass.Wait();
00111 Log.Add(cMark, cpLogEntry);
00112 MutexClass.Release();
00113 }
00114
00115
00116 inline void clSoundProxy::AddToLog (char cMark, const char *cpLogEntry,
00117 int iErrno)
00118 {
00119 MutexClass.Wait();
00120 Log.Add(cMark, cpLogEntry, iErrno);
00121 MutexClass.Release();
00122 }
00123
00124
00125 int clSoundProxy::FindFreeSlot ()
00126 {
00127 int iResIdx = -1;
00128 int iLoopCntr;
00129
00130 MutexClass.Wait();
00131 for (iLoopCntr = 0; iLoopCntr < SP_MAXCLIENTS; iLoopCntr++)
00132 {
00133 if (!bServeClient[iLoopCntr])
00134 {
00135 iResIdx = iLoopCntr;
00136 break;
00137 }
00138 }
00139 MutexClass.Release();
00140 return iResIdx;
00141 }
00142
00143
00144 clSoundProxy::clSoundProxy ()
00145 {
00146 int iLoopCntr;
00147
00148 bRun = true;
00149 Cfg.SetFileName(SP_CFGFILE);
00150 if (!Cfg.GetStr("LogFile", cpLogFile))
00151 strcpy(cpLogFile, SP_DEF_LOGFILE);
00152 Cfg.GetStr("ServerHost", cpServerHost);
00153 Cfg.GetInt("ServerPort", &iServerPort);
00154 Cfg.GetInt("Port", &iServicePort);
00155 Log.Open(cpLogFile);
00156 cpFirstMsg = (char *) FirstMsg.Size(GLOBAL_HEADER_LEN);
00157 cpDataMsg = (char *) DataMsg.Size(SP_BUFFER_SIZE);
00158 DataMsg.Lock();
00159 for (iLoopCntr = 0; iLoopCntr < SP_MAXCLIENTS; iLoopCntr++)
00160 bServeClient[iLoopCntr] = false;
00161 }
00162
00163
00164 clSoundProxy::~clSoundProxy ()
00165 {
00166 }
00167
00168
00169 int clSoundProxy::Exec ()
00170 {
00171 int iSigNum;
00172 sigset_t ssSignals;
00173
00174 sigemptyset(&ssSignals);
00175 sigaddset(&ssSignals, SIGHUP);
00176 sigaddset(&ssSignals, SIGINT);
00177 sigaddset(&ssSignals, SIGTERM);
00178 sigprocmask(SIG_BLOCK, &ssSignals, NULL);
00179 AddToLog('*', "Started");
00180 pthread_create(&ptidSoundIn, NULL, WrapSoundInThread, NULL);
00181 pthread_create(&ptidWaitConnect, NULL, WrapWaitConnectThread, NULL);
00182 sigwait(&ssSignals, &iSigNum);
00183 switch (iSigNum)
00184 {
00185 case SIGHUP:
00186 AddToLog(' ', "Received SIGHUP");
00187 break;
00188 case SIGINT:
00189 AddToLog(' ', "Received SIGINT");
00190 break;
00191 case SIGTERM:
00192 AddToLog(' ', "Received SIGTERM");
00193 break;
00194 default:
00195 AddToLog(' ', "Received unexpected signal");
00196 break;
00197 }
00198 Stop();
00199 pthread_join(ptidWaitConnect, NULL);
00200 pthread_join(ptidSoundIn, NULL);
00201 AddToLog('*', "Stopped");
00202 return 0;
00203 }
00204
00205
00206 void clSoundProxy::Stop ()
00207 {
00208 MutexClass.Wait();
00209 bRun = false;
00210 MutexClass.Release();
00211 }
00212
00213
00214 void *clSoundProxy::SoundInThread (void *vpData)
00215 {
00216 bool bLocalRun;
00217 int iSockH;
00218 char cpConvBuf[SP_CONV_BUF_LEN];
00219 char *cpMsgBuf;
00220 #ifndef BSDSYS
00221 uid_t uidCurrent;
00222 struct sched_param sSchedParam;
00223 #endif
00224 clAlloc MsgBuf;
00225 clSockClie SClient;
00226 clSockOp SOp;
00227
00228 iSockH = SClient.Connect(cpServerHost, NULL, iServerPort);
00229 if (iSockH < 0)
00230 {
00231 g_snprintf(cpConvBuf, SP_CONV_BUF_LEN,
00232 "Unable to connect to %s:%i", cpServerHost, iServerPort);
00233 AddToLog('!', cpConvBuf, SClient.GetErrno());
00234 Stop();
00235 return NULL;
00236 }
00237 SOp.SetHandle(iSockH);
00238 g_snprintf(cpConvBuf, SP_CONV_BUF_LEN, "Connected to %s:%i",
00239 cpServerHost, iServerPort);
00240 AddToLog('#', cpConvBuf);
00241 if (SOp.ReadSelect(SP_1ST_MSG_TIMEOUT))
00242 {
00243 MutexData.Wait();
00244 if (SOp.ReadN(cpFirstMsg, GLOBAL_HEADER_LEN) != GLOBAL_HEADER_LEN)
00245 {
00246 AddToLog('!', "Unable to get first message from server",
00247 SOp.GetErrno());
00248 Stop();
00249 }
00250 MutexData.Release();
00251 }
00252 else
00253 {
00254 AddToLog('!', "Unable to get first message from server (timeout)");
00255 Stop();
00256 }
00257 cpMsgBuf = (char *) MsgBuf.Size(SP_BUFFER_SIZE);
00258 MsgBuf.Lock();
00259 AddToLog(' ', "SoundIn thread running");
00260 #ifndef BSDSYS
00261 uidCurrent = getuid();
00262 setuid(0);
00263 sSchedParam.sched_priority = sched_get_priority_min(SCHED_FIFO) +
00264 SP_SCHED_PRIORITY;
00265 pthread_setschedparam(pthread_self(), SCHED_FIFO, &sSchedParam);
00266 setuid(uidCurrent);
00267 #endif
00268 SOp.DisableNagle();
00269 SOp.SetTypeOfService(IPTOS_LOWDELAY);
00270 MutexClass.Wait();
00271 bLocalRun = bRun;
00272 MutexClass.Release();
00273 while (bLocalRun)
00274 {
00275 if (SOp.ReadSelect(SP_MSG_TIMEOUT))
00276 {
00277 if (SOp.ReadN(cpMsgBuf, SP_BUFFER_SIZE) == SP_BUFFER_SIZE)
00278 {
00279 MutexData.Wait();
00280 memcpy(cpDataMsg, cpMsgBuf, SP_BUFFER_SIZE);
00281 CondData.NotifyAll();
00282 MutexData.Release();
00283 }
00284 else
00285 {
00286 AddToLog('!', "Message receive error", SOp.GetErrno());
00287 Stop();
00288 }
00289 }
00290 MutexClass.Wait();
00291 bLocalRun = bRun;
00292 MutexClass.Release();
00293 }
00294 SOp.Shutdown(2);
00295 SOp.Close();
00296 AddToLog(' ', "SoundIn thread ending");
00297 return NULL;
00298 }
00299
00300
00301 void *clSoundProxy::WaitConnectThread (void *vpData)
00302 {
00303 bool bLocalRun = true;
00304 int iSockH;
00305 int iSlotIdx;
00306 clSockServ SServer;
00307
00308 AddToLog(' ', "WaitConnect thread running");
00309 SServer.Bind(iServicePort);
00310 while (bLocalRun)
00311 {
00312 iSlotIdx = FindFreeSlot();
00313 if (iSlotIdx >= 0)
00314 {
00315 iSockH = SServer.WaitForConnect(SP_WAIT_CONN_TIMEOUT);
00316 if (iSockH >= 0)
00317 {
00318 MutexClass.Wait();
00319 bServeClient[iSlotIdx] = true;
00320 iClientSockH[iSlotIdx] = iSockH;
00321 MutexClass.Release();
00322 pthread_create(&ptidServeClient[iSlotIdx], NULL,
00323 WrapServeClientThread, GINT_TO_POINTER(iSlotIdx));
00324 }
00325 }
00326 else
00327 {
00328 sched_yield();
00329 }
00330 MutexClass.Wait();
00331 bLocalRun = bRun;
00332 MutexClass.Release();
00333 }
00334 AddToLog(' ', "WaitConnect thread ending");
00335 return NULL;
00336 }
00337
00338
00339 void *clSoundProxy::ServeClientThread (void *vpData)
00340 {
00341 bool bLocalRun = true;
00342 int iThisIdx = GPOINTER_TO_INT(vpData);
00343 int iSockH;
00344 char *cpMsgBuf;
00345 char cpHdrBuf[GLOBAL_HEADER_LEN];
00346 char cpLogBuf[SP_CONV_BUF_LEN];
00347 socklen_t iPeerAddrLen;
00348 struct sockaddr_in sPeerAddr;
00349 #ifndef BSDSYS
00350 uid_t uidCurrent;
00351 struct sched_param sSchedParam;
00352 #endif
00353 clAlloc MsgBuf;
00354 clSockOp SOp;
00355
00356 MutexClass.Wait();
00357 iSockH = iClientSockH[iThisIdx];
00358 MutexClass.Release();
00359 SOp.SetHandle(iSockH);
00360
00361 iPeerAddrLen = sizeof(sPeerAddr);
00362 SOp.GetPeerName((struct sockaddr *) &sPeerAddr, &iPeerAddrLen);
00363 g_snprintf(cpLogBuf, SP_CONV_BUF_LEN, "Client connected from %s:%i",
00364 inet_ntoa(sPeerAddr.sin_addr), ntohs(sPeerAddr.sin_port));
00365 AddToLog('+', cpLogBuf);
00366 if (!SOp.DisableNagle())
00367 {
00368 AddToLog('#', "Unable to disable nagle algorithm", SOp.GetErrno());
00369 }
00370 if (!SOp.SetTypeOfService(IPTOS_LOWDELAY))
00371 {
00372 AddToLog('#', "Unable set type of service flag", SOp.GetErrno());
00373 }
00374 cpMsgBuf = (char *) MsgBuf.Size(SP_BUFFER_SIZE);
00375 MsgBuf.Lock();
00376 MutexData.Wait();
00377 memcpy(cpHdrBuf, cpFirstMsg, GLOBAL_HEADER_LEN);
00378 MutexData.Release();
00379 if (SOp.WriteSelect(SP_1ST_MSG_TIMEOUT))
00380 {
00381 if (SOp.WriteN(cpHdrBuf, GLOBAL_HEADER_LEN) != GLOBAL_HEADER_LEN)
00382 {
00383 AddToLog('!', "Unable to send first message", SOp.GetErrno());
00384 bLocalRun = false;
00385 }
00386 }
00387 else
00388 {
00389 AddToLog('!', "Unable to send first message (timeout)");
00390 bLocalRun = false;
00391 }
00392 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
00393 AddToLog(' ', "ServeClient thread running");
00394 #ifndef BSDSYS
00395 uidCurrent = getuid();
00396 setuid(0);
00397 sSchedParam.sched_priority = sched_get_priority_min(SCHED_FIFO) +
00398 SP_SCHED_PRIORITY;
00399 pthread_setschedparam(pthread_self(), SCHED_FIFO, &sSchedParam);
00400 setuid(uidCurrent);
00401 #endif
00402 while (bLocalRun)
00403 {
00404 MutexData.Wait();
00405 CondData.Wait(MutexData.GetPtr());
00406 memcpy(cpMsgBuf, cpDataMsg, SP_BUFFER_SIZE);
00407 MutexData.Release();
00408 MutexClass.Wait();
00409 bLocalRun = bRun;
00410 MutexClass.Release();
00411 if (!bLocalRun) break;
00412 if (SOp.WriteSelect(SP_MSG_TIMEOUT))
00413 {
00414 if (SOp.WriteN(cpMsgBuf, SP_BUFFER_SIZE) != SP_BUFFER_SIZE)
00415 {
00416 AddToLog('-', "Client disconnected?", SOp.GetErrno());
00417 break;
00418 }
00419 }
00420 }
00421 SOp.Close();
00422 AddToLog(' ', "ServeClient thread ending");
00423 return NULL;
00424 }
00425