Example 3. hello, world
To use the shellout(3m) module effectively there are a few important rules that must be followed. One, is that the sho_close function calls wait(2) and will not return until the process spawned within the shell exits. If the child program is well behaved this can be achived by sending "exit $?\n" however doing so will not trigger the shell to exit if it is not ready to receive input. In this case it may be necessary to write a "\x03" which is the special Ctrl-C character. If the process does not respond to that kill(2) can be used to send a SIGKILL to the shell however even though this garunteed to get shell control back take care that it does not leave a zombie process lingering in the background.
The below code is a minimal "hello, world" example that opens a new /bin/sh shell with sho_open and writes the command "echo hello, world\n" to it. Note the '\n' character is as important as it is equivalent to pressing the 'Enter' key. Then it calls sho_expect which will wait for the command prompt 'sh> ' to come back. The command prompt is defined by the ps1 parameter to sho_open so the caller will know what it is and that it is uniqe to the expected output.
struct sho *sh; const char *cmd = "echo hello, world\n"; const char *pv[] = { "sh> " }; char buf[256]; int i; sh = sho_open("sh", pv[0], 0); writen(sh->ptym, cmd, strlen(cmd)); i = sho_expect(sh, pv, 1, buf, 256, 10); if (i == 1) { fprintf(stderr, "success: %s\n", buf); } else if (i == -1) { perror("timeout"); } else if (i == 0) { fputs("EOF\n", stderr); } /* output */ success: hello, world sh>In this example the caller knows when the command has completed when the command prompt is returned. Keep in mind that it is usually a better stategy to wait for the command prompt and then interpret the output in the buffer so that unexpected output doesn't result in a timeout. Provided the logic is equipted to handle the unexcepted output it might be perfectly reasonable to call sho_expect with other strings in the pattern vector. For example, to get a list of mounted filesystems the mount(8) command might be used with no arguments. If the pattern vector were change to: const char *pv[] = { "sh> ", "\n" }; for example the sho_expect function would return with each line in the buffer which assists in parsing the values in each mount entry.
The struct sho structure
Synopsis
Description
#include <mba/shellout.h> SHO_FLAGS_INTERACT SHO_FLAGS_ISATTY struct sho { char ps1[32]; int flags; pid_t pid; int ptym; struct termios t0; };
The sho_open function
Description
#include <mba/shellout.h> struct sho *sho_open(const char *sh, const char *ps1, int flags);
The sho_close function
Description
#include <mba/shellout.h> int sho_close(struct sho *sh);
The sho_expect function
Description
#include <mba/shellout.h> int sho_expect(struct sho *sh, const char *pv[], int pn, char *dst, size_t dn, int timeout);
The sho_loop function
Description
#include <mba/shellout.h> int sho_loop(struct sho *sh);
The sho_loop function uses the select(2) system call to read from the shell and write to stdout at the same time. This permits the shell output to be displayed in a terminal window as it is returned by the shell. When combined with the SHO_FLAGS_INTERACT flag passed to sho_open this will effectively create a shell within a shell. Note, "bash" might be a better first parameter to sho_open for an interactive shell. Also, some programs will not operate correctly (e.g. vi) because certain termios flags are not optimal for interactive sessions. This would need futher study.