static void drivers()
{
+ check_privileges();
driver_manager()->print(cout);
}
exit(1);
}
set_scheduler();
+ check_privileges();
return run(&i);
}
/// has insufficient privilege, this may emit a warning and/or do nothing.
void set_scheduler();
+ /// Check the user's privileges, and warn if they are inappropriate.
+ void check_privileges();
+
/// Virtual base class for all driver classes.
class Base {
private:
// program.
namespace Driver {
AudioInput * Tone(const char * parameter);
+ AudioInput * AudioInALSA(const char * parameter);
AudioOutput * AudioSink(const char * parameter);
AudioOutput * AudioOutALSA(const char * parameter);
Codec * CodecNoOp(const char * parameter);
namespace Enumerator {
std::ostream & Tone(std::ostream &);
std::ostream & AudioSink(std::ostream &);
+ std::ostream & AudioInALSA(std::ostream &);
std::ostream & AudioOutALSA(std::ostream &);
std::ostream & CodecNoOp(std::ostream &);
std::ostream & FramerNoOp(std::ostream &);
--- /dev/null
+/// The ALSA audio output driver.
+
+#include "drivers.h"
+#include <alsa/asoundlib.h>
+#include <sstream>
+#include <stdexcept>
+
+namespace FreeDV {
+ std::ostream & ALSAEnumerate(std::ostream & stream, snd_pcm_stream_t mode);
+
+ /// Audio output "sink", discards the audio, for testing.
+ class AudioInALSA : public AudioInput {
+ private:
+ snd_pcm_t * handle;
+
+ public:
+
+ /// Instantiate the audio sink.
+ AudioInALSA(const char * parameters);
+ ~AudioInALSA();
+
+ /// Return the number of audio samples the device can handle in
+ /// a write without blocking.
+ virtual std::size_t
+ ready();
+
+ /// Read audio into the "short" type.
+ virtual std::size_t
+ read16(std::int16_t * array, std::size_t length);
+ };
+
+ AudioInALSA::AudioInALSA(const char * p)
+ : AudioInput("alsa", p)
+ {
+ const int error = snd_pcm_open(
+ &handle,
+ p,
+ SND_PCM_STREAM_CAPTURE,
+ 0);
+
+ if ( error ) {
+ std::ostringstream str;
+
+ str << "Can't open audio device " << p << ": " << snd_strerror(error);
+ throw std::runtime_error(str.str().c_str());
+ }
+ }
+
+ AudioInALSA::~AudioInALSA()
+ {
+ }
+
+ // Read audio into the "short" type.
+ std::size_t
+ AudioInALSA::read16(std::int16_t * array, std::size_t length)
+ {
+ return length;
+ }
+
+ AudioInput *
+ Driver::AudioInALSA(const char * parameter)
+ {
+ return new ::FreeDV::AudioInALSA(parameter);
+ }
+
+ std::ostream &
+ Enumerator::AudioInALSA(std::ostream & stream)
+ {
+ return ALSAEnumerate(stream, SND_PCM_STREAM_CAPTURE);
+ }
+
+ std::size_t
+ AudioInALSA::ready()
+ {
+ return SIZE_MAX;
+ }
+
+ static bool
+ initializer()
+ {
+ driver_manager()->register_audio_input(
+ "alsa",
+ Driver::AudioInALSA,
+ Enumerator::AudioInALSA);
+ return true;
+ }
+ static const bool initialized = initializer();
+}
--- /dev/null
+#include <iostream>
+#include <unistd.h>
+#include <limits.h>
+#include <grp.h>
+
+static const char insufficient_privilege_message[] =
+"Warning: The user running the program is not a member of the "
+"\"audio\" group.\n"
+"This may make it impossible to access audio devices.\n"
+"To fix, use one of these solutions:\n"
+"\tadd the user to the \"audio\" group.\n"
+"\tadd the setgid-audio privilege to the executable file\n"
+"with these commands, as root:\n"
+"\n"
+"\t\tchgrp audio filename\n"
+"\t\tchmod 2755 filename\n"
+"\n"
+"Alternatively, you can execute this program as root.\n\n";
+
+namespace FreeDV {
+ void
+ check_privileges()
+ {
+ const int uid = getuid();
+ const int euid = geteuid();
+
+ if ( uid == 0 || euid == 0 )
+ return;
+
+ const struct group * audio = getgrnam("audio");
+ const int gid = getgid();
+ const int egid = getgid();
+
+ if ( audio ) {
+ gid_t groups[NGROUPS_MAX];
+ int length = sizeof(groups) / sizeof(*groups);
+
+ if ( gid == audio->gr_gid || egid == audio->gr_gid )
+ return;
+
+ if ( getgroups(length, groups) == 0 ) {
+ for ( int i = 0; i < length; i++ ) {
+ if ( groups[i] == audio->gr_gid )
+ return;
+ }
+ }
+ std::cerr << insufficient_privilege_message << std::endl;
+ }
+ }
+}