Reading and Writing to a Named Pipe - c

I'm working with two programs. The customer.c program writes an int to a named pipe and the bank.c reads the pipe and prints the int. The customer will choose one of two named pipes, "atm1" and "atm2".
Ultimately I would like to run two customer.c programs, one for each pipe at the same time, but I have some issues with the writing and reading to the named pipe.
If I just run bank.c and one customer.c I don't get any output.
If I run bank.c and two customer.c the output doesn't always print or is out of order when it does.
I tried to use fsync() to flush but that didn't work either.
customer.c
int main(int argc, char *argv[]){
int fd, num =0;
if((fd = open(argv[1], O_WRONLY)) == -1){
...
}
while(1){
printf("Enter a integer:\n");
scanf("%d", &num);
if(num < 0){
break;
}
if(write(fd, &num, sizeof(num)) <= 0){...}
fsync(fd);
}
close(fd);
return 0;
}
bank.c
int main(){
int fd, sd, num=0, sret, fret, maxfd;
//fd_set readfds;
//struct timeval timeout;
if(mkfifo("atm1", 0666) == -1){...}
if(mkfifo("atm2", 0666) == -1){...}
if((fd = open("atm1", O_RDONLY)) == -1){...}
if((sd = open("atm2", O_RDONLY)) == -1){...}
while(1){
if((sret = read(sd, &num, sizeof(num))) > 0){
printf("%d\n", num);
}
if((fret = read(fd, &num, sizeof(num))) > 0){
printf("%d\n", num);
}
if(sret <= 0 && fret <= 0){
break;
}
}
close(fd);
close(sd);
return 0;
}
Any pointer?

you need to make multi-threads server for multiple same time recv. and require to check pipe name is already exist.
blank.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
void *client(void *data)
{
char *pname = (char*) data ;
int sd =0 ;
int num ;
int sret=0 ;
if((sd = open(pname, O_RDONLY)) == -1) {
printf("open failed:%s\n", pname) ;
return NULL ;
}
printf("client[%d] start\n", sd) ;
while(1){
num=0 ;
if((sret = read(sd, &num, sizeof(num))) > 0){
printf("[%d] recv:%d\n", sd, num);
}
if(sret <= 0) {
break;
}
}
close(sd);
printf("client[%d] end\n", sd) ;
return NULL;
}
int main(){
int status ;
pthread_t t1, t2 ;
if(mkfifo("atm1", 0666) == -1) {
printf("mkfifio failed : %d\n", errno) ;
if (errno==EEXIST ) {
printf("already exist. ignore\n") ;
}
else
return -1;
}
if(mkfifo("atm2", 0666) == -1) {
printf("mkfifio failed2 : %d\n", errno) ;
if (errno==EEXIST ) {
printf("already exist. ignore\n") ;
}
else
return -1;
}
pthread_create(&t1, NULL, client, (void*)"atm1") ;
pthread_create(&t2, NULL, client, (void*)"atm2") ;
pthread_join(t1, (void**)&status) ;
pthread_join(t2, (void**)&status) ;
return 0;
}
you can access the atm1 and atm2 at the same time.
./customer atm1
./customer atm2

Related

Why can't I simulate auto-completion with '\t' in busybox

I want to use "yp\t\n\0" to run “ypdomainname” command by exploiting auto-completion in busybox, but it failed. my code and result are below:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#define DEFAULT_BUSYBOX_PATH "/bin/busybox"
#define MAX_BUF 1000
int main()
{
int fd[2];
pid_t pid;
FILE *file;
int status;
if(pipe(fd) < 0){
fprintf(stderr, "pipe error!\n");
return -1;
}
if((pid = fork()) < 0){
fprintf(stderr, "pipe error!\n");
}else if(pid == 0){ //child
close(fd[1]);
int fd_output;
fd_output = open("result", O_CREAT | O_RDWR, 777);
if(fd_output != STDOUT_FILENO){
if(dup2(fd_output, STDOUT_FILENO) != STDOUT_FILENO)
fprintf(stderr, "dup2 error to stdout\n");
}
if(fd[0] != STDIN_FILENO){
if(dup2(fd[0], STDIN_FILENO) != STDIN_FILENO)
fprintf(stderr, "dup2 error to stdin\n");
}
execl(DEFAULT_BUSYBOX_PATH, DEFAULT_BUSYBOX_PATH, "ash", NULL);
close(fd[0]);
close(fd_output);
return 0;
}else{ //parent
close(fd[0]);
char buf[MAX_BUF] = "yp";
buf[2] = '\t';
buf[3] = '\n';
buf[4] = '\0';
write(fd[1], buf, strlen(buf));
close(fd[1]);
return 0;
}
}
Result of my code
What makes me confused is that the character is not changed in function lineedit_read_key() in file lineedit.c and it will run the function input_tab() when the character is '\t'.
input_tab will be executed when character is '\t'
Recently, i took some time to learn about terminal to simulate auto-completion, but i still failed. My code is below:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <signal.h>
#include <pty.h>
#define DEFAULT_BUSYBOX_PATH "/bin/busybox"
#define MAX_BUF 1000
#define BUFFSIZE 512
typedef void Sigfunc(int);
static void sig_term(int);
static volatile sig_atomic_t sigcaught;
ssize_t writen(int fd, const void *ptr, size_t n){
size_t nleft;
ssize_t nwritten;
nleft = n;
while(nleft > 0){
if((nwritten = write(fd, ptr, nleft)) < 0){
if(nleft == n){
return(-1);
}else{
break;
}
}else if(nwritten == 0) {
break;
}
nleft -= nwritten;
ptr += nwritten;
}
return(n - nleft);
}
Sigfunc *signal_intr(int signo, Sigfunc *func){
struct sigaction act;
struct sigaction oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
if (sigaction(signo, &act, &oact) < 0)
return(SIG_ERR);
return(oact.sa_handler);
}
int ptym_open(char *pts_name, int pts_namesz)
{
char ptr[50];
int fdm;
int err;
if((fdm = posix_openpt(O_RDWR)) < 0){
return(-1);
}
if(grantpt(fdm) < 0){
goto errout;
}
if(unlockpt(fdm) < 0){
goto errout;
}
if(ptsname_r(fdm, ptr, 50) != 0){
goto errout;
}
strncpy(pts_name, ptr, pts_namesz);
pts_name[pts_namesz - 1] = '\0';
return(fdm); /* return fd of master */
errout:
err = errno;
close(fdm);
errno = err;
return(-1);
}
int ptys_open(char *pts_name){
int fds;
if((fds = open(pts_name, O_RDWR)) < 0)
return(-1);
return(fds);
}
pid_t pty_fork(int *ptrfdm, char *slave_name, int slave_namesz, const struct termios *slave_termios, const struct winsize *slave_winsize){
int fdm, fds;
pid_t pid;
char pts_name[20];
if((fdm = ptym_open(pts_name, sizeof(pts_name))) < 0){
fprintf(stderr, "can't open master pty: %s, error %d", pts_name, fdm);
}
if(slave_name != NULL) {
strncpy(slave_name, pts_name, slave_namesz);
slave_name[slave_namesz - 1] = '\0';
}
if((pid = fork()) < 0) {
return(-1);
}else if (pid == 0) { /* child */
if(setsid() < 0){
fprintf(stderr, "setsid error");
}
if((fds = ptys_open(pts_name)) < 0){
fprintf(stderr, "can't open slave pty");
}
close(fdm);
if(slave_termios != NULL) {
if (tcsetattr(fds, TCSANOW, slave_termios) < 0)
fprintf(stderr, "tcsetattr error on slave pty");
}
if(slave_winsize != NULL) {
if (ioctl(fds, TIOCSWINSZ, slave_winsize) < 0)
fprintf(stderr, "TIOCSWINSZ error on slave pty");
}
if(dup2(fds, STDIN_FILENO) != STDIN_FILENO){
fprintf(stderr, "dup2 error to stdin");
}
if(dup2(fds, STDOUT_FILENO) != STDOUT_FILENO){
fprintf(stderr, "dup2 error to stdout");
}
if(dup2(fds, STDERR_FILENO) != STDERR_FILENO){
fprintf(stderr, "dup2 error to stderr");
}
if(fds != STDIN_FILENO && fds != STDOUT_FILENO && fds != STDERR_FILENO){
close(fds);
}
return(0);
} else { /* parent */
*ptrfdm = fdm;
return(pid);
}
}
void loop(int ptym, int ignoreeof)
{
pid_t child;
int nread;
char buf[BUFFSIZE];
if((child = fork()) < 0) {
fprintf(stderr, "fork error");
}else if(child == 0) {
/*for ( ; ; ){
if((nread = read(STDIN_FILENO, buf, BUFFSIZE)) < 0){
fprintf(stderr, "read error from stdin");
}else if(nread == 0){
break;
}
if(writen(ptym, buf, nread) != nread)
fprintf(stderr, "writen error to master pty");
}*/
char *temp_buf = "yp\t\n";
if(writen(ptym, temp_buf, strlen(temp_buf)) != strlen(temp_buf)){
fprintf(stderr, "writen error to master pty");
}
if(ignoreeof == 0){
kill(getppid(), SIGTERM);
}
exit(0);
}
if (signal_intr(SIGTERM, sig_term) == SIG_ERR)
fprintf(stderr, "signal_intr error for SIGTERM");
for( ; ; ){
if ((nread = read(ptym, buf, BUFFSIZE)) <= 0){
}
//printf("nread = %d\n", nread);
if (writen(STDOUT_FILENO, buf, nread) != nread){
fprintf(stderr, "writen error to stdout");
}
}
if (sigcaught == 0){
printf("sigcaught == 0 and kill child\n");
kill(child, SIGTERM);
}
}
static void sig_term(int signo)
{
sigcaught = 1;
}
int main(int argc, char *argv[]){
int fd[2];
pid_t pid;
FILE *file;
int status;
int fdm;
int ignoreeof;
char slave_name[40];
struct termios orig_termios;
struct winsize size;
pid = pty_fork(&fdm, slave_name, sizeof(slave_name), &orig_termios, &size);
if(pid < 0){
fprintf(stderr, "fork error!\n");
}else if(pid == 0){ //child
if(execl(DEFAULT_BUSYBOX_PATH, DEFAULT_BUSYBOX_PATH, "ash", NULL) < 0){
fprintf(stderr, "can't execute: %s", DEFAULT_BUSYBOX_PATH);
}
/*if(execvp(argv[1], &argv[1]) < 0){
fprintf(stderr, "can't execute: %s", argv[1]);
}*/
}
loop(fdm, ignoreeof);
}
like the result of my first try, the result is: ash: yp: not found.
Your code fails because a pipe is not a terminal. Many programs will use isatty(3) and alike to detect if the standard input is connected to a terminal and adjust their behaviour depending on the result.
What you can do is to open a pseudo terminal pair using openpty(3) and run the command with the slave duplicated to its standard input, output and error descriptors, and using the master to communicate with it. Unfortunately I have no time right now writing a full solution as it is rather intricate; I've done it ever in Python and it was tricky even there.

bad file descriptor in c program with forks

this program is supposed to simulate a posix shell in regards to commands with pipes. The example I've tried to simulate and wanna make work is "ls | nl", but it doesn't and I can't figure out why. I've debugged this code for many hours with no success.
I get the error: "nl: input error: Bad file descriptor", and when I've tried not closing any of the file descriptors or closing only some (or in only one of the forks, or only the parent, etc...), and the errors change, or it works but then nl keeps waiting for input. Anyways, I'm pretty sure the errors are in fork_cmd or fork_cmds and has to do with close.
I've included all the code. I know there's nothing wrong with parser.h. I know this is pretty shitty code but it should still work I think.
I'm probably blind, but I would really appreciate it if someone could help me figure it out. Hopefully it's something that I and maybe others can learn something from.
#include "parser.h"
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdbool.h>
#define READ 0
#define WRITE 1
void fork_error() {
perror("fork() failed)");
exit(EXIT_FAILURE);
}
void close_error() {
perror("Couldn't close file descriptor");
exit(EXIT_FAILURE);
}
void fork_cmd(char* argv[], int n, int read_pipe[2], int write_pipe[2], int (*all_fds)[2]) {
pid_t pid;
switch (pid = fork()) {
case -1:
fork_error();
case 0:
if (read_pipe != NULL) {
if (dup2(read_pipe[READ], STDIN_FILENO) < 0) {
perror("Failed to redirect STDIN to pipe");
exit(EXIT_FAILURE);
}
}
if (write_pipe != NULL) {
if (dup2(write_pipe[WRITE], STDOUT_FILENO) < 0) {
perror("Failed to redirect STDOUT to pipe");
exit(EXIT_FAILURE);
}
}
for (int i = 0; i < n - 1; i++) {
if (close(all_fds[i][READ]) == -1 || close(all_fds[i][WRITE] == -1)) {
close_error();
}
}
execvp(argv[0], argv);
perror("execvp");
exit(EXIT_FAILURE);
default:
printf("Pid of %s: %d\n", argv[0], pid);
break;
}
}
void fork_cmds(char* argvs[MAX_COMMANDS][MAX_ARGV], int n, int (*fds)[2]) {
for (int i = 0; i < n; i++) {
if (n == 1) {
fork_cmd(argvs[i], n, NULL, NULL, fds);
}
// n > 1
else if (i == 0) {
fork_cmd(argvs[i], n, NULL, fds[i], fds);
}
else if (i == n - 1) {
fork_cmd(argvs[i], n, fds[i - 1], NULL, fds);
}
else {
fork_cmd(argvs[i], n, fds[i - 1], fds[i], fds);
}
}
for (int i = 0; i < n - 1; i++) {
if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1)) {
close_error();
}
}
}
void get_line(char* buffer, size_t size) {
getline(&buffer, &size, stdin);
buffer[strlen(buffer)-1] = '\0';
}
void wait_for_all_cmds(int n) {
// Not implemented yet!
for (int i = 0; i < n; i++) {
int status;
int pid;
if ((pid = wait(&status)) == -1) {
printf("Wait error");
} else {
printf("PARENT <%ld>: Child with PID = %ld and exit status = %d terminated.\n",
(long) getpid(), (long) pid, WEXITSTATUS(status));
}
}
}
int main() {
int n;
char* argvs[MAX_COMMANDS][MAX_ARGV];
size_t size = 128;
char line[size];
printf(" >> ");
get_line(line, size);
n = parse(line, argvs);
// Debug printouts.
printf("%d commands parsed.\n", n);
print_argvs(argvs);
int (*fds)[2] = malloc(sizeof(int) * 2 * (n - 1)); // should be pointer to arrays of size 2
for (int i = 0; i < n - 1; i++) {
if (pipe(fds[i]) == -1) {
perror("Creating pipe error"); // Creating pipe error: ...
exit(EXIT_FAILURE);
}
printf("pipe %d: read: %d, write: %d\n", i, fds[i][READ], fds[i][WRITE]);
}
fork_cmds(argvs, n, fds);
wait_for_all_cmds(n);
exit(EXIT_SUCCESS);
}
The problem was that one of the parenthesis was at the wrong place in both fork_cmd and fork_cmds, it should be like this of course: close(fds[i][WRITE]). This was the original code:
for (int i = 0; i < n - 1; i++) {
if (close(fds[i][READ]) == -1 || close(fds[i][WRITE] == -1))<--
{
close_error();
}
}

linux - exec'd program not terminating

I am writing program in C on Linux which has to fork 2 children.
First child will send two random numbers over pipe to the second child. It will listen for SIGUSR1 signal and will then terminate.
The second child will duplicate(dup2) pipe input as STDIN and file fp as STDOUT. It will then execl program which will print out some data according to its input and end.
My problem is, that the execl'd program will never terminate and I don't know why. Any help or tips will be appreciated.
main.c (parent):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
const int BUFFER_SIZE = 30;
int pipefd[2] = {0,0};
int parent_pid = 0;
int first_pid = 0;
int second_pid = 0;
int sleep_time = 5;
int debug = 0;
FILE *fp;
void parent_func() {
int wstatus = 0;
sleep(sleep_time);
kill(first_pid, SIGUSR1);
wait(&wstatus);
waitpid(second_pid, &wstatus, 0);
}
static void sigusr1_handler(int sig) {
if (sig == SIGUSR1) {
fputs("TERMINATED", stderr);
close(pipefd[1]);
exit(0);
}
}
void first_func() {
struct sigaction act;
char buffer[BUFFER_SIZE];
close(pipefd[0]);
memset(&act, '\0', sizeof(act)); // clear the sigaction struct
act.sa_handler = &sigusr1_handler; // sets function to run on signal
if (sigaction(SIGUSR1, &act, NULL) < 0) { // assign sigaction
fputs("cannot assign sigaction - exiting...", stderr);
exit(1);
}
while (1) {
sprintf(buffer, "%d %d\n", rand(), rand());
write(pipefd[1], buffer, strlen(buffer));
puts(buffer);
sleep(1);
}
}
void second_func() {
close(pipefd[1]);
fp = fopen("out.txt", "w");
char buf[30];
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
//dup2(fileno(fp), STDOUT_FILENO);
execl("./test", "", NULL);
perror("Error");
}
int main(int argc, char *argv[]) {
int fork_val = 0;
parent_pid = getpid();
if (pipe(pipefd)) {
fputs("cannot create pipe - exiting...", stderr);
return 1;
}
if (debug) {
sleep_time *= 10;
}
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
first_func();
} else {
first_pid = fork_val;
if ((fork_val = fork()) == -1) {
fputs("cannot fork process - exiting...", stderr);
return 1;
} else if (fork_val == 0) {
second_func();
} else {
second_pid = fork_val;
parent_func();
}
}
fclose(fp);
exit(0);
}
test.c (the execl'd file):
#include "nd.h"
#include "nsd.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
int num1 = 0;
int num2 = 0;
char buffer[100];
while (fgets(buffer, 100, stdin) != NULL) {
if (sscanf(buffer, "%d %d", &num1, &num2) == 2) {
(num1 < 0) ? num1 = (num1 * -1) : num1;
(num2 < 0) ? num2 = (num2 * -1) : num2;
if (num1 == 1 || num2 == 1) {
puts("1");
} else if (num1 == num2) {
if (nd(num1) == 1) {
puts("prime");
} else {
printf("%d\n", num1);
}
} else if (nd(num1) == 1 && nd(num2) == 1) {
puts("prime");
} else {
printf("%d\n", nsd(num1, num2));
}
} else {
fputs("error\n", stderr);
}
}
fputs("DONE", stderr);
exit(0);
}
To be able to detect an end of file from a pipe you need to read from a empty pipe with no writer (no process with an open for writing descriptor).
As your writer (first_func()) never closes its descriptor and always writes something in a never ending loop the reader will either wait for some data or read some data.
Be also careful about closing non useful descriptors, if not you may encounter some problems with pipes, such has a single process that is a reader and a writer, so being unable to detect the end of file...

working with named pipes and semaphores in linux

I've been trying to get my program to work for several hours now and I just can't fgure out what's wrong with my code. It's about passing a variable between processess using pipes. Each process increments it M times. The program works perfectly when I use shared memory, but when I change it to using pipes it's a disaster. Creating or using named pipes doesn't seem to work at all, or I guess I'm just doing it the wrong way. Here's the source code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory.h>
#include <fcntl.h>
#include <sys/stat.h>
#define PIPE_NAME "MY_PIPE"
#define N 5
#define M 10
struct sembuf operations;
int semid;
key_t key;
int marker;
void semWait(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop wait\n");
exit(-1);
}
}
void semPost(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop post\n");
exit(-1);
}
}
void worker(int id) {
int j, nmarker;
int fd = open(PIPE_NAME, O_RDWR);
read(fd, &nmarker, sizeof(int));
for (j = 0 ; j < M; j++) {
semWait(semid, id);
nmarker = nmarker + 1 ;
printf("%d ", marker);
semPost(semid, N);
}
write(fd, &nmarker, sizeof(nmarker));
close(fd);
}
main() {
int i, tempPID;
int sarray[N+1] = {0};
key = 23;
marker = 0;
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1) {
perror("ERROR: semget\n");
exit(-1);
}
if ((semctl(semid, N+1, SETALL, sarray)) < 0) {
perror("ERROR: semctl - val\n");
exit(-1);
}
if(mkfifo(PIPE_NAME, S_IFIFO | 0666) < 0) {
perror("ERROR:pipe\n");
exit(-1);
}
int fd;
if( fd = open(PIPE_NAME, O_WRONLY) < 0 ){
perror("ERROR:open\n");
exit(-1);
}
write(fd, &marker, sizeof(marker));
close(fd);
for(i = 0; i < N; i++) {
tempPID = fork();
if (tempPID < 0) {
perror("ERROR: fork\n");
exit(-1);
}
else if (tempPID == 0) { // if child
worker(i);
exit(0);
}
}
for (i = 0 ; i < (M*N); i++) {
semPost(semid, i%N);
semWait(semid, N);
}
printf("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
unlinc(PIPE_NAME);
}
I create N worker processes and each one has to increment the marker value M times. I have to create a pool of 'sleeping' processes and waken them one by one using semaphores but it's all a blur so the current source code is all I came up with... :\
This is a version of the same program but with shared memory instead of pipes:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#define N 5
#define M 10
struct sembuf operations;
int semid;
key_t key;
int *sharedmem;
void semWait(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop wait\n");
exit(-1);
}
}
void semPost(int semid, int sempos) {
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0) {
perror("ERROR: semop post\n");
exit(-1);
}
}
void worker(int id) {
int j;
for (j = 0 ; j < M; j++) {
semWait(semid, id);
(*sharedmem)++;
semPost(semid, N);
}
}
main() {
int i, tempPID;
int sarray[N+1] = {0};
int protect = PROT_READ | PROT_WRITE;
int flags = MAP_SHARED | MAP_ANONYMOUS;
if ((key = ftok("/dev/null", 4343)) == -1) {
perror("ERROR: ftok\n");
exit(-1);
}
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1) {
perror("ERROR: semget\n");
exit(-1);
}
if ((semctl(semid, N+1, SETALL, sarray)) < 0) {
perror("ERROR: semctl - val\n");
exit(-1);
}
sharedmem = (int*)mmap(NULL, sizeof(int), protect, flags, 0, 0);
*(sharedmem) = 0;
for(i = 0; i < N; i++) {
tempPID = fork();
if (tempPID < 0) {
perror("ERROR: fork\n");
exit(-1);
}
else if (tempPID == 0) { // if child
worker(i);
exit(0);
}
}
for (i = 0 ; i < (M*N); i++) {
semPost(semid, i%N);
semWait(semid, N);
}
printf("Marker = %d\n", *sharedmem);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
munmap(sharedmem, sizeof(int));
}
Some of your problems are in the worker code - these two lines:
int fd = open(PIPE_NAME, O_RDWR);
read(fd, &nmarker, sizeof(int));
If you open the pipe for reading and writing, you are asking for trouble (IMNSHO). Open it for reading only, read it, close it. Then open it for writing only, write to it, close it. Now you have to consider where the semaphore operation should occur. You actually need to wake the next process before you try to open the pipe for writing, because the open for writing will block until there is a process available to read from it. Similarly, the process that opens for reading will block until there is a process available to write to it. So, the kernel will coordinate the processes.
You don't check the return value from open(), so you've no idea whether you got a valid file descriptor. Always check the return status of open().
You don't check the return value from read(), so you've no idea whether you got anything valid off the pipe. Always check the return status of read().
(You can decide to ignore the return status of write() if there is no meaningful error recovery possible for a failed write, but it is not a bad idea to check that it did work. You can decide to ignore the return status of close() for similar reasons, though you might not get to know about problems until you do the close().)
Continuing in the worker code:
for (j = 0 ; j < M; j++) {
semWait(semid, id);
nmarker = nmarker + 1 ;
printf("%d ", marker);
semPost(semid, N);
}
It is surprising to see you printing marker rather than nmarker; and surely, basic diagnostic technique prints the value of nmarker when it is read. You might or might not print j and nmarker on each iteration. Note that since nothing in this code increments marker, the value printed won't change.
The logic sequence here is interesting...it combines with the loop in main() most oddly. The parent process writes one value to the FIFO. Only one child gets to read that value - the rest get EOF immediately, or hang indefinitely (depending on whether you use O_RDONLY or O_RDWR in the children). Each child gets signalled to increment its value, does so, and then goes back to sleep until woken again. There is nothing that sends the incremented value to the next child. So each child is independently incrementing whatever value it chooses - which is probably garbage. With shared memory, if you had a pointer to the shared value, then the increments were seen by all processes at once - that's why it is called shared memory. But here there is no shared memory, so you have to communicate explicitly to get it to work. (I wonder if your FIFO plus shared memory implementation worked because the communication was via shared memory - by accident, in other words?)
So, if the child is to increment the variable it reads each time, it must both read the current value and write the new value each time around the loop. This would be an error-checked read, of course. You might be OK with O_RDWR because of the semaphores, but I'd personally be happier with the separate opens for read and write - on each iteration if need so be. But I haven't implemented this to check that it really does run into problems; it is simply aconventional to use O_RDWR on a FIFO.
After your child has incremented its value N times, it writes the result to the pipe.
write(fd, &nmarker, sizeof(nmarker));
close(fd);
The main program then does:
printf("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1) {
perror("ERROR: semctl free\n");
exit(-1);
}
unlinc(PIPE_NAME);
Since it has not modified marker, the value printed will be 0. You should be having the main process read the replies from each of the children.
The correct function for unlinking a FIFO is unlink() or remove().
Discussion
As noted in a comment, one problem was that opening the FIFO was blocking - no readers. However, that was far from the only problem.
The code below runs. I haven't verified that the number is being incremented as it should (but it is being incremented). I've not checked that every process is getting its turn. I've revised the error handling (one line per call instead of 3 or 4), and added a printing function that includes the PID in the output. I've error checked every system call (but none of the printing statements). I fixed a problem if (fd = open(...) < 0). As far as I could tell, closing the FIFO in the master process discarded the content written to it - so the parent no longer closes the FIFO immediately. But mainly I moved the read and write of the FIFO into the worker loop - leaving open and close outside. The code is also laced with diagnostic printing so I can see where it is going wrong when it is going wrong. I haven't done header minimization or any of a number of other cleanups that should occur. However, everything except main() is static so it doesn't have to be pre-declared. It compiles clean under:
/usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra fifocircle.c -o fifocircle
Code
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/mman.h>
#include <unistd.h>
#include <memory.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
static const char *arg0 = "undefined";
static void err_error(const char *fmt, ...)
{
int errnum = errno;
va_list args;
fflush(0);
fprintf(stderr, "%s: pid %d:", arg0, (int)getpid());
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "(%d: %s)", errnum, strerror(errnum));
fputc('\n', stderr);
exit(1);
}
static void print(const char *fmt, ...)
{
va_list args;
printf("pid %d: ", (int)getpid());
va_start(args, fmt);
vfprintf(stdout, fmt, args);
va_end(args);
fflush(0);
}
#define PIPE_NAME "MY_PIPE"
#define N 5
#define M 10
static struct sembuf operations;
static int semid;
static key_t key;
static int marker;
static void semWait(int semid, int sempos)
{
operations.sem_num = sempos;
operations.sem_op = -1;
operations.sem_flg = 0;
if (semop(semid, &operations, 1) < 0)
err_error("semop wait");
}
static void semPost(int semid, int sempos)
{
operations.sem_num = sempos;
operations.sem_op = 1;
operations.sem_flg = IPC_NOWAIT;
if (semop(semid, &operations, 1) < 0)
err_error("semop post");
}
static void worker(int id)
{
int j;
int fd = open(PIPE_NAME, O_RDWR);
if (fd < 0)
err_error("failed to open FIFO %s for read & write", PIPE_NAME);
print("Worker %d: fd %d\n", id, fd);
for (j = 0 ; j < M; j++)
{
int nmarker;
print("waiting for %d\n", id);
semWait(semid, id);
if (read(fd, &nmarker, sizeof(int)) != sizeof(int))
err_error("short read from FIFO");
print("Got %d from FIFO\n", nmarker);
nmarker = nmarker + 1 ;
if (write(fd, &nmarker, sizeof(nmarker)) != sizeof(nmarker))
err_error("short write to FIFO");
print("Wrote %d to FIFO\n", nmarker);
print("posting %d\n", id);
semPost(semid, N);
}
if (close(fd) != 0)
err_error("failed to close FIFO");
print("done\n");
}
int main(int argc, char **argv)
{
int i;
int sarray[N+1] = {0};
key = 23;
marker = 0;
arg0 = argv[0];
if (argc != 1)
err_error("Usage: %s\n", arg0);
if ((semid = semget(key , N+1, 0666 | IPC_CREAT)) == -1)
err_error("semget");
if ((semctl(semid, N+1, SETALL, sarray)) < 0)
{
perror("ERROR: semctl - val\n");
exit(-1);
}
if (mkfifo(PIPE_NAME, S_IFIFO | 0666) < 0)
err_error("failed to create FIFO %s\n", PIPE_NAME);
print("FIFO created\n");
int fd;
if ((fd = open(PIPE_NAME, O_RDWR)) < 0 )
err_error("failed to open FIFO %s\n", PIPE_NAME);
print("FIFO opened\n");
if (write(fd, &marker, sizeof(marker)) != sizeof(marker))
err_error("short write to FIFO");
print("FIFO loaded\n");
print("Master: about to fork\n");
for (i = 0; i < N; i++)
{
pid_t pid = fork();
if (pid < 0)
err_error("failed to fork");
else if (pid == 0)
{
worker(i);
exit(0);
}
}
print("Master: about to loop\n");
for (i = 0 ; i < (M*N); i++)
{
print("posting to %d\n", i%N);
semPost(semid, i%N);
print("waiting for %d\n", N);
semWait(semid, N);
}
if (close(fd) != 0)
err_error("failed to close FIFO");
print("Marker = %d\n", marker);
if (semctl( semid, 1, IPC_RMID ) == -1)
err_error("semctl remove");
if (unlink(PIPE_NAME) != 0)
err_error("failed to remove FIFO %s", PIPE_NAME);
return(0);
}

Followup to previous question about client/server echo program

Earlier I got some great advice about Client/server sockets in c. I have modified the code slightly and it works fine when I have the server running in one terminal and the clients running in other windows. Now I would like to make a TUI for the program, but I am running into a problem. Since the server is constantly listening for a client it is in an infinite loop; once the server is established nothing else can go on in the terminal. What I thought I should do is use fork() so that the server can be running in the background, leaving the TUI free to create clients. Here is my code so far (again, I cannot take much credit for the client/server code):
Server code
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
struct sockaddr myname;
char buf[80];
ssize_t readLine(int sockd, char *vptr, size_t maxlen) {
ssize_t n, rc;
char c, *buffer;
buffer = vptr;
for(n = 1; n < maxlen; n++) {
if((rc = read(sockd, &c, 1)) == 1) {
*buffer++ = c;
if(c== '\n')
break;
}
else if(rc == 0){
if(n == 1)
return 0;
else
break;
}
else {
if(errno == EINTR)
continue;
return -1;
}
}
*buffer = 0;
return n;
}
ssize_t modifyBuf(int sockd, char *vptr, size_t n) {
int i, j;
char temp;
for(i = 0, j = n-1; i < j; i++, j--) {
temp = vptr[i];
vptr[i] = vptr[j];
vptr[j] = temp;
}
for(i = 0; i < n; i++)
vptr[i] = toupper(vptr[i]);
return writeLine(sockd,vptr,n);
}
ssize_t writeLine(int sockd, const void *vptr, size_t n) {
size_t nleft;
size_t nwritten;
const char *buffer;
buffer = vptr;
nleft = n;
while(nleft > 0) {
if((nwritten = write(sockd,buffer,nleft)) <= 0){
if(errno == EINTR)
nwritten = 0;
else
return -1;
}
nleft -= nwritten;
buffer += nwritten;
}
return n;
}
makeServer() {
int sock, new_sd, adrlen, cnt;
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if(sock < 0) {
printf("server socket failure %d\n", errno);
perror("server: ");
exit(1);
}
myname.sa_family = AF_UNIX;
strcpy(myname.sa_data, "/tmp/billb");
adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);
unlink("/tmp/billb"); /*defensive programming */
if(bind(sock, &myname, adrlen) < 0) {
printf("server bind failure%d\n", errno);
perror("server: ");
exit(1);
}
if(listen(sock, 5) < 0) {
printf("server listen failure %d\n", errno);
perror("server: ");
exit(1);
}
while(1) {
if((new_sd = accept(sock, &myname, &adrlen)) < 0) {
printf("server accept failure %d\n", errno);
perror("server: ");
exit(1);
}
printf("Socket address in server %d\n", getpid());
if(fork() == 0) {
close(sock);
readLine(new_sd,buf,999);
modifyBuf(new_sd,buf,strlen(buf));
exit(0);
}
close(new_sd);
}
}
Client code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
char buf[80];
struct sockaddr myname;
void replyBack(FILE *fp, int sockfd) {
char sendline[1000], recvline[1000];
printf("Enter your echo: \n");
while(fgets(sendline,1000,stdin) != NULL) {
write(sockfd,sendline,sizeof(sendline));
if(read(sockfd,recvline,1000) == 0) {
printf("str_cli: server terminated prematurely");
exit(-1);
}
fputs(recvline, stdout);
}
}
makeClient() {
int sock, adrlen, cnt;
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if(sock < 0) {
printf("client socket failure%d\n", errno);
printf("client: ");
exit(1);
}
myname.sa_family = AF_UNIX;
strcpy(myname.sa_data, "/tmp/billb");
adrlen = strlen(myname.sa_data) + sizeof(myname.sa_family);
if((connect(sock, &myname, adrlen)) < 0) {
printf("client connect failure %d\n", errno);
perror("client: ");
exit(1);
}
replyBack(stdin,sock);
exit(0);
}
TUI
#include <stdio.h>
#include </server.c>
#include </client.c>
void displayWelcomeMessage(){
....
}
void showChoices(){
printf("\nTo make a new client, enter 1.\n");
printf("To close server, enter 2.\n");
printf(">>> ");
int choice;
scanf("%d",&choice);
if(choice == 1){
makeClient();
showChoices();
} else if(choice == 2){
printf("Goodbye.");
exit(0);
} else
printf("Invalid choice.\n");
showChoices();
}
void main() {
displayWelcomeMessage();
printf("Server is now listening for clients.\n");
if(fork() == 0)
makeServer();
if(fork() > 0)
showChoices();
}
The TUI should create the server when it is executed and then show a short menu. The user can chose to create a new client or exit the program. If the user creates a client then the user should be prompted for a message to send to the server; once the message is sent back the menu should be shown again. If the user elects to exit the program then the program will terminate and the socket should close. If the user makes an invalid choice, then the menu should display again.
What is happening is the server is created and I can see the menu, but if I select to create a new client the program terminates or terminates after I input any text + enter (so the message is not getting sent to the server). Like I said, the client/server works without the TUI so I believe the problem is in my usage of fork(), but I'm not sure what other way there is to solve this problem.
Phew, that was long. Thank you for your time!
You seem to be calling fork() twice - surely you only want to call it once? I think your code should be:
if(fork() == 0)
makeServer();
else
showChoices();
Also, you may want to investigate the use of wait(), which will allow you to wait for a child process to complete.

Resources