multiple processes via socketpair hang sometimes - c

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.

Related

Question about sharing mmapped area between 2 different processes

I'm trying to share mmapped area in 2 processes.
In my program, I create memory_update() process and memory_read() process.
This memory_update() process update mmapped area and tried to read that area in memory_read() process.
But I got when I tried to read the mmapped area.
So far, I don't have good luck to find a solution for this problem.
If you have any idea, please leave your comments.
Here is the source code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define COLUMN 80
#define ROW 10
#define BUFSIZE 80
#define SHM_KEY 0x9998
struct _row {
int32_t flag;
unsigned char *buffer;
};
typedef enum {
DATA_READY,
DATA_RESET
} msg_type_t;
struct _ipc_message {
msg_type_t type;
int32_t value;
};
typedef struct _ipc_message ipc_message_t;
typedef struct _row row_t;
int32_t format_number_string(char *buf, int32_t num)
{
sprintf(buf, "%02d", num);
return 0;
}
int32_t update_row(char *buf, char *str)
{
strncpy(buf, str, 80);
return 0;
}
int32_t print_row(char *buf)
{
printf("print_row buf = %p\n", (void *)buf);
printf("%s\n", buf);
return 0;
}
int32_t memory_update(int32_t sk)
{
row_t *p_row;
ipc_message_t msg;
unsigned char *shared_mem;
unsigned char *ptr;
char rbuf[BUFSIZE];
char nbuf[3];
int32_t shmid;
int32_t i;
int32_t ret;
int32_t fd;
shmid = shmget(SHM_KEY, ROW * sizeof(row_t), 0644 | IPC_CREAT);
if (shmid == -1)
{
perror("Shared Memory Error");
return -1;
}
/* Attach Shared Memory */
shared_mem = shmat(shmid, NULL, 0);
if (shared_mem == (void *)-1)
{
perror("Shared Memory Attach Error");
return -1;
}
fd = open("testfile", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0)
{
perror("File Open Error");
}
ptr = mmap(
NULL,
COLUMN * ROW,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0);
printf("ptr = %p\n", (void *)ptr);
for (i = 0 ; i < ROW ; i++)
{
format_number_string(nbuf, i);
memset(rbuf, 0x20, BUFSIZE);
sprintf(rbuf, "LINE %s :", nbuf);
rbuf[strlen(rbuf)] = ' ';
rbuf[BUFSIZE-1] = 0x0;
update_row(&ptr[i * COLUMN], rbuf);
}
for (i = 0 ; i < ROW ; i++)
{
p_row = (row_t *)&shared_mem[i * sizeof(row_t)];
p_row->flag = 0x99;
p_row->buffer = &ptr[i * COLUMN];
// print_row(p_row->buffer);
}
msg.type = DATA_READY;
msg.value = 0;
send(sk, (void *)&msg, sizeof(ipc_message_t), 0);
i = 0;
for ( ; i < ROW ; i++)
{
p_row = (row_t *)&shared_mem[i * sizeof(row_t)];
if (p_row->flag == 0x0)
{
printf("row[%d] has processed\n", i);
}
else
{
i--;
sleep(1);
}
}
/* Detach Shared Memory */
ret = shmdt(shared_mem);
if (ret == -1)
{
perror("Shared Memory Detach Error");
return -1;
}
ret = munmap(ptr, COLUMN * ROW);
if (ret != 0)
{
printf("UnMapping Failed\n");
return -1;
}
close(fd);
return 0;
}
int32_t memory_read(int32_t sk)
{
row_t *p_row;
ipc_message_t msg;
unsigned char *shared_mem;
int32_t shmid;
int32_t ret;
int32_t i;
while (1)
{
ret = recv(sk, (void *)&msg, sizeof(ipc_message_t), 0);
if (ret < 0)
{
perror("recv error");
return -1;
}
if (msg.type != DATA_READY)
{
continue;
}
else
{
break;
}
}
shmid = shmget(SHM_KEY, ROW * sizeof(row_t), 0644 | IPC_CREAT);
if (shmid == -1)
{
perror("Shared Memory Error");
return -1;
}
/* Attach Shared Memory */
shared_mem = shmat(shmid, NULL, 0);
if (shared_mem == (void *)-1)
{
perror("Shared Memory Attach Error");
return -1;
}
for (i = 0 ; i < ROW ; i++)
{
p_row = (row_t *)&shared_mem[i * sizeof(row_t)];
printf("memory_read process [%d]\n", i);
print_row(p_row->buffer);
p_row->flag = 0x0;
sleep(1);
}
/* Detach Shared Memory */
ret = shmdt(shared_mem);
if (ret == -1)
{
perror("Shared Memory Detach Error");
return -1;
}
return 0;
}
int32_t main(void)
{
pid_t pid;
int32_t sp[2];
int32_t ret;
static const int32_t ps = 0;
static const int32_t cs = 1;
ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sp);
if (ret == -1)
{
perror("socketpair");
return -1;
}
pid = fork();
if (pid == 0)
{
close(sp[ps]);
memory_update(sp[cs]);
}
else
{
close(sp[cs]);
memory_read(sp[ps]);
}
return 0;
}
And this is the output.
$ ./mmm
ptr = 0x7fdc214a8000
memory_read process [0]
print_row buf = 0x7fdc214a8000
Segmentation fault (core dumped)
Modifed code to mmap before forking.
And it is working as expected.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define COLUMNS 80
#define ROWS 60
#define BUFSIZE 80
typedef enum {
DATA_PRODUCED,
DATA_CONSUMED
} msg_type_t;
struct _ipc_message {
msg_type_t type;
int32_t value;
};
typedef struct _ipc_message ipc_message_t;
int32_t memory_update(int32_t sk, char *buf)
{
ipc_message_t msg;
int32_t ret;
char *msg2 = "updated buffer";
printf("memory_update : 1.buf = %s\n", buf);
memset(buf, 0, 80);
strncpy(buf, msg2, strlen(msg2));
buf[strlen(msg2)] = 0;
printf("memory_update : 2.buf = %s\n", buf);
printf("memory_update : send message from memory_update process\n");
msg.type = DATA_PRODUCED;
msg.value = 0;
send(sk, (void *)&msg, sizeof(ipc_message_t), 0);
while (1)
{
printf("memory_update : receive message from memory_read process\n");
ret = recv(sk, (void *)&msg, sizeof(ipc_message_t), 0);
if (ret < 0)
{
perror("recv error");
return -1;
}
if (msg.type != DATA_CONSUMED)
{
continue;
}
else
{
break;
}
}
printf("memory_update : 3.buf = %s\n", buf);
return 0;
}
int32_t memory_read(int32_t sk, char *buf)
{
ipc_message_t msg;
int32_t ret;
int32_t i;
char *msg3 = "buffer processed";
printf("memory_read : 1.buf = %s\n", buf);
while (1)
{
printf("memory_read : receive message from memory_update process\n");
ret = recv(sk, (void *)&msg, sizeof(ipc_message_t), 0);
if (ret < 0)
{
perror("recv error");
return -1;
}
if (msg.type != DATA_PRODUCED)
{
continue;
}
else
{
break;
}
}
printf("memory_read : 2.buf = %s\n", buf);
memset(buf, 0, 80);
strncpy(buf, msg3, strlen(msg3));
buf[strlen(msg3)] = 0;
printf("memory_read : 3.buf = %s\n", buf);
printf("memory_read : send message from memory_update process\n");
msg.type = DATA_CONSUMED;
msg.value = 0;
send(sk, (void *)&msg, sizeof(ipc_message_t), 0);
return 0;
}
int32_t main(void)
{
pid_t pid;
int32_t sp[2];
int32_t ret;
int32_t fd;
int32_t i;
char *ptr;
char *msg1 = "initial message";
static const int32_t ps = 0;
static const int32_t cs = 1;
fd = open("80bytes", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0)
{
perror("File Open Error");
}
ptr = mmap(
NULL,
80,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
0);
memset(ptr, 0, 80);
strncpy(ptr, msg1, strlen(msg1));
ptr[strlen(msg1)] = 0;
printf("ptr = %s\n", ptr);
ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sp);
if (ret == -1)
{
perror("socketpair");
return -1;
}
pid = fork();
if (pid == 0) /* child process */
{
close(sp[ps]);
memory_update(sp[cs], ptr);
}
else /* parent process */
{
close(sp[cs]);
memory_read(sp[ps], ptr);
ret = munmap(ptr, 80);
if (ret != 0)
{
printf("UnMapping Failed\n");
return -1;
}
close(fd);
}
return 0;
}

C - little code, file descriptor, segmentation fault

I have write this code but i have a problem
first i have a function that create a file descriptor (fd)
int fd;//global
static int init_fd(int fd) {
remove("file descriptor.txt");//if yet present
fd = open("file descriptor.txt", O_WRONLY | O_CREAT, 0666);
if (fd == -1) {
printf("Error in opening the file descriptor!\n");
exit(0);
}
return fd;
}
the second function is a handler function
static int handler(struct connection *conn, enum event ev) {
...
int i;
for (i = 0; i < array_size; i++) {
if (!strncmp(conn->uri, uri_array[i], strlen(uri_array[i]))) {
func_array[i](conn->request_method, conn->uri, NULL, init_fd(fd));
close(fd);
fd = open("file descriptor.txt", O_RDONLY);
ret = read(fd, &buf, BUFSIZ);
if (ret == -1) {
printf("Error in reading!\n");
exit(0);
}
...
}
with fun_array is a pointer to function
httpCallback_t func_array[MAXARRAY];
and the function is
void http_serve1(const char *method, const char *path, const httpOptions_t *options, int fd) {
const char *string = "All is ok1!";
int ret_value;
// send header: 200 OK
ret_value = sendHeaders(fd, TIMEOUT_SEC, NETHTTP_HTTP_HEADER_200, NETHTTP_Content_Type_text_html_utf8, NETHTTP_CRLF, NULL);
// close the file descriptor
close(fd);
}
and the function sendHeaders is
size_t sendHeaders(int fd, int seconds, const char* header1, ...) {
va_list args;
va_start(args, header1);
size_t totalSize = 0;
const char* hdr = header1;
while (hdr != NULL) {
size_t result = sendHeaders(fd, seconds, hdr, NULL); // segmentation fault
if (result < 0) {
return result;
}
totalSize += result;
hdr = va_arg(args, const char*);
va_end(args);
return totalSize;
}
if (size == SIZE) {
setErrorCode(ERROR);
return ERROR;
}
size_t sizewrite = 1024;
tmp[size] = strdup(hdr);
write(fd, tmp, sizewrite);
setErrorCode(SUCCESS);
return SUCCESS;
}
my problem is that my code create a file descriptor, but it doesn't write inside, and during the run i have problem with segmentation fault. anyone have a suggest?

C socket: recv and send all data

I would like to obtain a behavior similar to this:
Server run
Client run
Client type a command like "help" or other
Server responds appropriately
go to 3
The problem is that when my function excCommand("help") run just a little text is received and printed.
My text file is this:
COMMAND HELP:
help - Display help
quit - Shutdown client
only COMMAND HELP is printed.
Another problem is that when i type a command nothing is printed and after 2 command client exit.
This is the piece in particular:
while (quit)
{
getLine("client> ", command, 10);
if (strcmp(command, "quit") == 0)
quit = 0;
else
excCommand(command);
}
This is the server:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
int main(int argc, char *argv[])
{
if (argc != 2)
ErrorWithUserMessage("Parameter(s)", "<Server Port>");
char *service = argv[1];
int servSock = SetupTCPServerSocket(service);
if (servSock < 0)
ErrorWithUserMessage("SetupTCPServerSocket() failed: ", "unable to establish");
unsigned int childProcessCount = 0;
while (1)
{
int clntSock = AcceptTCPConnection(servSock);
pid_t processID = fork();
if (processID < 0)
ErrorWithSystemMessage("fork() failed");
else if (processID == 0)
{
close(servSock);
HandleTCPClient(clntSock);
exit(EXIT_SUCCESS);
}
printf("with child process: %d\n", processID);
close(clntSock);
childProcessCount++;
//clean up zombies
while (childProcessCount)
{
processID = waitpid((pid_t) - 1, NULL, WNOHANG);
if (processID < 0)
ErrorWithSystemMessage("waitpid() failed");
else if (processID == 0)
break;
else
childProcessCount--;
}
}
}
Handler:
void HandleTCPClient(int clntSock)
{
char buffer[BUFSIZE];
ssize_t numBytesRcvd = recv(clntSock, buffer, BUFSIZE, 0);
buffer[numBytesRcvd] = '\0';
if (numBytesRcvd < 0)
ErrorWithSystemMessage("recv() failed");
if (strcmp(buffer, "help") == 0)
{
FILE *fp = fopen("help.txt", "r");
if (fp)
{
char line[128];
while (fgets(line, sizeof(line), fp) != NULL)
{
if (send(clntSock, line, sizeof(line), 0) < 0)
ErrorWithSystemMessage("send() failed");
}
fclose(fp);
}
}
close(clntSock);
}
and this is my client:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "common.h"
int sock;
void getLine(char *message, char *buf, int maxLen)
{
printf("%s", message);
fgets(buf, maxLen, stdin);
buf[strlen(buf) - 1] = 0;
}
void excCommand(char *command)
{
if ( send(sock, command, strlen(command), 0) < 0)
ErrorWithSystemMessage("send() failed");
char replyMessage[BUFSIZE];
ssize_t numBytesRecv = 0;
do
{
numBytesRecv = recv(sock, replyMessage, BUFSIZE, 0);
if ( numBytesRecv < 0)
ErrorWithSystemMessage("recv() failed");
printf("%s\n", replyMessage);
memset(&replyMessage, 0, sizeof(replyMessage));
}
while (numBytesRecv > 0);
}
void PrintFile(const char *filename)
{
FILE *fp;
fp = fopen(filename, "r");
if (fp)
{
char line[128];
while (fgets(line, sizeof(line), fp) != NULL)
fputs(line, stdout);
fputs("\n", stdout);
fclose(fp);
}
}
int main(int argc, char *argv[])
{
int quit = 1;
char command[10];
if (argc < 2 || argc > 3)
{
ErrorWithUserMessage("Parameter(s)", "<Server Address> <Server Port>");
}
char *server = argv[1];
char *service = argv[2];
sock = SetupTCPClientSocket(server, service);
if (sock < 0)
ErrorWithUserMessage("SetupTCPClientSocket() failed: ", "unable to connect");
printf("Connection established!\n\n");
PrintFile("menu.txt");
excCommand("help");
while (quit)
{
getLine("client> ", command, 10);
if (strcmp(command, "quit") == 0)
quit = 0;
else
excCommand(command);
}
fputs("\n", stdout);
close(sock);
exit(EXIT_SUCCESS);
}
sorry for being so long-winded
The recv() and send() functions do not guarantee to send/recv all data (see man recv, man send)
You need to implement your own send_all() and recv_all(), something like
bool send_all(int socket, void *buffer, size_t length)
{
char *ptr = (char*) buffer;
while (length > 0)
{
int i = send(socket, ptr, length);
if (i < 1) return false;
ptr += i;
length -= i;
}
return true;
}
The following guide may help you Beej's Guide to Network Programming
Usual problems.
void excCommand(char *command)
{
if ( send(sock, command, strlen(command), 0) < 0)
ErrorWithSystemMessage("send() failed");
char replyMessage[BUFSIZE];
ssize_t numBytesRecv = 0;
do
{
numBytesRecv = recv(sock, replyMessage, BUFSIZE, 0);
if ( numBytesRecv < 0)
ErrorWithSystemMessage("recv() failed");
printf("%s\n", replyMessage);
Invalid. numBytesRecv could have been zero, in which case there is no message at all, otherwise at this point must be positive, as you've already tested for negative, and it indicates the actual length of the message, which isn't necessarily null-terminated. Change to:
if (numBytesRecv == 0)
break;
printf("%.*s\n", numBytesRecv, replyMessage);
and then:
memset(&replyMessage, 0, sizeof(replyMessage));
Pointless. Remove.
}
while (numBytesRecv > 0);
At this point you should check for numBytesRecv < 0 and call perror() or one of its friends.
I choose to send before each send() if i have to continue or not.
so i first have 3 define
#define BUFFSIZE 1024
#define CONT "CONT"
#define DONE "DONE"
Then to send my data
int send_to_socket(int sock, char *msg)
{
size_t len;
int ret[2];
len = strlen(msg);
ret[0] = send(sock, (len <= BUFFSIZE) ? DONE : CONT, 4, 0);
ret[1] = send(sock, msg, BUFFSIZE, 0);
if (ret[0] <= 0 || ret[1] <= 0)
{
perror("send_to_socket");
return (-1);
}
if (len > BUFFSIZE)
return (send_to_socket(sock, msg + BUFFSIZE));
return (1);
}
And to receive it :
char *recv_from_socket(int cs)
{
char state[5];
char buff[BUFFSIZE+1];
char *msg;
int ret[2];
msg = NULL;
while (42)
{
bzero(state, 5);
bzero(buff, BUFFSIZE+1);
ret[0] = recv(cs, state, 4, 0);
ret[1] = recv(cs, buff, BUFFSIZE, 0);
if (ret[0] <= 0 || ret[1] <= 0)
{
perror("recv_from_socket");
return (NULL);
}
// strfljoin() is selfmade
// join the string and free the left argument to prevent memory leaks.
// return fresh new string
msg = (msg) ? ft_strfljoin(msg, buff) : strdup(buff);
if (strncmp(state, DONE, 4) == 0)
break ;
i++;
}
return (msg);
}

Segmentation fault when running simple shell program

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

Was given this code on SO, but how to make it output to stdout instead?

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(&current_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);

Resources