How to use the pulseaudio API as root? - c

I am currently trying to use the pulseaudio simple API to record microphone data from my USB sound card with my raspberry pi 3. I used the example program parec-simple from pulseaudio in my own program and it works quite nice.
The program i used this code for is accessing gpio's so i need to run this as root. However, when i try to execute the program as root, i get the following errors:
Home directory not accessible: Permission denied
W: [pulseaudio] core-util.c: Failed to open configuration file '/root/.config/pulse//daemon.conf': Permission denied
W: [pulseaudio] daemon-conf.c: Failed to open configuration file: Permission denied
pa_simple_new() failed: Connection refused
the code is used is the following:
static const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 44100,
.channels = 1
};
pa_simple *s = NULL;
int ret = 1;
int error;
/* Create the recording stream */
if (!(s = pa_simple_new(NULL, argv[0], PA_STREAM_RECORD, NULL, "record", &ss, NULL, NULL, &error))) {
fprintf(stderr, "pa_simple_new() failed: %s\n", pa_strerror(error));
goto finish;
}
while(1)
{
uint8_t buf[BUFSIZE];
/* Record some data ... */
if (pa_simple_read(s, buf, sizeof(buf), &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error));
goto finish;
}
/* And write it to STDOUT */
if (loop_write(STDOUT_FILENO, buf, sizeof(buf)) != sizeof(buf)) {
fprintf(stderr, __FILE__": write() failed: %s\n", strerror(errno));
goto finish;
}
}
ret = 0;
finish:
if (s)
pa_simple_free(s);
return ret;
I already tried chown pi:pi /home/pi as suggested here to try to fix it but it doesn't work. changing the owner of /home/pi from pi to root didn't work for me either.
I also tried a clean reinstall of pulseaudio but unfortunately it didn't fix it.
So what can i do to fix these errors?

If you need to run your program as user root, then you must impersonate root. I don't know if pulseaudio looks at the username in order to find configuration files, or it looks at the $HOME variable. In the second case, maybe that by setting HOME to the home of a "working" user helps.
Anyway what you told about the situation is clear: pulseaudio does not find a file:
'/root/.config/pulse//daemon.conf'
Place a correct "daemon.conf" in that directory - probably you can copy it from somewhere (like /home/auser/.config/pulse/daemon.conf).
Consider that directories with name starting with a dot are normally hidden; if using a file manager you must enable "show hidden files", if you use the shell, ls -a can help.
Your first target is to confirm that the file is there, and your program should not complain about a missing/unreadable config file. Then, maybe other errors will show up but, one after another, you can eliminate them.

When you run process with sudo it does not change Home directory to /root - sudo echo $HOME # /home/username. You need to specify HOME directory with by running sudo HOME=/root executable.
When you want to access pulseaudio from root you need to run it system wide with command - sudo pulseaudio --system=true.
Then you will receive an error from pulseaudio:
W: [pulseaudio] protocol-native.c: Denied access to client with invalid authentication data.
Which can be solved by adding root user to audio-pulse group - sudo adduser root pulse-access.

Related

Why are GPIO permissions different in C program from console?

I think I'm running the same functionality in a C program as on the console, but I have permissions problems setting a GPIO direction.
These console commands, run as a normal user, work fine:
$ echo 436 > /sys/class/gpio/export
$ echo out > /sys/class/gpio/gpio436/direction
... while this code, in a C program (which I intend to have the same effect), fails:
...
int gpioNumber = 436;
/* Successful open... */
FILE *exportNode = fopen("/sys/class/gpio/export", "w");
if (exportNode == NULL) {
printf("Unable to open /sys/class/gpio/export\n");
return CT_GPIO_FAIL;
}
char buffer[100];
sprintf(buffer,"%i",mGpioNumber);
if (fprintf(exportNode, buffer) != (strlen(buffer)))
{
printf("Error writing to /sys/class/gpio/export\n");
return CT_GPIO_FAIL;
}
fclose(exportNode);
/* At this point the node /sys/class/gpio/gpio436 exists - I can see it in the console. */
// Set the pin to be input or output by writing "out" to /sys/class/gpio/gpio[xx]/direction
// I think this is the same file as gets opened in the second shell command.
printf("About to open direction.\n");
sprintf(buffer,"/sys/class/gpio/gpio%i/direction",mGpioNumber);
/* This line fails! */
FILE *directionNode = fopen(buffer, "w");
/* .. and the error is reported. */
if (directionNode == NULL) {
int errnum = errno;
printf("Unable to open %s. Last error: %s\n",buffer,strerror(errnum));
return CT_GPIO_FAIL;
}
This is the error report:
Unable to open /sys/class/gpio/gpio436/direction. Last error: Permission denied
If the direction node hasn't been created, I get a different error, so the reported permissions error seems to be accurately reported.
I run the program from the same console as the shell commands (after unexporting the GPIO, but without any other intervening actions. The program runs without error using sudo.
Why aren't the permissions issues the same inside this C program run from the console, as they are in shell commands? Am I missing a simple code error?
This is all running on a Jetson Xavier NX, Ubuntu 18.04.4 LTS
Well ... It does work as written, with the addition of a few milliseconds of sleep between "export" and trying to open the direction node. Apparently the creation of the direction node isn't synchronous in the write to export, at least on this system.
I apologize for the noise.

stderr with libssh in pty mode

I'm using libssh to execute commands on a remote server.
Here is my code (return codes are not checked here for simplification, but all of them are OK):
#include <stdio.h>
#include <stdlib.h>
#include <libssh/libssh.h>
int main() {
/* opening session and channel */
ssh_session session = ssh_new();
ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
ssh_options_set(session, SSH_OPTIONS_PORT_STR, "22");
ssh_options_set(session, SSH_OPTIONS_USER, "julien");
ssh_connect(session);
ssh_userauth_autopubkey(session, NULL);
ssh_channel channel = ssh_channel_new(session);
ssh_channel_open_session(channel);
/* command execution */
ssh_channel_request_exec(channel, "echo 'foo' && whoam");
char *buffer_stdin = calloc(1024, sizeof(char));
ssh_channel_read(channel, buffer_stdin, 1024, 0);
printf("stdout: %s\n", buffer_stdin);
free(buffer_stdin);
char *buffer_stderr = calloc(1024, sizeof(char));
ssh_channel_read(channel, buffer_stderr, 1024, 1);
printf("stderr: %s", buffer_stderr);
free(buffer_stderr);
ssh_channel_free(channel);
ssh_free(session);
return EXIT_SUCCESS;
}
The output is as axpected:
stdout: foo
stderr: command not found: whoam
Now if I add a call to ssh_channel_request_pty just after ssh_channel_open_session:
...
ssh_channel channel = ssh_channel_new(session);
ssh_channel_open_session(channel);
ssh_channel_request_pty(channel);
ssh_channel_request_exec(channel, "echo 'foo' && whoam");
...
There is no stderr output any more:
stdout: foo
stderr:
And if I change the command by:
ssh_channel_request_exec(channel, "whoam");
Now the error output is read on stdout!
stdout: command not found: whoam
stderr:
I am missing something with ssh_channel_request_pty?
For information, I'm using it because on some servers I get the following error when running a command with sudo:
sudo: sorry, you must have a tty to run sudo
Conceptually, a pty represents a terminal (or, at an even lower level, a serial port) -- there's only one channel in each direction. The standard output and standard error of a process running in a pty both go to the same place by default.
(Consider what happens when you run a command in a terminal, for instance -- if you haven't redirected anything, both stdout and stderr will appear on screen, and there's no way to tell them apart.)
Generally speaking, if you need to automate running commands remotely as root, you should either SSH in as root, or grant the user sudo access with the NOPASSWD option. Don't automate entering the password.

How to invoke linux ssh from the custom shell?

I have a custom shell, which runs in one process. I want to use the inbuilt ssh available in Linux. I want to redirect the user I/O from the custom CLI to the actual linux shell.
Here is what I have done to do ssh:
INT4 do_ssh(tCliHandle CliHandle, CHR1 *destIp)
{
FILE *writePipe = NULL;
char readbuff[1024];
char cmd[1024];
memset(cmd,'\0',sizeof(cmd));
sprintf(cmd,"/usr/bin/ssh %s",destIp);
printf("cmd = %s\r\n",cmd);
writePipe = popen(cmd,"w");
if(writePipe == NULL)
{
return -1;
}
while( fgets(readbuff,sizeof(readbuff) - 1, writePipe) != NULL)
{
CliPrintf(CliHandle,"%s\r\n", readbuff);
//printf("%s",readbuff);
}
printf("Closing writePipe\r\n");
pclose(writePipe);
return 0;
}
The problem, which I see is that after doing ssh I can't see the session anymore.
Here is the output:
cust# ssh 192.168.10.42
cmd = /usr/bin/ssh 192.168.10.42
Closing writePipe
Pseudo-terminal will not be allocated because stdin is not a terminal.
Could not create directory '/root/.ssh'.
root#192.168.10.42's password:
ssh 192.168.10.42
My expectation is to keep the session alive and let the user keep sending the command from the terminal and it should get printed on the terminal.
But, I see that after ssh the session is closed. I want to keep it open.
I want to close only when the window is closed or exit is passed.
What I should do? Any hints will be helpful.

Can't write in a .spp file remotely

I don't know if somebody can help me with my problem, because I don't know if it is a programming issue or a networking issue. However, I will explain the whole suitation.
I am working on a mesh testbed, and I would like to run the pacifier protocol which is developed by someone for some reason.
The problem is that when I try to run the probing (which is a part of the configuration to run the pacifier) on the testbed nodes using the perl script “run_probing.pl”, the .spp file that contains the results didn’t created, but when I run the probing command manually on any two nodes for example using the command ./probing –a 10.1.1.26 -i 26, I got that .spp files.
The command sent from perl script using pssh service. I check the process id using the command ps aux, and I found that the command
$cmd ="/usr/bin/pssh -p 1 -t 120 -l amr -A -h temp_overlay.crap \"/map/more_pacifier/probing -a $allusnips[$sitecount] -i $allusnipids[$sitecount] > /dev/null 2>&1 & \" ";
is executed in the background for both devices since the process id for that command is in the process id list which means that there is no problem with the pssh service itself. But still I don't get any .spp file in the /map/more_pacifier directory which is specified in the probing.c code.
For the probing.c code, since it is very long (around 400 lines), so I will put the part corresponding to writing in the .spp file:
FILE *fl;
if(argc < 3) {
usage();
exit(-1);
}
while ((ch = getopt(argc, argv, "h:a:i:d:")) != -1)
switch (ch) {
case 'h':
help();
exit(0);
case 'a':
inaddr = inet_addr(optarg); // IP Address
memset((char*) &myip, 0x0, sizeof(myip));
bcopy((char*) &inaddr, (char*) &myip, sizeof(inaddr));
strcpy(myipstr, inet_ntoa(myip));
fprintf(stderr, "My IP is %s, %s\n", inet_ntoa(myip), myipstr);
break;
case 'i':
myipid = atoi(optarg);
fprintf(stderr, "My ID is %d\n", myipid);
break;
default:
usage();
exit(-1);
}
sprintf(lossfile, "/map/more_pacifier/%d.spp", myipid);
fl = fopen(lossfile, "w");
for(i=0; i<NUMNODES; i++)
ND.NHBR_Table[i].recvcount = 0;
ND.numProbePktsSent = 0;
I don't know why I'm not getting the .spp file when running the command via run_probing.pl script.
Is it something with the permission access for this file? but I use chmod 755 command to give the full access of the user.
I will really appreciate your help, It waste lots of time on this strange behavior without any result :(

Opening a device file using erlang

Is there any way to open a terminal device file in erlang ?
I am on Solaris and I am trying the following::
Erlang (BEAM) emulator version 5.6 [source] [64-bit] [async-threads:0] [kernel-poll:false]
/xlcabpuser1/xlc/abp/arunmu/Dolphin/ebin
Eshell V5.6 (abort with ^G)
1> file:open("/dev/pts/2",[write]).
{error,eisdir}
2> file:open("/dev/null",[write]).
{ok,}
3>
As can be seen above the erlang file driver has no problem in opening a null fle but does not open a terminal device file!!
Unable to come to a conclusion as the file driver is able to open a null file.
Is there any other way to open terminal device files ?
Thanks
Update: I was able to work around the limitation described below using a port. For example, here is a sample program that prints "hello world" to /dev/stdout:
-module(test).
-export([main/1]).
main(X) ->
P = open_port({spawn, "/bin/cat >/dev/stdout"}, [out]),
P ! {self(), {command, "hello world"}}.
This is a bit inconvenient because a port doesn't act like a regular file, but at least it's one way to get the job done.
In efile_openfile() (in erts/emulator/drivers/unix/unix_efile.c) there is the following code:
if (stat(name, &statbuf) >= 0 && !ISREG(statbuf)) {
#if !defined(VXWORKS) && !defined(OSE)
/*
* For UNIX only, here is some ugly code to allow
* /dev/null to be opened as a file.
*
* Assumption: The i-node number for /dev/null cannot be zero.
*/
static ino_t dev_null_ino = 0;
if (dev_null_ino == 0) {
struct stat nullstatbuf;
if (stat("/dev/null", &nullstatbuf) >= 0) {
dev_null_ino = nullstatbuf.st_ino;
}
}
if (!(dev_null_ino && statbuf.st_ino == dev_null_ino)) {
#endif
errno = EISDIR;
return check_error(-1, errInfo);
#if !defined(VXWORKS) && !defined(OSE)
}
#endif
}
This code (confusingly) returns the EISDIR error if the file is not a regular file (which is the ISREG(statbuf) check), unless the file specifically is /dev/null. The file(3) documentation states:
eisdir :
The named file is not a regular file. It may be a directory, a
fifo, or a device.
so it's actually documented to do that. I'm not sure why that restriction exists, though—perhaps it's got something to do with performance because device drivers might block for more time than an ordinary file generally will.

Resources