I'm trying to elevate my program's privileges, to write a file to a system location. I am doing this in C on OSX, by forking a child process that uses authopen to create and write to a file.
I can create the file, however I'm having difficulty writing a string to it. From the man pages of authopen, I can use -w to direct stdin to the file if -stdoutpipe is not declared. I don't want to read from stdin, but I want to write a constant str to the file.
I find the description of -stdoutpipe confusing on the man pages, and there are no examples online on how to use this flag. Can anyone offer any advice how to accomplish this?
My code:
pid_t processId = fork();
if (processId == 0) {
//in child process
const char * authopenPath = "/usr/libexec/authopen";
//Create the file fromProg if it does not exist. This works OK.
execl(authopenPath,
authopenPath,
"-c",
"/etc/fromProg",
NULL);
//This is where I need help.
execl(authopenPath,
authopenPath,
"-stdoutpipe", //<- Not sure how to write a string to file using this
//-w -a", //<- Or this
"/etc/fromProg",
NULL);
exit(0);
}
Ok I got this to work so I'll answer my own question for others.
In a nutshell, the string should be sent by the parent process through a pipe, and the dup function conveniently duplicates the read end of the pipe to stdin.
Also, I found this reference on creating pipes very helpful.
int pip[2];
if (pipe(pip) != 0){
//error creating pipe
exit(1);
}
pid_t processId;
processId = fork();
if (processId == -1) {
//error creating fork
exit(1);
}
if (processId == 0) {
//in child process
//close write end of pipe
close(pip[1]);
//close stdin and duplicate the read end of pipe to stdin
close(0);
dup(pip[0]);
//test reading from stdin
//char buffer[35];
//read(STDIN_FILENO, buffer, 35);
//printf("Received string: %s", buffer);
const char * authopenPath = "/usr/libexec/authopen";
execl(authopenPath,
authopenPath,
"-c","-w","-a",
"/etc/fromProg",
NULL);
exit(0);
}
else {
//in parent process
//close read end of pipe
close(pip[0]);
//write to write end of pipe
char string[] = "Helloooo Pipe!\n";
write(pip[1], string, (strlen(string)+1));
}
Related
I am trying to pass the output of an excev call through a pipe read it and then do another execv call on it. Example, ls -l | more. I am unsure how to read the data that is coming through the pipe. I have tried to find a good answer on how to do this with other resources but all are using very basic examples with a fixed size string. Do I need to make reading the data dynamically allowing for more or less or can it be a fixed sized buffer?
else {
int pipefd[2];
pipe(pipefd);
int rc = fork();
if(rc == -1) {
error();
}
if (rc == 0) {
// close reading end in the child
close(pipefd[0]);
// send stdout to the pipe
if(dup2(pipefd[1], 1) == -1) {
error();
}
// send stderr to the pipe
if(dup2(pipefd[1], 2) == -1) {
error();
}
// this descriptor is no longer needed
close(pipefd[1]);
// example
// path: /bin/ls
// commands: ls -l
if (access(path, X_OK) == 0) {
execv(path, commands);
error();
}
free(path);
exit(1);
}
} else {
// parent process
wait(NULL);
// close the write end of the pipe in the parent
close(pipefd[1]);
// do I read here?
}
Expected results are to read them from the pipe and then to do another execv call on that data.
You need to develop a protocol between the two ends of the pipe enabling you to send a "SET_BUFF_SIZE" message between the two ends of the pipe.
or
Use a fixed size buffer with start and end of message character strings which will enable you to "chunk" your data into multiple reads if needed.
I create a function exec_in_child which takes the command arguments, pipe file descriptors (fds), read_flag and write_flag as input. When write_flag is set to 1, the child process should duplicate stdout to fds[1], and then execute the command. When read_flag is set to 1, the child should duplicate the stdin to fds[0] and the execute the command.
Do I have to close one end of the pipe when I'm reading/writing to
the other end?
The code below doesn't work. I'm trying to execute /bin/ls inside a child process, write the stdout to the pipe, and then read
it off in the parent process and print it. I'm not able to read in
the parent process.
Can I read and write to the pipe inside the same process without closing other? This situation arises when I want to child to read
from pipe, execute, and then write to the pipe.
#include <stdio.h> /* printf */
#include <stdlib.h>
#include <string.h> /* strlen, strcpy */
int exec_in_child(char *arguments[], const int temp[], int , int);
int main()
{
ssize_t bytes_read;
char *curr_dir = (char *)malloc(500);
int pipefd[2];
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
char *arguments[] = {"/bin/pwd",0};
exec_in_child(arguments, pipefd, 0, 1);
bytes_read = read(pipefd[0], curr_dir, strlen(curr_dir));
printf("%s = %d\n", "bytes read from pipe" ,(int)bytes_read);
printf("%s: %s\n","character read from the pipe",curr_dir);
return 0;
}
int exec_in_child(char * arguments[], const int fds[], int read_flag, int write_flag) {
pid_t pid;
pid = fork();
if (pid < 0) {
perror("Error: Fork Failed");
}
else if (pid == 0){ /*inside the child process */
if (read_flag == 1) {
dup2(fds[0], 0);
perror("Dup2 stdin");
}
if (write_flag == 1) {
dup2(fds[1], 1);
perror("Dup2 stdout");
}
execv(arguments[0], arguments);
perror("Error in child");
exit(1);
} /* if (pid == 0) */
else {
while(pid != wait(0));
} /* if(pid < 0) */
return 0;
}
I get this result:
hmwk1-skk2142(test) > ./a.out
Dup2 stdout: Success
bytes read from pipe = 0
character read from the pipe:
To answer your questions:
1) You do not need to close either end of the pipe in order to use the other end. However, you generally want to close any end(s) of the pipe you're not using. The biggest reason to do this is that the pipe will only close when all open write file descriptors are closed.
2) Your code isn't working because you're using strlen() improperly. This function calculates the length of a string by searching for the null (0) character. When you malloc() the storage for curr_dir you have no guarantee of what resides there (though it will usually be zeroed, as in this case).
Thus, your call strlen(curr_dir) returns zero, and the read() system call thinks you want to read up to zero bytes of data. Change your read call to the following:
bytes_read = read(pipefd[0], curr_dir, 500);
and your code will work perfectly.
3) You can read and write to any pipe you've got a valid file descriptor to. A single process can absolutely read and write the same pipe.
This is a homework assignment that has me stumped. I make two pipes, then two child processes to handle both sides of the pipe. The first child handles the first command and writes it to the first pipe, the second child handles the second command and writes it to the second pipe. However, when all is said and done, I read the contents from the second pipe and put it into a buffer and simply printf(buffer). Its at this step that my code is failing. I cannot read from the buffer. I have tested all my method calls such as getWordsBeforePipe() and I know they work. Do you guys see anything I am missing?
// Create the first pipe
pipeStatus = pipe(pfd1);
if (pipeStatus == -1) {
perror("pipe");
exit(1);
}
// create the first child
pid = fork();
if (pid == -1) {
printf("Bad first fork()...\n");
exit(1);
}
// Here we will run the first command inside of the first child.
if (pid == 0) {
printf("Im in the first child...\n");
getWordsBeforePipe(pipeLoc); // get the words before the pipe
close(pfd1[0]); // close read end because we arent reading anything
dup2(pfd1[1], 1); // copy to write-end of pfd instead of stdout
close(pfd1[1]); // close the write end
firstCommand = execve(pathFirst, beforePipeWords, environ);
perror("execve"); // we only get here if execve died
_exit(1);
}
// create the second pipe
pipeStatus = pipe(pfd2);
if (pipeStatus == -1) {
perror("pipe");
exit(1);
}
// create the second child
pid = fork();
if (pid == -1) {
printf("Bad second fork()...\n");
exit(1);
}
// Here we will run the second command and put its
// output into the second pipe
// first command business
if (pid == 0) {
printf("Im in the second child...\n");
getWordsAfterPipe(pipeLoc);
close(pfd1[1]); // close first child write end
dup2(pfd1[0], 0); // read from the pfd read end instead of stdin
close(pfd1[0]); // close the read end
// second command business
close(pfd2[0]); // close read end because we arent reading anything
dup2(pfd2[1], 1); // copy to write end of pfd instead of stdout
close(pfd2[1]);
secondCommand = execve(pathSecond, afterPipeWords, environ);
perror("execve"); // we only get here if execve died
_exit(1);
}
close(pfd1[0]);
close(pfd2[0]);
close(pfd2[1]);
// read from the second pipe and output the final value
readSuccess = read(pfd2[0], buffer, 256);
if (readSuccess < 0) {
printf("Failure reading the buffer...\n"); // I keep getting this error
exit(1);
}
if (readSuccess == 0) {
printf("Empty buffer...\n");
exit(1);
}
buffer[readSuccess] = '\0';
printf("%s", buffer);
The parent process is doing this:
close(pfd2[0]);
Followed by this:
readSuccess = read(pfd2[0], buffer, 256);
You can't read from a file descriptor after it's been closed.
You properly closed both ends of the pfd1 pair, since the two children read/write from them. The second child writes to pfd2[1], so the parent should be closing that instead of pfd2[0].
Check that the command specified by pathFirst writes to stdout, and that the command specified by pathSecond both reads from stdin and writes to stdout.
I am trying to run ls|wc using execvp. So I create a pipe and then fork to create a child. I close the appropriate(read./write) end in parent/child and then map the other end to stdout/stdin. Then I run the ls in parent using execvp and wc in child. When I run the program it says
wc:standard input:bad file descriptor.
0 0 0
wc: -:Bad file descriptor
Here is my code:
int main()
{
//int nbBytes = 0; //stream length
int pfd_1[2]; //file descriptor
//char buffer[MAX_FILE_LENGTH];
char* arg[MAX_FILE_LENGTH];
pid_t processPid;
//Create a pipe
if(pipe(pfd_1) == -1)
{
printf("Error in creating pipe");
return 0;
}
//Create a child
processPid = fork();
if(processPid == -1)
{
printf("Erro in fork");
exit(1);
}
else if(processPid == 0) //Child
{
//redirect read end file descriptor to standard input
dup2(pfd_1[0],0);
//Close the write end
if(close(pfd_1[1] == -1))
{
printf("Error in closing the write end file descriptor");
exit(1);
}
arg[0] = "wc";
//arg[1] = "-l";
arg[1] = '\0';
if(execvp(arg[0],arg) == -1)
{
printf("Error in executing ls");
}
}
else //Parent
{
//redirect standard output to the file descriptor
dup2(pfd_1[1],1);
//Close the read end
if(close(pfd_1[0] == -1))
{
printf("Error in closing the read end from parent");
exit(1);
}
//Command
arg[0] = "ls";
arg[1] = "/proc/1/status";
arg[2] = '\0';
if(execvp(arg[0],arg) == -1)
{
printf("Error in executing ls");
}
}
}
Any idea what might be wrong? Why would it consider standard input as bad file descriptor? My understanding was since the stdin and read end file descriptor are aliases so the wc -l would read whatever the output is from the parent process. Do I need to do scanf to read from the stdin?
The problem is in this line:
if(close(pfd_1[1] == -1))
You are closing the result of pfd_1[1] == -1, which is by necessity equal to 0 (as they will never be equal). The correct line would probably be:
if (close(pfd_1[1]) == -1)
Note that you do this again later in attempting to close the read end in the parent process.
If you're going to fork children, you have to call wait() in the parent process in order to avoid "zombie" child processes. So you don't want to overlay the parent process that did the original process forking with another executable via exec.
One quick way to setup a series of pipes in the way you want would be to fork a child for each executable you want to run, and read that data back into a buffer in the parent. Then feed that data from the first child into a new child process that the parent forks off. So each child is fed data from the parent, processes the data, and writes the data back to the parent process, which stores the transformed data in a buffer. That buffer is then fed to the next child, etc., etc. The final results of the data in the buffer are the final output of the pipe.
Here's a little pseudo-code:
//allocate buffer
unsigned char buffer[SIZE];
for (each executable to run in pipeline)
{
pipes[2];
pipe(pipes);
pid_t pid = fork();
if (pid == 0)
{
//setup the pipe in the child process
//call exec
}
else
{
//setup the pipe in the parent process
if (child executable is not the first in the pipeline)
{
//write contents of buffer to child process
}
//read from the pipe until the child exits
//store the results in buffer
//call wait, and maybe also check the return value to make sure the
//child returned successfully
wait(NULL);
//clean up the pipe
}
}
I have a task in Linux and I can't get it work.
I have a program that receives a text file as parameter. It then creates a child process using fork() and sends to the child process, line by line the content of the text file received as parameter. The child process needs to count the lines and return to the parent process the number of lines received.
This is what I have until now, but somewhat the child process does not receive all the lines. For my test I used a text file with 9 lines. The parent sent 9 lines as strings but the child process received only 2 or 3 of them.
What am I doing wrong?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char string[80];
char readbuffer[80];
int pid, p[2];
FILE *fp;
int i=0;
if(argc != 2)
{
printf("Syntax: %s [file_name]\n", argv[0]);
return 0;
}
fp = fopen(argv[1], "r");
if(!fp)
{
printf("Error: File '%s' does not exist.\n", argv[1]);
return 0;
}
if(pipe(p) == -1)
{
printf("Error: Creating pipe failed.\n");
exit(0);
}
// creates the child process
if((pid=fork()) == -1)
{
printf("Error: Child process could not be created.\n");
exit(0);
}
/* Main process */
if (pid)
{
// close the read
close(p[0]);
while(fgets(string,sizeof(string),fp) != NULL)
{
write(p[1], string, (strlen(string)+1));
printf("%s\n",string);
}
// close the write
close(p[1]);
wait(0);
}
// child process
else
{
// close the write
close(p[1]);
while(read(p[0],readbuffer, sizeof(readbuffer)) != 0)
{
printf("Received string: %s\n", readbuffer);
}
// close the read
close(p[0]);
}
fclose(fp);
}
A pipe is a unidirectional interprocess communication channel. You have to create 2 pipes, one to speak to the child process, the other to read data back.
Remember to close the unused side of the pipe on both processes.
You are sending the null terminator to the other process:
write(p[1], string, (strlen(string)+1));
That makes the result confusing because when you print what you've received, you only see up to the null terminator.
If you do this instead:
write(p[1], string, strlen(string));
you should get what you expect.
You're not counting the number of lines, you're counting the number of times read(2) returns.
When using pipes, read(2) will pull as much data as possible from the pipe: min(pipe_available, space_available). It doesn't care for newlines, 0 bytes etc. Simple tricks to make it work:
Use a loop to walk readbuffer and look for \n
Use fdopen + fgets (I have a feeling this is probably flawed)
look into manpage of pipe ( man 2 pipe ), the program you're trying to write is as an example there, compare it with yours :)