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.