Execve 没有按预期工作
Execve not working as expected
我正在用 c 编写一个基本的 shell,它将允许我执行简单的命令(我不需要检查可选参数),例如服务器(本地主机)中的 "ls"。该程序必须能够处理多个客户端。
我已经完成了必须使用 execve() 执行命令的部分(我必须使用此函数)。我发现 execve() returns -1 失败时它没有 return 任何成功所以我正在制作一个 fork() 来执行该过程中的命令。
现在,进入正题。我怎么知道 execve() 是否执行成功?我似乎找不到问题,我的代码总是 returns "OK" 到客户端。
"csapp.h" 它只是一个包含一些函数包装器的源文件。
#include "csapp.h"
void echo(int connfd, pid_t pid);
int main(int argc, char **argv)
{
int listenfd, connfd;
unsigned int clientlen;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp, *port;
pid_t pid;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(0);
}
port = argv[1];
listenfd = Open_listenfd(port);
while (1) {
clientlen = sizeof(clientaddr);
while(1){
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
if((pid=Fork())==-1){
Close(connfd);
}
if(pid > 0){
break;
}
}
/* Determine the domain name and IP address of the client */
hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
echo(connfd, pid);
Close(connfd);
}
exit(0);
}
void trim(char *string){
string[strlen(string)-1]=0;
}
char* concat(const char *s1, const char *s2)
{
char *result = malloc(strlen(s1) + strlen(s2) + 1); // +1 for the null-terminator
// in real code you would check for errors in malloc here
strcpy(result, s1);
strcat(result, s2);
return result;
}
void echo(int connfd, pid_t pid)
{
size_t n;
char buf[MAXLINE];
rio_t rio;
char *args[2];
args[1] = NULL;
Rio_readinitb(&rio, connfd);
while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
trim(buf);
args[0] = concat("/bin/", buf);
printf("server received %lu bytes\n", n);
printf("Command: %s\n",buf);
pid_t execPID;
int status;
if((execPID = fork()) > pid){
execve(args[0],args,NULL);
}else{
wait(&status);
if(WIFEXITED(status)){
if (WEXITSTATUS(status) == 0){
printf("status: %d\n", status);
printf("WIFEXITED: %d\n", WIFEXITED(status));
printf("WEXITSTATUS: %d\n", WEXITSTATUS(status));
Rio_writen(connfd, "OK\n", 3);
}
else
Rio_writen(connfd, "ERROR\n", 6);
}
}
/*if(status == -1){
Rio_writen(connfd, "ERROR\n", 6);
}
else{
Rio_writen(connfd, "OK\n", 3);
printf("%d\n", status);
}*/
}
}
客户端发送的 "m" 和 "ls" 的输出:
server received 2 bytes
Command: m
status: 0
WIFEXITED: 1
WEXITSTATUS: 0
server received 3 bytes
Command: ls
status: 0
WIFEXITED: 1
WEXITSTATUS: 0
Makefile client csapp.c csapp.o server.c
README.md client.c csapp.h server
非常感谢您的帮助,在过去的 14 个小时里我一直被困在这个问题上。
这一行:
{
execve(args[0],args,NULL);
}
应改为:
{
execve( args[0],args,NULL );
perror( "execve failed" );
exit( EXIT_FAILURE );
}
然后,如果对 execve()
的调用失败,代码将不会继续执行,如果失败,告诉您原因。
注:exit()
和EXIT_FAILURE
均来自头文件:stdlib.h
How do I know if execve() executed successfully?
首先,正如您已经观察到的,服务器知道 execve()
调用本身已经失败,如果 returns 根本没有。
其次,在 child 进程终止后,wait()
或 waitpid()
可以告诉您它的退出状态。你似乎也解决了这个问题。
所以当你继续说的时候,
I can't seem to find the problem, my code always returns "OK" to the client.
,这让我相信您的实际问题更像是 "how do I get the command's output?" 答案:通过 child 的标准输出和(可能)标准错误流。不然怎么办?
默认情况下,child 从其 parent 继承其标准输入、标准输出和标准错误,但您可以通过在 child 进程在 execve()
之前。您可以通过将 child 的流连接到 parent 或其他进程可以读取的 pipe()
来做各种相当复杂的事情,但最简单的事情是是使用 dup2()
简单地将 child 的输出和错误流定向到套接字:
// standard output:
if (dup2(connfd, 1) == -1) { /* handle error */ }
// standard error:
if (dup2(connfd, 2) == -1) { /* handle error */ }
然后命令的输出(如果有)将直接通过线路发送到客户端。
如@Stargateur said in the 下面的问题,我在execPID的比较上做错了。基本上,当它是父进程时 pid 为正,当它是子进程时为零,因此将 if ((execPID = fork()) > pid)
更改为 if ((execPID = fork()) == 0)
实际上可以解决问题。
我正在用 c 编写一个基本的 shell,它将允许我执行简单的命令(我不需要检查可选参数),例如服务器(本地主机)中的 "ls"。该程序必须能够处理多个客户端。
我已经完成了必须使用 execve() 执行命令的部分(我必须使用此函数)。我发现 execve() returns -1 失败时它没有 return 任何成功所以我正在制作一个 fork() 来执行该过程中的命令。
现在,进入正题。我怎么知道 execve() 是否执行成功?我似乎找不到问题,我的代码总是 returns "OK" 到客户端。 "csapp.h" 它只是一个包含一些函数包装器的源文件。
#include "csapp.h"
void echo(int connfd, pid_t pid);
int main(int argc, char **argv)
{
int listenfd, connfd;
unsigned int clientlen;
struct sockaddr_in clientaddr;
struct hostent *hp;
char *haddrp, *port;
pid_t pid;
if (argc != 2) {
fprintf(stderr, "usage: %s <port>\n", argv[0]);
exit(0);
}
port = argv[1];
listenfd = Open_listenfd(port);
while (1) {
clientlen = sizeof(clientaddr);
while(1){
connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
if((pid=Fork())==-1){
Close(connfd);
}
if(pid > 0){
break;
}
}
/* Determine the domain name and IP address of the client */
hp = Gethostbyaddr((const char *)&clientaddr.sin_addr.s_addr,
sizeof(clientaddr.sin_addr.s_addr), AF_INET);
haddrp = inet_ntoa(clientaddr.sin_addr);
printf("server connected to %s (%s)\n", hp->h_name, haddrp);
echo(connfd, pid);
Close(connfd);
}
exit(0);
}
void trim(char *string){
string[strlen(string)-1]=0;
}
char* concat(const char *s1, const char *s2)
{
char *result = malloc(strlen(s1) + strlen(s2) + 1); // +1 for the null-terminator
// in real code you would check for errors in malloc here
strcpy(result, s1);
strcat(result, s2);
return result;
}
void echo(int connfd, pid_t pid)
{
size_t n;
char buf[MAXLINE];
rio_t rio;
char *args[2];
args[1] = NULL;
Rio_readinitb(&rio, connfd);
while((n = Rio_readlineb(&rio, buf, MAXLINE)) != 0) {
trim(buf);
args[0] = concat("/bin/", buf);
printf("server received %lu bytes\n", n);
printf("Command: %s\n",buf);
pid_t execPID;
int status;
if((execPID = fork()) > pid){
execve(args[0],args,NULL);
}else{
wait(&status);
if(WIFEXITED(status)){
if (WEXITSTATUS(status) == 0){
printf("status: %d\n", status);
printf("WIFEXITED: %d\n", WIFEXITED(status));
printf("WEXITSTATUS: %d\n", WEXITSTATUS(status));
Rio_writen(connfd, "OK\n", 3);
}
else
Rio_writen(connfd, "ERROR\n", 6);
}
}
/*if(status == -1){
Rio_writen(connfd, "ERROR\n", 6);
}
else{
Rio_writen(connfd, "OK\n", 3);
printf("%d\n", status);
}*/
}
}
客户端发送的 "m" 和 "ls" 的输出:
server received 2 bytes
Command: m
status: 0
WIFEXITED: 1
WEXITSTATUS: 0
server received 3 bytes
Command: ls
status: 0
WIFEXITED: 1
WEXITSTATUS: 0
Makefile client csapp.c csapp.o server.c
README.md client.c csapp.h server
非常感谢您的帮助,在过去的 14 个小时里我一直被困在这个问题上。
这一行:
{
execve(args[0],args,NULL);
}
应改为:
{
execve( args[0],args,NULL );
perror( "execve failed" );
exit( EXIT_FAILURE );
}
然后,如果对 execve()
的调用失败,代码将不会继续执行,如果失败,告诉您原因。
注:exit()
和EXIT_FAILURE
均来自头文件:stdlib.h
How do I know if execve() executed successfully?
首先,正如您已经观察到的,服务器知道 execve()
调用本身已经失败,如果 returns 根本没有。
其次,在 child 进程终止后,wait()
或 waitpid()
可以告诉您它的退出状态。你似乎也解决了这个问题。
所以当你继续说的时候,
I can't seem to find the problem, my code always returns "OK" to the client.
,这让我相信您的实际问题更像是 "how do I get the command's output?" 答案:通过 child 的标准输出和(可能)标准错误流。不然怎么办?
默认情况下,child 从其 parent 继承其标准输入、标准输出和标准错误,但您可以通过在 child 进程在 execve()
之前。您可以通过将 child 的流连接到 parent 或其他进程可以读取的 pipe()
来做各种相当复杂的事情,但最简单的事情是是使用 dup2()
简单地将 child 的输出和错误流定向到套接字:
// standard output:
if (dup2(connfd, 1) == -1) { /* handle error */ }
// standard error:
if (dup2(connfd, 2) == -1) { /* handle error */ }
然后命令的输出(如果有)将直接通过线路发送到客户端。
如@Stargateur said in the if ((execPID = fork()) > pid)
更改为 if ((execPID = fork()) == 0)
实际上可以解决问题。