|
|
楼主 |
发表于 2018-9-29 13:15:53
|
显示全部楼层
任何一个成熟的项目,必须要提供出接口,就像探针一样,可以让我们探查进程内部的运行情况,进程不能是一个黑盒子。对于ceph而言,Admin Socket 提供了该功能。Admin Socket 不仅可以查看当前的配置,进程运行的状态,而且,还可以修改配置,获取log等。
& U3 ^( G/ Y8 `3 \ q
: M* v' L5 n/ \. P下面help可以看出,ceph给出了很多命令,来了解ceph内部的运行情况。
: D4 r5 U# p9 @, s1 E9 `root@test3:~# ceph daemon /var/run/ceph/ceph-osd.4.asok help$ |8 ?$ a2 ~+ ]3 b
{ "config get": "config get : get the config value",
4 P- ]( c; {4 w- C1 { "config set": "config set [ ...]: set a config variable",$ s4 j' c0 i) G& m
"config show": "dump current config settings",( J2 S' a! J' b3 R8 | I; a' J
"dump_blacklist": "dump blacklisted clients and times",* G- C- s: i# Q& y: V1 T% T
"dump_historic_ops": "show slowest recent ops",
3 G: \) Y( t! o N# N3 N "dump_op_pq_state": "dump op priority queue state",8 a4 G" c6 ?- m$ w5 T- @
"dump_ops_in_flight": "show the ops currently in flight",2 H) p- o- F; ]: U" C
"dump_watchers": "show clients which have active watches, and on which objects",6 x$ I* j4 K+ ?- Z+ R
"get_command_descriptions": "list available commands",4 x( F% R/ R. b* H+ w
"getomap": "output entire object map",, N/ a& T: l. p
"git_version": "get git sha1",! q4 F% x/ ]) m! b$ n
"help": "list available commands",
' {- P) c) y1 X, W2 C "injectdataerr": "inject data error into omap",( r& q* z2 Q2 x8 Y0 Q: {
"injectmdataerr": "inject metadata error",
' ]9 @+ X9 x5 @, \: `: {1 |4 \ "log dump": "dump recent log entries to log file",
7 t% O1 v/ f4 s% \ e/ x6 t. \$ @* J "log flush": "flush log entries to log file",
; n4 ~; e* T3 R1 J/ X "log reopen": "reopen log file",7 N" X( j& }0 @( ^
"perf dump": "dump perfcounters value",4 p+ W* i" g; d% B5 Q
"perf schema": "dump perfcounters schema",
' ~- i. C6 D& \ "rmomapkey": "remove omap key",3 X8 \: }2 D' J% }6 V3 K' a) |7 P
"setomapheader": "set omap header",- |. c- Z5 r& F
"setomapval": "set omap key",% S& R# r4 ?' M8 t+ A. G* W7 `
"truncobj": "truncate object to length",5 J; B8 w, m% f k; ?; x4 n
"version": "get ceph version"}
; n) U4 x( H7 `5 i0 D/ eroot@test3:~# ceph daemon /var/run/ceph/ceph-mon.*.asok help4 D1 M' J* {9 Y: o8 Y! F6 l
{ "add_bootstrap_peer_hint": "add peer address as potential bootstrap peer for cluster bringup",) r9 N! j: }/ A2 R% E3 o9 S
"config get": "config get : get the config value",
3 |7 ], t0 I2 [, o0 ?6 q "config set": "config set [ ...]: set a config variable",3 H5 Z5 x2 c% i) j9 m0 O7 G4 h9 X
"config show": "dump current config settings",
; `* M5 B- j r5 X- T "get_command_descriptions": "list available commands",
9 ~0 N/ b# g$ ^# C9 m "git_version": "get git sha1",: L2 N ~4 v$ B/ ^( ~5 i5 U( s, R4 y
"help": "list available commands",
# O6 ]/ x+ h! { "log dump": "dump recent log entries to log file",1 R5 \$ G4 d: n1 o. ?
"log flush": "flush log entries to log file",& d7 @6 N9 q/ N
"log reopen": "reopen log file",' b7 [% Y" I8 ^' b! \3 v
"mon_status": "show current monitor status",
3 a" m0 _6 `/ c( X) n8 ^ "perf dump": "dump perfcounters value",
: x% O! b# e# w/ d2 W8 e "perf schema": "dump perfcounters schema",1 q6 L i) L% O2 Z0 Z2 r0 u7 \
"quorum_status": "show current quorum status",; }' F, S* @6 H z
"sync_force": "force sync of and clear monitor store",+ k( @, c: L2 r: a8 M, }, ^
"version": "get ceph version"}
9 M! B& d4 L. Z) F, x- ` p+ J% W3 Zroot@test3:~# ceph daemon /var/run/ceph/ceph-mds.*.asok help$ a3 [* s. k/ N6 t: D) ^2 B
{ "config get": "config get : get the config value",
9 h l4 P5 d& `8 q, O# v "config set": "config set [ ...]: set a config variable",
/ p. y" A2 [) y) h "config show": "dump current config settings",' y ?' d( s1 L) c! F% C' e
"get_command_descriptions": "list available commands",* J6 O) W; ] S/ m
"git_version": "get git sha1", Q* V" h9 b% y' U
"help": "list available commands",7 M* l8 }, u7 b
"log dump": "dump recent log entries to log file",
g e" l2 t$ v* L "log flush": "flush log entries to log file",9 S. k0 D3 \+ H) ?
"log reopen": "reopen log file",1 x2 K0 G: z+ m
"objecter_requests": "show in-progress osd requests",: B$ q3 r, ~$ w1 a2 V/ }
"perf dump": "dump perfcounters value",
- A2 P6 p" @0 X0 F. } "perf schema": "dump perfcounters schema",' [3 t6 z9 W3 r$ {. ?/ Q$ ^" _
"version": "get ceph version"}# L. {: O4 \+ w+ z6 G
9 C1 d. R9 j) z# T1 V8 `) E
比如可以查看 ceph的各个模块的当前配置
! C, S: l) }1 x8 X Z5 }* D7 X/ b
4 N4 Z* v" ~2 l2 }* wceph daemon /var/run/ceph/ceph-mds.*.asok config show- i% i1 M$ W2 `" H ?4 M
ceph daemon /var/run/ceph/ceph-mon.*.asok config show
/ I) A4 S9 F! M! d- xceph daemon /var/run/ceph/ceph-osd.4.asok config show
& J& N) O5 o* C J9 f8 [这个是怎么实现的呢? 这是依靠ceph的AdminSocket机制完成。. r% @0 }# ~4 g$ X
CephContext中会创建一个AdminSocket对象,该对象本质是一个线程。ceph-mon/ceph-osd/ceph-mds这些进程都会有创建一个AdminSocket的线程,负责响应用户的探查命令。- k& O) T& T7 v! K! W
% u! g& L h" }6 O* H6 i# U; H从上面的OSD MON MDS help的不同输出可以看出,他们支持的命令有共同的,也有各自独立的。
( T2 p& e1 v: m4 r首先是共同支持的命令有:9 K8 q a+ y" v3 M
config show 显示所有的配置项
* i, R, ^/ J/ T6 Z' sconfig get 获取某个配置项2 I- N" U" ~9 B8 P
config set 设置某个配置项
/ N* C5 M) \6 I7 p- Alog flush 将log 刷入日志文件
/ A9 x1 H3 i! L* ^% [* J" \( Hlog dump 将最近的若干笔log刷入到log文件& g+ m1 U6 j" I: c
log reopen 重新打开log文件
0 P! y( ?! f3 S+ }0 ?/ E4 ]% S4 {4 Qperf dump 输出统计信息
8 v" b. k' A; R+ {& W/ d1 C* Kperf schema 输出统计信息的类型0 F5 y, Z& }, d$ [9 K) ^- V
version 版本信息
+ m" U) x( B2 k6 a& N+ v+ Z( Z* k+ t$ qgit_version : git 版本信息
3 M9 v) M& s! |+ x2 ]支持的大部分common的命令,注册发生在 CephContext的构造函数:
! { q0 D) I3 I) J0 ^0 p' A8 p$ { _admin_hook = new CephContextHook(this);
# r7 ]8 i, U6 t. ?# A8 [2 b _admin_socket->register_command("perfcounters_dump", "perfcounters_dump", _admin_hook, "");6 `6 H+ C. e# w, M0 `1 s# I) r
_admin_socket->register_command("1", "1", _admin_hook, "");
, \$ d3 H( s" _: Y# A! U _admin_socket->register_command("perf dump", "perf dump", _admin_hook, "dump perfcounters value");
" a7 R2 I' K& o: R _admin_socket->register_command("perfcounters_schema", "perfcounters_schema", _admin_hook, "");, }: j, ]+ f$ Q: L% w* v# v
_admin_socket->register_command("2", "2", _admin_hook, "");
" b1 L! A: v$ q. j& V2 L1 _2 T _admin_socket->register_command("perf schema", "perf schema", _admin_hook, "dump perfcounters schema");8 i( g1 q4 S0 h- U& k7 e
_admin_socket->register_command("config show", "config show", _admin_hook, "dump current config settings");
( A' P3 z$ u4 N" h; j _admin_socket->register_command("config set", "config set name=var,type=CephString name=val,type=CephString,n=N", _admin_hook, "config set [ ...]: set a config variable");
9 s4 V& C3 ^ w5 `* J _admin_socket->register_command("config get", "config get name=var,type=CephString", _admin_hook, "config get : get the config value");
2 \7 C o( f( |+ \7 ]( A# \' L/ B$ Y _admin_socket->register_command("log flush", "log flush", _admin_hook, "flush log entries to log file");! J: l9 f$ P; c9 S6 `1 C2 y
_admin_socket->register_command("log dump", "log dump", _admin_hook, "dump recent log entries to log file");7 V: w/ h7 x7 ^- J! b+ H Z ^
_admin_socket->register_command("log reopen", "log reopen", _admin_hook, "reopen log file")
~: Y6 K. C( m9 c' v" F7 E+ i3 O5 K9 J0 }7 x" C2 m. ^
首先定义了个CephContextHook,注册该Hook也作为一个参数传递进去,这个register_command比较简单,就是建立了command字符串和Hook的关联,这个关联有何作用,后面会分析到,暂时按下不表。( M; }" }& C5 {
, ~ C+ r, o0 g! t8 B1 a" B& X0 h4 Hint AdminSocket::register_command(std::string command, std::string cmddesc, AdminSocketHook *hook, std::string help)
% _0 U) `' D/ O{/ x$ c8 V4 T5 y! ^4 z j' q
int ret;9 |; S+ \/ D: N" L$ b4 a
m_lock.Lock();
r9 B+ M( t: k I- `: Z if (m_hooks.count(command)) {
; s' q3 F( \7 K: O ldout(m_cct, 5) << "register_command " << command << " hook " << hook << " EEXIST" << dendl;
: ?) y8 m4 C t# ~ ret = -EEXIST;1 G$ x) L3 E- g( @
} else {
; N/ z# L) E2 D/ p% S; u ldout(m_cct, 5) << "register_command " << command << " hook " << hook << dendl; {1 C: J* v( m
m_hooks[command] = hook;
9 L+ W3 ]: P4 H) K( [ m_descs[command] = cmddesc;
% {! B' M7 V. t2 G+ u5 k+ S m_help[command] = help;6 j1 G$ `5 S1 C
ret = 0;
% T# z' L/ E$ ?/ b6 w% b }
* ~+ E" k% ^9 p2 u: v$ f2 K( @% D* G m_lock.Unlock();
0 J$ n. e0 ^# }; J# U return ret;1 i8 `7 D. G& [5 C8 m* N
}$ d- [2 ^( E9 L2 n! q! x+ W# H6 d( v
$ w P) S% H! `. E- q
后面从注册部分的代码我们可以知道下面三个命令是等效的。$ ]' \3 l4 M* a/ r$ e
! _9 S7 U( B$ c, x2 `% t9 Yceph daemon /var/run/ceph/ceph-mds.bfudz.asok perf dump
: _; i' G; z( L* Qceph daemon /var/run/ceph/ceph-mds.bfudz.asok perfcounters_dump
) A0 e4 z9 _; h$ G* @" j; x6 F% kceph daemon /var/run/ceph/ceph-mds.bfudz.asok 1
: x( Q. ~- N+ K还有下面三个命令是等效的:* t" O$ |/ b$ d* ?* l
4 E' x; E. y/ t* a" S4 Z9 |ceph daemon /var/run/ceph/ceph-mds.bfudz.asok perf schema+ J) K, c7 `& j; X$ T( [5 Q% p1 J
ceph daemon /var/run/ceph/ceph-mds.bfudz.asok perfcounters_schema, N* k* r( b, F- p) o" r( H) j
ceph daemon /var/run/ceph/ceph-mds.bfudz.asok 2
$ @+ K/ A* d$ S8 {! I% t ceph-osd/ceph-mon/ceph-mds有一些自己的独特的注册函数,大家检索register_command可以检索的到这些独特的命令。' \5 N5 L, l! F- ?( J% s$ m( U* L
# G/ h# d0 H+ \$ A6 X# U7 ^) h! d! v: @. R( E: e' I q# v9 H
这个AdminSocket 对应的线程,在common_init_finish函数中负责创建。* e6 A$ J" I+ {7 f4 \
) X. K) R0 f9 ~& Xvoid common_init_finish(CephContext *cct), R& M4 ? Y; h& o$ S( P
{
* b0 h) i3 b8 {6 G- t# ?7 X I3 W7 R ceph::crypto::init(cct);
$ F- d8 K+ z/ o cct->start_service_thread();5 s' f5 r/ H! _( H
if (cct->_conf->lockdep) {
. J+ K" `: U2 o7 d, W g_lockdep = true;
* x8 m7 v( Z" u2 {* |- W5 T ldout(cct,0) << "lockdep is enabled" << dendl; H& J7 V* e7 N: N$ ~
lockdep_register_ceph_context(cct);
+ [. }# u9 x8 H: e& E" D4 n }
" K/ e7 A# h5 G$ a+ ?}: I/ ]; R# @, ]5 U8 g, Q7 X
而在start_service_thread函数中会调用adminsocket的init函数:; l5 d$ w' L7 q* b
+ A3 D* e" E k+ g0 x( Bvoid CephContext::start_service_thread()5 z2 z. G% D; t( ^" ]7 l* {
{4 f9 G: Q, ?! q9 |2 |
pthread_spin_lock(&_service_thread_lock);
6 ^# T) E. p8 V" _ if (_service_thread) {
" n' c* b- [% b' Q6 C& l pthread_spin_unlock(&_service_thread_lock);6 o1 x+ x: z- E6 l+ z1 H8 R
return;- V& e* U" X9 t, r$ [
}
/ `0 S6 h, P) n: h5 d C8 p _service_thread = new CephContextServiceThread(this);
2 p, Q! Z* w1 [9 W% j p; C _service_thread->create();
E$ [% ~5 P3 Q, }- S: a+ A' z: q' a1 c pthread_spin_unlock(&_service_thread_lock);
: s. K& l6 B& O* F. y1 q$ n I0 w // make logs flush on_exit()
" M5 V; B8 z& y) t6 f* F if (_conf->log_flush_on_exit)
7 N6 a, z4 ~* x( j; @0 W4 Q$ d _log->set_flush_on_exit();, }6 e" _6 `& c
// Trigger callbacks on any config observers that were waiting for
8 p" o) T& s% b. o1 Q // it to become safe to start threads.
# j2 ?$ M. @3 N. l* ~( b _conf->set_val("internal_safe_to_start_threads", "true");
2 `9 F! D8 j+ x9 w' { _conf->call_all_observers();" n/ e: f2 o6 F0 Q9 v) J
// start admin socket
/ F4 W* A C5 K4 L" b8 g T; \- I if (_conf->admin_socket.length())
) p$ \' ~2 `6 u# @% O; O2 K _admin_socket->init(_conf->admin_socket);, L4 G! h- g! p% C7 e6 }/ X3 q
}
! n& Q* Q {: k/ ]. g& o6 ~6 U: _) H- S# R( p' G G; C u
接下来可以分析下admin_socket的初始化函数init:' w# n, j3 @$ `6 w& |. h9 {* m
2 W8 H* m3 x/ E; ?9 }$ Tbool AdminSocket::init(const std::string &path)$ i( c1 }; B: i- ^* T3 m
{2 x$ h4 Y/ E& Y5 E3 h" D- w/ ~5 N
ldout(m_cct, 5) << "init " << path << dendl;6 D4 Y* M, s& r5 U
/* Set up things for the new thread */7 Y9 m+ p! ?6 {8 J
std::string err;* \- m, N) z+ W3 ?, |. t
int pipe_rd = -1, pipe_wr = -1;- `# {; `$ U ^' Z3 s# M
err = create_shutdown_pipe(&pipe_rd, &pipe_wr);0 R5 w' F# ?6 ~( z) l
if (!err.empty()) {
7 ?2 [' z0 j. w9 _) g/ g/ p, | lderr(m_cct) << "AdminSocketConfigObs::init: error: " << err << dendl;- z8 z% \! y: n; W; {7 a$ r
return false;# |& Y/ s( r4 g ]/ f* ]5 C6 x4 T
}8 e" v* _8 i! ^% X4 q; [! O& I, {
int sock_fd;: x. x; i/ F% \# A9 o
err = bind_and_listen(path, &sock_fd);# {7 c3 K c/ e4 m" A# W% o
if (!err.empty()) {
- e2 D$ y4 a" b; g* P8 H, g2 ^5 h5 Q lderr(m_cct) << "AdminSocketConfigObs::init: failed: " << err << dendl;
4 ?/ L/ Z) ~- i" J! c: [ close(pipe_rd);* H3 F V: e' ~
close(pipe_wr);
9 y: H0 A9 e9 P return false;
4 r; o) N8 _. f e m, W }
$ [# z% a0 S' |7 Z$ J+ F: { /* Create new thread */# f- |, _0 E3 \( o
m_sock_fd = sock_fd;
[; w7 K6 T7 n, Z1 d* S m_shutdown_rd_fd = pipe_rd;1 @* }. h6 T6 R. y
m_shutdown_wr_fd = pipe_wr;1 X* C2 @; i+ S, ^6 g; G
m_path = path;
4 T0 u, z3 y" p m_version_hook = new VersionHook;
) p0 j$ K) ?* W1 @+ {# z register_command("0", "0", m_version_hook, "");
N' y5 `0 j' N) _/ K register_command("version", "version", m_version_hook, "get ceph version");
5 c+ W* R$ {" e register_command("git_version", "git_version", m_version_hook, "get git sha1");
3 e2 ^6 X! @- z" g5 | L9 _ m_help_hook = new HelpHook(this);$ g/ e+ ^' f' o- \+ ^
register_command("help", "help", m_help_hook, "list available commands");
- m" [* A% x! v: P5 A2 h# B! B m_getdescs_hook = new GetdescsHook(this);# N6 I6 F4 R3 c# T# P
register_command("get_command_descriptions", "get_command_descriptions",; G. w# Z* p) u: Z. J& W
m_getdescs_hook, "list available commands");
: n E3 [8 C8 r create();6 k! g2 T N3 v6 K3 X6 ^/ K8 |$ P
add_cleanup_file(m_path.c_str());2 a+ o7 ?+ h# _! \. @- F
return true;" Y, l. T, y \+ ^: X5 d: t
}
6 |& Q7 @) D9 `: f4 A& y4 G+ Y6 B, C1 L' a& C' }# Z
首先是创建了管道,读取端的文件描述符记录在m_shutdown_rd_fd中,写入端的文件描述符记录在m_shutdown_wr_fd中。8 r' z \/ M" j% w( }+ G- |
从变量名字也可以看出,该文件描述符的作用是收取关闭信息。因为adminsocket一旦创建,必须能够通知到该线程及时退出。; G' E/ j: V/ T: r9 y7 Z
退出的事情会写入管道的写入端,而线程会通过多路复用接口,监听读取端,一旦发现m_shutdown_rd_fd中读出内容,线程就知道,可以退出了。
+ e, v/ c: S% d/ w按下不表。
, m. x9 l1 g5 \" j. L: B/ ]$ k* `; Z& X
AdminSocket最重要的是监听发过来的请求,它是用socket来实现的,初始化在bind_and_listen 函数:
$ h; @$ g$ `% Y5 a- \: F, y P/ G' t+ b5 B- `' a7 a
std::string AdminSocket::bind_and_listen(const std::string &sock_path, int *fd)
$ W) r3 H) ^2 h% R0 E{
; h5 h; }5 a }* E* h7 c8 k' H ldout(m_cct, 5) << "bind_and_listen " << sock_path << dendl;, w. {2 Y _9 J5 d
* D7 x, X+ @" `! C0 a6 d/ {0 J; v
struct sockaddr_un address;
6 v! M3 F. W" ^9 ^' U! U if (sock_path.size() > sizeof(address.sun_path) - 1) {4 b- t G5 r4 N5 C' Q( S
ostringstream oss;8 d) N0 G3 `6 L. y8 F" j( x
oss << "AdminSocket::bind_and_listen: "
9 ~: P7 n) `1 g! Q) ~ << "The UNIX domain socket path " << sock_path << " is too long! The "/ n" Q! c) \+ O/ v
<< "maximum length on this system is "6 ?; N* q+ Q2 ?& P& P2 S% U
<< (sizeof(address.sun_path) - 1);
8 t9 t" k0 j0 }" z return oss.str();
0 C, f6 b. G/ b }9 J! A) q' G( |+ S* E/ c
int sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);
' n( ]% S# w# c# W) L! U8 y$ b if (sock_fd < 0) {
% R$ M' S- d. }% H+ S) c5 l* f! Y+ ? int err = errno;
7 o# Z4 `* t; I0 B ostringstream oss;
6 c; d8 g! V* t' \6 { oss << "AdminSocket::bind_and_listen: "
5 |% C* v1 u" E0 J# X9 K << "failed to create socket: " << cpp_strerror(err);* a6 U7 e: z1 c8 D1 |& y
return oss.str();5 {& m0 {' ~ O; U- `
}9 f6 Q3 _0 a3 J/ A2 P$ z
int r = fcntl(sock_fd, F_SETFD, FD_CLOEXEC);
# T3 @" x! y' _3 c! J# q n/ y if (r < 0) {
9 o4 ^6 @; M& t r = errno;
6 s8 Y* F9 M; y$ J2 v TEMP_FAILURE_RETRY(::close(sock_fd));$ ^; X8 v% q0 e4 F4 d1 F( x
ostringstream oss;4 u; ?# P+ r1 w
oss << "AdminSocket::bind_and_listen: failed to fcntl on socket: " << cpp_strerror(r);4 i' h; ]! a' p( t2 S# m' L1 Z) @3 R
return oss.str();% \: O( B% F H3 V3 l
}
g; B. a! Q" g: L v6 U+ f; Y memset(&address, 0, sizeof(struct sockaddr_un));
9 O2 P+ h8 P# b4 i! t. {6 F1 q2 ] address.sun_family = AF_UNIX;" ]8 W" A8 o4 C- @7 H
snprintf(address.sun_path, sizeof(address.sun_path),1 R, E& x7 q$ q, K
"%s", sock_path.c_str());, Y8 f2 ^3 W7 a9 I
if (bind(sock_fd, (struct sockaddr*)&address,- b4 E7 H d, K: L6 K$ ~
sizeof(struct sockaddr_un)) != 0) {
4 ~6 i& X8 s2 m A% L# l9 O int err = errno;6 w3 k- G) m9 i( q3 x9 o0 _
if (err == EADDRINUSE) {9 j C7 C+ |2 j" o$ ]
AdminSocketClient client(sock_path);
5 n9 w& F7 g9 m3 i5 g0 k* V bool ok;' \" t" D; S9 z/ H7 n
client.ping(&ok);
* I/ D+ }# @+ E, @; o if (ok) {
# [& g9 c5 T9 N' B ldout(m_cct, 20) << "socket " << sock_path << " is in use" << dendl;
8 C- }* E6 [* _1 S3 w0 p/ F x err = EEXIST;; v; \$ Q8 B( j* k5 P
} else {( q7 A. h+ t3 x% r, {2 k
ldout(m_cct, 20) << "unlink stale file " << sock_path << dendl;# T2 e4 x; w# G" S
TEMP_FAILURE_RETRY(unlink(sock_path.c_str()));: N: R9 h; a* }; Q# q. x/ u' U0 b9 N
if (bind(sock_fd, (struct sockaddr*)&address,
9 R, b) @* }/ Y. Z" U sizeof(struct sockaddr_un)) == 0) {
4 n7 A% o# _: C: }: ^ err = 0;1 J& D0 H$ g5 A5 B. O0 D7 Z
} else {
/ C8 b' d8 `8 B9 ]0 I2 [9 l" W err = errno;( W+ u1 @! k- |) w
}
3 X9 U# [, }5 A0 A! ]# k }
+ j; h/ N2 K S m# F6 W) B3 N1 S }
( Q+ h r9 W* M0 g if (err != 0) {5 z- Y- S4 Z; V' d a
ostringstream oss;
6 J9 q/ O2 k t3 l: Z& Y5 h6 K$ ` oss << "AdminSocket::bind_and_listen: " H- J8 t% X7 Z6 s0 Z- N2 s- J
<< "failed to bind the UNIX domain socket to '" << sock_path4 D* B& L' o/ O$ N. V, C
<< "': " << cpp_strerror(err);
3 E4 ^+ ?1 P! y close(sock_fd);6 G" _5 ?% b7 b2 ?- o) w
return oss.str();
; R# j% }' Z7 O7 p2 G9 [5 n }% \4 A1 V7 C j8 x$ O& J `
}
8 c) s4 N2 H! }* x/ A2 i3 Y if (listen(sock_fd, 5) != 0) {
/ k) i' I) P7 k% u int err = errno;+ Q2 l8 h/ z& Q
ostringstream oss;; }$ Q$ R7 j- j# i; y
oss << "AdminSocket::bind_and_listen: "
# \5 t, n7 w; u& R << "failed to listen to socket: " << cpp_strerror(err);+ j1 O$ T3 s2 p5 O8 P/ M& c R/ s
close(sock_fd);* r; w# M* h# _3 l7 n W
TEMP_FAILURE_RETRY(unlink(sock_path.c_str()));
. v3 }- D& X. d2 }9 M return oss.str();
% Q" \9 a( y( X- x8 x4 V }
9 i( v, p- {- b/ X *fd = sock_fd;
+ @, c1 w1 k. W5 a0 J: z return "";
5 a, q, U" x+ j. f- u}$ t! P/ J8 `9 q( z; ~2 Q1 O
8 v/ o0 I3 K! m% ?/ X L
这个函数做的事情并不难理解,做的事情比较老套:
, X% W0 [ J3 m+ Q1 创建一个socket,该函数需要一个入参,指定socket路径名:
8 O* z- s, ~. [4 V: ^# b& Y! D9 q2 bind
1 e. V0 A5 b# M# O$ |3 listen
! E3 J0 `% w8 |: j( k8 {; n; o而传入的路径名是这个
; ^' ]: t/ r- @8 t- Mroot@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok config get admin_socket# {8 k! _- W" Y! s0 J8 A1 d
{ "admin_socket": "\/var\/run\/ceph\/ceph-mds.bfudz.asok"}
6 A! Y4 p- D9 F8 ~0 [socket建好之后,因为线程还没有创建,所以至今还没有accept接口 。然后我们再次回到init函数,该函数又注册了几个函数:
* b$ M; I: [4 v2 a4 r6 w0 t* \
. B. t; E0 `0 z9 Z& D, \. p, U( D3 n, lm_version_hook = new VersionHook;4 K& E4 _- e" ]7 Y1 U6 ~" T" g; j
register_command("0", "0", m_version_hook, "");; Z! s: B% l. A+ Z" F
register_command("version", "version", m_version_hook, "get ceph version");
7 S4 g$ f2 o! n' N4 G% Q+ C register_command("git_version", "git_version", m_version_hook, "get git sha1");) L5 E4 j5 z# \
m_help_hook = new HelpHook(this);0 O" }7 W3 |6 y7 H! V( ^
register_command("help", "help", m_help_hook, "list available commands");7 J5 P9 j ?% y2 _ }
m_getdescs_hook = new GetdescsHook(this);- t d0 c" b7 r6 ]2 F( v' a5 X# L
register_command("get_command_descriptions", "get_command_descriptions",
6 f2 Z1 |1 ?% c2 ~/ g m_getdescs_hook, "list available commands");; k8 w) ]1 _" Y3 B7 ?( T+ M
这几个函数的重要性并不大,基本是用来查版本信息的,如下所示,按下不表。
6 f" Y! K) Q3 W& A' ~8 V
( R: N) O! x! Jroot@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok version
4 d ]0 `- Y! s{"version":"0.67.9-222-g014b35f"}* ?" _; g5 _& g& t. u+ K/ D
root@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok git_version) W i5 P. b7 G8 Y: [, W
{"git_version":"014b35fc1ee0a1ad1f699a3705f3481a88614d36"}$ w( s: o/ J' H( f7 |1 V
root@test3:/var/run/ceph#% d" c# O/ y2 T1 ~8 _
; W* Y" y [- D+ J$ k: s1 U Z" m$ oinit函数最后调用了create函数。create函数是老朋友,前面分析Log的时候已经提到,对于Thread这个类,做的事情无非就是创建线程。关键内容是,线程执行的函数是哪个?
9 s4 w0 t9 g* e# q M. f( t和Log一样,是entry函数。AdminSocket类也有entry函数,该函数是AdminSocket 对应线程指定的函数:
" T- S6 ?8 b3 X* W8 M
3 z; j- N4 a* X0 R# I' ]void* AdminSocket::entry()
# t6 E8 m8 G3 e+ H/ D! k{
0 h. O0 B2 W/ }# M4 m ldout(m_cct, 5) << "entry start" << dendl;0 _7 c- f4 w/ ^6 |' N
while (true) {
* }" K+ r3 t: H. f1 d w struct pollfd fds[2];
. _+ H% H, H/ X! C memset(fds, 0, sizeof(fds));
& g! Y: i) P- D1 A- \ fds[0].fd = m_sock_fd;7 [) K! u4 \1 k' f6 e
fds[0].events = POLLIN | POLLRDBAND;
! `0 {& A. s; \( r fds[1].fd = m_shutdown_rd_fd;
- u. J1 d8 W0 ?/ q* \ fds[1].events = POLLIN | POLLRDBAND;3 ~3 J) o$ }& g9 W" L) F, {; I! q
int ret = poll(fds, 2, -1);& C3 G; O6 F3 Q+ `6 j, w
if (ret < 0) {( x p" q( K7 ^& E! i
int err = errno;
2 v. r; t+ U. E$ X# h: {/ l if (err == EINTR) { F# ?. L4 A& X* a
continue;
2 p+ Y" @% k( x2 @8 A+ D) t1 d' P8 S# w8 O }0 B) C4 q# N7 }. B+ E
lderr(m_cct) << "AdminSocket: poll(2) error: '"- Y! T% s5 q& u% {1 e
<< cpp_strerror(err) << dendl;0 E' M( G9 w" A2 r
return PFL_FAIL;( _( m6 l0 L3 q" W3 V6 m' s. X
}4 S: `" {9 A- }0 e U6 I1 A
if (fds[0].revents & POLLIN) {% S7 d2 a) r1 X* H
// Send out some data
1 c$ ]4 x# e7 g { do_accept();
, X J: D0 B) K+ o& m2 J- ^ }
" ?+ ]3 T/ ]; ` if (fds[1].revents & POLLIN) {" j7 w& D8 i2 V8 I- s
// Parent wants us to shut down6 M4 _& f! Z" s, R& E
return PFL_SUCCESS;
7 m6 V8 k, ^4 g }
9 ^% q9 I$ {' x }' Q6 i( |* E* k$ w/ m
ldout(m_cct, 5) << "entry exit" << dendl;3 Z2 u. I4 W ]" e5 q
}
8 e* |5 [! \) i
# N4 F q0 t. H% t5 k; V这个线程函数比较简单,它监听socket fd和管道的读取端。6 W. u. H( A+ e
1 管道的读取端负责管理何时退出# s+ `/ u$ U( s9 K
2 socket fd 负责监听用户发过来的指令。
" x# }9 v7 A) ^3 H- l! J8 g% Q7 Q" E7 @1 V! l: ^$ P* Y# s
处理用户发过来的命令,是do_accept函数干的事情:+ T4 W' p) T! i2 p& V
3 t& B% {: P- \( P! sbool AdminSocket::do_accept()/ I/ W4 E: A$ g
{3 C+ u, A9 ^3 ~# c
struct sockaddr_un address;
' U) s0 C, _* |* G, v socklen_t address_length = sizeof(address);
% l C8 F9 f1 i' M9 _$ U/ f# q ldout(m_cct, 30) << "AdminSocket: calling accept" << dendl;
- n3 h- `4 e. C7 N( r" A H int connection_fd = accept(m_sock_fd, (struct sockaddr*) &address,
4 [3 c+ _* z# e% y &address_length);$ _: z" K; ^$ z* Y3 _- n
ldout(m_cct, 30) << "AdminSocket: finished accept" << dendl;
& C- K, f+ M2 h1 d% n if (connection_fd < 0) {
' u" x+ |* j5 q5 o7 u int err = errno;5 F, g4 L; z: i; T( U0 V4 ?/ K. k
lderr(m_cct) << "AdminSocket: do_accept error: '") A7 R" ?9 b* @/ M
<< cpp_strerror(err) << dendl;! T/ j& D' d( N5 i
return false;
4 i4 E( w5 p# U. F }4 z5 p ~( C" E/ _) ^: A3 O e& ^
/ }! i$ W, q3 \0 v/ I& t
char cmd[1024];
* |; {# B4 D: ^$ A; R- p- D int pos = 0;6 U G x3 I. m
string c;
P( G- W }8 z- V& H8 L while (1) {: X: W" p" X( K* L6 g q* E
int ret = safe_read(connection_fd, &cmd[pos], 1);
' ^! x- \& t0 V0 c w9 @3 ` if (ret <= 0) {* X. q+ r7 L! v ~& z- m; S& P
lderr(m_cct) << "AdminSocket: error reading request code: ", T% T; {4 U3 M5 ^) S) u4 a
<< cpp_strerror(ret) << dendl;
1 e+ q" ^( J0 o6 b, e' C% ^ close(connection_fd);
7 `5 ?7 @$ j! V/ B return false;) {8 z0 J/ y0 q
}5 y7 p# {5 I5 [7 q, D
//ldout(m_cct, 0) << "AdminSocket read byte " << (int)cmd[pos] << " pos " << pos << dendl;
' r1 B* d- Z {" n* F if (cmd[0] == '\0') {/ @0 j5 D! z4 E) y* L% D5 M" E
// old protocol: __be32
( i" |3 G7 [3 @8 z* M( N if (pos == 3 && cmd[0] == '\0') {
9 v- N& _& |/ s. X$ R9 M# u. f switch (cmd[3]) {
& s* ?; g2 I. g& B case 0:0 m, f5 |, a# }6 [& b
c = "0";9 \2 W+ D. ~) b4 D% Q5 }' g
break;9 i" {& i4 l, ?# c, T7 m" ]9 v
case 1:9 c) B4 x6 x7 r; l' W) X( ]9 D
c = "perfcounters_dump";
3 w% X1 P4 ]; U8 d* l break;5 D3 A( a, ?; a! f/ `
case 2:/ Z! N7 X# C' b& W! e- m5 ^
c = "perfcounters_schema";
3 D: S/ |" {) k B) T break;$ J* M6 {' J$ G4 h2 j4 f
default:! X5 a4 R" U: w! x: O2 [: `: q1 _
c = "foo";4 a ]" e) |( j0 Q8 o
break;
# [; @" Q* ^3 @* T }* B- z3 m, H+ x4 z6 G- p8 T
break;
+ Y W; ^: Y0 c% u7 O L }4 L0 a1 B3 }. Y7 a$ F- \ |
} else {. {* ]; [, z8 S
// new protocol: null or \n terminated string
* ]$ t9 v' r) H if (cmd[pos] == '\n' || cmd[pos] == '\0') {# f( y! p z& Y5 O8 }
cmd[pos] = '\0';
! R' L5 ~" U& T/ J2 y! K o c = cmd;
. ]3 {3 m6 [( G break;
1 K2 q6 a8 k( }: ?5 K2 V7 v& V; V }% }$ O) Y5 @0 r' w" @/ ~
}
c( y( f3 a w8 H pos++; e. v# B1 c- }) D) \' k* J# G
}
3 Y& Q& n0 W: ]$ A! Q4 l) J
, a) i2 Q- N% V bool rval = false;+ a3 s8 O+ S8 Z, q( D- Q* J
! H+ j. T4 o9 G map<string, cmd_vartype> cmdmap;
: g! L+ S* ~2 T( a, v: J6 c string format;; d/ h& z- v' u# E w
vector<string> cmdvec;
* T( ?7 l8 ?# M. ? stringstream errss;2 ~4 p# a# T7 ^& \& Q
cmdvec.push_back(cmd);3 T& ?2 ^) }7 v$ F6 B# \' i0 X' _
if (!cmdmap_from_json(cmdvec, &cmdmap, errss)) {$ j0 C" v" ~& j7 F& I3 n7 V
ldout(m_cct, 0) << "AdminSocket: " << errss << dendl;
" ^2 Q1 b) N' L* ~/ x- m return false;3 y# s0 ^. e o1 Q' K
}
9 Z; v8 T" q) \- H5 V( b- f/ M cmd_getval(m_cct, cmdmap, "format", format);# @( M3 W2 a9 a0 d
if (format != "json" && format != "json-pretty" &&
7 {$ u, W% I0 G. n! X+ X" i2 ~ format != "xml" && format != "xml-pretty")" o9 A1 M' K2 l$ _# u" Z6 M( F
format = "json-pretty";
- v6 O3 ?5 A8 B, b) J cmd_getval(m_cct, cmdmap, "prefix", c);* X5 ]$ N$ [+ k. Z6 m
% J% ~1 o2 v4 D% E) v: ?
string firstword;
4 v* r, ]: Q% Q if (c.find(" ") == string::npos)
; Y) z' I$ h+ k7 T! v firstword = c;
, U/ Z& E) M4 } else
1 }* L: e5 g$ ^+ E# u firstword = c.substr(0, c.find(" "));& [9 `' n. X6 o, O* V1 A3 H$ e- T: J
1 r5 R" ^! B- u" x; _% R. s2 L" C
m_lock.Lock();
0 c6 D3 i+ i8 T1 P: e) W. o map<string,AdminSocketHook*>::iterator p;
6 \4 d" _' T; c1 g string match = c;
7 P$ M8 J5 M, w while (match.size()) {
; ?, D5 _* Y5 Y9 @# V* p0 K3 ^ p = m_hooks.find(match);1 Y4 Y+ M$ _7 ]0 u1 ~
if (p != m_hooks.end())4 g$ J. X) M, S$ {! p' C8 ]
break; L- l9 Q* D$ R- h
+ [( Q7 d7 I+ U0 F% Y // drop right-most word
+ D! ?7 C7 e. G3 t size_t pos = match.rfind(' ');
; w0 K+ {3 O4 Z if (pos == std::string::npos) {
" C5 H: M% a7 G% K0 j9 o: z `# f match.clear(); // we fail
) V; B$ L: v7 g" O7 M: S break;) a: [0 o( ?5 `) C; F
} else {
2 \' ]7 X% A5 v3 l# _" _; \% ` match.resize(pos);% R8 d* M; @6 r0 `& O
}
1 V" M3 h$ w$ }/ e$ ^7 u1 x/ O }
, [" D2 F/ y7 n' ^, ]6 B& n2 c. P) o+ L e, S
bufferlist out;$ }+ R8 L: }4 a$ C
if (p == m_hooks.end()) {
. E+ n% z) I; c/ a/ n lderr(m_cct) << "AdminSocket: request '" << c << "' not defined" << dendl;
$ G9 R5 X8 Z" a/ k3 E+ r8 [ } else {4 h7 D" L8 |+ n g% F5 _: e
string args;
3 B& e4 t& R0 r if (match != c)5 t( O! P p2 G+ M
args = c.substr(match.length() + 1);% z5 |- r& ^, \ c+ M8 M
bool success = p->second->call(match, cmdmap, format, out);, H+ l" V- A" D, A
if (!success) { q7 _( D7 W k! h- j
ldout(m_cct, 0) << "AdminSocket: request '" << match << "' args '" << args
5 L z b, O# L4 ~: T << "' to " << p->second << " failed" << dendl;
3 U9 I/ O# E$ J4 f8 n out.append("failed");
. \' w" D3 x$ } } else {
O e) Y( [5 @3 z- C( {$ i ldout(m_cct, 5) << "AdminSocket: request '" << match << "' '" << args3 x# U3 K- q0 N% J* O3 x2 R
<< "' to " << p->second
4 \1 e/ c( X& X: f: ^5 a << " returned " << out.length() << " bytes" << dendl;3 w# }8 o3 N, S' F4 Q+ ?
}
. ~) {, m! z9 Y uint32_t len = htonl(out.length());
: b( M" `/ Q* Q8 Z$ F5 T8 [ o9 b0 t! V; @ int ret = safe_write(connection_fd, &len, sizeof(len));
3 C' v; }* a" U9 H if (ret < 0) {/ l# \5 X1 W; o1 d
lderr(m_cct) << "AdminSocket: error writing response length "" b7 A B& [8 C7 y0 Q7 A
<< cpp_strerror(ret) << dendl;
8 R# W- b! \! d+ \2 o. n% a3 y } else {; @3 D/ v" E, x* a3 {& g
if (out.write_fd(connection_fd) >= 0)! m, m i# J$ X6 O7 L! V1 P
rval = true;
& h6 G$ I8 m- H4 K }
: {! |% O! T, W2 ]% c ]" d }" x# O' t, I, u! v3 Q
m_lock.Unlock();4 k9 ^: f/ C; n! [7 `! p
* w. G0 ^. O& K0 \* N+ n6 E# ~; B. ~
TEMP_FAILURE_RETRY(close(connection_fd));
( M2 Q/ N0 c; v7 L6 S return rval;
/ A7 d8 h8 f( X% k}
5 E3 }6 f1 J/ C8 K% y. U+ c( i& C2 }4 R; |
这个函数有点长,但是并不复杂。简单说,如果有个client尝试 connect ,该线程就poll就会感知到,然后进入do_accept函数。1 A7 k4 R i- k5 M0 ?& f
do_accept首先执行accept,和client 搭上线,然后开始通信。
* A& ^' d0 y3 d1 T& p- _$ c
+ z* D J" c8 ksafe_read负责 读取客户发过来的指令。前面已经提到过,AdminSocket支持的命令是有限的,初始化之前都已注册过了。
" n U. A% Y0 f9 E* m$ v如果client 发来的指令时注册过的指令,就见招拆招,返回相应的结果给客户端。$ K! Y) m6 `' a0 e
5 Q/ [) Q9 k: i+ D& b! v每一个命令的字符串,都是和一个AdminSocketHook 的类型关联的,但是一个AdminSocketHook可以对应多个command5 | X7 v2 u Z5 m7 N* a0 \) [9 Z- h6 Y
$ M& b* e# g3 G2 b& S8 l
std::map<std::string,AdminSocketHook*> m_hooks
& W$ T* ~$ \0 ]* _+ E# R: g! w1 Q比如说 config show / config get / perf dump对应的多事 CephContextHook,前面已经提到过。
3 H9 Q4 C6 d, E$ z, J& Q1 c) |) a) K+ g1 s
见招拆招的函数,就记录在对应的Hook上:
( K+ j, @& A, K/ K5 ~
) q. a) r. U7 T S( h+ c* H7 k9 Rclass CephContextHook : public AdminSocketHook {, w7 W* V! N) L
CephContext *m_cct;+ m. Z% H! _( v1 r0 ^$ u' x6 B# w
public:! F4 s4 l2 ^& q- q+ t" m) ^7 }/ @
CephContextHook(CephContext *cct) : m_cct(cct) {}' M4 v# Y4 S' }2 G
bool call(std::string command, cmdmap_t& cmdmap, std::string format,
) a; R6 {, X7 o+ D c bufferlist& out) {
$ N0 G+ ?; w1 C4 L) l# d m_cct->do_command(command, cmdmap, format, &out);
$ x5 ]+ X; ?9 ?+ ]" `! Q+ {- e& a return true;
0 q% c3 R5 m; x$ U1 o$ b9 N }4 T8 J* q! Z5 G) I' B: j
};; H% t/ e+ P9 M4 X8 J" {$ Y" O4 q
6 u: H/ H1 C. h7 \% @8 h
. z% I: r9 f) @" Udo_accept函数中黄色的一行,具体是实现,就是对应Hook的call函数,对于CephContextHook,就是这个类的call方法。1 @/ W4 ?, W ?
1 Q: g% A" V# l3 n下面我们看下CephContextHook的call方法:即它的do_command函数:- ^3 r9 s X7 x3 V7 O, ~
1 l/ ^$ V J E- t1 h% L
void CephContext::do_command(std::string command, cmdmap_t& cmdmap,
( W3 [+ l! a" d# l std::string format, bufferlist *out)
9 W8 m) w! k& B/ H: T- }{
6 P- p/ l6 F. r; d8 M2 g Formatter *f = new_formatter(format);
6 E8 _8 e# H0 Z8 Y+ m+ Q9 G# L% E if (!f)
/ [* W) k2 u- t. y( @ f = new_formatter("json-pretty");: b' c" D0 P, d" {) Y/ {% t' }
stringstream ss;# _2 y: p# w* b
for (cmdmap_t::iterator it = cmdmap.begin(); it != cmdmap.end(); ++it) {$ k# u& C2 }$ s6 _" F
if (it->first != "prefix") {
1 A; a+ ~4 s: z+ a% q ss << it->first << ":" << cmd_vartype_stringify(it->second) << " ";$ b0 s8 G9 J7 j7 q
}
0 k- X* D, [. O }* K# G& P' w# E
lgeneric_dout(this, 1) << "do_command '" << command << "' '"- P$ G6 J7 U- `9 c0 m' {
<< ss.str() << dendl;
- c: G4 ?8 J% y: Y if (command == "perfcounters_dump" || command == "1" ||0 Q# r; X- T n+ V4 O& \$ _5 F X
command == "perf dump") {
5 Y1 N! r* \( s) K' R _perf_counters_collection->dump_formatted(f, false);
" F! v* H j& Z }
- {( d! t$ W+ ]/ _! L3 e else if (command == "perfcounters_schema" || command == "2" ||: X( S) }4 h3 l M3 m1 p% ^: ]
command == "perf schema") {! ?- c8 ~5 n) m9 |, s9 n- d9 c2 ^
_perf_counters_collection->dump_formatted(f, true);
% C, m8 j" ]; g% ?; F' W }) g+ T+ M" W. N3 q! C
else {3 I, P7 U' C( U) E# ?: B% b; _, O
f->open_object_section(command.c_str());
7 F# N$ l% R- K0 B+ F3 U if (command == "config show") {
* S9 R. F$ D1 g: L5 V2 s3 P, P _conf->show_config(f);
6 B7 x7 h: t, z/ |1 L( z: Q2 e1 ~, f3 R' H }
( p4 [8 B& a! C8 x( V else if (command == "config set") {% r) f2 M) Q% z9 J, }5 _
std::string var;
/ ^+ E& C6 S: t7 i! e' ^! n. c std::vector<std::string> val;
% C% _. B* T! t% ?
, g9 }7 ^2 R# x, h if (!(cmd_getval(this, cmdmap, "var", var)) ||
) ^$ n f$ k7 l. f0 X$ c( Y; N !(cmd_getval(this, cmdmap, "val", val))) {
5 z6 J$ `* T6 s f->dump_string("error", "syntax error: 'config set '");" x4 _" _# H/ l3 u' }
} else {2 J1 `2 e$ v# W/ C
// val may be multiple words( Q9 R7 Y: w. v. |0 H& L% d
string valstr = str_join(val, " ");; o- }) L$ ^1 x6 C
int r = _conf->set_val(var.c_str(), valstr.c_str());+ P* E, ~0 y/ Z; U& P* s8 {
if (r < 0) {
# i. h& ~( f3 q0 | f->dump_stream("error") << "error setting '" << var << "' to '" << valstr << "': " << cpp_strerror(r);2 f) t0 P: [8 Z6 v
} else {6 n& Q: Q$ j3 p* g6 I
ostringstream ss;0 }7 X" h, K2 G0 `
_conf->apply_changes(&ss);
$ }* \* z: Y% Z4 Y" C0 c/ X f->dump_string("success", ss.str());6 Z' N- E P; d8 e
}
/ |" I9 [# _3 Z) C8 ]$ f }
' \( o P8 G. f; B8 n } else if (command == "config get") {
5 O; A, d0 G& Y2 r6 U" O: u) y std::string var;
; i2 X, w8 v$ ^ if (!cmd_getval(this, cmdmap, "var", var)) {
* q) Z" k8 x' x: Z1 O( O. p2 H% K f->dump_string("error", "syntax error: 'config get '");% a4 y% ]4 W5 ~0 V8 L1 Z
} else {. x8 b: [) x' y$ p( K
char buf[4096];
. J" G. _( i2 D( Q memset(buf, 0, sizeof(buf));% g9 J8 F6 X3 C& N0 [
char *tmp = buf;
: p2 R% @4 t5 r- R" D- n: c) d& ` int r = _conf->get_val(var.c_str(), &tmp, sizeof(buf));
& M; h$ y: O& I( k `4 ?2 _) g if (r < 0) {) s$ ~7 s) i' s3 c. a: I
f->dump_stream("error") << "error getting '" << var << "': " << cpp_strerror(r);+ j5 ]9 K, ]- g2 J6 i+ \
} else {6 |) [' u2 Z: S7 i- r
f->dump_string(var.c_str(), buf);4 w( G# o0 C7 Q
}
% E }+ |0 O1 L# Y# f }
; W. _* ` `! j2 A8 x } else if (command == "log flush") {( [5 I% A' A& s% L
_log->flush();
/ o2 L. L! n% m8 p3 ~* G6 A! l5 { }
- }0 @' G- Y! Z7 Z& k2 j, W else if (command == "log dump") {
) U, R1 f( Z0 x; m7 T A, j _log->dump_recent();% \9 G+ f# M' ^$ \& R) y6 F
}2 L* B3 ^: Y% B1 B! W2 k
else if (command == "log reopen") {8 D$ H2 Y. y( E( e
_log->reopen_log_file();
/ B, O: ^% I: l# E, c6 v }* T5 J# e/ h6 N; G: G5 i
else {# ?" d. i. n- Q: \: N
assert(0 == "registered under wrong command?"); $ Y- _/ j9 n ~1 v+ ]' c
}1 ?! S; B+ l8 G- a: z& j
f->close_section();. t0 y+ _: s% n9 ~
}: K( s$ p4 E# F+ D/ e
f->flush(*out);! ^' b, [5 P0 C: A# L L. H( o
delete f;1 n& t3 l2 K% _: L
lgeneric_dout(this, 1) << "do_command '" << command << "' '" << ss.str()
* I. H0 }( s/ H << "result is " << out->length() << " bytes" << dendl;+ Z9 x* r2 U. ?2 Q8 D
};
9 p9 d; O( b' i. c 毫不意外,前面注册的每一个方法,在do_command函数中都做了处理。 |
|