How to implement a kill command in my own shell - c

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)

Related

shared memory producer consumer in C

I have to implement a producer-consumer problem via shared memory and semaphores. It should takes input from input.txt file and save it to output.txt file. Process of saving information should be showed in terminal. I have a problem with, I guess, synchronization. When I call fork() and then execl() producer.c file inside, the program seems to be not responding. producer(at least) was working before, but during modifying some lines of code it stopped working and I can't solve it. Here's the piece of code:
#include <stdio.h> // main.c
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include "semaphore.h"
#include "memory.h"
#define SIZE 10
typedef struct cyclicalBuf
{
int size;
char bufor[SIZE];
} cyclicalBuf;
int main(int argc, char *argv[])
{
if(argc != 1)
{
perror("Need no arguments!");
exit(1);
}
int semid;
int memoryID;
int key;
key = semGetKey('A');
semid = semCreate(key, 2); // 2 sem made
setVal(semid, 0, SIZE);
setVal(semid, 1, 0);
memoryID = memoryCreate('A', sizeof(cyclicalBuf));
pid_t pid;
pid = fork();
switch(pid)
{
case -1:
perror("fork() error in producer section");
exit(2);
break;
case 0:
execl("./producer", "./producer", NULL);
perror("execl() error in producer section");
exit(3);
break;
default:
break;
}
sleep(1);
switch(pid)
{
case -1:
perror("fork() error in consumer section");
exit(4);
break;
case 0:
execl("./consumer", "./consumer", NULL);
perror("execl() error in consumer section");
exit(5);
break;
default:
break;
}
int i;
for(i = 0; i < 2; i++)
{
if(wait(0) < 0 )
{
perror("wait() error [main.c]");
}
}
semDelete(semid, 2);
memoryDelete(memoryID);
return 0;
}
#include <stdio.h> // producer.c
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include "memory.h"
#include "semaphore.h"
#define SIZE 10
typedef struct cyclicalBuf
{
int size;
char bufor[SIZE];
} cyclicalBuf;
void producer()
{
FILE *fp;
int i;
int semid;
int memoryID;
cyclicalBuf * buf;
char c;
if((fp = fopen("./input.txt", "r")) == NULL)
{
perror("fopen() error [producer]");
exit(1);
}
int key;
key = semGetKey('A');
semid = semCreate(key, 2); y
memoryID = memoryAccess('A');
buf = (cyclicalBuf *)memoryLink(memoryID);
printf("Producer: ");
for(i = 0; (c = fgetc(fp)) != EOF; i++)
{
semRelease(semid, 0);
usleep(rand()%5555);
buf->bufor[i%SIZE] = c;
buf->size++;
semAcquire(semid, 1);
printf(" %c", c);
}
if(fclose(fp) == EOF)
{
perror("fclose() error [producer]");
exit(2);
}
memoryUnlink(buf);
}
int main(int argc, char *argv[])
{
if(argc != 1)
{
perror("Need no arguments!");
exit(3);
}
srand(time(NULL));
producer();
return 0;
}
#include <stdio.h> // consumer.c
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/sem.h>
#include "memory.h"
#include "semaphore.h"
#define SIZE 10
typedef struct cyclicalBuf
{
int size;
char bufor[SIZE];
} cyclicalBuf;
void consumer()
{
FILE *fp;
int c_cnt = 0;
int i = 0;
int semid;
int memoryID;
cyclicalBuf * buf;
char c;
if((fp = fopen("./output.txt", "w")) == NULL)
{
perror("fopen() error [consumer]");
exit(1);
}
int key;
key = semGetKey('A');
semid = semCreate(key, 2);
memoryID = memoryAccess('A');
buf = (cyclicalBuf *)memoryLink(memoryID);
printf("Consumer: ");
while(!((semctl(semid, 0, GETVAL) == SIZE) && (c_cnt == buf->size)))
{
semRelease(semid, 1);
usleep(rand()%5555);
c = buf->bufor[i%SIZE];
semAcquire(semid, 0);
fputc(c, fp);
printf(" %c", c);
i++;
c_cnt++;
}
if(fclose(fp) == EOF) //
{
perror("fclose() error [consumer]");
exit(2);
}
memoryUnlink(buf); //
}
int main(int argc, char *argv[])
{
if(argc != 1)
{
perror("Need no arguments!");
exit(3);
}
srand(time(NULL));
consumer();
return 0;
}
I can provide semaphore and shared memory functions if needed.
OUTPUT:
./main
| --> this is just blinking cursor, nothing happens

How to use execvp() to execute a command

So I'm trying to create a custom shell for my school project. My method was to create child process, and have that process execute the command using the execvp() function that my professor briefly mentioned in class that we are meant to use. Here's my code, as always, any help is appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#define MAX_LINE 80
int main(int argc, char *argv[])
{
char *input = (char*)malloc(MAX_LINE*sizeof(char));
int should_run = 1;
while(should_run){
printf("osh>");
fflush(stdout);
pid_t pid;
pid = fork();
if(pid < 0){
printf("error with creating chiled process");
return 0;
}
if(pid == 0){
fgets(input, MAX_LINE, stdin);
char *token = strtok(input," ");
if(execvp(token[0], token) < 0){
printf("Error in execution.");
return(0);
}
//should_run = 0;
}
waitpid(pid, 1, 0);
}
return 0;
}
The prototype of execvp is
int execvp(const char *file, char *const argv[]);
It expects a pointer to char as the first argument, and a NULL-terminated
pointer to an array of char*. You are passing completely wrong arguments.
You are passing a single char as first argument and a char* as the second.
Use execlp instead:
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
So
char *token = strtok(input," \n");
if(token == NULL)
{
fprintf(stderr, "only delimiters in line\n");
exit(1);
}
if(execlp(token, token, NULL) < 0){
fprintf(stderr, "Error in execution: %s\n", strerror(errno));
exit(1);
}
Also the convention in UNIX is to print error messages to stderr and a process with an error should
have an exit status other than 0.
As Pablo's states, you are passing the wrong arguments to execvp().
You can consider coding by yourself a function (char **strsplit(char *str, char delim)) which takes a string and split it into smaller pieces, returning an array of strings.
Also don't ignore compiler's warnings, they tell you a lot of things, and I suggest you to compile with gcc -Wall -Wextra -Werror to get almost any possible error in your program.
I tell you this because waitpid() takes as second argument a pointer to integer, to get an update of the status of the forked program. With this status you how the program exited (normally, segf, bus error...), you can use it to print an error if something went wrong.
You can consider using execv() instead (I know I'm going off topic, but you can learn useful things doing this), and find by yourself the correct executable(s).
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#define MAX_LINE 255
char **strsplit(char *str, char delim);
char *strjoin(char const *s1, char const *s2);
int isexec(char *path)
{
struct stat buf;
lstat(path, &buf);
if (S_ISREG(buf.st_mode) && (S_IXUSR & buf.st_mode))
return (1);
return (0);
}
static char *find_exec_readdir(char *paths, char *cmd)
{
DIR *dir;
struct dirent *dirent;
char *exec;
exec = NULL;
if ((dir = opendir(paths)) != NULL)
{
while ((dirent = readdir(dir)) != NULL)
{
if (!strcmp(dirent->d_name, cmd))
{
exec = strdup(dirent->d_name);
break ;
}
}
if (closedir(dir))
dprintf(2, "Failed closing dir.\n");
}
return (exec);
}
char *find_exec(char *cmd, char **paths)
{
char *exec;
char *path;
char *tmp;
int i;
i = -1;
exec = NULL;
path = NULL;
if ((cmd[0] == '.' || cmd[0] == '/'))
{
if (isexec(cmd))
return (strdup(cmd));
return (NULL);
}
while (paths[++i])
if ((exec = find_exec_readdir(paths[i], cmd)) != NULL)
{
tmp = strjoin(paths[i], "/");
path = strjoin(tmp, exec);
free(tmp);
free(exec);
break ;
}
return (path);
}
int handle_return_status(int status)
{
int sig;
int i;
if (!WIFEXITED(status) && WIFSIGNALED(status))
{
sig = WTERMSIG(status);
i = -1;
while (++i <= 13)
{
if (print_signal_error(sig))
{
return (-1);
}
}
dprintf(2, "Process terminated with unknown signal: %d\n", sig, NULL);
return (-1);
}
return (0);
}
int main(int argc, char *argv[])
{
char *input = NULL;
char **command = NULL;
int should_run = 1;
int status = 0;
(void)argc;
(void)argv;
if ((input = (char*)malloc(MAX_LINE*sizeof(char))) == NULL)
return (dprintf(2, "Failed to malloc, abort.\n"));
while(should_run){
printf("osh> ");
fflush(stdout);
pid_t pid;
pid = fork();
if(pid < 0)
return (dprintf(2, "error with creating chiled process\n"));
if(pid == 0){
fgets(input, MAX_LINE, stdin);
command = strsplit(input, ' ');
command[0] = find_exec(command[0], strsplit(getenv("PATH"), ':'));
if(execv(command[0], &command[1]) < 0)
return (dprintf(2, "Error in execution.\n"));
//should_run = 0;
}
waitpid(pid, &status, 0);
handle_ret_status(status);
}
return 0;
}

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

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;
}

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

Global variable not staying set, maybe caused by fork()

I'm trying to write a very very simple unix shell in C, and I have the basics of what I need working, except support for a history command. I have a global 2D char array that holds the history of all entered commands. Commands are added before the fork() system call, and I was originally printing out the value of the history global array after strings were added, and they were printing out correctly, so I'm not sure why it doesn't print out when the command "history" is used at the shell.
Thank to anyone who takes a look.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "myhistory.h"
int BUFFER_SIZE = 1024;
char history[100][80];
int command_index = 0;
int main(int argc, char *argv[]){
int status = 0;
int num_args;
pid_t pid;
while(1){
char *buffer_input, *full_input;
char command[BUFFER_SIZE];
char *args[BUFFER_SIZE];
printf("myshell> ");
buffer_input = fgets(command, 1024, stdin);
full_input = malloc(strlen(buffer_input)+1);
strcpy(full_input, buffer_input);
if (command_index >= 100) {
command_index = 0;
}
strncpy(history[command_index], full_input, strlen(full_input) + 1);
command_index += 1;
parse_input(command, args, BUFFER_SIZE, &num_args);
//check exit and special command conditions
if (num_args==0)
continue;
if (!strcmp(command, "quit" )){
exit(0);
}
if(!strcmp(command, "history")){
int i;
fprintf(stderr,"%d\n",(int)pid);
for(i = 0; i < command_index; i++){
fprintf(stdout, "%d: %s\n",i+1,history[command_index]);
}
continue;
}
errno = 0;
pid = fork();
if(errno != 0){
perror("Error in fork()");
}
if (pid) {
pid = wait(&status);
} else {
if( execvp(args[0], args)) {
perror("executing command failed");
exit(1);
}
}
}
return 0;
}
void parse_input(char *input, char** args,
int args_size, int *nargs){
char *buffer[BUFFER_SIZE];
buffer[0] = input;
int i = 0;
while((buffer[i] = strtok(buffer[i], " \n\t")) != NULL){
i++;
}
for(i = 0; buffer[i] != NULL; i++){
args[i] = buffer[i];
}
*nargs = i;
args[i] = NULL;
}
Change:
fprintf(stdout, "%d: %s\n",i+1,history[command_index]);
to:
fprintf(stdout, "%d: %s\n",i+1,history[i]);

Resources