1 /*  cdrdao - write audio CD-Rs in disc-at-once mode
2  *
3  *  Copyright (C) 1998-2001  Andreas Mueller <andreas@daneb.de>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 #include <config.h>
21 
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <signal.h>
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 #include <assert.h>
31 
32 #ifdef linux
33 #include <linux/unistd.h>
34 #include <linux/types.h>
35 #endif
36 
37 #ifdef HAVE_SYS_MMAN_H
38 #include <sys/mman.h>
39 #endif
40 
41 #ifdef USE_POSIX_THREADS
42 #include <pthread.h>
43 #else
44 #include <sys/ipc.h>
45 #include <sys/shm.h>
46 #endif
47 
48 #include "dao.h"
49 #include "util.h"
50 #include "log.h"
51 #include "port.h"
52 #include "log.h"
53 
54 
55 #define DEBUG_WRITE 0
56 
57 
58 
59 #if defined(__FreeBSD__)
60 #define IPC_ARG_T void
61 #else
62 #define IPC_ARG_T msgbuf
63 #endif
64 
65 
66 struct ShmSegment {
67   int id;
68   char *buffer;
69 };
70 
71 struct Buffer {
72   long bufLen;  // number of blocks in buffer that should be written
73   TrackData::Mode mode; // data mode for writing
74   TrackData::Mode trackMode; // mode of track may differ from 'mode' if data
75                              // blocks must be encoded in audio blocks,
76                              // only used for message printing
77   TrackData::SubChannelMode subChanMode; // sub-channel data mode
78   int trackNr; // if != 0 a new track with given number has started
79   int trackProgress; // reading progress of current track 0..1000
80   char *buffer; // address of buffer that should be written
81 };
82 
83 struct BufferHeader {
84   long buffersRead;    // number of blocks that are read and put to the buffer
85   long buffersWritten; // number of blocks that were taken from the buffer
86   int buffersFilled;   // set to 1 by reader process when buffer is filled the
87                        // first time
88   int readerFinished;
89   int readerTerminated;
90   int terminateReader;
91 
92   long nofBuffers;     // number of available buffers
93   Buffer *buffers;
94 };
95 
96 // buffer size in blocks
97 int BUFFER_SIZE = 75;
98 
99 static int TERMINATE = 0;
100 
101 
102 
103 
104 static int getSharedMemory(long nofBuffers, BufferHeader **header,
105 			   long *nofSegments, ShmSegment **shmSegments);
106 static void releaseSharedMemory(long nofSegments, ShmSegment *shmSegments);
107 
108 
109 
terminationRequest(int sig)110 static RETSIGTYPE terminationRequest(int sig)
111 {
112   if (sig == SIGQUIT || sig == SIGTERM)
113     TERMINATE = 1;
114 
115 #if 0
116   if (sig == SIGCHLD) {
117     log_message(0, "SIGCHLD received.");
118   }
119 #endif
120 }
121 
122 
123 #ifndef USE_POSIX_THREADS
124 // Waits or polls for termination of a child process.
125 // noHang: 0: wait until child terminates, 1: just poll if child terminated
126 // status: filled with status information, only valid if 0 is returned
127 // return: 0: child exited
128 //         1: no child exited, can only happen if 'noHang' is 1
129 //         2: wait failed, 'errno' contains cause
waitForChild(int noHang,int * status)130 static int waitForChild(int noHang, int *status)
131 {
132   int ret;
133 
134   do {
135     if (noHang)
136       ret = wait3(status, WNOHANG, NULL);
137     else
138       ret = wait(status);
139 
140     if (ret > 0)
141       return 0;
142 
143     if (ret < 0 && errno != EINTR
144 #ifdef ERESTARTSYS
145 	&& errno != ERESTARTSYS
146 #endif
147 	) {
148       return 2;
149     }
150   } while (ret < 0);
151 
152   return 1;
153 }
154 
155 #endif
156 
157 // Blocks all signals that are handled by this module.
blockSignals()158 static void blockSignals()
159 {
160   sigset_t set;
161 
162   sigemptyset(&set);
163   sigaddset(&set, SIGCHLD);
164   sigaddset(&set, SIGQUIT);
165   sigaddset(&set, SIGTERM);
166 
167 #ifdef USE_POSIX_THREADS
168 
169 #ifdef HAVE_PTHREAD_SIGMASK
170   pthread_sigmask(SIG_BLOCK, &set, NULL);
171 #endif
172 
173 #else
174   sigprocmask(SIG_BLOCK, &set, NULL);
175 #endif
176 }
177 
178 // Blocks all signals that are handled by this module.
unblockSignals()179 static void unblockSignals()
180 {
181   sigset_t set;
182 
183   sigemptyset(&set);
184   sigaddset(&set, SIGCHLD);
185   sigaddset(&set, SIGQUIT);
186   sigaddset(&set, SIGTERM);
187 
188 #ifdef USE_POSIX_THREADS
189 
190 #ifdef HAVE_PTHREAD_SIGMASK
191   pthread_sigmask(SIG_UNBLOCK, &set, NULL);
192 #endif
193 
194 #else
195   sigprocmask(SIG_UNBLOCK, &set, NULL);
196 #endif
197 }
198 
199 // return: 0: OK
200 //         1: child process terminated and has been collected with 'wait()'
201 //         2: error -> child process must be terminated
writer(const Toc * toc,CdrDriver * cdr,BufferHeader * header,long lba,int speed)202 static int writer(const Toc *toc, CdrDriver *cdr, BufferHeader *header,
203 		  long lba, int speed)
204 {
205   long total = toc->length().lba() * AUDIO_BLOCK_LEN;
206   long totalTracks = toc->nofTracks();
207   long cnt = 0;
208   long blkCount = 0;
209   long len = 0;
210   long cntMb;
211   long lastMb = 0;
212   long buffered;
213   int buffFill;
214   int writerFill = 0;
215   int minFill = 100;
216   int maxFill = 0;
217   int actTrackNr = 0;
218   long actProgress;
219   TrackData::Mode dataMode;
220   TrackData::SubChannelMode subChanMode;
221 #ifndef USE_POSIX_THREADS
222   int status;
223 #endif
224 
225   log_message(3, "Waiting for reader process");
226 
227   while (header->buffersFilled == 0) {
228     sleep(1);
229 
230     if (header->readerTerminated) {
231       log_message(-2, "Reader process terminated abnormally.");
232       return 1;
233     }
234 
235 #ifndef USE_POSIX_THREADS
236   // Check if child has terminated
237     switch (waitForChild(1, &status)) {
238     case 0: // Child exited
239       log_message(-2, "Reader process terminated abnormally.");
240       return 1;
241     case 2:
242       log_message(-2, "wait failed: %s", strerror(errno));
243       return 2;
244     }
245 #endif
246   }
247 
248 #if DEBUG_WRITE
249   FILE *fp = fopen("test.out", "w");
250 #endif
251 
252   log_message(3, "Awaken, will start writing");
253 
254   if (cdr != NULL) {
255     cdr->sendWriteCdProgressMsg(CdrDriver::WCD_LEADIN,
256 				totalTracks, 0, 0, 0, 100);
257 
258     blockSignals();
259     if (cdr->startDao() != 0) {
260       unblockSignals();
261       return 2;
262     }
263     unblockSignals();
264   }
265 
266   do {
267     //log_message(4, "Slave: waiting for master.");
268 
269     while (header->buffersWritten == header->buffersRead) {
270       if (header->readerTerminated) {
271 	log_message(-2, "Reader process terminated abnormally.");
272 	return 1;
273       }
274 
275 #ifndef USE_POSIX_THREADS
276       // Check if child has terminated
277       switch (waitForChild(1, &status)) {
278       case 0: // Child exited
279 	log_message(-2, "Reader process terminated abnormally.");
280 	return 1;
281       case 2:
282 	log_message(-2, "wait failed: %s", strerror(errno));
283 	return 2;
284       }
285 #endif
286 
287       mSleep(10);
288     }
289 
290     Buffer &buf = header->buffers[header->buffersWritten % header->nofBuffers];
291     len = buf.bufLen;
292     dataMode = buf.mode;
293     subChanMode = buf.subChanMode;
294 
295     if (header->readerFinished) {
296       buffFill = 100;
297       if (maxFill == 0)
298 	maxFill = 100;
299     }
300     else {
301       buffered = header->buffersRead - header->buffersWritten;
302 
303       if (buffered == header->nofBuffers ||
304 	  buffered == header->nofBuffers - 1) {
305 	buffFill = 100;
306       }
307       else {
308 	buffFill = 100 * buffered;
309 	buffFill /= header->nofBuffers;
310       }
311 
312       if (buffFill > maxFill)
313 	maxFill = buffFill;
314     }
315 
316     if (buffFill < minFill)
317       minFill = buffFill;
318 
319     if (len == 0) {
320       // all data is written
321       log_message(1, "");
322       if (cdr == NULL)
323 	log_message(1, "Read %ld blocks.", blkCount);
324       else
325 	log_message(1, "Wrote %ld blocks. Buffer fill min %d%%/max %d%%.",
326 		blkCount, minFill, maxFill);
327 
328 #if DEBUG_WRITE
329       if (fp != NULL)
330 	fclose(fp);
331 #endif
332 
333       if (cdr != NULL) {
334 	cdr->sendWriteCdProgressMsg(CdrDriver::WCD_LEADOUT,
335 				    totalTracks, 0xaa, 1000, 1000, 100);
336 
337 	blockSignals();
338 	if (cdr->finishDao() != 0) {
339 	  unblockSignals();
340 	  return 2;
341 	}
342 	unblockSignals();
343       }
344 
345       return 0;
346     }
347 
348     cnt += len * AUDIO_BLOCK_LEN;
349     blkCount += len;
350 
351     if (buf.trackNr > 0) {
352       log_message(1, "Writing track %02d (mode %s/%s %s)...", buf.trackNr,
353 	      TrackData::mode2String(buf.trackMode),
354 	      TrackData::mode2String(dataMode),
355 	      TrackData::subChannelMode2String(subChanMode));
356 
357       actTrackNr = buf.trackNr;
358     }
359 
360     //log_message(4, "Slave: writing buffer %p (%ld).", buf, len);
361 
362 #if DEBUG_WRITE
363     if (fp != NULL) {
364       if (cdr != NULL) {
365 	log_message(0, "dao: blockSize: %ld", cdr->blockSize(dataMode, subChanMode));
366 
367 	fwrite(buf.buffer, cdr->blockSize(dataMode, subChanMode), len, fp);
368       }
369       else {
370 	fwrite(buf.buffer, 2352, len, fp);
371       }
372     }
373 #endif
374 
375     // Write track data.
376     if (cdr != NULL) {
377       blockSignals();
378       if (cdr->writeData(dataMode, subChanMode, lba, buf.buffer, len) != 0) {
379 	log_message(-2, "Writing failed - buffer under run?");
380 	unblockSignals();
381 	return 2;
382       }
383 
384       // Print stat line update every megabyte.
385       cntMb = cnt >> 20;
386       if (cntMb > lastMb) {
387         long totalcap, availcap;
388         if (cdr->readBufferCapacity(&totalcap, &availcap)) {
389           writerFill = (int)((1.0 - ((double)availcap / (double)totalcap))
390                              * 100.0);
391           log_message(1, "Wrote %ld of %ld MB (Buffers %3d%% %3d%%).\r",
392                   cnt >> 20, total >> 20, buffFill, writerFill);
393         } else {
394           log_message(1, "Wrote %ld of %ld MB (Buffer %3d%%).\r",
395                   cnt >> 20, total >> 20, buffFill);
396         }
397         lastMb = cntMb;
398       }
399 
400       unblockSignals();
401 
402       actProgress = cnt;
403       actProgress /= total / 1000;
404 
405       cdr->sendWriteCdProgressMsg(CdrDriver::WCD_DATA, totalTracks, actTrackNr,
406 				  buf.trackProgress, actProgress, buffFill,
407                                   writerFill);
408     }
409     else {
410       if (speed > 0) {
411 	log_message(1, "Read %ld of %ld MB (Buffer %3d%%).\r", cnt >> 20, total >> 20, buffFill);
412 	mSleep(1000 / speed);
413       }
414       else {
415 	log_message(1, "Read %ld of %ld MB.\r", cnt >> 20, total >> 20);
416       }
417     }
418 
419 
420     header->buffersWritten += 1;
421 
422   } while (!TERMINATE);
423 
424   log_message(-1, "Writing/simulation/read-test aborted on user request.");
425 
426   return 2;
427 }
428 
429 struct ReaderArgs {
430   const Toc *toc;
431   CdrDriver *cdr;
432   int swap;
433   BufferHeader *header;
434   long startLba;
435 };
436 
reader(void * args)437 static void *reader(void *args)
438 {
439   const Toc *toc = ((ReaderArgs*)args)->toc;
440   CdrDriver *cdr = ((ReaderArgs*)args)->cdr;
441   int swap = ((ReaderArgs*)args)->swap;
442   BufferHeader *header = ((ReaderArgs*)args)->header;
443   long lba = ((ReaderArgs*)args)->startLba + 150; // used to encode the sector
444                                                   // header (MSF)
445 
446   long length = toc->length().lba();
447   long n, rn;
448   int first = header->nofBuffers;
449   const Track *track;
450   int trackNr = toc->firstTrackNo() == 0 ? 1 : toc->firstTrackNo();
451   TrackData::Mode dataMode;
452   TrackData::SubChannelMode subChanMode;
453   int encodingMode = 0;
454   int subChanEncodingMode = 1;
455   int newTrack;
456   long tact; // number of blocks already read from current track
457   long tprogress;
458 
459   setRealTimeScheduling(4);
460 
461   giveUpRootPrivileges();
462 
463   if (cdr != NULL) {
464     if (cdr->bigEndianSamples() == 0) {
465       // swap samples for little endian recorders
466       swap = !swap;
467     }
468     encodingMode = cdr->encodingMode();
469   }
470   log_message(4, "Swap: %d", swap);
471 
472   TrackIterator itr(toc);
473   TrackReader reader;
474 
475   track = itr.first();
476   reader.init(track);
477 
478   if (reader.openData() != 0) {
479     log_message(-2, "Opening of track data failed.");
480     goto fail;
481   }
482 
483   newTrack = 1;
484   tact = 0;
485 
486   dataMode = (encodingMode == 0) ? TrackData::AUDIO : track->type();
487   subChanMode = track->subChannelType();
488 
489   if (cdr != NULL)
490     subChanEncodingMode = cdr->subChannelEncodingMode(subChanMode);
491 
492   do {
493     n = (length > BUFFER_SIZE ? BUFFER_SIZE : length);
494 
495     Buffer &buf = header->buffers[header->buffersRead % header->nofBuffers];
496 
497     do {
498       rn = reader.readData(encodingMode, subChanEncodingMode, lba, buf.buffer,
499 			   n);
500 
501       if (rn < 0) {
502 	log_message(-2, "Reading of track data failed.");
503 	goto fail;
504       }
505 
506       if (rn == 0) {
507 	track = itr.next();
508 	reader.init(track);
509 
510 	if (reader.openData() != 0) {
511 	  log_message(-2, "Opening of track data failed.");
512 	  goto fail;
513 	}
514 
515 	trackNr++;
516 
517 	if (encodingMode != 0)
518 	  dataMode = track->type();
519 
520 	subChanMode = track->subChannelType();
521 
522 	if (cdr != NULL)
523 	  subChanEncodingMode = cdr->subChannelEncodingMode(subChanMode);
524 
525 	newTrack = 1;
526 	tact = 0;
527       }
528     } while (rn == 0);
529 
530     lba += rn;
531     tact += rn;
532 
533     if (cdr != NULL &&
534 	((track->type() == TrackData::AUDIO && swap) ||
535 	 (encodingMode == 0 && cdr->bigEndianSamples() == 0))) {
536       // swap audio data
537       long blockLen = cdr->blockSize(dataMode, subChanMode);
538       char *brun = buf.buffer;
539       int i;
540 
541       for (i = 0; i < rn; i++, brun += blockLen)
542 	swapSamples((Sample *)brun, SAMPLES_PER_BLOCK);
543     }
544 
545     buf.bufLen = rn;
546     buf.mode = dataMode;
547     buf.trackMode = track->type();
548     buf.subChanMode = subChanMode;
549 
550     tprogress = tact * 1000;
551     tprogress /= track->length().lba();
552 
553     buf.trackProgress = tprogress;
554 
555     if (newTrack) {
556       // inform write process that it should print message about new track
557       buf.trackNr = trackNr;
558     }
559     else {
560       buf.trackNr = 0;
561     }
562 
563     header->buffersRead += 1;
564 
565     length -= rn;
566 
567     if (first > 0) {
568       first--;
569       if (first == 0 || length == 0) {
570 	log_message(3, "Buffer filled");
571 
572 	header->buffersFilled = 1;
573       }
574     }
575 
576     // wait for writing process to finish writing of previous buffer
577     //log_message(4, "Reader: waiting for Writer.");
578     while (header->buffersRead - header->buffersWritten
579 	   == header->nofBuffers &&
580 	   header->terminateReader == 0) {
581       mSleep(10);
582     }
583 
584 
585     newTrack = 0;
586   } while (length > 0 && header->terminateReader == 0);
587 
588   header->readerFinished = 1;
589 
590   if (header->terminateReader == 0) {
591     Buffer &buf1 = header->buffers[header->buffersRead % header->nofBuffers];
592     buf1.bufLen = 0;
593     buf1.trackNr = 0;
594     header->buffersRead += 1;
595   }
596 
597 #ifndef USE_POSIX_THREADS
598   // wait until we get killed
599   while (1)
600     sleep(1000);
601 
602   exit(0);
603 #endif
604 
605   return NULL;
606 
607 fail:
608   header->readerTerminated = 1;
609 
610 #ifndef USE_POSIX_THREADS
611   exit(1);
612 #endif
613 
614   return NULL;
615 }
616 
617 
writeDiskAtOnce(const Toc * toc,CdrDriver * cdr,int nofBuffers,int swap,int testMode,int speed)618 int writeDiskAtOnce(const Toc *toc, CdrDriver *cdr, int nofBuffers, int swap,
619 		    int testMode, int speed)
620 {
621   int err = 0;
622   BufferHeader *header = NULL;
623   long nofShmSegments = 0;
624   ShmSegment *shmSegments = NULL;
625   long startLba = 0;
626 
627 #ifdef USE_POSIX_THREADS
628   pthread_t readerThread;
629   pthread_attr_t readerThreadAttr;
630   int threadStarted = 0;
631 #else
632   int pid = 0;
633   int status;
634 #endif
635 
636 #if 1
637   if (nofBuffers < 10) {
638     nofBuffers = 10;
639     log_message(-1, "Adjusted number of FIFO buffers to 10.");
640   }
641 #endif
642 
643   if (getSharedMemory(nofBuffers, &header, &nofShmSegments,
644 		      &shmSegments)  != 0) {
645     releaseSharedMemory(nofShmSegments, shmSegments);
646     return 1;
647   }
648 
649   header->buffersRead = 0;
650   header->buffersWritten = 0;
651   header->buffersFilled = 0;
652   header->readerFinished = 0;
653   header->readerTerminated = 0;
654   header->terminateReader = 0;
655 
656   TERMINATE = 0;
657 
658   installSignalHandler(SIGINT, SIG_IGN);
659   installSignalHandler(SIGPIPE, SIG_IGN);
660   installSignalHandler(SIGALRM, SIG_IGN);
661   installSignalHandler(SIGCHLD, terminationRequest);
662   installSignalHandler(SIGQUIT, terminationRequest);
663   installSignalHandler(SIGTERM, terminationRequest);
664 
665   if (!testMode) {
666     const DiskInfo *di;
667 
668     if (cdr->initDao(toc) != 0) {
669       err = 1; goto fail;
670     }
671 
672     if ((di = cdr->diskInfo()) != NULL) {
673       startLba = di->thisSessionLba;
674     }
675   }
676 
677   // start reader process
678 #ifdef USE_POSIX_THREADS
679 
680   if (pthread_attr_init(&readerThreadAttr) != 0) {
681     log_message(-2, "pthread_attr_init failed: %s", strerror(errno));
682     err = 1; goto fail;
683   }
684 
685   ReaderArgs rargs;
686 
687   rargs.toc = toc;
688   rargs.cdr = cdr;
689   rargs.swap = swap;
690   rargs.header = header;
691   rargs.startLba = startLba;
692 
693   if (pthread_create(&readerThread, &readerThreadAttr, reader, &rargs) != 0) {
694     log_message(-2, "Cannot create thread: %s", strerror(errno));
695     pthread_attr_destroy(&readerThreadAttr);
696     err = 1; goto fail;
697   }
698   else {
699     threadStarted = 1;
700   }
701 
702 #else /* USE_POSIX_THREADS */
703 
704   if ((pid = fork()) == 0) {
705     // we are the new process
706 
707     setsid(); // detach from controlling terminal
708 
709 #ifdef HAVE_MLOCKALL
710     if (geteuid() == 0) {
711       if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0) {
712 	log_message(-1, "Cannot lock memory pages: %s", strerror(errno));
713       }
714       log_message(4, "Reader process memory locked");
715     }
716 #endif
717 
718     ReaderArgs rargs;
719 
720     rargs.toc = toc;
721     rargs.cdr = cdr;
722     rargs.swap = swap;
723     rargs.header = header;
724     rargs.startLba = startLba;
725 
726     reader(&rargs);
727   }
728   else if (pid < 0) {
729     log_message(-2, "fork failed: %s", strerror(errno));
730     err = 1; goto fail;
731   }
732 #endif /* USE_POSIX_THREADS */
733 
734   switch (setRealTimeScheduling(5)) {
735   case 1:
736     log_message(-1, "No super user permission to setup real time scheduling.");
737     break;
738   case 2:
739     log_message(2, "Real time scheduling not available.");
740     break;
741   }
742 
743 #ifdef HAVE_MLOCKALL
744   if (geteuid() == 0) {
745     if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0) {
746       log_message(-1, "Cannot lock memory pages: %s", strerror(errno));
747     }
748     log_message(4, "Memory locked");
749   }
750 #endif
751 
752   giveUpRootPrivileges();
753 
754   switch (writer(toc, cdr, header, startLba, speed)) {
755   case 1: // error, reader process terminated abnormally
756 #ifndef USE_POSIX_THREADS
757     pid = 0;
758 #endif
759     err = 1;
760     break;
761   case 2: // error, reader process must be terminated
762     err = 1;
763     break;
764   }
765 
766   if (err != 0 && cdr != NULL)
767     cdr->abortDao(); // abort writing process
768 
769  fail:
770 #ifdef HAVE_MUNLOCKALL
771   munlockall();
772 #endif
773 
774 #ifdef USE_POSIX_THREADS
775   if (threadStarted) {
776     header->terminateReader = 1;
777 
778     if (pthread_join(readerThread, NULL) != 0) {
779       log_message(-2, "pthread_join failed: %s", strerror(errno));
780       err = 1;
781     }
782 
783     pthread_attr_destroy(&readerThreadAttr);
784   }
785 
786 #else
787   if (pid != 0) {
788     if (kill(pid, SIGKILL) == 0) {
789       waitForChild(0, &status);
790     }
791   }
792 #endif
793 
794   releaseSharedMemory(nofShmSegments, shmSegments);
795 
796   installSignalHandler(SIGINT, SIG_DFL);
797   installSignalHandler(SIGPIPE, SIG_DFL);
798   installSignalHandler(SIGALRM, SIG_DFL);
799   installSignalHandler(SIGCHLD, SIG_DFL);
800   installSignalHandler(SIGQUIT, SIG_DFL);
801   installSignalHandler(SIGTERM, SIG_DFL);
802 
803   return err;
804 }
805 
806 
807 #ifdef USE_POSIX_THREADS
getSharedMemory(long nofBuffers,BufferHeader ** header,long * nofSegments,ShmSegment ** shmSegment)808 static int getSharedMemory(long nofBuffers,
809 			   BufferHeader **header, long *nofSegments,
810 			   ShmSegment **shmSegment)
811 {
812   long b;
813   long bufferSize = BUFFER_SIZE * (AUDIO_BLOCK_LEN + PW_SUBCHANNEL_LEN);
814 
815   *header = NULL;
816   *nofSegments = 0;
817   *shmSegment = NULL;
818 
819   if (nofBuffers <= 0) {
820     return 1;
821   }
822 
823   *shmSegment = new ShmSegment;
824   *nofSegments = 1;
825 
826   (*shmSegment)->id = -1;
827 
828   (*shmSegment)->buffer = new char[sizeof(BufferHeader) +
829 				  nofBuffers * sizeof(Buffer) +
830 				  nofBuffers * bufferSize];
831 
832   if ( (*shmSegment)->buffer == NULL) {
833     log_message(-2, "Cannot allocated memory for ring buffer.");
834     return 1;
835   }
836 
837   *header = (BufferHeader*)((*shmSegment)->buffer);
838   (*header)->nofBuffers = nofBuffers;
839   (*header)->buffers =
840 	(Buffer*)((*shmSegment)->buffer + sizeof(BufferHeader));
841 
842   char *bufferBase = (*shmSegment)->buffer + sizeof(BufferHeader) +
843                       nofBuffers * sizeof(Buffer);
844 
845   for (b = 0; b < nofBuffers; b++)
846     (*header)->buffers[b].buffer = bufferBase + b * bufferSize;
847 
848   return 0;
849 }
850 
releaseSharedMemory(long nofSegments,ShmSegment * shmSegment)851 static void releaseSharedMemory(long nofSegments, ShmSegment *shmSegment)
852 {
853   if (shmSegment == NULL || nofSegments == 0)
854     return;
855 
856   if (shmSegment->buffer != NULL) {
857     delete[] shmSegment->buffer;
858     shmSegment->buffer = NULL;
859   }
860 
861   delete shmSegment;
862 }
863 
864 #else /* USE_POSIX_THREADS */
865 
getSharedMemory(long nofBuffers,BufferHeader ** header,long * nofSegments,ShmSegment ** shmSegments)866 static int getSharedMemory(long nofBuffers,
867 			   BufferHeader **header, long *nofSegments,
868 			   ShmSegment **shmSegments)
869 {
870   long i, b;
871   long bufferSize = BUFFER_SIZE * (AUDIO_BLOCK_LEN + PW_SUBCHANNEL_LEN);
872   long maxSegmentSize = 0;
873   long bcnt = 0;
874 
875   *header = NULL;
876   *nofSegments = 0;
877   *shmSegments = NULL;
878 
879   if (nofBuffers <= 0) {
880     return 1;
881   }
882 
883 #if defined(linux) && defined(IPC_INFO)
884   struct shminfo info;
885 
886   if (shmctl(0, IPC_INFO, (struct shmid_ds*)&info) < 0) {
887     log_message(-1, "Cannot get IPC info: %s", strerror(errno));
888     maxSegmentSize = 4 * 1024 * 1024;
889     log_message(-1, "Assuming %ld MB shared memory segment size.",
890 	    maxSegmentSize >> 20);
891   }
892   else {
893     maxSegmentSize = info.shmmax;
894   }
895 
896 #elif defined(__FreeBSD__)
897   maxSegmentSize = 4 * 1024 * 1024; // 4 MB
898 #else
899   maxSegmentSize = 1 * 1024 * 1024; // 1 MB
900 #endif
901 
902   log_message(4, "Shm max segement size: %ld (%ld MB)", maxSegmentSize,
903 	  maxSegmentSize >> 20);
904 
905   if (maxSegmentSize < sizeof(BufferHeader) + nofBuffers * sizeof(Buffer)) {
906     log_message(-2, "Shared memory segment cannot hold a single buffer.");
907     return 1;
908   }
909 
910   maxSegmentSize -= sizeof(BufferHeader) + nofBuffers * sizeof(Buffer);
911 
912   long buffersPerSegment = maxSegmentSize / bufferSize;
913 
914   if (buffersPerSegment == 0) {
915     log_message(-2, "Shared memory segment cannot hold a single buffer.");
916     return 1;
917   }
918 
919   *nofSegments = nofBuffers / buffersPerSegment;
920 
921   if (nofBuffers % buffersPerSegment != 0)
922     *nofSegments += 1;
923 
924   *shmSegments = new ShmSegment[*nofSegments];
925 
926   log_message(4, "Using %ld shared memory segments.", *nofSegments);
927 
928   for (i = 0; i < *nofSegments; i++) {
929     (*shmSegments)[i].id = -1;
930     (*shmSegments)[i].buffer = NULL;
931   }
932 
933   long bufCnt = nofBuffers;
934   long n;
935   long segmentLength;
936   char *bufferBase;
937 
938   for (i = 0; i < *nofSegments; i++) {
939     n = (bufCnt > buffersPerSegment ? buffersPerSegment : bufCnt);
940 
941     segmentLength = n * bufferSize;
942     if (*header == NULL) {
943       // first segment contains the buffer header
944       segmentLength += sizeof(BufferHeader) + nofBuffers * sizeof(Buffer);
945     }
946 
947     (*shmSegments)[i].id = shmget(IPC_PRIVATE, segmentLength, 0600|IPC_CREAT);
948     if ((*shmSegments)[i].id < 0) {
949       log_message(-2, "Cannot create shared memory segment: %s",
950 	      strerror(errno));
951       log_message(-2, "Try to reduce the buffer count (option --buffers).");
952       return 1;
953     }
954 
955     (*shmSegments)[i].buffer = (char *)shmat((*shmSegments)[i].id, 0, 0);
956     if (((*shmSegments)[i].buffer) == NULL ||
957 	((*shmSegments)[i].buffer) == (char *)-1) {
958       (*shmSegments)[i].buffer = NULL;
959       log_message(-2, "Cannot get shared memory: %s", strerror(errno));
960       log_message(-2, "Try to reduce the buffer count (option --buffers).");
961       return 1;
962     }
963 
964 
965     if (*header == NULL) {
966       bufferBase = (*shmSegments)[i].buffer + sizeof(BufferHeader) +
967 	           nofBuffers * sizeof(Buffer);
968       *header = (BufferHeader*)(*shmSegments)[i].buffer;
969       (*header)->nofBuffers = nofBuffers;
970       (*header)->buffers =
971 	(Buffer*)((*shmSegments)[i].buffer + sizeof(BufferHeader));
972     }
973     else {
974       bufferBase = (*shmSegments)[i].buffer;
975     }
976 
977     for (b = 0; b < n; b++)
978       (*header)->buffers[bcnt++].buffer = bufferBase + b * bufferSize;
979 
980     bufCnt -= n;
981   }
982 
983   assert(bcnt == nofBuffers);
984 
985   return 0;
986 }
987 
releaseSharedMemory(long nofSegments,ShmSegment * shmSegments)988 static void releaseSharedMemory(long nofSegments, ShmSegment *shmSegments)
989 {
990   long i;
991 
992   if (shmSegments == NULL || nofSegments == 0)
993     return;
994 
995   for (i = 0; i < nofSegments; i++) {
996     if (shmSegments[i].id >= 0) {
997       if (shmSegments[i].buffer != NULL) {
998 	if (shmdt(shmSegments[i].buffer) != 0) {
999 	  log_message(-2, "shmdt: %s", strerror(errno));
1000 	}
1001       }
1002       if (shmctl(shmSegments[i].id, IPC_RMID, NULL) != 0) {
1003 	log_message(-2, "Cannot remove shared memory: %s", strerror(errno));
1004       }
1005     }
1006   }
1007 
1008   delete[] shmSegments;
1009 }
1010 #endif /* USE_POSIX_THREADS */
1011