Here is the following code I am current having issues with:
#include <stdio.h>
#include <unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#define MAX_LINE 80
int main(void)
{
char *args[MAX_LINE/2+1];
int background= 0;//integer that acts a boolean for if & appears
int should_run = 1;
int status;
while(should_run)//Just to keep the interface going until the user chooses to stop
{
printf("osh>");//prompt
fflush(stdout);
int i = 0;
while(getchar() != '\n')//Use scanf until a new line is found
{
scanf("%s", &args[i]);
if(strcmp(&args[i], "exit") == 0)//temporary exit protocal
{
printf("Exiting now...");
return 0;
}
if(strcmp(&args[i], "&") == 0)//If we find a & in our input then we changed background to 1 or true
background = 1;
printf("Args[%i] = %s\n", i, &args[i]);//Tester
i++;
}
printf("Background = %i\n",background);//Test
pid_t pid= fork();// Create new child process
printf("process %i created\n", pid);//Test
if(pid < 0)//fork() failed
{
printf("Fork Failed.\n");
return 1;
}
else if(pid == 0)//Child process id
{
printf("Child process started %s command\n", &args[0]);
if(execvp(args[0], args) < 0)//change the current child process to execute the input given by the user
//with args[0] being the command and args being the parameters(ls -l is an example).
{
printf("Command failed\n");
}
return 0;
}
else//Parent Process
{
if(background == 1)//If the user typed in a & then the parent will wait for a change in state from the child, if there is no &
//then we will just finish the parent process
{
printf("Parent process waiting on child\n");
wait(NULL);
}
}
}
return 0;
I have one major issue and one minor issue right now. The major issue is that I have a printf method before execvp starts that says "Child Process started" and I get this line to print, but then nothing else happens. No interrupts are thrown, the program just seems to be frozen on my execvp command.
My minor issue is that when my program starts a prompt "osh>" before asking for input. Now if, for example, I would type in "osh>ls -l" then I get args[0] = s, args1 = -l. Now if I put "osh> ls -l" in that exact format I get args[0] = ls, args1 = -l. Is that a part of scanf() that I am not using properly here to make sure I get ever character after "osh>" and between blank spaces as strings?
EDIT:
here is my output for user input "ls -l"
The problem you're having with the missing character is because getchar() is consuming the first character of your input before scanf gets to take a stab at it. You probably want to do something like:
while (scanf("%s", &buffer) > 0)
{
strcpy(args[i], buffer);
/* then do stuff with args[i] */
}
Related
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));
}
}
My program is a rudimental little shell.
It allow you to run programs in PATH as ls, cd..also with arguments.
To run the program type from terminal "./myshell2" then it starts and you can insert how many commands you want.
It starts a child process, runs execvp,it returns and restarts so you can type a new command.
When typed "Q" or "q" all the entire program should terminates.
The problem is that I don't know how to stop it,the code is below.
My idea is, when typed "Q" or "q", to kill the child process created and send a signal to comunicate its bad termination(of child process).
So the final status(from parent) 'll be not 1 and the function returns.
I commented some parts of the code hoping that it's easier to understand.
It works the problem is that to stop it I need of ctrl C.
I would like to say to child process that he must ends with a non-zero value.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <signal.h>
int main(int argc, char * argv[]) {
while(1)
{
pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(EXIT_FAILURE);
}
if (pid == 0) { // child process
printf("type the command to start (and arguments if required) \n"
"Q to quit\n");
char *dest[10]; // allow you to insert
char line[4096];//commands from terminal
if (fgets(line,sizeof(line),stdin)==0) return 1;
int i;
line[strcspn(line, "\n")] = '\0';
char *st = line;
for (i=0; i< 10 && (dest[i]=strsep(&st," "))!=NULL;i++)
continue;//now you typed the command
if ( ( memcmp(dest[0],"Q",1)==0 ) // if Q or q the program
|| (memcmp(dest[0],"q",1)==0) ) //must end
{
printf("got it!\n");
if (kill(getpid(),SIGSEGV)==-1) printf("kill error\n");
//in theory the process should terminates with bad status
// and the value of the variable "status" 'll be not 0
// I think that the problem is in this part of the code
}
if( strcmp(dest[0]," ")!=0 )
{
int res = execvp(dest[0], dest);
}
else
{ int res= execvp(dest[1],dest+1);}
perror("execvp error");
exit(EXIT_FAILURE);
}
int status;
pid_t child = wait(&status);
if (child == -1) {
perror("wait error");
exit(EXIT_FAILURE);
}
if (status==1)
break; //so it can exit from the loop that creates new process
setenv("WAIT","TRUE",0); //dont' worry about
//perror("setenv error\n");
if (memcmp("TRUE",getenv("WAIT"),4) == 0 ) //these 6 lines
printf("WAIT=TRUE\n");
else if(memcmp("FALSE",getenv("WAIT"),4) == 0 )
printf("WAIT=FALSE\n");
printf("end current process (status=%d, child=%d)\n", WEXITSTATUS(status), son);
}
return EXIT_SUCCESS;
}
You're printing out WEXITSTATUS() for all cases, but that isn't right. You need to check if the status returned by wait is an exit status or not using WIFEXITED(). If it's non-zero then the child exited normally. Otherwise, you can use WIFSIGNALED() to see if the child was terminated and you'll get the signal from WTERMSIG()
if(WIFEXITED(status))
{
printf("end current process (status=%d, child=%d)\n", WEXITSTATUS(status), son);
}
else if(WIFSIGNALED(status))
{
printf("end current process (signal=%d, child=%d)\n", WTERMSIG(status), son);
}
You really should have the parent process handle the inputting of the command and leave the child process to run it though.
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.
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.
I've been working on creating my own Unix Shell in C to get practice with its interworkings...I'm having some issues getting my process to run in the background while allowing my shell to continue taking user input. If you could take the time to dissect what I've got below it would be much appreciated!
My variables are below, just incase that helps understand things more...
#define TRUE 1
static char user_input = '\0';
static char *cmd_argv[5]; // array of strings of command
static int cmd_argc = 0; // # words of command
static char buffer[50]; // input line buffer
static int buffer_characters = 0;
int jobs_list_size = 0;
/* int pid; */
int status;
int jobs_list[50];
Here is my main function.
int main(int argc, char **argv)
{
printf("[MYSHELL] $ ");
while (TRUE) {
user_input = getchar();
switch (user_input) {
case EOF:
exit(-1);
case '\n':
printf("[MYSHELL] $ ");
break;
default:
// parse input into cmd_argv - store # commands in cmd_argc
parse_input();
//check for zombie processes
check_zombies();
if(handle_commands() == 0)
create_process();
printf("\n[MYSHELL] $ ");
}
}
printf("\n[MYSHELL] $ ");
return 0;
}
Parse Input...I know, I can't get readline to work on this box :(
If provided the & operator, create the job in the background... (see below)
void parse_input()
{
// clears command line
while (cmd_argc != 0) {
cmd_argv[cmd_argc] = NULL;
cmd_argc--;
}
buffer_characters = 0;
// get command line input
while ((user_input != '\n') && (buffer_characters < 50)) {
buffer[buffer_characters++] = user_input;
user_input = getchar();
}
// clear buffer
buffer[buffer_characters] = 0x00;
// populate cmd_argv - array of commands
char *buffer_pointer;
buffer_pointer = strtok(buffer, " ");
while (buffer_pointer != NULL) {
cmd_argv[cmd_argc] = buffer_pointer;
buffer_pointer = strtok(NULL, " ");
//check for background process execution
if(strcmp(cmd_argv[cmd_argc], "&")==0){
printf("Started job %d\n", getpid());
make_background_job();
}
cmd_argc++;
}
}
Make background job. Closes child process STDIN, opens new STDIN, and executes.
void make_background_job()
{
int pid;
pid = fork();
fclose(stdin); // close child's stdin
fopen("/dev/null", "r"); // open a new stdin that is always empty
fprintf(stderr, "Child pid = %d\n", getpid());
//add pid to jobs list
jobs_list[jobs_list_size] = getpid();
/* printf("jobs list %d", *jobs_list[jobs_list_size]); */
jobs_list_size++;
execvp(*cmd_argv,cmd_argv);
// this should never be reached, unless there is an error
fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);
}
The meat of my job control. Fork spawns child, returns 0 for child and PID for parent.
void create_process()
{
pid_t pid;
pid = fork();
status = 0;
switch(pid){
case -1:
perror("[MYSHELL ] $ (fork)");
exit(EXIT_FAILURE);
case 0:
make_background_job();
printf("\n\n----Just made background job in case 0 of create_process----\n\n");
break;
default:
printf("\n\n----Default case of create_process----\n\n");
// parent process, waiting on child...
waitpid(pid, &status, 0);
if (status != 0)
fprintf (stderr, "error: %s exited with status code %d\n", cmd_argv[0], status);
else
break;
}
}
My problem is when I execute a job in the background, its executing the command twice, and exiting out of the shell. (It functions correctly otherwise if no background process is enabled). Where am I getting confused? I think it may have to do with issues regarding my PID's, as I'm not populating the list correctly either in 'make_background_job'
Here is my output, the example.sh just throws out helloWorld:
[MYSHELL] $ ./example.sh &
Started job 15479
Child pid = 15479
Child pid = 15481
Hello World
Hello World
What seems to happen is
in main() the prompt is displayed, expecting a command
when a command is input, parse_input() is called
it builds the commands array until it finds & where it calls make_background_jobs()
that function forks quickly, and executes in parallel, in two processes, execvp()
execvp() replaces each of the two processes to execute the command
thus two "Hello world" appear.
The problem is in make_background_jobs() where, I think, the expected behavior was that only one of the two processes should execute the command, and the other one (father) returns, to keep the program active.
This can be solved by modifying that function, making the father process return:
void make_background_job()
{
int pid;
pid = fork();
if (pid) return; // The father process returns to keep program active
...
edit
I gave it a try, removing the unnecessary
void make_background_job()
{
int pid;
pid = fork();
if ( ! pid)
{
fclose(stdin); // close child's stdin
fopen("/dev/null", "r"); // open a new stdin that is always empty
fprintf(stderr, "Child Job pid = %d\n", getpid());
//add pid to jobs list
jobs_list[jobs_list_size] = getpid();
/* printf("jobs list %d", *jobs_list[jobs_list_size]); */
jobs_list_size++;
execvp(*cmd_argv,cmd_argv);
// this should never be reached, unless there is an error
fprintf (stderr, "unknown command: %s\n", cmd_argv[0]);
exit(1);
}
waitpid(pid, &status, 0);
}
The background job is created in another process. The father waits for the job to complete.
void parse_input()
{
// clears command line
while (cmd_argc != 0) {
cmd_argv[cmd_argc] = NULL;
cmd_argc--;
}
buffer_characters = 0;
// get command line input
while ((user_input != '\n') && (buffer_characters < 50)) {
buffer[buffer_characters++] = user_input;
user_input = getchar();
}
// clear buffer
buffer[buffer_characters] = 0x00;
// populate cmd_argv - array of commands
char *buffer_pointer;
buffer_pointer = strtok(buffer, " ");
int ok = 0;
while (buffer_pointer != NULL) {
cmd_argv[cmd_argc] = buffer_pointer;
buffer_pointer = strtok(NULL, " ");
//check for background process execution
if(strcmp(cmd_argv[cmd_argc], "&")==0){
ok = 1;
break;
}
cmd_argc++;
}
if (!ok) cmd_argv[cmd_argc = 0] = NULL; // If no & found, reset commands
}
Only parses input.
Below a new handle_commands() that return 0 if there is a command to play, and the main follows.
int handle_commands() { return cmd_argc > 0 ? 0:1; }
int main(int argc, char **argv)
{
printf("[MYSHELL] $ ");
while (TRUE) {
user_input = getchar();
switch (user_input) {
case EOF:
exit(-1);
case '\n':
printf("[MYSHELL] $ ");
break;
default:
// parse input into cmd_argv - store # commands in cmd_argc
parse_input();
//check for zombie processes
check_zombies();
if(handle_commands() == 0)
make_background_job(); // Call directly the bg job
printf("\n[MYSHELL] $ ");
}
}
printf("\n[MYSHELL] $ ");
return 0;
}
The main() calls directly make_background_job().
There is only one fork() in make_background_job. create_process() has been removed.