Look for popen output changes - c

I need to create a program that does this:
execute a command with popen
do things with the output of popen(use the FILE in a lot of things)
stay checking for popen output changes, if have one, re execute everything.
The source code is here: https://gitorious.org/clyv/clyv
So I only want to execute all the rest of the program AGAIN if there is a change in the output of popen (must be compared with the first output)
The program should do everything first time, and after, only do everything and print again if there is a change on popen output. The popen should verified once a second.
Update
I didn't get any answer that solve my problem here, but reading a C tutorial i saw something about threads, and it sounds like the solution to me, i will see what i can do.

You are free to call popen() as many times as needed. But, to properly release resources used by a call to popen(), you need to call pclose().
In your case, you probably want to just poll the output occasionally, and emit something whenever it is necessary to do so.
first_time = 1;
need_to_print = 1;
for (;;) {
FILE *fp = popen(...);
/* read input ... */
pclose(fp);
/* parse input ... */
if (first_time) {
/* save contents for future comparison... */
first_time = 0;
} else {
need_to_print = /* result of comparing saved contents with new contents */;
}
if (need_to_print) {
/* print something ... */
}
sleep(INTERVAL);
}

You can use
//an array of 2 string buffers
char output[2][1024];
//the string buffer index in use
int flip = 0;
FILE *fp;
void executeTestCommand() {
int resultSize;
fp = popen("free -m | awk 'NR==3 {print $3}'" "r");
if (fp == NULL) {
perror("Failed to run command");
}
else {
result_size = fread(output, 1, 1024 - 1, fp);
//place string terminator
output[flip][result_size] = '\0';
}
//flip the buffers
flip = flip ^ 1;
pclose(fp);
}
int main() {
//init with empty string
output[0][0] = '\0';
output[1][0] = '\0';
//for ever
for(;;) {
executeTestCommand();
//if the last command output differs from the current
//command output
if (strcmp(output[0], output[1])) {
//execute the rest of the command here
}
}
return 0;
}
Cheers!
You want to close the file pointer (you don't want any memory leaks) since you are going to use the same FILE * fp variable again with popen.
Later edit: hope it helps:). You might want to insert a sleep statement inside the for ever block because this will be cpu intensive.

#AlinUngureanu #JonathanLeffler #jxh
I published the source on git! Now you can see what i want
https://gitorious.org/clyv/clyv

Related

Sending exec output from function to main method

I have a method I call from the main method called that executes ls-l on a certain directory, I want it to execute it and send the result as a string to the main method.
My current flawed code:
char *lsl(){
char *stringts=malloc(1024);
chdir("/Users/file/path");
char * lsargs[] = { "/bin/ls" , "-l", NULL};
stringts="The result of ls-l in the created directory is:"+ execv(lsargs[0], lsargs);
return stringts;
}
Currently I am only getting the exec output on the screen, I understand why this is happening(exec getting called before reaching return point). However I don't know how I could possibly do what I want and if it's actually doable.
I was thinking of using pipes and dup2() so I don't let the exec function use stdout but I don't know if it would be possible to put the output in a string.
As Jonathan Leffler already pointed out in comments, there is no '+' operator for concatenating strings in C.
A possibility to dynamically extends strings is to use realloc together with strcat.
For each number of bytes you read from the pipe, you could check the remaining capacity of the originally allocated memory for the string and, if this is not enough, reallocate twice the size.
You have to keep track of the size of the current string yourself. You could do this with a variable of type size_t.
If you combine this with the popen handling, it could look something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
FILE *fp;
if ((fp = popen("ls -l", "r")) == NULL) {
perror("popen failed");
return EXIT_FAILURE;
}
size_t str_size = 1024;
char *stringts = malloc(str_size);
if (!stringts) {
perror("stringts allocation failed");
return EXIT_FAILURE;
}
stringts[0] = '\0';
char buf[128];
size_t n;
while ((n = fread(buf, 1, sizeof(buf) - 1, fp)) > 0) {
buf[n] = '\0';
size_t capacity = str_size - strlen(stringts) - 1;
while (n > capacity) {
str_size *= 2;
stringts = realloc(stringts, str_size);
if (!stringts) {
perror("stringts realloation failed");
return EXIT_FAILURE;
}
capacity = str_size - strlen(stringts) - 1;
}
strcat(stringts, buf);
}
printf("%s\n", stringts);
free(stringts);
if (pclose(fp) != 0) {
perror("pclose failed");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
You have several flaws in your code:
char *lsl(){
char *stringts=malloc(1024);
chdir("/Users/file/path");
char * lsargs[] = { "/bin/ls" , "-l", NULL};
stringts="The result of ls-l in the created directory is:"+ execv(lsargs[0], lsargs);
return stringts;
}
If you malloc(3) a 1024 byte buffer into stringts pointer, but then you assign a different value to the pointer, making your buffer to be lost in the immensity of your RAM.
When you do execv(2) call, all the memory of your process is freed by the kernel and reloaded with an execution of the command ls -l, you'll get the output in the standard output of the process, and then you'll get the prompt of the shell. This makes the rest of your program unuseful, as once you exec, there's no way back, and your program is unloaded and freed.
You can add (+) to a pointer value (you indeed add to the address pointing to the string "The result of the ls -l..." and ---as the result of exec is nothing, as a new program is loaded--- you get nothing) If execv fails, then you get a pointer pointing to the previous char to that string, which is a valid expression in C, but makes your program to behave erratically in an Undefined Behaviour. Use strcpy(3), strcat(3), or snprintf(3), depending on the exact text you want to copy in the space of the buffer you allocated.
Your return an invalid address as a result. The problem here is that, if execv(2) works, it doesn't return. Only if it fails you get an invalid pointer that you cannot use (by the reason above), and of course ls -l has not been executed. Well, you don't say what you got as ouptut, so it is difficult for me to guess if you actually exec()d the program or not.
On other side, you have a popen(3) library function that allows you to execute a subprogram and allows you to read from a file descriptor its output (I recommend you not to chdir gratuitously in your program, as that is a global change in your program environment, IMHO it is better to pass ls(1) the directory you want to list as a parameter)
#include <stdio.h>
FILE *lsl() {
/* the call creates a FILE * descriptor that you can use as input and
* read the output of the ls command. It's bad resources use to try to
* read all in a string and return the string instead. Better read as
* much as you can/need and then pclose() the descriptor. */
return popen("/bin/ls -l /Users/file/path|", "rt");
}
and then you can read (as it can be very long output, you probably don't have enought buffer space to handle it all in memory if you have a huge directory)
FILE *dir = lsl();
if (dir) {
char buffer[1024];
while (fgets(buffer, sizeof buffer, dir)) {
process_line_of_lsl(buffer);
}
pclose(dir); /* you have to use pclose(3) with popen(3) */
}
If you don't want to use popen(3), then you cannot use execv(2) alone, and you have to fork(2) first, to create a new process, and exec() in the child process (after mounting the redirection yourself). Read a good introduction to fork()/exec() and how to redirect I/O between fork() and exec(), as it is far longer and detailed to put it here (again)

In C, how can I copy part of an array of strings for use as an argv in execve?

My code is really messy right now, so I think it would be easier to convey what I'm trying to do by just describing it.
I'm working on a shell for homework that needs to be able to redirect output to a file, just like the default shell. We were provided a preexisting shell and asked to modify it. The shell already sets up an argv for execve, but in order to implement redirection I need to remove the last two entries from the argv the program built (> and the file name), and after testing that by just freeing up the last two entries I think it's probably better to just make a copy, minus those two entries, as this program handles freeing up the argv and if I try to do that at some point before the predesignated time to do so I run into problems when I try to run another command.
My point is I'm having a hard time copying part of an array of strings that's going to serve as an argv. I've seen a couple of solutions posted, but they're all in C++, and I'm asked to do this in C.
Alternatively, I suppose it would also be sufficient if I could properly empty part of an argv. Here's the code I tried:
for(i=0;i<10;i++)
{
if(my_argv[i] == NULL)
{
break;
}
if(strcmp(my_argv[i], ">") == 0)
{
if(my_argv[i+1] != NULL)
{
strncpy(fileName, my_argv[i+1], strlen(my_argv[i+1]));
strncat(fileName, "\0", 1);
//bzero(my_argv[i+1], strlen(my_argv[i+1])+1);
//my_argv[i+1] = NULL;
//free(my_argv[i+1]);
} else {
printf("no file name given\n");
return;
}
//bzero(my_argv[i], strlen(my_argv[i])+1);
//my_argv[i] = NULL;
//free(my_argv[i]);
redirectOutput(cmd, fileName);
return;
}
}
The commented sections are where I copied in code from the function that empties my_argv to attempt to free up the contents of argv where the > and file name are. It still runs without those lines, but then I kick the can down the road with having to deal with the extra entries in my redirectOutput() function, which is an absolute train wreck. The free_argv() function looks like this:
void free_argv()
{
int index;
for(index=0;my_argv[index]!=NULL;index++) {
bzero(my_argv[index], strlen(my_argv[index])+1);
my_argv[index] = NULL;
free(my_argv[index]);
}
}
You're going to much more work than you need to do. You can safely do whatever you want to the pre-prepared argv, as long as you do it after the fork(), in the child (before the execve(), of course). No such modifications will affect the parent, and you don't need to worry about any cleanup because the exec replaces the old process image with the new one.
Example:
/* these are already provided: */
char *filename = /* ... */;
char **child_argv = /* ... */;
char **child_env = /* ... */;
pid_t pid = fork();
if (pid == 0) {
/* the child */
char **arg;
for (arg = child_argv; *arg && strcmp(*arg, ">"); arg += 1) { /* empty */ }
*arg = NULL; /* terminate the arg list at the ">", if present */
/* no need to clean up anything before execve() */
execve(filename, child_argv, child_env);
exit(1); /* execve() failed */
} else if (pid < 0) {
/* handle error */
}
/* the parent continues ... */

easy way to parse a text file?

I'm making a load balancer (a very simple one). It looks at how long the user has been idle, and the load on the system to determine if a process can run, and it goes through processes in a round-robin fashion.
All of the data needed to control the processes are stored in a text file.
The file might look like this:
PID=4390 IDLE=0.000000 BUSY=2.000000 USER=2.000000
PID=4397 IDLE=3.000000 BUSY=1.500000 USER=4.000000
PID=4405 IDLE=0.000000 BUSY=2.000000 USER=2.000000
PID=4412 IDLE=0.000000 BUSY=2.000000 USER=2.000000
PID=4420 IDLE=3.000000 BUSY=1.500000 USER=4.000000
This is a university assignment, however parsing the text file isn't supposed to be a big part of it, which means I can use whatever way is the quickest for me to implement.
Entries in this file will be added and removed as processes finish or are added under control.
Any ideas on how to parse this?
Thanks.
Here is a code that will parse your file, and also account for the fact that your file might be unavailable (that is, fopen might fail), or being written while you read it (that is, fscanf might fail). Note that infinite loop, which you might not want to use (that's more pseudo-code than actual code to be copy-pasted in your project, I didn't try to run it). Note also that it might be quite slow given the duration of the sleep there: you might want to use a more advanced approach, that's more sort of a hack.
int pid;
float idle, busy, user;
FILE* fid;
fpos_t pos;
int pos_init = 0;
while (1)
{
// try to open the file
if ((fid = fopen("myfile.txt","rw+")) == NULL)
{
sleep(1); // sleep for a little while, and try again
continue;
}
// reset position in file (if initialized)
if (pos_init)
fsetpos (pFile,&pos);
// read as many line as you can
while (!feof(fid))
{
if (fscanf(fid,"PID=%d IDLE=%f BUSY=%f USER=%f",&pid, &idle, &busy, &user))
{
// found a line that does match this pattern: try again later, the file might be currently written
break;
}
// add here your code processing data
fgetpos (pFile,&pos); // remember current position
pos_init = 1; // position has been initialized
}
fclose(fid);
}
As far as just parsing is concerned, something like this in a loop:
int pid;
float idle, busy, user;
if(fscanf(inputStream, "PID=%d IDLE=%f BUSY=%f USER=%f", %pid, &idle, &busy, &user)!=4)
{
/* handle the error */
}
But as #Blrfl pointed out, the big problem is to avoid mixups when your application is reading the file and the others are writing to it. To solve this problem you should use a lock or something like that; see e.g. the flock syscall.
Use fscanf in a loop. Here's a GNU C tutorial on using fscanf.
/* fscanf example */
#include <stdio.h>
typedef struct lbCfgData {
int pid;
double idle;
double busy;
double user;
} lbCfgData_t ;
int main ()
{
// PID=4390 IDLE=0.000000 BUSY=2.000000 USER=2.000000
lbCfgData_t cfgData[128];
FILE *f;
f = fopen ("myfile.txt","rw+");
for ( int i = 0;
i != 128 // Make sure we don't overflow the array
&& fscanf(f, "PID=%u IDLE=%f BUSY=%f USER=%f", &cfgData[i].pid,
&cfgData[i].idle, &cfgData[i].busy, cfgData[i].user ) != EOF;
i++
);
fclose (f);
return 0;
}

problem using fprintf

I'm trying to print to a text file numerous variables yet it doesn't work.
I checked and verified that i write it in the correct syntax.
I also checked the return value and it's positive therefore i know it did write to the file, however when i open the file it's empty.
I would be happy for some help.
This is the code:
I initiate DynsaleDayPtr in the main:
FILE* DynsaleDayPtr = CreateTextFiles("sale_day.txt");
Create function:
FILE* CreateTextFiles (char* fileName)
{
FILE* saleFilePtr=NULL;
if((saleFilePtr=fopen(fileName,"a+"))==NULL)
printf("File couldn't be opened\n");
return saleFilePtr;
}
The call to the function TextAddSale is done from a function that is called in the main:
TextAddSale(DynSaleDayPtr,dynNumOfRecords);
Bool TextAddSale (FILE* DynsaleDayPtr, int* dynNumOfRecords)
{
char id[6];
char name [50];
char priceChar[20];
char* tmp = NULL;
int price=-1;
DynamicRecord * newRec=NULL;
scanf("%s%s%s",id,name,priceChar);
newRec = (DynamicRecord *)malloc(sizeof(DynamicRecord));
if (newRec == NULL)
return False;
tmp = (char*)malloc(strlen(name)+1);
if (tmp == NULL)
{
free (newRec);
return False;
}
strcpy(tmp,name);
newRec->productName = tmp;
strcpy(newRec->productId, id);
newRec->productPrice=atoi (priceChar);
if (fprintf(DynsaleDayPtr,"%d %s %s %d", strlen(newRec->productName),
newRec->productId, newRec->productName, newRec->productPrice)>0)
{
*dynNumOfRecords=(*dynNumOfRecords)+1;
return True;
}
}
thanks!
You need to flush the stream.
fflush(FILE*);
Of course, you have to close the stream if you have done with it.
fclose(FILE*);
Agree with #pmg - try something like this:
FILE *pFile = fopen("foo.txt","w");
if (pFile==NULL)
bad();
fprintf(pfile,"Hello world\n");
fclose(pfile);
make that work first - then fix whatever's wrong in the big app -
A thought:
scanf("%s%s%s",id,name,priceChar);
the above statement is a bit dodgy since you haven't said how many bytes
should go in each string.
better to use fgets() then parse the string retrieving the individual values
or create a better format specifier.
If the above statement causes a memory overwrite the rest of your program
could malfunction causing things like what you describe.
fprintf() most likely uses buffered output. Therefore, you should either fflush() the DynSaleDayPtr stream or, better yet, print a newline to the file. The latter has the added benefit of making the file contents actually readable...
Also, don't forget to close() the stream when you're finished with writing. This will also render fflush() unnecessary.

CGI Buffering issue

I have a server side C based CGI code as:
cgiFormFileSize("UPDATEFILE", &size); //UPDATEFILE = file being uploaded
cgiFormFileName("UPDATEFILE", file_name, 1024);
cgiFormFileContentType("UPDATEFILE", mime_type, 1024);
buffer = malloc(sizeof(char) * size);
if (cgiFormFileOpen("UPDATEFILE", &file) != cgiFormSuccess) {
exit(1);
}
output = fopen("/tmp/cgi.tar.gz", "w+");
inc = size/(1024*100);
fptr = fopen("progress_bar.txt", "w+");
while (cgiFormFileRead(file, b, sizeof(b), &got_count) == cgiFormSuccess)
{
fwrite(b,sizeof(char),got_count,output);
i++;
if(i == inc && j<=100)
{
fprintf(fptr,"%d", j);
fflush(fptr);
i = 0;
j++; // j is the progress bar increment value
}
}
fclose(fptr);
cgiFormFileClose(file);
retval = system("mkdir /tmp/update-tmp;\
cd /tmp/update-tmp;\
tar -xzf ../cgi.tar.gz;\
bash -c /tmp/update-tmp/update.sh");
However, this doesn't work the way as is seen above. Instead of printing 1,2,...100 to progress_bar.txt (referred by fptr)one by one it prints at ONE GO, seems it buffers and then writes to the file.
fflush() also didn't work.
Any clue/suggestion would be really appreciated.
First, open the file before the loop and close after it ends. Too much IO.
The problem is here w+ - this truncates your file. use a+. (fopen help)
It is writing it one-by-one, it's just that it does it so fast that you're vanishingly unlikely to ever see the file with a value other than 99 in it.
This is easily demonstrated if you put a sleep(1) within the loop, so that it's slow enough for you to catch it.

Resources