修复 gspawn-win32-helper 获取参数失败

使用 MinGW 中的 GCC 4.8.1 编译 GLib 2.38.1,执行 valac 编译会报如下错误:

ERROR:gspawn-win32-helper.c:226:WinMain: assertion failed: (__argc >=
ARG_COUNT)

阅读代码 glib/gspawn-win32-helper.c 发现此程序依赖于 __argc 和 __argv 全局变量进行参数访问,但有相关说明这两个全局变量是由 Microsoft 编译和 msvcrt.dll 提供,调试发现使用 MinGW 编译的 gspawn-win32-helper.exe 无论是否在命令行下进行都会导致上述错误,而且打印发现 __argc 永远是 0。

查看 /mingw/include/stdlib.h 会发现还有一组访问参数相关的全局变量 _argc 和 _argv,验证后是可行的。解决问题,下面是补丁文件:

From ebe6e6989b49bd119acc2d29b2f8ad0b6bb03313 Mon Sep 17 00:00:00 2001
From: Heiher <root@heiher.info>
Date: Wed, 30 Oct 2013 22:17:29 +0800
Subject: [PATCH] glib/gspawn-win32-helper.c: Fix get arguments for GCC
 compiler.
 
---
 glib/gspawn-win32-helper.c | 61 ++++++++++++++++++++++++++--------------------
 1 file changed, 34 insertions(+), 27 deletions(-)
 
diff --git a/glib/gspawn-win32-helper.c b/glib/gspawn-win32-helper.c
index 7896941..8d2bcbd 100644
--- a/glib/gspawn-win32-helper.c
+++ b/glib/gspawn-win32-helper.c
@@ -222,28 +222,35 @@ main (int ignored_argc, char **ignored_argv)
   _CrtSetReportMode(_CRT_ASSERT, 0);
 #endif
 
+#ifdef _MSC_VER // for Microsoft compiler
+#define __ARGC __argc
+#define __ARGV __argv
+#else           // for GCC compiler (MinGW)
+#define __ARGC _argc
+#define __ARGV _argv
+#endif
 
-  g_assert (__argc >= ARG_COUNT);
+  g_assert (__ARGC >= ARG_COUNT);
 
   /* Fetch the wide-char argument vector */
   __wgetmainargs (&argc, &wargv, &wenvp, 0, &si);
 
-  /* We still have the system codepage args in __argv. We can look
+  /* We still have the system codepage args in __ARGV. We can look
    * at the first args in which gspawn-win32.c passes us flags and
-   * fd numbers in __argv, as we know those are just ASCII anyway.
+   * fd numbers in __ARGV, as we know those are just ASCII anyway.
    */
-  g_assert (argc == __argc);
+  g_assert (argc == __ARGC);
 
   /* argv[ARG_CHILD_ERR_REPORT] is the file descriptor number onto
    * which write error messages.
    */
-  child_err_report_fd = atoi (__argv[ARG_CHILD_ERR_REPORT]);
+  child_err_report_fd = atoi (__ARGV[ARG_CHILD_ERR_REPORT]);
 
   /* Hack to implement G_SPAWN_FILE_AND_ARGV_ZERO. If
    * argv[ARG_CHILD_ERR_REPORT] is suffixed with a '#' it means we get
    * the program to run and its argv[0] separately.
    */
-  if (__argv[ARG_CHILD_ERR_REPORT][strlen (__argv[ARG_CHILD_ERR_REPORT]) - 1] == '#')
+  if (__ARGV[ARG_CHILD_ERR_REPORT][strlen (__ARGV[ARG_CHILD_ERR_REPORT]) - 1] == '#')
     argv_zero_offset++;
 
   /* argv[ARG_HELPER_SYNC] is the file descriptor number we read a
@@ -252,16 +259,16 @@ main (int ignored_argc, char **ignored_argv)
    * duplicate the process handle we sent it. Duplicating a handle
    * from another process works only if that other process exists.
    */
-  helper_sync_fd = atoi (__argv[ARG_HELPER_SYNC]);
+  helper_sync_fd = atoi (__ARGV[ARG_HELPER_SYNC]);
 
   /* argv[ARG_STDIN..ARG_STDERR] are the file descriptor numbers that
    * should be dup2'd to 0, 1 and 2. '-' if the corresponding fd
    * should be left alone, and 'z' if it should be connected to the
    * bit bucket NUL:.
    */
-  if (__argv[ARG_STDIN][0] == '-')
+  if (__ARGV[ARG_STDIN][0] == '-')
     ; /* Nothing */
-  else if (__argv[ARG_STDIN][0] == 'z')
+  else if (__ARGV[ARG_STDIN][0] == 'z')
     {
       fd = open ("NUL:", O_RDONLY);
       if (fd != 0)
@@ -272,7 +279,7 @@ main (int ignored_argc, char **ignored_argv)
     }
   else
     {
-      fd = atoi (__argv[ARG_STDIN]);
+      fd = atoi (__ARGV[ARG_STDIN]);
       if (fd != 0)
 	{
 	  dup2 (fd, 0);
@@ -280,9 +287,9 @@ main (int ignored_argc, char **ignored_argv)
 	}
     }
 
-  if (__argv[ARG_STDOUT][0] == '-')
+  if (__ARGV[ARG_STDOUT][0] == '-')
     ; /* Nothing */
-  else if (__argv[ARG_STDOUT][0] == 'z')
+  else if (__ARGV[ARG_STDOUT][0] == 'z')
     {
       fd = open ("NUL:", O_WRONLY);
       if (fd != 1)
@@ -293,7 +300,7 @@ main (int ignored_argc, char **ignored_argv)
     }
   else
     {
-      fd = atoi (__argv[ARG_STDOUT]);
+      fd = atoi (__ARGV[ARG_STDOUT]);
       if (fd != 1)
 	{
 	  dup2 (fd, 1);
@@ -301,9 +308,9 @@ main (int ignored_argc, char **ignored_argv)
 	}
     }
 
-  if (__argv[ARG_STDERR][0] == '-')
+  if (__ARGV[ARG_STDERR][0] == '-')
     ; /* Nothing */
-  else if (__argv[ARG_STDERR][0] == 'z')
+  else if (__ARGV[ARG_STDERR][0] == 'z')
     {
       fd = open ("NUL:", O_WRONLY);
       if (fd != 2)
@@ -314,7 +321,7 @@ main (int ignored_argc, char **ignored_argv)
     }
   else
     {
-      fd = atoi (__argv[ARG_STDERR]);
+      fd = atoi (__ARGV[ARG_STDERR]);
       if (fd != 2)
 	{
 	  dup2 (fd, 2);
@@ -322,19 +329,19 @@ main (int ignored_argc, char **ignored_argv)
 	}
     }
 
-  /* __argv[ARG_WORKING_DIRECTORY] is the directory in which to run the
+  /* __ARGV[ARG_WORKING_DIRECTORY] is the directory in which to run the
    * process.  If "-", don't change directory.
    */
-  if (__argv[ARG_WORKING_DIRECTORY][0] == '-' &&
-      __argv[ARG_WORKING_DIRECTORY][1] == 0)
+  if (__ARGV[ARG_WORKING_DIRECTORY][0] == '-' &&
+      __ARGV[ARG_WORKING_DIRECTORY][1] == 0)
     ; /* Nothing */
   else if (_wchdir (wargv[ARG_WORKING_DIRECTORY]) < 0)
     write_err_and_exit (child_err_report_fd, CHILD_CHDIR_FAILED);
 
-  /* __argv[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
+  /* __ARGV[ARG_CLOSE_DESCRIPTORS] is "y" if file descriptors from 3
    *  upwards should be closed
    */
-  if (__argv[ARG_CLOSE_DESCRIPTORS][0] == 'y')
+  if (__ARGV[ARG_CLOSE_DESCRIPTORS][0] == 'y')
     for (i = 3; i < 1000; i++)	/* FIXME real limit? */
       if (i != child_err_report_fd && i != helper_sync_fd)
         if (_get_osfhandle (i) != -1)
@@ -360,16 +367,16 @@ main (int ignored_argc, char **ignored_argv)
   child_err_report_fd = dup_noninherited (child_err_report_fd, _O_WRONLY);
   helper_sync_fd = dup_noninherited (helper_sync_fd, _O_RDONLY);
 
-  /* __argv[ARG_WAIT] is "w" to wait for the program to exit */
-  if (__argv[ARG_WAIT][0] == 'w')
+  /* __ARGV[ARG_WAIT] is "w" to wait for the program to exit */
+  if (__ARGV[ARG_WAIT][0] == 'w')
     mode = P_WAIT;
   else
     mode = P_NOWAIT;
 
-  /* __argv[ARG_USE_PATH] is "y" to use PATH, otherwise not */
+  /* __ARGV[ARG_USE_PATH] is "y" to use PATH, otherwise not */
 
-  /* __argv[ARG_PROGRAM] is executable file to run,
-   * __argv[argv_zero_offset]... is its argv. argv_zero_offset equals
+  /* __ARGV[ARG_PROGRAM] is executable file to run,
+   * __ARGV[argv_zero_offset]... is its argv. argv_zero_offset equals
    * ARG_PROGRAM unless G_SPAWN_FILE_AND_ARGV_ZERO was used, in which
    * case we have a separate executable name and argv[0].
    */
@@ -379,7 +386,7 @@ main (int ignored_argc, char **ignored_argv)
    */
   protect_wargv (wargv + argv_zero_offset, &new_wargv);
 
-  if (__argv[ARG_USE_PATH][0] == 'y')
+  if (__ARGV[ARG_USE_PATH][0] == 'y')
     handle = _wspawnvp (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
   else
     handle = _wspawnv (mode, wargv[ARG_PROGRAM], (const wchar_t **) new_wargv);
-- 
1.8.4.2

Over!

复制安装 Linux 系统的一个教训

昨天从 ThinkPad 中复制整个 Arch Linux 到 Panasonic CF-19 的硬盘中,复制和安装 GRUB 过程都一切正常。

但是目标系统中不管是 root 用户还是在 sudoers 中的用户都无法正确执行 sudo 命令,错误大概是:

Unable to stat '/etc/sudoers': Permission Denied! ...

而且,GDM 服务也无法正常启动!

经过各种排查,包含重新安装 sudo 及相关的依赖,检查 /etc/sudoers 文件的权限等等都不能解决。

最后,在一个论坛找到了问题的原因:即根分区根目录的权限不对!

# chmod 0755 /

一切正常!

Over!

HevSCGIHandlerCGI 的多用户配置

HevSCGIHandlerCGI 是 HevSCGIServer 的 CGI 接口 Bridge,目前它被用于我的博客,实现 Web Server 与 PHP 的连接。现在它己支持多用户,根据 Web Server 的不同用户请求使用不同的用户权限执行 CGI 程序。

HevSCGIHandlerCGI 的用户与组配置默认会从两个地方读取:首先是解析 Web Server 的“环境变量”;其次就是模块的配置文件。取得即被应用,不会覆盖。在我的博客应用中没有在模块配置文件中配置用户与组,而是从 Web Server 的变量中取得,下面是 Nginx 的配置示例:

server {
    ...
 
    location ~ \.php$ {
        ...
        scgi_param  _USER heiher;
        scgi_param  _GROUP heiher;
        ...
    }
 
    ...
}

Over!

简单的 HTTP 文件共享服务 – FileBox

FileBox 是一个基于 HTTP 的简单文件共享服务,支持匿名的文件上传与下载、文件有效期限管理等。它实现为 HevSCGIServer 的一个 Handler,通过 SCGI 协议与 Web Server 交互。

filebox

安装部署
1. Web Server (Nginx)
Nginx 自身的安装部署就不多说了,主要说一下与 HevSCGIServerFilebox 相关的配置文件部分:

server {
    ...
 
    location /scgi {
        client_max_body_size 1024m; # 限制每组最大上传大小
        scgi_pass   127.0.0.1:9000; # HevSCGIServer 侦听地址端口
        #scgi_pass   unix:/opt/extra/hev-scgi-server/run/conn.sock; # 安全配置建议使用 Unix Socket
        include     scgi_params;
    }
 
    ...
}

2. HevSCGIServer Core
a. 创建目录结构

mkdir -p /opt/extra/hev-scgi-server/{bin,conf,lib/modules,run}
mkdir -p /var/lib/hev-scgi-server/filebox/{pool,meta,temp}

b. 下载、安装 hev-scgi-server-library

git clone git://gitcafe.com/heiher/hev-scgi-server-library.git
cd hev-scgi-server-library
make
cp bin/libhev-scgi-server.so /opt/extra/hev-scgi-server/lib/

c. 下载、安装 hev-scgi-server

git clone git://gitcafe.com/heiher/hev-scgi-server.git
cd hev-scgi-server
make
cp bin/hev-scgi-server /opt/extra/hev-scgi-server/bin/

3. FileBox

git clone git://gitcafe.com/heiher/hev-scgi-handler-filebox.git
cd hev-scgi-handler-filebox
make
cp bin/libhev-scgi-handler-filebox.so /opt/extra/hev-scgi-server/lib/modules/
cp -r ui/* /srv/http/ # 复制到 Web 根目录

4. 配置文件
/opt/extra/hev-scgi-server/conf/main.conf

[Server]
Address=127.0.0.1:9000
ModuleDirPath=/opt/extra/hev-scgi-server/lib/modules

/opt/extra/hev-scgi-server/conf/modules.conf

[Module0]
Alias=HevSCGIHandlerFilebox
Pattern=^/scgi/fb((/|\\?)(.*))?$
FileName=libhev-scgi-handler-filebox.so
 
BaseURI=/scgi/fb/
CleanInterval=600
FilePoolPath=/var/lib/hev-scgi-server/filebox/pool
FileMetaPath=/var/lib/hev-scgi-server/filebox/meta
FileTempPath=/var/lib/hev-scgi-server/filebox/temp

Tips: 命令行上传

curl -i -F filedata=@文件名称 -F one-off=true http://127.0.0.1/scgi/fb/upload # one-off=true 即“下后即删”

Over!