找回密码
 注册
查看: 7233|回复: 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 directory+ g9 z5 {( B. v+ Y* C" |- y
ID WEIGHT  TYPE NAME           UP/DOWN REWEIGHT PRIMARY-AFFINITY 0 J% `" C' `) i

1

主题

0

回帖

12

积分

管理员

积分
12
QQ
 楼主| 发表于 2018-9-29 13:15:53 | 显示全部楼层
任何一个成熟的项目,必须要提供出接口,就像探针一样,可以让我们探查进程内部的运行情况,进程不能是一个黑盒子。对于ceph而言,Admin Socket 提供了该功能。Admin Socket 不仅可以查看当前的配置,进程运行的状态,而且,还可以修改配置,获取log等。; C, [; j1 ^  u8 W2 k

# F/ n3 M5 q( ]4 ^3 A- E: w下面help可以看出,ceph给出了很多命令,来了解ceph内部的运行情况。
0 Q; a# e' A+ e: Y' {root@test3:~# ceph daemon /var/run/ceph/ceph-osd.4.asok help
- _% A% j- l0 P/ @" D9 T{ "config get": "config get : get the config value",
; ^9 c$ `5 o" L- X( T  "config set": "config set [ ...]: set a config variable",
) B, o6 w2 a8 q8 X3 t5 l2 L  "config show": "dump current config settings",' p: H8 b5 \4 I2 L) ~
  "dump_blacklist": "dump blacklisted clients and times",
$ f) d$ o" b8 N, v) H  "dump_historic_ops": "show slowest recent ops",
6 s0 `9 w9 M5 u3 Q/ V  "dump_op_pq_state": "dump op priority queue state",
9 `. ~, L' S" s% B, z1 B6 C9 A  "dump_ops_in_flight": "show the ops currently in flight",
# c9 n7 r, k+ j& c( j- {4 W% q  "dump_watchers": "show clients which have active watches, and on which objects",* b& j- a! v: d, b
  "get_command_descriptions": "list available commands",
0 }: G: R! Z% m4 Q" o  "getomap": "output entire object map",0 r! G! g$ S! E% v' v
  "git_version": "get git sha1",
* s9 v; ^* u& G  "help": "list available commands",
  L, U3 U6 f) M  "injectdataerr": "inject data error into omap",. ?" m7 h; T# \0 N" U* K
  "injectmdataerr": "inject metadata error",
8 ]9 Q; d& f: u0 I4 C  "log dump": "dump recent log entries to log file",
# p9 C9 U. P5 }9 M+ }  "log flush": "flush log entries to log file",7 J% y, j/ O( ?! e% [  c
  "log reopen": "reopen log file",' k. V3 p% M3 m; s8 ?; y- K; {  |/ \
  "perf dump": "dump perfcounters value",
) U; ]% p% @5 R( w( w& ~  "perf schema": "dump perfcounters schema",  C1 r7 [, R+ Z1 L  a; E
  "rmomapkey": "remove omap key",( n9 m# L5 N/ C. l/ ?) Q/ Z
  "setomapheader": "set omap header",% }+ m& c; B  {& U( K4 S1 w
  "setomapval": "set omap key",
, g+ J# K- G& x- v  "truncobj": "truncate object to length",8 Y7 s  p: @0 m5 W- j, G1 N
  "version": "get ceph version"}5 T$ I9 K0 G! @1 P- J& \: j6 r
root@test3:~# ceph daemon /var/run/ceph/ceph-mon.*.asok help
3 z  Q4 E" A% S{ "add_bootstrap_peer_hint": "add peer address as potential bootstrap peer for cluster bringup",0 o7 A# G; U4 F( d( y) Y
  "config get": "config get : get the config value",
$ A* t, Q3 V- F2 u  "config set": "config set [ ...]: set a config variable",
( O! F( R0 `: T4 Z* F' k  "config show": "dump current config settings",) ]4 W; q% X  p# v- {  A& M8 c" B
  "get_command_descriptions": "list available commands",
0 ~' D! v3 A( l& {) M  "git_version": "get git sha1",9 l- l2 K* m+ @
  "help": "list available commands",+ ?- |' |& H2 V* ^0 r% ?0 e" R
  "log dump": "dump recent log entries to log file",2 M/ s8 A, E; ~, z3 w+ D& i4 g6 D
  "log flush": "flush log entries to log file",- K5 @9 W1 l3 j0 T8 k$ T
  "log reopen": "reopen log file",: Z; d" Z5 v: l, }
  "mon_status": "show current monitor status",$ i+ d' [: v! _; {! o1 \/ A
  "perf dump": "dump perfcounters value",  i) ^' G. G6 b5 M/ V, H5 H
  "perf schema": "dump perfcounters schema",
- {& e, o4 Q8 q, m  r: N$ ]) T  "quorum_status": "show current quorum status",
8 J' V+ [2 t+ Y  "sync_force": "force sync of and clear monitor store",, a7 i, M8 k/ d  N8 [. c5 u
  "version": "get ceph version"}8 |/ {$ @; ?$ R7 M$ a+ x
root@test3:~# ceph daemon /var/run/ceph/ceph-mds.*.asok help
" |7 {1 `6 i% v6 E{ "config get": "config get : get the config value",
+ P. A0 R0 b# Y7 G  "config set": "config set [ ...]: set a config variable",! J( Z5 N( M! X  O
  "config show": "dump current config settings",/ L8 K* Z5 y- A* o" U  z/ O$ y& @
  "get_command_descriptions": "list available commands",) p# N/ \3 @2 V
  "git_version": "get git sha1",
+ p! O9 Y4 ~, T/ s2 K  "help": "list available commands",! v" E. D' g& y9 u+ n0 l/ J" S
  "log dump": "dump recent log entries to log file",
  {- s+ _# S: j. _' A8 M: T  "log flush": "flush log entries to log file",  c# Y" p1 {4 V: U  R
  "log reopen": "reopen log file",
$ O, g# r  d; D( Q) ?7 M  "objecter_requests": "show in-progress osd requests",6 v' K. H5 Z/ `
  "perf dump": "dump perfcounters value",+ n6 Z& H- L: l% A
  "perf schema": "dump perfcounters schema",
+ N/ B: C$ `3 ~* G. a; t. @  "version": "get ceph version"}6 T# @7 Q; E- o; e- Z

" b% O+ n" u2 _比如可以查看 ceph的各个模块的当前配置2 I# c7 N  l' O

# s+ F5 _4 w9 y1 u0 e( ]; lceph daemon /var/run/ceph/ceph-mds.*.asok config show/ |7 h; U- d4 T$ g0 a. Q8 g
ceph daemon /var/run/ceph/ceph-mon.*.asok config show* o/ k( }& z8 k, S. ?2 t
ceph daemon /var/run/ceph/ceph-osd.4.asok config show" v1 T. `# D* n+ T1 Y. U, F
这个是怎么实现的呢? 这是依靠ceph的AdminSocket机制完成。; @/ b0 Q) G: p
CephContext中会创建一个AdminSocket对象,该对象本质是一个线程。ceph-mon/ceph-osd/ceph-mds这些进程都会有创建一个AdminSocket的线程,负责响应用户的探查命令。2 q$ A( O  o7 T) k& D
2 x6 F' b5 @6 L2 `& E
从上面的OSD MON MDS help的不同输出可以看出,他们支持的命令有共同的,也有各自独立的。
# L* a) V( ?% T$ x首先是共同支持的命令有:4 O: [6 H5 ^& }# j4 A( G
config show        显示所有的配置项
! P8 R9 Q0 L. jconfig get           获取某个配置项
& h/ j) ^( d; t% Yconfig set           设置某个配置项
" q6 _* \0 U3 q- f/ {: n3 ^log flush        将log 刷入日志文件4 F- _& [: e* Y0 V  V
log dump           将最近的若干笔log刷入到log文件
1 _9 E$ b; [' G, r0 glog reopen         重新打开log文件  o' k, b) [7 @) n2 M3 T+ k" m
perf dump         输出统计信息; ~' d; i. ~3 \! R8 n
perf schema      输出统计信息的类型
1 p$ d7 ?4 s8 z+ x) Aversion             版本信息* b- W4 l- j6 w* P( i
git_version   :    git 版本信息
. ]  u$ s6 U1 m$ w+ Z% J支持的大部分common的命令,注册发生在 CephContext的构造函数:
2 z9 z' n- h+ P  _admin_hook = new CephContextHook(this);
# j; d8 H1 e/ l! S! b  _admin_socket->register_command("perfcounters_dump", "perfcounters_dump", _admin_hook, "");
( D) W( E+ s! Y! I& g3 T5 E7 N: }  _admin_socket->register_command("1", "1", _admin_hook, "");
& P+ M; w' ?: V  _admin_socket->register_command("perf dump", "perf dump", _admin_hook, "dump perfcounters value");' U7 N- W8 Q! |6 {4 W
  _admin_socket->register_command("perfcounters_schema", "perfcounters_schema", _admin_hook, "");
9 Z& z) s0 c9 N8 x) d! |  _admin_socket->register_command("2", "2", _admin_hook, "");
. i, Q  j& u6 i( n, W4 |  _admin_socket->register_command("perf schema", "perf schema", _admin_hook, "dump perfcounters schema");5 ?/ [( S* o+ M0 J7 w
  _admin_socket->register_command("config show", "config show", _admin_hook, "dump current config settings");
& e3 m" o0 H0 R4 o" X; l% G  _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");' x/ O7 A4 O/ D7 x, \# Y
  _admin_socket->register_command("config get", "config get name=var,type=CephString", _admin_hook, "config get : get the config value");
, T( \& w; s6 b% P' z) s- [0 P( P  _admin_socket->register_command("log flush", "log flush", _admin_hook, "flush log entries to log file");6 E$ o9 `  Y" |
  _admin_socket->register_command("log dump", "log dump", _admin_hook, "dump recent log entries to log file");2 J1 x' \* W# m
  _admin_socket->register_command("log reopen", "log reopen", _admin_hook, "reopen log file")
5 c6 ]4 p8 v' X, O6 z
1 t) t# z, W8 d1 `' [0 |0 D首先定义了个CephContextHook,注册该Hook也作为一个参数传递进去,这个register_command比较简单,就是建立了command字符串和Hook的关联,这个关联有何作用,后面会分析到,暂时按下不表。
4 F5 Z& m9 ^9 Q0 N
$ n6 ?$ A4 j8 W1 ]  E- Sint AdminSocket::register_command(std::string command, std::string cmddesc, AdminSocketHook *hook, std::string help)
+ S% z" }! m) V# m{
3 F' b( {, A$ _  u6 D" S  int ret;
! ^1 W1 |* U8 {8 z$ V  m_lock.Lock();8 h8 o$ J! |% j' }$ J( ?/ l
  if (m_hooks.count(command)) {
3 P: e* z6 E. e5 r9 W- e. H    ldout(m_cct, 5) << "register_command " << command << " hook " << hook << " EEXIST" << dendl;
/ c9 r8 j5 c3 Z/ U( \    ret = -EEXIST;
" {) ]$ d* _4 o( i2 l  } else {( Z3 ~/ P4 L5 q( q. p* d
    ldout(m_cct, 5) << "register_command " << command << " hook " << hook << dendl;2 [9 ?% l/ |. c$ l' ~
    m_hooks[command] = hook;! c. @& P0 |' M& Y
    m_descs[command] = cmddesc;6 f0 _2 }, {9 y: x1 R8 ~  v% L
    m_help[command] = help;
& I' }3 j. d" Y: C* ~    ret = 0;, {( C* A+ ?( \$ ~! y' w' |6 e
  }
3 r; ~5 `: D7 X' N) T# W  m_lock.Unlock();
" ~9 x8 A1 j+ X0 J- q9 T  return ret;2 ?1 s: a% k+ t
}
% B. f8 O9 r8 R1 j) N2 ?0 I, Z# r$ z2 \
后面从注册部分的代码我们可以知道下面三个命令是等效的。
2 ~2 k3 ]$ Y9 p9 }2 f5 X" h' `! L
1 J* @& Y6 f3 t# {" M  s) v) ~2 qceph daemon /var/run/ceph/ceph-mds.bfudz.asok perf dump
* K5 ?3 f, X- c6 A. Aceph daemon /var/run/ceph/ceph-mds.bfudz.asok perfcounters_dump
/ o6 u% D- H+ S7 I# Q* h7 tceph daemon /var/run/ceph/ceph-mds.bfudz.asok 1
; g$ {" c$ b* h2 }& u  @还有下面三个命令是等效的:
% v' y6 \6 ~. f5 j0 K% L3 e
$ W. g+ M- n& X% q: Aceph daemon /var/run/ceph/ceph-mds.bfudz.asok perf schema
# n" m& g& g8 O  zceph daemon /var/run/ceph/ceph-mds.bfudz.asok perfcounters_schema
  A' c. t" R7 g3 v+ Z) R9 Bceph daemon /var/run/ceph/ceph-mds.bfudz.asok 2
' Q1 h4 \( p  j! a% Y ceph-osd/ceph-mon/ceph-mds有一些自己的独特的注册函数,大家检索register_command可以检索的到这些独特的命令。
8 r% [2 c  n! t2 A2 y& t1 _! H5 p9 u: t" T2 Z! a

( t+ g- C4 n. u1 X* _/ Y, ^! K这个AdminSocket 对应的线程,在common_init_finish函数中负责创建。
# F- C* Z5 `+ z5 |" a, O* ]" z1 a8 y, t1 T4 i
void common_init_finish(CephContext *cct)& I# H8 j6 R: q3 _- D6 G& g3 f
{. Y/ d8 k1 S& |/ n9 F) u8 d
  ceph::crypto::init(cct);9 I# ]5 f+ k. n* n) f" e# k, F! T0 c
  cct->start_service_thread();( b( G0 l: e; O# z4 D5 G3 y' `
  if (cct->_conf->lockdep) {
; A* q4 t2 r3 {6 A- R    g_lockdep = true;
( s+ o$ R# D/ H8 |& @, B    ldout(cct,0) << "lockdep is enabled" << dendl;9 |0 S+ T9 n" _) w0 f0 A$ x+ n3 O/ X
    lockdep_register_ceph_context(cct);9 e# S& P, p% L  Y
  }6 O( \8 p2 i* r+ j( @/ r
}$ E# E8 x5 d5 ^/ P
而在start_service_thread函数中会调用adminsocket的init函数:
, o$ {" b+ {! s% P& q+ m) }
# ^' k$ D% M: G: P6 D5 Ovoid CephContext::start_service_thread()
4 y3 [* E+ B( E8 _{3 o% Z8 n8 F; x6 ]" \1 ~5 z' m
  pthread_spin_lock(&_service_thread_lock);  I7 ~' D0 `4 }  Y9 n" J; ~
  if (_service_thread) {+ ~4 ^2 @0 T2 T/ ]
    pthread_spin_unlock(&_service_thread_lock);9 n! N4 N% }7 i, T% m) L
    return;! h6 \" F3 K+ Y9 P) ?! o1 N$ `
  }8 p) l6 ~) n1 @
  _service_thread = new CephContextServiceThread(this);
& t6 g# F( I8 }& t9 n  _service_thread->create();
+ }. Z% V8 F4 H. ~) v3 y  pthread_spin_unlock(&_service_thread_lock);" q% l# E! g% G
  // make logs flush on_exit()
9 j' k6 L. w; V" o+ A+ l  if (_conf->log_flush_on_exit)7 a) |  j, O5 f8 _: E) ^  q6 O
    _log->set_flush_on_exit();
2 Q) \8 c! I5 P: E, [6 n  // Trigger callbacks on any config observers that were waiting for3 }) [9 j5 L3 V' N: N/ ?
  // it to become safe to start threads.
6 x5 _3 W( P3 j; K3 S, ^; n/ _  _conf->set_val("internal_safe_to_start_threads", "true");
8 `; ~8 a( `' h2 J  _conf->call_all_observers();$ X! h# E7 `, s- o
  // start admin socket
1 f2 ?1 \% g0 G3 t% |1 Q  {  if (_conf->admin_socket.length())
& t& r/ v0 X4 J! M5 M* _3 M    _admin_socket->init(_conf->admin_socket);
$ \' ]* H& ^2 ]}* |; L  ]6 g" G
" H- @9 G) g2 n: g0 ?
接下来可以分析下admin_socket的初始化函数init:
# w4 F4 {9 L: b  n( O- S5 v( ]6 Y  A- b8 E6 ?; d$ j2 R- l, G
bool AdminSocket::init(const std::string &path)( C- J( ^; C& M- V, y
{& e$ p" h( R, D. d! ?
  ldout(m_cct, 5) << "init " << path << dendl;- O- i2 q0 b  `/ a! J. y& D
  /* Set up things for the new thread */; n; Z! x8 F# p2 a- J
  std::string err;
, S/ Z1 v/ [' e/ F9 k/ b) ~5 L  int pipe_rd = -1, pipe_wr = -1;+ @6 y' C. u; _# p! X, w7 c
  err = create_shutdown_pipe(&pipe_rd, &pipe_wr);) K, z+ {) N) M* [% o1 \
  if (!err.empty()) {
8 h7 y# ]  }& t7 Q/ p3 H    lderr(m_cct) << "AdminSocketConfigObs::init: error: " << err << dendl;
6 h8 F7 _1 w  w4 h; s    return false;# F( M8 ^* S% X+ b6 c
  }
. w* X& O2 a% ?6 a' D  int sock_fd;- u$ L( k; r. u0 `
  err = bind_and_listen(path, &sock_fd);$ k$ {: }0 Z& M" z* d
  if (!err.empty()) {
; ~/ m6 M5 P3 O. K' Q, d    lderr(m_cct) << "AdminSocketConfigObs::init: failed: " << err << dendl;, `) L6 o: e1 `! \) \
    close(pipe_rd);! R# \, i5 M$ \0 s& U" Q3 m8 k
    close(pipe_wr);* X; N% I4 _8 ?9 J
    return false;" V% e  b4 @: l% `: ^
  }+ w% v1 k5 z! f
  /* Create new thread */# ?6 ~' L, c1 r# e: O
  m_sock_fd = sock_fd;
8 }. s4 y# O7 L4 K, q5 \  m_shutdown_rd_fd = pipe_rd;) s$ _& P, y: U
  m_shutdown_wr_fd = pipe_wr;
* h5 Q( r' j- Y! J( b  m_path = path;
' f6 R# P0 o' L" j5 o6 m! h$ S  m_version_hook = new VersionHook;
% D# W3 ~+ D: Y+ H6 q: j4 f* P  register_command("0", "0", m_version_hook, "");5 e& u! G0 X0 u9 f
  register_command("version", "version", m_version_hook, "get ceph version");+ w. P, i# ~+ Q" s) D" a( _1 e
  register_command("git_version", "git_version", m_version_hook, "get git sha1");
/ r6 i" \+ [! _7 O% q, ]  m_help_hook = new HelpHook(this);
+ u  f+ N6 D( O8 {/ _" y; |  register_command("help", "help", m_help_hook, "list available commands");
0 _  s; b. A/ s! w  m_getdescs_hook = new GetdescsHook(this);$ g9 a( O3 L2 R- e; F" B* o6 d6 f8 X
  register_command("get_command_descriptions", "get_command_descriptions",( _/ i* @! j  Y. q! l' `
  m_getdescs_hook, "list available commands");8 k, B! y2 O3 s$ s# b2 c
  create();% c7 U3 h. E* [& o  b- v- L* X
  add_cleanup_file(m_path.c_str());
/ S# E+ u% U, H9 k" W  return true;7 r% Z( _& b8 I- J8 l
}
8 U% I: A0 l; X- j4 d8 _6 K) Y8 N, E  Y( \. X
首先是创建了管道,读取端的文件描述符记录在m_shutdown_rd_fd中,写入端的文件描述符记录在m_shutdown_wr_fd中。- F4 X$ V  v# x/ f* I) s! a* U# L& n
从变量名字也可以看出,该文件描述符的作用是收取关闭信息。因为adminsocket一旦创建,必须能够通知到该线程及时退出。
, z6 [& V8 l: J/ s3 y1 O退出的事情会写入管道的写入端,而线程会通过多路复用接口,监听读取端,一旦发现m_shutdown_rd_fd中读出内容,线程就知道,可以退出了。
3 F: f9 K' k! @* i7 W# R, v. }按下不表。
7 c. N0 D& o7 H! |7 I: n" d% _* v" \2 O- @. i  y6 Q
AdminSocket最重要的是监听发过来的请求,它是用socket来实现的,初始化在bind_and_listen 函数:
3 n, A. g" l) a2 Q9 s3 \& w2 ?, I, y. D" @' ]5 R9 g2 Q
std::string AdminSocket::bind_and_listen(const std::string &sock_path, int *fd)- t4 Z6 E9 y+ r
{
. a& B* f: e. D4 e  H# K  ldout(m_cct, 5) << "bind_and_listen " << sock_path << dendl;6 S6 w9 a" x  z4 p

9 [* q' g1 a/ V5 j  struct sockaddr_un address;
. j/ a2 x6 {0 {! b% n7 v0 k: K  if (sock_path.size() > sizeof(address.sun_path) - 1) {8 O0 {# k2 u$ H0 i/ u
    ostringstream oss;
/ T7 a6 |! v0 P5 P, B    oss << "AdminSocket::bind_and_listen: "
0 E% v( n3 b& D5 f& D- t. _  f    << "The UNIX domain socket path " << sock_path << " is too long! The "9 H1 y! L1 j# E
    << "maximum length on this system is "& K2 i( [* }2 C/ w
    << (sizeof(address.sun_path) - 1);0 n8 w1 x+ q3 w; t5 L, @
    return oss.str();8 h4 l9 l* L4 N0 B" v. i( g% _
  }2 Z* E, B/ E; V& o; h
  int sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);8 t8 V' O7 H) h8 W% P* r" l: j
  if (sock_fd < 0) {9 J% W, h7 f$ J, h
    int err = errno;
; S; [) s* k/ o: U6 I7 J    ostringstream oss;
# f& b0 ~# B0 d    oss << "AdminSocket::bind_and_listen: "
5 F! i5 O0 R) S5 y    << "failed to create socket: " << cpp_strerror(err);& w/ |$ H" e7 O. h$ k6 L+ @8 c2 {
    return oss.str();
- v/ s9 e- l  n1 {# `0 U  }
, r6 O+ ^1 _/ W- d. H$ \* ~4 Z  int r = fcntl(sock_fd, F_SETFD, FD_CLOEXEC);# }- \  e9 C2 o+ c/ c
  if (r < 0) {
  B. {  |: F5 l. O5 C    r = errno;
  H# n9 s1 r4 [3 i2 @    TEMP_FAILURE_RETRY(::close(sock_fd));
  O$ X/ D' n! N+ w# y    ostringstream oss;
) V+ G) p/ a, d) s* Z! G+ ]- _& s  t    oss << "AdminSocket::bind_and_listen: failed to fcntl on socket: " << cpp_strerror(r);' y2 b; V+ _- Y2 h8 L+ i
    return oss.str();+ u- @/ q6 w' Q; U! x1 N
  }
  }9 I' S3 G5 a! ~/ a4 d  memset(&address, 0, sizeof(struct sockaddr_un));8 V7 F7 ^2 @9 g+ d4 b
  address.sun_family = AF_UNIX;
9 Z3 H# h; P7 _! Q  snprintf(address.sun_path, sizeof(address.sun_path),
6 e+ x; ~1 T! E/ ?' n3 k% l" u     "%s", sock_path.c_str());9 D0 |$ U2 n1 {) I/ F
  if (bind(sock_fd, (struct sockaddr*)&address,
- ~. q* l6 w4 W  }     sizeof(struct sockaddr_un)) != 0) {
+ l: r: v$ O* P# \3 m/ x    int err = errno;6 Y% w7 e5 i( @
    if (err == EADDRINUSE) {
% @$ r  ?& L: N$ G/ t      AdminSocketClient client(sock_path);- L  u1 R9 ^  s7 [4 V. Z/ F: n
      bool ok;1 I/ T/ i9 g; S
      client.ping(&ok);
  r. O& Q; g( p. L2 _0 |* i. u      if (ok) {3 v! [- J% l( Z5 u4 y4 L! ^* l
    ldout(m_cct, 20) << "socket " << sock_path << " is in use" << dendl;
# ^- D- Z, ?0 h* {5 j    err = EEXIST;
6 a+ U+ t% C) S8 p+ _      } else {
1 o1 c4 }6 \, e    ldout(m_cct, 20) << "unlink stale file " << sock_path << dendl;, V: c! g/ o; ?- R
    TEMP_FAILURE_RETRY(unlink(sock_path.c_str()));, |+ S4 E: N4 Y7 `1 d( E+ p
    if (bind(sock_fd, (struct sockaddr*)&address," b; d. R9 Q  [/ K  D! j
         sizeof(struct sockaddr_un)) == 0) {" W. Q$ m2 @& R  H9 y
     err = 0;" u+ g- z2 @5 w! K" L8 Q
    } else {, z3 c& |7 x- _; e& H5 t  W- E
     err = errno;& V6 a3 l/ J  y
    }% \( e: J. Q$ ~! A: G& D; y' K& h
      }5 `: H! ^. Q+ K0 @1 m: m) o' m4 M( z
    }% \! B. y  W: f7 v9 [/ I6 q
    if (err != 0) {9 ^) c) ]2 S8 p. p, x$ R6 t& V
      ostringstream oss;
+ a* ?* i' s6 I% X" w% q1 S; k      oss << "AdminSocket::bind_and_listen: "
( C7 l$ A/ B/ Z  v% ?/ Y+ J( c     << "failed to bind the UNIX domain socket to '" << sock_path
5 S9 H2 u: J+ h/ T5 c     << "': " << cpp_strerror(err);! {0 Y3 ]8 S7 b9 Y! L5 T
      close(sock_fd);
& X# o0 n( }9 @" d( Y8 O      return oss.str();
- l; J5 d+ q$ E2 c8 x) ?1 i& f    }8 Y6 h! j8 O# k! D! j0 I
  }! x& ^, @* Q  B" s8 ]6 z
  if (listen(sock_fd, 5) != 0) {  {+ A0 {, p+ U5 U1 Q- G& t
    int err = errno;
% s: Z9 M7 E) T/ E+ }1 d! t7 G    ostringstream oss;- U: q$ O7 n( R% C
    oss << "AdminSocket::bind_and_listen: "$ U7 v( k6 w8 p# U# X
     << "failed to listen to socket: " << cpp_strerror(err);
% }& r4 l3 z' B9 {7 V' G9 V    close(sock_fd);
  B" k0 M& X$ [! l# G  P    TEMP_FAILURE_RETRY(unlink(sock_path.c_str()));# w- Y8 R- n- z- H7 ~
    return oss.str();
! V5 ^: m& L; {6 f8 _+ C" T  }2 i: p* j! W' v* J
  *fd = sock_fd;
2 B. l0 s. ?. b" A$ s1 I& b0 J" ?  return "";
( Z) T" r' `% U}3 ~0 O3 x- l5 H4 U0 x
2 ?' D6 L9 Q1 }$ ~
这个函数做的事情并不难理解,做的事情比较老套:
/ {2 d9 U$ t  p+ t/ J  N1 创建一个socket,该函数需要一个入参,指定socket路径名:
% {+ O6 E- w& ?% h2  bind
/ K" s+ z( F$ _' E  u; B: z, _0 I2 H3 listen
$ S- _4 W7 x/ |0 U, y1 \而传入的路径名是这个
6 m# F' a- b5 u$ h1 kroot@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok config get admin_socket' i3 A) Q/ B6 F8 n
{ "admin_socket": "\/var\/run\/ceph\/ceph-mds.bfudz.asok"}
/ R3 ?. E2 p, P. O: ^socket建好之后,因为线程还没有创建,所以至今还没有accept接口 。然后我们再次回到init函数,该函数又注册了几个函数:% c1 {4 a, ]2 X0 k

! T. |' W. {! l! K7 ym_version_hook = new VersionHook;, a% b; r, g9 R: ~. t) I
  register_command("0", "0", m_version_hook, "");8 I# H9 E( z, g
  register_command("version", "version", m_version_hook, "get ceph version");
, L0 O0 ], ]' w  register_command("git_version", "git_version", m_version_hook, "get git sha1");
2 j" W% O1 ]9 |- M2 X% P- I  m_help_hook = new HelpHook(this);& l! Q5 d, y( h9 A. U1 Q& G
  register_command("help", "help", m_help_hook, "list available commands");
6 _$ R  D* w5 c0 N" O- S  m_getdescs_hook = new GetdescsHook(this);
8 v2 {3 y& c! ]* O5 i6 C' p7 h, R  register_command("get_command_descriptions", "get_command_descriptions",
3 p, t5 |' K0 \2 G2 T9 t6 G  m_getdescs_hook, "list available commands");" n9 g; \2 k, N2 ~
这几个函数的重要性并不大,基本是用来查版本信息的,如下所示,按下不表。% v6 `6 F0 e, X1 v3 Y# q$ m  d
  v2 B! H  K( f6 [! ^
root@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok version" c$ `5 ^2 E  d
{"version":"0.67.9-222-g014b35f"}
5 l3 y4 u3 ]* M6 U- Broot@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok git_version7 g2 J1 u! B. e! I1 n" M, y
{"git_version":"014b35fc1ee0a1ad1f699a3705f3481a88614d36"}
. w" O0 g8 z7 G( v9 Q, xroot@test3:/var/run/ceph#- n' Z( c3 [0 u. `5 v% ]

8 Z: x0 w. j& Ginit函数最后调用了create函数。create函数是老朋友,前面分析Log的时候已经提到,对于Thread这个类,做的事情无非就是创建线程。关键内容是,线程执行的函数是哪个?
, B- h3 o: _. ]; @: D" d" o和Log一样,是entry函数。AdminSocket类也有entry函数,该函数是AdminSocket 对应线程指定的函数:7 ^: B" ]- {6 H: x9 Y3 a/ k+ v' c5 Z+ a
6 ?( O3 H5 A# Q5 ]+ i- W8 h& N8 x
void* AdminSocket::entry()) u" v$ _) N0 V3 F9 R
{
* R( s, K0 S3 p  ldout(m_cct, 5) << "entry start" << dendl;
+ Y9 w' @2 f- a8 T) V9 D$ N  while (true) {  w1 O6 ?# `0 Q4 n% B8 H
    struct pollfd fds[2];
$ l' ^% `2 F! ]/ ?7 `    memset(fds, 0, sizeof(fds));; C1 s4 u$ w5 x
    fds[0].fd = m_sock_fd;
' R$ |9 e0 L1 d! Z$ O  a. p7 n2 r    fds[0].events = POLLIN | POLLRDBAND;
. r0 v- c: n3 D& w6 h    fds[1].fd = m_shutdown_rd_fd;
2 Y/ b2 N$ F1 @    fds[1].events = POLLIN | POLLRDBAND;: S  {# \+ }: ]% p3 F& P( V
    int ret = poll(fds, 2, -1);+ I2 F+ X% s% J+ v5 \
    if (ret < 0) {
7 ~& P0 ~% Y% T) q' l      int err = errno;0 u* g) ^4 T$ i
      if (err == EINTR) {, {: y% H/ e$ r+ O' p
continue;
' \% l8 ^- E/ w  i6 P0 a      }
& L2 L! t, j2 c* a' [) x      lderr(m_cct) << "AdminSocket: poll(2) error: '"1 C) z- e8 I, o. R
  << cpp_strerror(err) << dendl;* h" ^" p  Q. B( N0 K2 X9 T
      return PFL_FAIL;
8 ~2 b$ I4 k$ V! d$ Q: ]% t8 w4 A    }
( l  ]* V9 o0 l: a1 R( x! i  |* c    if (fds[0].revents & POLLIN) {" n# |/ {) N! L- V
      // Send out some data  R# v6 m$ p; X% F/ e3 Y6 T9 A
      do_accept();
/ U0 C# [$ C) N3 V3 {    }: ?; U: j- |' z" ?
    if (fds[1].revents & POLLIN) {
# n" D2 P  b* R      // Parent wants us to shut down) U* ^* Z& {! {. a) |/ J/ |
      return PFL_SUCCESS;* [/ A5 y* }6 U# B5 A7 K
    }5 A2 X+ @, ?6 T9 w# N
  }/ A9 R( z4 H! L
  ldout(m_cct, 5) << "entry exit" << dendl;4 ?, f+ e, l! L/ A6 m1 r+ b
}! u# M5 {+ M+ v6 E( W% Y
+ s0 n' k) T* Y( W
这个线程函数比较简单,它监听socket fd和管道的读取端。
1 L1 y) m- ?( H& S& G1 H1 管道的读取端负责管理何时退出
& V: |9 |3 e) r+ ~0 v- v2 socket fd 负责监听用户发过来的指令。5 Z# V. [5 p- j5 O$ O

# G4 S; E5 h) V$ {: o5 `  W处理用户发过来的命令,是do_accept函数干的事情:
: H( m; U1 @' n7 U; {6 ]  I9 m: w+ P9 n" C) k2 F+ `
bool AdminSocket::do_accept()! m2 a: E/ m( u# K3 y
{
! L5 t8 Q& X5 V  struct sockaddr_un address;
5 O4 U! W* v; N: ?  socklen_t address_length = sizeof(address);! H  A1 g% e3 Q/ W4 A  N1 J
  ldout(m_cct, 30) << "AdminSocket: calling accept" << dendl;9 z  M$ ]* T) [' n7 \6 Y
  int connection_fd = accept(m_sock_fd, (struct sockaddr*) &address,: p) ?. f: \% U
             &address_length);
/ W6 Y0 u4 y- s7 k+ k( G  ldout(m_cct, 30) << "AdminSocket: finished accept" << dendl;
' W/ |# ]6 l8 \* x0 K$ E  if (connection_fd < 0) {' \" [2 d, t, s3 {
    int err = errno;! c/ z% E3 q& a( v' D
    lderr(m_cct) << "AdminSocket: do_accept error: '"
+ X) Z3 B" P* o* ?, S7 q             << cpp_strerror(err) << dendl;9 \2 |( o) _. j# m5 s6 ]5 ^+ l
    return false;
8 M- x- @' s. z5 ?- J  }
/ y. |! E/ a5 b/ |5 Q% j0 L+ G0 K1 C$ J# X3 O: }
  char cmd[1024];
8 M! R/ Z4 n  [; f$ S$ _  int pos = 0;
. H0 J3 k6 M5 F9 d  string c;0 N1 N: s4 A1 S/ h  ^
  while (1) {
# r4 ~8 F* u* M6 n4 M: m4 A; f    int ret = safe_read(connection_fd, &cmd[pos], 1);+ w0 n0 J7 R! m% r
    if (ret <= 0) {
: Q; r) b3 U7 s) y( b+ u      lderr(m_cct) << "AdminSocket: error reading request code: "% a, r' I/ k) i3 q
         << cpp_strerror(ret) << dendl;/ m2 W3 j' J% {  y7 U! ?# Q& \
      close(connection_fd);) q& Q/ L) G1 w/ M. A
      return false;
, T: l- \5 |+ J' l0 S    }  z3 Q$ Q1 `" D6 L; I6 q* n
    //ldout(m_cct, 0) << "AdminSocket read byte " << (int)cmd[pos] << " pos " << pos << dendl;
' B+ r$ ]7 o% u2 Q2 N$ b2 q( S    if (cmd[0] == '\0') {
; [" h0 W" k$ R2 J# Q      // old protocol: __be325 _4 k6 D$ o* O, c% m
      if (pos == 3 && cmd[0] == '\0') {
$ w" P0 W- z3 `& y    switch (cmd[3]) {
+ L( F  N" M+ U* \$ r' ?    case 0:
/ W" e/ Y; g- E# L. W     c = "0";
) H0 G* L( U' D- B6 j/ t5 ]& T, n     break;# i1 j! A/ \  n" k
    case 1:+ U/ V" t7 u1 c/ }& @% }' Q
     c = "perfcounters_dump";) q: p: J( D, v
     break;
  }, e& y- r4 j0 T3 I, R    case 2:
/ e4 ~" @- [) k2 D2 T) z- i     c = "perfcounters_schema";) ]3 N% U4 y6 y" [6 G+ ]' a
     break;+ c6 ^0 G: I6 _/ C" P6 r
    default:
( ^- _0 i/ D$ g, Q/ h* E     c = "foo";: z/ c9 [/ i) E& l! ^! t
     break;  y1 L$ A! |6 N  \7 r' j
    }2 j: c/ C9 u( u$ f, X% _
    break;4 T+ V4 m' Q9 v) P; z
      }, {. u( o: i, F& o) G, k
    } else {
8 u& X5 }) n# U8 z6 D      // new protocol: null or \n terminated string, c& X) h2 H: ^- l! J$ l2 c
      if (cmd[pos] == '\n' || cmd[pos] == '\0') {6 t/ p) X8 t- @  W
    cmd[pos] = '\0';
- v' o- W2 V' u    c = cmd;# G$ }. g, G2 A0 l7 z
    break;
& f0 q+ n- C* k7 @      }
2 }% ~% Q: \  n; s! C    }
& O- X3 j  `* s# r9 l* N$ ~& Z, a    pos++;
1 x; q8 z6 [/ Y. U  }
( n$ w2 s/ N5 d7 F
) }+ a% ]5 [$ h. `  bool rval = false;
2 `  R. e' f" U( e: _& N( Q( [2 [8 T& Z3 g# P) f$ u
  map<string, cmd_vartype> cmdmap;! M8 @. `. R) m! R2 Z$ w
  string format;
! Q" d, A9 v4 z) a  vector<string> cmdvec;& N* u( d4 |, ]9 n
  stringstream errss;
8 d9 g! [5 h$ }  cmdvec.push_back(cmd);; Q8 b) |: N) s, Z0 n+ c! t/ [
  if (!cmdmap_from_json(cmdvec, &cmdmap, errss)) {3 H# l7 D( `: ~5 x! M# b
    ldout(m_cct, 0) << "AdminSocket: " << errss << dendl;
! a2 W2 c- P: D* d# r1 e+ u8 ]0 X& {6 R    return false;$ P: n5 Q. f/ y1 T# n: B
  }
2 B. X1 |8 {, j& X- \- f  cmd_getval(m_cct, cmdmap, "format", format);
" [* [% n$ F* f  if (format != "json" && format != "json-pretty" &&
' Q8 C. B1 b4 z/ a2 m4 ?      format != "xml" && format != "xml-pretty")9 q- {3 P: b) i/ n
    format = "json-pretty";" {! C. V, U. B0 B8 ?+ c
  cmd_getval(m_cct, cmdmap, "prefix", c);/ f& X0 e+ V6 v4 m4 h
  u7 y& a! c! \$ b( H; w: O! C
  string firstword;$ K$ h0 w* \, N2 q2 T
  if (c.find(" ") == string::npos)& W. s3 X& S- [3 e+ f
    firstword = c;' L( s- c7 _5 o
  else2 `0 [1 A6 L  O
    firstword = c.substr(0, c.find(" "));( Y3 j/ M1 T. k4 J9 n' }* n& X$ e/ j4 \

  y+ L( j8 S) h( J# z3 S6 d  m_lock.Lock();
6 L1 G$ p& h" F( A$ G  map<string,AdminSocketHook*>::iterator p;7 u5 {2 v( u7 h- d0 j' P2 T
  string match = c;
2 y. d$ f8 f  a- _* J  while (match.size()) {
8 b, s$ s5 a. u7 O9 E    p = m_hooks.find(match);5 y0 y2 t) t9 W; R2 j. V1 W3 k
    if (p != m_hooks.end())
3 `" A! r3 M) A3 U% r0 r      break;
3 y+ W" h4 r' N% I4 G    # a4 }4 D. K) k
    // drop right-most word$ S% o, E$ @  j9 B% d" p
    size_t pos = match.rfind(' ');
1 _7 l% p1 v: [0 i. ?* S    if (pos == std::string::npos) {( i, T6 f5 c  N# }
      match.clear(); // we fail
! E- g; Z' h3 \& W      break;
% N- Z9 [; V0 |  C, L" {+ P$ h: [    } else {' Y; O' m' c/ x# S
      match.resize(pos);; B' m# Z% l- m" u: _* k& Z9 E" o
    }
5 w' M3 H' z; i( F/ g5 w  }
2 b4 X5 h! Q7 p7 `) T3 E# o/ T# D/ ~7 T# g
  bufferlist out;
0 }1 U1 G, N/ J& K9 ~  I4 Y  if (p == m_hooks.end()) {
; E8 Y& A% E4 ]    lderr(m_cct) << "AdminSocket: request '" << c << "' not defined" << dendl;
# c+ k3 c: M( w& _1 p  } else {
3 V! b1 F! R* ~  Y. m7 X9 s    string args;
" w: l$ {; i9 A# s* ~; T( a3 N6 ~# \    if (match != c)7 ^. f7 b! G: J
      args = c.substr(match.length() + 1);
. i* q% _* ?) z    bool success = p->second->call(match, cmdmap, format, out);- i0 M) z- n4 A8 Y, X
    if (!success) {- X: x5 Q& e: x7 Q* f
      ldout(m_cct, 0) << "AdminSocket: request '" << match << "' args '" << args2 E- O) z$ M& G6 C
         << "' to " << p->second << " failed" << dendl;
7 o) w* d/ [0 t8 e" c      out.append("failed");1 W9 d( W- a$ `% x$ u3 k  N
    } else {$ _+ T3 ?7 q6 k8 x& e
      ldout(m_cct, 5) << "AdminSocket: request '" << match << "' '" << args
5 L! V( \( }1 h3 F1 C. Z         << "' to " << p->second
! b- S9 i' k6 G' m1 {         << " returned " << out.length() << " bytes" << dendl;
$ d7 r: b! @; O. A7 h) ~; W    }
* e' Q' ^/ k  N$ |/ s    uint32_t len = htonl(out.length());
( i" n& D9 M( a/ k7 _% I$ S6 W    int ret = safe_write(connection_fd, &len, sizeof(len));
& Z) e) G. G2 T8 Z2 y" ?: f    if (ret < 0) {
% }) X- b3 q: ^4 U7 {7 e* P" h& l! o      lderr(m_cct) << "AdminSocket: error writing response length "
9 ~; H6 S4 t/ b1 @. b' A( x& E4 s         << cpp_strerror(ret) << dendl;
4 @5 N  F' Z$ Q    } else {5 ~1 F, P' j& d  b- g0 o% {3 Y
      if (out.write_fd(connection_fd) >= 0)
( P/ \: g  Z8 p9 s* i    rval = true;
8 Q9 s* W3 U' d8 n) d! Y+ o    }
% m+ ?5 M6 f9 z0 L3 p  }3 E# Z& O# x) U7 {
  m_lock.Unlock();0 B$ d& c, l' R0 L  ^. q
8 u+ N  \2 j" U* T/ X8 F7 B
  TEMP_FAILURE_RETRY(close(connection_fd));
. s( v* P  t+ E: I% M  return rval;2 Q7 J3 n1 r$ k. T
}
  d# z' B0 _7 V- o7 s) P% D( g# d8 ~. x
这个函数有点长,但是并不复杂。简单说,如果有个client尝试 connect ,该线程就poll就会感知到,然后进入do_accept函数。8 ~# w% E4 ~- a; R
do_accept首先执行accept,和client 搭上线,然后开始通信。
! y% B/ V3 M$ s2 ?# k- l/ r, J2 }+ w7 I# j
safe_read负责 读取客户发过来的指令。前面已经提到过,AdminSocket支持的命令是有限的,初始化之前都已注册过了。5 h6 Y7 u; }( h( o
如果client 发来的指令时注册过的指令,就见招拆招,返回相应的结果给客户端。4 `+ O2 F  D0 a$ q

3 U- x4 G5 q, ^+ c; y+ Z# j! @2 A每一个命令的字符串,都是和一个AdminSocketHook 的类型关联的,但是一个AdminSocketHook可以对应多个command9 P! _8 n. C  z7 f( w% m
% M4 C, N0 m2 a! F
std::map<std::string,AdminSocketHook*> m_hooks
* j1 U/ q) ~5 B比如说 config show  / config get   / perf dump对应的多事 CephContextHook,前面已经提到过。9 N  c8 z& W0 R/ K" Y7 C  _- u

3 q9 t% j( }+ t% @, m8 P见招拆招的函数,就记录在对应的Hook上:
" [. o9 e0 {5 t3 C4 r/ ~0 ^$ p5 t- h- q, y
class CephContextHook : public AdminSocketHook {, l  |2 X4 ~5 s% `/ U  V
  CephContext *m_cct;5 b, ]+ h2 c) y# B
public:2 G) j3 L( x# c+ p- S5 ]4 |2 X
  CephContextHook(CephContext *cct) : m_cct(cct) {}
% A# K" I! x# A  bool call(std::string command, cmdmap_t& cmdmap, std::string format,
2 y9 }' B, q# f; k2 ?& |) ~# }7 [8 M2 U6 i   bufferlist& out) {1 J  `; K8 J& r& F' M( M3 k7 I
    m_cct->do_command(command, cmdmap, format, &out);9 C! t9 f. m# P
    return true;
/ y- ^0 @" U0 v2 N  }( [5 ^. e7 L0 O4 J  G  A, Q
};- R' ^( c% T0 E
( n: S4 C9 @9 L/ z* Z! ?. C

  h: h- n) y) U* M+ c# A8 Y% Fdo_accept函数中黄色的一行,具体是实现,就是对应Hook的call函数,对于CephContextHook,就是这个类的call方法。3 J/ z; L! a, o8 y( F% W

4 j' c. ~/ ^+ D4 e下面我们看下CephContextHook的call方法:即它的do_command函数:) v: X5 Z/ v" i# N
: U7 ]. I- g& V' H6 U8 b+ v
void CephContext::do_command(std::string command, cmdmap_t& cmdmap,$ h' o/ y9 h+ }  z7 g
             std::string format, bufferlist *out)
7 h* K# P2 k& P& F{
5 [( |+ p- R1 c8 m1 {) \" Y; {* I  Formatter *f = new_formatter(format);7 F5 U! h( |" y: ]8 b
  if (!f)) r6 o) r5 v8 x7 C/ b
    f = new_formatter("json-pretty");$ Y4 f! h( h( i% d8 o" B4 A4 {5 m2 [  Y
  stringstream ss;
7 z7 \" Y" }! w  for (cmdmap_t::iterator it = cmdmap.begin(); it != cmdmap.end(); ++it) {7 D5 x: S4 J- z6 q3 t2 y* q
    if (it->first != "prefix") {7 V/ `0 ^6 K& t! K3 {
      ss << it->first << ":" << cmd_vartype_stringify(it->second) << " ";1 q! n( P% F+ Z% X  e& r" Q
    }
: ^1 `; n& V% @3 }" K  }4 l6 T3 y# V4 Y2 O6 I" o
  lgeneric_dout(this, 1) << "do_command '" << command << "' '"
" {' ~8 Z6 z: P4 |) f( ?% C             << ss.str() << dendl;5 G) k  |3 Q2 F3 \1 y( L& B) Q5 ?
  if (command == "perfcounters_dump" || command == "1" ||+ x) Z( T. b7 B! V& E* ?
      command == "perf dump") {
' f# h) J# @1 {5 _' d    _perf_counters_collection->dump_formatted(f, false);
" ~* U) A$ T/ w. w2 ^  }
# n+ K% ~$ v- f. K  else if (command == "perfcounters_schema" || command == "2" ||
1 }! x+ K' v+ p* `    command == "perf schema") {
1 S- J8 M0 A0 ^$ S4 N    _perf_counters_collection->dump_formatted(f, true);4 d$ u- \8 R; F  x4 X$ W7 |
  }
% m9 O& @1 }, H# N  else {3 }% U$ ~1 z6 m1 m& I; C
    f->open_object_section(command.c_str());1 Y# H% t$ t* J' k4 ]3 s
    if (command == "config show") {
( Y/ @/ |7 `- z3 |; ~      _conf->show_config(f);$ l8 f! E* S+ D& a' q+ ]9 x. L
    }
/ i# o. B" j; @' R( E    else if (command == "config set") {
+ x. j# _" ~. y+ W      std::string var;
. a- A, I" [* z! m. w. s' h      std::vector<std::string> val;: x9 U( g: R6 D  G$ b: |
( c3 @, L0 B2 z" V* B
      if (!(cmd_getval(this, cmdmap, "var", var)) ||
. [4 x: o8 ^* c          !(cmd_getval(this, cmdmap, "val", val))) {
" |3 C. V2 ], l  b* T        f->dump_string("error", "syntax error: 'config set '");
: w6 A; v7 }' X5 N( Z; G      } else {
" E% b2 {9 p8 s$ k* N7 L7 T    // val may be multiple words5 E  ^; D% h. S! A4 [
    string valstr = str_join(val, " ");
& \/ F1 `( m" {6 @; n$ X        int r = _conf->set_val(var.c_str(), valstr.c_str());- b" v  ^& e' i& W! T
        if (r < 0) {7 G$ E! U0 B3 n5 X
          f->dump_stream("error") << "error setting '" << var << "' to '" << valstr << "': " << cpp_strerror(r);% N) j! M" E/ c9 k1 `6 W, C; ?# @
        } else {" K& f/ N! ^2 b7 M. E, g7 [6 L
          ostringstream ss;
( n, ~* u2 a: \8 l! I          _conf->apply_changes(&ss);& D8 J1 s+ {& ~6 e
          f->dump_string("success", ss.str());, \, {; M5 p, ]5 C4 \; J8 f
        }1 D  S& `; Y: A+ X( A/ a
      }4 p6 m" {! m; p+ z; g
    } else if (command == "config get") {
( ?1 V; B% u1 n" i( i      std::string var;
& C$ l! U8 Z, f: @4 c0 Y      if (!cmd_getval(this, cmdmap, "var", var)) {" t' h+ v, `7 a# @
    f->dump_string("error", "syntax error: 'config get '");
# b! j$ k: X9 `4 {! Y$ N      } else {# e2 ^% k* I  B; J5 }' g' L$ K( M5 q+ H
    char buf[4096];
& Q+ a! I7 O9 B, y( K% N. n6 e    memset(buf, 0, sizeof(buf));, Y6 c& X8 {3 e) Z8 g3 b
    char *tmp = buf;+ \3 p; M/ t% `: P+ e4 \
    int r = _conf->get_val(var.c_str(), &tmp, sizeof(buf));9 {, c8 B" ~7 I" b( w1 r
    if (r < 0) {7 z6 y1 O+ y1 u  \8 X  i
     f->dump_stream("error") << "error getting '" << var << "': " << cpp_strerror(r);: s8 t0 \8 W$ c
    } else {, Q5 p) A6 |  U4 i1 U
     f->dump_string(var.c_str(), buf);
& u# ^' d9 L# |3 H    }
( [# p+ s; `3 ?( D      }% @$ {2 u3 R1 U: l0 i" o4 o$ j! X
    } else if (command == "log flush") {( ]% O# E0 H: C, ]! v; @
      _log->flush();5 |8 w, s  `) s
    }( f. ^8 ~2 Q: }4 c& T
    else if (command == "log dump") {2 q4 F# g, t. p& t9 h
      _log->dump_recent();4 h) v/ R0 X) {6 b- R( z
    }4 \) ~8 d+ W2 q. ?
    else if (command == "log reopen") {
8 H, _$ h! A  e; }! \$ n! }      _log->reopen_log_file();
, h$ F: ?3 q& \  R/ S# N' o    }. V" r& L* S$ m* g! ?
    else {
3 K6 S. X4 p" z      assert(0 == "registered under wrong command?"); & E" z7 q% j9 {* F0 e* P+ n
    }# A* B* n/ v8 G2 ~
    f->close_section();( i5 ?5 y7 ?( L" H( B* d
  }0 c7 U& g: x) T5 L: Z4 E7 ]( h9 J
  f->flush(*out);
# W/ B5 f! |7 F& h& t! _, B  delete f;* _8 r/ v$ _# r: \5 y- F  U9 O# ~6 B
  lgeneric_dout(this, 1) << "do_command '" << command << "' '" << ss.str(), L- N* j& B, M/ ?6 `
         << "result is " << out->length() << " bytes" << dendl;1 S; K8 y0 [: Y& n! ?* G
};5 L3 m, W' H  k2 S1 N
  毫不意外,前面注册的每一个方法,在do_command函数中都做了处理。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-6-12 02:02 , Processed in 0.019029 second(s), 23 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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