Communication between interactive ocaml interpreter and another process - c

I need to load a *.ml file into the Ocaml toplevel (the interactive interpreter, when you type 'ocaml' in a shell) and then send an instruction from a Matlab process, get back the result of the instruction, send back another instruction, ...
I've made this C program. The parent process gets the Matlab's instruction from a named pipe, sends it to the child process (with ocaml running) and gets the response back so it can send it to Matlab.
But there is some kind of bug: when I send an instruction, I get back some weird characters, I send another instruction and then I receive the response of the first instruction...
(I didn't copy the perror() test to have less text)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(void) {
// Parent -> Child
int pipe_in[2];
// Child -> parent
int pipe_out[2];
/*
pipe[0] = output
pipe[1] = input
*/
pipe(pipe_in);
pipe(pipe_out);
pid_t pid;
if ((pid = fork()) == 0) {
// CHILD SIDE
close(pipe_in[1]);
close(pipe_out[0]);
dup2(pipe_in[0], STDIN_FILENO);
dup2(pipe_out[1], STDOUT_FILENO);
dup2(pipe_out[1], STDERR_FILENO);
close(pipe_in[0]);
close(pipe_out[1]);
char *args[] = {"ocaml", NULL};
execvp("ocaml", args);
printf("FAIL\n");
exit(EXIT_FAILURE);
} else {
// PARENT SIDE
printf("[*] PID : %d\n", (int) pid);
close(pipe_in[0]);
close(pipe_out[1]);
char cmd[1024];
char feedback[1024];
ssize_t cmd_read;
ssize_t feedback_read = sizeof(feedback);
while (1) {
// Get the instruction from Matlab.
printf("[>] ");
int fifo_in = open("/tmp/pipe_in", O_RDONLY);
cmd_read = read(fifo_in, cmd, sizeof(cmd));
close(fifo_in);
printf("%s\n", cmd);
// Send the instruction to the ocaml interpreter.
write(pipe_in[1], cmd, cmd_read);
// Read the response of the ocaml interpreter.
while (feedback_read == sizeof(feedback)) {
feedback_read = read(pipe_out[0], feedback, sizeof(feedback));
printf("[-] %d\n", (int) feedback_read);
}
printf("[<] %s\n", feedback);
// Send to Matlab the response.
int fifo_out = open("/tmp/pipe_out", O_WRONLY);
write(fifo_out, feedback, feedback_read);
close(fifo_out);
cmd_read = 0;
feedback_read = sizeof(feedback);
}
close(pipe_in[1]);
close(pipe_out[0]);
}
}
I compile the code with gcc -Wall -std=c99 -o tphr tphr.c
I run the programm in one shell and in another :
> printf 'let x = 10;;\n' > /tmp/pipe_in
> cat /tmp/pipe_out
OCaml version 4.03.0
# %
> printf 'let y = 5;;\n' > /tmp/pipe_in
> cat /tmp/pipe_out
val x : int = 10
# %
How can I fix the result ?

If, by "weird characters", you mean
OCaml version 4.03.0
that is simply because this is what the OCaml toplevel prints out on startup. So you need to read this line when your own program starts up.
If you mean the # symbol, also known as the prompt, you can turn it off by running ocaml -nopromt.

You don't want to run the interactive ocaml toplevel here for multiple reasons:
the toplevel parses the configfiles of the user and then loads different modules. This can change the available values, change behaviour, and make your matlab process get different results per user.
the output of the toplevel may change between versions making it difficult to parse and return the right reply to matlab
Did you know that you can call the toplevel from ocaml bytecode to interprete strings? I suggest dumping the C code and writing ocaml byte code to read from the pipe, interpret the command and reply with the result.

Related

Pipe file bigger than 64Kb and get the size and send it to char *

I am trying to create a simple program using pipes, even though that are easier options for the same task (fopen(), lseek(), ftell(), etc).
First I use execve() to perform a terminal cat, and send the information through the pipe so I may be able to print the size of the file descriptor and read it to a malloc'd char pointer. My solution is this one:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <fcntl.h>
int main (int argc, char *argv[]) {
int fd[2], active;
int pipe_sz = 0;
char *name;
pipe(fd);
//change /bin/cat according to your system
char *cmd[] = {"/bin/cat", "example.txt", NULL};
if (fork() == 0) {
//CHILD
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
active = execve(cmd[0], cmd, NULL);
} else {
//PARENT
wait(NULL);
int ioctl_sz = ioctl(fd[0], FIONREAD, &pipe_sz);
name = malloc(pipe_sz + 1);
int result = read(fd[0], name, pipe_sz);
name[pipe_sz] = '\0';
printf("LEN NAME: %ld\n", strlen(name));
close(fd[0]);
close(fd[1]);
}
return 0;
}
Everything works fine as long as I keep myself inside the pipe's limits (my LINUX system is able to hold 65536 bytes). I decided to push the limits, and see what could happen. As expected, the program stays stuck when my file was above 65536 bytes.
I do not want to manually change my pipe's limits through fcntl(pipefd, F_SETPIPE_SZ, size) (since I "don't know" my file's size). Therefore, I researched, and came across the pipe2(pipefd, O_NONBLOCK) in order to avoid my pipe from stopping the reading process. Unfortunately, I received only this message:
/bin/cat: write error: Resource temporarily unavailable
I even tried a while loop read() and realloc() to see if my code could at least give the char pointer result, but I was not successful, and part of file was lost.
Is it possible to produce a code that may provide the same results as mine above with files bigger than 65536 bytes?
Is there a way to interrupt the pipe if it takes too long to finish the process? Or even make a while loop to guess and resize the pipe through
fcntl()?
Thanks everyone for the help!

How is the speed of printf affected by a presence of a forked process and '\n'?

I had this simple shell like program that works both in interactive and non-interactive mode. I have simplified the code as much as I can to present my question, but it is still a bit long, so sorry for that!
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/**
*main-entry point for gbk
*Return: returns the index of 0 on sucess
*/
int main(void)
{
char *cmd = malloc(1 * sizeof(char)), *cmdargs[2];
size_t cmdlen = 0;
int childid, len;
struct stat cmdinfo;
while (1)
{
printf("#cisfun$ ");
len = getline(&cmd, &cmdlen, stdin);
if (len == -1)
{
free(cmd);
exit(-1);
}
/*replace the ending new line with \0*/
cmd[len - 1] = '\0';
cmdargs[0] = cmd;
cmdargs[1] = NULL;
childid = fork();
if (childid == 0)
{
if (stat(*cmdargs, &cmdinfo) == 0 && cmdinfo.st_mode & S_IXUSR)
execve(cmdargs[0], cmdargs, NULL);
else
printf("%s: command not found\n", *cmdargs);
exit(0);
}
else
wait(NULL);
}
free(cmd);
exit(EXIT_SUCCESS);
}
To summarize what this program does, it will first print the prompt #cisfun$ , waits for an input in interactive mode and takes the piped value in non-interactive mode, creates a child process, the child process checks if the string passed is a valid executable binary, and if it is, it executes it other wise it prints a command not found message and prompts again.
I have got this program to work fine for most of the scenarios in interactive mode, but when I run it in non-interactive mode all sorts of crazy (unexpected) things start to happen.
For example, when I run echo "/bin/ls"|./a.out, (a.out is the name of the compiled program)
you would first expect the #cisfun$ message to be printed since that is the first thing performed in the while loop, and then the output of the /bin/ls command, and finally #cisfun$ prompt, but that isn't what actually happens. Here is what happens,
It is very weird the ls command is run even before the first print message. I, at first, thought there was some threading going on and the printf was slower than the child process executing the ls command. But I am not sure if that is true as I am a noob. and also things get a bit crazier if I was printing a message with '\n' at the end rather than just a string. (if I change printf("#cisfun$ "); to printf("#cisfun$\n");) the following happens,
It works as it should, so it got me thinking what is the relation between '\n', fork and speed of printf. Just in short what is the explanation for this.
The second question I have is, why doesn't my program execute the first command and go to an interactive mode, I don't understand why it terminates after printing the second #cisfun$ message. By checking the status code (255) after exit I have realized that the effect is the same as pressing ctr+D in the interactive mode, which I believe is exited by the getline function. But I dont understand why EOF is being inserted in the second prompt.

How to skip iteration if linux command fails in C

procfs.c
for(i=0;i<10;i++)
{
//linux command to check process status
sprintf(cmd, "cat /proc/%d/status", pid[i]);
pf = popen(cmd,"r");
fread(data, 1024, 1, pf);
pclose(pf);
//......big chunk of code afterwards
}
This is part of the code I'm running on my ubuntu. Basically, pid array has some the process id's, and I want those data to be parsed in some sort of way - which did succeed, so this isn't the problem.
The problem is with some part of the structure. Initially when I saved the pid array, I used "ls /proc/" command - the same way I used "cat /proc/%d/status" command in the above code - to check the /proc/ folder for all the processes that are currently running. The above code runs some time later, so when I use the pid array list for execution, some programs are no longer running, and thus, is not in /proc/ folder (for example, the program itself). So while all the pid data are printed out the way I want them to, some data come out as below:
In order to cope with this I added a line of code like this:
if(!pf) continue;
I thought that this would see that the command has failed, and skip this iteration, but it didn't change anything.
Is there any way to deal with that error message?
edit: I also tried if(pf < 0), but this didn't work either.
Use the stat function to see if a file exists, which works perfectly well for /proc files.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
struct stat buf;
int ret = stat("/proc/2023", &buf);
if(ret == -1) {
//file doesn't exist, so proc with pid 2023 isn't running
}
else {
//file exists, so proc with pid 2023 is running
}
Incorporating this into your loop, we have:
for(i=0;i<10;i++)
{
struct stat buf;
sprintf(cmd, "/proc/%d", pid[i]);
int ret = stat(cmd, &buf);
if(ret == -1) continue;
//linux command to check process status
sprintf(cmd, "cat /proc/%d/status", pid[i]);
pf = popen(cmd,"r");
fread(data, 1024, 1, pf);
pclose(pf);
//......big chunk of code afterwards
}

execute less with execv?

I have the following c code. I want to display my file with less by calling execv()
however the following seems never work. The program terminates and noting pop out.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(void){
int pid;
if(pid=fork()>0){
//read in from stdin and pass to pipe
}else if(pid==0){
//read from pipe
//write to out.txt
//everything up to here works fine
char* para[]={"less","/Desktop/out.txt"};
execv("/bin/less",para);
}
return 0;
}
(The original code contained execv("bin/less", para);.) Unless the current directory is the root directory, /, or unless there is a program less in the subdirectory ./bin/less, then one of your problems is that you have a probable typo in the name of the executable. That assumes the program is /bin/less and not /usr/bin/less. You might even use execvp() to do a PATH-based search for the program.
There's an additional problem: you need to include a null pointer to mark the end of the argument list.
Finally, you can print an error message after the execv() returns. The mere fact that it returns tells you it failed.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int pid;
if ((pid = fork()) != 0)
{
// read in from stdin and pass to pipe
// Need to test for fork() error here too
}
else
{
// read from pipe
// write to out.txt
// everything up to here works fine
char *para[] = { "/bin/less", "Desktop/out.txt", 0 };
execv(para[0], para);
fprintf(stderr, "Failed to execute %s\n", para[0]);
exit(1);
}
return 0;
}
Or:
char *para[] = { "less", "Desktop/out.txt", 0 };
execvp(para[0], para);
fprintf(stderr, "Failed to execute %s\n", para[0]);
The remarks in the code about pipes are puzzling since there is no sign of pipes other than in the comments. As it stands, less will read the file it is told to read. Note that less will not paginate its output if the output is not going to a terminal. Since we can see no I/O redirection, we have to assume, then, that less will ignore anything the program tries to write to it, and will not send any data back to the program.
char* para[]={"less","/Desktop/out.txt"};
execv("/bin/less",para);
How does execv know when to stop reading parameters?
I think if you'd put code in there to handle execv() returning an error you'd have found this. You're also not testing for errors from fork().

Can a program in C generate and call a bashscript in background and even parsing arguments?

Can a program in C generate a bashscript, and use some arguments from the C program to parse arguments to run the bashscript in background mode (without user noticing if really underground its not possible), so I can use some "cool" functions of bashscript to do some job in "C"? I need the result of bashscript coming back to the C program.
Thanks in any advice.
Use fork() to create a 'clone' of you process.
If you want the results from bash (or whatever other command you will exec) use pipe() and dup() to create a connection between processes from the standard output (and standard error if you want that too) of the exec'd command. Your process will be able to read (and write if you create more pipes) exec'd programs output text.
Normally the 'clone' process either use dup() to reconnect standard input/output/error onto pipe's, or closes standard standard input/output/error to prevent the exec'd program interacting with the user.
Then use one of the forms of exec().
If you're process does not wait() the exec'd process will run 'in the background', actually concurrently. The effect of being in the background is mainly the result of being disconnected from the terminal (keyboard and window).
If the pipe's are set up correctly, your program will be able to talk to the fork+exec'd process, but it is easy to get dead-lock, so I suggest you limit your self to reading its output.
The system() command can do the job if you specify the & parameter to the scipt process.
The argument of the system() should be the command you want to execute, just as if you have executed it from bash. In this case it would be script.sh &, where the & tells the operatinf system to run the process in the background.
Note that the use of system is operating system dependent and can be insecure, so use it accordingly.
The standard library system() function may help. An example might look like:
#include <stdlib.h>
int main( void ){
int scriptResult;
scriptResult = system("myscript.sh");
return 0;
}
There are probably better, more portable solutions to your problem, however because the behavior changes from platform to platform.
Did you take a look at the exec function and variants ? Basically, it replaces the current process with another, according to what you're supplying. I suggest reading the manpages, because there's quite a bit to know about those functions. But I think this is what you're looking for
Working example on Linux. change /bin/bash to your own bash location
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <assert.h>
#include <sys/wait.h>
int plumbing( const char * work, char * results, size_t so_results) {
int p1[2],p2[2];
pid_t pid,pidw;
void (*old)(int);
int bytes,cnt,status,rc;
char * bash[2] = {"/bin/bash",0};
old=signal(SIGCHLD,SIG_DFL);
rc = pipe(p1); if(rc){perror("pipe()");return -1;}
rc = pipe(p2); if(rc){perror("pipe()");return -1;}
pid = fork();
if (pid<0) { perror("fork"); return -1;}
if (0 == pid) {
rc = dup2(p1[0],0); if (-1 == rc) {perror("dup2"); return -1;}
rc = dup2(p2[1],1); if (-1 == rc) {perror("dup2"); return -1;}
close(p1[1]); close(p2[0]);
rc = execvp(bash[0],bash);
if (-1 == rc) {perror("execvp"); return -1;} exit(-1);
}
close(p1[0]);close(p2[1]);
write(p1[1],work,strlen(work));
cnt = 0;
close(p1[1]);
bytes = read(p2[0],results,so_results);
while(bytes > 0) {
cnt+=bytes;
if (so_results== cnt) break;
bytes = read(p2[0],results+cnt,so_results-cnt);
}
if (cnt == so_results) {
char dummy[4096];
bytes = read(p2[0],dummy,4096);
while (bytes>0) {
bytes = read(p2[0],dummy,4096);
}
}
pidw = waitpid(pid,&status,WCONTINUED|WUNTRACED);
if (pidw != pid || status !=0 ) { perror("waitpid"); return -1;}
signal(SIGCHLD,old);
return 0;
}
int main() {
int rc;
char buffer[4096];
rc = plumbing(
"t=$(cat<< ###\n"
"ICAgICAgIGE4ODg4Yi4KICAgICAgZDg4ODg4OGIuCiAgICAgIDhQIllQIlk4OAogICAgICA4fG98fG98\n"
"ODgKICAgICAgOCcgICAgLjg4CiAgICAgIDhgLl8uJyBZOC4KICAgICBkLyAgICAgIGA4Yi4KICAgIGRQ\n"
"ICAgLiAgICBZOGIuCiAgIGQ4OicgICIgIGA6Ojg4YgogIGQ4IiAgICAgICAgICdZODhiCiA6OFAgICAg\n"
"JyAgICAgIDo4ODgKICA4YS4gICA6ICAgICBfYTg4UAouXy8iWWFhXzogICAufCA4OFB8ClwgICAgWVAi\n"
"ICAgIGB8IDhQICBgLgovICAgICBcLl9fXy5kfCAgICAuJwpgLS0uLl9fKTg4ODhQYC5fLicK\n"
"###)\n"
"r64=\'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\'\n"
"i=0; while [ $i -lt 256 ] ; do tab[$i]=-1 ; let i=$i+1 ;done\n"
"i=0; while [ $i -lt 64 ] ; do tab[`printf \"%d\" \"\'${r64:$i:1}\"`]=$i ; let i=$i+1; done\n"
"bi=0\n"
"i=0 \n"
"while ((i < ${#t} )) \n"
" do\n"
" x=${t:$i:1}\n"
" in=${tab[`printf \"%d\" \"\'$x\"`]}\n"
" if [ $in -ge 0 ]; then case $bi in\n"
" 0 ) out=$(($in<<2)); bi=6 ;;\n"
" 2 ) out=$(($out|$in)); printf \\\\$(printf \'%03o\' $(($out&255)) ); bi=0 ;;\n"
" 4 ) out=$(($out+($in>>2))); printf \\\\$(printf \'%03o\' $(($out&255)) );\n"
" bi=0; out=$(($in<<6)); bi=2 ;;\n"
" * ) out=$(($out+($in>>4))); printf \\\\$(printf \'%03o\' $(($out&255)) );\n"
" bi=0; out=$(($in<<4)); bi=4 ;;\n"
" esac fi\n"
" i=$((1+$i))\n"
" done\n"
,buffer,4096);
printf("plumbing rc %d\n",rc);
printf("%s", buffer);
}

Resources