Simple shell implementation with C in linux, fork causing infinite loop - c

I'm trying to implement a simple shell in C and my program is causing an infinite loop and creating a ton of new processes/printing Myshell> before the user has a chance to enter in any commands. I can't seem to find a way to prevent this and if anyone could help that would be great! (Didn't paste the #include headers at top
int main(int argc, char *argv[]){
char buffer[512];
int *status;
size_t nargs;
pid_t pid;
char delim[] = "\n";
while(1){
pid = fork();
if(pid){
printf("Myshell> ");
fgets(buffer, 512, stdin);
//parse(buffer, argv);
argv[0] = strtok(buffer, delim);
for(argc=1; argv[argc-1]; argc++){
argv[argc] = strtok(NULL, delim);
}
if(!strcmp(argv[0], "exit"))
exit(0);
printf("Waiting for child (%d)\n", pid);
pid = wait(status);
printf("Child (%d) finished\n", pid);
exit(0);
}else{
if(execvp(argv[0], argv)){
printf("error");
exit(1);
}else{
exit(0);
}
}
}
return 0;
}

Because you are reading command into buffer using fgets but not from command line argument So argv[argc] for argc > 1 is wrong - undefined behaviour.
When you don't pass any extra command line argument then argv[0] is your program name and argv[1] is NULL. indexing to argv[] for value more then 1 causes array out of index problem.
Instead of declare argv[] and argc as main function parameter declare within main as formal variable something like:
int argc;
char* argv[MAX]; // max number of argument can be pass to your shell
One more rectification in your code, change:
int *status;
as
int status;
and accordingly correct
pid = wait(status);
as
pid = wait(&status);

Related

Execvp linux: trying to make my shell work in C

I'm trying to make simple shell, but with a specific condition, I have to use the following structure:
typedef struct cmd_struct{
char cmd[80];
char args[10][80];
int nargs;
} cmd_type;
Inside cmd I will save de main command and arguments in args.
Then I read from a file, different commands, and save them into and array of cmd_type. My program or fake shell, ask for a number and should take it from this array.
My function that executes the command looks like:
void execCmd(cmd_type* cmds_arg, int idxCmd){
pid_t pid;
printf("Father: my pid is %d\n", getpid());
char* buff;
pid = fork();
if (pid == 0) {
printf("Child process: My pid is %d\n", getpid());
printf("-------------- Child doing exec: %s\n", cmds_arg[idxCmd].cmd);
execvp(cmds_arg[idxCmd].cmd,&cmds_arg[idxCmd].args);
_exit(2);
_exit(1);
}
printf("Father: Gonna wait for Child\n");
int status;
wait(&status);
printf("-------------- Father: Child finished\n");
// WIFEXITED, WEXITSTATUS Macro of the gnu lib POSIX standard to recover end status
if ( WIFEXITED(status) ) {
const int es = WEXITSTATUS(status);
printf("Father: Child Complete with exit status %d\n", es);
if(es == 1) printf("Father: Child didn't execute any command\n");
else if(es == 2) printf("Father: Child command was not found\n");
}
}
As you can see, when I'm calling the execvp() system call, I'm doing it wrong. First argument I think that it is right, second one it's totally wrong.
First of all, I have a conversion problem right there, and the second problem is that the array should contain "main command", "arg1", "arg2" ... and mine only has the arguments. Am I wrong?
Is there a way to add the "main command" using services like sscanf ()? And the most important part, do I have any chance to make it work this way?
With char args[10][80];, execvp(cmds_arg[idxCmd].cmd,&cmds_arg[idxCmd].args); won't work (it won't even compile), because execvp needs a const char* pointer to each argument, and your .args doesn't have any pointers.
Something like this may work:
const char *p[11]; /* Contains up to 10 pointers + trailing NULL. */
cmd_type *this_cmd = &cmds_arg[idxCmd];
for (int i = 0; i < this_cmd->nargs; ++i) {
p[i] = &this_cmd->args[i];
}
p[this_cmd->nargs] = NULL;
execvp(this_cmd->cmd, p);
In the same spirit of the answer from #pts, you can copy the parameters for execvp() in a dynamically allocated table:
void execCmd(cmd_type* cmds_arg, int idxCmd){
pid_t pid;
printf("Father: my pid is %d\n", getpid());
char* buff;
pid = fork();
if (pid == 0) {
int i;
char **args = (char **)malloc((1 + cmds_arg[idxCmd].nargs + 1) * sizeof(char *));
args[0] = cmds_arg[idxCmd].cmd;
for (i = 1; i < (cmds_arg[idxCmd].nargs + 1); i ++) {
args[i] = cmds_arg[idxCmd].args[i - 1];
}
args[i] = NULL;
printf("Child process: My pid is %d\n", getpid());
printf("-------------- Child doing exec: %s\n", cmds_arg[idxCmd].cmd);
execvp(cmds_arg[idxCmd].cmd, args);
_exit(2);
}
printf("Father: Gonna wait for Child\n");
int status;
wait(&status);
printf("-------------- Father: Child finished\n");
// WIFEXITED, WEXITSTATUS Macro of the gnu lib POSIX standard to recover end status
if ( WIFEXITED(status) ) {
const int es = WEXITSTATUS(status);
printf("Father: Child Complete with exit status %d\n", es);
if(es == 1) printf("Father: Child didn't execute any command\n");
else if(es == 2) printf("Father: Child command was not found\n");
}
}

c programming using execvp

I'm working on a side project where I want to spawn 5 children processes then execvp into a second program called isEven which will tell if the number passed to it is even or not. However, the execvp call keeps failing as I ran it in gdb and it doesn't execute.
Args and load(args) are both called in another function.
Thanks in advance!!!
//Code to load binary
void load(char* args[])
{
execvp("./isEven", args);
printf("ERROR\n");
}
//args being passed in
char *args[] = {"./isEven", number, NULL};
load(args);
Here is a smaller program I wrote which does the same thing I'm trying to do right now. Gdb says I am getting a segmentation fault.
int main(int argc, char *argv[])
{
int returnCode = fork();
if(returnCode == 0)
{
char *args[] = {"./isEven", 12345, NULL};
execvp("./isEven", args);
printf(errno);
printf("ERROR: execvp failed\n");
exit(-1);
}
else if(returnCode < 0)
{
fprintf(stderr, "fork failed\n");
}
else
{
//i am the parent
printf("I am the parent");
}
return 1;
}
The main problem is with this line:
char *args[] = {"./isEven", 12345, NULL};
The args passed to execvp must be an array of char*. Having 12345 as an argument makes it interpret it as address and it's unlikely to be a valid address. In any case, that's not what you wanted to pass. So do:
char *args[] = {"./isEven", "12345", NULL};
You need to convert number into a string. For example:
int number = 12345;
char num_str[256];
snprintf(num_str, sizeof num_str, "%d", number);
char *args[] = {"./isEven", num_str, NULL};
execvp("./isEven", args);
perror("exec");
In your modified example, you are printing errno as:
printf(errno);
This will fail because printf expects a string as its first argument. You can use perror() to print the error as shown above.

how to store execvp result in a char array as a string

If a user types "ls" execvp displays the result of "ls" to the screen. I would like to store this in a char array as a string. Can anyone help me?
THanks in advance.
int main () {
char response[1000];
char *buffer[100];
int pid, status;
printf("Please enter the shell command: ");
scanf("%s",&response);
pid = fork();
if (pid < 0) {
printf("Unable to create child process, exiting.\n");
exit(0);
}
if (pid == 0) {
printf("I'm the child.\n");
*buffer = response;
execvp(*buffer,buffer);
printf("execvp failed\n");
}
else{
wait(&status);
exit(0);
}
}
popen() is more appropriate for your purpose than execvp() since you want to read the output from the exec'ed command (See the linked manual for an example).
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen() returns FILE * using which you can read the output returned by the command.

Getting pid trying to stop a program with C

Probably the core of this question has been asked a lot on this site.
I'mm working with pocketsphinx and I'm trying to play music each time I request it.
When I say "MUSIC" the program executes the music, my idea is that when I say "STOP" music should stop. I'm trying to get the PID the following way. I got this idea from this question
I though using popen I will get the PID, but isn't that way when it get to pid_t pid = strtoul(line, NULL, 10); it's returning me 0.
How I can get this PID and continue with the program running at the same time?
I'm using the same template that you will find on the pocketsphinx want to see it with modifications here: http://pastebin.com/Duu2nbCA
if(strcmp(word, "MUSIC") == 0)
{
FILE *fpipe;
char *command = (char *)"aplay BobMarley.wav";
char line[256];
if ( !(fpipe = (FILE*)popen(command,"r")) )
{ // If fpipe is NULL
perror("Problems with pipe");
exit(1);
}
fgets( line, sizeof line, fpipe);
pid_t pid = strtoul(line, NULL, 10);
printf("The id is %d\n", pid);
}
You can refer the below code to find the PID of a process. Execute with "root" permission
The argument to the executable will the name of the process for which the PID has to obtained
#define TMP_FILE "/tmp/pid"
int main(int argc, char** argv)
{
FILE *fpipe;
int pid = 0;
char command[50] = "pidof -s ";
if (argc != 2) {
printf("Invalid input\n");
return -1;
}
strcat(command, argv[1]);
strcat(command, " > "TMP_FILE);
system(command);
fpipe = fopen(TMP_FILE, "r");
fscanf(fpipe, "%d", &pid);
printf("The pid is %d\n", pid);
fclose(fpipe);
return 0;
}
Based on the sizeof the process name vary the length of the command.
Implementation 2
int main( int argc, char *argv[] )
{
FILE *fp;
char path[10];
fp = popen("/sbin/pidof -s YOUR_APP", "r");
if (fp == NULL) {
printf("Failed to run command\n" );
exit(1);
}
/* Read the output a line at a time - output it. */
while (fgets(path, sizeof(path), fp) != NULL) {
printf("%s", path);
}
pclose(fp);
return 0;
}
Change YOUR_APP with your application name.
Tested with other commands.

Shell program in C has odd fork behaviour

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".

Resources