読者です 読者をやめる 読者になる 読者になる

臥薪嘗胆

インフラエンジニアのあれこれ

TLPI memo 3

2.15 疑似端末

自分の端末はwででてきた、TTYでわかる

vagrant@precise64:~$ w
 12:53:33 up 0 min,  1 user,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM              LOGIN@   IDLE   JCPU   PCPU WHAT
vagrant  pts/0    10.0.2.2         12:53    0.00s  0.20s  0.00s w
vagrant@precise64:~$
vagrant@precise64:~$ ls -la /dev/pts/0
crw--w---- 1 vagrant tty 136, 0 Jan 19 12:53 /dev/pts/0

3.1 システムコール

カーネルの機能を利用するAPI

straceにコマンドを渡すと実行されるシステムコールがドバドバ出る

strace ls .

こんなかんじで

execve("/bin/ls", ["ls", "."], [/* 18 vars */]) = 0
brk(0)                                  = 0x10b8000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f723661d000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=18510, ...}) = 0
mmap(NULL, 18510, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7236618000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)

標準関数(関数)とは

glibcに含まれるもの

「TLPI」を読んでく No.2

mmap()

仮想アドレス空間に領域を確保する

  • ファイルマッピンク
    • ファイルをメモリ領域にマップする
    • その領域を書き換えると、ファイルも書き換わる
  • 無名マッピング
    • 対応するファイルは無い。

プロセス間通信

プロセスがお互いにやりとりする

  • シグナル
    • SIGなんとか
  • パイプ(FIFO)
    • プロセス間でのデータのやり取り
  • socket
    • プロセス間でのデータのやり取り
  • ファイルロック
    • 読み取り、書き込みロック
  • メッセージキュー
    • 非同期
  • セマフォ
    • 他のプロセスと共有している物へのアクセスを制御する
  • 共有メモリ
    • 複数のプロセスが同じ領域を見ていて、誰かが変えるとみんなも変わる

スレット

プロセスが内部的に持つ実行単位

mod_mruby install してみた

mod_mruby

なぜやるのか?

インフラエンジニアをやっていて、かゆいところに手が届かないことがたくさんあって
もどかしい思いをずっとしてきた。
でもいまは時代が変わったようで、自分でmoduleを作らなくても
チョイチョイっとかゆいところに手が届くそうだ
そうmrubyならね!
この流れ(あってる?)に乗り遅れるな!

で、matsumoto-r さん作の mod_mrubyを使ってみよう

親切にここに書かれていたので、この通りにする。

結果、test.rbが動くまではサクッとできる。 あとは何をしたいか。 ちょっとこれから考えてみよう。

以下やったログ

apacheいれとく

sudo yum install httpd.x86_64
sudo yum install httpd-devel.x86_64

mod_mrubyのsrcをもってくる

git clone git://github.com/matsumoto-r/mod_mruby.git

テストとビルド

cd mod_mruby
sh test.sh
sh build.sh

私はvagrant環境で試した 以下にビルド後の.soファイルができてた

ls -la /home/vagrant/mod_mruby/src/.libs/mod_mruby.so

これをコピってあげた。

cp /home/vagrant/mod_mruby/src/.libs/mod_mruby.so /usr/lib64/httpd/modules/
chown root: /usr/lib64/httpd/modules/mod_mruby.so

コンフもマニュアルのとおりに

[root@localhost mod_mruby]# cat /etc/httpd/conf.d/mruby.conf
LoadModule mruby_module modules/mod_mruby.so
<Location /mruby-test>
    mrubyHandlerMiddle /etc/httpd/conf.d/test.rb cache
</Location>

予め準備されているものを読み込むべくコピー

cp -p test/test.rb /etc/httpd/conf.d/
chown root: /etc/httpd/conf.d/test.rb

apacheを起動

systemctl status httpd.service
systemctl start httpd.service

curl してみた結果

[root@localhost mod_mruby]# curl http://127.0.0.1/mruby-test
# mod_mruby Test Page
__mod_mruby test start.__
## Apache Class Test
- Server: Apache
- Apache version: Apache/2.4.6 (CentOS)
- Apache build: Nov 19 2015 21:43:13
- module name: mod_mruby
- module version: 1.13.10
- syslogger function: OK
- errlogger function: OK
- OK status = 0
- DECLINED status = -1
- HTTP_OK status = 200
- HTTP_SERVICE_UNAVAILABLE status = 503
## Request Class Test
- filename = /var/www/html/mruby-test
- uri = /mruby-test
- ---- request_rec changed ----
- filename = /var/www/html/index.html
- uri = /index.html
r.document_root: /var/www/html
r.document_root: /tmp
## Server Class Test
- document_root = /var/www/html
- error_fname = logs/error_log
- hostname = localhost.localdomain
- timeout = 60000000
## Connection Class Test
- remote_ip 127.0.0.1
- local_ip 127.0.0.1
- keepalives = 0
##Notes Class Test
- memo = hello
## Env Class Test
- AUTHOR = matsumoto_r
- SERVER_SOFTWARE = Apache/2.4.6 (CentOS)
- env hash size = 25
- env key = UNIQUE_ID val = VsMsFszCek7oPhdkOOPzNAAAAAM
- env key = HTTP_USER_AGENT val = curl/7.29.0
- env key = HTTP_HOST val = 127.0.0.1
- env key = HTTP_ACCEPT val = */*
- env key = PATH val = /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
- env key = SERVER_SIGNATURE val =
- env key = SERVER_SOFTWARE val = A p a c h e
- env key = SERVER_NAME val = 127.0.0.1
- env key = SERVER_ADDR val = 127.0.0.1
- env key = SERVER_PORT val = 80
- env key = REMOTE_ADDR val = 127.0.0.1
- env key = DOCUMENT_ROOT val = /var/www/html
- env key = REQUEST_SCHEME val = http
- env key = CONTEXT_PREFIX val =
- env key = CONTEXT_DOCUMENT_ROOT val = /var/www/html
- env key = SERVER_ADMIN val = root@localhost
- env key = SCRIPT_FILENAME val = /var/www/html/index.html
- env key = REMOTE_PORT val = 57100
- env key = GATEWAY_INTERFACE val = CGI/1.1
- env key = SERVER_PROTOCOL val = HTTP/1.1
- env key = REQUEST_METHOD val = GET
- env key = QUERY_STRING val =
- env key = REQUEST_URI val = /mruby-test
- env key = SCRIPT_NAME val = /index.html
- env key = AUTHOR val = matsumoto_r
## Headers_in Class Test
- Accept-Encoding = nil
- Accept-Encoding = gzip
- headers_in hash size = 4
- headers_in key = User-Agent val = curl/7.29.0
- headers_in key = Host val = 127.0.0.1
- headers_in key = Accept val = */*
- headers_in key = Accept-Encoding val = gzip
## Headers_out Class Test
- headers_out hash size = 0
## Scoreboard Class Test
- child pid = 15737
- thread limit    = 1
- server limit    = 256
- cpu load        = 792739788800
- load avereage   = [0, 0.0099999997764826, 0.090000003576279]
- total kbyte     = 3
- total access    = 1
- restart time    = 1.4556306719778e+15
- idle worker     = 4
- busy worker     = 1
- uptime          = 710
- access counter  = -1
- scoreboard status hash size = 1
- scoreboard key = 127.0.0.1 val = SERVER_GRACEFUL
- scoreboard counter hash size = 11
- scoreboard key = SERVER_READY val = 4
- scoreboard key = SERVER_STARTING val = 0
- scoreboard key = SERVER_BUSY_READ val = 0
- scoreboard key = SERVER_BUSY_WRITE val = 1
- scoreboard key = SERVER_BUSY_KEEPALIVE val = 0
- scoreboard key = SERVER_BUSY_LOG val = 0
- scoreboard key = SERVER_BUSY_DNS val = 0
- scoreboard key = SERVER_CLOSING val = 0
- scoreboard key = SERVER_DEAD val = 251
- scoreboard key = SERVER_GRACEFUL val = 0
- scoreboard key = SERVER_IDLE_KILL val = 0
## Finfo Class Test
- permission = 1877
- filetype regular file = false
- user = 0
- group = 0
- device = 64768
- inode = 35205976
- nlink = 2
- size = 6
- csize = 0
- atime = 1.447969437e+15
- mtime = 1.447969437e+15
- ctime = 1.4556292035898e+15
__Test Complete. Wellcome to mod_mruby world!!__

Cやってみよう #6

C

前年の続き

だいぶ間が空いてしまった。
2016年、restartです。
続けるように。

mod_vhost_maxclients.c のソースを見ていきましょう。

174   for (i = 0; i < vhost_maxclients_server_limit; ++i) {
175     for (j = 0; j < vhost_maxclients_thread_limit; ++j) {
176       worker_score *ws_record = ap_get_scoreboard_worker(i, j);
177 #ifdef __APACHE24__
178       char *client_ip = r->connection->client_ip;
179 #else
180       char *client_ip = r->connection->remote_ip;
181 #endif
  • 174行目は vhost_maxclients_server_limit まで繰り返しの処理が開始
  • 175行目は vhost_maxclients_thread_limit まで繰り返しの処理が開始
    • 例えば両方のlimitの値が10の場合は、10*10=100回、繰り返すわけですね。
  • 176行目が肝のようだ。
    ap_get_scoreboard_worker この処理で、apacheが持つscoreboardから値を取って
    *ws_record に値を入れている。
    http://apache.org/server-status
    ↑のserver-statusをimageしとけばいいのだろうか。
  • 178,180行目は、接続元のIPを取得している。
183       switch (ws_record->status) {
184          case SERVER_BUSY_READ:
185          case SERVER_BUSY_WRITE:
186          case SERVER_BUSY_KEEPALIVE:
187          case SERVER_BUSY_LOG:
188          case SERVER_BUSY_DNS:
189          case SERVER_CLOSING:
190          case SERVER_GRACEFUL:
  • ws_record->status のstatusで分岐をおこない、以下の状態の時にカウントをしている
#define SERVER_BUSY_READ 3  /* Reading a client request */
#define SERVER_BUSY_WRITE 4 /* Processing a client request */
#define SERVER_BUSY_KEEPALIVE 5 /* Waiting for more requests via keepalive */
#define SERVER_BUSY_LOG 6   /* Logging the request */
#define SERVER_BUSY_DNS 7   /* Looking up a hostname */
#define SERVER_CLOSING 8    /* Closing the connection */
#define SERVER_GRACEFUL 9   /* server is gracefully finishing request */

ここからが本題なのです。

190       case SERVER_GRACEFUL:
191         /* check maxclients per vhost */
192         if (strcmp(vhostport, ws_record->vhost) == 0) {
193           vhost_count++;
194           ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "DEBUG: (increment %s): %d/%d", vhostport,
195                        vhost_count, scfg->vhost_maxclients);
196           if (vhost_count > scfg->vhost_maxclients) {
197             if (scfg->dryrun > 0) {
198               ap_log_error(
199                   APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
200                   "NOTICE: [DRY-RUN] [VHOST_COUNT] return 503 from %s : %d / %d client_ip: %s uri: %s filename: %s",
201                   vhostport, vhost_count, scfg->vhost_maxclients, client_ip, r->uri, r->filename);
202             } else {
203               ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
204                            "NOTICE: [VHOST_COUNT] return 503 from %s : %d / %d client_ip: %s uri: %s filename: %s",
205                            vhostport, vhost_count, scfg->vhost_maxclients, client_ip, r->uri, r->filename);
206             }
207             return (scfg->dryrun > 0) ? DECLINED : HTTP_SERVICE_UNAVAILABLE;
208           }
209
210           /* check maxclients per ip in same vhost */
211           if (scfg->vhost_maxclients_per_ip > 0) {
212             if (strcmp(client_ip, ws_record->client) == 0) {
213               ip_count++;
214               if (ip_count > scfg->vhost_maxclients_per_ip) {
215                 if (scfg->dryrun > 0) {
216                   ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, "NOTICE: [DRY-RUN] [CLIENT_COUNT] return "
217                                                                             "503 from %s : %d / %d client_ip: %s uri: "
218                                                                             "%s filename: %s",
219                                vhostport, ip_count, scfg->vhost_maxclients_per_ip, client_ip, r->uri, r->filename);
220                 } else {
221                   ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
222                                "NOTICE: [CLIENT_COUNT] return 503 from %s : %d / %d client_ip: %s uri: %s filename: %s",
223                                vhostport, ip_count, scfg->vhost_maxclients_per_ip, client_ip, r->uri, r->filename);
224                 }
225                 return (scfg->dryrun > 0) ? DECLINED : HTTP_SERVICE_UNAVAILABLE;
226               }
227             }
228           }
229         }
230         break;
  • 192行目でリクエストの hoge.com:80 とscoreboardとが一致していれば vhost_count++ をインクリメント。
  • 196行目で設定されているlimit値を超えているかの判定を行っている。
    超えている場合、以下の処理を行う

    • 197行目でドライランかの判定を行い、それぞれでログを出力している
    • 207行目でドライランであれば DECLINED
      ドライランでなければ、 HTTP_SERVICE_UNAVAILABLE = 503 となる
  • 211行目移行は、同一のvhostに対して同じIPからのアクセスを制御する

こんな感じで、このモジュールは書かれていることが分かった。

一旦、おわりー

Cも時間を割けばわかるものなのですね。。 つぎは何しよう。

Cやってみよう #5

C

先日の続き

有識者とはタイミングが合わず、今回も独自の視点からみてみましょう!
その前に、apacheに由来する部分を抑えようと思い立ちました。

request_rec構造体というモノを軽く見てみましょう。
とおもってググったら素晴らしいサイトに出会いました
ここを見れば一発OK!
request_recはもう怖くない。
クライアントから送信された、リクエストの中身が見て取れますね!

ではこちら:mod_vhost_maxclients 続けましょう!

140 static int vhost_maxclients_handler(request_rec *r)
141 {
142   int i, j;
143   int vhost_count = 0;
144   int ip_count = 0;
145   char *vhostport;
146   vhost_maxclients_config *scfg =
147       (vhost_maxclients_config *)ap_get_module_config(r->server->module_config, &vhost_maxclients_module);
  • 140行目は関数の定義
  • 147行目はap_get_module_configで設定を読み込んでいる
    • 読み込んでるものは、ここ(334〜345行目)に定義されているものであろう(詳細不明)
334 #ifdef __APACHE24__
335 AP_DECLARE_MODULE(vhost_maxclients) = {
336 #else
337 module AP_MODULE_DECLARE_DATA vhost_maxclients_module = {
338 #endif
339     STANDARD20_MODULE_STUFF, NULL,         /* create per-dir config structures     */
340     NULL,                                  /* merge  per-dir    config structures  */
341     vhost_maxclients_create_server_config, /* create per-server config
342                                               structures  */
343     vhost_maxclients_create_server_merge_conf,                                  /* merge  per-server config structures  */
344     vhost_maxclients_cmds,                 /* table of config file commands        */
345     vhost_maxclients_register_hooks};

つぎー

149   if (!ap_is_initial_req(r)) {
150     return DECLINED;
151   }
152
153   if (scfg->vhost_maxclients <= 0) {
154     return DECLINED;
155   }
156
157   if (r->hostname == NULL) {
158     return DECLINED;
159   }
160
161   if (!ap_extended_status) {
162     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, "DEBUG: only used when ExtendedStatus On");
163     return DECLINED;
164   }
165
166   /* check ignore extesions */
167   if (check_extension(r->filename, scfg->ignore_extensions)) {
168     return DECLINED;
169   }
  • 149行目のap_is_initial_reqは以下の判定を行う
    • 最初のリクエストであれば、true
    • リダイレクトだったりした場合は、false
      ので、falseの場合は、DECLINED処理終了(mod_vhost_maxclientsの処理終了)となる
  • よってこの辺(149〜169行名)は、変数やリクエストの中身をチェックして、処理できない場合は処理終了としている。

つぎー

171   /* build vhostport name */
172   vhostport = build_vhostport_name(r);
  • 172行目は先週書いたbuild_vhostport_nameからhoge.com:80を取得してる

つぎー。
ここからがこのモジュールのメインの部分であろう。
が、長いので明日に!

Cやってみよう #4

C

先日の続き

おそろしい、C言語は恐ろしい。
たった10数行にも関わらず、頭に入ってこない。
俺の目と頭が連動しない...
慣れるしか無いのか!

112 static int check_extension(char *filename, apr_array_header_t *exts)
113 {
114   int i;
115   for (i = 0; i < exts->nelts; i++) {
116     const char *extension = ((char **)exts->elts)[i];
117     ssize_t name_len = strlen(filename) - strlen(extension);
118     if (name_len >= 0 && strcmp(&filename[name_len], extension) == 0)
119       return 1;
120   }
121   return 0;
122 }
  • 112行目で引数を2つもらう関数を定義してる。返り値はint
  • 115行目のexts->neltsは数値なのであろう。これは配列の要素数なんだろう。
  • 116行目のconst charは読み取り専用の文字列に配列の内容を入れているのだろう!
  • 116行目の(char **)これはなんだ?
    • Dereferencingとな
      • 変数のアドレスから値を取得する
  • 117行目のssize_tは、書き込まれたバイト数,エラー時は-1
  • 117行目はファイル名の文字数から配列の要素の文字数を引いて、name_lenにいれとる。
  • 118行目のstrcmpは文字列比較
  • 結果、この関数は0 or 1を返す

次々

124 static char *build_vhostport_name(request_rec *r)
125 {
126 #ifdef __APACHE24__
127   ssize_t vhostport_len;
128   char *vhostport;
129
130   vhostport_len = strlen(r->hostname) + sizeof(":65536");
131   vhostport = apr_pcalloc(r->pool, vhostport_len);
132   apr_snprintf(vhostport, vhostport_len, "%s:%d", r->hostname, r->connection->local_addr->port);
133
134   return vhostport;
135 #else
136   return (char *)r->hostname;
137 #endif
138 }
  • 124行目で引数を1つもらう関数を定義してる。返り値はchar
  • 124行目のrequest_recapacheの持ってる構造体をrというアドレスに定義?してる
  • #コメントアウトなの?
  • 130行目でvhostport_lenにこの場合はvirtualhostの長さと65536のメモリサイズを足した値を返してる
    • よくわからん
  • 131行目はapr_pcallocで初期化してる
  • 132行目はapr_snprintfぐぐってもわからん。
  • 結局この関数はhoge.com:80みたいなものを返してるのではないだろうか。

ちょっと憶測が多くなってきて、ぐぐっても分からないので有識者に聞こう。。。
聞いたら更新しますからね!

Cやってみよう #3

C

先日の続き

偉大な先輩が言ってた
「時間があったら人のソースを読んで勉強しろ」
ということで、最近話題のソースを読んで勉強します。

お題はこちら:mod_vhost_maxclients
一つのapache複数のバーチャルホストを運用している場合は有用なのではないでしょうか!

C言語、勉強中ですので間違ってるかも知れません。
その時はすみません。
ツッコミお願いします!

情報はググッて仕入れてみます。
まず git cloneしてみよう

git clone https://github.com/matsumoto-r/mod_vhost_maxclients.git

このような内容でした

-rw-r--r--   1 kyswtnb  staff    148 10 13 21:39 .clang-format
drwxr-xr-x  12 kyswtnb  staff    408 10 13 21:39 .git
-rw-r--r--   1 kyswtnb  staff    112 10 13 21:39 .gitignore
-rw-r--r--   1 kyswtnb  staff   6529 10 13 21:39 .travis.yml
-rw-r--r--   1 kyswtnb  staff    175 10 13 21:39 LEGAL
-rw-r--r--   1 kyswtnb  staff   1073 10 13 21:39 MITL
-rw-r--r--   1 kyswtnb  staff   1293 10 13 21:39 Makefile
-rw-r--r--   1 kyswtnb  staff   2224 10 13 21:39 README.md
-rw-r--r--   1 kyswtnb  staff  12179 10 13 21:39 mod_vhost_maxclients.c
drwxr-xr-x  13 kyswtnb  staff    442 10 13 21:39 test

cで書かれているソースは mod_vhost_maxclients.c これですね。
では読んでみましょう。

まず、apache関連のヘッダーファイルを取り込んでます。たぶん。

28 #include "httpd.h"
29 #include "http_config.h"
30 #include "http_request.h"
31 #include "http_protocol.h"
32 #include "http_core.h"
33 #include "http_log.h"
34
35 #include "ap_mpm.h"
36 #include "apr_strings.h"
37 #include "scoreboard.h"

これはわかるぞ!
モジュール名とバージョンですね。

39 #define MODULE_NAME "mod_vhost_maxclients"
40 #define MODULE_VERSION "0.3.2"

これは分からない。。

42 #if (AP_SERVER_MINORVERSION_NUMBER > 2)
43 #define __APACHE24__
44 #endif
  • httpd-2.4.16.tar.gzを落としてきて、grepしたら↓が書いてあった
#define AP_SERVER_MINORVERSION_NUMBER 4
  • #includeしたヘッダーファイルの値を判定してるんですね。
  • 調べてみればわかるもんだ。

↑で定義した__APACHE24__があったらさらに変数を定義

46 #ifdef __APACHE24__
47 #define ap_get_scoreboard_worker ap_get_scoreboard_worker_from_indexes
48 #endif

そんな感じで、定義するところが続きます

50 #define _log_debug                                                                                                     \
51   ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL, "DEBUG: " MODULE_NAME "/" MODULE_VERSION "%s:%d", __func__, __LINE__)
52
53 #ifdef __APACHE24__
54 #include "http_main.h"
55 #else
56 #define ap_server_conf NULL
57 #endif
58
59 #define VHOST_MAXEXTENSIONS 16
60
61 #if !defined(__APACHE24__) && defined(_WIN32)
62 /*
63  * libhttpd.dll does not export following variables.
64  * This won't work correctly, but working well for other functional.
65  */
66 int ap_extended_status = 0;
67 #endif

windows,unix互換のマクロ AP_MODULE_DECLARE_DATA を記述。たぶん。

 69 module AP_MODULE_DECLARE_DATA vhost_maxclients_module;

スタティックな変数を宣言。同時に2つ宣言できるんですね。

 70 static int vhost_maxclients_server_limit, vhost_maxclients_thread_limit;

構造体(変数の塊)を vhost_maxclients_config という名前で定義している

72 typedef struct {
73
74   /* vhost max clinetns */
75   int dryrun;
76   signed int vhost_maxclients;
77   signed int vhost_maxclients_per_ip;
78   apr_array_header_t *ignore_extensions;
79
80 } vhost_maxclients_config;

これがポインタ?
* apr_pool_t bufferの保存場所?
* server_rec apacheが持ってる構造体
* apr_pcalloc メモリ領域を確保 * apr_array_make 配列を作成

ここでは、設定を読み込み領域を定義しているのかな〜と憶測

82 static void *vhost_maxclients_create_server_config(apr_pool_t *p, server_rec *s)
83 {
84   vhost_maxclients_config *scfg = (vhost_maxclients_config *)apr_pcalloc(p, sizeof(*scfg));
85
86   scfg->dryrun = -1;
87   scfg->vhost_maxclients = 0;
88   scfg->vhost_maxclients_per_ip = 0;
89   scfg->ignore_extensions = apr_array_make(p, VHOST_MAXEXTENSIONS, sizeof(char *));
90
91   return scfg;
92 }

ここも↑と同じで設定を読み込む構造体を定義しているように見える

94 static void* vhost_maxclients_create_server_merge_conf(apr_pool_t* p, void* b, void* n)
95 {
96   vhost_maxclients_config *base = (vhost_maxclients_config *)b;
97   vhost_maxclients_config *new = (vhost_maxclients_config *)n;
98   vhost_maxclients_config *scfg = (vhost_maxclients_config *)apr_pcalloc(p, sizeof(*scfg));
99
100   if (new->dryrun > -1) {
101       scfg->dryrun = new->dryrun;
102   } else {
103       scfg->dryrun = base->dryrun;
104   }
105   scfg->vhost_maxclients = new->vhost_maxclients;
106   scfg->vhost_maxclients_per_ip = new->vhost_maxclients_per_ip;
107   scfg->ignore_extensions = new->ignore_extensions;
108
109   return scfg;
110 }

ちょっとここからは難しくなってきたので、仕切りなおしてまた次の日に・・・。
慣れだけなのだろうけど、ぱっとみて何やってるのか把握するのが難しい。
続けよう。