Seeking help with execv() or variant.

I can't figure out why my calls to execv() are failing in my child processes. I can hard code a C-string literal in place of my variable arguments and it works fine, but it doesn't like the variable version.

I haven't dealt with C-strings much so it may be some confusion on my part between various types (const char* pathname, char* const args[], char buffer[30]).

The command line argument is intended to be a text file that consists of various Linux executables, one per line such as:
/bin/pwd
/usr/bin/whoami
/bin/df
/bin/date

I appreciate any help with this!

My code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using std::cout;
using std::endl;
using std::ifstream;


int main(int argc, char* argv[]) {
	// check proper usage
	if (argc != 2) {
		cout << "Usage: " << argv[0] << " input_filename" << endl;
		exit(1);
	}

	// variables global to main()
	char fileName[30];
	ifstream fin;
	char buffer[30];
	pid_t pid;

	// open input file
	strncpy(fileName, argv[1], sizeof fileName);
	fin.open(fileName);
	if (fin.fail()) {
		cout << "Could not open " << fileName << endl;
		exit(2);
	}

	// for each line create process and replace core image
	cout << "********************* START *********************" << endl;
	fin.getline(buffer, sizeof buffer);
	while (!fin.eof()) {

		cout << "size[" << strlen(buffer) << "], command: " 
                    << buffer << endl;

		if ((pid = fork()) < 0) {
			cout << "Fork failed." << endl;
			exit(3);
		}
		else if (pid > 0) {
			waitpid(pid, NULL, 0);
			cout << endl;
		}
		else {
			char* const args[] = {buffer, (char*) 0};
			if (execv(buffer, args) == -1) {
				cout << "Error in execv()" << endl;
				exit(4);
			}
		}

		fin.getline(buffer, sizeof buffer);
	}

	cout << "*********************  END  *********************" << endl;
	fin.close();
	return 0;
}
If you include error.h and change:
 
cout << "Error in execv()" << endl;
to
 
cout << "Error in execv(): " << errno << ": " << strerror(errno)  << endl;
it'll tell you what's wrong.

Hint: You program does already work.
Interesting...thanks for the tip! So the issue is with the pathname argument to execv()

using the above input (I also added a line to output args[0] to the screen to verify it's contents) I get:

********************* START *********************
size[9], command: /bin/pwd
argv[0] will be: /bin/pwd
Error in execv(): 2: No such file or directory

size[16], command: /usr/bin/whoami
argv[0] will be: /usr/bin/whoami
Error in execv(): 2: No such file or directory

size[8], command: /bin/df
argv[0] will be: /bin/df
Error in execv(): 2: No such file or directory

size[10], command: /bin/date
argv[0] will be: /bin/date
Error in execv(): 2: No such file or directory

*********************  END  *********************


How is it not finding these? Are they not absolute pathnames? I can type them into the shell and it has no problem running the command... The documentation I've got in front of me for the exec() library functions seems to be telling me the argument format is correct.
Last edited on
I just ran your app (on MacOS X) with an input file containing:
/bin/pwd
/usr/bin/whoami
/bin/df
/usr/bin/bc

It seems ok to me.
Last edited on
Well, that is good to know. Thanks for your assistance with this problem.

I'm running the code above as-is except with the modified block you suggested utilizing errno and it's not performing the commands for me. I get the 'No such file or directory' error message shown in the output above.

Maybe it's something machine-specific? I'm running Ubuntu 12.10. I've got access to some remote machines running Fedora I can try this code on.
Seems fine on Linux too (running ArchLinux).

Assuming the input is prog.dat, try this:
 
for f in `cat prog.dat`; do $f; done


You should see the same output.
odd. does this shed any light on it?


james@lucy:~/eclipse_workspace/cs431_prog2$ for f in `cat launch.txt`; do $f; done
: No such file or directory
: No such file or directory
: No such file or directory
: No such file or directory
james@lucy:~/eclipse_workspace/cs431_prog2$ cat launch.txt
/bin/pwd
/usr/bin/whoami
/bin/df
/bin/date


changed the single quotes from ` to '

james@lucy:~/eclipse_workspace/cs431_prog2$ for f in 'cat launch.txt'; do $f; done
/bin/pwd
/usr/bin/whoami
/bin/df
/bin/date
That's worrying. It ought to run the programs but can't seem to find them.

What happens when you run:
 
for f in `cat prog.dat`; do which $f; done
or even
 
for f in `cat prog.dat`; do ls -l $f; done


Anyway, we're being distracted here. Both your app and the shell are happing the same problem when trying to do the same thing.
Last edited on
for f in 'cat launch.txt';
Sets f to the string "cat launch.txt".
$f;
Variable f expands to cat launch.txt.
do "cat launch.txt"
catinates the input of file launch.txt to stdout.

But your initial question: Your program is also running well on my
- MacOSX10.8.2/Darwin12.2.0 with bash 3.2.48(1)
- Ubuntu Linux 3.2.0-38-generic
Thanks for your help kbw & tcs
Turns out that .getline() was filling the buffer char array with the entire line of the input file including the cntrl character (newline, specifically). So for the first input line it had the strlen() of 9 with contents "/bin/pwd\n" and my system wasn't recognizing a program named "pwd\n" that execv() was passing to replace the child process' image.

Adding the following code to overwrite a cntrl char with the C-string null terminating char fixed my issue:

1
2
3
4
5
6
7
8
9
10
11
#include <cctype>

char buffer[30];
char* p = buffer;

fin.getline(buffer, sizeof buffer);


if (iscntrl(buffer[strlen(buffer) - 1])) {
        p[strlen(p) - 1] = '\0';
}
I think you've edited your test case file on MS-Windows and your test process is running on Linux? Then you got an extra carriage return character before the final newline.
Topic archived. No new replies allowed.