Calling 'ls' with execv - c

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;
}

Related

How can I get my C Shell to recognize that this is a command?

I am very new at C but am currently working on creating a C program to serve as a shell interface. It is supposed to accept commands and then execute each command in a separate process. I am currently stuck trying to get C to recognize that it is a command. I am unsure how to do this, and can't seem to find any useful examples.
Here is my code, it is saying that everything is not a valid command ("no cmd"). Does anyone have any idea why this would be occurring? Is C not able to recognize it is a command in the execvp() function or do I need to implement something for that specific purpose?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_LINE 80
/* 80 chars per line per command */
int main(void) {
//char *args[MAX_LINE/2 + 1];
char *args = calloc(MAX_LINE, (MAX_LINE/2 +1));
const size_t sz = MAX_LINE;
pid_t pid;
/* command line (of 80) has max of 40 arguments*/
int should_run = 1;
while (should_run) {
printf("osh>"); //beginning of command line
fgets(args, sz, stdin); //gets the stdin
char *token = strtok(args, " \n"); //supposed to break str if it has a space or line and recognize there are 2 commands
printf("%s\n", token);
token = strtok(NULL," \n");
printf("%s\n", token);
pid_t parent = getpid();
pid = fork(); //forking child
if(pid == 0){ //if forking occurred
int status = execvp(&args[0], &args); //status of input, is it a command?
printf("%d", status);
printf("forked!");
if(status == -1) { //if cmd err, print
printf("no cmd");
return 1;
} else {
printf("line will be printed");
}
return 0;
}
fflush(stdout); //flush
/*
* After reading user input, the steps are :
* 1: fork a child process
* 2: the child process will invoke execvp()
* 3: parent process waits for the child to exit before
continuing
*/
}
exit(0);
/**
return to the operating system:
-exit this process
-flush all
*/
}
If you look at the documentation for the exec family of functions, you'll note that the functions only return if the exec failed. That's because exec, when successful, completely replaces the calling process with the invoked program.
What you need to do is, from the parent process (i.e., the one that got a positive value returned from fork), wait on the child process via waitpid.
pid_t pid;
pid = fork();
if ( pid < 0 ) {
// Handle the error.
}
else if ( pid == 0 ) {
execvp(&args[0], &args);
// The fact that we've reached this line means that execvp failed.
exit(1);
}
else {
int status;
while ( waitpid(pid, &status, 0) != pid ) {} // We need this loop in case waitpid gets interrupted by a signal.
// status doesn't equal the return value of the child process. We need to extract that with macros.
if ( WIFEXITED(status) ) {
printf("Child process exited with code %i\n", WEXITSTATUS(status));
}
else {
printf("Child process was terminated by signal number %i\n", WTERMSIG(status));
}
}

Pass variable arguments to newly created process

In linux
I have temp.c file. In that i created new process and i need to pass all arguments to that
int main (int argc, char *argv[])
{
if( (cid1 = fork()) == 0 ) //child1
{
res = execv(proc1, &argv[1]);
}
}
Now i compile this and run as
./a.out "arg1 arg2 arg3"
Now i want to pass this arg1, arg2 and arg3 to new process created but inside that when i check argc it show me 2 instead of 4
Why this mismatch happen and inside proc1 i have only 1 argument which value is "arg1 arg2 arg3" but here i want 3 argument so argv[1] = arg1, argv[2]=arg2, argv[3]=arg3
How to achive this?
Very useful example at
https://support.sas.com/documentation/onlinedoc/sasc/doc/lr2/execv.htm
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
main()
{
pid_t pid;
char *const parmList[] = {"/bin/ls", "-l", "/u/userid/dirname", NULL};
if ((pid = fork()) == -1)
perror("fork error");
else if (pid == 0) {
execv("/bin/ls", parmList);
printf("Return not expected. Must be an execv error.n");
}
}
As you can see execv() accepts two arguments. The first is executable filename. The second is a pointer to an array of pointers to null-terminated character strings and NULL pointer is used to mark the end of the array.
Create new char* array, assign proc1 to first element (index 0) and required parameters to next elements.

Executing programs from main argument

I want to make a program that executes programs from the main argument.
Let's say I have the program progr1.c and progr2.c running . Now I compile the program progr3.c using the other two programs as arguments. This should be the result :
./progr3 progr1 progr2
Result of progr1
Result of progr2
This is what I have done so far :
void main (int args , char **s) {
int i , x , status;
if (args >= 2) {
x = fork ();
for ( i = 1 ; i < args ; i++) {
if (x == 0) {
execv (s[1],s);
}
else
{
wait (&status);
_exit(i);
}
x= fork();
}
}
_exit(0);
}
This code only does the output of the first argument program. So Im guessing fork() and execv() are not working together as I wanted them to . How can I change the program inorder to do what I want?
When you run
execv (s[1],s); // That should have been s[i]
prog3 is replaced by prog1. Hence, the logic of prog3 is lost.
You need to use fork and then execv on the child process if you want to retain the logic of prog3.
If you are interested in just the output of prog1 and prog2, you can use system(s[i])
Update
A working example using fork.
#include <unistd.h>
int main()
{
char* programs[] = {"/usr/bin/ls", "/usr/bin/date"};
char* args[] = {NULL, NULL};
for (int i = 0; i < 2; ++i )
{
int pid = fork();
if ( pid )
{
// Child
args[0] = programs[i];
execv(programs[i], args);
}
}
}
execv replaces the current process with the command specified.
Instead, use system to create a new process. No need for forking either:
int system(const char *command)

Using Fork for Command Line Arguements

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.

create a new process to execute ls command

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);
}
}

Resources