writing linux shell - c

I am trying to learn Unix C and doing some exercises for practice. I am currently working on writing my own shell that works similar to the linux bash shell.
The code I have below provides for a fairly basic shell. It now provides I/O redirection.
I am trying to add support for piping. Initially, I just want to add support for a single pipe.
I have tried to go through some tutorials online but can't quite figure out where to start.
Currently, the shell below can handle commands commands such as the following.
ls > abc, cat< file1 > file2, etc.
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <sys/wait.h>
#include <fcntl.h>
#define TRUE 1
int main(void)
{
char *arg_list[10];
int status;
int counter = 0;
int counter2 = 0;
pid_t pid;
char buf[100];
char inFile[10];
char outFile[10];
int fdIn, fdOut;
while(TRUE)
{
printf("> ");
if (!fgets(buf, 100, stdin))
return 0;
pid = fork();
switch(pid)
{
case -1:
return 1;
case 0:
{
arg_list[counter] = strtok(buf, " \n");
counter = 0;
while(arg_list[counter] != NULL)
{
counter++;
arg_list[counter] = strtok(NULL, " \n");
}
counter2 = 0;
while(arg_list[counter2] != NULL)
{
if(!strcmp(arg_list[counter2], "<"))
{
if(arg_list[counter2+1] != NULL)
{
fdIn = open(arg_list[counter2+1], O_RDONLY);
dup2(fdIn, STDIN_FILENO);
}
else
{
printf("No input file specified");
}
arg_list[counter2] = 0;
}
else if(!strcmp(arg_list[counter2], ">"))
{
if(arg_list[counter2+1] != NULL)
{
fdOut = open(arg_list[counter2+1], O_CREAT | O_WRONLY | O_TRUNC, 0666);
dup2(fdOut, STDOUT_FILENO);
}
else
{
printf("No output file specified");
}
arg_list[counter2] = 0;
}
counter2++;
}
execvp(arg_list[0], arg_list);
break;
}
default:
waitpid(-1, &status, 0);
break;
}
}
return 0;
}
If somebody can point me in the right direction, that would be appreciated.

After you use dup2() (successfully), you have two file descriptors open for the one file. You need to close the original file descriptor; the executed process should not have the extra file descriptors open.
You are also going to need to open the files in the appropriate way before using dup2(). Amongst other things, that means strtok() is not a good choice because it blats out the delimiter but you need to know which file to open for reading and which for writing).
You are also going to need to fettle the argument list; it should contain just the command name and a null pointer, not the two file names.

Why do you need to check the type of command? Unix shells don't treat any commands specially; all redirections, including pipes, are handled the same way. One thing to watch out for is that redirections can happen anywhere in a command, so you should parse them out first; try
>foo ls <bar -la
in a shell sometime. (Pipes are an obvious exception to this, since they also delimit commands; syntactically | is the same as ;, although semantically there is redirection involved in addition.)

Related

is it possible to read and write with the same file descriptor in C

I am trying to write to a file and display the output of the thing i wrote with another process. The code i come up with:
void readLine (int fd, char *str) {
int n;
do {
n = read (fd, str, 1);
} while (*str++ != '\0');
}
int main(int argc,char ** argv){
int fd=open("sharedFile",O_CREAT|O_RDWR|O_TRUNC,0600);
if(fork()==0){
char buf[1000];
while(1) {
readLine(fd,buf);
printf("%s\n",buf);
}
}else{
while(1){
sleep(1);
write(fd,"abcd",strlen("abcd")+1);
}
}
}
the output i want (each result spaced from the other with a period of one second):
abcd
abcd
abcd
....
Unfortunately this code doesn't work, it seems that the child process (the reader of the file "sharedFile") reads junk from the file because somehow it reads values even when the file is empty.
When trying to debug the code, readLine function never reads the written file correctly,it always reads 0 bytes.
Can someone help?
First of all, when a file descriptor becomes shared after forking, both the parent and child are pointing to the same open file description, which means in particular that they share the same file position. This is explained in the fork() man page.
So whenever the parent writes, the position is updated to the end of the file, and thus the child is always attempting to read at the end of the file, where there's no data. That's why read() returns 0, just as normal when you hit the end of a file.
(When this happens, you should not attempt to do anything with the data in the buffer. It's not that you're "reading junk", it's that you're not reading anything but are then pretending that whatever junk was in the buffer is what you just read. In particular your code utterly disregards the return value from read(), which is how you're supposed to tell what you actually read.)
If you want the child to have an independent file position, then the child needs to open() the file separately for itself and get a new fd pointing to a new file description.
But still, when the child has read all the data that's currently in the file, read() will again return 0; it won't wait around for the parent to write some more. The fact that some other process has a file open for writing don't affect the semantics of read() on a regular file.
So what you'll need to do instead is that when read() returns 0, you manually sleep for a while and then try again. When there's more data in the file, read() will return a positive number, and you can then process the data you read. Or, there are more elegant but more complicated approaches using system-specific APIs like Linux's inotify, which can sleep until a file's contents change. You may be familiar with tail -f, which uses some combination of these approaches on different systems.
Another dangerous bug is that if someone else writes text to the file that doesn't contain a null byte where expected, your child will read more data than the buffer can fit, thus overrunning it. This can be an exploitable security vulnerability.
Here is a version of the code that fixes these bugs and works for me:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void readLine (int fd, char *str, size_t max) {
size_t pos = 0;
while (pos < max) {
ssize_t n = read(fd, str + pos, 1);
if (n == 0) {
sleep(1);
} else if (n == 1) {
if (str[pos] == '\0') {
return;
}
pos++;
} else {
perror("read() failure");
exit(2);
}
}
fprintf(stderr, "Didn't receive null terminator in time\n");
exit(2);
}
int main(int argc, char ** argv){
int fd=open("sharedFile", O_CREAT|O_RDWR|O_TRUNC, 0600);
if (fd < 0) {
perror("parent opening sharedFile");
exit(2);
}
pid_t pid = fork();
if (pid == 0){
int newfd = open("sharedFile", O_RDONLY);
if (newfd < 0) {
perror("child opening sharedFile");
exit(2);
}
char buf[1000];
while (1) {
readLine(newfd, buf, 1000);
printf("%s\n",buf);
}
} else if (pid > 0) {
while (1){
sleep(1);
write(fd,"abcd",strlen("abcd")+1);
}
} else {
perror("fork");
exit(2);
}
return 0;
}

send up down arrow keys to process through pipe in linux using c language

I have created two processes using fork. Created a pipe. Parent will write keys at write end of pipe and child stdin(0) will be duplicated by read end of pipe. Up to know its working very well and good for alphabets.
But i want to send up and down arrow keys also, please help me.
int main()
{
int fd[2];
char enter = 10;
char *exit = "exit";
char up = 193;//what i have to use here
char down = 194;//what i have to use here
pipe(p);
if(fork())
{
write(p[1],&up,1); //not working
write(p[1],&down,1); //not working
write(p[1],exit,strlen(exit)); //working
write(p[1],&enter,1); //working
wait(NULL);
}
else
{
close(0);
dup(p[0]);
execl("/bin/sh","sh",NULL);
}
}
Please help me,
There are several points:
1.) You have to invoke a shell which supports terminal editing with arrows. On a usual Linux this may be /bin/bash instead of /bin/sh.
2.) bash is checking whether it's input is coming from a terminal device or not. Depending on this, it behaves like an interactive shell or not. It seems that you want to use it in interactive mode. However pipe is not a terminal device. To get it into interactive mode you can use bash option "-i" on its invocation.
3.) As pointed by commentaries, on a usual Linux X-terminal codes for arrow up and down are multi character strings like "\033[A" and "\033[B". It depends on the device and environment you are using, maybe your values are correct for your system.
The following code works on a usual Linux environment:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
int p[2];
char enter = 10;
char *exit = "exit";
char *up = "\033[A";
char *down = "\033[B";
pipe(p);
if(fork())
{
write(p[1],up,3);
write(p[1],down,3);
write(p[1],exit,strlen(exit));
write(p[1],&enter,1);
wait(NULL);
}
else
{
close(0);
dup(p[0]);
execl("/bin/bash","bash","-i",NULL);
}
}
Also, you shall test return values of pipe and fork. Personally I'd write it like:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
int p[2];
int r;
char command[] = "\033[A\033[Bexit\n";
r = pipe(p);
if (r < 0) {
perror("Can't create pipe");
return(-1);
}
r = fork();
if (r < 0) {
perror("Can't fork");
return(-1);
} else if (r > 0) {
close(p[0]);
write(p[1], command, sizeof(command)-1);
close(p[1]);
wait(NULL);
} else {
close(p[1]);
dup2(p[0], 0);
close(p[0]);
execl("/bin/bash","bash","-i",NULL);
}
}

exit command does not work properly in my own shell

I wrote a shell for an assignment and it works correctly, but there is a small run time error which i can not figure out. When the user enter the command 'exit' through the shell it should come out of newly created shell. But the problem is I have to type the command 'exit' several times to quit the shell. If someone can help me it will be a great pleasure for me! Thanks all!
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
char* cmndtkn[256];
char buffer[256];
char* path=NULL;
char pwd[128];
int main(){
//setting path variable
char *env;
env=getenv("PATH");
putenv(env);
system("clear");
printf("\t MY OWN SHELL !!!!!!!!!!\n ");
printf("_______________________________________\n\n");
while(1){
fflush(stdin);
getcwd(pwd,128);
printf("[MOSH~%s]$",pwd);
fgets(buffer,sizeof(buffer),stdin);
buffer[sizeof(buffer)-1] = '\0';
//tokenize the input command line
char* tkn = strtok(buffer," \t\n");
int i=0;
int indictr=0;
// loop for every part of the command
while(tkn!=NULL)
{
if(strcoll(tkn,"exit")==0 ){
exit(0);
}
else if(strcoll(buffer,"cd")==0){
path = buffer;
chdir(path+=3);}
else if(strcoll(tkn,"|")==0){
indictr=i;}
cmndtkn[i++] = tkn;
tkn = strtok(NULL," \t\n");
}cmndtkn[i]='\0';
// execute when command has pipe. when | command is found indictr is greater than 0.
if(indictr>0){
char* leftcmnd[indictr+1];
char* rightcmnd[i-indictr];
int a,b;
for(b=0;b<indictr;b++)
leftcmnd[b]=cmndtkn[b];
leftcmnd[indictr]=NULL;
for(a=0;a<i-indictr-1;a++)
rightcmnd[a]=cmndtkn[a+indictr+1];
rightcmnd[i-indictr]=NULL;
if(!fork())
{
fflush(stdout);
int pfds[2];
pipe(pfds);
if(!fork()){
close(1);
dup(pfds[1]);
close(pfds[0]);
execvp(leftcmnd[0],leftcmnd);
}
else{
close(0);
dup(pfds[0]);
close(pfds[1]);
execvp(rightcmnd[0],rightcmnd);
}
}else wait(NULL);
//command not include pipe
}else{
if(!fork()){
fflush(stdout);
execvp(cmndtkn[0],cmndtkn);
}else wait(NULL);
}
}
}
Like the cd command, the exit command has to be interpreted by the shell as a built-in; it must exit the loop or call the exit() function directly. However, it also appears that should be happening. Note that using strcoll() is a little unusual; normally, strcmp() is sufficient.
You should report problems if execvp() returns — and you must make sure the sub-shell exits so that you don't have multiple shell processes reading the input simultaneously. I'm left wondering if this problem is occurring, and that's why you have to type exit multiple times.
You also need to check that fgets() did not report an error. It always null terminates its input; your code does not zap the newline (you'd need strlen(buffer)-1 instead of sizeof(buffer)-1).
The code that reads and sets PATH is wrong. getenv("PATH") returns a pointer to the first character after the PATH= part; you then use that to 'set' the environment. Fortunately for you, the average value for PATH does not contain anything that looks like VAR=value, so it is functionally a no-op (though the information is probably copied into the environment, where it makes a mess without causing any major harm).
Your code indentation scheme is rococo at best — mostly, it is just woefully inconsistent. Please be consistent! The spacing of the lines in the code was also extremely erratic. When you're adding code in SO, do not use tabs, do use 4 spaces per indent level, do highlight a block of code that is left justified and use the {} button above the edit box to indent it as code. This also means you don't need to add blank lines to the code.
You aren't closing enough file descriptors. When you use dup() (or dup2()) to duplicate a pipe to standard input or standard output, you have to close both of the file descriptors returned by pipe().
On Linux, using fflush(stdin) is undefined behaviour, AFAIK. It is defined on Windows, but not on POSIX systems.
You don't check that your chdir() system call works.
Trying your code, I did get one runaway prompt. Unfortunately, I couldn't remember or see what triggered the runaway. The code below is mostly sanitized and seems to behave. I've annotated some critical changes — and not others. One of the things you should be doing for your own benefit is including trace like the dump_cmd() function so you can see what your program is doing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
char *cmndtkn[256];
char buffer[256];
char *path = NULL;
char pwd[128];
static void dump_cmd(char **argv);
int main(void)
{
/*
//setting path variable
char *env;
env=getenv("PATH");
putenv(env);
system("clear");
*/
printf("\t MY OWN SHELL !!!!!!!!!!\n ");
printf("_______________________________________\n\n");
while (1)
{
//fflush(stdin);
getcwd(pwd, 128);
printf("[MOSH~%s]$", pwd);
if (fgets(buffer, sizeof(buffer), stdin) == 0)
{
putchar('\n');
break;
}
//buffer[sizeof(buffer)-1] = '\0';
buffer[strlen(buffer)-1] = '\0';
//tokenize the input command line
char *tkn = strtok(buffer, " \t\n");
int i = 0;
int indictr = 0;
// loop for every part of the command
while (tkn != NULL)
{
if (strcoll(tkn, "exit") == 0)
{
printf("Got: exit\n");
fflush(stdout);
exit(0);
}
else if (strcoll(tkn, "cd") == 0) // Was buffer, not tkn
{
printf("Got: cd (%s)\n", buffer + 3);
fflush(stdout);
path = buffer;
chdir(path += 3);
}
else if (strcoll(tkn, "|") == 0)
{
indictr = i;
}
cmndtkn[i++] = tkn;
tkn = strtok(NULL, " \t\n");
}
cmndtkn[i] = 0;
// execute when command has pipe. when | command is found indictr is greater than 0.
if (indictr > 0)
{
char *leftcmnd[indictr+1];
char *rightcmnd[i-indictr];
int a, b;
for (b = 0; b < indictr; b++)
leftcmnd[b] = cmndtkn[b];
leftcmnd[indictr] = NULL;
for (a = 0; a < i-indictr-1; a++)
rightcmnd[a] = cmndtkn[a+indictr+1];
rightcmnd[i-indictr-1] = NULL; // Did not include -1
if (!fork())
{
fflush(stdout);
int pfds[2];
pipe(pfds);
if (!fork())
{
dump_cmd(leftcmnd);
close(1);
dup(pfds[1]);
close(pfds[0]);
close(pfds[1]);
execvp(leftcmnd[0], leftcmnd);
fprintf(stderr, "failed to execvp() %s\n", leftcmnd[0]);
exit(1);
}
else
{
dump_cmd(rightcmnd);
close(0);
dup(pfds[0]);
close(pfds[0]);
close(pfds[1]);
execvp(rightcmnd[0], rightcmnd);
fprintf(stderr, "failed to execvp() %s\n", rightcmnd[0]);
exit(1);
}
}
else
wait(NULL);
}
else
{
//command does not include pipe
if (!fork())
{
dump_cmd(cmndtkn);
fflush(stdout);
execvp(cmndtkn[0], cmndtkn);
fprintf(stderr, "failed to execvp() %s\n", cmndtkn[0]);
exit(1);
}
else
wait(NULL);
}
}
return 0;
}
static void dump_cmd(char **argv)
{
int i = 0;
fprintf(stderr, "%d: Command:\n", (int)getpid());
while (*argv != 0)
fprintf(stderr, "%d: %d: [[%s]]\n", (int)getpid(), i++, *argv++);
}
I'm not keen on the code, but it does seem mostly sane.

UNIX Commands Implemented in C

For my Operating Systems class I have an assignment due that is built onto a previous assignment. Unfortunately my previous project doesn't work correctly in addition to me not knowing where I need to start for the next project. The code which I have below is suppose to mimic a simple UNIX/Linux shell with some additional commands that cannot be performed with execvp: background processing via the ampersand operator, the 'jobs' shell command: list the pids of all living child processes (i.e. not ones that have terminated), "reaping" of "zombie" processes, and the 'cd' shell command: change the shell's working directory.
I believe, everything but the "jobs" command, and "cd" command work, but I'm not sure why these two don't.
The next assignment is to add some I/O redirection in the form of "mysh$ cmd arg1 arg2 argN > file.out" which I don't know where to even really begin...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>
#include <sys/types.h>
int main(int argc, char **argv) {
char bBuffer[BUFSIZ], *pArgs[10], *aPtr = NULL, *sPtr;
int jobs[100];
int jobList = 0;
int background;
ssize_t rBytes;
int aCount;
pid_t pid;
int status;
while(!feof(stdin)) {
pid = waitpid(-1, &status, WNOHANG);
if (pid > 0)
printf("waitpid reaped child pid %d\n", pid);
write(1, "\e[1;31mmyBash \e[1;32m# \e[0m", 27);
rBytes = read(0, bBuffer, BUFSIZ-1);
if(rBytes == -1) {
perror("read");
exit(1);
}
bBuffer[rBytes-1] = '\0';
if(!strcasecmp(bBuffer, "exit")){
exit(0);
}
sPtr = bBuffer;
aCount = 0;
do {
aPtr = strsep(&sPtr, " ");
pArgs[aCount++] = aPtr;
} while(aPtr);
background = (strcmp(pArgs[aCount-2], "&") == 0);
if (background)
pArgs[aCount-2] = NULL;
if (strlen(pArgs[0]) > 1) {
pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
} else if (pid == 0) {
jobs[jobList] = pid;
jobList++;
if(!strcasecmp(pArgs[0], "jobs")){
for(int i; i<jobList; i++) {
if(kill(jobs[i],0)==0){
printf(jobs[i]);
}
printf("these are jobs\n");
exit(1);
}
if(!strcasecmp(pArgs[0], "cd")){
int ret;
if (!pArgs[1])
strcpy(bBuffer, "pwd");
ret = chdir(pArgs[1]);
strcpy(bBuffer, "pwd");
exit(1);
}
fclose(stdin);
fopen("/dev/null", "r");
execvp(pArgs[0], pArgs);
exit(1);
} else if (!background) {
pid = waitpid(pid, &status, 0);
if (pid > 0)
printf("waitpid reaped child pid %d\n", pid);
}
}
}
return 0;
}
First you;ll want to parse your line and detect that you need to redirect to a file. So let;s say you use strsep or whatever and you found out output is going to file.out or input is coming from file.in.
At this point you want to redirect output using dup / dup2. For example, to redirect STDOUT:
int
do_redirect(int fileno, const char *name)
{
int newfd;
switch (fileno) {
case STDOUT_FILENO:
newfd = open(name, O_WRONLY | O_CREAT, S_IRUSR | S_IRUSR);
break;
}
if (newfd == -1) {
perror("open");
return -1;
}
return dup2(fileno, newfd);
}
/* ... */
pid = fork();
do_redirect(STDOUT_FILENO, name);
Things to note:
I didn't test the code - it might not even compile
I didn't do much error-checking - you should (the way I did for open)
You need to implement STDIN_FILENO redirection on your own
Note how I used a separate function, your main is WAY to large as it is
Your code has something like 7 levels of indentation - ever heard about arrow code ?
Since this is homework, I will not give you code directly.
dup, dup2 and freopen are good to look at for input/output redirection.
fork for starting a concurrent process (ampersand)
You are on the right track using waitpid to reap child processes.

Send input to a program and get control back

I have been stuck on this for some time.
Let's say I have a C program like the following. I want to be able to send this program some string and get the control after that.
If I do:
--> cat myfile | myprogram
or
--> echo "0123" | myprogram
or
--> myprogram < myfile
I get the ouput (myfile contains "0123")
30 31 32 33
Using the -n option raises a segfault
--> echo -n mystring | ./test
zsh: done echo -n "0123" |
zsh: segmentation fault ./test
I also tried with a named pipe, but it didn't work either.
I would like to be able to do something like
cat myfile | myprogram
and get back the control so that I can type other characters.
1 #include <stdlib.h>
2 #include <stdio.h>
3
4 int main (int argc, char *argv[]) {
6 int i = 0, j;
7 unsigned char buf[512];
8 unsigned char x;
9
10 while ((x = getchar()) != '\n') {
11 buf[i] = x;
12 i++;
13 }
14
16 for (j = 0; j < i; j++) {
17 printf("%x ", buf[j]);
18 }
19 printf ( "\n" );
20
21 return EXIT_SUCCESS;
22 } // end of function main
EDIT:
Below is the wrapper I have come up with.
It does everything I want, except that the output of the child exec-ed file is not properly displayed.
Without the wrapper:
$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
2+2
4
With the wrapper:
$ ./wrapper bc
2+2
enter
4
Deleting the line
dup2(pipefd[0], 0); // Set the read end of the pipe as stdin.
makes the child stdout display correctly, but of course breaks the wrapper.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <assert.h>
int main(int argc, char const *argv[]) {
int cpid;
int pipefd[2];
if (pipe(pipefd) == -1) { perror("pipe.\n"); exit(errno); }
cpid = fork();
if (cpid == -1) { perror("fork."); exit(errno); }
if (cpid) {
// Parent --------------------------------------------------------
int buf_size = 8192;
char buf[buf_size];
size_t file;
// Close the unused read end of the pipe.
close(pipefd[0]);
// Leave a bit of time to the child to display its initial input.
sleep(2);
while (1) {
gets(buf);
if (strcmp("enter", buf) == 0) {
write(pipefd[1], "\n", 1);
} else if (-1 != (file = open(buf, O_RDONLY))) {
// Dump the output of the file to the child's stdin.
char c;
while(read(file, &c, 1) != 0) {
switch(c) {
case '\n':
printf("(skipped \\n)");
break;
default:
printf("%c", c);
write(pipefd[1], &c, 1);
};
}
printf("\n");
} else {
// Dump input to the child's stdin, without trailing '\n'.
for (int i = 0; (buf[i] != 0); i++) {
write(pipefd[1], buf + i, 1);
}
}
}
// Wait for the child to exit.
printf("Waiting for child to exit.\n");
wait(NULL);
} else {
// Child ---------------------------------------------------------
// Close the unused write end of the pipe.
close(pipefd[1]);
// Set the read end of the pipe as stdin.
dup2(pipefd[0], 0); // Set the read end of the pipe as stdin.
char** program_arguments = (char**)(argv + 1);
if (execvp(argv[1], program_arguments) < 0) {
perror("execvp.\n");
exit(errno);
}
}
}
I do not think it is possible to achieve this using named pipes if you can not modify the behavior of the program. Since in essence named pipes are no different then giving the output from standard input with redirection.
I also do not think it is possible if you use pipe or redirection properties of the shell, since always an EOF is sent to your program in this case and you can not ignore EOF since you can not modify the program.
A possible solution is to use a wrapper. The wrapper will first read the prepared input, send them to your program, after the prepared input finishes the wrapper switches to standard input. Actual program just keeps consuming input, it is not aware of the actual source of the data.
Only drawback is, you can not provide prepared input with pipes or redirection, you have to supply a filename. (I'm not sure a named pipe will work or not.) The reason is obvious, if you provide the prepared input to wrapper from standard input then the same problem exists for wrapper. By this way you are just delegating the problem to wrapper, which you can design any way you want.
A possible implementation in C (modified from a similar wrapper I've used, not tested extensively):
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
int main(int argc, char * argv[]) {
char c;
char **pargs ;
char buf[20];
int n;
int pipe_fd[2];
int pid;
pargs = argv+2;
if (pipe(pipe_fd) < 0) {
perror("pipe failed");
exit(errno);
}
if ((pid=fork()) < 0) {
perror ("Fork failed");
exit(errno);
}
if (! pid) {
close(pipe_fd[1]);
dup2(pipe_fd[0],0);
close(pipe_fd[0]);
if (execvp(argv[2],pargs) < 0) {
perror("Exec failed");
exit(errno);
}
} else {
size_t filedesc = open(argv[1],O_RDONLY);
while((n = read(filedesc, buf, 100)) > 0)
write (pipe_fd[1], buf, n);
while((n = read(0, buf, 100)) > 0)
write (pipe_fd[1], buf, n);
}
}
You can run your program with this wrapper as :
./wrapper input.txt myprog possible command line arguments
You can put your initial input into input.txt.
A simpler solution is to reopen the standard input. However if you simply try to open it as if you are opening a file, it does not work. You should open the terminal stream and copy it to standard input of your application. You can do it (again by using a wrapper) with something like:
size_t tty = open("/dev/tty",O_RDONLY);
dup2(tty,0);
Not to mention this second solution is for Linux and not portable.
In this example I use tail -f, not your C program
mkfifo /tmp/pipe # Open Pipe
tail -f /tmp/pipe & # Start your program and put it into the background
Now you also can send data to your program that runs in the background
echo "foobar" > /tmp/pipe
I hope this helps?
You could modify your program to accept 1 null character then continue on...it might work:
replace line 10 with something like
while (TRUE)
{
x = getchar();
if (x == '\n')
break;
if (x == '\0')
{
if (seen)
break;
seen = TRUE;
}
...

Resources