Fork and execlp not executing my program? - c

So I'm trying to create a child using fork and have that child run a program using execlp, only problem is that it isnt running... kind of. Heres my simple program
else if (pid == 0)
{
fprintf(stderr,"Child process is accessing memory %d.\n", rand_num);
execlp("helloworld", "helloworld", (char*)0);
printf("hi\n");
exit(0);
}
I've done some research and read that if the execution is successful, the printf("hi\n) wont run, and thats what happened, it wont print out hi so that means it is accessing the program right? But my program test is a simple Hello World output
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
and it isn't running, any ideas why?

I think that your program is actually working perfectly. And also, "test" is a terrible name for a command.
When you are calling execlp("test",...), the kernel looks for a program named test along your PATH environment variable (that's the the p means in execlp). It will find one in /bin:
$ ls -l /bin/test
-rwxr-xr-x. 1 root root 37368 Oct 15 04:31 /bin/test
It is highly likely that /bin is in your $PATH before your current directory. In fact, it's likely that your current directory isn't even in your $PATH.
The /bin/test program is (well, mostly was) used to implement conditional statements in the shell. E.g., when you right:
if [ -f /my/file ]; then ...
The [ is actually just another name for /bin/test.

execlp("test", "test", (char*)0);
Is this on a unix or linux system? execlp() will search the command path (your PATH environment variable) for the program, if the program specified as the first argument doesn't contain any "/" characters. That's the case here, so it's going to search the command path for a program named "test".
It happens that unix systems normally have a program in /bin or /usr/bin named "test", and if you run that program without any arguments, it doesn't print anything. You may be executing that program instead of what you intended to execute.
If this "test" program that you want to execute is in your current directory, try calling execl() instead of execlp(). execl() doesn't search your PATH, so trying to invoke "test" would only look for that program in your current directory.

Related

What does "/bin/sh" stand for in execlp command?

I want to execute a shell command with execlp, I tried with the following instruction :
execlp("sh", "sh", "-c", p_command, (char*)NULL);
p_command is a pointer to a const char representing a shell command line.
My minimal test tells me the program succeded as expected. I first choose to use "/bin/sh" instead of "sh" but I've learned that p(ath) in execlp allows us to avoid writing the full path, as if exec will complete the path for us ; so I removed "/bin/".
My concern is that I never saw a code using execlp with only "sh", as it effectively does for exemple for ls we can directly use "ls" instead of "/bin/ls".
As a beginner I am wondering what "/bin/sh" stands for, what is the difference between "sh" and "/bin/sh" in this situation and why we have to write the full path for execlp to execute a shell ?
When the path passed to execlp is sh, execlp searches for it in the directories listed in the PATH environment variable. If an attacker is able to modify the PATH variable in the environment that runs your program, they can set it to list a directory of their choosing, and they can place their own program named sh in that directory. Then your program will execute their program instead of executing the system sh program. In some cases (depending on a bit in the file’s mode bits), programs are executed with the permissions of their owners rather than the permissions of the user executing the program. Such programs must be written carefully to avoid situations like this, where an attacker would be able to exploit the program.
When the path passed to execlp is /bin/sh, execlp looks for it in the path that is /bin/sh starting from the root of the file system, called /. This will always use the sh program that the system administrator has put in the /bin directory (usually done as part of system installation).

No such file or directory: Running Ruby as an interpreter script for execv()

I'm writing a C program that passes data to a Ruby script. The snippet of code in main.c is below. Whenever I run, I receive an error from execv() that there is no such file or directory. I guessed that I didn't add my file to the PATH but I did and I still ended up with the same outcome.
int temp = execv("#!/usr/bin/ruby xmlReader.rb", list);
if (spec == -1)
printf("%s\n", strerror(errno));
The exec*() family of system calls are very low level, they replace the executing program with the program mentioned as the first argument, so there's no return value unless there's an error. Upon success, your program is entirely gone, overlaid with the new program. It's a one-way program-execution chain.
It doesn't know how to run command lines, and exec() is usually usually is done after a fork() operation. You almost certainly don't want to do this.
What you probably want is int temp = system("ruby xmlreader.rb"); which will run the command line given in a subprocess (ultimately using fork and exec, though this aspect is invisible to you), wait for it to complete, then return the exit value to you.

exec function only running some commands, won't run echo

I'm trying to run command line arguments (specifically echo) through the exec family of functions. I can get the execv function to run if I write my own executable and run it, but if I try to run touch or echo it returns -1
#include <stdio.h>
#include <unistd.h> // exec functions
#include <sys/types.h> // pid_t
#include <sys/wait.h>
#define HIGH 1
#define LOW 0
int digitalWrite(int pin, short type) {
pid_t pid = fork();
if (pid == 0) {
printf("pid == %i\n", pid);
if (type == HIGH) {
char* args[] = {"echo", "1", ">", "/sys/class/gpio/gpio67/value", NULL};
int val = execv(args[0], args);
printf("ran function execl, %i\n", val);
} else {
printf("Unable to do anything but set pin to HIGH\n");
}
} else if (pid < 0) { // pid < 0
printf("fork failed\n");
}
wait(NULL);
}
int main() {
printf("Starting digitalWrite\n");
digitalWrite(0, HIGH);
printf("Completed digitalWrite()\n");
return 0;
}
Just for context here's my build:
$ gcc wiringbeagle.c
$ ./a.out
Starting digitalWrite
pid == 0
ran function execl, -1
Completed digitalWrite()
Completed digitalWrite()
$ ls
a.out wiringbeagle.c
The command echo 1 > /sys/class/gpio/gpio67/value runs fine in the terminal on it's own, and if I create a local file (i.e. touch tmpfile.txt) and try to run echo hi > tmpfile.txt it runs as expected in my command line but doesn't run in the program.
I must be not understanding something with execv, and any assistance would be greatly appreciated!
The first argument to execv is the file to be executed. Unlike your shell, execv does not search through the directories indicated by the PATH environment variable, so you need to give it the complete path to the executable. Unless there is an executable file called echo in your current working directory, execv("echo",...) will fail with a "file not found" error. (Use perror to get better error messages).
If you want to search for the executable as the shell does, use execvp. But note that your shell probably executes echo as a built-in command, so it won't be the same echo as your shell uses. In this case, that's fine.
Once you fix that, you will encounter a different problem. Since you are just invoking a command-line utility with arguments, rather than using a shell, the argument ">" is just an argument. It is the shell which handles redirections (as well as pipes, quoting, and a bunch of other useful stuff). So all you will accomplish is to send the three arguments to stdout.
You could use the system function to execute a command using the shell, or you could set up the redirection yourself by freopening stdout in your child before doing the execvp.
You can get quite a lot of information about system interfaces using the man command. For example, to learn what freopen does, use man freopen. You can also read manpages on the internet, eg. http://man7.org/linux/man-pages/man3/freopen.3.html, but the documentation on your own system is right there, and also applies to the actual version of the software installed on your system (assuming you installed the documentation).
I'm not entirely certain why you're even using the exec family to run external programs in this case. The C standard library provides perfectly adequate file I/O stuff.
For example, you can simply fopen, fprintf, and fclose the file without ever starting another external process to do that work for you:
int bytesWrit = 0;
FILE *gpioHndl = fopen("/sys/class/gpio/gpio67/value");
if (gpioHndl != NULL) {
bytesWrit = fprintf(gpioHndl, "1\n");
fclose(gpioHndl);
}
if (bytesWrit != 2) {
HandleError();
}
This is probably the preferred way to do what you want, which is simply writing a fixed value to a file.
In terms of why your execv call isn't working (though it's totally irrelevant if you take my advice above), there are several things you need to be aware of.
First, while some commands are actually files on the disk that you can exec, others may be internal bash commands(a). On my system, for example:
pax:~$ type ftp
ftp is /usr/bin/ftp
pax:~$ type echo
echo is a shell builtin
One way to solve this is to run the actual bash executable (which, being an on-disk command, can be done via exec), telling it to run its internal echo command. From the command line:
pax:~$ bash -c 'echo xyzzy'
xyzzy
Second, if want to use redirection, this is normally something that's done by the shell, not the exec calls or individual executables.
Trying to do redirection via the exec family will generally only result in the >somefile being passed as a literal parameter to the executable (in the argv array), not being used to attach standard output to a file. In other words, it won't work unless the executable specifically handles redirection, which is rare.
So that means you will have to run the shell with redirection and have it run the executable after performing those redirections, even if the command is not an internal one.
Thirdly, if you want the path searched for your executable, execvp is the call you want, not execv (the latter just uses the file you explicitly provide, either relative from the current working directory or an absolute path like /bin/ls). So, in your case, you should either:
use execvp to search the path; or
fully specify the path with execv.
(a) The echo command, while it is bash-internal may also be provided as a separate executable (I believe Posix requires this), so this may not be an issue here. It may be an issue if you expect them to act exactly the same in terms of more esoteric arguments :-)
execv() does not search the PATH environment variable in order to find an executable file. Per the Linux execv() man page (bolded text added):
...
Special semantics for execlp(), execvp(), and execvpe()
The execlp(), execvp(), and execvpe() functions duplicate the actions
of the shell in searching for an executable file if the specified
filename does not contain a slash (/) character. ...
...
So, those three will search the PATH environment variable if the filename passed does not contain a / character.
You're using execv(), which is not one of those three. Therefore, execv() will not search the PATH environment variable.
Since your current working directory doesn't contain an executable file called echo, execv() fails.
You need to use execvp() per the man page.
You need to use absolute path as first parameter in execv
Then, the correct is:
char* args[] = {"/bin/echo","echo", "1", ">", "/sys/class/gpio/gpio67/value", NULL};
But to run what you want (put value '1' in file '/sys/class/gpio/gpio67/value'), you need to use command sh:
char* args[] = {"/bin/sh", "sh","-c", "/bin/echo 1 > /sys/class/gpio/gpio67/value", NULL};
The parameter to "sh -c" is a string. Then, you need to put all command together as a string

Need to get C program name inside shell script

I have an occasion where a C program invokes a shell script, which in-turn does some copying stuff from the CD mount location to an installation directory.
Now my question is that, is there a straightforward approach to get the absolute path of this C program inside this shell script ?.
I tried a couple of approaches that includes using "$(ps -o comm= $PPID)" from within the script, but nothing did work out till now. I know that I can create a temporary file from the C program which contains its own name (argv[0]) and then make the shell script to read that file, but I don't want to follow that approach here.
Of course, it can be passed as an argument to the script, but I was thinking why the bash built-in macros or something cannot be used here
On linux there is a /proc/self/exe path that points the absolute path of the current executed file. So you can push an environment variable that contains the path before spawning the shell. Something like:
readlink("/proc/self/exe",...,buf);
putenv("MYEXE",buf);
system("thescript");
and accessing the variable in the script:
echo $MYEXE
Before running a foo command you could use which like
fooprog=$(which foo)
to get the full path of the program (scanning your $PATH). For example which ls could give /bin/ls ....
On Linux specifically you could use proc(5).
In your shell process (running bash or some POSIX compliant shell) started by your C program, $PPID give the parent process id, hopefully the pid of the process running your C program.
Then the executable is /proc/$PPID/exe which is a symbolic link. Try for example the ls -l /proc/$PPID/exe command in some terminal.
(notice that you don't run C source files or stricto sensu C programs, you often run some ELF executable which was built by compiling C code)
You might have weird cases (you'll often ignore them, but you might decide to handle them). Someone might move or replace or remove your executable while it is running. Or the parent process (your executable) died prematurely, so the shell process becomes orphan. Or the executable removed itself.

C program without output in shell or in bash [duplicate]

I am using chdir() to change directory to the value passed as an argument to this function.
I understand that when I run my C program using gcc myCd.c and ./a.out .. this changes the directory to the parent directory "within" the C program (i.e. a child process is spawned for the a.out process, and the change of directory happens within that child process).
What I want to do is, change the directory at the terminal using this C program. I tried writing a shell script for the same, and then sourcing it, and running, that works, but I wanted to achieve this using C.
What you are attempting to do can't be done. The current working directory is a per-process attribute.
If you run a program which changes its cwd, it does not affect any other processes, except for any children it might create after the chdir().
The correct way to change the terminal's working directory is to use the cd command which the shell executes on your behalf and remains within the same process. That is, cd is one of several commands that the shell does not fork(); this makes the cd command work as expected.
sourceing a shell file makes it run within the shell's process. However, if you were to run the script without source, you'd find there was the exact same problem as with a C program: the shell forks to create a process for the script to run, it runs and then exits, and then the shell continues, but without its cwd changed.
this is the way to change the current working directory in C
this needs the unistd.h header file to be included
if( 0 != chdir( "pathToNewDirectory" ) )
{ // then chdir failed
perror( "chdir failed" );
// handle error
}

Resources