I'm trying to execute the command "ls -l" but I'm not exactly sure how to approach it.
This is what I've tried:
int main(void) {
char * input;
char * args[2];
char buff[100];
input = malloc(sizeof(buff));
while(fgets(input,sizeof(input),stdin) != NULL) {
printf("Enter a command\n");
if(strcmp(input,"ls -l\n") ==0) {
pid_t childPid;
childPid = fork();
if(childPid == 0) {
args[0] = "/bin/ls -l";
args[1] = NULL;
execv(args[0],args);
}
}
}
free(input);
}
However, the command doesn't seem to work here. It works if I just simply use "ls" but I want to use "ls -l" is there another argument I have to pass to get this to work?
When you call any of the exec() variants, you have to pass each argument separately, as in
args[0] = "/bin/ls";
args[1] = "-l";
args[2] = NULL;
First you have to understand this simple example.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
/* status of child execution */
int status;
/* pointer * to array of char*(strings)*/
char ** args;
/* allocate memory for three char*(stings) */
args = (char**) malloc( 3 * sizeof(char*) );
/* fork program and store each fork id */
pid_t childPid = fork();
/* if this is child process */
if(childPid == 0) {
args[0] = "ls";
args[1] = "-l";
args[2] = NULL;
/* execute args[0] command with args arguments */
execvp(args[0],args);
/* send execution code 0 to parent and terminate child */
exit(0);
} else {
/* wait execution code from child*/
wait(&status);
/* free allocated space */
free(input);
free(args);
/* exit program with received code from child */
exit(status);
}
}
I commented every line, but tell me if you want more informations.
You have to understand how to execute commands from child and inform parent before continue to user's input commands.
Related
Hello i am creating a simple shell in C, currently i am having an issue with fork as it always seems to return a value > 0 even when i type in some non linux command like "ehshhduh" it will still print out"Chile process complete" instead of returning an Error message could anyone explain to me what i am doing wrong thanks?
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
char str_tokens[50];
char * get_input() {
pid_t pid;
char *tokens[30][50];
char input[512];
char *charp;
char *burner[20];
int i = 0;
while(true) {
printf(">");
if(fgets(input, 512, stdin) == 0 || strncmp(input, "exit", 4) == 0) { /* reads in input from user no more than 512 chars including null terminator, program terminates if "exit" or CTRL-D is detected*/
printf("exitting program");
exit(0);
}
const char delimiters[9] = {'&', ';', '\n', '|', ' ', '\t', '>', '<',};
charp = strtok(input, delimiters); /* splits the string on delimiters */
while (charp != NULL) {
strcpy(str_tokens, charp);
charp = strtok(NULL, delimiters); // sets the char pointer to null
//after each split of the string
//so the loop eventually terminates */
}
char *args[] = {str_tokens, 0};
pid = fork(); // fork a child process
if (pid == -1) { /* error occured */
fprintf(stderr, "Error occured with Fork");
return 1;
}
else if (pid > 0) { /* parent process */
/* parent will wait for the child process to complete */
wait(NULL);
printf("Child process completed") ;
}
else if(pid == 0){ // (pid == 0) /* this is the child process */
execvp(args[0], args); /* first param the file path, seconf is null terminated array of char pointers*/
printf("this is the child process");
_exit(EXIT_FAILURE); /* in the case our exec never returns exit */
}
}
return str_tokens;
}
void run_external(char* commands[50]) {
commands = get_input();
}
int main () {
run_external(get_input());
return(0);
}
The call to fork happens before you call execvp on the new program.
First you create a new process, then when you've done that successfully you attempt to start a new program in that process. If the program name is invalid then execvp returns and you _exit the child process.
Also, you should call perror when fork or execvp fail. That will print an error message describing why.
I am new to system calls and C programming and am working on my university assignment.
I want to call the 'ls' command and have it print the directory.
What I have: (I have added comments in so you can see what I see coming through each variable.
int execute( command* cmd ){
char full_path[50];
find_fullP(full_path, p_cmd);
//find_fullP successfully updates full_path to /bin/ls
char* args[p_cmd->argc];
args[0] = p_cmd->name;
int i;
for(i = 1; i < p_cmd->argc; i++){
args[i] = p_cmd->argv[i];
}
/*
* this piece of code updates an args variable which holds arguments
* (stored in the struct) in case the command is something else that takes
* arguments. In this case, it will hold nothing since the command
* will be just 'ls'.
*/
int child_process_status;
pid_t child_pid;
pid_t pid;
child_pid = fork();
if ( child_pid == 0 ) {
execv( full_path, args );
perror("fork child process error condition!" );
}
pid = wait( &child_process_status );
return 0;
}
I am not seeing anything happening and am confused, any idea?
Here's the minimal program that invokes ls using execv. Things to note
the list of args should include the executable as the first arg
the list of args must be NULL terminated
if the args are set up correctly, then args[0] can be passed as the first parameter to execv
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main( void )
{
int status;
char *args[2];
args[0] = "/bin/ls"; // first arg is the full path to the executable
args[1] = NULL; // list of args must be NULL terminated
if ( fork() == 0 )
execv( args[0], args ); // child: call execv with the path and the args
else
wait( &status ); // parent: wait for the child (not really necessary)
return 0;
}
I am writing a C program to emulate a simple shell. This shell will basically evaluate commands like any other shell (ls, cat, etc.), as well as handle pipelining and redirection.
Currently, I am trying to start out by getting user input, tokenizing it, and executing the command provided (e.g. executing only "ls" and not "ls -l"). However, I am having a lot of difficulty with the forking. It seems that every time I fork, something goes wrong and hundreds of identical processes are created, leading to my computer freezing and me having to restart. The code appears to be correct, but I have no idea what is causing this behaviour. Below is the relevant portion of my code (main method and input tokenizer method).
int main() {
char inputLine[512]; //user input
char *args[10]; //arguments
char* pathVar = "/bin/";//path for argument
char programPath[512]; //pathVar + args[0]
int n; //count variable
//loop
while (1) {
//print prompt, get input
printf("input> ");
fgets(inputLine, 512, stdin);
n = tokenizer(inputLine, args);
//fork process
pid_t pid = fork();
if (pid != 0) { //if parent
wait(NULL);
} else { //if child
//format input for execution
strcpy(programPath, pathVar);
strcat(programPath, args[0]);
//execute user command
int returnVal = execv(programPath, args);
}
}
return 0;
}
int tokenizer(char *input, char *args[]) {
char *line; //current line
int i = 0; //count variable
line = input;
args[i] = strtok(line, " ");
do {
i++;
line = NULL;
args[i] = strtok(line, " ");
} while (args[i] != NULL);
return i;
}
Putting it all together:
You need to check fork and execv for failure.
You should exit after an execv failure (and perhaps after a fork failure).
And you need to add \n to the strtok delimiters (or remove the newline from the input line in some other way).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define MAXARGS 10
#define PATH "/bin/"
int main() {
char inputLine[BUFSIZ];
char *args[MAXARGS];
char programPath[BUFSIZ + sizeof(PATH) + 10];
while (1) {
printf(":-> ");
if (fgets(inputLine, BUFSIZ, stdin) == NULL) /* ctrl-D entered */
break;
tokenize(inputLine, args);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (pid != 0) { /* parent */
wait(NULL);
} else { /* child */
strcpy(programPath, PATH);
strcat(programPath, args[0]);
execv(programPath, args); /* will not return unless it fails */
perror("execv");
exit(EXIT_FAILURE);
}
}
return 0;
}
int tokenize(char *input, char *args[]) {
int i = 0;
args[0] = strtok(input, " \n");
for (i = 0; args[i] && i < MAXARGS-1; ++i)
args[++i] = strtok(NULL, " \n");
return i;
}
You should check that execv doesn't fail and also be sure to exit() at the end of the child block.
//execute user command
int returnVal = execv(programPath, args);
// check return from execv
if (returnVal < 0) {
perror("execv");
exit(1);
}
Also, beware using functions like strcpy in this context since they may lead to buffer overflows. If an untrusted attacker type is talking to your shell this type of security issue could let them break out of the "sandbox".
I want to write a program which will create a new process and in that child process, it should execute the command: ls. In the meanwhile, the parent should wait for the child to die. However, my code does not work.
Please help me thank you very much!
int main()
{
char * read;
size_t size;
getline(&read , &size , stdin);
read[strlen(read)-1] = '\0';
printf("%s\n" , read);
int status;
pid_t f;
if((f = fork()) == 0)
{
execvp(read , &read);
exit(0);
}
else
{
wait(&status);
}
}
From man execvp:
The execv(), execvp(), and execvpe() functions provide an array of pointers to null-terminated strings that represent the argument list available to the new program. The first argument, by convention, should point to the filename associated with the file being executed. The array of pointers must be terminated by a NULL pointer.
You need to use an array of char* and set the last element to NULL.
I am unsure what the getline() is reading but I guess it is the directory to be lsd. The first argument to execvp() should be ls and the second argument the array of char*.
Consider the following:
#define _GNU_SOURCE
#define _POSIX_C_SOURCE 200809L
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(int argc, char *argv[])
{
char *input_arg[2];
char *input_str = NULL;
size_t input_len = 0;
char **args;
ssize_t len;
size_t n;
pid_t child, p;
int status;
if (argc < 2) {
/* No command line parameters. Read command from stdin. */
len = getline(&input_str, &input_len, stdin);
/* Find length excluding the newline at end. */
if (len > (ssize_t)0)
n = strcspn(input_str, "\r\n");
else
n = 0;
if (n > (size_t)0) {
/* Terminate input command before the newline. */
input_str[n] = '\0';
} else {
fprintf(stderr, "No input, no command.\n");
return 1;
}
input_arg[0] = input_str;
input_arg[1] = NULL;
args = input_arg;
} else {
/* Use command line parameters */
argv[argc] = NULL;
args = argv + 1;
}
child = fork();
if (child == (pid_t)-1) {
fprintf(stderr, "Cannot fork: %s.\n", strerror(errno));
return 1;
}
if (!child) {
/* This is the child process. */
errno = ENOENT;
execvp(args[0], args);
fprintf(stderr, "%s: %s.\n", args[0], strerror(errno));
exit(127);
}
do {
p = waitpid(child, &status, 0);
} while (p == (pid_t)-1 && errno == EINTR);
if (p == (pid_t)-1) {
fprintf(stderr, "Lost child process: %s.\n", strerror(errno));
return 127;
}
if (p != child) {
fprintf(stderr, "waitpid() library bug occurred.\n");
return 127;
}
if (WIFEXITED(status)) {
if (!WEXITSTATUS(status))
fprintf(stderr, "Command successful.\n");
else
fprintf(stderr, "Command failed with exit status %d.\n", WEXITSTATUS(status));
return WEXITSTATUS(status);
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "Command died by signal %s.\n", strsignal(WTERMSIG(status)));
return 126;
}
fprintf(stderr, "Command died from unknown causes.\n");
return 125;
}
The above uses the command line parameters if specified, otherwise it reads one from the standard input. Because the standard input is not tokenized, you can only supply the command name, no parameters. If you enlarge the input_arg[] array into
char *input_arg[4];
and modify the assignment into
input_arg[0] = "/bin/sh";
input_arg[1] = "-c";
input_arg[2] = input_str;
input_arg[3] = NULL;
args = input_arg;
then the input string will be processed using the /bin/sh shell, just like popen() does.
You can also use len = getdelim(&input_str, &input_len, '\0', stdin); and remove the input_str[n] = '\0'; assignment to allow multiline input; the shell should handle those fine, as long as it is short enough to fit in the command line argument buffer (maximum length depends on your OS).
The rules how shells split input into separate commands and parameters are rather complex, and you should not try to emulate them. Instead, find a simple way for the user to specify the parameters separately (like the command-line parameter case), or use the shell to do it for you. If you don't do any splitting, you will probably need to remove the newline at the end of the input line.
The point to note is that for execvp(file, args), args[0] is the name the application sees (as $0 or argv[0]), and args[1] is the first parameter. Each parameter is terminated by NUL (\0) just like strings are normally in C, and the args pointer array must end with a NULL pointer. If there are no parameters, then args[1] == NULL.
why dont you just use system command...
#include <stdio.h>
#include <stdlib.h>
int main ()
{
int i;
printf ("Executing command ls...\n");
i=system ("ls");
printf ("The value returned was: %d.\n",i);
return 0;
}
Update:
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void main(void)
{
pid_t pid;
pid = fork();
if (pid == 0) // this is child process
{
int i;
printf ("Executing command ls...\n");
i=system ("ls");
printf ("The value returned was: %d.\n",i);
}
else // this is paraent process
{
int status=0
wait(&status);
printf ("Child process is returned with: %d.\n",status);
}
}
Trying to implement a shell, mainly piping. I've written this test case which I expect to simply pipe ls to wc...it definitely doesn't work as expected. It prints ls to the terminal then prints memory exhausted.
I'm very lost in how to fix this and get it to work. find_path works in all of my tests.
Edit - I have to use execv for the project, its a class thing, but I've tried it with execvp just in case and it does the exact same thing. Also this is just an example, a test to see why it does not work, I call fork twice once for both commands and waitpid because I have nothing else to do.
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int find_path(char* execname, char** dst)
{
char *path = getenv("PATH");
path = strdup(path);
char *pos;
path = strtok_r(path, ":", &pos);
char *originalpath = path;
do
{
char* test = (char*)calloc(strlen(path) + strlen(execname) + 2, sizeof(char));
test = strcpy(test, path);
int testlen = strlen(test);
(*(test+testlen)) = '/';
strcpy(test + testlen + 1,execname);
struct stat buf;
int result = stat(test, &buf);
if (result == 0)
{
*dst = test;
free (originalpath);
return 1;
}
else
{
free(test);
}
} while ((path = strtok_r(NULL, ":", &pos)) != NULL);
free(originalpath);
return 0;
}
int main()
{
char *cmd1 = "ls";
char *cmd2 = "wc";
int filedes[2];
pipe(filedes);
char** argv = (char**)calloc(1, sizeof(char*));
argv[0] = (char*)malloc(sizeof(char*));
argv[0] = NULL;
pid_t pid = fork();
if (pid == 0)
{
char *path;
find_path(cmd1, &path);
dup2(filedes[1],stdout);
execv(path,argv);
}
pid = fork();
if (pid == 0)
{
dup2(filedes[0], stdin);
char *path;
find_path(cmd2, &path);
execv(path, argv);
}
else
waitpid(pid);
}
Often when it is hard to debug a program, it is best to simplify it a little to eliminate sources of error. Here is your program, simplified to remove find_path as a source of errors:
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(void)
{
int filedes[2];
pipe(filedes);
/* Run LS. */
pid_t pid = fork();
if (pid == 0) {
/* Set stdout to the input side of the pipe, and run 'ls'. */
dup2(filedes[1], 1);
char *argv[] = {"ls", NULL};
execv("/bin/ls", argv);
} else {
/* Close the input side of the pipe, to prevent it staying open. */
close(filedes[1]);
}
/* Run WC. */
pid = fork();
if (pid == 0) {
dup2(filedes[0], 0);
char *argv[] = {"wc", NULL};
execv("/usr/bin/wc", argv);
}
/* Wait for WC to finish. */
waitpid(pid);
}
This should behave as you expect.
During simplification, a few errors came out:
argv[] wasn't being setup correctly, in particular, argv[0] was being set to NULL;
The program was not closing the input side of the pipe that was being given to ls. When ls finished, the pipe wasn't being closed (because the wc process still had it open), preventing wc from ever finishing.
The program was confusing the values stdout and stdin (which are of type FILE*) with the file descriptor numbers 0 and 1 (used by dup, pipe, etc.)
There is a lot you can do to improve this code (e.g. breaking this into smaller functions would be a start), but I suspect your out of memory issue is from the code in find_path(), which you could avoid entirely by using execvp which will locate the executable using the standard PATH mechanism for you. It is probably a good idea to install a signal handler using sigaction to handle SIGCHLD and invoke waitpid from the signal handler, instead of just invoking waitpid() ad-hoc like you are doing. You appear to be forking more times than you want, and you aren't checking for errors. Hope these suggestions help.