I am new to Linux and i am trying to create a shared memory object which stores the collatz conjecture calculated in child process and prints it in parent process. I have already read the man pages for the commands.
When I create the object it prints permission denied and segmentation fault(core dumped). Only one time it somehow passed that step then I got the mapping failed error.I am using ubuntu 18.04 on a virtual machine
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/mman.h>
#include<sys/wait.h>
#include<sys/stat.h>
int main(int argc,char** argv)
{
pid_t pid;
pid = fork();
int page_size = 4096;
char obj[] = "name";
int num = atoi(argv[1]);
if(num < 1)
{
printf("Please input a greater number\n");
return 0;
}
if(pid < 0)
{
fprintf(stdout,"Fork failed\n");
}
else if(pid == 0)
{
int fd1 = 0;
void *ptr1 = NULL;
fd1 = shm_open(obj,O_CREAT|O_TRUNC,S_IRWXU);
if (fd1 == -1)
{
perror("error:");
exit(0);
}
ftruncate(fd1,page_size);
ptr1 = mmap(0,page_size,PROT_WRITE,MAP_SHARED,fd1,0);
if(ptr1 == MAP_FAILED)
{
fprintf(stdout,"Mapping failed");
exit(0);
}
else
{
while(num != 1)
{
if(num%2 == 0)
num = num / 2;
else
num = (num * 3) + 1;
sprintf(ptr1,"%d, ",num);
}
}
}
else
{
wait(NULL);
int fd = 0;
void *ptr = NULL;
shm_open(obj,O_RDONLY,S_IRWXU);
ptr = mmap(0,page_size,PROT_READ,MAP_SHARED,fd,0);
char *pr = (char *)ptr;
fprintf(stdout,pr);
shm_unlink(obj);
}
return 0;
}
Your code needs a couple of changes, some for better programming practices and some for fixing the actual functionality:
#include <all.h>
int main(int argc, char **argv) {
pid_t pid;
pid = fork();
int page_size = 4096;
char obj[] = "name";
// check argc otherwise you'll segfault
if (argc < 2) {
printf("Please provide a second argument.\n");
// if the program errors it should not return 0
return 1;
}
int num = atoi(argv[1]);
if (num < 1) {
printf("Please input a greater number\n");
return 1;
}
if (pid < 0) {
// no need for fprintf(stdout
printf("Fork failed\n");
} else if (pid == 0) {
int fd1 = 0;
char *ptr1 = NULL;
// we need to open with O_RDWR otherwise mmap will fail
fd1 = shm_open(obj, O_CREAT | O_TRUNC | O_RDWR, 0666);
if (fd1 == -1) {
perror("shm_open error");
return 1;
}
ftruncate(fd1, page_size);
// cast it to char* directly, no need for void pointer stuff
ptr1 = (char *) mmap(0, page_size, PROT_WRITE, MAP_SHARED, fd1, 0);
if (ptr1 == MAP_FAILED) {
// perror for debugging
perror("mmap error");
printf("Mapping failed.\n");
return 1;
} else {
while (num != 1) {
if (num % 2 == 0)
num = num / 2;
else
num = (num * 3) + 1;
// we need to shift the pointer so it stops truncating
ptr1 += sprintf(ptr1, "%d, ", num);
}
}
} else {
wait(NULL);
// move these to one like and cast to char* directly
int fd = shm_open(obj, O_RDONLY, S_IRWXU);
char *ptr = (char*) mmap(0, page_size, PROT_READ, MAP_SHARED, fd, 0);
// no need for fprintf(stdout, and also use %s\n as a format to add a newline
printf("%s\n", ptr);
shm_unlink(obj);
}
return 0;
}
Related
I'm trying to recreate the shell implementation (bash --posix) of "|" and ">" in c.
But I have trouble to find my problems with fd. When I do the following command: echo heyy | wc > txt.txt | wc everything is going smoothly but one in two the last wc display the good answer (0 0 0) or the false answer (result of the previous wc).
t_command is a list of cmd. Each node have a char **tab with the command, for example:
-first node is: tab[0] = echo , tab[1]=heyy
-third node is: tab[0] = wc
etc..
{
int count;
t_command *tmp;
tmp = all_cmd;
count = 0;
while (tmp /* && (tmp->type == WORD || tmp->type == PIPE) */)
{
if (tmp->type == WORD)
count++;
tmp = tmp->next;
}
return (count);
}
pid_t fork_pipe(int fd_0, int fd_1)
{
pid_t pid;
pid = fork();
if (pid < 0)
return (-1);
if (pid == 0)
{
if (fd_0 != 0)
{
dup2(fd_0, STDIN_FILENO);
close(fd_0);
}
if (fd_1 != 1)
{
dup2(fd_1, STDOUT_FILENO);
close(fd_1);
}
}
return (pid);
}
int execute_last(t_command *all_cmd, char **env, int fd_0, int fd_1)
{
int pid;
pid = fork_pipe(fd_0, fd_1);
if (pid == 0)
ft_exec(env, all_cmd->cmd_to_exec);
return (0);
}
int execute_pipe(t_command *all_cmd, char **env, int nb_cmd)
{
int fd[2];
int fd_0;
int fd_1;
int i;
pid_t pid;
i = 0;
fd_0 = STDIN_FILENO;
while (all_cmd && i < nb_cmd - 1)
{
if (pipe(fd) < 0)
return (-1);
if(all_cmd->next && all_cmd->next->type == CHV_R)
{
fd_1 = open(all_cmd->next->next->cmd_to_exec[0], O_WRONLY | O_CREAT | O_TRUNC, 00777);
execute_last(all_cmd, env, fd_0, fd_1);
if (all_cmd->next->next)
all_cmd = all_cmd->next->next;
i += 2;
}
else
{
fd_1 = fd[1];
pid = fork_pipe(fd_0, fd_1);
if (pid == 0)
ft_exec(env, all_cmd->cmd_to_exec);
close(fd[1]);
fd_0 = fd[0];
}
if (all_cmd && all_cmd->next && all_cmd->next->next)
all_cmd = all_cmd->next->next;
i++;
}
execute_last(all_cmd, env, fd_0, STDOUT_FILENO);
wait_pipe(nb_cmd);
return (0);
} ```
I am writing a C program where I am dealing out cards to n players, represented by n forked processes. I wish for them to all share the same deck of cards, so I am attempting to use mmap() to keep track of the deck size, however the machine I have to compile this program for does not allow MAP_ANONYMOUS or MAP_ANON. Is there another way that I can store a global variable in shared memory that would still be C89/pre Linux 2.4 compliant?
My program for context:
static int *deck_size;
int pop(int *arr, int *size, int loc)
{
int i;
int val = arr[loc];
for(i = loc; i < (*size - 1); i++)
{
arr[i] = arr[(i+1)];
arr[*size] = '\0';
}
*size = *size-1;
return val;
}
int main(int argc, char* argv[])
{
pid_t pid, wpid;
int status, index, players, rdm_card;
char outbuf[100];
int deck[] =
{1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11,11,11,11,12,12,12,12,13,13,13,13};
deck_size = mmap(NULL, sizeof *deck_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
*deck_size = 52;
/* reject an execution with no arguments */
if(argv[1] == NULL)
{
write(STDERR_FILENO, "Usage: dealer <n>\n", 18);
exit(EXIT_FAILURE);
}
else
{
if( (players = atoi(argv[1])) < 1)
{
write(STDERR_FILENO, "n cannot be less than 1\n", 24);
exit(EXIT_FAILURE);
}
}
srand ( time(NULL) );
rdm_card = rand() % *deck_size;
for(index = 0; index < players; index++)
{
pid = fork();
if (pid == 0) {
sprintf(outbuf, "random card: %d\n", pop(deck, deck_size, rdm_card));
write(STDOUT_FILENO, outbuf, 17);
printf("size of deck %d!\n", *deck_size);
exit(EXIT_SUCCESS);
} else if (pid < 0) {
write(STDERR_FILENO, "fork error\n", 11);
exit(EXIT_FAILURE);
} else {
do {
wpid = waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
}
return 0;
}
When you clearly read the man page MMAP(2) it clearly state that MAP_ANONYMOUS is supported since Linux kernel 2.4.
The use of
MAP_ANONYMOUS in conjunction with MAP_SHARED is supported on
Linux only since kernel 2.4.
Make sure you define _BSD_SOURCE or _SVID_SOURCE to get MAP_ANONYMOUS
#define _BSD_SOURCE
I've written code for a shell that we call opsh (the OPen SHell).
Now my friend found an issue about backslash preceeded characters that don't behave as expected.
Example
opsh does not seem to pass on the -character to echo.
In opsh:
$
$echo "TEXT\n"
TEXTn
$
whereas in ksh:
$
$echo "TEXT\n"
TEXT
I don't know how to do it. In my code I just use a pipeline for the commands (you may look at the entire code in the repo) where the relevant code looks as follows
int run_cmd(char *cmd, bool background) {
char buffer[2];
buffer[0] = '|';
buffer[1] = '\0';
struct str_list *chunks = list_split(cmd, buffer);
struct pipeline *pipe = malloc(chunks->pipes * sizeof *pipe);
if (pipe == NULL) {
fprintf(stderr, "malloc failed!\n");
}
int i = 0;
for (i = 0; i < chunks->pipes; i++) {
pipe[i].data = malloc(sizeof(char **) * BUFFER_SIZE * chunks[i].size);
if (pipe[i].data == NULL) {
fprintf(stderr, "malloc failed!\n");
}
int j = 0;
pipe[i].size = chunks[i].size;
for (j = 0; j < chunks[i].size; j++) {
if (chunks[i].argv[j] == NULL) {
chunks[i].argv[j] = '\0';
break;
}
pipe[i].option = malloc(sizeof(int) * 10);
if (pipe[i].option == NULL) {
fprintf(stderr, "malloc failed!\n");
}
pipe[i].data[j] = strdup(chunks[i].argv[j]);
if (pipe[i].data[j] == NULL) {
perror("strdup");
exit(EXIT_FAILURE);
}
* pipe[i].option = * chunks[i].option;
}
pipe[i].data[j] = '\0';
}
int status = execute_pipeline(chunks->pipes, pipe, background);
return status;
}
int execute_pipeline(int n, struct pipeline *pipe, bool background) {
// background = false;
int status = 0;
pid_t pid = -2;
if (n > -1)
pid = fork();
if (pid < 0) {
perror("fork failed");
return -1;
}
/* If we are the child process, then go execute the string.*/
if (pid == 0) {
/* spawn(cmd);*/
fork_pipeline(n, pipe);
}
/*
* We are the parent process.
* Wait for the child to complete.
*/
if (!background) {
while (((pid = waitpid(pid, &status, 0)) < 0) && (errno == EINTR));
fprintf(stderr, "%d: executed\n", (int) pid);
}
if (pid < 0) {
fprintf(stderr, "Error from waitpid: %s", strerror(errno));
return -1;
}
if (WIFSIGNALED(status)) {
fprintf(stderr, "pid %ld: killed by signal %d\n",
(long) pid, WTERMSIG(status));
return -1;
}
return WEXITSTATUS(status);
}
/* Helper function that forks pipes */
void fork_pipeline(int n, struct pipeline *structpipeline) {
int i;
int in = 0;
int fd[2];
for (i = 0; i < n - 1; ++i) {
if (pipe(fd) == -1) {
err_syserr("Failed creating pipe");
}
spawn_pipe(in, fd[1], structpipeline + i);
close(fd[1]);
in = fd[0];
}
if (dup2(in, 0) < 0) {
err_syserr("dup2() failed on stdin for %s: ", structpipeline[i].data[0]);
}
if (strstr(structpipeline[i].data[0], "=")) {
/* do nothing for now, handle in parser instead... */
}
else {
fprintf(stderr, "%d: executing %s\n", (int) getpid(), structpipeline[i].data[0]);
//fprintf(stderr, "\n");
if (* (structpipeline[i].option) == 1) { /* output redirection */
int length = structpipeline[i].size;
char *filename = structpipeline->data[length - 1];
for (int k = length - 2; k < length; k++)
structpipeline->data[k] = '\0';
fd[1] = open(filename, O_WRONLY | O_CREAT, 0666);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
} /* TODO: input redirection */
execvp(structpipeline[i].data[0], structpipeline[i].data);
err_syserr("failed to execute %s: ", structpipeline[i].data[0]);
}
}
I am trying to write a C program that uses pipes to send information between the parent and two children. The goal of the program is to achieve something similar to merge sort, for strings. I read the number of strings and then the Strings. The strings get divided between the 2 children, recursively until each child has only one string. I have to redirect the stdin of the child to read from the stdout of the parent.
For some reason none of the children read more than the first string.
How could I solve this problem?
int main(int argc, char * argv[]) {
int nrrows = 0;
char * buffer = NULL;
size_t n = 0;
getline(&buffer, &n, stdin);
char * endptr;
nrrows = strtol(buffer, &endptr, 10);
char rows[nrrows][MAX_LEN];
int i = 0;
n = 0;
while(i < nrrows) {
char * row = NULL;
getline(&row, &n, stdin);
strcpy(rows[i], row);
i++;
}
if(nrrows == 1) {
fprintf(stderr, "%s", rows[0]);
return 0;
}
int fdcp1[2];
int fdcp2[2];
if(pipe(fdcp1) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
if(pipe(fdcp2) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
pid_t chpid1 = fork();
if(chpid1 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}
else if(chpid1 == 0) {
close(fdcp2[0]);
close(fdcp2[1]);
close(fdcp1[1]);
dup2(fdcp1[0], STDIN_FILENO);
execlp("./forksort", "child1", NULL);
}else {
close(fdcp1[0]);
dup2(fdcp1[1], STDOUT_FILENO);
double half = (nrrows / 2);
int h = half;
char b[2];
b[0] = '0' + h;
b[1] = '\n';
write(fdcp1[1], b, sizeof(b));
for(i = 0; i < h; i ++) {
rows[i][strlen(rows[i])] = '\0';
write(fdcp1[1], rows[i], sizeof(rows[i]));
}
pid_t chpid2 = fork();
if(chpid2 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}else if(chpid2 == 0) {
close(fdcp1[0]);
close(fdcp1[1]);
close(fdcp2[1]);
dup2(fdcp2[0], STDIN_FILENO);
execlp("./forksort", "child2", NULL);
}else {
close(fdcp2[0]);
dup2(fdcp2[1], STDOUT_FILENO);
half = (nrrows / 2);
h = half;
char b[2];
b[0] = '0' + (nrrows - h);
b[1] = '\n';
write(fdcp2[1], b, sizeof(b));
for(i = h; i < nrrows; i ++) {
rows[i][strlen(rows[i])] = '\0';
write(fdcp2[1], rows[i], sizeof(rows[i]));
}
}
}
return 0;
}
It's bad news to modify a file descriptor that is associated with an open stream. I would account it highly likely to cause you trouble, and there is, moreover, no need to do that here. The parent should instead use fdopen() to open new streams on top of its ends of the pipes, and conduct I/O with its children via those instead of via the standard streams. In addition to being safer, that leaves the process's original standard streams available for it to communicate with its parent process.
With that approach, you could even stream the strings to be sorted back and forth among the processes, instead of redundantly buffering blocks of them in each process's memory. For instance, you might do something like this:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char * argv[]) {
char * buffer = NULL;
size_t buflen = 0;
int nrrows;
int fdpc1[2];
int fdcp1[2];
int fdpc2[2];
int fdcp2[2];
pid_t chpid1;
pid_t chpid2;
FILE *pipeout;
FILE *pipein1;
FILE *pipein2;
int half;
int i;
fprintf(stderr, "%s!!!!!!!!!!!!!!!!!\n", argv[0]);
getline(&buffer, &buflen, stdin);
fprintf(stderr, "number: %s from %s\n", buffer, argv[0]);
nrrows = strtol(buffer, NULL, 10);
if(nrrows <= 0) {
fprintf(stderr, "This is not a valid >0 number\n");
return EXIT_FAILURE;
} else if (nrrows == 1) {
/* ... read and echo back the one row ... */
getline(&buffer, &buflen, stdin);
fprintf(stderr, "%s", buffer);
return EXIT_SUCCESS;
}
/* There are at least two rows to sort */
if (pipe(fdcp1) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
if (pipe(fdpc1) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
chpid1 = fork();
if (chpid1 == 0) {
/* this is child process 1 */
close(fdcp1[1]);
close(fdpc1[0]);
dup2(fdcp1[0], STDIN_FILENO);
close(fdcp1[0]);
dup2(fdpc1[1], STDOUT_FILENO);
close(fdpc1[1]);
execlp("./forksort", "child1", NULL);
} else if (chpid1 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}
/* this is the parent process */
close(fdcp1[0]);
close(fdpc1[1]);
if (pipe(fdcp2) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
if (pipe(fdpc2) < 0) {
fprintf(stderr, "pipe unsuccessfull\n");
return EXIT_FAILURE;
}
chpid2 = fork();
if (chpid2 == 0) {
/* this is child process 2 */
close(fdcp1[1]);
close(fdpc1[0]);
close(fdcp2[1]);
close(fdpc2[0]);
dup2(fdcp2[0], STDIN_FILENO);
close(fdcp2[0]);
dup2(fdpc2[1], STDOUT_FILENO);
close(fdpc2[1]);
execlp("./forksort", "child2", NULL);
} else if (chpid2 < 0) {
fprintf(stderr, "fork unsuccessfull\n");
return EXIT_FAILURE;
}
/* this is the parent process */
close(fdcp2[0]);
close(fdpc2[1]);
/* copy the first half of the lines from input to child 1 */
pipeout = fdopen(fdcp1[1], "w");
if (pipeout == NULL) {
fprintf(stderr, "fdopen unsuccessful\n");
return EXIT_FAILURE;
}
half = nrrows / 2;
fprintf(pipeout, "%d\n", half);
for (i = 0; i < half; i += 1) {
getline(&buffer, &buflen, stdin);
fprintf(stderr,"row[%d] from %s: %s", i, argv[0], buffer);
fputs(buffer, pipeout);
}
fclose(pipeout);
/* copy the second half of the lines from input to child 2 */
pipeout = fdopen(fdcp2[1], "w");
if (pipeout == NULL) {
fprintf(stderr, "fdopen unsuccessful\n");
return EXIT_FAILURE;
}
fprintf(pipeout, "%d\n", nrrows - half);
for (; i < nrrows; i += 1) {
getline(&buffer, &buflen, stdin);
fprintf(stderr,"row[%d] from %s: %s", i, argv[0], buffer);
fputs(buffer, pipeout);
}
fclose(pipeout);
/* now read and merge sorted lines from the children */
pipein1 = fdopen(fdpc1[0], "r");
pipein2 = fdopen(fdpc2[0], "r");
if (pipein1 == NULL || pipein2 == NULL) {
fprintf(stderr, "fdopen unsuccessful\n");
return EXIT_FAILURE;
}
/* ... */
fclose(pipein1);
fclose(pipein2);
return 0;
}
My code worked fine until I add the extra stuffs into it, like identifying and deal with cd, >, <, >> and |. Could you please check my code and see where did the error happens?
Btw the requirement of the assignment is only up to 1 pipe. And I think the problem starts somewhere from the for loop, cause I put printf right after it to check if it would print args[k], and it did but then the bug popped up and the program stopped.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
int main()
{
const int MAX_ARGS = 10;
char *token[MAX_ARGS + 1]; /*assume max number of token <=10*/
char *temp;
char line[256], command[MAX_ARGS];
char prompt[] = "sh2 % ";
pid_t pid;
int i=0, j,k, status;
printf("%s", prompt);
while (fgets(line, sizeof line, stdin) != NULL)
{
line[strlen(line)-1] = '\0'; /*get rid of \n*/
token[0] = strtok(line," ");
strcpy(command,token[0]);
temp = strtok(NULL," ");
while (temp != NULL)
{
i = i+1;
token[i] = temp;
temp = strtok(NULL," ");
}
char *args[i+2];
for (j = 0; j < (i+1); j++)
{
args[j] = token[j];
}
args[i+1] = NULL;
if (!strcmp("exit",command))
exit(0);
if (!strcmp("cd", command))
{
int success;
if (success = chdir(args[1]) <0)
{
printf("Failed to change dir.\n");
}
}
else
{
int piping = 0;
int fd;
for (k = 0; k < sizeof args; k++)
{
if (!strcmp(">",args[k]))
{
fd = open(args[k+1],O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR);
if (fd <0) { printf("Open file failed.\n");}
else
{
dup2(fd, 1);
args[k] = '\0';
fflush(stdout);
close(fd);
}
}
if (!strcmp("<", args[k]))
{
fd = open(args[k+1], O_RDONLY);
if (fd <0) {printf("Open file failed.\n");}
else
{
dup2(fd, 0);
args[k] = '\0';
close(fd);
}
}
if (!strcmp(">>", args[k]))
{
fd = open(args[k+1], O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IXUSR);
if (fd <0) {printf("Open file failed");}
else
{
dup2(fd,1);
args[k] = '\0';
fflush(stdout);
close(fd);
}
}
if (!strcmp("|", args[k]))
{
piping = 1;
}
} /*end for*/
if (!(piping))
{
pid = fork();
if (pid <0) {}
else if (pid == 0)
{
if ( (status = execvp(command, args)) < 0 )
{
printf("Command not found\n");
}
}
else /*parent*/
{
wait(&status);
} /*end parent*/
}/*end if not pipe*/
else /*its pipe*/
{
int pfd[2];
pipe(pfd);
fd = fork();
if (fd < 0) {}
else if (fd ==0)
{
close(pfd[1]);
dup2(pfd[0],0);
close(pfd[0]);
execvp(args[2],args[2]);
}
else /*parent*/
{
close(pfd[0]);
dup2(pfd[1],1);
close(pfd[1]);
execvp(args[0],args[0]);
}
}/*end pipe*/
} /*end outter else*/
printf("%s", prompt);
}/*end while*/
return 0;
}
for (k = 0; k < sizeof args; k++)
This is not how you iterate through args: this will go far beyond the end of the array. You want something like:
num = sizeof(args) / sizeof(*args);
for (k = 0; k < num; k++)
Alternatively, since you set the last element as NULL, you could do
for (char **arg = args; *arg; arg++)
Also note that you iterate with k until the end of the array and then use k + 1, which is very likely to cause problems.