Why is argv[0] not consistent? - c

From what I understand, argv[0] is the program's path. However, we are doing an assignment and one of my friends gets the name of the first argument when invoking argv[0].
Why does this happen and how can I change this behaviour?
Edit: This is the parent process
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define SIZE 200
int main(int argc, char const *argv[])
{
char fileName[SIZE];
int bytesToRead;
int status;
bytesToRead = read(0, fileName, SIZE);
int p[2];
pipe(p);
pid_t pid;
if((pid = fork()) == -1) {
perror("error en el fork");
} else
if(pid == 0) {
close(p[0]);
dup2(STDOUT_FILENO, p[1]);
execl("./printTest", fileName, NULL);
close(p[1]);
exit(0);
}
waitpid(pid, &status, 0);
return 0;
}
And next is a child process:
#include <stdio.h>
#include <unistd.h>
#define SIZE 512
int main(int argc, char const *argv[])
{
printf("%s\n",argv[0]);
printf("Exec executed\n");
return 0;
}
From what we understand, argv[0] should hold the program's name, yet it's printing the first argument (whatever was input from stdin in the parent process)

argv[0] isn't necessarily the program's path. It is simply the first argument.
It just so happens that by convention, we use it for the program's name.
For your specific case, you need:
execl("./printTest", "./printTest", fileName, NULL);
Note that this means you always should check if argv[0] is defined before using it.

As per C standard argv[0] should contain the program name.

Related

How to execute a program by fork and exec

I have a binary file that contains a program with function written in C inside that looks like:
int main()
{
int a, b;
foo(a,b);
return 0;
}
And now I want to execute that program by using fork() and execve() in another program called "solver".
int main(int argc, char* argv[])
{
pid_t process;
process = fork();
if(process==0)
{
if(execve(argv[0], (char**)argv, NULL) == -1)
printf("The process could not be started\n");
}
return 0;
}
Is that a good way? Because it compiles, but I'm not sure whether the arguments of function inside "worker" program receive variables passed by command line to "solver" program
I believe you are trying to achieve something like that:
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
static char *sub_process_name = "./work";
int main(int argc, char *argv[])
{
pid_t process;
process = fork();
if (process < 0)
{
// fork() failed.
perror("fork");
return 2;
}
if (process == 0)
{
// sub-process
argv[0] = sub_process_name; // Just need to change where argv[0] points to.
execv(argv[0], argv);
perror("execv"); // Ne need to check execv() return value. If it returns, you know it failed.
return 2;
}
int status;
pid_t wait_result;
while ((wait_result = wait(&status)) != -1)
{
printf("Process %lu returned result: %d\n", (unsigned long) wait_result, status);
}
printf("All children have finished.\n");
return 0;
}
./work will be launched with the same arguments as your original program.

implementing pipeline using fork and pipe

I need to implement nameless pipes using fork for my OS class but I cant get it to work. Its a simple code and have nothing special in it but I just dont get anything. Im trying to run
ls -l | wc -l but I get 0 everytime.
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h> // for open flags
#include <time.h> // for time measurement
#include <assert.h>
#include <errno.h>
#include <string.h>
int pid,status;
int pipefd[2];
void my_exec(char* cmd, char** argv)
{
pipe(pipefd); // Fixed
pid = fork();
// pipe(pipefd); // Original
if(pid==0){
close(pipefd[0]);
dup2(pipefd[1],fileno(stdout));
close(pipefd[1]);
execvp(cmd, argv);
}
else {
close(pipefd[1]);
dup2(pipefd[0],fileno(stdin));
while(wait(&status)!=-1);
close(pipefd[0]);
return;
}
}
int main(int argc, char** argv)
{
assert(strcmp(argv[argc-1], "-"));
int i;
for (i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "-")) {
argv[i] = NULL;
my_exec(argv[1], &argv[1]);
argv = &argv[i];
argc -= i;
i = 0;
}
}
char* args[argc];
args[argc-1] = NULL;
for (i = 1; i < argc; ++i) {
args[i-1] = argv[i];
}
if (execvp(args[0], args) == -1)
perror("execvp failed");
return;
}
btw the input for the command Im trying is ls -l - wc -l (instead of | type -)
OK Duck solved it: i should create the pipe before the fork, updated.
Your biggest problem is that you have the fork in front of the pipe. This will effectively have each branch of the fork call pipe() and thus you'll end up with two different pipefd sets, not the same set. Reverse the calling order of the fork() and pipe() and then you're file descriptors in each fork will be the same.
As a side note, you can do a printf() for debugging inside each of the if() statement components to make sure you're never seeing more than two descriptor numbers total.

Two way pipe communication between parent and child

I'm trying to create two-way communication between parent and child processes using 2 pipes in C.the prog1 running in child1
I want to read 3+4+5 from prog1 after that send something to prog1 with write but I could not.
Where is the wrong?
/* prog1.c */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
void
main(void){
int FD;
unsigned int buf;
char buf[15];
printf("7+5+11=?\n");
FD=read(0,buf,10);
if(FD<0){
perror("FAIL\n");
exit(EXIT_FAILURE);
}
printf("TAKED:%s\n",buf);
}
prog2.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
void ERR_SYS(const char *msg);
int
main(void){
char buf[15];
int pipe1[2];
int pipe2[2];
pid_t childpid;
memset(buf,'\0',14);
if(pipe(pipe1) < 0 || pipe(pipe2) < 0)
ERR_SYS("fail_pipe");
if((childpid = fork()) < 0)
ERR_SYS("fail_fork");
if(childpid==0)
{
dup2(pipe2[1],1);
dup2(pipe1[0],0);
close(pipe1[1]);
close(pipe2[0]);
close(pipe2[1]);
close(pipe1[0]);
//close(1);
//close(0);
execle("./prog1",NULL,NULL,NULL);
}else{
close(pipe1[0]);
close(pipe2[1]);
read(pipe2[0],buf,4); /*I hope to read 3+4+5*/
printf("BuF::%s\n",buf);
write(pipe1[1],"off",3);/*send {off}*/
wait(NULL);
}
return 0;
}
void
ERR_SYS(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
There are few problems with your program:
You are not checking returned values of read, write and execle in prog2.c
You are sending "7+5+11=?\n" string which is 10 characters long but only expecting 4 characters ( 3+4+5 is not even four characters ).
Also "off" you are sending is 3 characters long but without including null termination.
When you read from an fd you will in both cases not get null terminated string and then you are trying to printf it. It's a quick way to undefined behaviour. Put an '\0' after the end of buffer you read from any file descriptor!
Especially what read returns is very important as it tells you how many characters were read. You should never ignore returned value of read (in some cases it's the same with write function).
Next time also provide some output of your program as it will be easier to give some help.
I didn't follow all your logic in setting up the pipes, so I modified and hopefully clarified your original. I should note that for whatever reason I named fd_in and fd_out from the external program's (prog1) point of view (e.g. fd_out is where prog1 is writing to, fd_in is where prog1 is reading from).
Here's the contents of my prog3.c:
...
#define READ_END 0
#define WRITE_END 1
void ERR_SYS(const char *msg);
int main(void) {
char buff[15];
char *msg = "hello";
int fd_out[2];
int fd_in[2];
int nbytes;
pid_t childpid;
if(pipe(fd_out) < 0 || pipe(fd_in) < 0) {
ERR_SYS("fail_pipe");
}
if((childpid = fork()) < 0) {
ERR_SYS("fail_fork");
}
if(childpid==0) { //child
//connect the write end of fd_out to stdout
dup2(fd_out[WRITE_END], STDOUT_FILENO);
close(fd_out[WRITE_END]);
//connect the read end of fd_in to stdin
dup2(fd_in[READ_END], STDIN_FILENO);
close(fd_in[READ_END]);
//the exec'd prog1 will inherit the streams
execlp("./prog1", "prog1", NULL); //TODO: check return
} else { //parent
nbytes = write(fd_in[WRITE_END], msg, strlen(msg));
//TODO: handle any errors from write
nbytes = read(fd_out[READ_END],buff,sizeof(buff)-1);
//TODO: handle any errors from read
buff[nbytes] = '\0';
printf("contents of buff::%s",buff);
}
return 0;
}
void ERR_SYS(const char *msg) {
perror(msg);
exit(EXIT_FAILURE);
}
And here's the contents of my prog1.c
int main(void){
char buff[15];
int nbytes;
nbytes = read(STDIN_FILENO, buff, sizeof(buff)-1);
buff[nbytes] = '\0';
printf("%s world\n", buff);
return 0;
}

execve won't run executable assembly file

I'm trying to create a c program that takes an executable and its arguments and runs them using execve, and then does some other stuff that shouldn't matter. The problem I'm having is that execve won't work when calling it on an exectuable assembly file. I think the problem is with my path because I can get the unix shell commands to work, but I can't get executables in the current directory (using ./spy ./executableName where spy is the name of my c program) to run. Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
extern char **environ;
int main(int argc, char* const argv[]) {
pid_t pid;
char filename[50];
char* arglist[argc];
int i = 1,count = 0;
int status;
strcpy(filename, "/bin/");
strcat(filename,argv[1]);
for(i = 1; i< argc; i++)
arglist[i-1] = argv[i];
arglist[argc-1] = 0;
arglist[0] = filename;
if (argc == 1) {
fprintf(stderr,"usage : %s <prog> ...\n",argv[0]);
return -1;
}
pid = fork();
if(pid == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
if(execve(filename, arglist, 0) < 0)
fprintf(stdout,"Invalid file.");
}
else {
while(1) {
waitpid(pid,&status,0);
if (WIFEXITED(status))
break;
ptrace(PTRACE_SINGLESTEP, pid,NULL, NULL);
count++;
}
}
return 0;
}
From the source you posted it looks as if you were always prefixing the name passed as parameter with /bin/. So if the file isn't in /bin/ it can not be found, nor run.
Just change these two lines:
strcpy(filename, "/bin/");
strcat(filename,argv[1]);
to be:
strcpy(filename,argv[1]);
Note that having applied this modification the program to be run needs to be specified with its full path.
So to run ls you need to do specfify /bin/ls as parameter to the program.
Some other comments:
So avoid buffer a overflow for long path/file names change:
char filename[50];
to be:
char filename[PATH_MAX];
To get more detailed information on why an execve() might have failed change:
if(execve(filename, arglist, 0) < 0)
fprintf(stdout,"Invalid file.");
to be:
if(execve(filename, arglist, (char*) 0) < 0)
perror("execve() failed");
To detect a possible failure of forking do change:
pid = fork();
to become:
if (-1 == (pid = fork())) then
{
perror("fork() failed");
}
else

C Unix Pipes Example

Trying to implement a shell, mainly piping. I've written this test case which I expect to simply pipe ls to wc...it definitely doesn't work as expected. It prints ls to the terminal then prints memory exhausted.
I'm very lost in how to fix this and get it to work. find_path works in all of my tests.
Edit - I have to use execv for the project, its a class thing, but I've tried it with execvp just in case and it does the exact same thing. Also this is just an example, a test to see why it does not work, I call fork twice once for both commands and waitpid because I have nothing else to do.
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int find_path(char* execname, char** dst)
{
char *path = getenv("PATH");
path = strdup(path);
char *pos;
path = strtok_r(path, ":", &pos);
char *originalpath = path;
do
{
char* test = (char*)calloc(strlen(path) + strlen(execname) + 2, sizeof(char));
test = strcpy(test, path);
int testlen = strlen(test);
(*(test+testlen)) = '/';
strcpy(test + testlen + 1,execname);
struct stat buf;
int result = stat(test, &buf);
if (result == 0)
{
*dst = test;
free (originalpath);
return 1;
}
else
{
free(test);
}
} while ((path = strtok_r(NULL, ":", &pos)) != NULL);
free(originalpath);
return 0;
}
int main()
{
char *cmd1 = "ls";
char *cmd2 = "wc";
int filedes[2];
pipe(filedes);
char** argv = (char**)calloc(1, sizeof(char*));
argv[0] = (char*)malloc(sizeof(char*));
argv[0] = NULL;
pid_t pid = fork();
if (pid == 0)
{
char *path;
find_path(cmd1, &path);
dup2(filedes[1],stdout);
execv(path,argv);
}
pid = fork();
if (pid == 0)
{
dup2(filedes[0], stdin);
char *path;
find_path(cmd2, &path);
execv(path, argv);
}
else
waitpid(pid);
}
Often when it is hard to debug a program, it is best to simplify it a little to eliminate sources of error. Here is your program, simplified to remove find_path as a source of errors:
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(void)
{
int filedes[2];
pipe(filedes);
/* Run LS. */
pid_t pid = fork();
if (pid == 0) {
/* Set stdout to the input side of the pipe, and run 'ls'. */
dup2(filedes[1], 1);
char *argv[] = {"ls", NULL};
execv("/bin/ls", argv);
} else {
/* Close the input side of the pipe, to prevent it staying open. */
close(filedes[1]);
}
/* Run WC. */
pid = fork();
if (pid == 0) {
dup2(filedes[0], 0);
char *argv[] = {"wc", NULL};
execv("/usr/bin/wc", argv);
}
/* Wait for WC to finish. */
waitpid(pid);
}
This should behave as you expect.
During simplification, a few errors came out:
argv[] wasn't being setup correctly, in particular, argv[0] was being set to NULL;
The program was not closing the input side of the pipe that was being given to ls. When ls finished, the pipe wasn't being closed (because the wc process still had it open), preventing wc from ever finishing.
The program was confusing the values stdout and stdin (which are of type FILE*) with the file descriptor numbers 0 and 1 (used by dup, pipe, etc.)
There is a lot you can do to improve this code (e.g. breaking this into smaller functions would be a start), but I suspect your out of memory issue is from the code in find_path(), which you could avoid entirely by using execvp which will locate the executable using the standard PATH mechanism for you. It is probably a good idea to install a signal handler using sigaction to handle SIGCHLD and invoke waitpid from the signal handler, instead of just invoking waitpid() ad-hoc like you are doing. You appear to be forking more times than you want, and you aren't checking for errors. Hope these suggestions help.

Resources