help with creating linux shell using C - c

I'm supposed to create a linux shell using C. Below is my code:
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#define SHELL "/bin/sh"
#include "extern.h"
int mysystem (char *command)
{
int status;
pid_t pid;
pid = fork ();
if (pid == 0)
{
execl (SHELL, SHELL, "-c", command, NULL);
_exit (EXIT_FAILURE);
}
else if (pid < 0)
status = -1;
else
if (waitpid (pid, &status, 0) != pid)
status = -1;
return status;
}
Everything is right when I test the code using different commands like "ls", "man", etc. but when I use notepad to create a testfile containing the following:
echo "hello"
exit 2
the return code come out to be 512 when it's supposed to be just 2.
Can anyone help me fix my code?

status is not the exit code; it contains other information as well. Normally the return value is in bits 8-15 of status, but you should be using the macros in wait.h to extract the return value from status in a portable way.
Note that 512 is 2<<8.

Make sure you're using the macros like WIFEXITED and WEXITSTATUS on your status value. See your operating system's man page for waitpid. Here is a description of the POSIX requirements on waitpid.

By notepad do you mean you're using a Windows program to create a Unix shell script? That doesn't work because you end up with CRLF at the end of each line instead of LF. Try the "dos2unix" command on the script to convert it to Unix format and then run it.
I assume you're aware that code is already available in the system() library call? Judging by your function name, I'd guess you're just trying to learn how to do it with system calls.

Try enclosing your command string you supply to /bin/sh with quotes, because otherwise the space character makes /bin/sh think you are supplying another option to the shell itself, not to the command you are calling. For example, try this in a terminal:
/bin/sh -c exit 2
echo $?
and
/bin/sh -c "exit 2"
echo $?
The first one gives 0, and the second one gives the desired 2.

Related

execl in C programming

I have a C program. I noticed that you can't put 2 execl's in it.
The code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
pid_t fork(void);
int system(const char *command);
execl("/bin/sh", "sh", "-c", "kdialog --warningcontinuecancel
\"Make sure to include: \n \n 1. py_lcd folder \n 2. 4x20
Raspberry Pi LCD Display \n 3. Python 2.7.12 to be installed \n
\n If you are missing something, kill the program process and
get them.\"", (char *) 0);
sleep(1);
execl("/bin/sh", "sh", "-c", "kdialog --msgbox \"Setting up files...\" --title \"Installing...\"", (char *) 0);
return(0);
}
Can someone help me if there is a way to bypass this or if i am making a mistake???
The exec family of functions don't return when they succeed. They replace the running process with the one being execed. If you want to run a program in a child process (with full control, unlike system), you need to use fork + exec + wait (or perhaps posix_spawn).
Anything written after execl is a deadcode. The main purpose of execl is to re-use the current process information for a new process to improve performance. You will be using sharing the same structures of process information(pid, stack, heap etc.) of the current process where execl is executed.
I found an answer myself. There is a system() command which works the exact same but you are able to insert it anywhere in the code without problems

When is execle needed instead of execl?

Consider this simple C program
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
if (fork() == 0)
{
execl("script.sh", "script.sh", NULL);
exit(EXIT_FAILURE);
}
int status;
wait(&status);
if (WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS)
{
return 0;
}
return -1;
}
with script.sh being
#!/bin/bash
case $DEBUG in
true)
echo "Debug mode on"
;;
*)
echo "Debug mode off"
;;
esac
If I compile the C program with gcc -o foo main.c and call it with
DEBUG=true ./foo
then the output is Debug mode on, so the script actually got the environment variables I passed to the program foo, even though I didn't use execle. In which scenario then is it necessary to use execle (other than wanting than wanting to specify the environment variables directly in the source code)? I'm talking about people doing something like
extern char **environ;
...
execle(path, path, NULL, environ)
What's the purpose of that?
When you don't want the environment to get inherited (you want the executable to start with an empty environment or an environment you've specifically set up for it).

How can I execute a Bash program from C? [duplicate]

This question already has an answer here:
Is it possible to include a shell script in a C program
(1 answer)
Closed 6 years ago.
How can I execute a command with bash? system() uses sh and not bash.
I know that I can execute commands in bash with system("/bin/bash -c command"). But I have a very long command and /bin/bash -c gives me problems. What I need is bashrun(command) or something else.
The command is a string, not a file
Case1 : script from a file - Use the shebang
#!/usr/bin/env bash
at the top of your script and then do
int status=system("/full/path/to/script");
if(status==-1){
// failure mode
}
Case2 : script stored as a string
Do something like below
char *command="$(which bash) -c 'ls'";
int status=system(command);
if (status==-1){
//failure mode
}
If you're creating a very long shell command and you need bash to interpret it, then you have two real options:
Save the text into a file and invoke bash with the filename as a single argument (equivalently, use a shebang in the file to specify bash as the interpreter, make the file executable, and invoke that as command), or
Start an instance of bash with popen() and write the shell command as standard input to the bash process.
If you're having problems due to shell script quoting (rather than the command length), then either of those options would work, or you could implement the equivalent of system() but using execl() to pass the argument without going through sh. I'm assuming a POSIX-type system here.
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int run_bash(const char *command)
{
int pid = fork();
if (pid < 0) {
/* failed */
perror("fork");
return pid;
} else if (pid == 0) {
/* child */
execl("/bin/bash", "bash", "-c", command, (char*)NULL);
perror("exec");
exit(EXIT_FAILURE);
} else {
/* parent */
int status;
do {
waitpid(pid, &status, 0);
} while (!WIFEXITED(status));
return WEXITSTATUS(status);
}
}
int main(void)
{
run_bash("echo '*'");
}

executing a program in C linux using fork and exec

I want to execute a C program in Linux using fork and exec system calls.
I have written a program msg.c and it's working fine. Then I wrote a program msg1.c.
When I do ./a.out msg.c, it's just printing msg.c as output but not executing my program.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* for fork */
#include <sys/types.h> /* for pid_t */
#include <sys/wait.h> /* for wait */
int main(int argc,char** argv)
{
/*Spawn a child to run the program.*/
pid_t pid=fork();
if (pid==0)
{ /* child process */
// static char *argv[]={"echo","Foo is my name.",NULL};
execv("/bin/echo",argv);
exit(127); /* only if execv fails */
}
else
{ /* pid!=0; parent process */
waitpid(pid,0,0); /* wait for child to exit */
}
return 0;
}
argv[0] contains your program's name and you are Echo'ing it.
Works flawlessly ;-)
/bin/echo msg.c will print msg.c as output if you need to execute your msg binary then you need to change your code to execv("path/msg");
your exec executes the program echo which prints out whatever argv's value is;
furthermore you cannot "execute" msg.c if it is a sourcefile, you have to compile (gcc msg.c -o msg) it first, and then call something like exec("msg")
C programs are not executables (unless you use an uncommon C interpreter).
You need to compile them first with a compiler like GCC, so compile your msg.c source file into a msg-prog executable (using -Wall to get all warnings and -g to get debugging info from the gcc compiler) with:
gcc -Wall -g msg.c -o msg-prog
Take care to improve the msg.c till you get no warnings.
Then, you might want to replace your execv in your source code with something more sensible. Read execve(2) and execl(3) and perror(3). Consider using
execl ("./msg-prog", "msg-prog", "Foo is my name", NULL);
perror ("execl failed");
exit (127);
Read Advanced Linux Programming.
NB: You might name your executable just msg instead of msg-prog ....

Unable to identify behaviour of execl() function call

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.

Resources