How to use the execl() function to execute a C program? - c

I have a C program that computes the next prime number after 100, I need to execute this program using execlp function. The prime number program and execlp function are both different files but are in the same directory. Any help is appreciated, below is my code for execlp function and my C program. I've already tried running the program using execlp, but it doesn't seem to work. I've included the path, the program to run and NULL as arguments.
//Main program, that runs execlp function
int main(int argc, char *argv[]) {
int pid;
int start=0;
pid = fork(); /* fork a child process */
if(pid > 0) { /* parent continues here */
count(start, 'P');
wait(NULL); /* to get printing done before shell prompt */
} else if(pid == 0) { /* child got here */
execlp("/home/student/Documents/FIT2100/PRAC3/", "./primeNum",
NULL); /* execute primeNum program*/
} else { /* there is a problem with fork */
perror("Failed to fork a process\n");
exit(1);
}
}
//Prime number program, filename is called primeNum.c
int main() {
bool isPrime = false;
int counter = 0;
int inputNum;
inputNum = 100;
while(isPrime == false){
inputNum += 1;
for(int i = 1; i <= inputNum; i++){
if(inputNum % i == 0){
counter++;
}
}
if(counter <= 2){
isPrime = true;
}
}
printf("Next prime after 100 is %d\n", inputNum);
}

The 'path' argument to execl() is the pathname of the executable, not the directory containing the executable. Making a guess, you probably need:
execlp("/home/student/Documents/FIT2100/PRAC3/primeNum", "primeNum", (char *)NULL);
The first argument is the absolute pathname of the program; the second is the string that will passed as argv[0]; the NULL (which should strictly be (char *)NULL or (char *)0 to avoid possible problems with #define NULL 0 — as shown) marks the end of the arguments. There's nothing to stop you using "pink elephant" as the argv[0] value if you want to.
A relative pathname is perfectly acceptable; if an open() call would find the executable file using the name, then it is OK for use with execl().

Related

process loop in C

I'm trying to write a code about a process that executes programs from $PATH using the execlp() command.(it doesn't need to be the execlp command but I've found it useful for this one) I've achieved my expected output, but I need to run more than one commands. More specifically I want the child process to run the exec command, then the parent process to print a text indicating that it's ready to accept another command. Then the child process will run the new exec command. My code is this:
int main ( int argc, char *argp[]) {
pid_t progpid = fork(); // the fork command for the creation of the child process
int status = 0;
char com[256];
if (progpid < 0) // the check in case of failure
{
printf("PROGRAM ABORTED!");
return 0;
}
do
{
if (progpid == 0) // the child process
{
scanf( "%s", com);
if (com == "exit")
{
exit(0);
}
else
{
execlp(com, com, NULL);
}
}
else //the parent process
{
wait(&status);
printf("$");
}
}while (com != "exit");
return 0;
}
The expected output is :
<program which I input from keyboard> ( for example : ls )
<output of the program>
$<next program>
<output of the next program>
.
.
.
$exit
In short I want to keep running programs till I enter exit where it ends without doing anything else. However the output I get is this:
<program>
<output of program>
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
It keeps printing $ until I shut it down. I'm new to processes so please don't be too harsh about my code so far.
Thank you in advance!
This
if (com == "exit")
should be
if (strcmp(com, "exit") == 0)
Similarly change the while condition as well.
In C, string comparisons are done using strcmp(). == in your case, simply compares the address of com and the address of the string literal "exit". (In expressions, an array gets converted into a pointer to its first element. Hence, "address" comparison. Also see: What is array decaying?).
Note that your execlp() call has an issue. NULL may be defined as 0, in which case execlp(), being a variadic function, may be able to recognize it as the last argument.
I'd suggest to change it to:
execlp(com, com, (char*)0);
You'd also want to check if wait() failed or not by checking its return code.
Here's a simple example based on yours with improved error checking.
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main ( int argc, char *argp[]) {
for(;;) {
char com[1024];
printf("$ ");
fgets(com, sizeof com, stdin);
com[strcspn(com, "\n")] = 0; /* Remove if there's a newline at the end */
if (strcmp(com, "exit") == 0) {
exit(0);
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child process */
execlp(com, com, (char*)0);
}
int status;
int rc = wait(&status);
/* You can inspect 'status' for further info. */
if (rc == -1) {
perror("wait");
exit(1);
}
}
return 0;
}
Note that if you want your to execute commands with arguments then you need to do argument processing.

Fork and execve segmentation fault

My program should use fork and exec system calls. The exec should change the child process such that it takes another command as an argument and executes
that command. For example, to display the message of the day:
./myexec cat /etc/motd
This is my current code
extern char **environ; /* environment info */
main(int argc, char **argv) {
/* argc -- number of arguments */
/* argv -- an array of strings */
char *argvNew[argc + 1];
int pid;
for(int i=0; i<argc; i++){
argvNew[i] = argv[i];
}
argvNew[argc + 1] = NULL;
printf("After For: %s\n",argvNew[0]);
printf("After For: %s\n",argvNew[1]);
printf("After For: %s\n",argvNew[2]);
printf("After For: %s\n",argvNew[3]);
if ((pid = fork()) < 0) {
fprintf(stderr, "Fork error%sstrerror\n", strerror(errno));
exit(1);
}
else if (pid == 0) {
/* child process */
if (execve(argvNew[0], argvNew, environ) < 0) {
fprintf(stderr, "Execve error%d %s\n",errno,strerror(errno));
exit(1);
}
}
else {
/* parent */
wait(0); /* wait for the child to finish */
}
}
After running ./myexecv cat etc/motd nothing happens; just the print statements. Any advice going forward?
There are multiple bugs in the shown code.
for(int i=0; i<argc; i++){
argvNew[i] = argv[i];
}
argvNew[argc+1] = NULL;
On it's face value, the NULL assignment is wrong, and will result in undefined behavior because argvNew is declared as
char *argvNew[argc + 1];
So the array contains values argvNew[0] through argvNew[argc], and argvNew[argc+1]=NULL; runs off past the end of the array, that results in undefined behavior. This should obviously be
argvNew[argc] = NULL;
But even that would also be wrong, because:
execve(argvNew[0], argvNew, environ);
argvNew[0] is copied from argv[0], which is the name of this program being executed. This is going to fork and run the same program, in the child process.
You will end up forkbombing yourself. If this is a shared server, you will make the system administrator very very mad.
You need to remove argv[0] from the equation, and copy over only argv[1], and on. The correct loop, and copy, is:
int pid;
char *argvNew[argc];
for(int i=1; i<argc; i++){
argvNew[i-1] = argv[i];
}
argvNew[argc-1] = NULL;
The calling parameters of execve() require the first parameter to be the filename to execute. Unfortunately you pass it argvNew[0] which is the same value as argv[0]. This means that you call and call again your own program and never the script. You need to shift the parameters by one:
...
for(int i=1; i<argc; i++){
argvNew[i-1] = argv[i];
}
argvNew[argc-1] = NULL;
...

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)

Writing a shell in C with fork() and execvp(). Why is execvp not executing anything?

I'm trying to implement a minimal shell using C. I will run it on a linux machine.
Basically, execvp() does not seem to execute anything. Why is this? I have some code that tries to see if there is an error. Any command I enter into my mini-shell returns that error statement. Also, nothing happens, a sure sign something isn't working.
The problem from the book I'm working on says to invoke execvp() as execvp(args[0], args). I see this referenced on stackoverflow as the way to invoke it, but some also suggest execvp(args[0],args[1]) Which one is it?
I think by now I have little to no errors in the way I'm handling user input, but if there's anything just point it out.
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<time.h>
#define MAXLINE 80
int main(void)
{
char *args[MAXLINE/2 + 1]; //cl w/ 40 max args
//the above is an array of pointers-to-char
char *s;
int k;
int pid;
int lastarg;
int status;
int should_run = 1; //determines when to exit
printf("CS149 Shell from MYNAME HERE\n");
while(should_run)
{
/* PROMPT */
printf("AUTHOR-L3006323213> ");
fflush(stdout);
s = (char*) calloc ( 200, sizeof(char) );
fgets(s,199,stdin);
/* PARSE */
k=0;
args[k] = strtok(s," \n\t");
while(args[k])
{
printf("%d %s\n",k, args[k] );
++k;
args[k] = strtok(NULL," \n\t");
}
args[k]=NULL;
lastarg=k-1;
/* HANDLE EXIT */
if (strcmp(args[0],"exit\n") == 0)
{
should_run=0;
continue;
}
//fork child process using fork();
pid = fork();
if(pid<0) printf("ERROR!\n");
else if(pid==0)
{ //child
if(execvp(args[0], &args[1]) < 0)
printf("Command not found.\n");
exit(1);
}
else
{ //parent
//check last arg for == &
printf("Parent is ");
if(strcmp(args[lastarg],"&") == 0)
printf(" not waiting...\n");
else
{
printf("waiting...\n");
while (wait(&status) != pid);
}
}
/* Cleanup for next prompt */
free(s);
}
return 0;
}
EDIT: So I've managed to fix somethings. I'm invoking execvp() using execvp(args[0], &args[1]) now. If I give my terminal ps &, I see a list of processes, but another prompt does not show up. When I give my terminal ps, I do not see any list of processes at all. I think there's an issue with the parent code, and maybe with my control logic.

Stream redirection and pipes when making a Linux shell

I have an assignment to create a Linux shell in C. Currently, I am stuck on implementing redirections and pipes. The code that I have so far is below. The main() parses user's input. If the command is built in, then that command is executed. Otherwise, the tokenized input is passed to execute() (I know that I should probably pull the built-in commands into their own function).
What execute() does is loop through the array. If it encounters <, >, or | it should take appropriate action. The first thing I am trying to get to work correctly is piping. I am definitely doing something wrong, though, because I cannot get it to work for even one pipe. For example, a sample input/output:
/home/ad/Documents> ls -l | grep sh
|: sh: No such file or directory
|
My idea was to get each of the directions and piping work for just one case, and then by making the function recursive I could hopefully use multiple redirections/pipes in the same command line. For example, I could do program1 < input1.txt > output1.txt or ls -l | grep sh > output2.txt.
I was hoping that someone can point out my errors in trying to pipe and perhaps offer some pointers in how to approach the case where multiple redirections/pipes are inputted by the user.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int MAX_PATH_LENGTH = 1024; //Maximum path length to display.
int BUF_LENGTH = 1024; // Length of buffer to store user input
char * delims = " \n"; // Delimiters for tokenizing user input.
const int PIPE_READ = 0;
const int PIPE_WRITE = 1;
void execute(char **argArray){
char **pA = argArray;
int i = 0;
while(*pA != NULL) {
if(strcmp(argArray[i],"<") == 0) {
printf("<\n");
}
else if(strcmp(argArray[i],">") == 0) {
printf(">\n");
}
else if(strcmp(argArray[i],"|") == 0) {
int fds[2];
pipe(fds);
pid_t pid;
if((pid = fork()) == 0) {
dup2(fds[PIPE_WRITE], 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
char** argList;
memcpy(argList, argArray, i);
execvp(argArray[0], argArray);
}
if((pid = fork()) == 0) {
dup2(fds[PIPE_READ], 0);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
execvp(argArray[i+1], pA);
}
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
wait(NULL);
wait(NULL);
printf("|\n");
}
else {
if(pid == 0){
execvp(argArray[0], argArray);
printf("Command not found.\n");
}
else
wait(NULL);*/
}
*pA++;
i++;
}
}
int main () {
char path[MAX_PATH_LENGTH];
char buf[BUF_LENGTH];
char* strArray[BUF_LENGTH];
/**
* "Welcome" message. When mash is executed, the current working directory
* is displayed followed by >. For example, if user is in /usr/lib/, then
* mash will display :
* /usr/lib/>
**/
getcwd(path, MAX_PATH_LENGTH);
printf("%s> ", path);
fflush(stdout);
/**
* Loop infinitely while waiting for input from user.
* Parse input and display "welcome" message again.
**/
while(1) {
fgets(buf, BUF_LENGTH, stdin);
char *tokenPtr = NULL;
int i = 0;
tokenPtr = strtok(buf, delims);
if(strcmp(tokenPtr, "exit") == 0){
exit(0);
}
else if(strcmp(tokenPtr, "cd") == 0){
tokenPtr = strtok(NULL, delims);
if(chdir(tokenPtr) != 0){
printf("Path not found.\n");
}
getcwd(path, MAX_PATH_LENGTH);
}
else if(strcmp(tokenPtr, "pwd") == 0){
printf("%s\n", path);
}
else {
while(tokenPtr != NULL) {
strArray[i++] = tokenPtr;
tokenPtr = strtok(NULL, delims);
}
execute(strArray);
}
bzero(strArray, sizeof(strArray)); // clears array
printf("%s> ", path);
fflush(stdout);
}
}
Part of the problem is in the pipe handling code - as you suspected.
else if (strcmp(argArray[i], "|") == 0) {
int fds[2];
pipe(fds);
pid_t pid;
if ((pid = fork()) == 0) {
dup2(fds[PIPE_WRITE], 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
char** argList;
memcpy(argList, argArray, i);
execvp(argArray[0], argArray);
}
if ((pid = fork()) == 0) {
dup2(fds[PIPE_READ], 0);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
execvp(argArray[i+1], pA);
}
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
wait(NULL);
wait(NULL);
printf("|\n");
}
The first execvp() was probably intended to use argList since you've just copied some material there. However, you've copied i bytes, not i character pointers, and you've not ensured that the pipe is zapped and replaced with a null pointer.
memcpy(argList, argArray, i * sizeof(char *));
argList[i] = 0;
execvp(argList[0], argList);
Note that this has not verified that there is no buffer overflow on argList; Note that there is no space allocated for argList; if you use it, you should allocate the memory before doing the memcpy().
Alternatively, and more simply, you can do without the copy. Since you're in a child process, you can simply zap replace argArray[i] with a null pointer without affecting either the parent or the other child process:
argArray[i] = 0;
execvp(argArray[0], argArray);
You might also note that the second invocation of execvp() uses a variable pA which cannot be seen; it is almost certainly incorrectly initialized. As a moderately good rule of thumb, you should write:
execvp(array[n], &array[n]);
The invocations above don't conform to this schema, but if you follow it, you won't go far wrong.
You should also have basic error reporting and a exit(1) (or possibly _exit(1) or _Exit(1)) after each execvp() so that the child does not continue if it fails to execute. There is no successful return from execvp(), but execvp() most certainly can return.
Finally for now, these calls to execvp() should presumably be where you make your recursive call. You need to deal with pipes before trying to deal with other I/O redirection. Note that in a standard shell, you can do:
> output < input command -opts arg1 arg2
This is aconventional usage, but is actually permitted.
One good thing - you have ensured that the original file descriptors from pipe() are closed in all three processes (parent and both children). This is a common mistake which you have avoided making; well done.

Resources