I have tested the below code to sort the strings.
Reason i added sleep to check, if sort program will wait on its read pipe for the information to sort. Below program works without sleep() call, why?
#include <stdio.h>
#define MAXSTRS 5
int main(void)
{
int cntr;
FILE *pipe_fp;
char *strings[MAXSTRS] = { "echo", "bravo", "alpha",
"charlie", "delta"};
/* Create one way pipe line with call to popen() */
if (( pipe_fp = popen("sort", "w")) == NULL)
{
perror("popen");
exit(1);
}
//printf("sleeping\n");
//sleep(10);
/* Processing loop */
for(cntr=0; cntr<MAXSTRS; cntr++) {
fputs(strings[cntr], pipe_fp);
fputc('\n', pipe_fp);
}
/* Close the pipe */
pclose(pipe_fp);
return(0);
}
You probably are just running in a limitation of the site you are using to test your code. With smaller values for the sleep() (e. g. 1) this works just fine. I guess that the test site is applying timeouts to the programs it compiles and runs in order to have a decently reactive web site.
Get a decent unix and try it there; you probably will have as little trouble executing it as I did.
Related
I'm not an expert in C and I'm looking for some advice to to make my program more robust and reliable. Just to give some context: I've written a program to do some scientific computation that takes quite a long time (about 20h) that I'm executing on a large university HPC linux cluster using a SLRUM scheduling system and NFS mounted file systems. What seems to happen is that some time during the 20h the connection to the file system goes stale (on the entire machine; independent of my program) and the first attempt to open & write a file takes a really long time and that results in a segfault cored dumped error that I have so far not been able to precisely track down. Below is a minimal file that at least conceptually reproduces the error: The program starts, opens a file and everything works. The program does some long computation (simulated by sleep()), tries to open & write to the same file again, and it fails. What are some conventions to make my code more robust and reliably write my results to file without crashing?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv) {
// Declare variables
FILE *outfile;
char outname[150] = "result.csv";
// Open file for writing
printf("CHECKING if output file '%s' is writable?", outname);
outfile=fopen(outname, "w");
if (outfile == NULL) {
perror("Failed: ");
exit(EXIT_FAILURE);
}
fclose(outfile);
printf(" PASSED.\n");
// Do some computation that takes really long (around 19h)
sleep(3);
// Open file again and Write results
printf("Writing results to %s ...", outname);
outfile=fopen(outname, "w");
if (outfile == NULL) {
perror("Failed writing in tabulate_vector_new: ");
exit(EXIT_FAILURE);
}
fprintf( outfile, "This is the important result.\n");
fclose(outfile);
printf(" DONE.\n");
return 0;
}
It seems odd that your program would segfault due to an NFS issue. I would expect it to hang indefinitely, not crash. That having been said, I would suggest forking a new process to check whether the NFS mount is working. That way, your important code won't be directly involved in testing the problematic file system. Something like the following approach may be useful:
pid_t pid = fork();
if (pid == -1)
{
// error, failed to fork(). should probably give up now. something is really wrong.
}
else if (pid > 0)
{
// if the child exits, it has successfully interacted with the NFS file system
wait(NULL);
// proceed with attempting to write important data
}
else
{
// we are the child; fork df in order to test the NFS file system
execlp("df", "df", "/mnt", (char *)NULL)
// the child has been replaced by df, which will try to statfs(2) /mnt for us
}
The general concept here is that we utilize the df command to check whether the NFS file system (which I assume is at /mnt) is working. If it's temporarily not working, df should hang until it starts working again, and then exit, returning control to your program. If you suspect df might hang forever, you could enhance my example by using alarm(2) to wait a certain period of time, probably at least a few minutes, after which you could retry running df. Note that this could result in zombie df processes sticking around.
In the end, the correct solution is to try to get a more reliable NFS server, but until you can do that, I hope this is helpful.
Is The benefit of using popen is only to read the ouput produced by a command or there are some more benefits or advantages of popen over system.
Consider two programs below:
Program 1:
#include <stdio.h>
int main()
{
FILE *more,*who;
if (!(more = popen("more", "w")))
{
printf("Command `more` not found!");
return -1;
}
if (!(who = popen("who", "r")))
{
printf("Command `who` not found!");
return -1;
}
while (!feof(who))
{
char buffer[100];
if (fgets(buffer, 100, who) != NULL)
{
fputs(buffer, more);
}
}
fclose(more);
fclose(who);
return 0;
}
Program 2:
#include <unistd.h>
int main()
{
system("who|more");
return 0;
}
Why should i use Program1 if i can do the same thing in one line as done in Program2.
The two programs you have provided as examples are not equivalent. popen gives you a pair of file handles you can use to read and write input and output to and from stdin/stdout of the running process in an interactive manner. The system call is merely executing that and redirecting stdin of the current process to that of the invoked child process, and stdout of that process to stdout of the current (host) process.
It depends what you are trying to achieve, in general. If your goal is simply to run a command, system works fine. If you're interested in reading its output in a programmatic manner and processing it (and possibly generating more input), then popen is going to work better.
What is the best way to do this theoretically? I need to let the user enter the number of processes to send to a pipe for instance "3" and as it loops through the three [three whats?] on each iteration I need to create a process, send it [what?] to the pipe and print it.
The next time the user enters another number, say "4", it should print the previous 3 + 1.. I am working on this but can't understand how do it. Here is my code. I just need guidance, no need to try to solve it for me (but suggestions would be much appreciated).
Right now I am able to send one through the pipe and return it but then the pipe closes and it does not allow for the other processes to get in there.
Suggestion #1: Use functions
Use functions, even for little jobs such as:
void create_fifo(const char *name)
{
/* Create the first named - pipe */
int ret_val = mkfifo(name, 0666);
if ((ret_val == -1) && (errno != EEXIST))
{
perror("Error creating the named pipe");
exit(1);
}
}
Now you can simply write in your main program:
create_fifo(PIPE1);
create_fifo(PIPE5);
This cuts down on the clutter in your main program. It also adheres to the Agile principle DRY - Don't Repeat Yourself.
Suggestion #2: Error check system calls.
You did that for creating the FIFOs, which is good. You don't for the open() calls, or the read() or write() calls. You probably should. I use a function similar to the following in my programs:
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
static const char *arg0 = "did not call err_setarg0(argv[0])";
void err_setarg0(const char *argv0)
{
arg0 = argv0;
}
void err_exit(const char *fmt, ...)
{
int errnum = errno; /* Capture errno before it is changed */
va_lists args;
fprintf(stderr, "%s: ", arg0);
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
if (errnum != 0)
fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
exit(1);
}
You can then use:
if ((rdfd1 = open(PIPE1, O_RDONLY)) < 0)
err_exit("Failed to open FIFO %s for reading: ", PIPE1);
if ((wrfd1 = open(PIPE5, O_WRONLY)) < 0)
err_exit("Failed to open FIFO %s for writing: ", PIPE5);
Suggestion #3: Make an iterative server
Your server program currently opens the FIFOs once, then reads from one, write to the other, and terminates. You need a loop around some portion of this code, maybe two nested loops. You have to decide whether you need an inner loop to read until EOF. You also need to know how you will terminate the server.
Suggestion #4: Maybe the server needs pipe names as arguments
Your server currently works on fixed FIFO names. You probably need it to take input and output file names as command line arguments, so that when your client spawns multiple servers, each server can have its own set of FIFOs, rather than all processes sharing the same two FIFOs, which is going to lead to confusion and chaos.
Indeed, the need for generating names calls the whole design into question - are you sure using FIFOs is the best way to do this? It looks to me like a case where anonymous pipes would serve you better; you wouldn't have to invent names, and the server would simply read from its standard input and write the (modified?) data to its standard output, so you could even simply use cat or tr or sed or ... as your server.
Clearly, if you use pipes, you will need to do some careful plumbing, but you also need to do careful plumbing with the pairs of FIFOs per server.
I am writing a C program on unix which should redirect it's output to the file, and write to it some text every second in infinite loop:
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(void) {
int outDes = open("./output.txt", O_APPEND | O_WRONLY);
dup2(outDes, 1);
while(1) {
printf("output text\n");
sleep(1);
}
}
But it writes nothing to the output file. I tried to change the 'while' loop for 'for' with 10 loops, and I found that it writes all 10 lines to the file at once after the series ends. It is not very good for me, while I need to have an infinite loop.
When I'm not redirecting output, it is all ok, and new line appears every second on terminal.
I also tried to put one
printf("text\n");
before redirecting output to the file. Then the program wrote the lines to the file in real time, which is good, but wrote there the first (non redirected) line too. I don't want this first line in my output file, I don't understand how it could be written into file when output was not redirected yet (maybe redirect remained there since last run?), and how it could cause that the lines are suddenly written in real time.
Can anyone explain me how does it work?
You are not checking the return value of open() and dup2(). If either open() or dup2() failed, it won't write anything in output.txt.
if (outDes < -1) {
perror("open");
return 1;
}
if (dup2(outDes, 1) == -1) {
perror("dup2");
return 1;
}
stdio streams are buffered, and the writes happen in memory before being done on the real file description.
Try adding a fflush(stdout) after printf().
You're running afoul of a poorly documented DWIMmy feature in many Unix C libraries. The first time you write to stdout or stderr, the library probes the underlying file descriptor (with isatty(3)). If it's a (pseudo-)terminal, the library puts the FILE in "line buffered" mode, meaning that it'll buffer input until a newline is written and then flush it all to the OS. But if the file descriptor is not a terminal, it puts the FILE in "fully buffered" mode, where it'll buffer something like BUFSIZ bytes of output before flushing them, and pays no attention to line breaks.
This is normally the behavior you want, but if you don't want it (as in this case), you can change it with setvbuf(3). This function (although not the behavior I described above) is ISO standard C. Here's how to use it in your case.
#include <stdio.h>
#include <unistd.h>
int
main(void)
{
if (freopen("output.txt", "a", stdout)) {
perror("freopen");
return 1;
}
if (setvbuf(stdout, 0, _IOLBF, 0)) {
perror("setvbuf");
return 1;
}
for (;;) {
puts("output text");
sleep(1);
}
/* not reached */
}
I'm very new to C so please bear with me. I am struggling with this for really long time and I had a hard time to narrow down the cause of error.
I noticed that when forking process and writing to a file (only the original process writes to the file a strange thing happens, the output is nearly multiplied by the number of forks, hard to explain, thus I made a small test code where you can run and it recreates the problem.
#include <stdio.h>
#include <stdlib.h>
void foo()
{
FILE* file = fopen("test", "w");
int i=3;
int pid;
while (i>0)
{
pid=fork();
if(pid==0)
{
printf("Child\n");
exit(0);
}
else if(pid > 0)
{
fputs("test\n", file);
i=i-1;
}
}
}
int main()
{
foo();
exit(EXIT_SUCCESS);
}
Compile and run it once the way it is and once with file=stdout. When writing to stdout the output is:
test
test
test
But when writing to the file the output is:
test
test
test
test
test
test
Also if you add indexing and change i to a larger number you can see some kind of a pattern, but that doesn't help me.
Well frankly said I have no idea why could this happen, neither how to fix it. But I am a total novice at C so there might be just a normal logical explanation for all this =).
Thank you for all your time and answers.
stdout is usually unbuffered or line buffered; other files are typically block buffered. You need to fflush() them before fork(), or every child will flush its own copy of the buffer, leading to this multiplication.