Outputting to multiple terminals - c

I'm parsing a large log file. I would like to pull out particular messages based on a severity classification (critical, warning, etc). As I am parsing the log I would like to send messages, based on their severity, to a given, spawned, severity-specific, terminal. The terminals should not close when the main program finishes reading the log file. Environment is solaris 10/gcc 3.4.6. I found the following example that captures what I was thinking of, but doesn't quite work (xterm spawns, but no output is sent to it):
#include <stdio.h>
int main()
{
FILE *output;
int i;
output = popen ("xterm", "w");
for (i = 0; i < 10; i++)
fprintf (output, "%d\n", i);
pclose (output);
}
I'm not married to xterm, I simply used it as handy and I do like the ability to title, color and size them. I sort of get that writing to the handle generated by popen is not the same as writing to the terminal's output (just sort of...). This seems like an easy thing to do.

in general, what can be done is:
1) adjust your application so it can accept a parameter that indicates what level of severity to output to stdout.
2) open three terminals from the keyboard
3) run your application in each terminal, giving each execution the appropriate command line parameter
if your going to open the terminals from your application, for 'xterm' and others, the --hold parameter will stop the terminal from closing when you application exits

The idea of the pipe sounds reasonable, but there are a few problems:
you cannot, of course, simply write to a newly-spawned terminal via a pipe. xterm will ignore that; it is the application running within xterm which reads/writes from the pseudo-terminal.
there is no way to scroll back in the messages sent to a given terminal
the suggested writes to the pty device cannot be captured by a program running in the terminal
Rather than sending to a terminal, you might consider getting xless (a simple X application which might already be part of a package, but is simple enough to build), and structuring your output function to do this:
for each message category, open a pipe to xless once
write the message
do not close the pipe
That would give you windows which do not close. Here is a sample screenshot:
The source can be found on ftp.x.org, as noted in the Debian package description.

Thank you all for the great responses, I do appreciate them!
OK, sounds like a slight re-design is called for =) I did a quick prototype and it looks like it will work. Will write the output to separate severity files, then when complete, pop up an xterm for each file, something like
sprintf (Crit, "xterm <yadda yadda> -e sh -c 'cat <crit-log>; <yadda yadda>');
popen (Crit, "r");
Writing to intermediate log files is actually a bonus should they need to be reviewed at a later time without having to go through the large-log-parsing program again.
Thanks again everyone!

Related

How can I handle _popen() errors in C?

Good morning;
Right now, I'm writing a program which makes a Montecarlo simulation of a physical process and then pipes the data generated to gnuplot to plot a graphical representation. The simulation and plotting work just fine; but I'm interested in printing an error message which informs the user that gnuplot is not installed. In order to manage this, I've tried the following code:
#include <stdio.h>
#include <stdlib.h>
FILE *pipe_gnuplot;
int main()
{
pipe_gnuplot = _popen("gnuplot -persist", "w");
if (pipe_gnuplot==NULL)
{
printf("ERROR. INSTALL gnuplot FIRST!\n");
exit (1);
}
return 0;
}
But, instead of printing my error message, "gnuplot is not recognized as an internal or external command, operable program or batch file" appears (the program runs on Windows). I don't understand what I'm doing wrong. According to _popen documentation, NULL should be returned if the pipe opening fails. Can you help me managing this issue? Thanks in advance and sorry if the question is very basic.
Error handling of popen (or _popen) is difficult.
popen creates a pipe and a process. If this fails, you will get a NULL result, but this occurs only in rare cases. (no more system resources to create a pipe or process or wrong second argument)
popen passes your command line to a shell (UNIX) or to the command processor (Windows). I'm not sure if you would get a NULL result if the system cannot execute the shell or command processor respectively.
The command line will be parsed by the shell or command processor and errors are handled as if you entered the command manually, e.g. resulting in an error message and/or a non-zero exit code.
A successful popen means nothing more than that the system could successfully start the shell or command processor. There is no direct way to check for errors executing the command or to get the exit code of the command.
Generally I would avoid using popen if possible.
If you want to program specifically for Windows, check if you can get better error handling from Windows API functions like CreateProcess.
Otherwise you could wrap your command in a script that checks the result and prints specific messages you can read and parse to distinguish between success and error. (I don't recommend this approach.)
Just to piggy-back on #Bodo's answer, on a POSIX-compatible system you can use wait() to wait for a single child process to return, and obtain its exit status (which would typically be 127 if the command was not found).
Since you are on Windows you have _cwait(), but this does not appear to be compatible with how _popen is implemented, as it requires a handle to the child process, which _popen does not return or give any obvious access to.
Therefore, it seems the best thing to do is to essentially manually re-implemented popen() by creating a pipe manually and spawning the process with one of the spawn[lv][p][e] functions. In fact the docs for _pipe() give an example of how one might do this (although in your case you want to redirect the child process's stdin to the write end of your pipe).
I have not tried writing an example though.

How do I copy everything from my terminal to a file including the stdout and the prompt using C?

I know how to get the stdout into a file using dup/dup2 system calls, but how do I get the entire output that would be normally shown on my terminal(including the prompt that says my username along with the $ symbol and the current working directory) to a file?
Yes you can, but this may be difficult in many details (depending on your expert level). For the shell to behave normally (I would mean exactly as in a terminal), then it needs to interact with a terminal (special system object). So you need to create a program that behave like a terminal, this what pseudo-terminals devices (/dev) are intended for. Read documentation about this to implement it but roughly, your application should behave like the user so should be connected to the slave side of the pseudo-terminal, and the shell to the master side of the pseudo-terminal. Then you can easily log real inputs made by the user and catch outputs made by the shell.
Can't comment cause of low reputation.
I would say there is no way to do that inside a code in C. Instead, you could use bash for example to redirect everything to a file, and leave the code in C as it is.
In this way you have all the info you want to save: prompt, current directory, call to the program (including flags), and of course the output of the program.
Well, you can do:
-For bash prompt PS1: Echo expanded PS1 (in case you want it expanded, if not there is a simple way to do it just echong PS1)
- For executed command: https://unix.stackexchange.com/questions/169259/how-to-capture-command-line-input-into-logfile-and-execute-it-at-the-same-time
- Standard output and error output: Redirect stderr and stdout in a Bash script
And that's all you want to capture, I think.
Look up the script command in Unix systems. If you want to capture all keyboard and std in/out for a command, use the script executable. If you want to see how it's done, look up the source.

output to a new terminal

Suppose I create a thread from my "main" thread, which aims to monitor and print some variables in "main". But "main" is also generating some output, so I want to print the outputs of these two threads separately. How can I redirect the output of the new thread to a new console other than the one "main" thread is using in my c program?
I would output the monitoring information to a file, then use tail -f filename to display it in my other terminal.
That has the advantage that you can run grep or what-have-you on the output as well.
I would go with writing the data to a log file and then using tail -f log_file.txt from another window to watch it since that way you get to keep a copy of the data, but if you decide that you need to use separate terminals then you can just open the new terminal (tty) like a regular file. The main problem with this is that you probably really want it to open a new terminal window for you as well.
Opening a new terminal window to do something like this is tricky and is different between different terminal emulators. Here is another question about opening new terminals from a make file. You could try doing the same thing from your program or from a script that runs your program and run cat or tail in the new terminal to be your log window.
#!/bin/sh
truncate --size=0 ./logfile.txt
xterm "tail -f ./logfile.txt" 2>&1 > /dev/null &
your_program --log-file=./logfile.txt
I don't currently know of a better way to accomplish this.
Another thing you might want to look into is syslog.
What would you expect to happen in such case (externally)?
If you run a program you are attached to a console. You of course don't have to write output to the console (you can use files), but the console cannot be duplicated obviously.
Isn't using a different file descriptor good enough for you?

ftp client controlled by pipe in C

I am trying to control ftp client from C program (OS X). I did fork and execve - process is started ok. The problem is with pipes - I can send command to ftp client process and get feedback from it just fine (If i send "help\n" i get back help output) but what I never get in pipe is "ftp> " prompt. Any ideas?
Ivan
Your ftp client is probably behaving differently if stdin/stdout is a terminal or something else (lots of program do, for a start the C library does buffering in a different way...) If you want to control that, search information about pseudo-terminals, that's a little too technical to be explained here. (And looks first at programs like expect, it's possible you won't have to write yours).
A program can examine stdin to find out whether it's a terminal or a pipe. In your case, the FTP program probably does that (for example to know whether it can use escape sequences to render progress bars or offer command line editing).
If you really need the prompt, you have to look into PTYs (pseudo terminals) which emulate a console.
wild guess: isn't the "ftp>" prompt written to STDERR ?

How to access the default stdin while using file redirection?

I need to run a script and have access to the default stdin (terminal input) in my program. I could do ./program "script", opening and parsing the script through the program, but I want to make it POSIX style, accepting input from pipes or from redirection.
I mean, since my program is a parser, I could run ./program, type the script and still use stdin (in a scanf, for example). But I'd like to run ./program < script and still be able to use stdin (in a scanf).
My program is a simplified Pascal interpreter, that's why I need to run read(x) and write(x) in my scripts.
Yes, it's homework (the intepreter), but the doubt just popped up in the brainstorming process.
The current controlling terminal can be accessed using /dev/tty, even if stdin has been redirected.
ttyname(0) will return the filename of the current terminal associated with stdin. You can then open that and read from it.
If I understand what you're asking, you're asking for the ability to take in interactive input from a user when using file redirection, like the ./program < script bit above.
I don't believe there's a way to do that. A POSIX system will feed the script in via stdin and that's that. No interaction from the user.
It's also worth noting that you don't have to do anything special to realize that. Just treat stdin like you normally would. You don't have to think about whether it's coming in interactively or from a file, which is really quite nice.

Resources