I'm trying to make a custom shell for a class assignment. The TLDR is... I don't really know what's wrong with it. From the debugging, it looks like there's an error in the loop() function, but I don't know why, or where the error exists.
What's happening is the application executes the command correctly. However, on the next loop for the next command input, it segfaults. I've tried to make sure all the variables are freed so I'm not writing attempting to write into memory that I can't. That's the major issue. The second (much more minor) issue is that I can't get piping to work. I'm also not sure why. I thought I re-wrote stdin/stdout correctly. (Yes, I know strtok id deprecated. I do plan on moving to "strtok_r")
Does anyone have guidance or suggestions on what I'm not doing correctly?
Cheers,
M
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <limits.h>
#define TOK_DELIM " \t\r\n\a\v" // \a is Alert. \v is vertical tab.
#define FG_EXEC 0
#define BG_EXEC 1
#define MAXARG 20
#define CMD_MAX 100 //Some arbitrary value...
/* Set up Command Segements */
struct cmd_seg {
char *args[MAXARG];
struct cmd_seg *next; // Linked List. Next node
};
struct cmd {
struct cmd_seg *root;
int mode; //BG, FG
};
/* Helper Functions - Core Functionality */
void cd(char *path){
int code = chdir(path); //May only return -1 (error) or 0 (success)
if(code == -1){
printf("\nFailed to change your path to \"%s\"\n", path);
}
else{
printf("\nChanged path to: %s\n", path);
}
}
/* Helper Functions - Job Control */
void bg(pid_t pid){
int status;
if(kill(pid, SIGCONT) < 0){ printf("\nBG: JNF"); }
else{ waitpid(pid, &status, WUNTRACED); }
}
void fg(pid_t pid){
setpgid(pid, pid);
int status;
if (tcsetpgrp(1, getpgid(pid)) != 0){
printf("\nFG: JNF");
}
else{
kill(pid, SIGCONT);
waitpid(pid, &status, WUNTRACED);
}
}
/* Helper Functions - PID, Child, Forks, etc */
void kill_child(pid_t pid){ // What an unforunate name... Dark humour.
kill(pid, SIGTERM); //Safe Termination. SIGKILL may ne required?
}
/*
void destroyList(struct cmd** cmd1){
struct cmd* cmd_cur = cmd1;
struct cmd_seg* cs_cur;
struct cmd_seg* next;
cs_cur = cur->root;
while(cs_cur != NULL){
next = cs_cur->next;
free(cs_cur);
cs_cur = next;
}
free(cs_cur);
free(cmd1);
}
*/
/* Helper Functions - Check for Built-in Command */
int builtins(struct cmd_seg *segment){
/* Check for Internal Commands - CD, Exit, etc */
if(strcmp(segment->args[0], "cd") == 0){
cd(segment->args[1]);
char path[PATH_MAX];
getcwd(path, sizeof(path));
printf("%s%%", path);
fflush(stdout);
return 1;
}
else if(strcmp(segment->args[0], "exit") == 0){
printf("\nExiting Core Application.\n\n");
exit(0);
}
/*
else if(strcmp(segment->args[0], "kill") == 0){
pid_t pid;
pid = atoi(segment->args[1]);
if(pid!=0){ // if 0, implies it's not an integer, failed extraction;
kill_child(pid);
}
return 1;
}
*/
else{
return -1;
}
}
int exec_cmd_seg(struct cmd_seg *segment, int in_fd, int out_fd, int mode, int pGrpID){
int status = 1;
int IsBuiltin = builtins(segment);
if(IsBuiltin == 0){ return -1; }
else if(IsBuiltin == 1){ return 1; }
/*
* Not a Builtin Command, Application has not Exited
* Therefore fork
*/
pid_t child_pid;
child_pid = fork();
if(child_pid < 0){
printf("\nForking failed - Exiting Gracefully");
exit(0);
}
else if(child_pid == 0){
/* Forked */
int damnPIDs = getpid();
if(mode == FG_EXEC){ printf("\nFG Execution # PID %d", damnPIDs); }
else{ printf("\nBG Execution # PID %d", damnPIDs); }
signal(SIGINT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
signal(SIGCONT, SIG_DFL);
/* Re-write In-Outs */
dup2(in_fd, 0);
dup2(out_fd, 1);
if(in_fd != 0){
close(in_fd);
}
else if(out_fd != 1){
close(out_fd);
}
/* Check args[1] */
int result = execvp(segment->args[0], segment->args);
if(result < 0){
printf("\nError Executing Command: %s", segment->args[0]);
//exit(0);
}
}
else{
signal(SIGINT, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGINT, SIG_DFL);
if (mode == BG_EXEC)
signal(SIGCHLD, SIG_IGN);
else
waitpid(child_pid, &status, WUNTRACED);
if(in_fd != 0){
close(in_fd);
}
if(out_fd != 1){
close(out_fd);
}
return 1;
}
return 1;
}
int exec_cmd(struct cmd *command){
int status = 1;
struct cmd_seg* cur;
struct cmd_seg* pfree;
int fd = 0;
//int count = 0;
//printf("\nEXEC_CMD: Entering For1");
//fflush(stdout);
for(cur = command->root; cur != NULL; cur = cur -> next){
//printf("\nExec_CMD - Command %d: %s", count, command->root->args);
//fflush(stdout);
if(cur -> next){
//printf("\nExec_CMD - Command %d: %s", count, cur->args);
//fflush(stdout);
int fd2[2];
pipe(fd2);
status = exec_cmd_seg(cur, fd, fd2[1], command->mode, 0);
fd = fd2[0];
}
else{
status = exec_cmd_seg(cur, fd, 1, command->mode, 0);
}
cur = command -> root;
pfree = cur;
while(cur != NULL){
cur = cur -> next;
/* Clear pfree before setting it */
free(pfree);
pfree = cur;
}
}
cur = NULL;
pfree = NULL;
free(command);
command = NULL;
return status;
}
/* Fix to use strtok_r */
struct cmd* parser(char *line){
struct cmd* command = (struct cmd*)malloc(sizeof(struct cmd));
command->root = (struct cmd_seg*)malloc(sizeof(struct cmd_seg));
struct cmd_seg* cur;
struct cmd_seg* pnew;
cur = command->root;
/* Check if it is background command */
char* pStart = line; //pointer
int count = 0;
while ((*pStart != '\n') && (*pStart != '\0')) {
if (*pStart == '&') {
count = 1;
*pStart = '\0';
break;
}
pStart++;
}
command->mode = count;
/* Parse line as command Link list */
char *res = line;
char *temp;
int i = 0;
temp = strsep(&res, "|");
for (i = 0; i < MAXARG - 1 && (cur->args[i] = strtok(temp, TOK_DELIM)) != NULL; i++)
temp = NULL;
cur->args[i] = NULL;
while ((temp = strsep(&res, "|")) != NULL) {
pnew = (struct cmd_seg*) malloc(sizeof(struct cmd_seg));
cur->next = pnew;
cur = pnew;
for (i = 0; i < MAXARG - 1 && (cur->args[i] = strtok(temp, TOK_DELIM)) != NULL; i++)
temp = NULL;
cur->args[i] = NULL;
}
cur->next = NULL;
//free(cur);
//free(pnew);
//free(pStart);
return command;
}
char* readLine(){
char *buffer = malloc(sizeof(char)*CMD_MAX);
char ch;
int pos = 0;
if(buffer){
//printf("Inside Buffer If");
//fflush(stdout);
while(1){
ch = getchar();
//printf("Inside While\n");
//fflush(stdout);
/* Buffer Allocated */
if(ch == EOF || ch == '\n'){
printf("Command: %s\n", buffer);
return buffer;
}
else{
buffer[pos] = ch;
}
pos++;
}
}
else{
printf("Allocation Failed\n");
fflush(stdout);
exit(0);
}
}
void loop(){
/* Loop Command Input */
int status = 1;
//int l_count = 0;
while(status >= 0){
char *line;
struct cmd *cmd;
//printf("Inside Loop - 1\n");
//fflush(stdout);
/* Print Shell Prompt */
char path[PATH_MAX];
getcwd(path, sizeof(path));
printf("%s%%", path);
fflush(stdout);
line = readLine();
if(strlen(line) == 0){
printf("Failed to read line.");
exit(0);
}
else{
cmd = parser(line);
if(cmd!=NULL){
//printf("\nCMD not null - executing");
//fflush(stdout);
status = exec_cmd(cmd);
}
else{
printf("\nFailed to parse command correctly");
fflush(stdout);
exit(0);
}
}
free(line);
//free(cmd);
//free(path); Not a heap device -- GCC Output
}
}
int main(int argc, char **argv){
loop();
return 1;
}
The segfault occurs in the for statement. The body of the for-loop has executed one time, then the loop-expression cur = cur->next is executed, but cur is NULL.
It's NULL because of some code in the body of the for-loop which frees all the cmd_segs in the linked list (I suspect that code is supposed to be after the for-loop rather than inside it).
int exec_cmd(struct cmd *command){
int status = 1;
struct cmd_seg* cur;
struct cmd_seg* pfree;
for(cur = command->root; cur != NULL; cur = cur -> next){
if(cur -> next){
...
}
cur = command -> root;
pfree = cur;
while(cur != NULL){
cur = cur -> next;
free(pfree);
pfree = cur;
}
}
...
}
The cond-expression cur != NULL doesn't catch this because it is executed after the init-expression and then after each loop-expression.
After I fixed this by moving the cmd_seg-freeing code to be after the for-loop, I looked at pipeline commands, but they all succeeded for me.
Related
My program currently is traversing through directories finding mp3 files. In each thread, I'm finding mp3 files found in each directory. When I find an mp3 file, I'm inserting the file into an array.
Problem: I'm getting different segmentation faults every time I run the program.
I believe my pthread_mutex_lock and pthread_mutex_unlock are positioned in the correct area but I don't understand why my program keeps failing. Any help or advice is appreciated!
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
void *func(void *);
char **data;
int data_length;
char **directories;
int length;
int num = 0;
int directory_accum = 0;
unsigned long i = 0;
struct dirent *entry;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
void listFilesRecursively(char *path);
int main(int argc, char **argv)
{
// INITIALIZE DATA TO NULL
data = NULL;
directories = NULL;
if (argc != 2)
{
printf("Must give a path\n");
return -1;
}
// SWITCH CURRENT WORKING DIRECTORY TO DIR GIVEN ON COMMAND LINE
chdir(argv[1]); // JUST ONCE
// DIRECTORY POINTER
// OPEN THE CURRENT WORKING DIRECTORY
DIR *current = opendir(".");
//printf("%lx\n", (unsigned long)current);
int directoriessize = 1;
// read entries and output based on type
while( (entry = readdir(current)) )
{
if (entry->d_type == DT_DIR)
{
if((strcmp(entry->d_name, ".") != 0) && (strcmp(entry->d_name, "..") != 0))
{
printf("IN MAIN FUNCTION:\n");
directoriessize++;
directories = realloc(directories, sizeof(char*) * directoriessize);
directories[directory_accum] = malloc(sizeof(char*));
printf("%s is a directory\n", entry->d_name);
directories[directory_accum] = entry->d_name;
directory_accum++;
//listFilesRecursively(entry->d_name);
printf("\n");
}
} else if (entry->d_type == DT_REG) {
printf("IN MAIN FUNCTION:\n");
printf("%s is a regular file\n\n", entry->d_name);
} else {
printf("IN MAIN FUNCTION:\n");
printf("%s is idk\n\n", entry->d_name);
}
}
printf("\n");
printf("# of Directories: %d\n\n", directory_accum);
for(int i = 0; i < directory_accum; i++)
{
printf("Directories in array: %s\n", directories[i]);
}
printf("\n");
pthread_t tids[directory_accum];
// initialize data to NULL
data = NULL;
// start all of the threads ( AFTER SEEING HOW MANY DIRECTORIES YOU HAVE)
// FOR EXAMPLE ONLY START TWO THREADS FOR a AND b in top. c DIRECOTRY WILL BE
// HELD INSIDE THE b THREAD
for(i = 0; i < directory_accum; ++i)
{
int err = pthread_create(&tids[i], NULL, func, (void**) directories[i]);
if (err != 0)
{
fprintf(stderr, "! Couldn't create thread");
}
}
// wait for threads and join them
for(int i = 0; i < directory_accum; ++i)
{
pthread_join(tids[i], NULL);
}
// print out data
for(int i = 0; i < length; ++i)
{
printf("%s", data[i]);
free(data[i]);
}
free(data);
return 0;
}
// THREAD FUNCTION
// RECURSAL TRAVERSAL OF THE DIRECTORIES HAPPEN HERE OR MAYBE IN SECONDAY FUNCTION
//void *func(void *arg)
void *func(void *arg)
{
char *directory = arg;
// critical section : (LOCKS THIS SO ONLY ONE FUNCTION WILL RUN THIS AT THE TIME)
//pthread_mutex_lock(&lock);
printf("NEW THREAD\n");
listFilesRecursively(directory);
printf("\n\n");
//pthread_mutex_unlock(&lock);
return NULL;
}
void listFilesRecursively(char *basePath)
{
char path[1000];
DIR *current = opendir(basePath);
// Unable to open directory stream
if (!current)
return;
while ((entry = readdir(current)) != NULL)
{
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0)
{
// Construct new path from our base path
strcpy(path, basePath);
strcat(path, "/");
strcat(path, entry->d_name);
printf("Path: %s\n", path);
if(entry->d_type == DT_REG)
{
pthread_mutex_lock(&lock);
printf("------------------\n");
printf("Writing %s to file\n", entry->d_name);
printf("------------------\n");
if(length < (num + 1))
{
length = num + 1;
}
data = realloc(data, sizeof(char*) * length);
data[num] = entry->d_name;
num++;
pthread_mutex_unlock(&lock);
}
listFilesRecursively(path);
}
}
closedir(current);
return;
}
I have a some multithreading application. This is a part of main function:
/* ...some code before... */
for(i=0; i<THREADS_COUNT; i++){
status = pthread_create(&threads[i], NULL, thread_main, NULL);
if(status < 0){
fprintf(stderr, "threads error\n");
exit(2);
}
}
status = sem_init(&sem, 0, 0);
if(status < 0){
fprintf(stderr, "sem_init error\n");
exit(4);
}
/* recv loop */
while (1) {
rv = recv(fd, buf, BUFSIZE, 0);
if(rv >= 0){
current = malloc(sizeof(struct queue_msg_list));
/* adding to our local queue */
if(current != NULL){
current->rv = rv;
current->h = h;
memcpy(&(current->buf), &buf, BUFSIZE);
current->next = NULL;
if(main_head == NULL){
main_head = main_tail = current;
}
else {
main_tail->next = current;
main_tail = current;
}
count++;
}
/* if we can carry the local queue to the queue for threads then we are doing it */
if(!pthread_mutex_trylock(&mlock)){
if(thread_head == NULL){
/* if the threads-queue is empty then replace queues */
thread_head = main_head;
thread_tail = main_tail;
} else {
/* if the threads-queue is not empty then add the local queue to the threads-queue */
thread_tail->next = main_head;
thread_tail = main_tail;
}
/* we increasing a semaphore of number of added elements */
for(i=0; i<count; i++){
sem_post(&sem);
printf("sem_post \n");
}
count = 0;
pthread_mutex_unlock(&mlock);
main_head = NULL;
main_tail = NULL;
}
}
}
/* ...some code after... */
And this is a function for threads:
void *thread_main(void *arg)
{
struct queue_msg_list *current;
char buf[BUFSIZE] __attribute__ ((aligned));
struct nfq_handle *h;
int rv;
while(1){
sem_wait(&sem);
pthread_mutex_lock(&mlock);
/* if no blocking then we are working with packet and removing it from list after */
current = thread_head;
rv = current->rv;
h = current->h;
memcpy(&buf, &(current->buf), BUFSIZE);
thread_head = thread_head->next;
pthread_mutex_unlock(&mlock);
nfq_handle_packet(h, buf, rv);
free(current);
}
}
This application always works true on PC. I have put this application to some router (linux kernel version in firmware is 2.6.30). It works correctly sometimes but sometimes it works incorrectly. :)
Threads hang on calling sem_wait(&sem); sometimes but the semaphore value is above zero (1, 2, 3, etc). Why?
P.S. I tried to check the return value of sem_wait(&sem); but did not get it.
I am trying to make a simple shell program with the C language which have the options of redirecting stdin and stdout and making a pipe but it's giving me a segmentation fault error. Maybe the problem is in the getline but I'm not sure. Here is the code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#define R 0
#define W 1
#define LINE_LEN 25
struct Job {
char* command;
char** argv;
int stdin;
int stdout;
} typedef Job;
int tokens_number = 0;
int sign_place = 0;
int contain_left = 0;
int contain_right = 0;
int contain_line = 0;
char** parse_cmdline (char * cmdline ){
char** arg = calloc(15, sizeof(char*));
char temp_cmd[LINE_LEN*10];
strcpy(temp_cmd, cmdline);
char * tmp;
tmp = strtok(temp_cmd, " ");
while(tmp != NULL) {
arg[tokens_number] = (char*) malloc(LINE_LEN * sizeof(char*));
strcpy(arg[tokens_number],tmp);
tmp = strtok(NULL, " ");
tokens_number++;
}
//LAST ELEMENT IS NULL
arg[tokens_number+1] = NULL;
return arg;
}
void check_for_special_signs(char** argv){
int i;
for(i=0; i<tokens_number; i++){
if(strcmp(argv[i], "<") == 0){
contain_left = 1;
sign_place = i;
return;
}else if(strcmp(argv[i], ">") == 0){
contain_right = 1;
sign_place = i;
return;
}else if(strcmp(argv[i], "|") == 0){
contain_line = 1;
sign_place = i;
return;
}
}
}
void fork_child(Job* my_job) {
pid_t pid = fork();
if (pid == 0) {
execv(my_job -> command, my_job -> argv);
perror(my_job -> command);
} else if (pid > 0) {
int status;
wait(&status);
} else
perror("fork");
}
char** create_argv(char** argv){
int i;
int j = 0;
char** argvs = calloc(sign_place,sizeof(char*));
if(sign_place!=0){
for(i=0; i < sign_place ; i++){
argvs[i] = (char*) malloc(sizeof(char*));
strcpy(argvs[i],argv[i]);
}
return argvs;
}else{
return argv;
}
}
void close_job(Job* my_job) {
if (my_job -> stdin != STDIN_FILENO)
close(my_job -> stdin);
if (my_job -> stdout != STDOUT_FILENO)
close(my_job -> stdout);
free(my_job);
}
int main() {
size_t s = 512;
char* buffer = malloc(s * sizeof(char));
char** sep_cmd = malloc(s * sizeof(char));
while (getline(&buffer, &s, stdin) != EOF) {
Job* my_job;
int my_pipe[2];
int in = 0;
int out = 1;
sep_cmd = parse_cmdline(buffer);
my_job->command = sep_cmd[0];
my_job->argv = sep_cmd;
my_job->stdin = in;
my_job->stdout = out;
check_for_special_signs(my_job->argv);
pid_t pid = fork();
if (pid == 0) {
if(contain_left == 1){
in = open(my_job->argv[sign_place + 1], O_RDONLY);
if(in < 0){
perror("open()");
}
my_job->argv = create_argv(my_job->argv);
my_job->stdin = in;
}else if(contain_right == 1){
out = open(my_job->argv[sign_place + 1], O_WRONLY | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (out < 0)
perror("open()");
my_job->argv = create_argv(my_job->argv);
my_job->stdout = out;
}else if(contain_line == 1){
pipe(my_pipe);
if (my_job -> stdin == my_pipe[R])
close(my_pipe[W]);
else
close(my_pipe[R]);
}
execv(my_job -> command, my_job -> argv);
perror(my_job -> command);
} else if (pid > 0) {
int status;
wait(&status);
} else{
perror("fork");
}
close_job(my_job);
free(buffer);
buffer = (char*) malloc(s * sizeof(char));
}
free(buffer);
return 0;
}
That way I can't see if there are more mistakes in the code. Please if you see more mistakes list them too.
Thank you.
You forgot to allocate memory for my_job in main function
I am trying to implement something that will give me a solution for:
| --> cmd3 --> cmd4 -->
cmd2-->|
| --> cmd5 --> cmd6 -->
and so on...
This is multiple executions of processes and pipe the results via chains of other's processes with threads, each commands chain should run in different thread.
I choose socketpair for the implementation of IPC, because pipe has a a bottleneck with the buffer size limit 64K.
When I test the program with single chain - it's work as expected, but when I am running master command and the output of it I send via socketpair to read end of multiple processes in each thread - the program stuck (look like a deadlock)
Whats I am doing wrong:
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/socket.h>
typedef struct command {
char** argv;
int num_children;
struct command* master_cmd;
struct command** chains;
struct command* next;
int fd;
} command;
void be_child(command* cmd);
int execute_master_command_and_pipe_to_childs(command* cmd, int input);
int run_pipeline_sockets(command *cmd, int input);
void waitfor(int fd);
int main(int argc, char* argv[]) {
handle_segfault();
command* cmd1 = (command*) malloc(sizeof(command));
command* cmd2 = (command*) malloc(sizeof(command));
command* cmd3 = (command*) malloc(sizeof(command));
command* cmd4 = (command*) malloc(sizeof(command));
command* cmd5 = (command*) malloc(sizeof(command));
command* cmd6 = (command*) malloc(sizeof(command));
command* chains1[2];
chains1[0] = cmd3;
chains1[1] = cmd5;
char* args1[] = { "cat", "/tmp/test.log", NULL };
char* args3[] = { "sort", NULL, NULL };
char* args4[] = { "wc", "-l", NULL };
char* args5[] = { "wc", "-l", NULL };
char* args6[] = { "wc", "-l", NULL };
cmd1->argv = args1;
cmd2->argv = NULL;
cmd3->argv = args3;
cmd4->argv = args4;
cmd5->argv = args5;
cmd6->argv = args6;
cmd1->master_cmd = NULL;
cmd1->next = NULL;
cmd1->chains = NULL;
cmd1->num_children = -1;
cmd2->master_cmd = cmd1;
cmd2->chains = chains1;
cmd2->next = NULL;
cmd2->num_children = 2;
cmd3->master_cmd = NULL;
cmd3->next = cmd4;
cmd3->chains = NULL;
cmd3->num_children = -1;
cmd4->master_cmd = NULL;
cmd4->next = NULL;
cmd4->chains = NULL;
cmd4->num_children = -1;
cmd5->master_cmd = NULL;
cmd5->next = cmd6;
cmd5->chains = NULL;
cmd5->num_children = -1;
cmd6->master_cmd = NULL;
cmd6->next = NULL;
cmd6->chains = NULL;
cmd6->num_children = -1;
int rc = execute_master_command_and_pipe_to_childs(cmd2, -1);
return 0;
}
int execute_master_command_and_pipe_to_childs(command* cmd, int input) {
int num_children = cmd->num_children;
int write_pipes[num_children];
pthread_t threads[num_children];
command* master_cmd = cmd->master_cmd;
pid_t pid;
int i;
for (i = 0; i < num_children; i++) {
int new_pipe[2];
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, new_pipe) < 0) {
int errnum = errno;
fprintf(STDERR_FILENO, "ERROR (%d: %s)\n", errnum,
strerror(errnum));
return EXIT_FAILURE;
}
if (cmd->chains[i] != NULL) {
cmd->chains[i]->fd = new_pipe[0];
if (pthread_create(&threads[i], NULL, (void *) be_child,
cmd->chains[i]) != 0) {
perror("pthread_create"), exit(1);
}
write_pipes[i] = new_pipe[1];
} else {
perror("ERROR\n");
}
}
if (input != -1) {
waitfor(input);
}
int pipefd = run_pipeline_sockets(master_cmd, input);
int buffer[1024];
int len = 0;
while ((len = read(pipefd, buffer, sizeof(buffer))) != 0) {
int j;
for (j = 0; j < num_children; j++) {
if (write(write_pipes[j], &buffer, len) != len) {
fprintf(STDERR_FILENO, "Write failed (child %d)\n", j);
exit(1);
}
}
}
close(pipefd);
for (i = 0; i < num_children; i++) {
close(write_pipes[i]);
}
for (i = 0; i < num_children; i++) {
if (pthread_join(threads[i], NULL) != 0) {
perror("pthread_join"), exit(1);
}
}
}
void waitfor(int fd) {
fd_set rfds;
struct timeval tv;
int retval;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 500000;
retval = select(fd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1)
perror("select()");
else if (retval) {
printf("Data is available now on: %d\n", fd);
} else {
printf("No data on: %d\n", fd);
///waitfor(fd);
}
}
void be_child(command* cmd) {
printf(
"fd = %d , argv = %s , args = %s , next = %d , master_cmd = %d , next_chain = %d\n",
cmd->fd, cmd->argv[0], cmd->argv[1], cmd->next, cmd->master_cmd,
cmd->chains);
waitfor(cmd->fd);
int fd = run_pipeline_sockets(cmd, cmd->fd);
waitfor(fd);
int buffer[1024];
int len = 0;
while ((len = read(fd, buffer, sizeof(buffer))) != 0) {
write(STDERR_FILENO, &buffer, len);
}
close(cmd->fd);
close(fd);
}
int run_pipeline_sockets(command *cmd, int input) {
int pfds[2] = { -1, -1 };
int pid = -1;
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pfds) < 0) {
int errnum = errno;
fprintf(STDERR_FILENO, "socketpair failed (%d: %s)\n", errnum,
strerror(errnum));
return EXIT_FAILURE;
}
if ((pid = fork()) == 0) { /* child */
if (input != -1) {
dup2(input, STDIN_FILENO);
close(input);
}
if (pfds[1] != -1) {
dup2(pfds[1], STDOUT_FILENO);
close(pfds[1]);
}
if (pfds[0] != -1) {
close(pfds[0]);
}
execvp(cmd->argv[0], cmd->argv);
exit(1);
} else { /* parent */
if (input != -1) {
close(input);
}
if (pfds[1] != -1) {
close(pfds[1]);
}
if (cmd->next != NULL) {
run_pipeline_sockets(cmd->next, pfds[0]);
} else {
return pfds[0];
}
}
}
void segfault_sigaction(int signal, siginfo_t *si, void *arg) {
printf("Caught segfault at address %p\n", si->si_addr);
printf("Caught segfault errno %p\n", si->si_errno);
exit(0);
}
void handle_segfault(void) {
struct sigaction sa;
memset(&sa, 0, sizeof(sigaction));
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = segfault_sigaction;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
}
I would come at this problem from a very different angle: rather than coming up with a large data structure to manage the pipe tree, and using threads (where an io blockage in a process may block in its threads) I would use only processes.
I also fail to see how a 64K buffer is your bottleneck when you're only using a 1K buffer.
2 simple functions should guide this: (error handling omitted for brevity, and using a pseudocodey parsecmd() function which turns a space separated string into an argument vector)
int mkproc(char *cmd, int outfd)
{
Command c = parsecmd(cmd);
int pipeleft[2];
pipe(pipeleft);
if(!fork()){
close(pipeleft[1]);
dup2(pipeleft[0], 0);
dup2(outfd, 1);
execvp(c.name, c.argv);
}
close(pipeleft[0]);
return pipeleft[1];
}
Mkproc takes the fd it will write to, and returns what it will read from. This way chains are really easy to initalize:
int chain_in = mkproc("cat foo.txt", mkproc("sort", mkproc("wc -l", 1)));
the next is:
int mktree(char *cmd, int ofd0, ...)
{
int piperight[2];
pipe(piperight);
int cmdin = mkproc(cmd, piperight[1]);
close(piperight[1]);
if(!fork()){
uchar buf[4096];
int n;
while((n=read(piperight[0], buf, sizeof buf))>0){
va_list ap;
int fd;
va_start(ap, ofd0);
for(fd=ofd0; fd!=-1; fd=va_arg(ap, int)){
write(fd, buf, n);
}
va_end(ap);
}
}
return cmdin;
}
Between the two of these, it is very easy to construct trees of arbitrary complexity, as so:
int tree_in = mktree("cat foo.txt",
mktree("rot13",
mkproc("uniq", mkproc("wc -l", 1)),
mkproc("wc -l", open("out.txt", O_WRONLY)), -1),
mkproc("sort", 2), -1);
This would output a sorted foo.txt to stderr, the number of lines in rot13'd foo.txt to out.txt, and the number of non-duplicate lines of rot13'd foo.txt to stdout.
a kind user here gave me some code to work with for a command line shell, but I want it to output to stdout and stderr instead of using a screen or whatever it is doing right now. I am new to C so I don't know anything about converting it. I also need its ability to detect arrow keys preserved... I'm trying to make a simplistic bash clone. This is what I have right now, it's about 50% my code and 50% others'... yes, it is buggy. There are large sections commented out because they were no longer being used or because they were broken. Ignore them. :)
The particular difficulty is in the use of draw_frame() in main().
#include "os1shell.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h> /* standard unix functions, like getpid() */
#include <sys/types.h> /* various type definitions, like pid_t */
#include <signal.h> /* signal name macros, and the kill() prototype */
#include <ncurses/curses.h> /* a library for cursor-based programs */
#include <poll.h>
#include <termios.h>
#include <time.h>
/** VT100 command to clear the screen. Use puts(VT100_CLEAR_SCREEN) to clear
* the screen. */
#define VT100_CLEAR_SCREEN "\033[2J"
/** VT100 command to reset the cursor to the top left hand corner of the
* screen. */
#define VT100_CURSOR_TO_ORIGIN "\033[H"
struct frame_s {
int x;
int y;
char *data;
};
char* inputBuffer; /* the command input buffer, will be length 65 and null
* terminated. */
char** cmdHistory; /* the command history, will be no longer than 20
* elements and null terminated. */
int historySize = 0;
void addToHistory(char* newItem) {
char** h;
int historySize = 0;
for (historySize; historySize < 21; ++historySize) {
if (cmdHistory[historySize] == NULL) break;
}
if (historySize == 20) {
char** newPtr = cmdHistory + sizeof(char *);
free(cmdHistory[0]);
cmdHistory = newPtr;
h = (char**)realloc(cmdHistory,21*sizeof(char *));
cmdHistory = h;
cmdHistory[19] = newItem;
cmdHistory[20] = NULL;
} else {
h = (char**)realloc(cmdHistory,(historySize+2)*sizeof(char *));
cmdHistory = h;
cmdHistory[historySize] = newItem;
cmdHistory[historySize+1] = NULL;
}
}
/* Some help from http://stackoverflow.com/users/1491/judge-maygarden*/
char** getArguments(char* input) {
char** arguments;
int k = 0;
char* tokenized;
arguments = calloc(1, sizeof (char *));
tokenized = strtok(input, " &");
while (tokenized != NULL) {
arguments[k] = tokenized;
++k;
arguments = realloc(arguments, sizeof (char *) * (k + 1));
tokenized = strtok(NULL, " &");
}
// an extra NULL is required to terminate the array for execvp()
arguments[k] = NULL;
return arguments;
}
void printHistory(struct frame_s *frame) {
snprintf(frame->data, frame->x, "\n\n");
char** currCmd = cmdHistory;
while (*currCmd != NULL) {
snprintf(frame->data[(2*frame->x)], frame->x, "%s\n", *currCmd);
currCmd++;
}
snprintf(frame->data, frame->x, "\n\n");
}
/* Some help from http://stackoverflow.com/users/659981/ben*/
static int draw_frame(struct frame_s *frame) {
int row;
char *data;
int attrib;
puts(VT100_CLEAR_SCREEN);
puts(VT100_CURSOR_TO_ORIGIN);
for ( row = 0, data = frame->data;
row < frame->y;
row++, data += frame->x ) {
// 0 for normal, 1 for bold, 7 for reverse.
attrib = 0;
// The VT100 commands to move the cursor, set the attribute,
// and the actual frame line.
fprintf( stdout,
"\033[%d;%dH\033[0m\033[%dm%.*s",
row + 1,
0,
attrib, frame->x, data);
fflush(stdout);
}
return (0);
}
/* Some help from http://stackoverflow.com/users/659981/ben*/
int main(void) {
const struct timespec timeout = { .tv_sec = 1, .tv_nsec = 0 };
struct frame_s frame;
struct termios tty_old;
struct termios tty_new;
unsigned char line[65]; // the input buffer
unsigned int count = 0; // the count of characters in the buff
int ret;
struct pollfd fds[1];
sigset_t sigmask;
struct tm *tp;
time_t current_time;
cmdHistory = (char**)calloc(21,sizeof(char *)); // initialize the
// command history
cmdHistory[20] = NULL; // null terminate the history
int histInd = 0; // an index for the history for arrows
int t;
int r;
char** downTemp;
char** enterTemp;
// Set up a little frame.
frame.x = 80;
frame.y = 32;
frame.data = malloc(frame.x * frame.y);
if (frame.data == NULL) {
fprintf(stderr, "No memory\n");
exit (1);
}
memset(frame.data, ' ', frame.x * frame.y);
// Get the terminal state.
tcgetattr(STDIN_FILENO, &tty_old);
tty_new = tty_old;
// Turn off "cooked" mode (line buffering) and set minimum characters
// to zero (i.e. non-blocking).
tty_new.c_lflag &= ~ICANON;
tty_new.c_cc[VMIN] = 0;
// Set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &tty_new);
// Un-mask all signals while in ppoll() so any signal will cause
// ppoll() to return prematurely.
sigemptyset(&sigmask);
fds[0].events = POLLIN;
fds[0].fd = STDIN_FILENO;
// Loop forever waiting for key presses. Update the output on every key
// press and every 1.0s (when ppoll() times out).
do {
fd_set rdset;
int nfds = STDIN_FILENO + 1;
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO, &rdset);
ret = pselect(nfds, &rdset, NULL, NULL, &timeout, &sigmask);
if (ret < 0) { // check for pselect() error.
if (errno == EINTR) {
continue;
} else {
break;
}
}
if (FD_ISSET(STDIN_FILENO, &rdset)) {
ret = read(STDIN_FILENO,&line[count],sizeof(line)-count);
// do {
// fds[0].revents = 0;
// ret = poll(fds, sizeof(fds) / sizeof(struct pollfd), 1000);
//
// if (fds[0].revents & POLLIN) {
// ret = read(STDIN_FILENO,&line[count],sizeof(line)-count);
if (ret > 0) {
line[count + ret] = '\0';
if (strcmp(&line[count], "\033[A") == 0) {
if (histInd > 0) {
--histInd;
}
count = 0;
if(cmdHistory[histInd]!=NULL) {
snprintf(&frame.data[(2*frame.x)],
frame.x,
"hist: %s",
cmdHistory[histInd]);
strcpy(line, cmdHistory[histInd]);
}
} else if (strcmp(&line[count],"\033[B")==0) {
char** downTemp = cmdHistory;
r = 0;
while (*downTemp != NULL) {
++downTemp;
++r;
}
if (histInd < r-1 && r!= 0) {
++histInd;
}
count = 0;
if(cmdHistory[histInd]!=NULL) {
snprintf(&frame.data[(2*frame.x)],
frame.x,
"hist: %s",
cmdHistory[histInd]);
strcpy(line, cmdHistory[histInd]);
}
} else if (line[count] == 127) {
if (count != 0) {
line[count] = '\0';
count -= ret;
}
snprintf(&frame.data[(2*frame.x)], frame.x, "backspace");
} else if (line[count] == '\n') {
char** arguments = getArguments(line);
snprintf( &frame.data[(2*frame.x)],
frame.x,
"entered: %s",
line);
if (count > 0) {
int hasAmpersand = 0;
char* cmd = (char*)
malloc(65*sizeof(char));
strcpy(cmd, line);
addToHistory(cmd);
/*
char* temp = cmd;
while (*temp != '\0') {
if (*temp == '&') {
hasAmpersand = 1;
}
++temp;
}
pid_t pid;
pid = fork();
if (pid == 0) {
int exeret;
exeret = execvp(*arguments,
arguments);
if (exeret < 0) {
snprintf(
&frame.data[
(2*frame.x)],
frame.x,
"Exec failed.\n\n");
exit(1);
}
} else if (pid < 0) {
snprintf(
&frame.data[
(2*frame.x)],
frame.x,
"Fork failed.\n\n");
exit(1);
} else if (pid > 0) {
if (!hasAmpersand) {
wait(NULL);
}
free(arguments);
snprintf(frame.data,
frame.x,
"\n\n");
}*/
} else {
free(arguments);
}
enterTemp = cmdHistory;
t = 0;
while (*enterTemp != NULL) {
++enterTemp;
++t;
}
if (t > histInd) histInd = t;
count = 0;
} else {
//snprintf( frame.data,
// frame.x,
// "char: %c",
// line[count]);
count += ret;
}
}
}
// Print the current time to the output buffer.
//current_time = time(NULL);
//tp = localtime(¤t_time);
//strftime( &frame.data[1 * frame.x],
// frame.x,
// "%Y/%m/%d %H:%M:%S",
// tp);
// Print the command line.
line[count] = '\0';
snprintf( frame.data,
frame.x,
"OS1Shell -> %s",
line);
draw_frame(&frame);
} while (1);
// Restore terminal and free resources.
tcsetattr(STDIN_FILENO, TCSANOW, &tty_old);
free(frame.data);
int n = 0;
while (n < 21) {
free(cmdHistory[n]);
++n;
}
free(cmdHistory);
return (0);
}
Any help getting it to act more like bash would be highly appreciated! Part of the credit is for using stderr correctly anyways, so it would definitely help to take the stdin/stdout/stderr approach.
It looks to me it already is going to STDOUT
fprintf( stdout,
"\033[%d;%dH\033[0m\033[%dm%.*s",
row + 1,
0,
attrib, frame->x, data);
fflush(stdout);