How to use a created Linux Kernel character driver from the commandline - c

I've been following the tutorial on Linux Kernel programming over here: http://www.tldp.org/LDP/lkmpg/2.6/html/index.html
I've gotten to the section that is dedicated to "character device drivers" and while I've gotten it to compile, it will not function on the described case:
"Called when a process writes to dev file: echo "hi" > /dev/chardev"
I've tried several Linux console commands such as:
echo "hi" > sudo /dev/chardev/
and
sudo sh -c 'printf "hi" > sudo /dev/chardev/'
I'm running my code on a Raspberry Pi 3 B+
When I run the first command I will get nothing in return, and nothing is added to /var/logs/messages
When I run the second command I get:
sh:printf: I/O error
Full code over at: http://www.tldp.org/LDP/lkmpg/2.6/html/x569.html
I've modified the code with my snippet below.
/*
* Called when a process writes to dev file: echo "hi" > /dev/chardev
*/
static ssize_t
device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
printk(KERN_INFO "%s\n", buff);
return -EINVAL;
}
What I'm expecting to happen is when I use echo "hi" > sudo /dev/chardev that in my /var/logs/messages a line will appear that simply says "hi".

echo "hi" > /dev/chardev
This is ok.
echo "hi" > sudo /dev/chardev/
This is invalid. This will echo hi /dev/chardev/ and write that to file named sudo. And don't /dev/chardev/, it't not a directory, it's a file, it's /dev/chardev (without the / on the end).
sudo sh -c 'printf "hi" > sudo /dev/chardev/'
Same error as above.
If you want to append to a file using sudo, use tee, as in echo hi | sudo tee /dev/chardev. Or if you have to sudo sh -c 'echo "hi" > /dev/chardev'.

Related

bin/sh error and mekefile error 127 but i write bin/bash in my script?

I have this bash script which works (it print the output), but when i run the program, at the end there is the error /bin/sh not found, makefile error 127. I even didn't have bin/sh, i wrote bin/bash. analisi.sh is the script, this is the part of the makefile, supermercato.PID is where the PID of the thread supermercato is saved.
./run:
cd src; \
(./supermercato & echo $$! > supermercato.PID) & \
cd ..; \
sleep 25s; \
kill -1 $$(cat src/supermercato.PID); \
chmod +x ./analisi.sh
./analisi.sh $$(cat src/supermercato.PID); \
this is the script analisi.sh, test.log is a file where the output is written
#!/bin/bash
if [ -f "test.log" ]; then
while read line; do echo $line; done < test.log
else
echo "$0:Errore" 1>&2
fi
Here is an example of the error (SIGHUP is part of the project). TEST is written on the log file named test.log which i read from the script.
Received SIGHUP
./analisi.sh $(cat src/supermercato.PID); \
******TEST******
/bin/sh: 1: : not found
make: *** [Makefile:15: run] Error 127
This line:
./analisi.sh $$(cat src/supermercato.PID); \
has a trailing space (!) and the previous command ended in a semicolon, so BASH thinks you're trying to execute two commands: ./analisi.sh followed by a command whose name consists of a single literal space character.
You can reproduce it in the terminal. Put a space after the backslash and hit Enter:
$ echo hey; \
hey
-bash: : command not found
$
Also check the format of the error message:
$ wow
-bash: wow: command not found
So the format is:
<BASH>: <line number>: <command_name>: not found
In your case, it's
/bin/sh: 1: : not found
Note how there are two spaces here: : :.

Handling error cases with scp and sshpass

I am trying to implement command line interface command for file transfer and that will call internally
sshpass -p "password" scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -r user#remote-machine:/home/QA.txt /home/faadmin/
Here error handling is not happening properly , when I am running this command using system().if in case route not there for file transfer ,or file not found errors some time they blocking the execution of CLI command.so I have check return values after executing the above Linux command not showing other than 0 and 1.how can I get other return values?
You should use popen() because system() return value tell you if the command has been executed or not. E.G.:
#include <stdio.h>
void main(void)
{
FILE *output = NULL;
char text[2048];
char cmd[256];
sprintf(cmd, "%s", "sshpass -p \"password\" scp -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -r user#remote-machine:/home/QA.txt /home/faadmin/");
output = popen(cmd, "r");
while(fgets(text, 1024, output) != NULL)
printf("%s", text);
pclose(output);
}

Get the return code of a C program in my shell program

Suppose I have a C program named Foo.c which is printing few things and returning a value named rc which I am executing in my shell program as follows :
foobar=$(Foo | tail -1)
Now, the variable foobar has the last printed value of the program Foo. But without disturbing this, I want to get the return code rc of the program in my shell program.
You can use "set -o pipefail" option.
[root#myserver Test]# set -o pipefail
[root#myserver Test]# ./a.out | tail -l
[root#myserver Test]# echo $?
100
Here my program a.out returns 100.
Or another options is to use pipestatus environment variable. You can read about it here.
http://www.linuxnix.com/2011/03/pipestatus-internal-variable.html
If you are using bash shell, you can use PIPESTATUS array variable to get the status of the pipe process.
$ tail sat | wc -l
tail: cannot open ‘sat’ for reading: No such file or directory
0
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
1 0
$
From man bash:
PIPESTATUS
An array variable containing a list of exit status values from the processes in the most-recently-executed foreground pipeline (which may contain only a single command).
This assigns the last line of the output of Foo to foobar and Foo's exit code is assigned to code:
{ read -r foobar; read code; } < <( (Foo; echo $? ) | tail -2)
The <(...) construct is called process substitution. In the code above, the read commands receive their stdin from the process substitution. Because of the tail -2, the process substitution produces a total of two lines. The first line is the last line produced by Foo and it is assigned to foobar. The second is assigned to code.
The space between the first and second < is essential.
Example
After creating a function Foo, the above can be tested:
$ Foo() { echo "Testing"; false; }
$ { read -r foobar; read code; } < <( (echo "Testing"; false; echo $? ) | tail -2)
$ echo "foobar=$foobar code=$code"
foobar=Testing code=1
And:
$ Foo() { echo "2nd Test"; true; }
$ { read -r foobar; read code; } < <( (Foo; echo $? ) | tail -2)
$ echo "foobar=$foobar code=$code"
foobar=2nd Test code=0
I'm afraid that you have to use a temporal file to store the output of the Foo program, get the return code and then perform the tail -1. Just like the following:
Foo > /tmp/temp_file
ret=$?
foobar=$(tail -1 /tmp/temp_file)
$? gives the return value of the last executed command.

C: stdout hangs linux pipes

I've written a simple I/O echoing program in C to test a problem with a bigger real program. Here, linux FD redirection doesn't work.
The echoing program (aka a.out) is:
#include <stdio.h>
int main(int argc, char **argv) {
char buff[10];
while (1) {
if (fgets(buff, 10, stdin) == NULL) break;
printf("PRINT: %s \n", buff);
}
}
From Bash, I run it as:
$ mkfifo IN OUT
$ # this is a method to keep the pipes IN and OUT opened over time
$ while :; do read; echo Read: $REPLY >&2; sleep 1; done <OUT >IN &
$ a.out >OUT <IN &
$ echo xyz >IN
and there is no output produced: the Bash while loop isn't able to read from OUT.
Let's compare this a.out with cat, which instead works as expected:
$ mkfifo IN OUT
$ while :; do read; echo Read: $REPLY >&2; sleep 1; done <OUT >IN &
$ cat >OUT <IN &
$ echo xyz >IN
Read: xyz
This last line is printed on console to stderr.
cat's output, differently from a.out's, is able to travel across OUT and reach the Bash while loop, which then prints it on console.
What's wrong with a.out?
try to add fflush(stdout) after printf(...).

execve("/bin/sh", 0, 0); in a pipe

I have the following example program:
#include <stdio.h>
int
main(int argc, char ** argv){
char buf[100];
printf("Please enter your name: ");
fflush(stdout);
gets(buf);
printf("Hello \"%s\"\n", buf);
execve("/bin/sh", 0, 0);
}
I and when I run without any pipe it works as it should and returns a sh promt:
bash$ ./a.out
Please enter your name: warning: this program uses gets() which is unsafe.
testName
Hello "testName"
$ exit
bash$
But this does not work in a pipe, i think I know why that is, but I cannot figure out a solution. Example run bellow.
bash$ echo -e "testName\npwd" | ./a.out
Please enter your name: warning: this program uses gets() which is unsafe.
Hello "testName"
bash$
I figure this has something to do with the fact that gets empties stdin in such a way that /bin/sh receives a EOF and promtly quits without an error message.
But how do I get around this (without modifying the program, if possible, and not removing gets, if not) so that I get a promt even though I supply input through a pipe?
P.S. I am running this on a FreeBSD (4.8) machine D.S.
You can run your program without any modifications like this:
(echo -e 'testName\n'; cat ) | ./a.out
This way you ensure that your program's standard input doesn't end after what echo outputs. Instead, cat continues to supply input to your program. The source of that subsequent input is your terminal since this is where cat reads from.
Here's an example session:
bash-3.2$ cc stdin_shell.c
bash-3.2$ (echo -e 'testName\n'; cat ) | ./a.out
Please enter your name: warning: this program uses gets(), which is unsafe.
Hello "testName"
pwd
/home/user/stackoverflow/stdin_shell_question
ls -l
total 32
-rwxr-xr-x 1 user group 9024 Dec 14 18:53 a.out
-rw-r--r-- 1 user group 216 Dec 14 18:52 stdin_shell.c
ps -p $$
PID TTY TIME CMD
93759 ttys000 0:00.01 (sh)
exit
bash-3.2$
Note that because shell's standard input is not connected to a terminal, sh thinks it is not executed interactively and hence does not display the prompt. You can type your commands normally, though.
Using execve("/bin/sh", 0, 0); is cruel and unusual punishment for the shell. It gives it no arguments or environment at all - not even its own program name, nor even such mandatory environment variables as PATH or HOME.
Not 100% sure of this (the precise shell being used and the OS might throw these answers a bit; I believe that FreeBSD uses GNU bash by default as /bin/sh?), but
sh may be detecting that its input is not a tty.
or
Your version of sh might go into non-interactive mode like that also if called as sh, expecting login will prepend a - onto argv[0] for it. Setting up execve ("/bin/sh", { "-sh", NULL}, NULL) might convince it that it's being run as a login shell.

Resources