stderr with libssh in pty mode - c

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.

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.

SSH communication using pipes and read() write()

I'm recently writing a piece of code to access an external server using SSH and then communicating with interactive shell-like application to which I'm directly connecting through it.
I'm using embedded Linux with only basic libraries available, with no possibility to use any additional software nor library. Also, I have to do it via C/C++ code inside the application. So I've decided to use pipes and read(), write() system calls, and I would rather stick to that.
I've written some code to better understand and test the concept. But it doesn't work as expected. I've used a snippet from here. It seems to work fine, but then the loop in main behaves not as expected
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
static bool
start_subprocess(char *const command[], int *pid, int *infd, int *outfd)
{
int p1[2], p2[2];
if (!pid || !infd || !outfd)
return false;
if (pipe(p1) == -1)
goto err_pipe1;
if (pipe(p2) == -1)
goto err_pipe2;
if ((*pid = fork()) == -1)
goto err_fork;
if (*pid) {
/* Parent process. */
*infd = p1[1];
*outfd = p2[0];
close(p1[0]);
close(p2[1]);
return true;
} else {
/* Child process. */
dup2(p1[0], STDIN_FILENO);
dup2(p2[1], STDOUT_FILENO);
close(p1[0]);
close(p1[1]);
close(p2[0]);
close(p2[1]);
execvp(*command, command);
/* Error occured. */
fprintf(stderr, "error running %s: %s", *command, strerror(errno));
abort();
}
err_fork:
close(p2[1]);
close(p2[0]);
err_pipe2:
close(p1[1]);
close(p1[0]);
err_pipe1:
return false;
}
int main() {
char *cmd[4];
cmd[0] = "/usr/bin/ssh";
cmd[1] = "-tt";
cmd[2] = "user#localhost";
cmd[3] = NULL;
char buf[65535];
char msg[65535];
int pid, infd, outfd;
start_subprocess(cmd, &pid, &infd, &outfd);
printf ("Started app %s as %d\n\n", *cmd, pid);
while(1) {
read(outfd, buf, 65535);
printf(">>> %s\n", buf);
printf("<<< ");
scanf("%s", msg);
if(strcmp(msg, "exit") == 0) break;
write(infd, msg, strlen(msg));
}
return 0;
}
I've experimeted with various SSH -t settings and it seems to somehow work with -tt option enabled (As I understand it forces pseudoterminal), without it I'm getting
Pseudo-terminal will not be allocated because stdin is not a terminal.
So I assume -tt is correct here. But the beheviour is strange. I wanted to connect through SSH, then issue ls command and see the output, which should be similar to normal SSH :
user#xubuntuLTS ~/dev/cpp/pipes$ ssh localhost
>>>> WELCOME TO SSH SERVER <<<<
Last login: Thu Jan 3 22:34:35 2019 from 127.0.0.1
user#xubuntuLTS ~$ ls
Desktop dev Documents Downloads Music Pictures Public Templates TEST_FILE Videos
user#xubuntuLTS ~$
But insted, my application works like this:
user#xubuntuLTS ~/dev/cpp/pipes$ ./a.out
Started app /usr/bin/ssh as 18393
>>>> WELCOME TO SSH SERVER <<<<
>>> Last login: Thu Jan 3 22:35:28 2019 from 127.0.0.1
<<< ls
>>> user#xubuntuLTS ~$
<<<
ls
>>> ls0;user#xubuntuLTS: ~user#xubuntuLTS ~$
<<< ls
>>> ls0;user#xubuntuLTS: ~user#xubuntuLTS ~$
Can you hint me what is wrong in my code? I want to read exactly the same output as I see during "normal" SSH session from terminal, possibly having "appending" output during each read() call, so I can easily perform some automated task with this type of interactive communication. Please note that using standard terminal here is just an example, in the real world solution I'm connecting to some kind of command line interface program directly by logging through SSH, without actual access to shell.
I'm pretty sure that there is something wrong with correct usage of write() and read() here, but I'm not an expert in that matter.

How to use the pulseaudio API as root?

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.

C: setup pseudoterminal and open with xterm

The following simplified piece of code is executed by a thread in the background. The thread runs until he is told to exit (by user input).
In the code below I have removed some error checking for better readability. Even with error checking the code works well and both the master and the slave are created and/or opened.
...
int master, slave;
char *slavename;
char *cc;
master = posix_openpt(O_RDWR);
grantpt(master);
unlockpt(master);
slavename = ptsname(master);
slave = open(slavename, O_RDWR);
printf("master: %d\n",master);
printf("slavename: %s\n",slavename);
On my machine the output is the following:
master: 3
slavename: /dev/pts/4
So I thought that opening an xterm with the command xterm -S4/3 (4 = pt-slave, 3 = pt-master) while my program is running should open a new xterm window for the created pseudoterminal. But xterm just starts running without giving an error or any further informations but does not open a window at all. Any suggestions on that?
EDIT:
Now with Wumpus Q. Wumbley's help xterm starts normally, but I can't redirect any output to it. I tried:
dup2(slave, 1);
dup2(slave, 2);
printf("Some test message\n");
and opening the slave with fopen and then using fprinf. Both didn't work.
The xterm process needs to get access to the file descriptor somehow. The intended usage of this feature is probably to launch xterm as a child process of the one that created the pty. There are other ways, though. You could use SCM_RIGHTS file descriptor passing (pretty complicated) or, if you have a Linux-style /proc filesystem try this:
xterm -S4/3 3<>/proc/$PID_OF_YOUR_OTHER_PROGRAM/fd/3
'
You've probably seen shell redirection operators before: < for stdin, > for stdout, 2> for stderr (file descriptor 2). Maybe you've also seen other file descriptors being opend for input or output with things like 3<inputfile 4>outputfile. Well the 3<> operator here is another one. It opens file descriptor 3 in read/write mode. And /proc/PID/fd/NUM is a convenient way to access files opened by another process.
I don't know about the rest of the question. I haven't tried to use this mode of xterm before.
OK, the trick with /proc was a bad idea. It's equivalent to a fresh open of /dev/ptmx, creating a new unrelated pty.
You're going to have to make the xterm a child of your pty-creating program.
Here's the test program I used to explore the feature. It's sloppy but it revealed some interesting things. One interesting thing is that xterm writes its window ID to the pty master after successful initialization. This is something you'll need to deal with. It appears as a line of input on the tty before the actual user input begins.
Another interesting thing is that xterm (the version in Debian at least) crashes if you use -S/dev/pts/2/3 in spite of that being specifically mentioned in the man page as an allowed format.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
int master;
char *slavename, window[64], buf[64];
FILE *slave;
master = posix_openpt(O_RDWR);
grantpt(master);
unlockpt(master);
slavename = ptsname(master);
printf("master: %d\n", master);
printf("slavename: %s\n", slavename);
snprintf(buf, sizeof buf, "-S%s/%d", strrchr(slavename,'/')+1, master);
if(!fork()) {
execlp("xterm", "xterm", buf, (char *)0);
_exit(1);
}
slave = fopen(slavename, "r+");
fgets(window, sizeof window, slave);
printf("window: %s\n", window);
fputs("say something: ", slave);
fgets(buf, sizeof buf, slave);
fprintf(slave, "you said %s\nexiting in 3 seconds...\n", buf);
sleep(3);
return 0;
}

GDB - Display output of target application in a separate window

I'm using GDB to debug some of my C applications. What I currently do is load the target application, set a breakpoint on line 30 and run it.
I would like to make GDB display the output of my own application in a new terminal window while I'm still able to control the breakpoint handling via the GDB terminal window, but I couldn't seem to find a proper switch. Is there any way making GDB display my program's output in its own window?
For people wondering how to use the GDB tty command here is a short description...
Open a new console window. We will redirect output from the program running under GDB here. This is our output window.
Run the tty command in the output window. This will show the name of the tty being used by the underlying console.
$ tty
/dev/pts/4
Open another console window and start GDB here. Let us call this the GDB window.
Now run the tty command in GDB using the tty file name obtained above and then start the debugging process.
(gdb) tty /dev/pts/4
(gdb) run
Now you should be able to see the program output separately in the output window.
Note: The GDB set new-console on command does not work on Linux! It is meant to be run on windows only. Use the tty method described above on Linux.
You can use set new-console on to accomplish this as shown here.
Another way to do this would be to start your target program with gdbserver (assuming it is available to you). Then you can connect GDB started in a separate window to gdbserver.
GNU gdbserver documentation
From window A:
gdbserver :12345 myprog [args...]
From window B:
gdb myprog
GNU gdb 6.6
...
(gdb) target remote localhost:12345
Remote debugging using localhost:12345
0x009867c0 in ?? ()
(gdb) b main
Breakpoint 1 at 0x804834a: file myprog.c, line 40.
(gdb) c
Continuing.
Breakpoint 1, main (argc=1, argv=0xffff8904) at myprog.c:40
40 int i = 1;
(gdb)
The best way I know is to redirect the output of the program to a file, and then tail -f that file in another terminal. Redirection is done with run > filename, as documented in the GDB documentation.
GDB's tty command does work, but it doesn't work well with interactive programs, like if you wanted to debug bash. Even for non-interactive programs, you get the following:
warning: GDB: Failed to set controlling terminal: Operation not permitted
I wrote a small program to fix both of these issues:
// Open a pty and let it idle, so that a process spawned in a different window
// can attach to it, start a new session, and set it as the controlling
// terminal. Useful for gdb debugging with gdb's `tty` command.
#include <inttypes.h>
typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64;
typedef int8_t i8; typedef int16_t i16; typedef int32_t i32; typedef int64_t i64;
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <pty.h>
#include <liburing.h>
#define BSIZE 4096
void raw_terminal(void)
{
if (!isatty(0))
return;
struct termios t;
tcgetattr(0, &t);
t.c_lflag &= ~(ISIG | ICANON | ECHO);
tcsetattr(0, TCSANOW, &t);
}
// Refers to the state of a Joint /while it's waiting in io_uring_enter/.
enum State {
READ,
WRITE
};
// Joins two fds together, like splice, but not a syscall and works on any two
// fds.
struct Joint {
u8 buf[BSIZE];
i32 ifd;
i32 ofd;
enum State state;
u32 nread;
};
void roll_joint(struct Joint *j, struct io_uring *ur, i32 ifd, i32 ofd)
{
j->ifd = ifd;
j->ofd = ofd;
j->state = READ;
struct io_uring_sqe *sqe = io_uring_get_sqe(ur);
io_uring_prep_read(sqe, j->ifd, j->buf, BSIZE, 0);
io_uring_sqe_set_data(sqe, j);
io_uring_submit(ur);
}
i32 main(i32 argc, char **argv)
{
raw_terminal();
struct io_uring ur;
assert(io_uring_queue_init(256, &ur, 0) == 0);
i32 ptm, pts;
assert(openpty(&ptm, &pts, NULL, NULL, NULL) == 0);
dprintf(2, "pid = %u tty = %s\n", getpid(), ttyname(pts));
struct Joint jkbd;
roll_joint(&jkbd, &ur, 0, ptm);
struct Joint jscreen;
roll_joint(&jscreen, &ur, ptm, 1);
for (;;) {
struct io_uring_cqe *cqe;
for (;;) {
// Actions like suspend to RAM can interrupt the io_uring_enter
// syscall. If we get interrupted, try again. For all other errors,
// bail. Also, wait_cqe negates the error for no reason. It never
// returns positive numbers. Very silly.
u32 res = -io_uring_wait_cqe(&ur, &cqe);
if (res == 0)
break;
else if (res != EINTR) {
dprintf(2, "io_uring_enter returns errno %d\n", res);
exit(res);
}
}
struct Joint *j = io_uring_cqe_get_data(cqe);
if (j->state == READ) {
// Exiting READ state. Finish with the read...
j->nread = cqe->res;
assert(j->nread > 0);
// Now, start the write.
j->state = WRITE;
struct io_uring_sqe *sqe = io_uring_get_sqe(&ur);
io_uring_prep_write(sqe, j->ofd, j->buf, j->nread, 0);
io_uring_sqe_set_data(sqe, j);
io_uring_submit(&ur);
}
else if (j->state == WRITE) {
// Exiting WRITE state. Finish with the write...
i64 nwritten = cqe->res;
assert(nwritten == j->nread);
// Now, start the read.
j->state = READ;
struct io_uring_sqe *sqe = io_uring_get_sqe(&ur);
io_uring_prep_read(sqe, j->ifd, j->buf, BSIZE, 0);
io_uring_sqe_set_data(sqe, j);
io_uring_submit(&ur);
}
io_uring_cqe_seen(&ur, cqe);
}
io_uring_queue_exit(&ur);
return 0;
}
Suppose you save the program to idleterm.c. To compile it:
> gcc -o idleterm idleterm.c -luring
To use it, start one terminal window, and on that window, run idleterm. It will print the name of the tty to attach to:
> ./idleterm
pid = 3405922 tty = /dev/pts/0
█
Copy that tty path and paste it into a gdb session in a second window:
> gdb bash
Reading symbols from bash...
(No debugging symbols found in bash)
(gdb) tty /dev/pts/0
(gdb) r
Starting program: /usr/bin/bash
…
A bash prompt will appear in the first window. All of the interactions with bash requiring special TTY behaviour will work normally, including ^C, ^Z, etc.
idleterm passes all keyboard input through to the child process being debugged, including ^C and ^Z. So in order to stop idleterm, you'll have to kill it from a separate window. This is why idleterm prints its pid. Copy the pid, then paste it into the kill command:
> kill 3405922
If there's only one instance of idleterm running on the system, you can of course just use killall.
Just use tty command. If you want output of your program redirected to /dev/pts/5 type:
tty /dev/pts/5
With lldb on Mac the following runs the program in w new terminal window while the debugger controls from the original window:
$ lldb <prog>
(lldb) b main # if you want to set a breakpoint
(lldb) process launch --tty -- <args>
This runs the program as a process in specified tty (terminal window):
$ tty # (in other window, get tty name)
/dev/ttys002
$ lldb
(lldb) b main # if you want to set a breakpoint
(lldb) process launch --tty=/dev/ttys002 -- <args>

Resources