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