For some reason if I do a second open, it compiles but when I try to run it, it does nothing like it's locked. It's missing a lot of other functions, because it's a work in progress for a school project. If I remove one of the open(), the program runs just fine.
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFFER_SIZE 100
#define INPUT "/tmp/father"
int main(int argc, char **argv)
{
int fds;
int fd;
char mode[BUFFER_SIZE];
char buffer[BUFFER_SIZE];
unlink(INPUT);
mkfifo(INPUT, S_IRUSR | S_IWUSR);
if(argc != 2)
{
fputs("Argumentos invalidos\n", stderr);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_WRONLY);
if(fd == -1)
{
fprintf(stderr, "\nCan't open pipe\n");
exit(EXIT_FAILURE);
}
fds = open(INPUT, O_RDONLY);
if(fds == -1)
{
fprintf(stderr, "\nCan't open pipe\n");
exit(EXIT_FAILURE);
}
while(1)
{
fgets(buffer,BUFFER_SIZE,stdin);
sscanf(buffer,"%s", mode);
write(fd,buffer,strlen(buffer));
}
}
Are you sure there's a problem? You are reading from stdin (the fgets at the bottom), and writing to the pipe. What you're missing is something reading from the pipe. So if in another terminal you type:
$ cat /tmp/father
then anything you type into your prog will appear there.
So, in one terminal I do:
$ ./test /tmp/father
line one
line two
And in the second terminal:
$ cat /tmp/father
and I see:
line one
line two
No?
P.S. You are doing sscanf to read from buffer and write to mode, then writing out the buffer string. Not that it matters, but you're not using mode.
Related
I have built a IPC prgram with FIFO(named piped).
A very interesting problem is that my written message may be lost.
the following is the code snippet.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
int main()
{
char buffer[2000] = {0};
strcpy(buffer, "abc");
char *write_path = "test-123";
mkfifo(write_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
int dummy = open(write_path, O_RDONLY | O_NONBLOCK);
int fd = open(write_path, O_WRONLY | O_NONBLOCK);
int bytes = write(fd, buffer, 2000);
printf("write_path:%d %s %d %d\n", bytes, write_path, dummy, fd);
close(fd);
close(dummy);
}
how to reproduce?
ubuntu 1804
gcc main.c -o main
./main
cat < test-123
it will be pending. I think, it should output abc.
You open the FIFO in nonblocking mode, which in means I/O functions can fail with (-1 and errno set to) EAGAIN or EWOULDBLOCK, instead of blocking (waiting) for the function to complete. In Linux, open descriptors to a FIFO have the same semantics as pipes.
If you bothered to check for errors (printing strerror(errno) whenever open() or write() returns -1 indicating an error), you'd already know the reason why this fails. As is, the code shown does not even reproduce the problem. The following code,
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
const char *msg = "The Example Message.\n";
const size_t msg_len = strlen(msg);
int rfd, wfd;
if (argc != 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *mypath = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", mypath);
fprintf(stderr, " %s FIFO\n", mypath);
fprintf(stderr, "\n");
fprintf(stderr, "This program tests opening and writing a short message to the FIFO.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
rfd = open(argv[1], O_RDONLY | O_NONBLOCK);
if (rfd == -1) {
fprintf(stderr, "%s: Cannot open FIFO for reading: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
wfd = open(argv[1], O_WRONLY | O_NONBLOCK);
if (wfd == -1) {
fprintf(stderr, "%s: Cannot open FIFO for writing: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
}
ssize_t n = write(wfd, msg, msg_len);
if (n == -1) {
fprintf(stderr, "%s: Cannot write to FIFO: %s.\n", argv[1], strerror(errno));
return EXIT_FAILURE;
} else
if (n != (ssize_t)msg_len) {
fprintf(stderr, "%s: Wrote only %zd of %zu bytes to the FIFO.\n", argv[1], n, msg_len);
return EXIT_FAILURE;
} else {
printf("Success!\n");
}
close(wfd);
close(rfd);
return EXIT_SUCCESS;
}
compiles (gcc -Wall -Wextra -O2 source.c -o binary) cleanly, and implements the shown code (but with error checking, and using a command line parameter for the name of the FIFO to be opened). It does not verify the named file is a FIFO, though. If run on a non-existent FIFO, it complains "FIFO: Cannot open FIFO for reading: No such file or directory."
If you create the FIFO (mkfifo test-fifo) and run the test program, there are no errors; the only output is "Success!".
If you create the FIFO, and repeatedly write data to it but never read from it, at some point the kernel buffer for the FIFO becomes full, and running the test program will report "test-fifo: Cannot write to FIFO: Resource temporarily unavailable." (which means that write() returned -1 with errno==EWOULDBLOCK or errno==EAGAIN).
You can simulate this by running e.g. bash -c 'exec 4<>test-fifo ; dd if=/dev/zero of=test-fifo bs=1 oflag=nonblock status=progress', which opens the test-fifo FIFO read-write (which in Linux always succeeds) to descriptor 4, then runs dd to fill it with zeroes, using Bash (sub-)shell. On my system, a FIFO can hold 65536 bytes (64k). After filling the FIFO like this, running the test program (./binary test-fifo) will fail as described.
Hopefully, you'll see the light and the importance and usefulness of error checking. It is NOT something you should consider as "I'll add them in later when/if I have time"; they are also an important development tool.
I have to use 3 processes in order to solve the problem.
First processes gets input from (entered via keyboard) and sends it to the second procces
The second process replaces all the vocals from the text with 12345 (a with 1, e with 2, ...). I got a well working sh script (tested it) that uses sed to do this task. I will put it here.
Thrid process outputs on the screen only the alphanumeric lines. I also got a script that uses grep to do this task and also works fine (tested it).
This processes should communicate trough a named pipe (a FIFO file) and i'm running into some difficulties sending and receiving the data trough the FIFO. When i use the write function to write the data to the FIFO it outputs the data on the screen and when i'm in the second process and i try to read the data from the FIFO it just waits for a new input entered by me.
First process:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main() {
char text[500]; // buffer for the inputed text
char aux[100]; // Also a buffer to save multi-line input into text buffer
char* myfifo = "myfifo";
int fd_text;
printf("\nInput: (to stop giving input just type 0 on a new line):\n");
// Forming the text
while(scanf("%[^\n]%*c", aux) == 1) {
if(strcmp(aux, "0") == 0)
break;
strcat(aux, "\n");
strcat(text, aux);
}
strcat(text, "\0");
// Everything works well reading the input
int returnValue = mkfifo(myfifo, 0666);
if(returnValue < 0) {
printf("mkfifo() failed\nerrno = %d\n", errno);
if(errno == EEXIST)
printf("That file already exists.\n");
}
if(fd_text = open(myfifo, O_WRONLY) < 0) {
printf("error while opening FIFO");
exit(0);
}
int indicator = write(fd_text, text, strlen(text) + 1);
if(indicator == 0) {
printf("error while writing to FIFO");
}
close(fd_text);
return 0;
}
Second process:
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
char text[500];
char* myfifo = "myfifo";
int fd_text2;
if (fd_text2 = open(myfifo, O_RDONLY) < 0) {
printf("error while opening FIFO");
exit(1);
}
int result = read(fd_text2, text, 500);
if (result < 0)
printf("ERROR WHILE READING FROM THE FILE\n");
printf("\n%d bytes were read from the file\n", result);
printf("\nText read from FIFO:\n%s\n", text);
// Saving the text into a txt to perfom the sed command on it
FILE *fp_replace_text;
fp_replace_text = fopen("replace_text.txt", "w");
fprintf(fp_replace_text, "%s", text);
fclose(fp_replace_text);
system("chmod 777 replace.sh");
system("./replace.sh replace_text.txt");
close(fd_text2);
unlink(myfifo);
return 0;
}
replace.sh
#!/bin/sh
sed -i 's/a/1/gi' $1
sed -i 's/e/2/gi' $1
sed -i 's/i/3/gi' $1
sed -i 's/o/4/gi' $1
sed -i 's/u/5/gi' $1
If i can figure out why communication between process 1 and 2 is not working, process 3 will be the same, so i'm not posting it anymore.
Edit:
Second Process
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main() {
char text[500];
char* myfifo = "myfifo";
int fd_text2;
if ((fd_text2 = open(myfifo, O_RDWR)) < 0) {
printf("error while opening pipe");
exit(1);
}
int result = read(fd_text2, text, 500);
if (result < 0)
printf("ERROR WHILE READING FROM THE FILE\n");
FILE *fp_replace_text;
fp_replace_text = fopen("replace_text.txt", "w");
fprintf(fp_replace_text, "%s", text);
fclose(fp_replace_text);
system("chmod 777 replace.sh");
system("./replace.sh replace_text.txt");
fp_replace_text = fopen("replace_text.txt", "r");
fread(text, 500, 1, fp_replace_text);
fclose(fp_replace_text);
write(fd_text2, text, strlen(text) + 1);
close(fd_text2);
return 0;
}
Third process
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main() {
char text[500];
char* myfifo = "myfifo";
int fd_text3;
if ((fd_text3 = open(myfifo, O_RDONLY)) < 0) {
printf("error while opening pipe");
exit(1);
}
int result = read(fd_text3, text, 500);
if (result < 0)
printf("ERROR WHILE READING FROM THE FILE\n");
FILE *fp_replace_text;
fp_replace_text = fopen("replace_text.txt", "r");
fread(text, 500, 1, fp_replace_text);
fclose(fp_replace_text);
printf("Textul final este:\n");
system("chmod 777 output.sh");
system("./output.sh replace_text.txt");
unlink(myfifo);
return 0;
}
I'm also trying to send the content of the txt filo to the third process trough FIFO (i know i could just use the txt file, but i want to use the FIFO file and read from it again). So i'm use the shell script on the txt file, i read from and and then i want to send what i read from the txt file to the thrid process to FIFO, but after i run the second process the execution stops and doesn't blocks. When i'm writing to the FIFO in the first process, it blocks until the second process reads from the FIFO. Second process keeps executing and third process can't get anything. I guess there has to be something wrong with FIFO principles, that if u want to perform input/output operations on a FIFO both ends should be opened.
The problem (which ought to have cause a warning from your compiler) is in statements like this:
if(fd_text = open(myfifo, O_WRONLY) < 0)
You seem to assume that this will be evaluated from left to right, first assigning a value to fd_text, then testing that value against 0 to give a boolean value which will determine whether to enter the if block. But in C, relational operators (like <) take precedence over assignment operators (like =). So first we compare the result of the open command to 0, then assign the boolean result of that comparison to fd_text. So the file descriptor is lost, and further attempts to reach the fifo will fail. We can correct this with a pair of parentheses:
if((fd_text = open(myfifo, O_WRONLY)) < 0)
More generally, it's a good idea to develop new functionality in isolation as much as possible. If you want to send text to a fifo and receive it, just hard-code some text in the first module, don't have all that code to read user input, and don't attempt the further step of sending the text from the second module to some other process for modification, not until the fifo is working perfectly. Small steps.
The code is following.
Q1:
If dup2(fd3, STDOUT_FILENO), string2 will be in log.txt.
If dup2(g_ctl[0], STDOUT_FILENO), string2 won't be received by g_ctl[1].
string1 and ls -al output will be received, Why ?
Q2:
The third library have some stdout/stderr log, if using dup2(socket_fd, STDOUT_FILENO), all logs will be collected by socket. But I also want to print all logs to screen at the same time, how to do it?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
static pthread_t g_pid;
static int g_ctl[2] = {-1, -1};
void *_run_loop(void *args) {
char buf[1024];
int n;
while (1) {
n = recv(g_ctl[1], buf, 1024, 0);
if (n > 0) {
fprintf(stderr, "%.*s\n", n, buf);
}
}
}
int main(int argc, char const *argv[])
{
int fd3 = open("./log.txt", O_CREAT | O_RDWR | O_APPEND, 0666);
int ret = socketpair(AF_UNIX, SOCK_STREAM, 0, g_ctl);
assert(ret == 0);
ret = dup2(g_ctl[0], STDOUT_FILENO);
assert(ret > 0);
pthread_create(&g_pid, NULL, _run_loop, NULL);
send(STDOUT_FILENO, "string1", 5, 0);
system("ls -al");
printf("string2\n");
sleep(5);
return 0;
}
Q1: You need to fflush(stdout); after your printf. Otherwise printf may buffer your output. It will be written when your program exits if it hasn't already, but by that time your reading thread has already been canceled, so you don't get to read it.
Q2: As far as I know, the only way to get your output written to two files is to actually write it to both file descriptors. There is no way in Unix to "double-dup" a file descriptor. Even a command like tee is really just calling write() twice for each chunk of data read. You can do it manually, or inside a function, or in a thread, but you have to do it.
I need to get the file from the terminal, I know the command will look like:
./a.out < fileName.txt
I'm not sure how to use fgets() in my program to use the file requested from the terminal.
Using redirection sends the contents of the input file to stdin, so you need to read from stdin inside your code, so something like (error checking omitted for clarity)
#include <stdio.h>
#define BUFFERSIZE 100
int main (int argc, char *argv[])
{
char buffer[BUFFERSIZE];
fgets(buffer, BUFFERSIZE , stdin);
printf("Read: %s", buffer);
return 0;
}
1.) you close stdin then assign a different file handler to it
2.) replace stdin with any other file handler
using dup2 function you can achieve it
Short Answer
open() your file, then dup2() your file descriptor towards Standard Input.
A dummy example
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd;
char *command[] = {"/usr/bin/sort", NULL};
if (close(STDIN_FILENO) < 0)
{
perror("Error close()");
exit(EXIT_FAILURE);
}
if (fd = open(argv[2], O_RDONLY, S_IWUSR | S_IRUSR) < 0)
{
perror("Error open()");
exit(EXIT_FAILURE);
}
if (dup2(fd, STDIN_FILENO) < 0)
{
perror("Error dup2()");
exit(EXIT_FAILURE);
}
if (execv(command[0], command) < 0)
{
perror("Error execv()");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
Output:
$ ./a.out < JohnLennon_Imagine_Lyrics.txt
Above us, only sky
A brotherhood of man
Ah
And no religion, too
And the world will be as one
And the world will live as one
But I'm not the only one
But I'm not the only one
I hope someday you'll join us
I hope someday you'll join us
Imagine all the people
Imagine all the people
Imagine all the people
Imagine no possessions
Imagine there's no countries
Imagine there's no heaven
It isn't hard to do
It's easy if you try
I wonder if you can
Livin' for today
Livin' life in peace
No hell below us
No need for greed or hunger
Nothing to kill or die for
Sharing all the world
You
You
You may say I'm a dreamer
You may say I'm a dreamer
You can use fread function
#include <stdio.h>
#include <malloc.h>
int main () {
fseek(stdin, 0, SEEK_END);
long size = ftell(stdin);
rewind(stdin);
char *buffer = (char*) malloc(size);
fread(buffer, size, 1, stdin);
printf("Buffer content:\n%s\n", buffer);
return 0;
}
I need to write to an executable file that is being executed, but I can't open it for writing. For example:
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
int fd = open(argv[0], O_RDWR);
if (fd == -1) perror(NULL);
return 0;
}
% uname -rs
FreeBSD 8.0-STABLE
% ./example_ETXTBSY
Text file busy
There are some explanations what the heck is ETXTBSY in Linux, but nevertheless, is it possible to override this error?
P.S.
I'm not trying to write a virus.
If you are trying to replace an executing file, as opposed to modifying an executable on the fly, you can unlink() it first and then open it for writing.
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
int main(int argc, char **argv)
{
unlink(argv[0]);
int fd = open(argv[0], O_RDWR|O_CREAT, S_IRWXU|S_IRWXG|S_IRWXO);
if (fd == -1) perror(NULL);
return 0;
}
If you are trying to get access to the actual running process, your best bet is ptrace().
(Edited to add the mode bits.)