I've written a small program for a simple shell as an axercise to learn C. Unfortunaltely, the program crashes the second time it is called.
$ ./a.out
miniShell>> ls
ls
a.out digenv.c~ miniShell.c~ test
digenv digenv.c.orig miniShell.c.orig testshell.c
digenv2.c digenv.old.c README.md testshell.c~
digenv2.c~ LICENSE smallshell.c testshell.c.orig
digenv.c miniShell.c smallshell.c.orig
Child exited
miniShell>> ls
ls
Segmentation fault (core dumped)
Can you help me troubleshoot it? The code is:
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
int mystrcmp(char const *, char const *);
void err_syserr(char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
exit(EXIT_FAILURE);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_LEN 1024
#define BUFFERSIZE 1024
int main() {
char line[BUFFER_LEN];
char* argv[100];
char* path= "/bin/";
char progpath[20];
int argc;
size_t length;
char *token;
int i=0;
int pid;
while(1) {
printf("miniShell>> ");
if(!fgets(line, BUFFER_LEN, stdin)) {
break;
}
length = strlen(line);
if (line[length - 1] == '\n') {
line[length - 1] = '\0';
}
if(strcmp(line, "exit")==0) {
break;
}
token = strtok(line," ");
while(token!=NULL) {
argv[i]=token;
token = strtok(NULL," ");
i++;
}
argv[i]=NULL;
argc=i;
for(i=0; i<argc; i++) {
printf("%s\n", argv[i]);
}
strcpy(progpath, path);
strcat(progpath, argv[0]);
for(i=0; i<strlen(progpath); i++) {
if(progpath[i]=='\n') {
progpath[i]='\0';
}
}
pid= fork();
if(pid==0) {
execvp(progpath,argv);
fprintf(stderr, "Child process could not do execvp\n");
} else {
wait(NULL);
printf("Child exited\n");
}
}
return (0);
}
int mystrcmp(char const *p, char const *q)
{
int i = 0;
for(i = 0; q[i]; i++)
{
if(p[i] != q[i])
return -1;
}
return 0;
}
int cd(char *pth) {
char path[BUFFERSIZE];
char cwd[BUFFERSIZE];
char * return_value;
int other_return;
strcpy(path,pth);
if(pth[0] != '/')
{
return_value = getcwd(cwd,sizeof(cwd));
strcat(cwd,"/");
strcat(cwd,path);
other_return = chdir(cwd);
} else {
other_return = chdir(pth);
}
printf("Spawned foreground process: %d\n", getpid());
return 0;
}
The program is supposed to behave like a small shell that is able to take arbitrary commands and execute them (without using the system call). Can you help me?
The problem is caused by the value of i not being reset to 0 at the start of the top level while loop.
Add the line
i = 0;
just before
printf("miniShell>> ");
and all should be well.
Related
I am building a simple shell I want to handle the argument of exit. How can I handle it here?
I have already handle exit but I want to handle exit with argument.
Usage: exit status, where status is an integer used to exit the shell
For example
exit 98
exit 1
Present output
sh: No file or directory found
Expected Output
Exit the shell with the specific status code
shell.h
#ifndef SHELL_H
#define SHELL_H
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
void print_prompt(void);
void tok_str(char *, char **);
int add_path(char **cmd);
void print_env(char **);
#endif
shell.c
#include "shell.h"
int main(int argc, char **argv, char **env)
{
char *buf = NULL;
size_t buflen = 0;
char *cmd[20];
do
{
fprintf(stderr, ":) ");
if (getline(&buf, &buflen, stdin) == -1)
{
if (feof(stdin))
{
free(buf);
buf = NULL;
exit(EXIT_SUCCESS);
}
perror("Error occurred");
free(buf);
buf = NULL;
exit(EXIT_FAILURE);
}
if (buf[0] == '\0' || strcmp(buf, "\n") == 0)
{
free(buf);
buf = NULL;
continue;
}
if (strcmp(buf, "exit\n") == 0)
{
free(buf);
buf = NULL;
exit(EXIT_SUCCESS);
}
if (strcmp(buf, "env\n") == 0)
{
print_env(env);
free(buf);
buf = NULL;
continue;
}
tok_str(buf, cmd);
add_path(cmd);
if (fork() == 0)
{
execve(cmd[0], cmd, env);
printf("%s: No such file or directory\n", argv[0]);
exit(EXIT_SUCCESS);
}else
{
wait(NULL);
free(buf);
buf = NULL;
}
} while (1);
exit(EXIT_SUCCESS);
}
utils.c
#include "shell.h"
void tok_str(char *buf, char **cmd)
{
char *ptr;
int i = 0;
ptr = strtok(buf, " \n");
while (ptr)
{
cmd[i] = ptr;
ptr = strtok(NULL, " \n");
i++;
}
cmd[i] = NULL;
}
int add_path(char **cmd)
{
const char *PATH[] = {"/usr/local/bin/ls", "/bin/", "/sbin/", "/usr/bin/", NULL};
unsigned int i = 0;
struct stat st;
while (PATH[i])
{
char s[120] = "";
strcat(s, PATH[i]);
strcat(s, cmd[0]);
if (stat(s, &st) == 0)
{
cmd[0] = s;
return (0);
}
i++;
}
return (-1);
}
void print_env(char **env)
{
int i = 0;
while (env[i])
printf("%s\n", env[i++]);
}
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
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;
}
I'm writing a small shell in C as an exercis to learn about linux and C. Now I can execute custom commands and the exit command, but I can't execute a builtin CD command since I don't know how to split it in two part (the cd command and the name of the directory to CD to).
The desired functionality is that my program should accept the cd command with a parameter that is the directory. I can do it with command-line arguments but I don't know how to do it in its current form. How can it be done?
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#define BUFFER_LEN 1024
#define BUFFERSIZE 1024
int mystrcmp(char const *, char const *);
void err_syserr(char *fmt, ...)
{
int errnum = errno;
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)\n", errnum, strerror(errnum));
exit(EXIT_FAILURE);
}
int main() {
char line[BUFFER_LEN];
char* argv[100];
char* path= "/bin/";
char progpath[20];
int argc;
size_t length;
char *token;
int i=0;
int pid;
while(1) {
i = 0;
printf("miniShell>> ");
if(!fgets(line, BUFFER_LEN, stdin)) {
break;
}
length = strlen(line);
if (line[length - 1] == '\n') {
line[length - 1] = '\0';
}
if(strcmp(line, "exit")==0) {
break;
}
if(strcmp(line, "cd")==0) {
/*printf("change directory to %s\n", argv[2]);
chdir(argv[2]);*/
}
token = strtok(line," ");
while(token!=NULL) {
argv[i]=token;
token = strtok(NULL," ");
i++;
}
argv[i]=NULL;
argc=i;
for(i=0; i<argc; i++) {
printf("%s\n", argv[i]);
}
strcpy(progpath, path);
strcat(progpath, argv[0]);
for(i=0; i<strlen(progpath); i++) {
if(progpath[i]=='\n') {
progpath[i]='\0';
}
}
pid= fork();
if(pid==0) {
execvp(progpath,argv);
fprintf(stderr, "Child process could not do execvp\n");
} else {
wait(NULL);
printf("Child exited\n");
}
}
return (0);
}
int mystrcmp(char const *p, char const *q)
{
int i = 0;
for(i = 0; q[i]; i++)
{
if(p[i] != q[i])
return -1;
}
return 0;
}
int cd(char *pth) {
char path[BUFFERSIZE];
char cwd[BUFFERSIZE];
char * return_value;
int other_return;
strcpy(path,pth);
if(pth[0] != '/')
{
return_value = getcwd(cwd,sizeof(cwd));
strcat(cwd,"/");
strcat(cwd,path);
other_return = chdir(cwd);
} else {
other_return = chdir(pth);
}
printf("Spawned foreground process: %d\n", getpid());
return 0;
}
Use strpbrk or strsep to split your input into white-space separated tokens, then use strcmp on the first one and use the remaining ones as arguments.
This answer to a related question has an example, and here are some notes on portability.
I'd suggest to parse your argument with the getopt function from unistd library. It's covered in this question too.
Here is the documentation for getopt command.
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]);