Create process and anonymous pipe - c

UPDATE Question: So I have been able to create the process and get the programs to compiled. However, I run into a new problem. When I try to pipe the Source program into the Filter program. It doesn't seem to feed in any input for the sink program. There are no error message. I have also test all of my standalone program using the pipe operator in Windows' cmd.
I'm trying to do this small project to learn about anonymous pipe and create process. I created 3 small standalone programs called Source, Filter, and Sink. These 3 are already compiled and run fine. Here's the descriptions for the 3 standalone programs.
Source: obtains a source text-file filename from its commandline, opens the file,and reads and copies the file contents one character at a time directly to standard output(stdout). When the file has been copied, Source terminates (closing all of its open file handles).
Filter program does not utilize any filename commandline parameters. Instead, Filter reads a text file from standard input (stdin) and writes to standard output (stdout) a copy of the input with all upper-case letters converted to lower-case. Filter specifically must be designed to read one character, convert it, output it, and then loop until the incoming data is finished.
Sink program obtains a destination text-file filename from its commandline, opens the file for writing, and then reads characters one at a time from the standard input file (stdin) and writes each incoming character directly to the destination sink file.
Next I'm driving a main driver program separately that creates 2 pipes and spawn 3 independent child with inputs and outputs configured to carry out the indicated concurrent execution and dataflow. Something like this:
srcfile -> Source -> pipe1 -> Filter -> Pipe2 -> Sink -> destfile
The driver program will requires 2 command line parameters:
C:\> Driver.exe srcfile destfile
where srcfile is an existing data text-file, and destfile is the filename of a new destination file that is to be created by the Sink application.
Here's my code for the driver program. It's not finished yet. But I encountered a hiccup when trying to create a process for the Source program.
UPDATED CODE:
#include <windows.h>
#include <WinBase.h>
#include <stdio.h>
#define DELAY_A_WHILE() {volatile long j; for(j = 1; j< 10000; j++) ; }
int main(int argc, char *argv[])
{
HANDLE hPipeRead, hPipeWrite, hPipeRead2, hPipeWrite2;
STARTUPINFO StartupInfoSource;
STARTUPINFO StartupInfoFilter;
STARTUPINFO StartupInfoSink;
PROCESS_INFORMATION ProcInfoSource;
PROCESS_INFORMATION ProcInfoFilter;
PROCESS_INFORMATION ProcInfoSink;
SECURITY_ATTRIBUTES PipeAttributes;
SECURITY_ATTRIBUTES PipeAttributes2;
char cmdline[200];
PipeAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
PipeAttributes.lpSecurityDescriptor = NULL; //ignore
PipeAttributes.bInheritHandle = TRUE; //child can inherit
//Create first pipe
if (!CreatePipe(&hPipeRead, &hPipeWrite, &PipeAttributes, 0)) {
fprintf(stderr, "Error creating pipe: %d\n", GetLastError());
exit(1);
}
sprintf_s(cmdline, 200, "Source.exe %s", argv[1]);
printf("Create process: %s\n", cmdline);
GetStartupInfo(&StartupInfoSource);
StartupInfoSource.dwFlags = StartupInfoSource.dwFlags | STARTF_USESTDHANDLES;
//Mapping
StartupInfoSource.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
StartupInfoSource.hStdOutput = hPipeWrite;
StartupInfoSource.hStdError = GetStdHandle(STD_ERROR_HANDLE);
if (!CreateProcess(
NULL, cmdline, NULL, NULL,
TRUE,
CREATE_NEW_CONSOLE, NULL, NULL,
&StartupInfoSource,
&ProcInfoSource))
{
fprintf(stderr, "Error creating child process: %d",GetLastError());
exit(1);
}
CloseHandle(hPipeWrite);
CloseHandle(ProcInfoSource.hProcess);
CloseHandle(ProcInfoSource.hThread);
PipeAttributes2.nLength = sizeof(SECURITY_ATTRIBUTES);
PipeAttributes2.lpSecurityDescriptor = NULL; //ignore
PipeAttributes2.bInheritHandle = TRUE; //child can inherit
//Create Second Pipe
if (!CreatePipe(&hPipeRead2, &hPipeWrite2, &PipeAttributes2, 0)) {
fprintf(stderr, "Error creating pipe: %d\n", GetLastError());
exit(1);
}
GetStartupInfo(&StartupInfoFilter);
StartupInfoFilter.dwFlags = StartupInfoFilter.dwFlags | STARTF_USESTDHANDLES;
//Mapping
StartupInfoFilter.hStdInput = hPipeRead;
StartupInfoFilter.hStdOutput = hPipeWrite2;
StartupInfoFilter.hStdError = GetStdHandle(STD_ERROR_HANDLE);
sprintf_s(cmdline, 200, "Filter.exe");
printf("Create process: %s\n", cmdline);
//Filter
GetStartupInfo(&StartupInfoFilter);
if (!CreateProcess(
NULL, cmdline, NULL, NULL,
TRUE,
CREATE_NEW_CONSOLE, NULL, NULL,
&StartupInfoFilter,
&ProcInfoFilter))
{
fprintf(stderr, "Error creating child process: %d", GetLastError());
exit(1);
}
// int exitStatus;
// GetExitCodeProcess(ProcInfoFilter.hProcess, &exitStatus);
CloseHandle(hPipeRead);
CloseHandle(hPipeWrite2);
CloseHandle(ProcInfoFilter.hProcess);
CloseHandle(ProcInfoFilter.hThread);
GetStartupInfo(&StartupInfoSink);
StartupInfoSink.dwFlags = StartupInfoSink.dwFlags | STARTF_USESTDHANDLES;
//Mapping
StartupInfoSink.hStdInput = hPipeRead2;
StartupInfoSink.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
StartupInfoSink.hStdError = GetStdHandle(STD_ERROR_HANDLE);
sprintf_s(cmdline, 200, "Sink.exe %s", argv[2]);
printf("Create process: %s\n", cmdline);
GetStartupInfo(&StartupInfoSink);
if (!CreateProcess(
NULL, cmdline, NULL, NULL,
TRUE,
CREATE_NEW_CONSOLE, NULL, NULL,
&StartupInfoSink,
&ProcInfoSink))
{
fprintf(stderr, "Error creating child process: %d", GetLastError());
exit(1);
}
CloseHandle(hPipeRead2);
CloseHandle(ProcInfoSink.hProcess);
CloseHandle(ProcInfoSink.hThread);
return 0;
}
The program compiles fine. However, when it try to creates the Process, it always fails and exits. The cmdline value when it parse in is "Source.exe test.txt", which is exactly what I used to execute my standalone Source program. Can someone explain why my CreateProcess fail? Is it because I parse in the wrong parameter?

The only problem I can see here is the possibility that Source.exe app is not located in the same directory where Driver.exe is located. I tried your code and it was the only case when CreateProcess failed.

I have figure out why. My project's property was using the Unicode character set. When I switch to Multi-byte character set it works fine.

Related

write() prints in wrong order

I'm trying to output ordered set of lines created by multiple processes.
I found that printf() and fprintf() is not suitable for such task. Right now I'm using this set of commands:
sprintf(buff,"%d: some string", (*counter)++); //build string (write can't do that)
write(file, buff, strlen(buff));
memset(buff, 0, strlen(buff)); //clear buffer before next writing
File opening and starting processes is shown below:
int file;
int main(){
pid_t busPID, riderPID;
file = open("outputFile.txt", O_WRONLY | O_CREAT | O_APPEND | O_TRUNC, 0666);
if((busPID = fork()) == 0){
bus();
else{
if((riderPID = fork()) == 0){
for(int i = 0; i < 10; i++){
rider();
}
}else{
pid_t waitPID;
while ((waitPid = wait(&status)) > 0); //wait for all riders to finish
}
}
waitpid(busPID, NULL, 0);
return 0;
}
Here are functions, which prints output:
void bus() {
char buff[50];
//writing
do {
//writing
if(*onStop > 0) {
//writing
sem_post(semRider);
sem_wait(semBus);
//writing
*onStop = 0; //internal value, irrelevant for answer
}
//writing
usleep(rand()%busSleep);
//writing
sem_post(semRider);
sem_wait(semBus);
departuredAkt += temp; //internal value, irrelevant for answer
} while(departuredAkt < departuredTotal);
//writing
exit(EXIT_SUCCESS); //exit this process
}
void rider() {
char buff[50];
//writing
int pos = ++(*onStop);
//writing
sem_wait(semRider);
//writing
sem_post(semBus);
sem_wait(semRider);
//writing
sem_post(semBus);
exit(EXIT_SUCCESS);
}
There is only 1 process using bus() function and N processes using rider()function (specified by argument). desired output is:
1: bus string
2: bus string
3: bus string
4: rider 1 string
5: rider 1 string
.
.
.
25: rider N string
26: bus string
My current output looks like this:
1: bus string
2: bus string
3: bus string
4: rider 1 string
6: bus string //here is the problem
5: rider 1 string
Question is, how can I achieve printing lines in correct order?
First of all, side note: never use sprintf, this function is completely unsecure. Use snprintf. Example:
snprintf (buff, sizeof (buff), "%d: some string", (*counter)++);
Second: you missed information we need to understand your question. I mean the following information:
How exactly you opened file?
How you started you processes?
Are this processes really processes or they are threads?
Did you open file in one process and somehow shared this opened file with other processes or did you opened the file in each process separately?
This details are critical for understanding your question.
Next time you will writing some question, please, provide FULL EXAMPLE. I. e. minimal working example we can compile and run. It should include all relevant details, i. e. starting processes, opening files, etc. And, of course, you should strip all unnecessary details.
Okey, there is two different notion in POSIX: "file descripTOR" and "file descripTION". Websearch for them. Type "man 2 open" in UNIX shell and read carefully, this manual page talks about distinction.
Exact details about how you started your processes and how you opened your file causes (or not causes) sharing of file description between processes and thus affects behavior of "write".
I wrote big text about file descriptors and descriptions. I put it here: https://zerobin.net/?eb2d99ee02f36b92#hQY7vTMCD9ekThAod+bmjlJgnlBxyDSXCYcgmjVSu2w= , because it is not much relevant to this question, but still will be useful for education.
Okey, what to do?
Well, if you for whatever reason cannot share ONE file DESCRIPTION, then simply open file with O_APPEND. :) You don't need to open the file every time you write to it. Simply open it with O_APPEND one time in each process and all will be OK.

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
}

Unable to create process inside a namedpipe in Windows 7

I have the following c code which I took from the first answer to this question, I am compiling it with C89 in VS2008 so I made a couple of changes for the code to work it properly, it compiles just fine, but it's not able to create process after creating the namedpipe successfully (CreateProcessA is not working) returning always error 2 and printing the message in the panic function.
The program that I am trying to run in the CreateProcessA can be downloaded here, and I normally run it and use it as follows:
> C:\qhichwa>cmd.exe /c "C:\qhichwa\flookup.exe -bx C:\qhichwa\qhichwa.fst"
wasi <user writes wasi>
wasi <program responds printing wasi>
hola <user writes hola>
+ ? <program responds printing + ?>
<pres ctrl + c to terminate program>
> C:\qhichwa>
The lines between < comments > are just comments.
what are the corrections needed in order to to create the named pipe successfully?
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
// name of our glorious pipe
#define PIPE_NAME L"\\\\.\\pipe\\whatever" // bloody unicode string
// exit on fatal error
void panic(const char * msg)
{
int err = GetLastError();
fprintf(stderr, "***PANIC*** %s\n", msg);
printf("In the child thread: Last Error is %lu\n", err);
exit(-1);
}
// father process
void father(const char * own_name) // name of our own executable to launch a copy of ourselve
{
printf("Father process starting\n");
// create a monodirectional father->child named pipe
HANDLE pipe = CreateNamedPipe(
PIPE_NAME, // name of the pipe
PIPE_ACCESS_OUTBOUND, // send only
PIPE_TYPE_BYTE, // send data as a byte stream
1, // only one instance
0, 0, 0, NULL); // default junk
if (pipe == INVALID_HANDLE_VALUE) panic("could not create pipe 1");
// spawn child process
{
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcessA( // using ASCII variant to be compatible with argv
"cmd.exe", // executable name (ourself)
"/c \"C:\\qhichwa\\flookup.exe -bx C:\\qhichwa\\qhichwa.fst\"", // command line. This will be seen as argv[0]
NULL, NULL, FALSE, // default junk
CREATE_NEW_CONSOLE, // launch in another console window
NULL, NULL, // more junk
&si, &pi)) // final useless junk
panic("could not create child process 2");
}
// connect to child process
BOOL result = ConnectNamedPipe(pipe, NULL);
if (!result) panic("could not connect to child process");
// talk to child
for (;;)
{
// read an input line
char line[100];
printf("Say something >");
if (fgets(line, sizeof(line), stdin) == NULL)
panic("could not read from standard input");
// exit on an empty line
if (!strcmp(line, "\n")) break;
// send the line to the child
DWORD written = 0;
if (!WriteFile(
pipe,
line, // sent data
strlen(line), // data length
&written, // bytes actually written
NULL))
panic("could not write to pipe");
}
// close the pipe
CloseHandle(pipe);
}
void child(void)
{
printf("Child process starting\n");
// retrieve communication pipe
HANDLE pipe = CreateFile(
PIPE_NAME, // name of the pipe
GENERIC_READ, // read ONLY access (or else the call will fail)
0, NULL, // junk
OPEN_EXISTING, // opens existing pipe
0, NULL); // more junk
if (pipe == INVALID_HANDLE_VALUE) panic("could not connect to the pipe");
// read father's input
for (;;)
{
char buffer[80];
DWORD read = 0;
if (!ReadFile(
pipe,
buffer, // read data
sizeof(buffer)-1, // max length (leave room for terminator)
&read, // bytes actually read
NULL))
break; // exit if the pipe has closed
// display what our father said
buffer[read] = '\0'; // make sure what we just read will be displayable as a string
printf("Father said: %s", buffer);
}
// close pipe
CloseHandle(pipe);
}
int main(int argc, char *argv[])
{
// wait for a <return> keypress on exit
atexit(getchar);
father(argv[0]);
// decide whether we are the father or the child
//if (!strcmp(argv[0], "child")) child();
//else father(argv[0]);
printf("Done\n");
return 0;
}
The problem is located here:
fprintf(stderr, "***PANIC*** %s\n", msg);
printf("In the child thread: Last Error is %lu\n", GetLastError());
This is a standard Windows programming bug, every programmer makes this mistake once. Just once, this is so hard to diagnose that you'll never forget losing the week of your life trying the discover it.
The underlying issue is the way GetLastError() works. It returns the value of an internal global variable, it is stored in the TEB (Thread Environment Block). It has the so common problem with global variables, every call you make to a winapi function is liable to change it, including ones that don't actually fail. Windows itself uses the variable as well. And ERROR_INVALID_NAME is a very popular internal error code.
This is compounded by you not being able to see these winapi calls being made. What is ruining the error code is fprintf(). It is implemented in the CRT with winapi calls. Necessarily so, I/O is an operating system duty.
So what is absolutely essential is that you immediately obtain the error code, before you do anything else. While it is preferable that you pass the value to your panic() function, since it cannot predict what other code runs before it, the quick fix is to rewrite it like this:
int err = GetLastError();
fprintf(stderr, "***PANIC*** %s\n", msg);
printf("In the child thread: Last Error is %lu\n", err);
You'll now get the true error code, the one produced by CreateNamedPipe(). Ought to give you a much better shot at diagnosing the problem. Update your question if you still have a problem interpreting it.

C Using popen to execute cmds on data

TL;DR How do you send a string to cmd.exe, execute grep/findstr or sort, and then print the output using popen?
I have a client and server setup. The user enters a command, such as 'list X: | grep name', which is then broken into 'list X:' and 'grep name'. 'list X:' is sent to the server which then executes a list function and returns a char[] containing the list result. The client then needs to feed this into cmd.exe and execute the 'grep name' command on that data and print it out to the user. I don't need to return the data from cmd to the client process, just output it.
I know my code isn't trying to write at the moment (I'm opening the file in read mode) but I had tried a few other things and can't seem to send the data to cmd.exe)
Code is as follows:
...
int n = recv(clientSocket, reply, sizeof(reply), 0);
std::string* cmdStr = new std::string(cmd);
cmdStr->erase(std::remove(cmdStr->begin(), cmdStr->end(), '\n'), cmdStr->end());
if(cmdStr->compare("") != 0) //Something to pipe to command
{
char pipeBuffer[8192];
FILE* pipe;
char c [8192];
strcat(c, "cmd.exe /C ");
strcat(c, reply);
strcat(c, " | ");
strcat(c, cmdStr->c_str());
if((pipe = _popen(c, "r")) == NULL)
{
printf("Unable to create pipe!\n");
}
while(!feof(pipe))
{
if(fgets(pipeBuffer, 8192, pipe) != NULL)
{
printf(pipeBuffer);
}
}
}
...
I ended up fixing it with this, just in case anyone else runs into a similar problem:
FILE* pipe;
if((pipe = _popen(cmdStr->c_str(), "w")) == NULL)
{
printf("Unable to create pipe!\n");
}
fprintf(pipe, "%s", reply);
printf("\nPipe process returned %d\n", _pclose(pipe));
well, if your application is already running as a console, you can use the function system() that executes a command on CMD, if not, you can simulate a console using AllocConsole() and then using system(), if you want to really emulate, you can use pipes

pipe not being read by subprocess?

I want to run node.js as a subprocess and feed it input. Using C, here is some sample code of mine that does that.
The issue I have is that although the subprocess's stdout is still directed to the terminal, I see nothing after having fed the subprocess stdin a print 'Hello World' line. Even if I fflush() the pipe, I see nothing on output. However, if I close the pipe's input, then the 'Hello World' appears on the terminal.
The subprocess seems to simply buffer - why is that?
I would like to eventually redirect the subprocess stdout to another pipe and read it
in from main().
int main(int argc, char* argv[]) {
int toNode[2];
pipe(toNode);
pid_t child_pid = fork();
if (child_pid == 0) { // child
// close write end
close(toNode[1]);
// connect read end to stdin
dup2(toNode[0], STDIN_FILENO);
// run node executable
char* arg_list[] = { "/usr/bin/node", NULL};
execvp(arg_list[0], arg_list);
fprintf(stderr, "process failed to start: %s\n", strerror(errno));
abort();
}
else { // parent
FILE* stream;
// close read end
close(toNode[0]);
// convert write fd to FILE object
stream = fdopen(toNode[1], "w");
fprintf(stream, "console.log('Hello World');\n");
fflush(stream);
//close(toNode[1]);
waitpid(child_pid, NULL, 0);
}
return 0; }
There's no problem with the pipe being read. The problem is that /usr/bin/node only invokes the REPL (read-eval-print loop), by default, if it detects that stdin is interactive. If you have a sufficiently recent version of nodejs, then you can provide the -i or --interactive command line flag, but that will do more than just execute each line as it is read; it also really will act as a console, including inserting ANSI colour sequences into the output and printing the value of each expression.
See this forum thread for more information.

Resources