找回密码
 注册
查看: 7234|回复: 1

ceph :failed to bind the UNIX domain socket to

[复制链接]

1

主题

0

回帖

12

积分

管理员

积分
12
QQ
发表于 2018-9-29 13:12:39 | 显示全部楼层 |阅读模式
2018-09-29 13:11:07.750257 7f1cb9063700 -1 asok(0x7f1cb4000f80) AdminSocketConfigObs::init: failed: AdminSocket::bind_and_listen: failed to bind the UNIX domain socket to '/var/run/ceph/guests/ceph-client.admin.18131.139761255714944.asok': (2) No such file or directory8 c+ O+ G5 h" [
ID WEIGHT  TYPE NAME           UP/DOWN REWEIGHT PRIMARY-AFFINITY
8 M( y1 F3 V8 s6 i

1

主题

0

回帖

12

积分

管理员

积分
12
QQ
 楼主| 发表于 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函数中都做了处理。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

返回首页|Archiver|手机版|小黑屋|易陆发现技术论坛 ( 蜀ICP备2026014127号-1 )

GMT+8, 2026-6-12 04:24 , Processed in 0.015050 second(s), 22 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表