Fork/Dup2/Exec not working with mkstemp - c

Why does word count output 0 0 0 in the following program?
int main(int argc, char **argv) {
pid_t pid = fork();
const char *data = "THIS IS MY DATA.";
if(pid == 0) {
// Child Process
char *tmpname = malloc(15);
strcpy(tmpname, "/tmp/datXXXXXX");
int f = mkstemp(tmpname);
//int f = open("tmpfile", O_RDWR | O_CREAT, S_IRWXU);
if(f == -1) {
perror("");
return;
}
int written = write(f, data, strlen(data));
dup2(f, STDIN_FILENO);
close(f);
char *wcargs[5] = {"wc", NULL};
execvp("wc", wcargs);
fprintf(stderr, "ERROR");
}
return 1;
}

Try rewinding stdin after the dup2() using lseek(0, 0, SEEK_SET). In your code, wc will be trying to read from the end of the file and obviously won't have any bytes to read.

Related

Reproduce the pipe command

I have a school project which consists of reproducing the shell pipe command between two commands that the operator will choose as it pleases.
./pipex infile "ls -l" "wc -l" outfile
should be equivalent to
< infile ls -l | wc -l > outfile
Here is my way of doing it:
void first_child(char **argv, char **envp, int pipefd[2], int fd[2])
{
char **cmd_and_options1;
char *path_ultime1;
int pid1;
pipefd[0] = open(argv[1], O_RDONLY);
pid1 = fork();
if (pid1 == -1)
error();
if (pid1 == 0)
{
dup2(fd[1], STDOUT_FILENO);
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
close(fd[1]);
execve(path_ultime1, cmd_and_options1, envp);
}
}
void second_child(char **argv, char **envp, int pipefd[2], int fd[2])
{
char **cmd_and_options2;
char *path_ultime2;
int pid2;
pipefd[1] = open(argv[4], O_CREAT | O_WRONLY | O_TRUNC, 0777);
pid2 = fork();
if (pid2 == -1)
error();
if (pid2 == 0)
{
dup2(fd[0], STDIN_FILENO);
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
close(fd[0]);
execve(path_ultime2, cmd_and_options2, envp);
}
}
int main(int argc, char *argv[], char **envp)
{
int pipefd[2];
int fd[2];
char **cmd_and_options1;
char **cmd_and_options2;
char *path_ultime1;
char *path_ultime2;
(void)argc;
cmd_and_options1 = ft_split(argv[2], ' ');
cmd_and_options2 = ft_split(argv[3], ' ');
path_ultime1 = find_path(cmd_and_options1[0], envp);
path_ultime2 = find_path(cmd_and_options2[0], envp);
if (pipe(fd) == -1)
error();
first_child(&argv[1], envp, &pipefd[0], &fd[0]);
second_child(&argv[4], envp, &pipefd[1], &fd[1]);
close(pipefd[0]);
close(pipefd[1]);
close(fd[0]);
close(fd[1]);
waitpid(-1, NULL, 0);
waitpid(-1, NULL, 0);
return (0);
}
Roles of my find_path function:
extract the contents of PATH from the envp
use split() to delimit the paths according to ":" and store them in a double pointer
use strjoin() to append a "/" to the end of each path, and add the user command
test each path with access() then return the valid path
char *find_path(char *cmd, char **envp)
{
char **array_of_paths;
char *path_ultime;
int i;
char *temp;
i = 0;
while (ft_strnstr(envp[i], "PATH=", 5) == 0)
i++;
array_of_paths = ft_split(envp[i] + 5, ':');
i = 0;
while (array_of_paths[i])
{
temp = ft_strjoin(array_of_paths[i], "/");
path_ultime = ft_strjoin(temp, cmd);
free(temp);
if (access(path_ultime, F_OK | X_OK) == 0)
return (path_ultime);
i++;
}
return (0);
}
When I put all my code in the main(), my program worked.
But when I try to separate it into functions it doesn't work anymore and I don't understand what the problem is.

Reproduce the shell pipe command

I have a school project which consists of reproducing the shell pipe command between two commands that the operator will choose as it pleases.
./pipex infile "ls -l" "wc -l" outfile
should be equivalent to
< infile ls -l | wc -l > outfile
Roles of my find_path function:
extract the contents of PATH from the envp
use split() to delimit the paths according to ":" and store them in a double pointer
use strjoin() to append a "/" to the end of each path, and add the user command
test each path with access() then return the valid path
char *find_path(char *cmd, char **envp)
{
char **array_of_paths;
char *path_ultime;
int i;
char *temp;
i = 0;
while (ft_strnstr(envp[i], "PATH=", 5) == 0)
i++;
array_of_paths = ft_split(envp[i] + 5, ':');
i = 0;
while (array_of_paths[i])
{
temp = ft_strjoin(array_of_paths[i], "/");
path_ultime = ft_strjoin(temp, cmd);
free(temp);
if (access(path_ultime, F_OK | X_OK) == 0)
return (path_ultime);
i++;
}
return (0);
}
My main takes care of:
split the argv[2] and the argv[3] with a space as delimiter, to keep only the command "ls" or "wc", and not the options
classic stuff like pipe(), fork(), execve() …
int main(int argc, char *argv[], char **envp)
{
(void)argc;
pid_t pid1;
pid_t pid2;
int fd[2];
char **cmd_and_options1;
char **cmd_and_options2;
char *path_ultime1;
char *path_ultime2;
cmd_and_options1 = ft_split(argv[2], ' ');
cmd_and_options2 = ft_split(argv[3], ' ');
path_ultime1 = find_path(cmd_and_options1[0], envp);
path_ultime2 = find_path(cmd_and_options2[0], envp);
if (pipe(fd) == -1)
return (1);
pid1 = fork();
if (pid1 == -1)
return (1);
if (pid1 == 0) // First child
{
dup2(fd[1], STDOUT_FILENO);
close(fd[0]);
close(fd[1]);
execve(path_ultime1, cmd_and_options1, envp);
}
pid2 = fork();
if (pid2 == -1)
return (1);
if (pid2 == 0) // Second child
{
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
close(fd[1]);
execve(path_ultime2, cmd_and_options2, envp);
}
close(fd[0]);
close(fd[1]);
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
return (0);
}
A friend told me that I lacked the management of the input and output file (argv[1] and argv[4]) that the operator will enter but I don't really understand what that means.
I know I have to use open() somewhere...
Can you give me some clues?

Simulating multi pipes "|" with a loop in c

I'm trying to simulate Unix shell multi pipes in c and I found a source code of a function doing the same thing but I didn't understand it well, if you can please explaint to me how's works, I know that pipe fd[2] creates fd[0] read input, and fd[1] write input as well dup2 close selected FD and duplicate it to FD through the pipe.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
/*
* loop over commands by sharing
* pipes.
*/
static void
pipeline(char ***cmd)
{
int fd[2];
pid_t pid;
int fdd = 0; /* Backup */
while (*cmd != NULL) {
pipe(fd); /* Sharing bidiflow */
if ((pid = fork()) == -1) {
perror("fork");
exit(1);
}
else if (pid == 0) {
dup2(fdd, 0);
if (*(cmd + 1) != NULL) {
dup2(fd[1], 1);
}
close(fd[0]);
execvp((*cmd)[0], *cmd);
exit(1);
}
else {
wait(NULL); /* Collect childs */
close(fd[1]);
fdd = fd[0];
cmd++;
}
}
}
/*
* Compute multi-pipeline based
* on a command list.
*/
int
main(int argc, char *argv[])
{
char *ls[] = {"ls", "-al", NULL};
char *rev[] = {"rev", NULL};
char *nl[] = {"nl", NULL};
char *cat[] = {"cat", "-e", NULL};
char **cmd[] = {ls, rev, nl, cat, NULL};
pipeline(cmd);
return (0);
}

How do I avoid terminating parent process with ctrl-c?

I'm trying to write a shell in c and I want to implement the behavior of "cat > file" to then let me write into a file.
My main function:
int main (int argc, char **argv)
{
pid_t childPid;
int status;
char * cmdLine;
int EXIT = 1;
parseInfo info;
int fd[2];
char buffer[1024];
char* env = getenv("USER");
char cwd[BUFFER];
if (getcwd(cwd, sizeof(cwd)) == NULL) {
perror("getcwd() error");
return 1;
}
while (EXIT){
printf("%s#%s > ",env,cwd);
cmdLine = readline();
info = parse(cmdLine);
info.cwd = cwd;
pipe(fd);
childPid = fork();
if (childPid == 0)
{
executeCommand(info,fd);
}
else
{
close(fd[1]);
if((read(fd[0], buffer, 1024 * sizeof(char))) == -1){
printf("Failed reading from pipe.\n");
}
else{
if(!strcmp(info.cmd,"cd")){
strcpy(cwd,buffer);
chdir(buffer);
}
}
while (wait(&status) != childPid);
}
}
}
the executeCommand:
void executeCommand(parseInfo info,int* fd){
//print_info(info); // FOR DEBUGGING
if(!strcmp(info.cmd,"cat")){
cat(info);
}
else if(!strcmp(info.cmd,"cd")){
info.cwd = changedir(info,info.cwd);
if(write(fd[1], info.cwd, ((strlen(info.cwd)+1)* sizeof(char))) == -1)
printf("Error while writing into pipe\n");
}
else if(execvp(info.cmd,info.args) == -1){
printf("Error: unknown command [%s]\n",info.cmd);
}
exit(1);
}
Here's my cat function:
void cat(parseInfo info){
char* write_to_file = ">";
char* append_to_file = ">>";
if(!strcmp(info.args[1],write_to_file) || !strcmp(info.args[1],append_to_file)){
if (info.args[2] == NULL){
printf("unsupported sytax, file name expected.\n");
}
char* line;
FILE* fp;
if(!strcmp(info.args[1],write_to_file))
fp = fopen(info.args[2],"w");
if(!strcmp(info.args[1],append_to_file))
fp = fopen(info.args[2],"a");
while(1){
line = readline();
fputs(line,fp);
}
fclose(fp);
}
}
How do I make ctrl-c not kill the parent process and just run fclose and kill the child only?
Or is there another method that's better here?
I'm not sure how to implement this.

Thread feeding other MultiThreading

I see it's easy to open pipe between two process using fork, but how we can passing open pipe to threads.
Assume we need to pass out of PROGRAM A to PROGRAM B "may by more than one thread",
PROGRAM B send his output to PROGRAM C
EDIT:
I come again after modifying the code to become more easy for reading.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <fcntl.h>
void *thread1(void *arg) {
int status, fd[2];
pid_t pid;
pipe(fd);
pid = fork();
if (pid == 0) {
int fd2 = *((int *) (arg));
dup2(STDIN_FILENO, fd2);
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
execvp("PROGRAM B", NULL);
exit(1);
} else {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execl("PROGRAM C", NULL);
wait(&status);
return NULL;
}
}
int main(void) {
FILE *fpipe;
char *command = "PROGRAM A";
char buffer[1024];
if (!(fpipe = (FILE*) popen(command, "r"))) {
perror("Problems with pipe");
exit(1);
}
char* outfile = "out.dat";
//FILE* f = fopen (outfile, "wb");
//int fd = fileno( f );
int fd[2];
fd[0] = open(outfile, O_WRONLY);
pthread_t thid;
if (pthread_create(&thid, NULL, thread1, fd) != 0) {
perror("pthread_create() error");
exit(1);
}
int len;
while (read(fpipe, buffer, sizeof (buffer)) != 0) {
len = strlen(buffer);
write(fd[0], buffer, len);
}
pclose(fpipe);
return (0);
}
For intra-process messaging, POSIX queues will probably suit your needs better than pipes. Check out man mq_overview (or online).

Resources