How to add '&' functionality to run program in background in dummy shell? - c

I know I have to fork() but what do I do after that? Also I know I have to skip a wait() call at some point but how do I implement that? When I type '&' after a command it says "cannot access &: No such file or directory". The dummy shell should return a prompt for users immediately for more command input if the & is typed after a program and its parameters (Because the program will be running in the background). How do I accomplish this?
Here is the code I'm pretty sure needs to be changed:
pid = fork();
if (pid){
wait(&retStatus);
}
else {
if( execvp(args[0], args)) {
fprintf(stderr, "%s\n", strerror(errno));
exit(127);
}
}
Here is all of my code:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define BUFFER_SIZE 1<<16
#define ARRAY_SIZE 1<<16
static void parseCmdArgs(char *buffer, char** cmdArgs,
size_t cmdArgsSize, size_t *nargs)
{
char *bufCmdArgs[cmdArgsSize];
char **temp;
char *buf;
size_t n, p;
cmdArgs[0] = buf = bufCmdArgs[0] = buffer;
for(temp=bufCmdArgs; (*temp=strsep(&buf, " \n\t")) != NULL ;){
if ((*temp != '\0') && (++temp >= &bufCmdArgs[cmdArgsSize]))
break;
}
for (p=n=0; bufCmdArgs[n]!=NULL; n++){
if(strlen(bufCmdArgs[n])>0)
cmdArgs[p++]=bufCmdArgs[n];
}
*nargs=p;
cmdArgs[p]=NULL;
}
//int main(int argc, char *argv[], char *envp[]){
int main(void)
{
char buffer[BUFFER_SIZE];
char *args[ARRAY_SIZE];
int retStatus;
size_t nargs;
pid_t pid;
printf("$dummyshell\n");
while(1){
printf("> ");
fgets(buffer, BUFFER_SIZE, stdin);
parseCmdArgs(buffer, args, ARRAY_SIZE, &nargs);
if (nargs==0)
continue;
if (!strcmp(args[0], "help"))
{
printf("cat cd (absolute path references only\n");
printf("exit\n");
printf("help history\n");
printf("jobs kill\n");
printf("ls more\n");
printf("ps pwd\n");
continue;
}
if (!strcmp(args[0], "exit" ))
exit(0);
pid = fork();
if (pid){
wait(&retStatus);
}
else {
if( execvp(args[0], args)) {
fprintf(stderr, "%s\n", strerror(errno));
exit(127);
}
}
/* pid = fork();
if (pid == 0)
setpgrp();
else if (pid)
pid = wait(&retStatus);
else {
if (execvp(args[0], args)){
fprintf(stderr, "%s\n", strerror(errno));
exit(127);
}
}*/
}
return 0;
}

Related

Custom shell is not recognizing the last command passed to it

I am trying to implement a custom Unix shell, however, the last command being passed to it is never recognized. When I run a "ls" nothing happens but if i do "ls ls" it acts as if only one ls has been passed, or if i do "ls -o" it only does "ls" but if i do "ls -o -o" it does "ls -o", it should function similarly to a standard Unix shell.
#include "apue.h"
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int parse(char* buf, char **cmds);
static void sig_int(int);
int
main(void)
{
char *path;
path = getenv("PATH");
char buf[MAXLINE];
char *envp[] = {path, NULL};
pid_t pid;
int status;
char *command[MAXLINE];
char *input;
if (signal(SIGINT, sig_int) == SIG_ERR)
err_sys("signal error");
printf("%% ");
while (fgets(buf, MAXLINE, stdin) != NULL) {
parse(buf, command);
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
input = command[0];
execvpe(input, command, envp);
err_ret("couldn't execute: %s", buf);
exit(127);
}
/* parent */
if ((pid = waitpid(pid, &status, 0)) < 0)
err_sys("waitpid error");
printf("%% ");
}
exit(0);
}
/*parses input into something runable*/
int parse(char* buf, char **cmds)
{
char *sep;
int count;
count = 0;
while((sep = strchr(buf, ' ')))
{
cmds[count]= buf;
*sep = '\0';
buf = sep +1;
count++;
while (*buf && (*buf == ' ')) /* Ignore spaces */
buf++;
}
cmds[count] = NULL;
if(count == 0)
return 1;
return 0;
}

SHELL with PIPE communication BETWEEN 2 child processes with the same parent process

I want to create a UNIX based shell that supports commands with 1 pipe.
For example, the command ls | wc
I have wrote the following code but when I try an input like the one above, the program actes weird and can't figure out where the fault is.
To be more specific, it appears to fork() properly, then exec() with the proper parameters but only the command before the pipe (ls) exits properly. The command after the pipe (wc) never exits actually and as a result there is no output from the program.
I 've put some printf to help me figure out where is the fault in the code.
Functions fetch_input and string_tokenizer are tested and work fine.
The fault must be somewhere below the point pointed in the code.
Any suggestion to help me find what is the fault would be appreciated.
Thanks in advance.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define ANSI_COLOR_GREEN "\x1b[32m"
#define ANSI_COLOR_CYAN "\x1b[36m"
#define ANSI_COLOR_RESET "\x1b[0m"
char * fetch_input(char * buffer);
char * trim(char * string);
char ** string_tokenizer(char * string, char c);
int main(int arc, char * argv[]){
char *input, *init_input, **commands, **com1, **com2;
pid_t fork_pid_1, fork_pid_2, ret_pid;
int stat,i, fd[2];
init_input = (char *)malloc(sizeof(char)*256);
input = init_input;
input = fetch_input(input);
while(strcmp(input,"exit")!=0){
commands = string_tokenizer(input,'|');
com1 = string_tokenizer(commands[0],' ');
if(commands[1]==0){
fork_pid_1 = fork();
if(fork_pid_1<0){
printf("Fork Error!\n");
_exit(1);
}
else if(fork_pid_1==0){
execvp(com1[0],com1);
printf("Exec Error!\n");
_exit(1);
}
else{
ret_pid=wait(&stat);
}
}
else{ //the fault is probably somewhere below that point
if(pipe(fd)<0){ _exit(1);}
fork_pid_1 = fork();
if(fork_pid_1<0){
printf("Fork Error!\n");
_exit(1);
}
else if(fork_pid_1==0){
printf("First command, ready to exec...(pid:%d)\n",getpid());
close(fd[0]);
dup2(fd[1],1);
close(fd[1]);
execvp(com1[0],com1);
printf("Exec Error!\n");
_exit(1);
}
else{
com2 = string_tokenizer(commands[1],' ');
fork_pid_2=fork();
if(fork_pid_2<0){
printf("Fork Error!\n");
_exit(1);
}
else if(fork_pid_2==0){
printf("Second command, ready to exec...(pid:%d)\n",getpid());
close(fd[1]);
dup2(fd[0],0);
close(fd[0]);
execvp(com2[0],com2);
printf("Exec Error!\n");
_exit(1);
}
else{
printf("Now we 're in the parent process...(p_pid:%d)\n",getpid());
while( (ret_pid=waitpid(-1,&stat,0)) >0 ){
printf("Child process (%d) exited with status:%d\n",ret_pid,stat);
}
}
}
}
input = init_input;
input = fetch_input(input);
}
return 0;
}
char * fetch_input(char * buffer){
int i, sum;
do{
printf(ANSI_COLOR_CYAN "$" ANSI_COLOR_RESET);
fflush(stdin);
fgets(buffer,256,stdin);
if(buffer[strlen(buffer)-1]=='\n'){
buffer[strlen(buffer)-1]='\0';
}
sum=0;
for(i=0;i<strlen(buffer);i++){
if(buffer[i]==' '){sum++;}
}
}while(strlen(buffer)==sum);
buffer = trim(buffer);
return buffer;
}
char * trim(char * string){
int i;
i=strlen(string);
while(string[i-1]==' '){
i--;
}
*(string+i)='\0';
while(isspace(*string)){string++;}
return string;
}
char ** string_tokenizer(char * string, char c){
int j=0,k,i,done,found,first,last;
char ** ret, *str;
str=string;
if( (str==0) || (strlen(str)==0) ) return NULL;
ret = (char **)malloc(sizeof(char*));
do{
done=0;
found=0;
i=0;
first=-1;
last=-1;
while( (str[i]!='\0') && (done==0) ){
if( (str[i]==c) && (found==0) ){
i++;
}
else if( (str[i]!=c) && (found==0) ){
found=1;
first=i;
i++;
}
else if( (str[i]!=c) && (found!=0) ){
i++;
}
else if( (str[i]==c) && (found!=0) ){
done=1;
last=i;
}
}
if(done!=0){
*(ret+j) = (char *)malloc(sizeof(char)*(last-first+1));
for(k=first;k<last;k++){
*(*(ret+j)+(k-first)) = str[k];
}
*(*(ret+j)+(k-first)) = '\0';
*(ret+j) = trim(*(ret+j));
j++;
str=str+last;
}
}while(done!=0);
if( (done==0) && (found==0) ){
*(ret+j)=NULL;
}
else if( (done==0) && (found!=0) ){
*(ret+j) = (char *)malloc(sizeof(char)*(i-first+1));
for(k=first;k<i;k++){
*(*(ret+j)+(k-first)) = str[k];
}
*(*(ret+j)+(k-first)) = '\0';
*(ret+j)=trim(*(ret+j));
*(ret+j+1) = NULL;
}
return ret;
}

How to implement a kill command in my own shell

I'm trying to create my shell in C, but I don't know how to implement the kill function.
I can't use the command kill(), I want to create my own function by insert for exemple " k process_pid ".
Here my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#define maxarg 20
void CtrlC(int sig);
void execute(char *argv[]);
char getargs(int *argcp, char *argv[], int max);
int main () {
signal(SIGINT,CtrlC);
char *argv[maxarg+1];
int argc;
while (1) {
printf(">");
if(!getargs(&argc, argv, maxarg) || argc == 0) {
continue;
}
if(strncmp(argv[0], "exit", 4) == 0) {
printf("Program completed.\n");
exit(0);
}
execute(argv);
}
}
char getargs(int *argcp, char *argv[], int max){
static char cmd[100];
char *cmdp;
int i;
if(gets(cmd) == NULL)
exit(0);
cmdp = cmd;
for(i=0; i<=max; i++){
if((argv[i] = strtok(cmdp, " \t")) == NULL)
break;
cmdp = NULL;
}
if(i > max){
printf(">Too many arguments!\n");
return -1;
}
*argcp = i;
return(1);
}
void execute(char *argv[]) {
int i;
switch (fork()) {
case -1:
printf(">Error in the creation of the process.\n");
return;
case 0:
execvp(argv[0], argv);
printf(">Can't execute.\n");
perror(">execvp");
exit(1);
default:
if(wait(NULL) == -1)
perror(">Wait");
}
}
void CtrlC (int sig) {
signal(sig, SIG_IGN);
printf("\n>To exit digit 'exit'.\n>");
signal(SIGINT,CtrlC);
fflush(stdout);
}
Exactly what you did with the exit command, you just have to:
test for !strncmp(argv[0],"k",1)
then decode the pid with sscanf(argv[1],"%d",&pid)
then kill it with kill(pid,SIGTERM) (or any other signal you want)

How can I make my dummy shell run a program in the background?

I want the user to be able to type and input a program and its parameters but if the user types a "&" at the end, the dummyshell should run the program in the background. In this case, the dummyshell will return immediately a prompt to users (for further command inputs). I know I have to use wait() but I'm not sure how to use it in order to run a program in the background.
For example when program runs in foreground:
$ dummyshell
> HelloWorld
...
>
For example when program runs in background:
$ dummyshell
> HelloWorld &
>
This is the code I'm working with so far:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define BUFFER_SIZE 1<<16
#define ARRAY_SIZE 1<<16
static void parseCmdArgs(char *buffer, char** cmdArgs,
size_t cmdArgsSize, size_t *nargs)
{
char *bufCmdArgs[cmdArgsSize];
char **temp;
char *buf;
size_t n, p;
cmdArgs[0] = buf = bufCmdArgs[0] = buffer;
for(temp=bufCmdArgs; (*temp=strsep(&buf, " \n\t")) != NULL ;){
if ((*temp != '\0') && (++temp >= &bufCmdArgs[cmdArgsSize]))
break;
}
for (p=n=0; bufCmdArgs[n]!=NULL; n++){
if(strlen(bufCmdArgs[n])>0)
cmdArgs[p++]=bufCmdArgs[n];
}
*nargs=p;
cmdArgs[p]=NULL;
}
//int main(int argc, char *argv[], char *envp[]){
int main(void)
{
char buffer[BUFFER_SIZE];
char *args[ARRAY_SIZE];
int retStatus;
size_t nargs;
pid_t pid;
while(1){
printf("$dummyshell ");
fgets(buffer, BUFFER_SIZE, stdin);
parseCmdArgs(buffer, args, ARRAY_SIZE, &nargs);
if (nargs==0)
continue;
if (!strcmp(args[0], "help"))
{
printf("cat cd (absolute path references only\n");
printf("exit\n");
printf("help history\n");
printf("jobs kill\n");
printf("ls more\n");
printf("ps pwd\n");
continue;
}
if (!strcmp(args[0], "exit" ))
exit(0);
pid = fork();
if (pid){
pid = wait(&retStatus);
}
else {
if( execvp(args[0], args)) {
fprintf(stderr, "%s\n", strerror(errno));
exit(127);
}
}
}
return 0;
}

Handling CTRL-C in dummy shell

I'm writing a dummy shell that should not terminate when the user types ctrl-C but should just generate a new prompt line. Currently, my shell does not terminate when I type ctrl-C but it still does not print the new prompt line. Do you know why this is the case and how I can fix this?
My code is below:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#define BUFFER_SIZE 1<<16
#define ARRAY_SIZE 1<<16
void INThandler(int);
static void parseCmdArgs(char *buffer, char** cmdArgs,
size_t cmdArgsSize, size_t *nargs)
{
char *bufCmdArgs[cmdArgsSize];
char **temp;
char *buf;
size_t n, p;
cmdArgs[0] = buf = bufCmdArgs[0] = buffer;
for(temp=bufCmdArgs; (*temp=strsep(&buf, " \n\t")) != NULL ;){
if ((*temp != '\0') && (++temp >= &bufCmdArgs[cmdArgsSize]))
break;
}
for (p=n=0; bufCmdArgs[n]!=NULL; n++){
if(strlen(bufCmdArgs[n])>0)
cmdArgs[p++]=bufCmdArgs[n];
}
*nargs=p;
cmdArgs[p]=NULL;
}
void INThandler(int sig)
{
printf("\n> ");
signal(sig, SIG_IGN);
}
int main(void)
{
char buffer[BUFFER_SIZE];
char *args[ARRAY_SIZE];
int retStatus;
size_t nargs;
pid_t pid;
printf("$dummyshell\n");
signal(SIGINT, INThandler);
while(1){
printf("> ");
fgets(buffer, BUFFER_SIZE, stdin);
parseCmdArgs(buffer, args, ARRAY_SIZE, &nargs);
if (nargs==0)
continue;
if (!strcmp(args[0], "help"))
{
printf("cat cd (absolute path references only\n");
printf("exit\n");
printf("help history\n");
printf("jobs kill\n");
printf("ls more\n");
printf("ps pwd\n");
continue;
}
if (!strcmp(args[0], "exit" ))
exit(0);
pid = fork();
if (pid){
wait(&retStatus);
}
else {
if( execvp(args[0], args)) {
fprintf(stderr, "%s\n", strerror(errno));
exit(127);
}
}
/* pid = fork();
if (pid == 0)
setpgrp();
else if (pid)
pid = wait(&retStatus);
else {
if (execvp(args[0], args)){
fprintf(stderr, "%s\n", strerror(errno));
exit(127);
}
}*/
}
return 0;
}
but what would I pass through fflush()?
It would be
fflush(stdout);
- but that is not needed because of the fgets(buffer, BUFFER_SIZE, stdin).
Output streams that refer to terminal devices are always line buffered
by default; pending output to such streams is written automatically
whenever an input stream that refers to a terminal device is read.
(See man stdio.)
I'm assuming you want the interrupt handler to jump into the while loop in your main function, instead of printing "\>".
You can use sigsetjmp and siglongjmp for this. You might want to take at [1] for an example.
#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf JumpBuffer;
void INThandler(int);
void main(void)
{
signal(SIGINT, INThandler);
while (1) {
if (setjmp(JumpBuffer) == 0) {
printf(">");
/*...*/
}
}
}
void INThandler(int sig)
{
signal(sig, SIG_IGN);
signal(SIGINT, INThandler);
longjmp(JumpBuffer, 1);
}
This was adapted from [2]. If you use sigaction(), sigprocmask(), or sigsuspend() you need to use the siglongjmp and sigsetjmp functions, respectively [3].
Sources:
[1] https://publib.boulder.ibm.com/iseries/v5r2/ic2924/index.htm?info/apis/siglngj.htm
[2] http://www.csl.mtu.edu/cs4411.ck/www/NOTES/non-local-goto/sig-1.html
[3] sigsetjmp - The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 Edition

Resources