Cygwin reading input piped in from tail -f - c

Using Cygwin on Windows, I wanted to have an audible notification of specific messages in a server's log. I wrote the following:
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *f = fopen("/dev/stdin", "r");
char bar=' ';
if(f==NULL) {
return 1;
}
do {
bar = fgetc(f);
if((bar=='\n') || (bar=='\r')) {
printf("\a");
}
if(bar!=EOF) {
printf("%c", bar);
}
} while(bar!=EOF);
fclose(f);
printf("Done.\n");
return 0;
}
I then ran the following command:
tail -f serverlog | grep myMessage | ./alerty.exe
Sometimes I get notices and sometimes I don't.
My questions are two-fold:
1) What, in my C program, is wrong? Why can't I consistently read the piped input? It's piqued my curiosity so I'm desperate to know.
2) How do I accomplish the original goal of making my system beep as specific text appears in a file?

By default stdin/stdout are line-buffered if they are terminal and block-buffered otherwise. That affects not just your program (actually gets will return immediately when something is available and you are printing lines), but also the grep. It needs --line-buffered flag.
Sed should be able to do the work for you. Try just:
tail -f serverlog | sed -une 's/myMessage/\a&/p'
(-u sets unbuffered—hopefuly cygwin supports it—I am checking on Linux)

stdout is buffered by default, so the output won't necessarily appear immediately. Try inserting a fflush(stdout) right after your printf("\a").
As Jan mentions, you also may be running into buffering issues on stdin. grep has
a --line-buffered option that might help. (tail -f does this on its own, so you shouldn't need to worry about it.)

Related

Why doesn't stdbuf line buffer the output of some simple c programs

I'm trying to use stdbuf to line buffer the output of a program but I can't seem to make it work as I would expect. Using this code:
#include <stdio.h>
#include <unistd.h>
int main (void)
{
int i=0;
for(i=0; i<10; i++)
{
printf("This is part one");
fflush(stdout);
sleep(1);
printf(" and this is part two\n");
}
return 0;
}
I see This is part one, a one second wait then and this is part two\nThis is part one.
I expected that running it as
stdbuf --output=L ./test.out
would cause the output to be a 1 second delay and then This is part one and this is part two\n repeating at one second intervals. Instead I see the same output as in the case when I don't use stdbuf.
Am I using stdbuf incorrectly or does the call to fflush count as "adjusting" the buffering as described in the sdtbuf man page?
If I can't use stdbuf to line buffer in this way is there another command line tool that makes it possible?
Here are a couple of options that work for me, given the sample code, and run interactively (the output was to a pseudo-TTY):
./program | grep ^
./program | while IFS= read -r line; do printf "%s\n" "$line"; done
In a couple of quick tests, both output a complete line at a time. If you need to do pipe it further, grep's --line-buffered option should be useful.

Simple Text GUI (TUI) Using NCurses and C to Display System Info

I am just beginning to toy around with combining ncurses and C to develop a very minimal TUI. The purpose of the TUI is to greet users with a basic login/welcome screen. The goal would be to display basic system information like the operating system, available memory, IP address, etc. Nothing beyond read-only.
What would be the best way to go about doing this? The part I'm struggling with is interfacing the shell commands like df, ls, ifconfig, etc with variables that I can then display or print in ncurses and C. I know something like this can be done, as well as calling the system command with a string, but this seems somewhat bulky:
#include <ncurses.h>
#include <stdlib.h>
#include <stdio.h>
int main(void) {
FILE *pp;
initscr();
cbreak();
if ((pp = popen("df", "r")) != 0) {
char buffer[BUFSIZ];
while (fgets(buffer, sizeof(buffer), pp) != 0) {
addstr(buffer);
}
pclose(pp);
}
getch();
return EXIT_SUCCESS;
}
Are there any methods to execute a command in the command line from within a C program and then selectively access the output of that command for later display? Or is this information generally stored somewhere in a parseable file on the machine? I'm new to trying to pull system information/use the command line in a "TUI" sense and any help would be appreciated. Thanks so much in advance!
The idea with the pipe is good and simple, anything else would lack at least one of "good and simple", most likely both. But your other question is about the availability of certain system informations. Well, these informations are wildly dispersed and the exact place depends on the operating system actually in use.
For the common, non-specialized Linux system: for the filesystem it is /etc/mtab (and use statfs() for the details) and many system informations are in /proc. If you need more, it gets complicated.
It is already quite complicated even if you want to build a simplified version of df for example (original code of df in $COREUTILS/src/df.c). Instead of just running df and read from a pipe, you have to do
read /etc/mtab and find the mount points (system might not have /etc/mtab although it should)
run statfs() on every mountpoint and print the result
you'll need over 100 error-prone lines of C-code for this, even if you skip everything fancy. And that for printing the filesystem alone.
No, just read the output of the old and well tested programs from a pipe, it's the easiest way.
EDIT:
You use the full shell if you use the pipe. That means that you can use all other tools, too.
To make it simpler to test, here is a simplified version without ncurses (making it much more complicated ;-) ), just for playing at the commandline.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SIZE 256
// ALL CHECKS OMMITTED!
char *sub_exec(const char *command)
{
char buf[BUF_SIZE];
char *ret;
size_t mem_alloc, mem_needed;
ret = malloc(4 * BUF_SIZE * sizeof(char));
// or use calloc or set \0 manually
memset(ret, '\0', 4 * BUF_SIZE);
// memory allocated
mem_alloc = 4 * BUF_SIZE;
// memory needed
mem_needed = 0;
// open the pipe read-only
FILE *out_pipe = popen(command, "r");
// until end of the output of the pipe (EOF)
while (!feof(out_pipe)) {
// read a chunk of the output
if (fgets(buf, BUF_SIZE, out_pipe) != NULL) {
mem_needed += BUF_SIZE;
if (mem_alloc < mem_needed) {
// no fancy algorithms
ret = realloc(ret, mem_needed + 4 * BUF_SIZE);
mem_alloc = mem_needed * 2;
}
// and conatenate it to the result
strncat(ret, buf, BUF_SIZE);
}
}
pclose(out_pipe);
// You may or may not readjust the memory used
// ret = realloc(ret, strlen(ret) + 1);
return ret;
}
int main(int argc, char **argv)
{
char *str_from_pipe;
if (argc < 2)
fprintf(stderr, "Usage: %s command\n", argv[0]);
str_from_pipe = sub_exec(argv[1]);
printf("%s\n", str_from_pipe);
free(str_from_pipe);
exit(EXIT_SUCCESS);
}
You can do simple things here, like
./readpipe "cat win*c | tr -d '\015' | perl -0777 -pe 's{/\*.*?\*/}{}gs' | indent -"
(concatenate all C-files, strip the \rs, strip most comments and run it through indent(1))
Or with df. Say you want the file systems with actual data in it, not tempfs or alike, so:
./readpipe "df -P -h -t ext4"
That prints here:
Filesystem Size Used Avail Use% Mounted on
/dev/sda2 48G 33G 14G 71% /
/dev/sda3 861G 761G 56G 94% /home
(quite full, as it seems)
You can use it as it is or massage it further:
./readpipe "df -h -t ext4 --output=target,fstype,size,used,avail|awk '{if(NR>1)print}'"
Prints:
/ ext4 48G 33G 14G
/home ext4 861G 761G 56G
Caveat:
./readpipe "df -h -t ext4 --output=target,fstype,size,used,avail| sed -n '1!p'"
does not work, you need to exchange the kind of quotes (but it is not always that simple)
./readpipe 'df -h -t ext4 --output=target,fstype,size,used,avail | sed -n "1!p"'
To be able to split the entries with e.g.: strtok(3) replace all whitespace with single tabs
./readpipe 'df -h -t ext4 --output=target,fstype,size,used,avail | sed -n "1!p" | sed -e "s/[ ]\+/\t/g"'
(yes, there are much more elegant ways to do it, but it is good enough)
More useful information in the files/directories about the CPU(s)
/sys/devices/system/cpu/cpu*/cpufreq/
/sys/devices/system/cpu/cpu*/cache/
Or by lscpu. There are many ls* programs that are useful, like lspci, lsusb, lskat…no, wait, that's something different, and not to forget lsblk (lists the block devices incl. partitions if avail.). A quite complete list of the installed hardware (some info need root-rights but it is already quite extensive without) is available with the help of lshw and uname is for information about the OS, also: free for the memory consumption, and many, many more. Most, if not all allow for some kind of formatting, see the respective manpages for the gory details.
If you tell me what you you need, I can tell you where to find it (he says boldly ;-) ). Just ask in a comment below, I'll add it.

How to redirect standard output to a file — what's wrong with this code?

I'm using a C program on my raspberry pi2 with a 433mhz receiver to read codes that are transmitted. This program sniffing 433mhz codes.
To run it, I use the following command: sudo ./RFSniffer and if a code is found, the program displays in the console something like :
Received 5204
But, I would like to be able to get these codes in a file, so I tried this:
sudo ./RFSniffer >> codes.txt
But nothing is appended to my codes.txt file...and I don't know why. What's wrong with my code? The file is always empty.
Here is my code :
#include "RCSwitch.h"
#include <stdlib.h>
#include <stdio.h>
RCSwitch mySwitch;
int main(int argc, char *argv[]) {
int PIN = 2;
if(wiringPiSetup() == -1)
return 0;
mySwitch = RCSwitch();
mySwitch.enableReceive(PIN);
while(1) {
if (mySwitch.available()) {
int value = mySwitch.getReceivedValue();
if (value == 0) {
printf("Unknown encoding");
} else {
printf("Received %i\n", mySwitch.getReceivedValue() );
}
mySwitch.resetAvailable();
}
}
exit(0);
}
Could the problem be exit(0) or printf() instead of anything else?
EDIT:
The program is compiled with WiringPI lib so there is a flag '-lwiringPi'
The tool is available here: https://github.com/ninjablocks/433Utils/tree/master/RPi_utils
EDIT2:
I changed the code to:
int main(int argc, char *argv[]) {
printf("yeah\n");
exit(0);
}
And it works only with:
sudo sh -c './RFSniffer >> /home/pi/433Utils/RPi_utils/codes.txt'
So the problem is maybe while(1) { printf... }? Or the file is only written when exit(0) is called?
You are writing on stdout which is buffered by default, and as I cannot see any break, return or exit in your loop, I assume that you quit your program with Ctrl-C.
And all the values that were buffered until there are simply discarded.
You should simply fflush stdout after each write to make sure that what is received will end in your file :
while(1) {
if (mySwitch.available()) {
int value = mySwitch.getReceivedValue();
if (value == 0) {
printf("Unknown encoding");
} else {
printf("Received %i\n", mySwitch.getReceivedValue() );
}
fflush(stdout); // force immediate output
mySwitch.resetAvailable();
}
And anyway, having to Ctrl-C to exit a program is not really nice ...
I believe your problem is "sudo". Are you sure you know where codes.txt is? Give it an absolute path (e.g. >> /tmp/codes.txt).
You can use tee command, that will store your stream data in file
You can use it as
./RFSniffer | tee codes.txt
I suspect that sudo might be executing your command with a different working directory. What does sudo pwd print? Have you considered looking for codes.txt in that directory?
edit: Alternatively, could it be that your OS is temporarily storing stdout somewhere until the program closes, at which point it writes to your codes.txt file? What happens if you inject an exit(0); immediately after each call to printf in this code (or terminate the loop, whatever...)
Either file codes.txt is not provided the write permission.
Check ls -l codes.txt and confirm file has write permission.
OR path of the file is not right, so provide absolute path of the file:
sudo ./RFSniffer >> codes.txt
replace it with
sudo sh -c `RFSniffer >> <absolute_path>/codes.txt`
OR
sudo ./RFSniffer | sudo tee <absolute_path>/codes.txt
This link give more details on file redirection using sudo : How do I use sudo to redirect output to a location I don't have permission to write to?

How to take the output of grep and write it to a new file

I want to take the output of the grep command on a file, create a new file and save that grep output to the new created file, can someone please point me to the right direction in how I would do that?
The path you choose depends a great deal on how simple you want it to be.
Perhaps the simplest method is the use of system:
#include <stdio.h>
#include <stdlib.h>
int main (void) {
system ("grep a *.c >outfile.txt");
return 0;
}
though you also could construct the command dynamically if you have different arguments to grep or a non-fixed output file.
Beyond that, you could use popen() (if available on your implementation - it's not mandated by ISO but is instead a POSIX thing) along with fgets() or fgetc() to read the output of that command and do whatever you want with it:
#include <stdio.h>
int main (void) {
int chr;
FILE *echo = popen ("echo hello there", "r");
if (echo != NULL) {
while ((chr = fgetc (echo)) != EOF)
putchar (chr);
fclose (echo);
}
return 0;
}
The next step up from there may be to not rely on an external grep at all but instead include something like PCRE (Perl-compatible regular expressions) into your own code, giving you much finer control over what happens.

Dynamic generation of file contents (poor man's proc file)

I'm trying to make a simple userspace program that dynamically generates file contents when a file is read, much like a virtual filesystem. I know there are programs like FUSE, but they seem a bit heavy for what I want to do.
For example, a simple counter implementation would look like:
$ cat specialFile
0
$ cat specialFile
1
$ cat specialFile
2
I was thinking that specialFile could be a named pipe, but I haven't had much luck. I was also thinking select may help here, but I'm not sure how I would use it. Am I missing some fundamental concept?
#include <stdio.h>
int main(void)
{
char stdoutEmpty;
char counter;
while (1) {
if (stdoutEmpty = feof(stdout)) { // stdout is never EOF (empty)?
printf("%d\n", counter++);
fflush(stdout);
}
}
return 0;
}
Then usage would be something like:
shell 1 $ mkfifo testing
shell 1 $ ./main > testing
shell 2 $ cat testing
# should be 0?
shell 2 $ cat testing
# should be 1?
You need to use FUSE. A FIFO will not work, because either your program keeps pushing content to stdout (in which case cat will never stop), or it closes stdout, in which case you obviously can't write to it anymore.

Resources