I'm trying to execute other programs within my C program. My first attempt was with popen. When I try to read from pipe I only get a reply of 1 byte and nothing in buf. I'm not sure as to the reasoning behind this.
popen example:
#include<stdio.h>
#include<unistd.h>
int main(int argc, char* argv[])
{
FILE* pipe;
if ((pipe=(FILE*)popen("./test.php","r"))==NULL)
printf("this is not working\n");
char buf[1024] = {'\0'};
int fd=fileno(pipe);
int bytes = read(fd, buf, 1024);
printf("bytes read %d\n", bytes);
printf("The program: %s\n", buf);
if(pclose(pipe)<0)
printf("not working\n");
return 0;
}
php example
#!/usr/bin/php
<?php
echo "THIS IS A TEST THAT WORKED\n";
?>
The output:
bytes read 1
The program:
The output of ls:
ls -l test.php
-rwxr-xr-x+ 1 tpar44 user 62 Nov 10 14:42 test.php
Any help in this would be greatly appreciated!
Thanks!
You need to execute the php interpreter and pass the name of the script as argument, when using popen if your script does not have the shebang because the shell won't know which interpreter to use:
fp = popen("php /path/to/script/test.php", "r");
If the script has the shebang line you can just execute it, because popen uses the shell to execute commands and it can find out which one to use, so you could just do this:
fp = popen("/path/to/script/test.php", "r");
However, make sure the script is executable:
chmod +x test.php
you could also use execl() but you have to specify the path to the binary because execl doesn't use the shell:
execl("/usr/bin/php", "/usr/bin/php", "-q",
"/path/to/script/test.php", (char *) NULL);
Don't forget to actually read from the pipe ;)
fread(buf, 1, 1024, pipe);
Related
I have a binary file which prints the result instead of returning the value, if I execute it using cmd I am getting printed text, I managed to execute it from C code but it seems like I can not get the text it usually prints to be stored in a variable I can use later for further decisions.
I do not have that much of experience in C and I googled a lot.
I came across the idea of using clip but my cmd is saying that clip command can not be found.
any help or ideas would be appreciated.
The correct function pair to use on POSIX systems is popen() and
pclose(). You can perhaps use Microsoft's _popen() and
_pclose() unless the warning 'This API cannot be used in applications that execute in the Windows Runtime' matters to you.
You would use it more or less like this. I've had to invent the name of the command you wish to execute since the question doesn't specify that. I chose ./example.exe as the name — and I'm assuming it needs no arguments.
char cmd[] = "./example.exe";
FILE *fp = popen(cmd, "r");
if (fp != NULL)
{
char buffer[4096];
size_t nbytes;
while ((nbytes = fread(buffer, sizeof(buffer), sizeof(char), fp)) != 0)
{
…process nbytes of data…
…it is not a null-terminated string unless you add the null byte…
}
pclose(fp);
}
else
{
…report error for failure to execute command…
}
You can use the system function from <stdlib.h> to run the command you want. To get the command's output, you modify your command like in this question to save the command's output to a file. Then you can use the file I/O functions in <stdio.h> to process the command output.
In Linux, you may do command substitution and pass its result as arguments to the program, Something like this
./your_program "$(/path/to/your/binary/file)"
Suppose your main is
int main(int argc,char* argv[]){
.
.
return 0;
}
Acess the arguments like argv[1] and so.
Here the $(command) does the substitution and it passes the printed values from the binary as arguments to the pgm. Hope this helps.
Use snprintf function. For e.g.
snprintf(cmdbuff, BUFFER_LEN, "dmidecode --type 17 | grep -i Size | grep -o '\\<[0-9]*\\>' | paste -sd+ | bc");
Here cmdbuff is character array where command will be stored , BUFFER_LEN is a size of the character array
Then use popen and fgets to get the output of command into some buffer as shown below
if((fd = popen(cmdbuff,"r")) != NULL)
{
fgets(buffer, BUFFER_LEN, fd);
sprintf(vnfc_configured_memory, "%s", buffer);
vnfc_configured_totalRAM = atof(vnfc_configured_memory);
}
I have a script starting with a shebang stored in a string. I would like to execute this script without writing it in a temporary file.
I saw that execve takes a filename as argument. Is it possible to do the same with a script in memory.
A script is not directly executable, when executing a script, the kernel identify which interpreter to launch then pass the file name as an argument to the interpreter, in your case a shell.
Should you want to execute a script stored in a string, you might directly launch the shell of your choice and pass your string as its standard input through a pipe.
Here is way to do it using popen:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
FILE *fp;
char *prefix="/bin/bash <<'%EOF%'\n";
char *script="#!/bin/bash\necho foo\ndate\n";
char *suffix="%EOF%\n";
char *command;
char buf[1024];
command=malloc(strlen(prefix)+strlen(script)+strlen(suffix)+1);
sprintf(command,"%s%s%s",prefix,script,suffix);
fp=popen(command, "r");
if(fp == NULL) {
perror("Error\n");
exit(1);
}
while(fgets(buf, sizeof(buf), fp) != NULL) {
printf("%s", buf);
}
pclose(fp);
return 0;
}
As you say that the script starts with a shebang string, you cannot directly pipe it into the standard input of a shell. But you can mimic what a shell would have done:
extract the shell command from the shebang line
start it with a pipe as standard input
pipe the remaining of the script into that shell command
A more generalist version would control whether the script string starts with a #!. If it does, use the above way, else just pipe the whole string into an instance of /bin/sh (or whatever shell you are used to)
I'm using "system" API calls to run shell commands in my C program, now
there is case where I want to redirect the output generated by an executableto a buffer instead of a file (named recv.mail)
An example of how I write the output to the file:
cmd[] = "mda "/bin/sh -c 'cat > recv.mail'";
system (cmd);
Similarly I want to replace input taken from the file (send.mail) with input taken from a buffer.
An example of how I take input from a file:
cmd[] = "msmtp < cat send.mail";
system (cmd);
NOTE: The send.mail and recv.mail files have formatted data.
Are pipes a better replacement?
Can anyone suggest another alternative?
popen/pclose may do what you want:
FILE *f = popen("program to execute", "r");
if (NULL != f)
{
char buffer[128];
while (fgets(buffer, sizeof buffer, f)
{
printf("Read from program: '%s'\n", buffer);
}
pclose (f);
}
popen/pclose again:
FILE *f = popen("program to execute", "w");
...
Basically what I want to do is have a program with int main(argc, *argv[]) and instead of writing chars into command line, I want to have my program read those words from a file. How could I accomplish this? Is there a special command in Linux for that?
You can use standard redirect operations in a *nix shell to pass files as input:
./myprogram < inputfile.txt
This statement executes your program (myprogram) and pumps the data inside of inputfile.txt to your program
You can also redirect the output of program to a file in a similar fashion:
./myprogram > outputfile.txt
Instead of doing
for(int i = 1; i < argc; i++)
{
insert(&trie, argv[i]);
}
you could doing something like
FILE *input;
char *line;
....
while (fscanf(input, "%ms", &line) != EOF) {
insert(&trie, line);
/* If you make a copy of line in `insert()`, you should
* free `line` at here; if you do not, free it later. */
free(line);
}
Use redirection
yourprogram < youtextfile
will offer the content of yourtextfile as standard input (stdin) to yourprogram. Likewise
yourprogram > yourothertextfile
will send everything the program writes to standard output (stdout) to yourothertextfile
You'll notice when reading man pages that most system calls have a version that works directly with stdin or stdout
For example consider the printf family:
printf ("hello world\n");
is a shorter version of
fprintf (stdout,"hello world\n");
and the same goes for scanf and stdin.
This is only the most basic usage of redirection, which in my opinion is one of the key aspects of "the unix way of doing things". As such, you'll find lots of articles and tutorials that show examples that are a lot more advanced than what I wrote here. Have a look at this Linux Documentation Project page on redirection to get started.
EDIT: getting fed input via redirection ior interactively "looks" the same to the program, so it will react the same to redirected input as it does to console input. This means that if your program expects data line-wise (eg because it uses gets() to read lines), the input text file should be organized in lines.
By default, every program you execute on POSIX-compliant systems has three file descriptors open (see <unistd.h> for the macros' definition): the standard input (STDOUT_FILENO), the standard output (STDOUT_FILENO), and the error output (STDERR_FILENO), which is tied to the console.
Since you said you want read lines, I believe the ssize_t getline(char **lineptr, size_t *n, FILE *stream) function can do the job. It takes a stream (FILE pointer) as a third argument, so you must either use fopen(3) to open a file, or a combination of open(2) and fdopen(3).
Getting inspiration from man 3 getline, here is a program demonstrating what you want:
#define _GNU_SOURCE
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fp;
size_t len;
char *line;
ssize_t bytes_read;
len = 0;
line = NULL;
if (argc > 1)
{
fp = fopen(argv[1], "r");
if (fp == NULL)
{
perror(*argv);
exit(EXIT_FAILURE);
}
}
else
fp = stdin;
while ((bytes_read = getline(&line, &len, fp)) != -1)
printf("[%2zi] %s", bytes_read, line);
free(line);
exit(EXIT_SUCCESS);
}
Without arguments, this program reads lines from the standard input: you can either feed it lines like echo "This is a line of 31 characters" | ./a.out or execute it directly and write your input from there (finish with ^D).
With a file as an argument, it will output every line from the file, and then exit.
You can have your executable read its arguments on the command line and use xargs, the special Linux command for passing the contents of a file to a command as arguments.
An alternative to xargs is parallel.
I have the code below that I refer the thread on here to use the popen function
int main(int argc,char *argv[]){
FILE* file = popen("ntpdate", "r");
char buffer[100];
fscanf(file, "%100s", buffer);
pclose(file);
printf("buffer is :%s\n", buffer);
return 0;
}
It outputs:
21 Apr 03:03:03 ntpdate[4393]: no server can be used, exiting
buffer is:
why printf does not output anything? If I use ls as a command, then printf outputs the ls output. what am I doing wrong ntpdate executing?
If I execute the code below (referring the webpage)
#define COMMAND_LEN 8
#define DATA_SIZE 512
int main(int argc,char *argv[]){
FILE *pf;
char command[COMMAND_LEN];
char data[DATA_SIZE];
// Execute a process listing
sprintf(command, "ntpdate");
// Setup our pipe for reading and execute our command.
pf = popen(command,"r");
if(!pf){
fprintf(stderr, "Could not open pipe for output.\n");
return;
}
// Grab data from process execution
fgets(data, DATA_SIZE , pf);
// Print grabbed data to the screen.
fprintf(stdout, "-%s-\n",data);
if (pclose(pf) != 0)
fprintf(stderr," Error: Failed to close command stream \n");
return 0;
}
I get
21 Apr 03:15:45 ntpdate[5334]: no servers can be used, exiting
-�2}�����"|�4#|�-
Error: Failed to close command stream
what are wrongs on the codes above?
Since the output is going to stderr you need to redirect stderr like so:
FILE* file = popen("ntpdate 2>&1", "r");
this will redirect stderr to stdout and so you will see output from both. Second issue fscanf will stop at the first space so you can replace with fgets:
fgets(buffer, 100, file);
As Shafik Yaghmour correctly diagnosed, the output you see from ntpdate is written (correctly) to its standard error, which is the same as your programs standard error.
To get the error messages sent down the pipe, use:
FILE *file = popen("ntpdate 2>&1", "r");
That sends the standard error output from ntpdate to the standard output of the command, which is the pipe you're reading from.
Of course, it looks like using ntpdate isn't going to work well until you've configured something.