找回密码
 注册
查看: 7231|回复: 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
; _( Q) F0 q0 L/ fID WEIGHT  TYPE NAME           UP/DOWN REWEIGHT PRIMARY-AFFINITY 8 F; K$ ^3 w% b- T# y* H' L

1

主题

0

回帖

12

积分

管理员

积分
12
QQ
 楼主| 发表于 2018-9-29 13:15:53 | 显示全部楼层
任何一个成熟的项目,必须要提供出接口,就像探针一样,可以让我们探查进程内部的运行情况,进程不能是一个黑盒子。对于ceph而言,Admin Socket 提供了该功能。Admin Socket 不仅可以查看当前的配置,进程运行的状态,而且,还可以修改配置,获取log等。7 f4 X3 B" s! j: U5 W/ I$ c( n
# C1 U! x# ?0 T: x& ^
下面help可以看出,ceph给出了很多命令,来了解ceph内部的运行情况。
) v2 m7 e: m8 Y+ l& D- x: ^& froot@test3:~# ceph daemon /var/run/ceph/ceph-osd.4.asok help& B7 c9 M1 `' Z
{ "config get": "config get : get the config value",
4 b; B8 _2 u' c: [$ p. e  "config set": "config set [ ...]: set a config variable",
; g. B3 c& l- L7 K- L  "config show": "dump current config settings",: }9 j7 k( ?6 i; \% P: e) j
  "dump_blacklist": "dump blacklisted clients and times",
4 f& B- `, U' c3 y  "dump_historic_ops": "show slowest recent ops",
* i3 b2 h; A) s  k0 a4 o+ a! \& B  "dump_op_pq_state": "dump op priority queue state",
; S1 K, |* Y$ R& D: j  "dump_ops_in_flight": "show the ops currently in flight",
+ j0 y9 `1 Y, i: i+ k  "dump_watchers": "show clients which have active watches, and on which objects",
7 N: q! C  u: b- u8 D  "get_command_descriptions": "list available commands",2 o! O; `1 e0 @. x& B' w' N
  "getomap": "output entire object map",
/ W) e4 n1 \) o  "git_version": "get git sha1",4 H7 m( n. i9 X, v2 z
  "help": "list available commands",
2 t" G0 _2 y/ t" P1 i  "injectdataerr": "inject data error into omap",
" w. P5 s6 O+ g4 v& d  "injectmdataerr": "inject metadata error",
- D5 c! S* K2 y* Z3 s' i" l7 x+ c  "log dump": "dump recent log entries to log file",8 ~, j+ _4 e0 i; n( ^+ s  X3 h4 W
  "log flush": "flush log entries to log file",; W4 H  U5 D" X7 J
  "log reopen": "reopen log file",
3 ?; S" c$ M3 o+ N& r$ p9 v  "perf dump": "dump perfcounters value",' t) I6 j' Q: _
  "perf schema": "dump perfcounters schema",; ?3 v* e* l2 J0 w" r( X4 Z
  "rmomapkey": "remove omap key",
. X% h8 x& s. r% k$ ?+ r! b$ I  "setomapheader": "set omap header",
$ z* Y0 j/ I4 U6 `/ \# i  "setomapval": "set omap key",1 Z3 q5 q# x9 t( R
  "truncobj": "truncate object to length",
/ o( ~5 B* h' P' J- F- R: X  "version": "get ceph version"}0 P$ A1 ]- i' i4 A3 t
root@test3:~# ceph daemon /var/run/ceph/ceph-mon.*.asok help5 F7 d6 V: |' b- t9 W+ V& l" H
{ "add_bootstrap_peer_hint": "add peer address as potential bootstrap peer for cluster bringup",
. c. ?- }  X% |  w) ?! @' U* r  "config get": "config get : get the config value",
* |% @# T0 q6 \0 A* u# f) G- v  "config set": "config set [ ...]: set a config variable",
5 Y. g6 A: _. n9 d  "config show": "dump current config settings",
3 s8 c7 |$ ]$ `2 U# n7 k% v  "get_command_descriptions": "list available commands",5 W3 }/ H- c5 ]) s* E) l  l5 c
  "git_version": "get git sha1",  z0 q; F9 E5 p$ x2 _; I# B+ d/ D7 N
  "help": "list available commands",
$ @, ~1 u2 V2 l; A  "log dump": "dump recent log entries to log file",$ [- d) c  @' o6 s& t" k% S  D
  "log flush": "flush log entries to log file",, X5 {5 S0 I2 x1 R: ?' Q+ d) M4 L
  "log reopen": "reopen log file",
/ `) Z& d2 i! D4 X! Z" E+ \9 n. c  "mon_status": "show current monitor status",
. d  J7 S8 @5 H5 C9 e+ r7 F  "perf dump": "dump perfcounters value",
' D% t+ @+ S3 F  p( T" O* E/ E  "perf schema": "dump perfcounters schema",
$ K: C7 J4 K* t% `; p8 o  "quorum_status": "show current quorum status",
! K( T3 Q; i" r  "sync_force": "force sync of and clear monitor store",
! ^5 d# s# H( y4 y  "version": "get ceph version"}  W* z+ \7 [0 n! ^# u7 c
root@test3:~# ceph daemon /var/run/ceph/ceph-mds.*.asok help
6 Y( C7 d$ q3 V6 W7 W{ "config get": "config get : get the config value",
: K# D4 w, F4 m' B4 ^% D5 S. \  "config set": "config set [ ...]: set a config variable",3 U* |" u$ ~% H8 G* Z1 U4 u
  "config show": "dump current config settings",3 Y& P2 a/ R# }: p8 `; H
  "get_command_descriptions": "list available commands",
" N$ g: [# W- w. M8 B7 }. _8 Z  "git_version": "get git sha1",
3 Q8 U7 U, Y- I  "help": "list available commands",
: H2 S. r' u5 d- `( n# z+ C7 F  "log dump": "dump recent log entries to log file",
. |4 S- Z4 }, r' L( I  "log flush": "flush log entries to log file",7 h/ W8 ^. s) a
  "log reopen": "reopen log file",4 C/ ]; T2 Q) |6 D4 V9 ?+ @
  "objecter_requests": "show in-progress osd requests",: ^- f& B3 q4 D5 ?% U  T, W
  "perf dump": "dump perfcounters value",
# e+ h) ]* z6 B: K3 ~9 [/ X  "perf schema": "dump perfcounters schema",) M% h4 A  ], B6 Q& ?! e6 M$ y6 F
  "version": "get ceph version"}- ~* L8 i7 Y" x9 i: }( P

% ^/ j3 o0 _) `) C* q, j0 ~比如可以查看 ceph的各个模块的当前配置" B) w/ `& h  D$ k# |; _# V

3 V, [9 l2 ?5 ~ceph daemon /var/run/ceph/ceph-mds.*.asok config show4 `" _1 Q# T6 D8 d
ceph daemon /var/run/ceph/ceph-mon.*.asok config show& I1 r" o& g  U
ceph daemon /var/run/ceph/ceph-osd.4.asok config show6 J# G' \) A9 u: e
这个是怎么实现的呢? 这是依靠ceph的AdminSocket机制完成。
  w: H* |3 G5 c6 tCephContext中会创建一个AdminSocket对象,该对象本质是一个线程。ceph-mon/ceph-osd/ceph-mds这些进程都会有创建一个AdminSocket的线程,负责响应用户的探查命令。5 R0 X- O6 g( W5 A6 {. N" M
8 K( w' v$ K. W5 z: ^, S5 a
从上面的OSD MON MDS help的不同输出可以看出,他们支持的命令有共同的,也有各自独立的。, B, M' v  h, _8 p6 r8 }
首先是共同支持的命令有:
5 I2 K; W. o. a. d% I- H+ Rconfig show        显示所有的配置项
4 W  G. j7 F  Q. Lconfig get           获取某个配置项) f" _% q9 _) a1 b5 `. J" J
config set           设置某个配置项
/ @7 V# m; Z/ Y( f1 alog flush        将log 刷入日志文件
7 H" ]: M. n3 X5 G/ _) Klog dump           将最近的若干笔log刷入到log文件% \$ W3 x2 L( h
log reopen         重新打开log文件
! y+ @- T6 d5 V4 W, w! Tperf dump         输出统计信息( x- ]+ o& g. a& \
perf schema      输出统计信息的类型( X4 \3 t: K0 O
version             版本信息
# O6 ?* h1 D4 Egit_version   :    git 版本信息
* ?0 ?1 }1 E7 p9 U( _7 y5 W2 o, F3 H支持的大部分common的命令,注册发生在 CephContext的构造函数:
; ^6 X% n* w* e1 q  _admin_hook = new CephContextHook(this);
! S1 n! W3 K& k  _admin_socket->register_command("perfcounters_dump", "perfcounters_dump", _admin_hook, "");. k7 ?' y& G9 [
  _admin_socket->register_command("1", "1", _admin_hook, "");/ h& e9 X2 ^/ m% b8 W
  _admin_socket->register_command("perf dump", "perf dump", _admin_hook, "dump perfcounters value");# ]- f* L2 T3 v' W0 V! W
  _admin_socket->register_command("perfcounters_schema", "perfcounters_schema", _admin_hook, "");
' _2 k% V. G" ]- E5 K  _admin_socket->register_command("2", "2", _admin_hook, "");
9 b0 C0 W+ G( ?: _/ z: |9 L( k  _admin_socket->register_command("perf schema", "perf schema", _admin_hook, "dump perfcounters schema");
, n  N  N1 K( X7 m: I  _admin_socket->register_command("config show", "config show", _admin_hook, "dump current config settings");; d! O) p! @% [- r, X+ s2 U
  _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");
- I; g* O- ], I' r3 M  _admin_socket->register_command("config get", "config get name=var,type=CephString", _admin_hook, "config get : get the config value");* D- ]5 z5 S1 _. g2 U
  _admin_socket->register_command("log flush", "log flush", _admin_hook, "flush log entries to log file");
0 B# X+ C. T9 w( b. g6 F; W  _admin_socket->register_command("log dump", "log dump", _admin_hook, "dump recent log entries to log file");0 w6 e# I9 |7 L3 C2 H6 r" P4 L
  _admin_socket->register_command("log reopen", "log reopen", _admin_hook, "reopen log file")
/ n0 i5 e$ o7 `$ u; ?9 h) K- O; M0 X. O9 F/ \' i5 M8 f! G  C
首先定义了个CephContextHook,注册该Hook也作为一个参数传递进去,这个register_command比较简单,就是建立了command字符串和Hook的关联,这个关联有何作用,后面会分析到,暂时按下不表。$ z! w* {" ?0 j% H- n) K
7 `7 D+ m) A6 r
int AdminSocket::register_command(std::string command, std::string cmddesc, AdminSocketHook *hook, std::string help)
- c6 f7 i! O. Y" g8 `% Z{
( F' J* c! \; @7 c+ {  int ret;$ ?8 y. ]# t7 \. ?
  m_lock.Lock();) [( m3 ^7 @+ A. L8 T( Y
  if (m_hooks.count(command)) {
* z2 ?" a) ?$ `$ z# _2 j! Z    ldout(m_cct, 5) << "register_command " << command << " hook " << hook << " EEXIST" << dendl;# y, v8 L( T0 S9 R9 |2 k
    ret = -EEXIST;
" J% m) ~0 B6 h0 v( c& g  }  } else {
6 }8 G- ]7 f4 p7 ~+ v    ldout(m_cct, 5) << "register_command " << command << " hook " << hook << dendl;
* y6 x  f& I3 ?& Z) j$ `    m_hooks[command] = hook;) W* b) a$ b6 D
    m_descs[command] = cmddesc;* A9 J7 ^' q% L
    m_help[command] = help;
4 |) M, J, A" d( P! t; g8 p5 h8 f' b8 ~    ret = 0;- v( h7 X/ w# J' m5 a8 ]
  }
2 i9 }3 l# P0 _2 y2 ~+ \1 q  m_lock.Unlock();
* _. s% J; _- u  return ret;
" Z# T! W/ f& I1 v- O, g' n' O' X}1 ~! Z* g( _; K0 V
, j% H& K7 }/ q- C8 [3 r
后面从注册部分的代码我们可以知道下面三个命令是等效的。
( J) U/ j/ I' U4 B* c, j5 `; X) _- R4 @5 V1 D) m6 }( b
ceph daemon /var/run/ceph/ceph-mds.bfudz.asok perf dump
- _9 U9 |3 o- I6 Rceph daemon /var/run/ceph/ceph-mds.bfudz.asok perfcounters_dump
; C- S$ w3 f' Z( mceph daemon /var/run/ceph/ceph-mds.bfudz.asok 1
( ~4 Y5 v7 c( u) e  }. d还有下面三个命令是等效的:' M: V/ E  L- g9 d
8 D& {1 p0 b# m+ s
ceph daemon /var/run/ceph/ceph-mds.bfudz.asok perf schema( E' M; q) B$ ^; E* g2 \
ceph daemon /var/run/ceph/ceph-mds.bfudz.asok perfcounters_schema/ K4 L; e- \8 ^; U! b- A: w) r7 J' e
ceph daemon /var/run/ceph/ceph-mds.bfudz.asok 2
4 o4 |0 d9 C$ _  T5 } ceph-osd/ceph-mon/ceph-mds有一些自己的独特的注册函数,大家检索register_command可以检索的到这些独特的命令。
9 x  d  A* V$ V" S, u$ S( V0 ?. T  j5 r
. b0 K2 H; b& Y# q4 Y
这个AdminSocket 对应的线程,在common_init_finish函数中负责创建。
5 l0 g! v$ n" E; k1 K* g4 }+ R+ o4 \8 t8 s  E
void common_init_finish(CephContext *cct)
0 y1 c8 `4 k/ x{
) ?7 F% |$ g$ V/ _! |. ~6 s. J  ceph::crypto::init(cct);4 i6 o+ V) m  L! `: h
  cct->start_service_thread();+ q( Q) P0 `2 N  y/ ?6 a
  if (cct->_conf->lockdep) {
" h% f$ ]! P% ^! R; j' U$ w) l    g_lockdep = true;
, ~! D( N5 i5 ?    ldout(cct,0) << "lockdep is enabled" << dendl;8 {- f+ F$ f% v2 k* h/ }$ t
    lockdep_register_ceph_context(cct);5 M& G1 m, @4 D
  }7 D) B( A* p5 f) H2 f
}  g0 X) q, _7 L8 V, _7 m
而在start_service_thread函数中会调用adminsocket的init函数:+ a! ?6 q( h' H/ m/ Z

' W7 ~# ~' w( f% a, ]' tvoid CephContext::start_service_thread(): D* a0 w) P9 D. v0 g: k6 l: ~
{
, w  R3 M* P  g$ `* N$ ?  pthread_spin_lock(&_service_thread_lock);
7 d" r( ?) h4 m$ g2 ?1 b  if (_service_thread) {
' J4 A/ a* v& `+ t: v: N    pthread_spin_unlock(&_service_thread_lock);# K  J$ F4 o% R1 l7 s2 V" ]$ i3 i, t
    return;
" B. `6 p" [/ k6 h/ u& E4 @  }
& }, N+ a* I' e- w; d2 J1 G  _service_thread = new CephContextServiceThread(this);
" C" s, G: ~; j! M6 q2 c/ x  _service_thread->create();0 f3 @6 t8 {/ v. I7 i
  pthread_spin_unlock(&_service_thread_lock);: V- n1 r# B, M  G' o
  // make logs flush on_exit()4 A0 D0 e9 P0 P) }8 S
  if (_conf->log_flush_on_exit)1 N, J4 n6 u) Y8 |4 G7 l5 i, i2 r
    _log->set_flush_on_exit();
9 N( s" a# W) g; G2 l, g3 J  // Trigger callbacks on any config observers that were waiting for
: V; ~% m, J* p; e5 o% H) p) L0 \  // it to become safe to start threads.' u7 h4 a% q3 v+ A4 l
  _conf->set_val("internal_safe_to_start_threads", "true");9 O9 Q6 H) U$ F' U4 b! n
  _conf->call_all_observers();6 p7 e7 P+ G1 i
  // start admin socket
/ h0 [7 R. h/ O$ d+ b  if (_conf->admin_socket.length())
& f+ Z% _! m2 e- J    _admin_socket->init(_conf->admin_socket);- `. i- F# U+ W  V
}
6 k1 Z0 L; V+ [# J) g
  i2 r' `# a- F0 T接下来可以分析下admin_socket的初始化函数init:
" @* q! ^5 o7 D. W' X( A* @1 M  w5 ^$ `" o9 T9 d
bool AdminSocket::init(const std::string &path)
5 k- P; G- g% u{
* o# C$ E; q" f7 u7 ~0 R6 O" E  ldout(m_cct, 5) << "init " << path << dendl;
9 ~$ f  \2 |, y  /* Set up things for the new thread */5 h5 }, x' y$ q* d" ^' i; g( [
  std::string err;
. Z5 w$ a+ z/ p) o  int pipe_rd = -1, pipe_wr = -1;
+ V8 h  ~+ @$ a  ?+ X, ~  err = create_shutdown_pipe(&pipe_rd, &pipe_wr);; t. \7 q; `/ L9 d/ o& ?
  if (!err.empty()) {
/ d; ^* D$ b2 |/ O    lderr(m_cct) << "AdminSocketConfigObs::init: error: " << err << dendl;  x0 L" t" ], o& R7 y) \% ]" k
    return false;" Q9 t1 c, M. a, \7 M
  }
; Q! _# Q. h7 T9 e' \  int sock_fd;6 R. ]1 E1 w; R
  err = bind_and_listen(path, &sock_fd);% g  c" v/ Y( J+ [0 R6 H! ~
  if (!err.empty()) {/ Y  l) m( y6 c: v5 [
    lderr(m_cct) << "AdminSocketConfigObs::init: failed: " << err << dendl;
- B) L( L/ B4 p6 P( B$ u& A. S    close(pipe_rd);+ @5 ^& N8 Q3 O) T2 z  v3 X$ q* d! d
    close(pipe_wr);7 B6 Q! L* a" j, F0 [3 V0 a5 X) j# M
    return false;
8 j4 R2 p' ]/ r  u8 t! `7 j  }6 D/ R, f: _0 x/ h7 e6 J
  /* Create new thread */5 [) [( e6 ]9 i7 B) {
  m_sock_fd = sock_fd;) T% ^. V: H; c( j- f% }9 K0 {
  m_shutdown_rd_fd = pipe_rd;! p) K0 ?9 Y: B+ s% b. V
  m_shutdown_wr_fd = pipe_wr;
/ U& h6 N! Y, A) x  m_path = path;
8 R. u* C2 `8 k  m_version_hook = new VersionHook;
3 a3 b' V2 D* w$ N  register_command("0", "0", m_version_hook, "");# c) ]; V6 X8 h# C
  register_command("version", "version", m_version_hook, "get ceph version");/ {. E) e! ]* }
  register_command("git_version", "git_version", m_version_hook, "get git sha1");
, R/ t5 W  _, p' q" h  m_help_hook = new HelpHook(this);
3 R* o: g# s! S  register_command("help", "help", m_help_hook, "list available commands");
0 v5 S! }& B) ?) g8 F5 b" n  m_getdescs_hook = new GetdescsHook(this);
- Z( z3 e3 J) g% l, r$ ~. X7 A  register_command("get_command_descriptions", "get_command_descriptions",
; a6 I. v; Q# j7 \# L: `# M7 _  m_getdescs_hook, "list available commands");( M, c! n6 y5 c( ~
  create();
3 F; d6 A( d' b3 [0 r  add_cleanup_file(m_path.c_str());
3 P3 j4 ?% c: D% m  return true;
; s0 G6 t/ V, v( [9 F1 G4 i0 i}* b; {1 I* s; V2 Y, l
0 f- l9 W% F6 n7 ]
首先是创建了管道,读取端的文件描述符记录在m_shutdown_rd_fd中,写入端的文件描述符记录在m_shutdown_wr_fd中。6 K$ T2 k( [2 p2 m, `# Y# v6 z
从变量名字也可以看出,该文件描述符的作用是收取关闭信息。因为adminsocket一旦创建,必须能够通知到该线程及时退出。
( i/ _( y1 P. ~! W1 R0 H  b退出的事情会写入管道的写入端,而线程会通过多路复用接口,监听读取端,一旦发现m_shutdown_rd_fd中读出内容,线程就知道,可以退出了。
5 A5 n8 p% W$ L  W4 X; S" S+ ]+ r按下不表。9 n7 K( e) j  @* L6 l8 d: T0 m
9 ?% C7 ~: W1 Z& i( h5 i
AdminSocket最重要的是监听发过来的请求,它是用socket来实现的,初始化在bind_and_listen 函数:$ M3 P6 f* M+ ]1 v7 B, u
. c2 ^, L0 u" j) {
std::string AdminSocket::bind_and_listen(const std::string &sock_path, int *fd)
$ w$ H7 O& m& d. t! z' t, [0 V6 j+ s{
3 O9 i0 V1 W0 ^, j8 G8 G  ldout(m_cct, 5) << "bind_and_listen " << sock_path << dendl;, Y! `# j* x& r  n
/ d3 S! c# g9 M9 u3 I" h( H
  struct sockaddr_un address;
  x+ L) b. t. q7 x3 V- F4 [  if (sock_path.size() > sizeof(address.sun_path) - 1) {3 ^4 d7 ?4 @. `: s
    ostringstream oss;
2 e  U) ]  R; W# N6 X6 O( @* _% L    oss << "AdminSocket::bind_and_listen: "
4 v2 e8 Y# b3 s/ ~    << "The UNIX domain socket path " << sock_path << " is too long! The "
# e* A0 [& f' K4 s: |    << "maximum length on this system is "
/ F3 d: _+ O6 w# E; z6 h    << (sizeof(address.sun_path) - 1);
7 g5 ]4 _4 H) {1 [2 H; e' o$ Q    return oss.str();
' f7 y  W. G! f9 K/ t5 Y  }: O7 C  g9 v- H6 R" v  A9 V( ~
  int sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);
: u& A" m6 g2 c" f' P  if (sock_fd < 0) {8 V. E8 j; v" t/ ~5 t5 m! E
    int err = errno;8 r" X, h, S# @' u
    ostringstream oss;3 m; B, R' d! g9 I2 u1 D4 _
    oss << "AdminSocket::bind_and_listen: "
8 y" K2 C  J+ u# ?9 E( v. q* l    << "failed to create socket: " << cpp_strerror(err);9 Q; ]4 h' _: q" }/ m" v$ c4 w
    return oss.str();
! t5 m$ |+ g5 R% q  }
3 d. e; J/ d) |0 u  int r = fcntl(sock_fd, F_SETFD, FD_CLOEXEC);& m. t1 {% l! ~+ |- a% A6 c8 m% m9 u1 x
  if (r < 0) {
% s' o3 S8 Q  {- r# _7 T* \4 ?* v    r = errno;
  M* Y& Q: Y' d) D    TEMP_FAILURE_RETRY(::close(sock_fd));, N& v4 n+ G  w2 V% O
    ostringstream oss;+ r( A- E' ]5 w5 z  A0 `# X
    oss << "AdminSocket::bind_and_listen: failed to fcntl on socket: " << cpp_strerror(r);
0 n) s! O0 G- F8 t! i    return oss.str();" S  I2 I6 D! d) r. N  E
  }
) @$ L1 T0 E* r/ R$ F& x* z1 \$ y  memset(&address, 0, sizeof(struct sockaddr_un));
; o6 n5 O6 \: r; H# }/ {  address.sun_family = AF_UNIX;
: q/ n+ _* C- ^( @  i  snprintf(address.sun_path, sizeof(address.sun_path),
5 W, B( W4 U: |" e     "%s", sock_path.c_str());# N" V, c2 ^8 h7 z8 A. p
  if (bind(sock_fd, (struct sockaddr*)&address,
2 k  T# h5 X, T! D     sizeof(struct sockaddr_un)) != 0) {2 P$ M) r& o$ x* [* O- S' C
    int err = errno;; n: K6 ~% s' j  @  H7 [4 }" u
    if (err == EADDRINUSE) {& X% J! V" Y  y
      AdminSocketClient client(sock_path);0 {* t  c) J( F; r# Q7 B+ u* q
      bool ok;
3 q- D1 `: l. r5 A& E      client.ping(&ok);
8 r5 Z* v( O+ p      if (ok) {! f! C3 v, T7 H3 a/ h' ?9 K* x
    ldout(m_cct, 20) << "socket " << sock_path << " is in use" << dendl;
, p$ q( P% I2 j  Q" l* \! F1 `$ d    err = EEXIST;: ?5 d3 t: Z6 s; s) @1 F- t
      } else {# E, [' m1 B7 `# z5 }
    ldout(m_cct, 20) << "unlink stale file " << sock_path << dendl;5 b' @5 j4 j% S) Y5 E  {
    TEMP_FAILURE_RETRY(unlink(sock_path.c_str()));2 T; C1 V9 c' [9 B" ^- S. ~9 j
    if (bind(sock_fd, (struct sockaddr*)&address,
! ?* Z1 t' [3 N6 ~         sizeof(struct sockaddr_un)) == 0) {' e' d+ Y  l! P! O: U# Z9 S
     err = 0;! P3 j) g8 r, P4 I. \& q
    } else {
9 G, ?" R% y+ b& }! g% Q9 I! J     err = errno;6 P1 b6 J6 v* c' Z5 W( [
    }; ^( `) ?* b  S4 w) C* o( r
      }6 T0 S7 d5 Q6 w. D9 m
    }
! [6 G  U" @! @2 s% w    if (err != 0) {
4 S. Y- y- E6 ^% q      ostringstream oss;
1 W8 I! t' [/ Q) Q, a4 {      oss << "AdminSocket::bind_and_listen: ": S& }( g! Y$ \4 W$ s8 Z( t( Z
     << "failed to bind the UNIX domain socket to '" << sock_path0 |" p" G  D1 \1 c% }+ P3 o
     << "': " << cpp_strerror(err);2 ~; i: H! S/ C" C3 _
      close(sock_fd);8 }* I3 c; U: G& b6 o
      return oss.str();
) @; I  X: l& _    }
- ?& l. p. K9 B) v' t0 z  }
/ O0 R+ v* _! Z% d  s4 T/ h  if (listen(sock_fd, 5) != 0) {
6 a; L3 ?, ^! Z% g/ l1 {    int err = errno;; ?2 B+ K& f* S8 N" m6 D2 a( n
    ostringstream oss;
8 K3 G+ l5 H: d+ l1 L    oss << "AdminSocket::bind_and_listen: "
( |$ j- L6 y+ @5 I  u     << "failed to listen to socket: " << cpp_strerror(err);* r) e* N- J2 P  y
    close(sock_fd);
+ m( X. o5 x" U- [' @& L, o    TEMP_FAILURE_RETRY(unlink(sock_path.c_str()));  |; b; ]2 Z% P$ O* z, y
    return oss.str();
9 n; `/ a9 c7 F& t  }
- {1 O% i2 ~* Q# `4 d( Y0 ]8 u  *fd = sock_fd;+ d. `1 u8 O- l+ z, E1 E! }
  return "";
$ ]) o6 E# s( d}3 y- S4 X" j3 c( t8 X3 {7 V) k
9 {; N# Z% V% \$ [) [
这个函数做的事情并不难理解,做的事情比较老套:
; K% `3 C2 f0 _. M0 j4 J8 h7 O1 @1 创建一个socket,该函数需要一个入参,指定socket路径名:
" p2 K4 }" I) m5 t2 p2  bind) N9 ]5 W* Y  E
3 listen4 Z3 Y2 v' J+ K% d( r/ f7 _
而传入的路径名是这个" a5 Y) e9 }5 S: p- l
root@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok config get admin_socket
6 A# o4 w' @, C7 y{ "admin_socket": "\/var\/run\/ceph\/ceph-mds.bfudz.asok"}) }' X) M" a2 u5 ^5 d  P/ l7 L, o
socket建好之后,因为线程还没有创建,所以至今还没有accept接口 。然后我们再次回到init函数,该函数又注册了几个函数:/ ]5 M( P/ m1 l/ f; k' Q: B
1 O8 ?4 G- _$ D# V! P
m_version_hook = new VersionHook;% l8 M9 t9 L4 d5 Q  X- O+ V
  register_command("0", "0", m_version_hook, "");6 S6 k& {" M! A+ r0 Z
  register_command("version", "version", m_version_hook, "get ceph version");
1 m/ `% }+ P: O' S& H2 j  register_command("git_version", "git_version", m_version_hook, "get git sha1");
) d3 }' g' H' p# w/ ^; f  m_help_hook = new HelpHook(this);, M# M8 k/ o. W9 K& i7 p
  register_command("help", "help", m_help_hook, "list available commands");( ~9 z- Z0 [5 ?1 i
  m_getdescs_hook = new GetdescsHook(this);
  x: ~( G( @8 _/ [# Q$ k3 L: Z1 Q0 Q  register_command("get_command_descriptions", "get_command_descriptions",
' r2 s7 {; q4 B+ n. a: t( i7 e# f  m_getdescs_hook, "list available commands");+ M, z$ H2 R& o, G/ x1 V! }
这几个函数的重要性并不大,基本是用来查版本信息的,如下所示,按下不表。
) {! H, E  [" S
. q3 |9 K+ o8 {7 `7 S1 h% xroot@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok version7 A# V5 V/ q  ~  n+ {/ b$ ~( {
{"version":"0.67.9-222-g014b35f"}/ X4 Y5 J9 l/ s! j' U* g: ^
root@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok git_version
0 e" ]+ P3 S, l+ v{"git_version":"014b35fc1ee0a1ad1f699a3705f3481a88614d36"}( `1 i. B: b, x, P
root@test3:/var/run/ceph#7 y2 c! X) b2 [! N+ X, Z' o9 o

6 z$ b* |0 y4 m, X, v5 Pinit函数最后调用了create函数。create函数是老朋友,前面分析Log的时候已经提到,对于Thread这个类,做的事情无非就是创建线程。关键内容是,线程执行的函数是哪个? ) K+ H9 S: _5 k/ U+ N) P. A+ @3 H  K
和Log一样,是entry函数。AdminSocket类也有entry函数,该函数是AdminSocket 对应线程指定的函数:
- X: x" T# J$ {5 t0 W1 U2 Z: [" v% n. A, _
void* AdminSocket::entry()
1 B1 }' E9 _4 H3 p: l{
6 }; M( E4 Q6 R: q; Y( M  ldout(m_cct, 5) << "entry start" << dendl;: D4 `4 L: O" u$ T# X0 L! _
  while (true) {1 n6 j# z# Q+ ?$ [* ~& x4 C
    struct pollfd fds[2];9 @2 k5 m" J6 h% i4 V+ Y
    memset(fds, 0, sizeof(fds));
# f* p! p8 P$ ?# s    fds[0].fd = m_sock_fd;
: D9 t6 N8 M3 |0 q" L8 G; Z    fds[0].events = POLLIN | POLLRDBAND;+ G8 _# J3 M6 ^5 [7 a
    fds[1].fd = m_shutdown_rd_fd;
  a0 ^: Z! }9 g2 R    fds[1].events = POLLIN | POLLRDBAND;
/ k5 A- P$ V0 n, }( F    int ret = poll(fds, 2, -1);
' r2 ]  Y( y7 S4 ?; F    if (ret < 0) {# U- n, w$ J* `- P8 `
      int err = errno;
3 F2 t9 p3 J& _: n! m" Z      if (err == EINTR) {  [) I4 F& U' V
continue;
1 `5 t# v0 \; P0 ^' a      }7 I( R8 \8 d  o. F
      lderr(m_cct) << "AdminSocket: poll(2) error: '"
, ]- n% K/ G8 @0 [* Y  << cpp_strerror(err) << dendl;0 [& O8 v6 X2 @. s. \+ Q
      return PFL_FAIL;0 H9 S1 o+ `$ F1 g. ~4 f2 O9 A
    }
! a$ t% {4 i& p! Y' q7 r0 L0 D    if (fds[0].revents & POLLIN) {
$ d4 v! ]+ v) p9 [& r5 Q      // Send out some data# q5 t8 j6 ^+ l& v4 k& F
      do_accept();
, w- y5 j5 n; ?" R- }% \    }
# G) s5 M' H, o9 D    if (fds[1].revents & POLLIN) {
- j7 w7 _  a- A& ?/ a, a9 d6 _* m      // Parent wants us to shut down
1 j# s/ G$ P6 j3 G, ]      return PFL_SUCCESS;- s$ d- {& u; V" I# R  h
    }
2 r4 R2 q# m( d) R  B- n  }8 ~+ M* z2 W' \% o; ~- Z3 o+ ]
  ldout(m_cct, 5) << "entry exit" << dendl;2 t; {  E( l  E/ ]4 Q: f% s
}
4 ^3 i& w, R( K
5 ]1 Z* u" q; D! a$ H这个线程函数比较简单,它监听socket fd和管道的读取端。
" s2 M" X- m1 K. c+ R1 管道的读取端负责管理何时退出
! I1 \7 Z4 Z* C( X6 ]" r9 d2 socket fd 负责监听用户发过来的指令。, q* ]9 ]! A6 h# I* C
5 z% R( M/ y; i9 S; X8 a
处理用户发过来的命令,是do_accept函数干的事情:  j9 }0 ?4 }( y, h7 R$ x
0 `  A: M4 a) t, k
bool AdminSocket::do_accept()+ T& C8 J  o) v  T5 S
{
; P* Q; f* l8 N/ t  struct sockaddr_un address;
7 J# n- h' f2 y& `  socklen_t address_length = sizeof(address);
7 t$ k4 ]7 _) }8 i. V; c  ldout(m_cct, 30) << "AdminSocket: calling accept" << dendl;/ {) L  K9 L/ A; }0 ?9 m/ @3 }
  int connection_fd = accept(m_sock_fd, (struct sockaddr*) &address,4 @7 r0 s) |/ ]2 M% n
             &address_length);8 k; ?6 s/ p! D# A# r* S2 A& l
  ldout(m_cct, 30) << "AdminSocket: finished accept" << dendl;
1 W  Z2 N5 k; L4 N' e1 D  if (connection_fd < 0) {
% d) z# R% M  S2 U5 e0 _    int err = errno;2 g4 d2 `2 f! h
    lderr(m_cct) << "AdminSocket: do_accept error: '"
, B, I9 F2 y5 W# Z2 Y, m             << cpp_strerror(err) << dendl;+ G2 @' V6 E1 }1 f; l
    return false;6 p* J* x; E/ @2 {+ [
  }
8 P4 S4 k5 ^1 [5 b' P: l; [3 G; D- E$ x: o0 C7 B5 b! G- r" S/ b
  char cmd[1024];
' v+ Q# r! x3 j& M6 Q* _  int pos = 0;1 f. Z5 d1 x5 D+ U
  string c;
; W7 W& r" O4 r$ c. B  while (1) {, T9 a6 I! i" f
    int ret = safe_read(connection_fd, &cmd[pos], 1);
. w4 C  |" W) b; w+ e    if (ret <= 0) {
" \% X% O1 l, S7 I9 j      lderr(m_cct) << "AdminSocket: error reading request code: "
7 @; B3 L6 |: @) m0 M1 T- ^8 G' w- M         << cpp_strerror(ret) << dendl;. v1 k' w% o/ T( e% M, S- e" G; a; o$ Y
      close(connection_fd);, N" n2 y4 c6 ^6 b* x& M, P9 [" E& y
      return false;& m% {& _4 n) f- c+ J% }' o7 g+ v
    }
' w$ e# K# B- R$ m& s: k( Z1 C    //ldout(m_cct, 0) << "AdminSocket read byte " << (int)cmd[pos] << " pos " << pos << dendl;
/ x5 J& o1 M& ~: I3 V  v$ a( M( ?    if (cmd[0] == '\0') {  m7 q/ J$ Z. m& }
      // old protocol: __be32
  t1 f' ^8 x7 h9 v9 F7 M! q      if (pos == 3 && cmd[0] == '\0') {& \+ E3 w9 U* W: m+ H/ d( p
    switch (cmd[3]) {- I' p% m6 o( q: W$ e
    case 0:
0 i  B6 [7 j) S, v) Y+ @3 G+ F: O     c = "0";* j3 y' ?4 U6 U" Y- x( T
     break;) M: f0 K& K2 i5 X
    case 1:$ o. d- `4 p0 X0 p% G( @3 g
     c = "perfcounters_dump";& i2 `" e& n, s. S2 U# ~* F
     break;
: o4 y' M! U% k5 K    case 2:
4 J/ J. c# U0 p5 V$ A) M) m     c = "perfcounters_schema";" S9 v6 b& P3 a* u6 A
     break;
! J/ T7 w4 N( U/ A    default:
8 O5 t+ `- R5 ~  j2 f1 V0 @& d/ o     c = "foo";
. w  b+ H& [# F* \1 I( {# D  o8 v     break;
* {- X& h7 d  _1 }    }
2 B7 e8 e9 l9 e6 D2 Q: Y# P9 v. C    break;7 h  j. Q1 f2 e3 T# Q- ?, M8 B* P
      }
! r2 n. p% Y5 V2 F9 ?9 D    } else {
! ~6 x) m; i* q; Q9 b% f- }      // new protocol: null or \n terminated string
" u/ X  u* w- F5 E      if (cmd[pos] == '\n' || cmd[pos] == '\0') {
3 h) Y) X$ N: O    cmd[pos] = '\0';
& ]1 e: ~0 l- k, M    c = cmd;
6 r" y3 l' N' N3 v    break;6 Q: B" @0 ]6 q+ ^  C. y
      }5 s" F2 {, B! b. P+ t4 d. }$ j
    }8 m: V7 T" A# j0 R* K% B/ S
    pos++;" |6 n7 y' h" K. t) o+ M
  }
- A  Y1 z: \& @/ {0 f* M1 s8 a+ e6 d% p- M1 x* R$ ^# r+ B
  bool rval = false;
/ Y& J# D( r9 s; u. A6 H% Y7 v4 W- b! S7 W2 C0 `- X
  map<string, cmd_vartype> cmdmap;7 H7 f4 u! Y, M4 w4 y. `6 |
  string format;
. C: l1 S2 u( N* p  vector<string> cmdvec;1 C* y. m' X3 U2 r1 G
  stringstream errss;
4 p2 Y# d! n5 x8 t( v  cmdvec.push_back(cmd);) g/ e, }, f6 q  K$ p
  if (!cmdmap_from_json(cmdvec, &cmdmap, errss)) {
5 K, q3 x6 W+ y+ O# V8 B5 o  C6 x    ldout(m_cct, 0) << "AdminSocket: " << errss << dendl;) J) o9 q0 B2 g
    return false;
) D  y4 ^* A2 ?. Z' P3 p  }
( c! w9 u7 m$ Z" f6 j+ {  cmd_getval(m_cct, cmdmap, "format", format);7 Q* B8 a# B, Q4 J% w. Q) E% V
  if (format != "json" && format != "json-pretty" &&
* ?& z1 E- d! ]/ Z! O      format != "xml" && format != "xml-pretty")
6 z3 k+ q  K) N0 w1 C  n( \9 i    format = "json-pretty";9 a6 I  Z4 e8 u+ @
  cmd_getval(m_cct, cmdmap, "prefix", c);
( n  B! w$ b5 M. i+ _% B" W6 y) R5 d( y( V( @; B/ U% C; y
  string firstword;8 O4 Y* a9 `( \5 `: W' B3 t4 U
  if (c.find(" ") == string::npos)
7 P7 ?# Q/ W9 j: u1 e( R4 q# U    firstword = c;) f. P; o1 G1 Q2 c  W1 \
  else
# Z2 g/ j/ z% _  H* P+ L6 n    firstword = c.substr(0, c.find(" "));( e3 U' _2 i2 ?4 w$ B
# |8 H6 I1 K( p3 O  B- ?+ G' V
  m_lock.Lock();% {( O) w% y2 p% h! V. k& c% [
  map<string,AdminSocketHook*>::iterator p;
( U; J& y; M  R- _% x: {! g  {* g0 Y9 L* D% }  string match = c;: m7 l1 s8 `& t5 f# r  T1 }1 f
  while (match.size()) {
! [: C; L% `$ X! x    p = m_hooks.find(match);
' f  ~  ^( G8 k" }* ], _    if (p != m_hooks.end())
* Q6 {: c. ]3 {: Y% b" L, L      break;0 o$ c- r9 i) k0 X
    3 {0 k3 }  k- D- m% ^
    // drop right-most word( Q0 f9 f$ N4 r% p. c( i; I7 G- v# H
    size_t pos = match.rfind(' ');  _- Q% c7 z/ r- f2 u
    if (pos == std::string::npos) {) [5 N8 b& `9 }, {, D( I+ S- t
      match.clear(); // we fail4 m3 K% o7 I2 p; F, u  @6 B( X& Y, Y
      break;
  t, _* E0 i" z; }    } else {1 l4 [6 _3 N- Z( S
      match.resize(pos);
+ k0 H$ t" u% V4 Z; X% z+ w0 J' a    }- C7 q' W6 z5 d5 t- R5 z
  }
" `* e; A, Q  u$ H' ]5 B5 y, \6 m$ _5 \; e1 S8 V6 t
  bufferlist out;
* B5 Q. q8 g+ Q9 _9 z  if (p == m_hooks.end()) {" N/ Z; R1 U5 P' R; ^
    lderr(m_cct) << "AdminSocket: request '" << c << "' not defined" << dendl;
1 T; j; a+ q6 F; X+ D; w  } else {* W$ {, @- ]- N; b. @, I
    string args;- B: [- j/ o  }: P4 p% t1 d
    if (match != c)
, P: Y8 y# D9 r. t$ f      args = c.substr(match.length() + 1);( x  C$ y+ e6 h# B
    bool success = p->second->call(match, cmdmap, format, out);# r7 r! d3 y' q6 h$ Q
    if (!success) {
# ]( U, N; B8 t1 L& Y( b( M      ldout(m_cct, 0) << "AdminSocket: request '" << match << "' args '" << args
) C, L3 S+ B. I; V         << "' to " << p->second << " failed" << dendl;0 w/ c* u1 y' S# K
      out.append("failed");) y/ b) |6 \1 M2 [& `
    } else {* E: y4 p9 a% _$ a: S* \3 A
      ldout(m_cct, 5) << "AdminSocket: request '" << match << "' '" << args* e/ c* u4 o0 {! N- c; m1 m
         << "' to " << p->second5 H7 D9 Q( e  E& q$ D$ Y2 X
         << " returned " << out.length() << " bytes" << dendl;
- [  G0 R. Y8 x    }, l9 _9 h7 F9 H# d
    uint32_t len = htonl(out.length());
: U3 N* C7 y3 R6 H4 p    int ret = safe_write(connection_fd, &len, sizeof(len));9 L* _* u3 ?0 j
    if (ret < 0) {0 f2 J9 Z' Y; h/ _! g+ @3 g
      lderr(m_cct) << "AdminSocket: error writing response length "
! [  j) X, w! T6 H         << cpp_strerror(ret) << dendl;+ U7 A9 d  i. U/ ]$ j# a
    } else {4 a. p# v& v4 q) R! Y) j# x3 @
      if (out.write_fd(connection_fd) >= 0)
4 E# s8 k0 O; Y/ H, h    rval = true;
5 a) Q$ C( R" Q) F$ W6 c    }7 f! A2 H/ _, ]2 x- ?' p# ]" c
  }+ M8 X5 k2 U3 n9 E
  m_lock.Unlock();2 j3 l8 }& X6 s. X5 t

: `9 Z4 G) p- u' y  TEMP_FAILURE_RETRY(close(connection_fd));% l" J) D0 j7 {! T* Q' Q5 V
  return rval;
" R2 y! Q0 q1 W) V; B0 ^1 F}
) d% J% ?1 Q) [6 F- ?! r6 ?9 c
1 q: m. d  W9 L1 f% J这个函数有点长,但是并不复杂。简单说,如果有个client尝试 connect ,该线程就poll就会感知到,然后进入do_accept函数。8 }, O' S) F, i, C% J9 g- Q  p
do_accept首先执行accept,和client 搭上线,然后开始通信。
6 l4 X+ S+ ?4 v9 Y0 ^8 t& ~2 H" T  {1 \) o
safe_read负责 读取客户发过来的指令。前面已经提到过,AdminSocket支持的命令是有限的,初始化之前都已注册过了。8 Z3 M% k: c3 R7 _8 {  q- V
如果client 发来的指令时注册过的指令,就见招拆招,返回相应的结果给客户端。8 K! r( ~) e* z) \

9 n3 I7 r& j* U" r7 m每一个命令的字符串,都是和一个AdminSocketHook 的类型关联的,但是一个AdminSocketHook可以对应多个command2 R. ~  ]& {  N# ?% ?, F& x! n
: W4 N8 F1 t9 G9 x; _' }; \
std::map<std::string,AdminSocketHook*> m_hooks4 O3 z# T) b7 X9 X
比如说 config show  / config get   / perf dump对应的多事 CephContextHook,前面已经提到过。; e7 [) j. h$ S; h
5 m+ o# g0 L9 q7 d. v
见招拆招的函数,就记录在对应的Hook上:
8 ~6 E7 l9 V% i! V& ]1 U6 S/ c- A& y
class CephContextHook : public AdminSocketHook {( a" ], V5 _9 t$ C% c
  CephContext *m_cct;; R& N  a' O& z/ S9 Q
public:
* m. t- W& a, S  CephContextHook(CephContext *cct) : m_cct(cct) {}3 y6 N7 h% e5 o
  bool call(std::string command, cmdmap_t& cmdmap, std::string format,$ }6 g+ J7 a# M' i# d+ h! B6 N
   bufferlist& out) {# j* j' \3 ]' H0 C) X
    m_cct->do_command(command, cmdmap, format, &out);
" l) u, \  c! [" d" A. q6 _1 x; f    return true;# ~- d- k& j4 A
  }
6 u! v+ |; D: l) g6 n6 P! |/ C9 m! ]6 u9 J};
" V9 G6 S. w3 g( \
2 k: l7 a* }; I& |) c$ o( q1 ]& l* D4 ?9 O1 @9 i" T
do_accept函数中黄色的一行,具体是实现,就是对应Hook的call函数,对于CephContextHook,就是这个类的call方法。
& ]: A2 x. i# h3 r: Z
0 |( ]- Y* g8 N1 u) P% W下面我们看下CephContextHook的call方法:即它的do_command函数:1 q* F8 x! }7 u
+ @# @- e9 Y+ {7 P9 k
void CephContext::do_command(std::string command, cmdmap_t& cmdmap,8 l* {" D8 A+ K! S
             std::string format, bufferlist *out)
0 p; @! y, m/ M+ B- D3 s+ [{
/ l3 C8 W- D# [3 S! z+ J1 T  Formatter *f = new_formatter(format);
% g. v8 y& H3 N2 |: s4 l  if (!f): T# \$ G* W) `
    f = new_formatter("json-pretty");' l/ z; @! f0 l
  stringstream ss;, {$ q; L4 L' L1 I' m! s
  for (cmdmap_t::iterator it = cmdmap.begin(); it != cmdmap.end(); ++it) {
/ i  g$ G  D. f0 l# ]6 D( V% \2 c  u) V    if (it->first != "prefix") {  Q# u% h+ c; u( N6 m
      ss << it->first << ":" << cmd_vartype_stringify(it->second) << " ";. X; [% w  k8 ^" @" @, I/ |* D
    }
% ~5 `! k: Y  l" k5 u( X8 j  }: y2 }: k1 f$ S0 [  H% g% z" F
  lgeneric_dout(this, 1) << "do_command '" << command << "' '"1 \4 j7 u, Y1 [- X2 H
             << ss.str() << dendl;9 m- g! o: J* i: z
  if (command == "perfcounters_dump" || command == "1" ||
# r# @( y+ O, h  w/ v* ?      command == "perf dump") {2 z) D) L0 u, }" J; J( @
    _perf_counters_collection->dump_formatted(f, false);$ k% C* G) B, s$ \+ s
  }
0 Y4 i* R0 x) t; r0 w  else if (command == "perfcounters_schema" || command == "2" ||& X( W- t; L4 }( Z* ^
    command == "perf schema") {4 T& x" c: t6 Q% ^: C! n
    _perf_counters_collection->dump_formatted(f, true);
/ R" g9 @. I8 g' }- h  }, ~- t/ [1 d4 R. p2 M# g
  else {
+ o7 }% D) a# Z  E8 T    f->open_object_section(command.c_str());  Y' V- B/ y& p/ _
    if (command == "config show") {( J. B( V% ^$ S3 R/ K3 j
      _conf->show_config(f);0 a7 ]6 ^# M5 {( {& {( q) w' b/ E
    }* \1 e7 M, j5 [" E( D7 e
    else if (command == "config set") {
: D! c$ e" V' `8 Q8 S      std::string var;
, c( q' [2 x9 E      std::vector<std::string> val;( K2 V/ O) o4 A' a! j
  F) p! a0 `; M
      if (!(cmd_getval(this, cmdmap, "var", var)) ||
! s( e" `; A6 Q: Q# [          !(cmd_getval(this, cmdmap, "val", val))) {
8 e6 j# X- \+ G2 D2 U        f->dump_string("error", "syntax error: 'config set '");
: J/ e1 y+ ?+ }& s& L( ~( F" o. P      } else {, x6 L& I& N3 v( J% Y- c. a
    // val may be multiple words
+ N2 i5 c0 {6 b0 O' n0 L3 Y4 B! ~    string valstr = str_join(val, " ");
: |( {/ t1 k6 A% `( G8 e7 v        int r = _conf->set_val(var.c_str(), valstr.c_str());- B# g, i3 C9 m
        if (r < 0) {
! G* ^1 T3 b; i          f->dump_stream("error") << "error setting '" << var << "' to '" << valstr << "': " << cpp_strerror(r);3 l4 G/ D5 Q& ^7 W: [0 l
        } else {3 P) {* I& o- k9 M$ l7 @3 q6 y7 Y
          ostringstream ss;, \* u/ L9 o6 M1 J4 \
          _conf->apply_changes(&ss);
: @- b8 W: M: L; b' _) ~+ @          f->dump_string("success", ss.str());6 Y. g( W" A9 r
        }8 x& }  ?7 i( p% H
      }
* g) v* N: B9 W' c  C7 g    } else if (command == "config get") {  v1 v6 q# {8 ~
      std::string var;1 G& t& m" O& P* o! G% I! s
      if (!cmd_getval(this, cmdmap, "var", var)) {6 z: b: u- `5 n8 ^
    f->dump_string("error", "syntax error: 'config get '");
  h6 ]' s, u- k5 i- d      } else {! M6 U) j2 E4 l3 j
    char buf[4096];
5 L8 U* g( S7 ?$ @, u. b    memset(buf, 0, sizeof(buf));( ?" k5 n1 @. w. ]; r& f, Z
    char *tmp = buf;
. Y! O, D. m2 o* B. j7 f    int r = _conf->get_val(var.c_str(), &tmp, sizeof(buf));
4 O8 D4 T  @: m  ~9 \1 L3 x3 N    if (r < 0) {
6 N9 h! f# ?+ S& a     f->dump_stream("error") << "error getting '" << var << "': " << cpp_strerror(r);2 f6 w6 [& X9 S4 r7 B% n9 n" [
    } else {- R' w2 ~2 ]) E$ r
     f->dump_string(var.c_str(), buf);. R5 L. Y7 z2 c9 T
    }) b2 _; `5 N6 V8 G
      }
% O4 N$ p  Q" v    } else if (command == "log flush") {6 L% z. H! t+ P% R) q* V: `6 v  z( N
      _log->flush();
! m# z* G& O& _" d    }5 g+ A" z5 A' j7 e* I& k
    else if (command == "log dump") {
5 P; l) J4 I3 J; a1 w      _log->dump_recent();- @/ l0 v+ J$ B6 J
    }
) [1 v  h  h  o( A5 k* ]0 i    else if (command == "log reopen") {
. G& @& q! ]4 W1 R      _log->reopen_log_file();
. S# _! l# ?' B    }) U3 _4 k0 O5 T$ ~0 O
    else {
4 `8 a/ M3 `8 f      assert(0 == "registered under wrong command?"); : c9 X' _# u$ F; M2 Z
    }4 R6 @; e# m0 j
    f->close_section();1 o/ f5 J  r. |
  }! D% U. \* i, }
  f->flush(*out);  i7 N2 {  E; O. G
  delete f;5 E  W: p8 W9 W, [  d- {
  lgeneric_dout(this, 1) << "do_command '" << command << "' '" << ss.str()" X4 K% h2 Z4 r5 ^5 |
         << "result is " << out->length() << " bytes" << dendl;
+ W4 e) v5 g6 X1 R2 f. I7 f( y};
" P6 S" b# J/ j1 u  毫不意外,前面注册的每一个方法,在do_command函数中都做了处理。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-6-12 00:06 , Processed in 0.024764 second(s), 23 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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