1

我想为GNUNet创建一个远程控制,所以我开始为 GNU OS 编写一个自制的多线程通用网络服务器,能够验证用户(从系统用户数据库读取)并能够执行通用 CGI 程序/脚本. 我从头开始,现在只是一个草稿。但是,一切似乎都运行良好。

我只有一个问题。

如您所知,CGI 程序/脚本从 STDIN 读取 POST 字符串并将其内容发送到 STDOUT。以下是我编写的代码(部分)。它似乎有效。

if (pipe(cgiPipe))
{
  perror("pipe");
}

cgiPid = fork();

if (cgiPid == 0)
{

  /* child */

  /* piping the POST content... */

  /* first, send the truncated part of the POST string contained within the request string... */
  if (nPOSTLength && (nSentChrs = write(cgiPipe[1], sPOSTSegment, 
      nReqLen + requestString - sPOSTSegment)) > 0)
  {
    nPOSTLength -= nSentChrs;

    /* after, read and send the rest of the POST string not received yet... */
    while (nPOSTLength > 0 && (nReadChrs = read(nRemote, reservedBuffer, 
        BUFFER_SIZE_PER_USER)) > 0 && (nSentChrs = write(cgiPipe[1], reservedBuffer, 
        nReadChrs)) > 0 && nReadChrs == nSentChrs)
    {
      nPOSTLength -= nReadChrs;
    }

    if (nReadChrs < 0)
    {
      printf("Error reading POST string.\n");
      goto closeThread;
    }
    if (nSentChrs < 0)
    {
      printf("Error sending POST string.\n");
      goto closeThread;
    }
  }
  else
  {
    write(cgiPipe[1], "(null)", 6);
  }

  close(cgiPipe[1]);

  /* redirecting the output of the pipe to the STDIN of the child process */
  dup2(cgiPipe[0], STDIN_FILENO);
  /* redirecting STDOUT of the child process to the remote client */
  dup2(nRemote, STDOUT_FILENO);
  setuid(nUserID);

  if (execve(sLocalPath, NULL, aCGIEnv))
  {
    /* unable to execute CGI... */
    perror("execve");
    sendString(nRemote,
        "HTTP/1.1 200 OK\r\n"
        "Content-length: 97\r\n"
        "Content-Type: text/html\r\n\r\n"
        "<!doctype html><html><head><title>CGI Error</title></head><body><h1>CGI Error.</h1></body></html>\r\n"
    );
  }

  goto closeThread;

}
else if (cgiPid > 0)
{

  /* parent */

  close(cgiPipe[0]);

  /* wait for child process. */
  if (waitpid(cgiPid, NULL, 0) == -1)
  {
    perror("wait");
  }

  goto closeThread;

}
else
{

  /* parent */

  perror("fork");

  /* let's try to send it as normal file, if the user has the right permissions... */

}

如您所见,执行 CGI 程序之前,从客户端接收整个 POST 字符串并通过管道传输(首先是请求字符串中包含的截断部分——通常是几个字节——然后是其余部分)。然后,执行 CGI 程序。

现在我的问题...</p>

如果我尝试上传几个 MB 的文件,在调用 CGI之前会通过管道传输几个 MB:有没有办法将套接字直接重定向到新进程的 STDIN,以便之前不读取它?但是,可以肯定的是,我必须在之前发送 POST 字符串的读取截断部分。所以,我可以用这种方式来概括我想做的事情:

  • 将字符串(几个字节)传输到 STDIN,然后
  • 将套接字(客户端)重定向到 STDIN,然后
  • 执行外部进程(CGI 程序)

可能吗?你能告诉我怎么做吗?

4

2 回答 2

1

解决了!!

我只需要将发送过程放在父级而不是子级中。通过这种方式,CGI 立即执行:

if (pipe(cgiPipe))
{
  perror("pipe");
}

cgiPid = fork();

if (cgiPid == 0)
{

  /* child */

  /* piping the POST content... */

  close(cgiPipe[1]);

  /* redirecting the output of the pipe to the STDIN of the child process */
  dup2(cgiPipe[0], STDIN_FILENO);
  /* redirecting STDOUT of the child process to the remote client */
  dup2(nRemote, STDOUT_FILENO);
  setuid(nUserID);

  if (execve(sLocalPath, NULL, aCGIEnv))
  {
    /* unable to execute CGI... */
    perror("execve");
    sendString(nRemote,
        "HTTP/1.1 200 OK\r\n"
        "Content-length: 97\r\n"
        "Content-Type: text/html\r\n\r\n"
        "<!doctype html><html><head><title>CGI Error</title></head><body><h1>CGI Error.</h1></body></html>\r\n"
    );
  }

  goto closeThread;

}
else if (cgiPid > 0)
{

  /* parent */

  close(cgiPipe[0]);

  /* first, send the truncated part of the POST string contained within the request string... */
  if (nPOSTLength && (nSentChrs = write(cgiPipe[1], sPOSTSegment, 
      nReqLen + requestString - sPOSTSegment)) > 0)
  {
    nPOSTLength -= nSentChrs;

    /* after, read and send the rest of the POST string not received yet... */
    while (nPOSTLength > 0 && (nReadChrs = read(nRemote, reservedBuffer, 
        BUFFER_SIZE_PER_USER)) > 0 && (nSentChrs = write(cgiPipe[1], reservedBuffer, 
        nReadChrs)) > 0 && nReadChrs == nSentChrs)
    {
      nPOSTLength -= nReadChrs;
    }

    if (nReadChrs < 0)
    {
      printf("Error reading POST string.\n");
      goto closeThread;
    }
    if (nSentChrs < 0)
    {
      printf("Error sending POST string.\n");
      goto closeThread;
    }
  }
  else
  {
    write(cgiPipe[1], "(null)", 6);
  }

  /* wait for child process. */
  if (waitpid(cgiPid, NULL, 0) == -1)
  {
    perror("wait");
  }

  goto closeThread;

}
else
{

  /* parent */

  perror("fork");

  /* let's try to send it as normal file, if the user has the right permissions... */

}

谢谢您的帮助!

而且……让我们希望尽早看到 GNUNet 的遥控器!:)

于 2013-12-14T23:37:34.083 回答
0

这可以通过使用dup2()将文件描述符替换STDIN_FILENO为打开的套接字来实现。然后,您还应该关闭子进程中的原始套接字:

dup2(socket_fd, STDIN_FILENO);
close(socket_fd); ; 
execve("cgi_process", args, env);

execve将另一个STDIN绑定到socket_fd.

于 2013-12-14T08:40:50.337 回答