I have an assignment, to create a simple linux shell using exec() that runs basic commands (e.g ls, ps) without arguments. When i run the code the execv is not working.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char argv[100];
char* path= "/bin/";
char progpath[20];
while(1)
{
printf("My shell>> ");
gets(argv);
if(strcmp(argv, "exit\n")==0){
break;
}
strcpy(progpath, path);
strcat(progpath, argv);
for(int i=0; i<strlen(progpath); i++){
if(progpath[i]=='\n'){
progpath[i]='\0';
}
int pid= fork();
if(pid==0){
execvp(progpath,argv);
exit(1);
}else{
wait(NULL);
}
return 0;
}
}
}
gets(argv) is expecting a char array, not a pointer to a array of char arrays.
change
char* argv[100];
to
char argv[100];
And then
strcat(progpath, argv[0]);
to
strcat(progpath, argv);
Note also that gets etc, is assuming you are not going to provide too many characters to fit in the array, so if the user enters a value that will be more than the 14 characters then progpath will overflow.
You are missing includes for fork, wait etc - likely to be
#include <sys/types.h>
#include <unistd.h>
After that, why for(int i=0; i<strlen(progpath); i++) and run the execvp each character of the progpath ? I assume you are meaning to have to the closing brackets before then.
for(int i=0; i<strlen(progpath); i++){
if(progpath[i]=='\n'){
progpath[i]='\0';
}
}
int pid= fork();
execva expects an array of char arrays, which is probally why you decided to use char *argv[] originally, but is not valid now - use one of the execl type functions instead.
Lastly, there is a chance that ls is a bashonly command - not a real command, so may not work anyway.
use
argv is declared as an array of unallocated strings and is used as an allocated string. As a first step you should remove the * in char* argv[100];.
Related
I am trying to build an echo-like command which I named code at xv6. The problem is that if:
Input:
$code Hello World
Output:
user space:Hello user space:World
While the correct output should be:
user space: Hello World
Can somebody help me?
int
main(int argc, char *argv[])
{
int i;
if(argc <= 1){
printf(1," %s user space:", argv[0]);
exit();
}
for(i = 1; i < argc; i++){
printf(1, " print in user space:%s",argv[i]);
}
printf(1, "\n");
exit();
}
program name is also passed as argument to your main function so you have three(as hello and world are considered separate arguments) arguments here .there is several issues in your code ,as you have two arguments first if statement will be false ,and you are not printing correctly in for loop ,following might be helpful :
int main(int argc, char *argv[])
{
int i;
printf("number of arguments : %d ",argc);
//argv array index count from 0
for(i = 0; i < argc; i++){
printf("argument number %d : %s",i,argv[i]); // issue in your code
}
printf("\n");//issue in your code
exit(0);
}
It's possible to use the write command so that you can output to stdout. Between each command line argument an additional space needs to be written.
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
int i;
write(1, "user space: ", 12);
for (i = 1; i < argc; i++) {
size_t len = strlen(argv[i]);
write(1, argv[i], len);
write(1, " ", 1);
}
write(1, "\n", 1);
return 0;
}
The problematic part in your code is here:
for(i = 1; i < argc; i++){
printf(1, " print in user space:%s",argv[i]); //" print in user space:" is printed
//for each iteration
}
As others pointed out, you should rather do:
printf(" print in user space:\n");
for(i = 1; i < argc; i++){
printf("%s",argv[i]);
}
That said, a few more remarks for you:
I never seen a printf that takes an integer as first argument, I assume that the first parameter is not important/is a typo
I am not an expert unix developer, but you can just use "return 0" (or "return 1") to quit your program. You will also be able to return an error code this way, as most unix programs do
If you decide to use printf, be aware that it comes with a nasty security exploit which can expose the caller's stack to a malicious user. Make sure your input is well formed or use a different function if possible
I'm trying to create a simple shell program which execute the program specified in input. There are two main function: scanner() (use strtok to split the input in token) and execute() (fork the process and execute the program).
Unfortunately it doesn't work... I've tried to print string[0] at the end of scanner() and at the beginning of execute(). The first time the output is correct but the second time string[] seems to be modified in a sequence of random numbers so execvp() doesn't work...
I really can't figure out why the values of string[] changes, probably is a very stupid error but I can't see it. I really need your help! Thanks in advice.
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define DIM 256
int scanner(char*[]);
int execute(char*[]);
int main()
{
char* string[DIM];
scanner(string);
execute(string);
}
/* scan: read the input in token*/
int scanner(char* string[])
{
char input[1024];
char delimit[]=" \t\r\n\v\f";
int i = 0;
if(fgets(input, sizeof input, stdin)) {
string[i] = strtok(input, delimit);
while(string[i]!=NULL){
i++;
string[i]=strtok(NULL,delimit);
}
return 0;
}
return 1;
}
/* execute: execute the command*/
int execute(char* string[])
{
int pid;
printf("%s\n", string[0]);
switch(pid = fork()){
case -1:
return 1;
case 0:
execvp(string[0], string);
return 1;
default:
wait((int*)0);
return 0;
}
}
The string variable input in scanner is a local variable, with storage class "auto". That means that when that function returns, that variable disappears, and the memory it occupied can be re-used for other things. That is unfortunate, since strtok returns pointers into that string variable.
Here is the general problem:
The program must fork() and wait() for the child to finish.
The child will exec() another program whose name is INPUT by the user.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(void) {
int status;
char input[BUFSIZ];
printf(">");
scanf("%s",input);
char *args[] = {"./lab1"};
pid_t pid = fork();
if(pid==0){
execvp(args[0],args);
}else if(pid<0){
perror("Fork fail");
}else{
wait(&status);
printf("My Child Information is: %d\n", pid);
}
return 0;
}
My problem is getting the user to input a program name to run (at the ">" prompt) and getting that input into execvp (or another exec() function if anyone has any ideas)
I'm going to hold off lambasting you for using scanf("%s") for now, though you should be aware it's really not robust code.
Your basic task here is going to be taking a character array entered by the user and somehow turning that into an array of character pointers suitable for passing to execvp.
You can use strtok to tokenise the input string into tokens separated by spaces, and malloc/realloc to ensure you have enough elements in an array to store the strings.
Alternatively, since you already have a potential buffer overflow issue, it may be good enough to just use a fixed size array.
For example, the following program shows one way of doing this, it uses a fixed string echo my hovercraft is full of eels and tokenises it to be suitable for execution:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static char *myStrDup (char *str) {
char *other = malloc (strlen (str) + 1);
if (other != NULL)
strcpy (other, str);
return other;
}
int main (void) {
char inBuf[] = "echo my hovercraft is full of eels";
char *argv[100];
int argc = 0;
char *str = strtok (inBuf, " ");
while (str != NULL) {
argv[argc++] = myStrDup (str);
str = strtok (NULL, " ");
}
argv[argc] = NULL;
for (int i = 0; i < argc; i++)
printf ("Arg #%d = '%s'\n", i, argv[i]);
putchar ('\n');
execvp (argv[0], argv);
return 0;
}
Then it outputs the tokenised arguments and executes it:
Arg #0 = 'echo'
Arg #1 = 'my'
Arg #2 = 'hovercraft'
Arg #3 = 'is'
Arg #4 = 'full'
Arg #5 = 'of'
Arg #6 = 'eels'
my hovercraft is full of eels
I have this in my parent.c file
int main()
{
int n = 6;
int pid;
int status;
char* command = "./child";
for (i=1; i<=n; i++){
if((pid = fork()) == 0) {
execvp(command, NULL);
}
wait(&status);
}
My child.c file looks like this
int main(int argc, char *argv[]){
char *processnum = argv[0];
printf("This is the child %s\n", processnum);
return 0;
}
I basically just ran
gcc -o parent parent.c
gcc -o child child.c
./parent
This prints outs "This is the child (null)" 6 times, which is what I expect. But I want to be able to pass a parameter as I run child, in this case the process number.
So I changed my parent.c to look like this
for (i=1; i<=n; i++){
if(i == 1){
char* args = "1";
}
if(i == 2){
char* args = "2";
}
if(i == 3){
char* args = "3";
}
if(i == 4){
char* args = "4";
}
if(i == 5){
char* args = "5";
}
if(i == 6){
char* args = "6";
}
if((pid = fork()) == 0) {
execvp(command, args);
}
wait(&status);
}
What I thought would happen is that my program would print "This is the child 1", "This is the child 2" etc...
However, what actually happened is that the program seemed to run parent.c numerous times (I put a print statement at the start of parent.c and the output printed that statement like 20 times) instead of child.c
Can anyone explain why this is happening? Is there another way I can pass a parameter to child.c?
Thanks
here is the critical excerpt from the man page for 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.
There are several issues with your program as it stands. First, as described by others, the second argument to execvp is a char ** type ... it ends up as the argv in the exec'ed program.
Second, the
if(i == 1){
char* args = "1";
}
...
code sets a variable args whose scope ends at the next line. You must declare the args in a scope in which you want to use it.
Third, converting a number to a string like this is very limited and tedious! You would do better using the standard C function sprintf (or even better snprintf).
Here is an updated parent.c:
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
int n = 6;
int pid;
int status;
char* command = "./child";
char *child_args[2];
char number_buff[32]; // more than big enough to hold any number!
child_args[1] = NULL;
for (int i=1; i<=n; i++){
sprintf(number_buff, "%d", i);
child_args[0] = number_buff;
if((pid = fork()) == 0) {
execvp(command, child_args);
}
wait(&status);
}
return 0;
}
Can anyone explain why this is happening? Is there another way I can pass a parameter to child.c?
You should be passing a char* array to execvp, not a char*. As mentioned in the comments, the last element of the array should be a null pointer. The following should work:
char* args[] = {"1\0", NULL};
execvp(command, args);
I'm guessing that execvp is failing since it can't dereference args as a char**, and thus the forked process is continuing the loop as a parent. You should be checking the return value of execvp to see that the function call worked.
Also, command and args should be null-terminated. I suggest using the function itoa to convert an int to a c string.
I have a task to write a c program that can search for a directory in all the directories listed in $PATH using fork and exec. My question is how do i get the paths from $PATH in a manner that i can then use it in my code with execl
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char* argv[]) {
int pid = 0;
for(int i = 0; i < argc; i++) {
if (pid = fork() != 0){
printf("Arg%d: %c\n", i, *argv[i]); // replace with exec ls -l <dir>/<arg>
return 0;
}
}
return 0;
}
You can get the PATH environment variable using getenv() (man 3 getenv). Copy the string into a char* and then split it with strtok() (man 3 strtok) using ':' as delimiter. You should copy the original string into a new char* because the pointer you get from getenv() actually points inside the environment and strtok() will modify the argument you pass to it. You can then fork in a loop for each substring.