Unix Processes - compile and run c program - c

Create a parent process that gets from the command line n arguments arg1, arg2, ... , argn. arg1 is the name to a source C, arg2 is the name of the executable file results from compile arg1, and arg3, ... , argn are arguments to start.
The parent compiles arg1 and creates the executable arg2, after that runs it into a son process.
I tried to solve the problem, using some examples, but I didn't really understand them, so the program is not working. I really need some help...
#include<unistd.h>
#include<stdio.h>
#include<sys/wait.h>
#include<string.h>
int main(int argc, char* argv[]){
char com[200];
int p;
p=fork();
strcpy(com,"gcc -o prog.c");
strcat(com,argv[1]);
if(p==0){
if(WEXITSTATUS(system(com))==0)
execl("./prog.c","./prog.c",argv[3],argv[4],argv[5],NULL);
}
wait(0);
exit(0);
return 0;
}
The C program I want to use, reads some input data from two files and stores data into another file.

This code more or less does what you say your program should do. In particular, it uses argv[2] as the program name. It uses snprintf() to avoid overflows with long arguments (but doesn't verify that it didn't overrun). It prints various status messages — partly as a debugging aid, partly to give meaning to the various parts of the program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int p;
if (argc != 6)
{
fprintf(stderr, "Usage: %s source program file1 file2 file3\n", argv[0]);
return(1);
}
if ((p = fork()) == 0)
{
char com[200];
snprintf(com, sizeof(com), "gcc -o %s %s", argv[2], argv[1]);
if (system(com) == 0)
{
printf("Compilation of %s successful\n", argv[2]);
fflush(0);
execl(argv[2], argv[2], argv[3], argv[4], argv[5], (char *)NULL);
fprintf(stderr, "Failed to execute %s\n", argv[2]);
return(1);
}
fprintf(stderr, "Compilation of %s from %s failed\n", argv[2], argv[1]);
return(1);
}
int status;
wait(&status);
printf("Compilation and execution of %s yielded status %d\n",
argv[2], WEXITSTATUS(status));
return 0;
}
When this file is named gc.c and is compiled to make gc, it can be run as:
$ ./gc gc.c ./gc2 gc.c gc.c gc.c
Compilation of ./gc2 successful
Usage: ./gc2 source program file1 file2 file3
Compilation and execution of ./gc2 yielded status 1
$
The usage message from gc2 is correct; the program expects 6 arguments, not the 4 it is given by the program.

You should look into the manual of exec which will tell you how to run exec to fork another process that behaves according to the specification. This code can help you how to pass on variables to a child process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* for fork */
#include <sys/types.h> /* for pid_t */
#include <sys/wait.h> /* for wait */
int main()
{
/*Spawn a child to run the program.*/
pid_t pid=fork();
if (pid==0) { /* child process */
static char *argv[]={"echo","Foo is my name.",NULL};
execv("/bin/echo",argv);
exit(127); /* only if execv fails */
}
else { /* pid!=0; parent process */
waitpid(pid,0,0); /* wait for child to exit */
}
return 0;
}

Related

Redirection of stdin and stdout via pipes in C works for external programmes but not for recursive call

I am trying to communicate with forked child processes via pipe redirection of stdin and stdout in C. I already managed to get this to work for shell commands (like ls, for example) executed in child processes. However, I wasn't able to recursively execute the same program and redirect the output (printed by printf(), fprintf() to stdout, ...) via the pipes from the child process to the parent (in this test to stdout of the parent), although this works fine for ls or similar commands.
Here's how I tried to approach this:
I create a pipe, the reading end is for the parent, the child process should write to the writing end.
The Process forks, both processes close the unused end, respectively.
The writing end of the pipe is redirected to STDOUT_FILENO and closed
The child process executes the program recursively (it is called ./to2)
As mentioned, this does work if I execute ls in the child process, but not if I try to call the same program recursively. Here's my test program where I tried to get this to work:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <netdb.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <fcntl.h>
static void usage(void){
fprintf(stderr,"RIP");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]){
if(argc > 1){
dprintf(STDOUT_FILENO,"Please work\n");
printf("\n THIS IS A MESSAGE FROM THE CHILD \n");
fputs("Pretty Please!\n",stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if(-1 == pipe(p1)) {
fprintf(stderr,"pipe\n");
fprintf(stderr,"%s\n",strerror(errno));
usage();
}
int f = fork();
if(f == 0){
close(p1[0]);
if(dup2(p1[1],STDOUT_FILENO) < 0){
fprintf(stderr,"dup2\n");
usage();
}
close(p1[1]);
//I want this to work:
//execlp("./to2", "./to2", "-e");
//This works fine:
execlp("ls", "ls");
exit(EXIT_SUCCESS);
} else if (f == -1) {
usage();
} else {
close(p1[1]);
int w = -1;
if(-1 == wait(&w)) usage();
char b[12];
memset(b,0,12);
read(p1[0],&b,12);
char reading_buf[1];
while(read(p1[0], reading_buf, 1) > 0){
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
}
}
For testing purposes, the function is called recursively with additional arguments, while the parent program is called without additional arguments (hence the if(argc>1)).
In the final program, endless recursion is being avoided by other means.
Did I understand something wrongly? I am pretty confused by the fact that the only thing that doesn't seem to work is redirecting the output of my own
program...
Thank you very much in advance, any help or ideas are greatly appreciated.
The primary problem is precisely as outlined in the comments — you are not calling execlp() correctly (nor ls in the alternative). You must make the last argument on those function calls into an explicit null pointer, as shown in this code, which is a mostly mildly edited version of what's in the question:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
static void usage(void)
{
fprintf(stderr, "RIP\n");
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[])
{
if (argc > 1)
{
dprintf(STDOUT_FILENO, "Please work\n");
printf("THIS IS A MESSAGE FROM THE CHILD\n");
fputs("Pretty Please!\n", stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
}
int p1[2];
if (-1 == pipe(p1))
{
fprintf(stderr, "pipe: %s\n", strerror(errno));
usage();
}
int f = fork();
if (f == 0)
{
close(p1[0]);
if (dup2(p1[1], STDOUT_FILENO) < 0)
{
fprintf(stderr, "dup2: %s\n", strerror(errno));
usage();
}
close(p1[1]);
execlp(argv[0], argv[0], "-e", (char *)0);
fprintf(stderr, "failed to exec %s again\n", argv[0]);
exit(EXIT_FAILURE);
}
else if (f == -1)
{
usage();
}
else
{
close(p1[1]);
char b[13];
memset(b, 0, 13);
if (read(p1[0], &b, 12) < 0)
{
fprintf(stderr, "Failed to read from pipe (%s)\n", strerror(errno));
exit(EXIT_FAILURE);
}
int len = strcspn(b, "\n");
printf("M1 [%.*s]\n", len, b);
char reading_buf[1];
while (read(p1[0], reading_buf, 1) > 0)
{
write(1, reading_buf, STDOUT_FILENO);
}
close(p1[0]);
int w = -1;
if (-1 == wait(&w))
usage();
}
return 0;
}
Two important changes should be highlighted:
This code echoes the first line of data — the one written by dprintf() — whereas the original code just read it and discarded it.
The wait() call is after the input, not before. If the child had more data to write than a set of fixed messages, it could block waiting for the parent to read some of the data, while the parent is blocked waiting for the child to exit. This would be a deadlock.
The usage() function is not appropriately named — it doesn't report how to run the program. I also exit with a failure status, not success, if the child process fails the execlp().
Under peculiar circumstances, the wait() call might report on the exit status from some child other than the one that was forked. It is generally best to use a loop to reap such children. However, the circumstances required are extremely peculiar — the process which launched the parent with an exec*() function must have previously created some children for which it didn't wait, so that they are inherited by the parent process (because the PID doesn't change across an exec*() call).

fork exec wait exits with error code

I am writing own shell-like program and I keep getting errors on exec* function call.
Here is source code of core processes.c:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/uio.h>
#define BUFSIZE 128
#define EXIT_STR "exit"
int main(int argc, char ** argv) {
const char *prompt = "> ";
char buffer[BUFSIZE];
int bytes_read;
int status;
pid_t child_p;
while(1) {
printf("%s", prompt);
fflush(stdout);
bytes_read = read(0, buffer, BUFSIZE);
buffer[bytes_read-1] = '\0';
if(strncmp(EXIT_STR, buffer, bytes_read) == 0)
exit(0);
if((child_p = fork()) == 0) {
printf("[*] %d executing: %s\n", getpid(), buffer);
execlp(buffer, buffer);
printf("[*] %d got error on execlp\n", getpid());
exit(1);
} else {
waitpid(child_p, &status, 0);
printf("[*] child returned: %d\n", status);
}
}
}
I have also simple other.c program for testing:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv){
printf("Hello. I am %s with pid: %d\n", argv[0], getpid());
exit(0);
}
I use llvm on MacOS High Sierra for compilation:
$ llvm-gcc processes.c -o processes -Wall
$ ./processes
> other
[*] 6040 executing: other
[*] 6040 got error on execl
[*] child returned: 256
> ls
[*] 6041 executing: ls
[*] 6041 got error on execl
[*] child returned: 256
> exit
What am I missing?
Together, the second argument argument to execlp() and any subsequent arguments correspond to the strings provided to the new program's main() function in its argument vector. They must all be pointers to null-terminated C strings, except that the end of the list must be marked by a null pointer of type char *. For example:
execlp(buffer, buffer, (char *) NULL);
This is a documented requirement for the arguments to this function, and your program mail fail if you do not satisfy it. If you wish, you may rationalize it as providing a means for the system to count the elements of the argument vector, so as to pass that number to the new main(). You may also consider that the argument vector itself is documented to be terminated by a null pointer.

Attach process to new Terminal (Mac OS)

I write a program, which should create new process (I use fork(), and next in child process call execl()) and communicate with it. Here is my server:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
int main(int argc, char *argv[]) {
pid_t process;
process = fork();
if (process == 0) {
printf("The program will be executed %s...\n\n", argv[0]);
printf("Executing %s", argv[0]);
execl("hello", "Hello, World!", NULL);
return EXIT_SUCCESS;
}
else if (process < 0) {
fprintf (stderr, "Fork failed.\n");
return EXIT_FAILURE;
}
waitpid(process, NULL, NULL);
return 0;
}
And here is my client:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i=0;
printf("%s\n",argv[0]);
printf("The program was executed and got a string : ");
while(argv[++i] != NULL)
printf("%s ",argv[i]);
return 0;
}
The problem is the next: my client and server show output in the same terminal. I want them to show output in separate terminals. So, how can I do it?
You need to have two open terminals. The idea is to run your program in the first terminal and see the output of the client in the second terminal.
First, you need to know what is the ID of the second terminal. So in the second terminal do:
$ tty
/dev/pts/1
(note your output will be probably different because mine is a SSH connection and hence pts, yours will be /dev/tty)
And then in your child process, you tell it to use this other terminal for its output. Like this:
#include <stdio.h>
#include <fcntl.h>
int main(int argc, char *argv[]) {
int fd = open("/dev/pts/1",O_RDWR) ; // note that in your case you need to update this based on your terminal name
// duplicate the fd and overwrite the stdout value
if (fd < 0){
perror("could not open fd");
exit(0);
}
if (dup2(fd, 0) < 0 ){
perror("dup2 on stdin failed");
exit(0);
}
if (dup2(fd, 1) < 0 ){
perror("dup2 on stdout failed");
exit(0);
}
// from now on all your outputs are directed to the other terminal.
// and inputs are also come from other terminal.
}

using the command gcc with execlp

I have a problem while I use gcc with execlp.
Here is the result of the execution
root#ubuntu:~/sys/TP# ./sys
shoum.c: fatal error: no input files
compilation terminated.
done2
My code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* for fork */
#include <sys/types.h> /* for pid_t */
#include <sys/wait.h> /* for wait */
int main(int argc,char** argv)
{
/*Spawn a child to run the program.*/
pid_t pid=fork();
if (pid==0)
{ /* child process */
execlp("gcc","shoum.c",NULL);
// execlp("ls","-liha",NULL);
printf("not working\n");
exit(127); /* only if execv fails */
}
else
{ /* pid!=0; parent process */
waitpid(pid,0,0); /* wait for child to exit */
printf("done2 \n");
}
return 0;
}
PS: When I use other commands like ls or cat the execlp works but it doesn't with gcc.
You're missing an argument, it should be:
execlp("gcc", "gcc", "shoum.c", (char*)NULL);
The first argument is the program to run, the remaining arguments are the argv[] array for the program. You were missing argv[0], which contains the name of the program being run. So gcc thought it was being run with the name shoum.c and no filename parameters.

how to pass command line arguments to a child process after fork()

I have the following code draft.
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
printf( "usage: %i filename", argc );
pid_t pID = fork();
if (pID == 0) // child
{
// Code only executed by child process
printf("Child PID: %i", pID);
int file = open("/tmp/rtail", O_CREAT | O_WRONLY);
//Now we redirect standard output to the file using dup2
dup2(file,1);
char tmp[30];
sprintf(tmp, "cat `tail -f %s`", argv[1]);
}
else if (pID < 0) // failed to fork
{
printf("Failed to fork");
exit(1);
// Throw exception
}
else // parent
{
}
// Code executed by both parent and child.
return 0;
}
How do I pass command line arguments to a child process? For example, running ./app alch.txt I want
sprintf(tmp, "cat `tail -f %s`", argv[1]);
to produce
cat `tail -f alch.txt`
in tmp.
How do I pass command line arguments to a child process?
You don't need to do anything special; fork ensures that each process gets all local variables, including argv.
Sorry for the trouble, it indeed works fine. My earlier version didn't work for some reason, but apparently I've changed something to make it right. Will run my code before a question next time.

Resources