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
Related
I'm trying to build a version of a linux shell wherein, I take in the commands from the user and execute the commands using execv() system call.
I'm using strtok_r to tokenize the commands and pass the arguments to execv(). For example, when the user types in the command "ls -l", I first remove the leading/trailing white spaces and then tokenize the command into two tokens 1) ls, 2) -l. I concatenate the first token with a char path[10] in which I've already copied "/bin/" path. So, after concatenation, path becomes "/bin/ls". However, the execv() call fails. But, when I directly pass "/bin/ls" (instead of passing it as a variable) as the argument in execv(), it works. Code is shown below:
Please let me know where I've committed the mistake.
`
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
void removeTrailingLeadingSpaces(char* stringInput);
int main ()
{
printf("dash> ");
char* input;
size_t characters;
size_t bufsize = 32;
input = (char *)malloc(bufsize*sizeof(char));
characters = getline(&input,&bufsize,stdin);
removeTrailingLeadingSpaces(input);
char *args[2];
char* last;
char* token;
token = strtok_r(input," ",&last);
printf("%s\n", token);
removeTrailingLeadingSpaces(token);
char path[10];
strcpy(path,"/bin/");
strcat(path,token);
args[0] = path;
printf("%s\n",args[0]);
args[1] = strtok_r(NULL," ",&last);
args[2] = NULL;
int i = access(args[0],X_OK);
printf("result is %d",i);
int j= execv(args[0],args);
printf("result2 is %d",j);
return 0;
}
void removeTrailingLeadingSpaces(char* input)
{
while(isspace((unsigned char)*input))
input++;
int i=strlen(input)-1;
while(i>0)
{
if(input[i] == ' '|| input[i] == '\n'|| input[i] =='\t')
i--;
else
break;
}
input[i+1] = '\0';
}
`
Working on a project for a class. We're supposed to write a C shell. I've found a bunch of good examples, but for the life of me, I can't get my version to generate any output.
It keeps printing the prompt, but nothing else.
I've followed along with some examples near-verbatim trying to fix this, but still nothing.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <sys/wait.h>
/******************************************
#brief Fork a child to execute the command using execvp. The parent should wait for the child to terminate
#param args Null terminated list of arguments (including program).
#return returns 1, to continue execution and 0 to terminate the MyShell prompt.
******************************************/
int execute(char **args)
{
pid_t pid;
int status;
if ((pid = fork()) < 0)
{
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0)
{
if (execvp(*args, args) < 0) {
printf("*** ERROR: exec failed\n");
exit(1);
}
}
else
{
while (wait(&status) != pid)
;
}
return 0;
}
/******************************************
#brief gets the input from the prompt and splits it into tokens. Prepares the arguments for execvp
#return returns char** args to be used by execvp
******************************************/
char** parse(void)
{
//Get the string and store it. Remove newline.
char strIn[255];
printf("MyShell>");
fgets(strIn, sizeof(strIn), stdin);
strIn[strlen(strIn)-1]='\0';
//Array, token, counters.
char *args[20];
char *tok;
int i = 0;
int count = 0;
//Allocate array.
for(i = 0; i < 20; i++)
{
args[i] = (char*)malloc(20 * sizeof(char));
}
//Fill in args[0]
tok = strtok(strIn, " ");
strcpy(args[count++], tok);
//Fill in array with tokens.
while (tok != NULL)
{
tok = strtok(NULL, " ");
if (tok == NULL)
break;
strcpy(args[count++], tok);
}
//Null terminate.
args[count] = NULL;
return args;
}
/******************************************
#brief Main function should run infinitely until terminated manually using CTRL+C or typing in the exit command
It should call the parse() and execute() functions
#param argc Argument count.
#param argv Argument vector.
#return status code
******************************************/
int main(int argc, char **argv)
{
bool run = true;
while(run)
{
char** argArr = parse();
execute(argArr);
}
return 0;
}
The output, regardless of what I do, is:
MyShell>
MyShell>
MyShell>
Can someone tell me where I went wrong?
parse() returns a pointer to the local array args. Since the lifetime of args ends when parse() returns, any attempt to use the return value of parse() is undefined behavior. You should allocate that array with malloc() instead (and free it later!).
What happens in my test is that the compiler notices that the return value of parse() can't legally be used (and it gives a warning!! which you should read and pay attention to!!), so it just returns NULL instead. When the child dereferences this pointer as *args to get the first argument for execvp, it segfaults and dies without calling execvp(). You could detect this if you checked the status returned by wait(), but you don't. So it just looks as if the child didn't do anything.
Oh, bonus bug: when end-of-file occurs on stdin (e.g. if you hit Ctrl-D), the string returned by fgets() will be empty and strIn[strlen(strIn)-1]='\0'; will store a null byte out of bounds. You need to test the return value of fgets().
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];.
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.
Making a simple -type shell, using fork and execvp functions to run the commands from the stdin line.
However, things like ls work, but not ls -all -S.
It will execute ls, but nothing will be printed for ls -all
The only idea I can come up with is that there is a "\n" somewhere in the command, but I don't know how to get it out or even where it is....
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
//Libs ^^^^ Defs vvvvvvvv
#define comlen 4096 //Max command length
#define comarg 32 //Max argument length
int main(int argc, char *argv[])
{
char buff; //command buffer
char* comand[comlen];
int i;
do
{
i = 0;
printf("simsh: ");
char* whtspc = strtok (fgets(&buff, comlen, stdin)," "); //get input and tokenize
printf("[%lu] :: %s------------\nEND OF BUFF TEST\n", strlen(&buff), &buff);
while (whtspc != NULL)
{
comand[i]=(char*)malloc((sizeof(char)*strlen(whtspc))); //alloctie mem for commands
strncpy(comand[i], whtspc, strlen(whtspc)-1); //coppy comand token to array index i
whtspc = strtok (NULL, " "); //grab next token
i++; //incriment
/*trying to change new line character to NULL so that commands can be passed properly*/
// if (comand[strlen(comand[i]) - 1] == "\n")
// {
// comand[strlen(comand[i]) - 1] = '\0';
// }
//breka out incase index oversteps
if (i == 4096)
break;
}
//last entry in command should be null
comand[i] = NULL;
//fork to run in background
pid_t pid = fork();
if (pid == 0)
{
//testing and pass comands to execvp
printf("START OF COMAND TEST\n!!!!!!!!!%s!!!!!!!!!!!!!!!!\n %lu\nEND OF COMAND TEST\n\n",comand[1], strlen(comand[0]));
execvp(comand[0], &comand);
}
else
{
//parent wait on child.
waitpid(pid, &i, WUNTRACED | WCONTINUED);
}
}
while(1);
return 0;
}
Any help would be welcomed.
If it helps at all , here is the terminal output of the code::
simsh: ls
[3] :: ls
------------
END OF BUFF TEST
START OF COMAND TEST
!!!!!!!!!(null)!!!!!!!!!!!!!!!!
2
END OF COMAND TEST
chop_line.c chop_line.h list.c list.h Makefile Makefile~ One simsh1 simsh1.c simsh1.c~
simsh: ls -all
[2] :: ls------------
END OF BUFF TEST
START OF COMAND TEST
!!!!!!!!!-all!!!!!!!!!!!!!!!!
1
END OF COMAND TEST
simsh: echo
[5] :: echo
------------
END OF BUFF TEST
START OF COMAND TEST
!!!!!!!!!(null)!!!!!!!!!!!!!!!!
4
END OF COMAND TEST
simsh: echo all
[4] :: echo------------
END OF BUFF TEST
START OF COMAND TEST
!!!!!!!!!all!!!!!!!!!!!!!!!!
3
END OF COMAND TEST
simsh: echo echo
[4] :: echo------------
END OF BUFF TEST
START OF COMAND TEST
!!!!!!!!!echo!!!!!!!!!!!!!!!!
3
END OF COMAND TEST
The first argument to fgets should be a pointer to a buffer where the string is copied into. You are passing a pointer to a single char.
Second, execvp expects two arguments: a filename and a null-terminated list of command-line arguments which, by convention, starts with the filename itself.
I took the liberty to make some modifications to your code, both fixing the issues I pointed above and making it a little more readable.
Note that there's a memory leak in the code below (fix it :). There might be other issues that I didn't notice.
I implemented a shell a while ago; if you want to take a look, my GitHub URL is in my profile (BEWARE: ugly college homework code).
Hope it helps!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#define COMLEN 4096
#define COMARG_N 32
#define TRUE 1
int main(int argc, char *argv[])
{
char *token;
char *args[COMARG_N];
char *buff;
int i;
pid_t pid;
while(TRUE) {
printf("simsh: ");
buff = (char*) malloc(sizeof(char) * COMLEN);
fgets(buff, COMLEN, stdin);
if (buff[strlen(buff) - 1] == '\n')
buff[strlen(buff) - 1] = '\0';
i = 0;
token = strtok (buff, " ");
while (token != NULL && i < COMARG_N - 1) {
args[i] = token;
token = strtok (NULL, " ");
i++;
}
args[i] = NULL;
pid = fork();
if (pid == 0)
execvp(args[0], &args[0]);
else
waitpid(pid, &i, WUNTRACED | WCONTINUED);
free(buff);
}
return 0;
}
First of all, your fgets is reading to a single character buff. You should read into a buffer of characters. Second, fgets keeps the newline at the end of the read string, so you may want to remove it first, e.g.:
char buff[4096];
if (!fgets(buff, sizeof(buff), stdin)) {
// error or EOF
return 1;
}
int len = strlen(buff);
if (len > 0 && buff[len-1] == '\n') {
buff[--len] = '\0';
}
char *whtspc = strtok(buff, " ");
You must also replace all references to &buff with buff.
In addition to this, your malloc is also wrong, and allocates one character less than is required (strlen is without the terminating NUL):
if (!(comand[i] = malloc(strlen(whtspc)+1))) {
return 1; // out of memory
}
(void) strcpy(comand[i], whtspc);
Correspondingly your strncpy was copying one character less than required. This is what made your original code accidentally work for a single-word input because it had the effect of removing the trailing '\n' for you in that case, but in every other case it removed the last character of the word itself.
And the second argument to execvp should be just the comand (sic) array:
execvp(comand[0], comand);