How to add new file descriptors under /dev/fd? - linux

I am using a remote linux server via ssh, so I don't have the super user authority. However, the mounted file descriptors in /dev/fd is not enough:
user >ls /dev/fd/
0 1 2
or:
user >ls /proc/self/fd
0 1 2
And what I want to is add new file descriptors, so that I can redirect the output stream in this way:
user >./main.exe 1>1.txt 2>2.txt 3>3.txt ...
Since the file descriptor is not enough, I can't create a file descriptor such as /dev/fd/3, an error triggered:
IOError: [Errno 2] No such file or directory: '/dev/fd/3'

/dev/fd is not a real directory. You don't add files to it, it just shows which fds the process (ls in your case) has open.
To open new FDs from the shell, you can just run
./yourprogram 3>myfile
If the program writes to FD 3, the output will end up in myfile.
Here's an example:
$ cat foo.c
#include <unistd.h>
void main() {
write(3, "hello world\n", 12);
}
$ gcc foo.c -o foo
$ ./foo 3> myfile
$ cat myfile
hello world

Related

How do I specify a label/path with special characters as 'ñ' in /etc/fstab?

I know that if a path has spaces one can encode it with \040:
Example for path:
"//server/folder with spaces"
fstab entry:
//server/folder\040with\040spaces /mnt/share/folder_local cifs nofail,credentials=/root/.credfile 0 0
But how to fill fstab if you have a path with non-English chars as 'ñ'?
Path example:
"//server/folderWith-ñ-char"
I've tried:
fstab entry:
//server/folderWith-\F1-char /mnt/share/folder_local cifs nofail,credentials=/root/.credfile 0 0
based on:
https://www.degraeve.com/reference/urlencoding.php
but I get the error:
mount -a
mount error(2): No such file or directory
Just type //server/folderWith-ñ-char, ñ is not anyhow special.
In case of problems, you can use mnt_mangle from linux-util. Compile this short program:
$ printf "%s\n" '#include "libmount/libmount.h"' 'int main(int argc, char *argv[]) { puts(mnt_mangle(argv[1])); }' | gcc -xc - -lmount -o mnt_mangle
Then you can use:
$ ./mnt_mangle '//server/folderWith-ñ-char'
//server/folderWith-ñ-char

In linux, stdin, stdout, stderr means open file descriptor?

I have some question about stdio of linux system.
I know that fd 0, fd 1, fd 2 each refer stdin, stdout, stderr.
Does stdin, stdout, stderr means open file descriptor?
If i redirect it like 2>&1, what happen to relation between file descriptor and open file descriptor?
Here is a small C program fd.c that uses stdout and stderr to print a message to the console.
#include <stdio.h>
int main(int argc, char*argv[])
{
fprintf(stdout,"message for stdout\n");
/* fflush(stdout); */
fprintf(stderr,"message for stderr\n");
/* fflush(stderr); */
return 0;
}
To compile: gcc -o fd fd.c
Open a terminal and test run:
./fd
./fd > /tmp/output.log
./fd > /tmp/stdout.log 2>/tmp/stderr.log
./fd > /tmp/all.log 2>&1
Examine each output file. If they look out of sequence, uncomment the fflush statements.
Nothing happens to their relations, they remain as they are as before. The 0,1,2 descriptors are always open by default.

Bash file descriptors vs Linux file descriptors

I'm just trying to reconcile these two seemingly similar concepts.
In Bash, one is allowed to make arbitrary redirections, and importantly, using one's chosen file descriptor number. However in Linux, the value returned by an open call (AFAIK) cannot be chosen by the calling process.
Thus, are Bash fd numbers the same as the fd numbers returned by system calls? If not, what's the difference?
Here's a little experiment that might shed some light on what's going on when you open a file descriptor in bash with a number of your choosing:
> cat test.txt
foobar!
> cat test.sh
#!/bin/bash
exec 17<test.txt
read -u 17 line
echo "$line"
exec 17>&-
> strace ./test.sh
//// A bunch of stuff omitted so we can skip to the interesting part...
open("test.txt", O_RDONLY) = 3
fcntl(17, F_GETFD) = -1 EBADF (Bad file descriptor)
dup2(3, 17) = 17
close(3) = 0
fcntl(17, F_GETFD) = 0
ioctl(17, TCGETS, 0x7ffc56f093f0) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(17, 0, SEEK_CUR) = 0
read(17, "foobar!\n", 128) = 8
write(1, "foobar!\n", 8foobar!) = 8
fcntl(17, F_GETFD) = 0
fcntl(17, F_DUPFD, 10) = 10
fcntl(17, F_GETFD) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
close(17) = 0
The part that answers your question is where it calls open() on test.txt, which returns a value of 3. This is what you would most likely get in a C program if you did the same, because file descriptors 0, 1, and 2 (i.e., stdin, stdout, and stderr) are all you have open initially. The number 3 is just the next available file descriptor.
And we see that in the strace output of the bash script as well. What bash does differently is that it then calls fcntl(17, F_GETFD) to check if file descriptor 17 is already open (because it wants to use that fd for test.txt). Then, when fcntl returns EBADF indicating no such fd is open, bash knows it is free to use it. So then it calls dup2(3, 17) to make fd 17 a copy of fd 3. Finally, it calls close() on fd 3 to free it up again, leaving fd 17 (and only fd 17) as an open file descriptor for test.txt.
So the answer to your question is that bash file descriptors are not special creatures set apart from the "normal" file descriptors that everyone else uses. They are in fact just the same thing. You could easily use the same trick in your C program to open files with file descriptor numbers of your choosing.
Also, it's worth pointing out that bash doesn't really get to choose its own file descriptor when it calls open(). It has to make do with whatever open() returns, like everyone else. All that's really going on in your bash script is some smoke and mirrors (via dup2()) to make it seem as if you get to choose your own file descriptor.

What does shell do when we redirect using "<"?

Say I have a program called fstatcheck. It takes one argument from the command line and treat it as file descriptor. It checks the stat information of the file pointed by the file descriptor.
For example:
$./fstatcheck 1
l = 1
type: other, read: yes
Another example:
$./fstatcheck 3 < foobar.txt
l = 3
Fstat error: Bad file descriptor
Questions:
What is the shell doing in the second example?
I can guess that it takes 3 as a file descriptor and starts to analyze the stat, but descriptor 3 is not open. But how does shell treat the redirection?
I assume the shell performs the following algorithm:
if (fork() == 0) {
// What does the shell do here?
execve("fstatcheck", argv, envp);
}
Is there any way I can create a file descriptor 3 and let it connect to an open file table which points to foobar.txt file stat by just using the shell command (instead of using C code)?
Let's find out with strace:
$ strace sh -c 'cat < /dev/null'
[...]
open("/dev/null", O_RDONLY) = 3
fcntl(0, F_DUPFD, 10) = 10
close(0) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 0) = 0
close(3) = 0
[...]
execve("/bin/cat", ["cat"], [/* 28 vars */]) = 0
[...]
So in your code, the relevant parts would be:
if (fork() == 0) {
int fd = open(filename, O_RDONLY); // Open the file
close(0); // Close old stdin
dup2(fd, 0); // Copy fd as new stdin
close(fd); // Close the original fd
execve("fstatcheck", argv, envp); // Execute
}
As for opening another fd, absolutely:
myprogram 3< file
This will open file for reading on fd 3 for the program. < alone is a synonym for 0<.

How can I continue sending to stdin after input from bash process substitution finishes?

I'm using gdb.
I run a command like the below to set up the program by sending it input to stdin:
r < <(python -c "print '1\n2\n3'")
I want that command to allow me to start typing input after it finishes (so I can interact with the debugee normally) instead of stdin being closed.
This would work in bash but you can't pipe to the gdb r command this way:
cat <(python -c "print '1\n2\n3'") - | r
The below doesn't work, I assume it waits for EOF before it sends it to the program.
r < <(cat <(python -c "print '1\n2\n3'") -)
Is there a third option that will work?
This sounds like a job for expect.
Given
#include <stdio.h>
int main()
{
char *cp = NULL;
size_t n = 0;
while(getline(&cp, &n, stdin) >= 0) {
fprintf(stderr, "got: %s", cp);
}
return 0;
}
gcc -g -Wall t.c
And this expect script:
#!/usr/bin/expect
spawn gdb -q ./a.out
send run\n
send 1\n2\n3\n
interact
Here is the session:
$ ./t.exp
spawn gdb -q ./a.out
run
1
2
3
Reading symbols from ./a.out...done.
(gdb) run
Starting program: /tmp/a.out
got: 1
got: 2
got: 3
Now the script is waiting for my input. I provide some:
foo bar baz
got: foo bar baz
I can also interact with GDB:
^C
Program received signal SIGINT, Interrupt.
0x00007ffff7b006b0 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
81 ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) bt
#0 0x00007ffff7b006b0 in __read_nocancel () at ../sysdeps/unix/syscall-template.S:81
#1 0x00007ffff7a8f5a0 in _IO_new_file_underflow (fp=0x7ffff7dd4640 <_IO_2_1_stdin_>) at fileops.c:613
#2 0x00007ffff7a840d5 in _IO_getdelim (lineptr=0x7fffffffdda0, n=0x7fffffffdda8, delimiter=10, fp=0x7ffff7dd4640 <_IO_2_1_stdin_>) at iogetdelim.c:77
#3 0x000000000040064e in main () at t.c:9

Resources