how do I get system() output

closed account (236Rko23)
well hi, i'm trying to get the output from X command, "ps -e" in this case.
I read something about using pipe(), fork() and others but i couldn't get it to work.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
char* GetSystemOutput(char* cmd){
	
	char* tmp = new char [strlen(cmd) + strlen(" > /tmp/Ab1Cd2Ed")];
	memset (tmp, 0, strlen(cmd) + strlen(" > /tmp/Ab1Cd2Ed"));
	
	strcpy (tmp, cmd);
	strcat (tmp, (const char*)&" > /tmp/Ab1Cd2Ed");
	system (tmp);
	
	ifstream asd ("/tmp/Ab1Cd2Ed");
	
	int tam = GetFileSize(&asd);
	
	char* out = new char [tam];
	memset (out, 0, tam);
	
	asd.read(out, tam);
	
	delete [] tmp; asd.close();
	
	return out;
}


i made this, but it's a very ugly way of doing this, and i don't like using "> file" and the reading it.

thanks in advance
Well, the usual approach is to use pipe(), fork(), and exec().

step 1. create a pipe
step 2. fork
step 3. in the parent, close the write side of the pipe
step 4. in the child, close the read side of the pipe
step 5. in the child, close stdout
step 6. in the child, use dup2 on the write side of the pipe on fd 1 (stdout)
step 7. in the child, exec() the process you want to capture stdout from

closed account (236Rko23)
thanks, i've been able to read the output but i'm leaving work now, so tomorrow i'll post the finished function :P
closed account (236Rko23)
finished function :D

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
char* GetSystemOutput(char* cmd){
	int buff_size = 32;
	char buff[buff_size+1]; memset((char*)&buff, 0, buff_size+1);
	
	char* ret = NULL;
	string str = "";
	
    int fd[2];
    int old_fd[3];
    pipe(fd);
	
	
	old_fd[0] = !STDIN_FILENO;
	old_fd[1] = !STDOUT_FILENO;
	old_fd[2] = !STDERR_FILENO;
	
	old_fd[0] = dup(STDIN_FILENO);
	old_fd[1] = dup(STDOUT_FILENO);
	old_fd[2] = dup(STDERR_FILENO);
	
	int pid = fork();
	switch(pid){
		case 0:
			close(fd[0]);
			close(STDOUT_FILENO);
			close(STDERR_FILENO);
			dup2(fd[1], STDOUT_FILENO);
			dup2(fd[1], STDERR_FILENO);
			system(cmd);
			close (fd[1]);
			exit(1);
			break;
		case -1:
			cerr << "GetSystemOutput/fork() error\n" << endl;
			exit(1);
		default:
			close(fd[1]);
			dup2(fd[0], STDIN_FILENO);
			
			while (read(fd[0], buff, buff_size)){
				str.append(buff);
				memset(buff, 0, buff_size);
			}
			
			ret = new_char (strlen((char*)str.c_str()));
			
			strcpy(ret, (char*)str.c_str());
			
			waitpid(pid, NULL, 0);
			close(fd[0]);
	}
	
	dup2(STDIN_FILENO, old_fd[0]);
	dup2(STDOUT_FILENO, old_fd[1]);
	dup2(STDERR_FILENO, old_fd[2]);
	
    return ret;
}
Line 3 is not standards compliant (the array declaration). You are taking advantage of a compiler extension
to the language to allow this code to compile. Array sizes must be compile-time constant by the standard.

Lines 13, 14, and 15 are completely unnecessary.

You _really_ should use one of the exec() family of functions instead of system(), in which case
lines 30, 31, and 32 are useless (just fall through to line 34 or 35).

Line 41 is a potential segfault since you are not guaranteed that read() will return a NULL-terminated
string, even with the memset(). You need to use the return value from read().


Something about the design of this function seems wrong if lines 53 through 55 are actually
needed to restore state. You should not need to modify the file descriptor state of the parent.

closed account (236Rko23)
how about now?
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
char* GetSystemOutput(char* cmd){
	int buff_size = 32;
    char* buff = new_char(buff_size);
	
	char* ret = NULL;
	string str = "";
	
    int fd[2];
    int old_fd[3];
    pipe(fd);
	
	
	old_fd[0] = dup(STDIN_FILENO);
	old_fd[1] = dup(STDOUT_FILENO);
	old_fd[2] = dup(STDERR_FILENO);
	
	int pid = fork();
	switch(pid){
		case 0:
			close(fd[0]);
			close(STDOUT_FILENO);
			close(STDERR_FILENO);
			dup2(fd[1], STDOUT_FILENO);
			dup2(fd[1], STDERR_FILENO);
			system(cmd);
			//execlp((const char*)cmd, cmd,0);
			close (fd[1]);
			exit(0);
			break;
		case -1:
			cerr << "GetSystemOutput/fork() error\n" << endl;
			exit(1);
		default:
			close(fd[1]);
			dup2(fd[0], STDIN_FILENO);
			
			int rc = 1;
			while (rc > 0){
				rc = read(fd[0], buff, buff_size);
				str.append(buff, rc);
				//memset(buff, 0, buff_size);
			}
			
			ret = new_char (strlen((char*)str.c_str()));
			
			strcpy(ret, (char*)str.c_str());
			
			waitpid(pid, NULL, 0);
			close(fd[0]);
	}
	
	dup2(STDIN_FILENO, old_fd[0]);
	dup2(STDOUT_FILENO, old_fd[1]);
	dup2(STDERR_FILENO, old_fd[2]);
	
    return ret;
}


i tried using execl/p, execv/p but neither of them returned anything, or in the case of execl it returned some random piece of memory instead of stdout.... x.x
exec() has to work since that is how shells run programs. You must have been doing something wrong.
execlp(), as with all other exec() family functions, return only if the program was not exec'ed at all,
otherwise it never returns.

Your loop on lines 38-42 is not signal safe, if your program uses signals. Specifically, you have to check
for -1 and errno == EINTR. This would actually be a segfault in your existing program since you'll
end up attempting to append 4GB worth of data to the string (-1 in unsigned is max unsigned int).

Line 44 and 46 assume that the data read from the child process is NULL terminated, which is not
necessarily the case.

You did not address my last comment. You should not need to 'save off' stdin/stdout/stderr of the parent,
because you should never be modifying them. For example, file descriptor 100 in the parent should be
the read end of the pipe. That is the only file descriptor the parent needs. In the child process, you
close 0, 1, and 2, and dup2 the write end of the pipe to fds 1 and 2.
closed account (236Rko23)
well thanks for the help and sorry for the lond delay. I tryed to address all you said but i had some problems understanding so I hope I haven't miss anything u.u

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
char* GetSystemOutput(char* cmd, char* arg, ...){

	int buff_size = 32;
    char* buff = new_char(buff_size);
	char* ret = NULL;
	string str = "";
	
    int fd[2]; pipe(fd);
	
	
	int cmdline_size = 512;
	int cmdline_cnt = 0;
	char** cmdline = new char* [cmdline_size+1]; 
	memset (cmdline,0x0,(cmdline_size+1)*4);
	
	va_list va;
	va_start (va, arg);
	
	cmdline[0] = arg;
	
	for (int i = 1; i < cmdline_size && cmdline[0] != NULL;i++){
		cmdline[i] = va_arg(va,char*);
		if (cmdline[i] == NULL){
			cmdline_cnt = i;
			break;}
	}
	
	int pid = fork();
	switch(pid){
		case 0:
			close(fd[0]);
			close(STDOUT_FILENO);
			dup2(fd[1], STDOUT_FILENO);
			execvp(cmd, cmdline);
			cout << "error, exec returned\n";
			//system("cmd");
			exit(0);
			
			break;
		case -1:
			cerr << "GetSystemOutput/fork() error\n" << endl;
			exit(1);
		default:
			close(fd[1]);
			
			for (int rc = 1; rc > 0;){
				rc = read(fd[0], buff, buff_size);
				if (errno || rc < 0){printf("Error, errno = %i / rc = %i\n", errno, rc); exit (errno);}
				str.append(buff, rc);
			}
			
			ret = new_char (strlen((char*)str.c_str()));
			
			strcpy(ret, (char*)str.c_str());
			
			waitpid(pid, NULL, 0);
			close(fd[0]);
	}
	va_end(va);
    return ret;
}


the problem is that even though i used exelp and come commands worked, some others did not...

1
2
3
4
5
6
char* aaa = GetSystemOutput("ps", "-e",  NULL);
cout << aaa << endl; delete [] aaa; // works
aaa = GetSystemOutput("echo", "/proc/cpuinfo", NULL);
cout << aaa << endl; delete [] aaa; // returns "\n"
aaa = GetSystemOutput("uname", NULL);
cout << aaa << endl; delete [] aaa; // ditto 


the thing is that I need the output of a command, not of an applitacion. I guess the last two don't work because bash uses the built in ones instead of those in the /bin folder (yes i know it doesn't make sense as execvp doesn't return but i can't think of anything else x.x)
Last edited on
Have you tried using popen(3C)? Here is a working example (on Solaris anyway): NOTE - my tab stops are set to 4 characters.
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/*

	@(#)    (c) 2010 C-Squared Computing - C2C

	@(#)    Created:    Fri Sep 10 16:05:51 EDT 2010

	@(#)    Description:    This is an example program that tests the
	@(#)	popen(3C) call.

	@(#)    History:
	@(#)        KEC     10 Apr 2010 - Creation.


*/

#include <iostream>
#include <string>

using namespace std;


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static const string
	OptcDefault = "ps -ef",
	OptdDefault = "0",
	OptiDefault = "standard in",
	Options = "c:d:hi:o:",
	OptoDefault = "standard out",
	Program = "TestPOpen";

static int Debug = 0;

void Help()
	{
	cout << "Usage: " << Program << " " <<
		"[ -c command ] [ -d debug level ] [ -h ] " << endl <<
		"    [ -i input file ] [ -o output file ]" <<
		endl;

	cout << endl << "where (defaults if any are in parentheses):" << endl;

	cout << "    -c specifies a command to run " <<
		"(" << OptcDefault << ")," << endl;

	cout << "    -d specifies the debug level " <<
		"(" << OptdDefault << ")," << endl;

	cout << "    -h displays this help and exits," << endl;

	cout << "    -i specifies an input file " <<
		"(" << OptiDefault << ")," << endl;

	cout << "    -o specifies an output file " <<
		"(" << OptoDefault << ")," << endl;

	}
		/*	Help()	*/


void RunTest( const string &Command, const string &InputFileName,
	const string &OutputFileName );


int main( int argc, char **argv )
	{
	int
		bContinue = true,
		bProcessingCommandLine = true,
		c = 0;


	string
		Command = "",
		InputFileName = "",
		OutputFileName = "";


	while( bProcessingCommandLine )
		{
		c = getopt( argc, argv, Options.data() );

		switch( c )
			{
			case EOF:
				bProcessingCommandLine = false;

				break;

			case 'c':
				Command = optarg;

				break;

			case 'd':
				Debug = atoi( optarg );

				break;

			case 'h':
				Help();

				exit( 0 );

				break;

			case 'i':
				InputFileName = optarg;

				break;

			case 'o':
				OutputFileName = optarg;

				break;

			default:
				break;

			}	/*	switch( c )	*/

		}	/*	while( bProcessingCommandLine )	*/

	if( bContinue )
		{
		if( Command.empty() )
			Command = OptcDefault;

		RunTest( Command, InputFileName, OutputFileName );

		}	/*	if( bContinue )	*/

	}
		/*	main()	*/


void RunTest( const string &Command, const string &InputFileName,
	const string &OutputFileName )
	{
	FILE *fd = NULL;

	char Buffer[ BUFSIZ ];

	size_t pos = string::npos;

	string InputLine = "";


	fd = popen( Command.data(), "r" );

	if( NULL != fd )
		{
		while( fgets( Buffer, sizeof( Buffer ), fd ) )
			{
			InputLine = Buffer;

			cout << InputLine << endl;

			}	/*	while( fgets() )	*/

		pclose( fd );

		fd = NULL;

		}	/*	if( NULL != fd )	*/
	else
		{
		cerr << "Error opening " << Command << endl;

		}	/*	if( NULL == fd )	*/

	}
		/*	RunTest()	*/
Last edited on
Topic archived. No new replies allowed.