前編は こちら

スレーブ側のコードを読む

スレーブは最初に TSP_SLAVEUP コマンドで奴隷ネットに参加する。

19 slave()
20 {
...
39 	struct tsp resp;
...
45 	if (slavenet) {
46 		resp.tsp_type = TSP_SLAVEUP;
47 		resp.tsp_vers = TSPVERSION;
48 		(void)strcpy(resp.tsp_name, hostname);
49 		bytenetorder(&resp);
50 		if (sendto(sock, (char *)&resp, sizeof(struct tsp), 0,
51 		    &slavenet->dest_addr, sizeof(struct sockaddr_in)) < 0) {
52 			syslog(LOG_ERR, "sendto: %m");
53 			exit(1);
54 		}
55 	}

マスター側での TSP_SLAVEUP コマンドの処理は前回解説した通り。

スレーブもビジーループだが逐一コマンド処理する必要あるしもういいやこれで(世界陸上投げやり男子金メダリスト)。

83 	(void)gettimeofday(&time, (struct timezone *)0);
84 	electiontime = time.tv_sec + delay2;
...
93 loop:
...
95 	(void)gettimeofday(&time, (struct timezone *)0);
96 	if (time.tv_sec > electiontime) {
...
99 		longjmp(jmpenv, 1);
100 	}
...
146 	wait.tv_sec = electiontime - time.tv_sec + 10;
147 	wait.tv_usec = 0;
148 	msg = readmsg(TSP_ANY, (char *)ANYADDR, &wait, (struct netinfo *)NULL);
149 	if (msg != NULL) {
150 		switch (msg->tsp_type) {
...
531 		}
532 	}
533 	goto loop;

この delay2 と longjump(jmpenv) については main() を読む必要がある。

32 #define MINTOUT		360
33 #define MAXTOUT		900
...
82 extern long delay1, delay2;
34 long delay2;
...
71 main(argc, argv)
72 int argc;
73 char **argv;
74 {
...
332 	/* election timer delay in secs. */
333 	delay2 = casual((long)MINTOUT, (long)MAXTOUT);
334 
335 	if (Mflag) {
...
349 		ret = setjmp(jmpenv);
350 
351 		switch (ret) {
...
388 		}
399 
390 		if (status == MASTER)
391 			master();
392 		else
393 			slave();
394 	} else {
...
397 		if (setjmp(jmpenv)) {
...
400 		}
...
406 		slave();
407 	}
408 }
...
575 long
576 casual(inf, sup)
577 long inf;
578 long sup;
579 {
580 	float value;
581 
582 	value = (float)(random() & 0x7fffffff) / 0x7fffffff;
583 	return(inf + (sup - inf) * value);
584 }

delay2 は 360 秒から900 秒までのランダムな値。 casual() が何やってんのかというと random() の戻り値を 0x7fffffff でマスクし 0x7fffffff で割ると value は必ず 1 以下になるので戻り値は inf から sup の間のランダムな数字になる。 これ RANDOM_MAX 使えばマスク要らない(イーロンもね、衛生マスクはまだ着用しろ)よなと思ったが当時まだ存在しなかったわ。 それよりも今の時代なら arc4random_uniform(3) で書き直せとなる。

なぜ delay2 ひいては electiontime をランダムにするかというと、新しいリーダーの選出要求投げるタイミングをスレーブの間でバラバラにしたいから。

electiontime に達するとスレーブは脱奴隷化を図るために longjmp(3)main() に戻る。

ただし Mflag が立ってて自らもマスター立候補しリーダー選挙に勝たない限りはそのまま slave() に戻ってくるだけである。

脳の腐食が進行してるとマゾフラグ立ってるのに奴隷止めてご主人様になりたいとは…と混乱するが Mflag はマゾの意味じゃねえから!

-M Allow this host to become a timed master if necessary.

つーわけで数分毎にリーダー選挙やっててずいぶんと忙しいなって印象を受けるのだが、マスターの死活監視も兼ねてるのでそう考えると妥当なのかもしれない。

TSP_ADJTIME コマンドを処理する

マスターから TSP_ADJTIME コマンドを受信したら時刻調整である。

181 		case TSP_ADJTIME:
...
184 			(void)gettimeofday(&time, (struct timezone *)0);
185 			electiontime = time.tv_sec + delay2;
186 			if (seq != msg->tsp_seq) {
187 				seq = msg->tsp_seq;
...
192 					adjclock(&(msg->tsp_time));
...
194 			}
195 			break;

adjclock() についてはマスター側で説明したからいいよねもう。 さて adjtime(2) の実装も読まないとならないがそれはまた次回に。

TSP_SETTIME コマンドを処理する

マスターから TSP_SETTIME コマンドを受信したら時刻の強制同期である。

196 		case TSP_SETTIME:
...
204 			(void)strcpy(olddate, date());
205 			(void)gettimeofday(&otime, (struct timezone *)0);
206 			(void)settimeofday(&msg->tsp_time,
207 				(struct timezone *)0);
208 			syslog(LOG_NOTICE, "date changed by %s from: %s",
209 				msg->tsp_name, olddate);
...
229 			break;

これも RFC868 Time Protocol の回 で解説済の settimeofday(2) なのでそっち参照。

次回

ついに本丸 adjtime(2) システムコールに攻め込むはず。