With the ever growing presence of embedded systems, it has become a very common requirement to do some tasks through shell commands while doing others through C in an embedded system. Very often people achieve this by using system() C library function, which is known for its inefficiencies and limitations. So, is there a better way to achieve it? Yes. The main C (commander) program may spawn a master shell script process, which would then keep on accepting & executing shell commands from the commander program. Here are the two components (a main C program and a master script) of the framework, put out:
/* File: commander.c */
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(char argc, char *argv[])
{
int pfds[2]; // 0 is read end, 1 is write end
int wfd;
pid_t pid;
char cmd[100];
int cmdlen;
int stop, status;
if (pipe(pfds) == -1)
{
perror(argv[0]);
return 1;
}
pid = fork();
if (pid == -1)
{
perror(argv[0]);
return 2;
}
else if (pid != 0) // Parent
{
close(pfds[0]); // Close the read end of the pipe
wfd = pfds[1];
// Continue doing other stuff, e.g.
// take commands from user and pass onto the master script
stop = 0;
do
{
printf("Cmd (type \"done\" to exit): ");
if ((status = scanf("%[^\n]", cmd)) <= 0)
{
getchar(); // Remove the \n
continue;
}
getchar(); // Remove the \n
if (strcmp(cmd, "done") == 0)
{
stop = 1;
}
else
{
cmdlen = strlen(cmd);
cmd[cmdlen++] = '\n';
//cmd[cmdlen] = '\0';
// Pass on the command to master script
write(wfd, cmd, cmdlen);
}
}
while (!stop);
close(wfd);
}
else
{
close(pfds[1]); // Close the write end of the pipe
dup2(pfds[0], 0); // Make stdin the read end of the pipe
if (execl("./master_script.sh", "master_script.sh", (char *)(NULL))
== -1)
{
perror("Master script process spawn failed");
}
}
return 0;
}
# File: master_script.sh
#!/bin/bash
while read cmd
do
#echo "Running ${cmd} ..."
${cmd}
done
One may compile the commander.c and try it as follows:
$ gcc commander.c -o commander $ ./commander
This approach becomes even more powerful, when the shell command execution is pretty often. Also note that the main thread need not block for the command execution to complete. Though, it can be customized to block as well, if required. And many more customizations can be achieved as desired, e.g. getting the command output back into the C program instead of stdout, redirecting the command error into some log file instead of stderr, getting the command status – to list a few. Post your comments below to discuss any customizations of your interest.