• 从apache派生cgi工作路径看软链接


    一、问题和背景
    对于apache生成的cgi服务来说,通常需要读取一些特有的配置,而这个配置通常使用的方法还是使用软链接。在使用软链接的场景中,由于二进制是在一个文件夹中,而配置文件和日志文件可能在一个软链接的文件夹,所以配置的时候禁不住要问下apache派生的cgi的当前工作路径在哪里,有没有一个确定的说法?
    二、apache的代码
    1、派生进程当前工作路径的设置
    apache对于cgi进程的派生通常是通过封装过的自己封装的动态运行时库apr实现,这里我们最为关心的就是进程的创建,这个代码其实并不难查找,所以怎么找到进程派生位置的代码在这里就略过不提了。以我们这里关注的linux系统来说,这个进程的派生是通过apr-1.4.6 hreadprocunixproc.c来派生。不要被这里的文件夹名称threadproc迷惑它只能创建线程,其实它后面的proc应该就是进程process的意思,所以这个文件夹创建进程也是妥妥的。由于我找这个问题的时候是自下而上的查找的,所以在这个地方看到的代码对于当前工作路径的设置非常醒目,下面是代码内容:
    APR_DECLARE(apr_status_t) apr_proc_create(apr_proc_t *new,
                                              const char *progname,
                                              const char * const *args,
                                              const char * const *env,
                                              apr_procattr_t *attr,
                                              apr_pool_t *pool)
    {
    ……
        if ((new->pid = fork()) < 0) {
            return errno;
        }
        else if (new->pid == 0) {
            /* child process */
    ……
            if (attr->currdir != NULL) {
                if (chdir(attr->currdir) == -1) {
                    if (attr->errfn) {
                        attr->errfn(pool, errno, "change of working directory failed");
                    }
                    _exit(-1);   /* We have big problems, the child should exit. */
                }
            }
    ……
    也就是新创建的进程的当前工作目录是通过传入参数的attr->currdir控制。
    而这个参数的设置也在该文件中
    APR_DECLARE(apr_status_t) apr_procattr_dir_set(apr_procattr_t *attr,
                                                   const char *dir)
    {
        attr->currdir = apr_pstrdup(attr->pool, dir);
        if (attr->currdir) {
            return APR_SUCCESS;
        }
     
        return APR_ENOMEM;
    }
    2、cgi模块对于该进程的设置
    apache_httpd-2.4.2modulesgeneratorsmod_cgi.c
    static apr_status_t run_cgi_child(apr_file_t **script_out,
                                      apr_file_t **script_in,
                                      apr_file_t **script_err,
                                      const char *command,
                                      const char * const argv[],
                                      request_rec *r,
                                      apr_pool_t *p,
                                      cgi_exec_info_t *e_info)
    {
    ……
        /* Transmute ourselves into the script.
         * NB only ISINDEX scripts get decoded arguments.
         */
        if (((rc = apr_procattr_create(&procattr, p)) != APR_SUCCESS) ||
            ((rc = apr_procattr_io_set(procattr,
                                       e_info->in_pipe,
                                       e_info->out_pipe,
                                       e_info->err_pipe)) != APR_SUCCESS) ||
            ((rc = apr_procattr_dir_set(procattr,
                            ap_make_dirstr_parent(r->pool,
                                                  r->filename))) != APR_SUCCESS) ||
    ……
            ((rc = apr_procattr_child_errfn_set(procattr, cgi_child_errfn)) != APR_SUCCESS)) {
            /* Something bad happened, tell the world. */
            ap_log_rerror(APLOG_MARK, APLOG_ERR, rc, r, APLOGNO(01216)
                          "couldn't set child process attributes: %s", r->filename);
    ……
     
    apache_httpd-2.4.2serverutil.c
    /*
     * return the parent directory name including trailing / of the file s
     */
    AP_DECLARE(char *) ap_make_dirstr_parent(apr_pool_t *p, const char *s)
    {
        const char *last_slash = ap_strrchr_c(s, '/');
        char *d;
        int l;
     
        if (last_slash == NULL) {
            return apr_pstrdup(p, "");
        }
        l = (last_slash - s) + 1;
        d = apr_pstrmemdup(p, s, l);
     
        return (d);
    }
    3、cgi的工作路径
    简言之,就是cgi的工作路径就是cgi二进制所在的文件夹。
    三、内核对于上一层文件夹的处理
    可见这个地方对于上层的处理非常直观,就是直接找到它的"文件系统父节点",这个节点是唯一确定的。
    static __always_inline void follow_dotdot(struct nameidata *nd)
    {
    struct fs_struct *fs = current->fs;
     
    while(1) {
    ……
    if (nd->dentry != nd->mnt->mnt_root) {
    nd->dentry = dget(nd->dentry->d_parent);
    spin_unlock(&dcache_lock);
    dput(old);
    break;
    }
    ……
    nd->mnt = parent;
    }
    follow_mount(&nd->mnt, &nd->dentry);
    }
    四、和shell直观经验的冲突
    但是这个效果和我们在shell中看到的现象有所违背:在bash中如果通过一个软链接cd到特定目录,然后在通过".."可以经过软链接原路返回。
    1、一个“直观”的例子
    tsecer@harry: pwd
    /home/harry
    tsecer@harry: mkdir -p tsecer/r/ -p terry/y  src
    tsecer@harry: ln -fs /home/harry/src terry/y/src
    tsecer@harry: ln -fs /home/harry/src tsecer/r/src
    tsecer@harry: find
    .
    ./src
    ./src/src
    ./tsecer
    ./tsecer/r
    ./tsecer/r/src
    ./terry
    ./terry/y
    ./terry/y/src
    tsecer@harry: cd tsecer/r/src/
    tsecer@harry: pwd
    /home/harry/tsecer/r/src
    tsecer@harry: /bin/pwd 
    /home/harry/src
    tsecer@harry: cd ../
    tsecer@harry: pwd
    /home/harry/tsecer/r
    可以看到,当shell通过cd切换到包涵软练级的/home/harry/tsecer/r/src文件夹之后,可以通过cd ..再次返回到"原始路径"。并且在通过软链接进入的src文件夹下,通过内置的pwd看到当前工作路径是/home/harry/tsecer/r/src,而独立的二进制命令/bin/pwd 看到的却是/home/harry/src。这个可能就是“旁观者清当局者迷”的一个注脚吧。
    2、bash对于工作路径的处理
    bash-4.0libshpathcanon.c文件的sh_canonpath (path, flags)函数对cd中的路径进行了所谓的正规化操作,其中的逻辑看起来比较繁琐,但是大致可以明白其中对于".."的预处理是当作一个消除前一个路径的操作,具体这里的操作,在/home/harry/tsecer/r/src文件夹下执行cd ..,此时的"/home/harry/tsecer/r/src/.."中的".."会消除和它紧邻的前一个非".."的目录项(这里的情况是src),所以结果是我们通常所见的/home/harry/tsecer/r/。
    但是,这个地方还有一个问题,当我执行cd tsecer/r/src/之后,系统的当前工作目录不应该是在真是文件系统路径/home/harry/scr下吗?这个就是由于bash在cd之后,会把当前的"正规化"之后的字符串保存在the_current_working_directory中,所以bash内置的pwd是从这个变量中取得的字符串。
    /* Make NAME our internal idea of the current working directory. */
    void
    set_working_directory (name)
         char *name;
    {
      FREE (the_current_working_directory);
      the_current_working_directory = savestring (name);
    }
    3、该选项的开关
    从代码中看到,可以通过shell的option来启停该功能,下面是bash手册中关于该功能的描述:
    -P

    If set, do not resolve symbolic links when performing commands such as cd which change the current directory. The physical directory is used instead. By default, Bash follows the logical chain of directories when performing commands which change the current directory.

    For example, if /usr/sys is a symbolic link to /usr/local/sys then:

    $ cd /usr/sys; echo $PWD
    /usr/sys
    $ cd ..; pwd
    /usr
    

    If set -P is on, then:

    $ cd /usr/sys; echo $PWD
    /usr/local/sys
    $ cd ..; pwd
    /usr/local
    ……
    Using ‘+’ rather than ‘-’ causes these options to be turned off. The options can also be used upon invocation of the shell. The current set of options may be found in $-.
    4、操作流程
    a、打印当前配置
    tsecer@harry: set -o
    allexport       off
    braceexpand     on
    emacs           on
    errexit         off
    errtrace        off
    functrace       off
    hashall         on
    histexpand      on
    history         on
    ignoreeof       off
    interactive-comments    on
    keyword         off
    monitor         on
    noclobber       off
    noexec          off
    noglob          off
    nolog           off
    notify          off
    nounset         off
    onecmd          off
    physical        off
    pipefail        off
    posix           off
    privileged      off
    verbose         off
    vi              off
    xtrace          off
    b、使能配置
    tsecer@harry: set -P
    tsecer@harry: set -o
    allexport       off
    braceexpand     on
    emacs           on
    errexit         off
    errtrace        off
    functrace       off
    hashall         on
    histexpand      on
    history         on
    ignoreeof       off
    interactive-comments    on
    keyword         off
    monitor         on
    noclobber       off
    noexec          off
    noglob          off
    nolog           off
    notify          off
    nounset         off
    onecmd          off
    physical        on
    pipefail        off
    posix           off
    privileged      off
    verbose         off
    vi              off
    xtrace          off
    c、关闭配置
    tsecer@harry: set +P
    tsecer@harry: set -o
    allexport       off
    braceexpand     on
    emacs           on
    errexit         off
    errtrace        off
    functrace       off
    hashall         on
    histexpand      on
    history         on
    ignoreeof       off
    interactive-comments    on
    keyword         off
    monitor         on
    noclobber       off
    noexec          off
    noglob          off
    nolog           off
    notify          off
    nounset         off
    onecmd          off
    physical        off
    pipefail        off
    posix           off
    privileged      off
    verbose         off
    vi              off
    xtrace          off
  • 相关阅读:
    关于晋升的5个建议
    不拘一格:网飞的自由与责任工作法
    博恩·崔西的人生管理课
    老板防止我上班摸鱼,给我装了个chrome插件
    彻底搞懂彻底搞懂事件驱动模型
    python 100 days
    不要懒惰地重复自己
    不要让“追求完美”阻碍你做决策
    Windows上使用Python Terminal(终端控制台) 打印日志带有特殊符号时显示不出来
    Python3 Mysql DBhelper封装
  • 原文地址:https://www.cnblogs.com/tsecer/p/10487959.html
Copyright © 2020-2023  润新知