Building a shell in C on Ubuntu using execvp - c

I'm about to write a simple shell in C on Ubuntu.
I thought about using the exevcp() function.
I'm only able to run the "ls" command, none of the other commands seems to work.
Can someone tell me why is it happening or give me an idea of a better way to build the shell?
My purpose is to build this shell; I don't understand why can't I just take the command line, and put it as it is, into the execvp() function.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
int main(int argc,char **argv,char **envp)
{
char* args[] = {"history" , NULL};
execvp(args[0],args);
}
Can you explain to me, please?

exec(3) can only be used to run external programs. history is a built-in in most shells, and cannot be run this way.

The execvp() system call has two arguments: the name of the program to be executed, and a pointer to a null-terminated list of strings that are the arguments to the command.
For example:
char *args[] = { "ls", "-l", "-t", "-r", ".", 0 };
execvp(args[0], args);
...report error...
You can't simply pass a whole string into execvp() and expect it to split it up; the invoking code must do that. Note that if string pointed at by the first argument to execvp() contains a slash, then the $PATH mechanism is not used to find the command. A plain name (as shown, "ls") is searched using $PATH.
You also have to remember that some commands, such as history and cd, are shell built-ins; there is no external executable that you can run to get the same effect You won't be able to run those. Other commands, such as test (aka [) are usually implemented as built-ins, but there's also usually a binary in /bin or /usr/bin that does (more or less) the same job, which you can therefore invoke.

Related

How to scp from a C program?

The following C program compiles and spits out a a directory listing of "/Users/home/tempdir" directory.
#include <stdio.h>
#include <unistd.h>
int main(int argc, const char * argv[])
{
execl("/bin/ls", "ls", "/Users/home/tempdir");
// execl("/usr/bin/scp", "scp", "myfile.txt home#127.0.0.1:/Users/home/scpdir");
return 0;
}
This shows it is possible to execute a binary file from a C program. I am trying to figure out how to execute the "scp" binary file and input the required passphrase. How can I modify the above code to achieve a successful file transfer via scp ?
I attempted the line that is commented out above however I get the following message when it is uncommented:
usage: scp [-1246BCEpqrv] [-c cipher] [-F ssh_config] [-i identity_file]
[-l limit] [-o ssh_option] [-P port] [-S program]
[[user#]host1:]file1 ... [[user#]host2:]file2
So it's obviously trying to perform the scp without the password. How do I get around this?
While you could call scp from the command line using expect or something, you are much better off using a library if at all possible.
The general rule when developing is to prefer libraries to system executions whenever possible. It keeps your code cleaner and more portable. The exception is if you are writing a shell script.
http://www.libssh2.org/
libssh2 is a free C library that supports password authentication as well as SCP/SFTP and supports OS X.
http://curl.haxx.se/libcurl/
libcurl supports pretty much every file transfer type you could want, works on just about every platform you could want, and has bindings for pretty much every language you could want.
Short answer, I know, but you'd typically use something like Expect for automating interactive applications in this way.
It appears to me that the output you are printing is from incorrect command syntax. According to the manual page for execl (at least on Arch Linux):
The const char *arg and subsequent ellipses in the execl(), execlp(),
and execle() functions can be thought of as arg0, arg1, ..., argn.
So it would seem the correct way to call it is:
execl("/usr/bin/scp", "scp", "myfile.txt", "home#127.0.0.1:/Users/home/scpdir");
Also, if you want to avoid having to put in the password completely, SSH (which scp is built on top of) can be configured with an "authorized key," which bypasses the password. It may still ask about unknown key fingerprints, though.
An easy way to set up password-less scp access is to run (from the computer initiating the scp):
ssh-copy-id home#127.0.0.1
The above command will ask for a password once, they you shouldn't need to enter it again on subsequent ssh operations.
The following code works for me:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
execl("/usr/bin/scp", "scp", "myfile.txt", "user#IP:path/to/file", (char *)0);
return 0;
}
The following code work for me:
#include <stdio.h>
#include <unistd.h>
int main()
{
execl("/usr/bin/sshpass","sshpass","-p","typeYourPasswd","/usr/bin/scp", "scp", "/home/harekrishna/Downloads/LoRaModule/ArduinoWithMeterCode/dlmsLoraV1.ino", "harekrishna#172.29.118.18:/home/harekrishna",(char *)0);
return 0;
}
but this work for me:
#include <stdio.h>
#include <unistd.h>
int main()
{
execl("/usr/bin/sshpass","sshpass","-p","typeYourPasswd", "scp", "/home/harekrishna/Downloads/LoRaModule/ArduinoWithMeterCode/dlmsLoraV1.ino", "harekrishna#172.29.118.18:/home/harekrishna",(char *)0);
return 0;
}

How to execute shell command inside a chroot jail

I have a problem with the execution of shell commands inside a chroot jail. Here is an exemple:
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
int main()
{
if (geteuid() == 0) // check root privileges
{
chroot("/bin");
chdir("/");
execl("/ls", "ls", "-l", (char *) NULL); // "/ls" should be equivalent to "/bin/ls"
perror(strerror(errno));
}
else
printf("Permission denied\n");
return 0;
}
The problem is the exec: according to errno, the error is "No such file or directory".
The same error appears if I use exec("/bin/ls", ...)
I think that "ls" cannot use the shared libraries he needs, because of chroot jail.
Any suggestion to solve this problem?
You're probably right regarding shared libraries being inaccessible. Setting up a chroot jail typically involves copying parts of /bin, /usr/bin, /lib, and /usr/lib into a parallel directory structure.
A simpler alternative is to use only statically linked executables. On many linux systems there will be a statically linked executable called busybox that provides the base functionality of many Unix commands including ls. Invoking it like busybox ls -l provides similar output to the regular ls program without needed to access addition shared libraries outside the chroot jail.

How to use chroot function in C programming?

I know that use the command "chroot" in linux need some files or directories such as usr, bin and so on. But when I use the function chroot() in C, do I need these files?
Here is my code, which "hw.out" is a binary file which just print "Hello, world". I compiled it and run it as root, but it was failed to print "Hello, world". What else should I do? Thank you!
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int result = chroot(".");
if(result == 0)
printf("Chroot Succese.\n");
char *arrays[]={"./hw.out",NULL};
execvp("./hw.out", arrays);
return 0;
}
execvp is most likely failing, probably with ENOENT: no such file or directory, if hw.out is a dynamically linked executable.
For that to work, all the libraries required by hw.out need to be findable in the chrooted environment.
Try linking hw.out statically, and it should work. (And add error checking after execvp to see what errno is set to after the call if it returns.)
Please test that your hw.out works with command line chroot.
Perhaps hw.out is dynamically linked and is missing some libraries or ld-linux.so in the chroot directory.
Nitpicks 1, what's the point of return 0 after execvp? it never gets executed unless there is an error. I would rather have perror("can't exec"); return 1;
Nitpick 2, chroot() doesn't change working directory, although it works in your case, as you are chrooting to ".", it won't work as you expect if you later change it to chroot("somedir").
Make sure that hw.out is in the correct direct. Perhaps it is easier to have it statically linked if it is using libraries. Otherwise need to enable after chroot that it can access the dynamic libraries.

running shell script using c programming

hello every one I want to ask that I am making a program in which i have to run shell script using c program. up till now i have separated the arguments. and i have searched that exec should be use to run shell scripts
but i am totally confused as there are many variants of exec and by reading the man pages i am unable to find which is best suitable
Also in some exec function first arg is
path
and some have
pointer to file
what is the difference and what should i write in place of it.kindly guide me
thanks
Running a shell script from a C program is usually done using
#include <stdlib.h>
int system (char *s);
where s is a pointer to the pathname of the script, e.g.
int rc = system ("/home/username/bin/somescript.sh");
If you need the stdout of the script, look at the popen man page.
#include <stdio.h>
#include <stdlib.h>
#define SHELLSCRIPT "\
for ((i=0 ; i < 10 ; i++))\n\
do\n\
echo \"Count: $i\"\n\
done\n\
"
int main(void)
{
puts("Will execute sh with the following script:");
puts(SHELLSCRIPT);
puts("Starting now:");
system(SHELLSCRIPT);
return 0;
}
Reference:
http://www.unix.com/programming/216190-putting-bash-script-c-program.html
All exec* library functions are ultimately convenience wrappers over the execve() system call. Just use the one that you find more convenient.
The ones that end in p (execlp(), execvp()) use the $PATH environment variable to find the program to run. For the others you need to use the full path as the first argument.
The ones ending in e (execle(), execve()) allow you to define the environment (using the last argument). This way you avoid potential problems with $PATH, $IFS and other dangerous environment variables.
The ones wih an v in its name take an array to specify arguments to the program to run, while the ones with an l take the arguments to the program to run as variable arguments, ending in (char *)NULL. As an example, execle() is very convenient to construct a fixed invocation, while execv* allow for a number of arguments that varies programatically.

Want the excutable run by execve() to use my preloaded library

I am executing a program say A from another by first fork-ing followed by execve(). Now the problem is I would want A to use my library that I would generaly do by using LD_PRELOAD. How do I do it within execve().
Thanks
you can pass the LD_PRELOAD in envp execve's argument:
the program that gets execved, named "run":
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
printf("%s\n",getenv("LD_PRELOAD"));
}
the program that does the execve, named "ex":
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv)
{
char *const args[] = {"./run",NULL};
char *const envs[] = {"LD_PRELOAD=caca",NULL};
execve("./run",args,envs);
}
running it:
root#pinkpony:~# ./ex
ERROR: ld.so: object 'caca' from LD_PRELOAD cannot be preloaded: ignored.
caca
EDIT:
the error shown gets thrown because "caca" lib can't be preloaded for run, so it works. (I skipped the fork() part for clarity but the usage is the same)
EDIT:
doing something like:
LD_PRELOAD=caca ./ex
will not automagically preload caca lib when execve()-ing run if you're not passing it via envp execve()'s argument
If you want to use LD_PRELOAD just for program A (and not for its parent) you could load it via the shell; pass to the shell the name of the program to execute and add LD_PRELOAD to the environment.
Update
After reading the added info from the question, I'm guessing that you might have to specify a complete path, or set LD_LIBRARY_PATH as well? Since the loader is acknowledging the fact that the preload is ordered.
Otherwise, I can imagine there being a security restriction (allthough it would have to be tied to being run invoked from a login shell, which seems quite brittle to detect). Nonetheless, you may wish to try running as root (use sudo -E to keep your environment)
It would appear from this earlier question that such behaviour is the default
LD_PRELOAD affects new child even after unsetenv("LD_PRELOAD")
Have you tested it?

Resources