From: bruceperens Date: Sun, 9 Mar 2014 07:34:13 +0000 (+0000) Subject: On Linux, warn about not being in the audio group, if the audio group exists. X-Git-Url: http://git.whiteaudio.com/gitweb/?a=commitdiff_plain;h=6b96cd3b41c787c2197a96e86a62324b3506ad0f;p=freetel-svn-tracking.git On Linux, warn about not being in the audio group, if the audio group exists. git-svn-id: https://svn.code.sf.net/p/freetel/code@1420 01035d8c-6547-0410-b346-abe4f91aad63 --- diff --git a/freedv-server/source/big_main.cpp b/freedv-server/source/big_main.cpp index f365e487..50ca23cc 100644 --- a/freedv-server/source/big_main.cpp +++ b/freedv-server/source/big_main.cpp @@ -26,6 +26,7 @@ using namespace FreeDV; static void drivers() { + check_privileges(); driver_manager()->print(cout); } @@ -191,5 +192,6 @@ main(int argc, char * * argv) exit(1); } set_scheduler(); + check_privileges(); return run(&i); } diff --git a/freedv-server/source/drivers.h b/freedv-server/source/drivers.h index fc3e37d3..d0d62073 100644 --- a/freedv-server/source/drivers.h +++ b/freedv-server/source/drivers.h @@ -108,6 +108,9 @@ namespace FreeDV { /// 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: @@ -702,6 +705,7 @@ namespace FreeDV { // 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); @@ -717,6 +721,7 @@ namespace FreeDV { 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 &); diff --git a/freedv-server/source/platform/linux/audio_in_alsa.cpp b/freedv-server/source/platform/linux/audio_in_alsa.cpp new file mode 100644 index 00000000..808136e0 --- /dev/null +++ b/freedv-server/source/platform/linux/audio_in_alsa.cpp @@ -0,0 +1,88 @@ +/// The ALSA audio output driver. + +#include "drivers.h" +#include +#include +#include + +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(); +} diff --git a/freedv-server/source/platform/linux/privilege.cpp b/freedv-server/source/platform/linux/privilege.cpp new file mode 100644 index 00000000..72c2acf6 --- /dev/null +++ b/freedv-server/source/platform/linux/privilege.cpp @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +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; + } + } +}