I am using Ubuntu with VirtualBox.
I am defining a new command to my shell to output some characteristics(like sibling tree etc.) of child processes. In order to output these characteristics, I created a kernel module and used task_struct. I also tested my kernel module outside of my shell and it works.
Now my problem is how to trigger this kernel module inside my shell(in C code) so that my kernel module will be loaded?
I searched and find that I need to use system calls like modprobe or insmod but did not understand how to use them. I tried the code below, but it did not work:
setuid(0);
system("/sbin/insmod /.../mymodule.ko");
Thank you for your help.
Loading module using system()
You are trying to become root in your application (by executing setuid(0)), but you don't have permissions to do that (if you run your program as regular user). Instead, you should check if your program was run from root (using getuid()). Also, it's good idea to test if your module file exists at all. Here is an example of such code (it's tested and does all checking needed):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#define ROOT_UID 0
#define INSMOD_PATH "/sbin/insmod"
#define MOD_PATH "/.../mymodule.ko"
int main(void)
{
uid_t uid;
int res;
/* Check if program being run by root */
uid = getuid();
if (uid != ROOT_UID) {
fprintf(stderr, "Error: Please run this program as root\n");
return EXIT_FAILURE;
}
/* Check if module file exists */
if (access(MOD_PATH, F_OK) == -1) {
fprintf(stderr, "Error: File \"%s\" doesn't exist\n", MOD_PATH);
return EXIT_FAILURE;
}
/* Load module */
res = system(INSMOD_PATH " " MOD_PATH);
if (res != 0) {
fprintf(stderr, "Error loading module: %d\n", res);
return EXIT_FAILURE;
}
printf("Module \"%s\" was successfully loaded\n", MOD_PATH);
return EXIT_SUCCESS;
}
Save this code as main.c file. Be sure to replace MOD_PATH definition with actual path of your module file.
Compile it using next command:
$ gcc -Wall -O2 main.c -o load_module
Now do the next:
$ su
# ./load_module
First command switches your user to root (you will be asked to enter root password). If you don't know root password, try using sudo -s command instead of su.
Second command runs your program.
Pay your attention to the last character at the command prompt:
# means you have root permissions at this point
$ means you only have regular user permissions.
Loading module using finit_module()
Using system() function in C is usually considered a bad practice (because it takes a lot of time for execution and basically just trying to replace a much more simple Bash script).
If you want to load kernel module in C without using system(), you can look into source code of insmod tool. See libkmod/libkmod-module.c file, kmod_module_insert_module() function. You can see those sources here.
Pay attention to finit_module() function call. A good explanation about this system call can be found at manual pages:
$ man finit_module
Here is an example how you can use finit_module() system call:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <fcntl.h>
#define ROOT_UID 0
#define MOD_PATH "/.../mymodule.ko"
static inline int finit_module(int fd, const char *uargs, int flags)
{
return syscall(__NR_finit_module, fd, uargs, flags);
}
int main(void)
{
uid_t uid;
long res;
int fd;
/* Check if program being run by root */
uid = getuid();
if (uid != ROOT_UID) {
fprintf(stderr, "Error: Please run this program as root\n");
return EXIT_FAILURE;
}
/* Check if module file exists */
if (access(MOD_PATH, F_OK) == -1) {
fprintf(stderr, "Error: File \"%s\" doesn't exist\n", MOD_PATH);
return EXIT_FAILURE;
}
/* Load module */
fd = open(MOD_PATH, O_RDONLY | O_CLOEXEC);
if (fd < 0) {
perror("Unable to open module file");
return EXIT_FAILURE;
}
res = finit_module(fd, "", 0);
if (res != 0) {
perror("Error when loading module");
close(fd);
return EXIT_FAILURE;
}
close(fd);
printf("Module \"%s\" was successfully loaded\n", MOD_PATH);
return EXIT_SUCCESS;
}
Related
I have two files in the same directory.
directory/
| a.c
| b.c
a.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
pid_t pid;
int status;
int wret;
if ((pid = fork()) < 0)
printf("error");
else if(pid == 0)
{
printf("%s", argv[1]);
execv(argv[1], &argv[1]);
}
else
{
/* respawn */
if ((wret = wait(&status)) != -1)
execv(argv[1], &argv[1]);
}
return 0;
}
b.c is just a simple program that print "hello".
I want to run ./a b from the command line to make the a program call exexXX to execute the b program.
I don't understand why if I use execv I can write just ./a b in the command line, instead if I use execvp I have to write ./a ./b.
The man exec page is not clear because it reports
"The initial argument for these functions is the name of a file that
is to be executed."
Thanks
If the program name argument contains no slashes, the execvp() function looks for the program to execute in the directories listed on your PATH environment variable. If you don't have . (the current directory) on your PATH and you aren't in one of the directories listed on your path, a plain name like b will not be executed, even if b is in the current directory. If the name contains a slash, it can be relative (./b) or absolute (/home/someone/src/programs/b) and it will be interpreted as a file name to be executed without consulting the PATH environment variable.
By contrast, execv() treats a plain b in the program name argument as ./b — the name of the file in the current directory and executes it if it is present, and fails if it is located somewhere else.
At one time, there was a comment that asked:
Are you saying if you have an executable b in . and you do execv("b", b_args), it will get executed?
On a normal Unix box, yes.
Code b.c:
#include <stdio.h>
int main(void)
{
puts("Hello");
return 0;
}
Code a.c:
#include <stdio.h>
#include <unistd.h>
int main(void)
{
char *argv[] = { "b", 0 };
execv(argv[0], argv);
fprintf(stderr, "failed to execute '%s'\n", argv[0]);
return 1;
}
Running these:
$ (PATH=$(clnpath "$PATH" ".:$PWD"); echopath PATH; ./a)
/Users/jleffler/bin
/opt/informix/12.10.FC6/bin
/Users/jleffler/oss/bin
/Users/jleffler/oss/rcs/bin
/usr/local/mysql/bin
/opt/gcc/v7.3.0/bin
/Users/jleffler/perl/v5.24.0/bin
/usr/local/bin
/usr/bin
/bin
/opt/gnu/bin
/usr/sbin
/sbin
Hello
$
The clnpath script modifies the string provided as its first argument ("$PATH") by removing any occurrences of any of the directory names listed in its second path-like argument (".:$PWD") — it's how I edit my PATH on the fly when I need to. The echopath script echoes the directories on PATH (or any other path-like variable, or it will process the result of expanding a pathlike variable, such as "$PATH"), one per line — the output shows that neither . nor /Users/jleffler/soq (which is where I run the program) is on $PATH in the sub-shell. The ./a runs the code from a.c (it would not be executed without that ./ in front), which in turn runs the code from b.c, which produces the Hello. (If there is some system where this does not work, please identify it.)
I could also arrange for b.c to be:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
puts("Hello");
const char *env = "PATH";
char *val = getenv(env);
if (val == 0)
val = "<nothing>";
printf("%s=%s\n", env, val);
return 0;
}
which would print the value of $PATH directly from the executable (to verify that neither . nor the value of the current working directory is listed).
I am writing a little test program for the open() function in C # open SuSE leap 42.2 x64.
Unfortunately the file being created gets -rwxrwxrwx permissions, although I hand over 0644 to the open() function after executing umask(0);
Could anyone please tell me, what I am doing wrong?
(I got the example code from an open book (link).
Here's my code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv) {
/* Zugriffsrechte 644 */
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH;
const char *new_file;
int file_descriptor;
/* Alle Zugriffsrechte der Einschraenkungsmaske erlauben */
umask(0);
/* Argument 2 der cmd line auswerten */
if (argv[1] == NULL) {
fprintf(stderr, "usage: %s datei_zum_oeffnen\n", *argv);
return EXIT_FAILURE;
}
new_file = argv[1];
file_descriptor = open(new_file, O_WRONLY|O_EXCL|O_CREAT, 0644);
/* or var mode instead of (0644) */
if (file_descriptor == -1) {
perror("Fehler bei open ");
return EXIT_FAILURE;
}
return (EXIT_SUCCESS);
}
Either way - handing over 0644 or the variable "mode" as open()'s third argument do not work as expected and the result when executing the program (as normal user) and handing in a filename is: -rwxrwxrwx and moreover: the file belongs to root:root instead of the executing user?!
What will I have to change?
Solved.
The failure was compiling it using NetBeans 8.2.
The folders and files created by NetBeans belong to root - creating the same .c-file in my home dir and compiling it there worked perfectly.
Added:
NetBeans is not running as root.
The folder which my workspace was created in is a NTFS drive, mounted during boot with user-access - but the whole drive belongs to root in the first place. I am 99% convinced that this is what led me to wrong assumptions about NetBeans creating folders and files with wrong permissions.
I was working on my project when I needed to use "curl" to obtain some data from www. Now firstly I tried direct system() function but it didn't worked, strangely everytime it corrupted the whole source code file while compiling with gcc. Luckily I was testing it separately.
Then I tested execl() function, this code compiles OK and gcc gives me a .exe file to run, but nothing happens when I run it,blank windows appears. CODE:
int main(){
execl("curl","curl","http://livechat.rediff.com/sports/score/score.txt",">blahblah.txt",NULL);
getch();
return 0;
}
Includes are not shown properly but I have included stdio,conio,stdlib and unistd.h.
How can I get output of program to store in text file? Also running the above command creates and stores text file in My Documents, I want it to be in local directory from where I run the program. How can I do that?
You need to provide the path of curl, and you cannot use redirection because the application will not be executed through bash. Instead use the -o flag and specify the filename. Also, execl does not return when successful:
#include <unistd.h>
#include <stdio.h>
int main(){
execl("/usr/bin/curl",
"curl","http://livechat.rediff.com/sports/score/score.txt",
"-oblahblah.txt",NULL
);
printf("error\n");
return 0;
}
If you want your code to return, you should fork a child process to run the command. This way you can check the return code.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define CURL "/usr/bin/curl"
int main()
{
pid_t pid;
int status;
pid = fork();
if (pid == 0)
{
execl(CURL, CURL, arg1, NULL);
}
else if (pid < 0)
{
printf("Fork failed\n");
exit (1);
}
else
{
if (waitpid(pid, &status, 0) != pid)
status = -1;
}
return status;
}
arg1 is whatever argument you want to use with curl or if you aren't using any than you obviously can omit it.
I can compile this program which was provided to me, but that I must further develop. I have some questions about it:
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#define TIMEOUT (20)
int main(int argc, char *argv[])
{
pid_t pid;
if(argc > 1 && strncmp(argv[1], "-help", strlen(argv[1])) == 0)
{
fprintf(stderr, "Usage: RunSafe Prog [CommandLineArgs]\n\nRunSafe takes as arguments:\nthe program to be run (Prog) and its command line arguments (CommandLineArgs) (if any)\n\nRunSafe will execute Prog with its command line arguments and\nterminate it and any remaining childprocesses after %d seconds\n", TIMEOUT);
exit(0);
}
if((pid = fork()) == 0) /* Fork off child */
{
execvp(argv[1], argv+1);
fprintf(stderr,"RunSafe failed to execute: %s\n",argv[1]);
perror("Reason");
kill(getppid(),SIGKILL); /* kill waiting parent */
exit(errno); /* execvp failed, no child - exit immediately */
}
else if(pid != -1)
{
sleep(TIMEOUT);
if(kill(0,0) == 0) /* are there processes left? */
{
fprintf(stderr,"\nRunSafe: Attempting to kill remaining (child) processes\n");
kill(0, SIGKILL); /* send SIGKILL to all child processes */
}
}
else
{
fprintf(stderr,"RunSafe failed to fork off child process\n");
perror("Reason");
}
}
What does my warning mean when I compile it?
$ gcc -o RunSafe RunSafe.c -lm
RunSafe.c: In function ‘main’:
RunSafe.c:30:44: warning: incompatible implicit declaration of built-in function ‘strlen’ [enabled by default]
Why can't I execute the file?
$ file RunSafe
RunSafe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x0a128c8d71e16bfde4dbc316bdc329e4860a195f, not stripped
ubuntu#ubuntu:/media/Lexar$ sudo chmod 777 RunSafe
ubuntu#ubuntu:/media/Lexar$ ./RunSafe
bash: ./RunSafe: Permission denied
ubuntu#ubuntu:/media/Lexar$ sudo ./RunSafe
sudo: ./RunSafe: command not found
First, you need to #include <string.h> to get rid of that warning.
Second, the OS is probably preventing you from executing programs on the /media/Lexar filesystem, no matter what their permission bits are. If you type mount you'll probably see the noexec option for /media/Lexar.
warning: incompatible implicit declaration of built-in function ‘strlen’ [enabled by default]
You need to include #include<string.h> because strlen() is declared in it.
Try running the exe on some other location in your filesystem and not the mounted partition as the error indicates for some reason you don't have permissions on that mounted partition.
I've been using a combination of fork() and exec() to execute some external command on linux, however, the code seems to fail whenever I try to execute /usr/bin/firefox which is a symbolic link to a real binary.
Does anyone know how to solve this problem? I've tested with other programs (which really are executable binaries and not symlinks to them) and it works.
Here's the code from the program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>
int main(int argc, char **argv) {
pid_t pid;
// this was the old line:
// char *parmList[] = {"", "index.html", NULL};
// and this is the one that solves the problem:
char *parmList[] = {"firefox", "index.html", NULL};
int a;
if ((pid = fork()) == -1)
perror("fork failed");
if (pid == 0) {
a = execvp("/usr/bin/firefox", parmList);
fprintf(stdout, "execvp() returned %d\n", a);
fprintf(stdout, "errno: %s (%d).\n", strerror(errno), errno);
}
else {
waitpid(pid, 0, 0);
}
return 0;
}
Edit: I updated the code to include the answer and changed the topic's title because the problem really didn't seem to be due to symbolic links at all. Thanks everyone.
You might want to add some code right after the execvp to output some diagnostic (i.e. check errno, print something meaningful ;)).
You could also try to analyze it w/o source modification using strace or gdb for that matter.
See also: execve.
Update as follow-up from the comments
Firefox is not happy with argv[0] being empty, which is what argList looked like, unfortunately.
Lessons learned: Be thoroughly aware of what you pass as argv to the program you execute. :)
Does Firefox insist on having a non-empty argv[0]? You should normally pass the name of the command (either just "firefox" or "/usr/bin/firefox") to the command, but you are not doing so.
[...going to check the deeper comments above - and it seems this is the correct diagnosis, but 21 minutes or so late...]