找回密码
 注册
查看: 7232|回复: 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 directory1 \" i$ t6 ^0 z4 s1 s9 }
ID WEIGHT  TYPE NAME           UP/DOWN REWEIGHT PRIMARY-AFFINITY
) M; M8 ]: ?/ k/ c

1

主题

0

回帖

12

积分

管理员

积分
12
QQ
 楼主| 发表于 2018-9-29 13:15:53 | 显示全部楼层
任何一个成熟的项目,必须要提供出接口,就像探针一样,可以让我们探查进程内部的运行情况,进程不能是一个黑盒子。对于ceph而言,Admin Socket 提供了该功能。Admin Socket 不仅可以查看当前的配置,进程运行的状态,而且,还可以修改配置,获取log等。9 j# M9 i& f3 ]- R) s) X; p* k
4 j9 m+ }+ _% q( Q, F
下面help可以看出,ceph给出了很多命令,来了解ceph内部的运行情况。
7 d5 D" b4 e- F- }root@test3:~# ceph daemon /var/run/ceph/ceph-osd.4.asok help
8 y. E8 K/ T6 ~3 ?" U5 C. w' ]# j{ "config get": "config get : get the config value",
' r$ C. A% S, Y) [6 b6 F1 o  "config set": "config set [ ...]: set a config variable",9 @; |* n# f: I
  "config show": "dump current config settings",
9 v- @9 p2 y6 q% ]- N0 p6 e: M  "dump_blacklist": "dump blacklisted clients and times",
3 h$ ^* ^1 Q3 y/ X7 x) ?! R# z  "dump_historic_ops": "show slowest recent ops",
% y2 D2 Y1 i6 }- l  "dump_op_pq_state": "dump op priority queue state",2 J: Z$ _2 e' Z4 p1 m( D; \
  "dump_ops_in_flight": "show the ops currently in flight",5 Q, a7 B2 c% t# v, j* [
  "dump_watchers": "show clients which have active watches, and on which objects",# P- ?7 [# E; K$ w! ^9 x
  "get_command_descriptions": "list available commands",/ I0 A7 H/ i8 C; M' y+ P; C7 R3 h1 ~
  "getomap": "output entire object map",
" W8 j1 \4 g& [$ A' _' f: |) Z  "git_version": "get git sha1",
# R3 t3 {/ A+ U5 ?/ F3 g  "help": "list available commands",
+ t& y/ D( d- |0 r  "injectdataerr": "inject data error into omap",0 C' k+ m1 Y9 f# n" Z+ H  f& @
  "injectmdataerr": "inject metadata error",
- @! B" k5 Q. s! R  "log dump": "dump recent log entries to log file",' c+ t, l7 U+ b, p$ `* D* B* ^9 V
  "log flush": "flush log entries to log file",
6 l( \* H5 ^  b: [  M+ i* d2 C  "log reopen": "reopen log file",: ^, \) j: b8 l+ A
  "perf dump": "dump perfcounters value",4 Y$ Y5 v2 m* v, L
  "perf schema": "dump perfcounters schema",1 V" I3 R' x- Z* M$ T
  "rmomapkey": "remove omap key",: p1 }" {3 q! Z; E1 O" P
  "setomapheader": "set omap header",5 g  L# t, h& s9 L- d% N: ]
  "setomapval": "set omap key",
; o8 n1 R5 c- k& `1 }  "truncobj": "truncate object to length",
6 `* _' ]% K- g6 I7 p1 j: J  "version": "get ceph version"}
. [1 m  j' k  n, _# oroot@test3:~# ceph daemon /var/run/ceph/ceph-mon.*.asok help; b3 g5 z9 s7 m* h( k
{ "add_bootstrap_peer_hint": "add peer address as potential bootstrap peer for cluster bringup",2 b0 y9 O5 c2 p
  "config get": "config get : get the config value",
7 t7 J+ b: ~2 f4 z  "config set": "config set [ ...]: set a config variable",
9 r* N! [6 g$ U& G7 c  "config show": "dump current config settings",
, u2 M* T7 \# A  "get_command_descriptions": "list available commands",
1 u% Y9 `, G( X, \! |1 k+ i# K  "git_version": "get git sha1",
( N$ l- k$ i" P9 k* t  "help": "list available commands",
4 O4 k! X4 f! i+ s2 K+ u: e  "log dump": "dump recent log entries to log file",, S4 D% l8 z- f% F9 ]3 \
  "log flush": "flush log entries to log file",
& Q  c( x/ W! L9 ]4 _8 N  "log reopen": "reopen log file",
: N; B' M( ~9 o4 X. V$ ?+ n5 J3 d  "mon_status": "show current monitor status",
8 o8 S  b' S- p2 S; {& {  "perf dump": "dump perfcounters value",% q& v9 p1 |5 a/ s; S0 Z
  "perf schema": "dump perfcounters schema",
8 B* _9 l1 G5 z9 p% M  "quorum_status": "show current quorum status",% V9 l1 e$ q3 Q4 N" q) p1 a/ b
  "sync_force": "force sync of and clear monitor store",
2 E6 x, M. B1 \) I4 f  "version": "get ceph version"}
! z" i' w( b! m6 l+ k7 b  A' [5 croot@test3:~# ceph daemon /var/run/ceph/ceph-mds.*.asok help. g& H; p+ z! p5 h' t
{ "config get": "config get : get the config value",
6 M# X, E; L; E9 y8 C0 r+ m1 g" C' E  "config set": "config set [ ...]: set a config variable",
! m6 k! A1 k2 N  J4 b  |0 ?  "config show": "dump current config settings",- Y0 Q9 x8 E* p5 c& E, D
  "get_command_descriptions": "list available commands",
! L1 h% Q2 @8 v% {8 Z4 Q1 D! J  "git_version": "get git sha1",! v% k3 P# p! n- X. H  E
  "help": "list available commands",
2 I8 \$ H4 i6 {+ X6 d! I" f2 }  "log dump": "dump recent log entries to log file",
9 O* |8 z5 Q2 r, S0 Y) ]  "log flush": "flush log entries to log file",8 A, `; H5 E5 U+ W  f4 P8 E
  "log reopen": "reopen log file",
- n, l4 q% J, j2 H- {% s: g  "objecter_requests": "show in-progress osd requests",0 K5 r4 Z3 @3 h9 e/ |4 T' s; v
  "perf dump": "dump perfcounters value",7 \9 p2 _+ n( p1 S
  "perf schema": "dump perfcounters schema",
4 ^. |3 W3 }; [- r+ M% Z  "version": "get ceph version"}
# \' i& ^1 e  n) b5 W9 |2 {: C; O  }% R/ W& `0 m; A
比如可以查看 ceph的各个模块的当前配置
* r8 Y' Z! f4 ]$ q4 s  U8 u/ S2 [/ v  M4 u3 H" p- z, r7 k3 R( m
ceph daemon /var/run/ceph/ceph-mds.*.asok config show
& D0 `9 g9 {, S5 |' D1 w; gceph daemon /var/run/ceph/ceph-mon.*.asok config show& B( e' @2 ]6 `4 E4 p3 D
ceph daemon /var/run/ceph/ceph-osd.4.asok config show9 k6 \( \0 q, R$ Z" _7 j
这个是怎么实现的呢? 这是依靠ceph的AdminSocket机制完成。
+ J! M8 g# W3 ?1 L* w- ^! A! _CephContext中会创建一个AdminSocket对象,该对象本质是一个线程。ceph-mon/ceph-osd/ceph-mds这些进程都会有创建一个AdminSocket的线程,负责响应用户的探查命令。& r: Q# I# x3 V8 t  r# e6 o

$ y9 O9 ^; }# m, g从上面的OSD MON MDS help的不同输出可以看出,他们支持的命令有共同的,也有各自独立的。
! Q/ ^4 `0 v" w! O' }1 [首先是共同支持的命令有:
$ ?6 x+ _8 a& @' z9 Q0 M; B2 @+ Hconfig show        显示所有的配置项
# E' D: k- Y/ x: e5 {$ A2 dconfig get           获取某个配置项8 m2 }6 q$ Z) K9 [( |
config set           设置某个配置项
; G1 j, U+ |$ _log flush        将log 刷入日志文件, k7 q: d( J" f8 P* g# r; U
log dump           将最近的若干笔log刷入到log文件: b! G7 g8 M2 p* ]" R2 y5 C
log reopen         重新打开log文件7 U& n9 e8 I, {0 w) P# V, f8 b9 h
perf dump         输出统计信息
) P0 S+ o, Q5 Cperf schema      输出统计信息的类型. _) L' X8 Q( h& l) l
version             版本信息
1 T0 j: y! i0 e2 u. M, s5 hgit_version   :    git 版本信息! a' j1 Z" J% h3 A- W4 A7 U9 ?- U
支持的大部分common的命令,注册发生在 CephContext的构造函数:7 O* n( k/ q8 r$ X) ^
  _admin_hook = new CephContextHook(this);$ V9 a9 f# e, R2 {& F2 Q
  _admin_socket->register_command("perfcounters_dump", "perfcounters_dump", _admin_hook, "");" i8 j. [7 Q4 b/ M0 j9 K8 P, X  s
  _admin_socket->register_command("1", "1", _admin_hook, "");
6 ?0 u* G1 |* R2 \+ ^, M1 Q- h4 ~  _admin_socket->register_command("perf dump", "perf dump", _admin_hook, "dump perfcounters value");
! e$ N, V6 l/ X3 C" R' ?7 [6 s  _admin_socket->register_command("perfcounters_schema", "perfcounters_schema", _admin_hook, "");
: T  R6 u; k/ s7 D" ]  _admin_socket->register_command("2", "2", _admin_hook, "");
" k' H6 k; S  T8 f6 z; i3 `& g8 r  _admin_socket->register_command("perf schema", "perf schema", _admin_hook, "dump perfcounters schema");- v+ I" j+ ]" g: l
  _admin_socket->register_command("config show", "config show", _admin_hook, "dump current config settings");: x" U) [  l- y
  _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");
7 l* U+ K7 a+ R$ p  _admin_socket->register_command("config get", "config get name=var,type=CephString", _admin_hook, "config get : get the config value");
  R( T+ \) e" T0 }  _admin_socket->register_command("log flush", "log flush", _admin_hook, "flush log entries to log file");7 b$ s" X, N5 P$ E$ j4 Z% ^$ N
  _admin_socket->register_command("log dump", "log dump", _admin_hook, "dump recent log entries to log file");2 \6 E/ x; v/ o5 D
  _admin_socket->register_command("log reopen", "log reopen", _admin_hook, "reopen log file")
( i+ N( q) j+ l( i/ r* D6 R/ ~- t( Q& s% F- l( W6 M3 o3 p( ~
首先定义了个CephContextHook,注册该Hook也作为一个参数传递进去,这个register_command比较简单,就是建立了command字符串和Hook的关联,这个关联有何作用,后面会分析到,暂时按下不表。6 d& U2 o5 W# j- }* {  l+ V2 H
' z2 H' N' `& t4 |, G. p8 D3 G& f
int AdminSocket::register_command(std::string command, std::string cmddesc, AdminSocketHook *hook, std::string help)
( r1 C" F5 f2 L* c9 K- D" I7 C{
+ \; s" t7 s' c  int ret;4 W# O3 o' f& h+ f+ Z
  m_lock.Lock();, b) x3 m0 r7 L. e# j) z
  if (m_hooks.count(command)) {
6 D; m. ?$ O- h! J! m    ldout(m_cct, 5) << "register_command " << command << " hook " << hook << " EEXIST" << dendl;4 ]* A! ?, Y/ X3 u
    ret = -EEXIST;
9 p5 c5 k) D" j7 C3 j  } else {
/ D2 y# _! S! X: E+ n% Q- _1 ^    ldout(m_cct, 5) << "register_command " << command << " hook " << hook << dendl;
9 Q3 {$ I7 ]' t5 d! ^    m_hooks[command] = hook;6 p5 v( _0 \6 D! W8 v; }# N
    m_descs[command] = cmddesc;
$ y& l: h/ i9 L% {! {: T& j    m_help[command] = help;/ k$ o6 {% c0 n, P+ j6 {
    ret = 0;* t+ c. D2 L4 e$ c
  }
& d. A9 ^5 [9 N0 @. O! e/ w) l  m_lock.Unlock();) t$ [4 B/ w) |. A" I
  return ret;1 E+ t/ W7 `; C5 @
}
3 p6 k6 s* B* N- d, E: a  E  V" ?1 Q0 o' z. k
后面从注册部分的代码我们可以知道下面三个命令是等效的。
) [9 t. t7 F# G8 V. X3 W% m9 E1 ~
& N' ~0 Y) q8 x5 Q8 ~" a2 Pceph daemon /var/run/ceph/ceph-mds.bfudz.asok perf dump
# ^) k, ~* H* h6 f4 b/ |ceph daemon /var/run/ceph/ceph-mds.bfudz.asok perfcounters_dump- r" x* x5 {: u4 S3 X# h- s7 `. E# e
ceph daemon /var/run/ceph/ceph-mds.bfudz.asok 1
4 {/ m! Q! n( o还有下面三个命令是等效的:  y: x, W, P$ i7 }9 b1 y6 p

7 \" W& f' A# o' o, I+ r6 {ceph daemon /var/run/ceph/ceph-mds.bfudz.asok perf schema. t( E9 v* N8 R' x
ceph daemon /var/run/ceph/ceph-mds.bfudz.asok perfcounters_schema
: a5 ^: N% {' E7 T2 D( Uceph daemon /var/run/ceph/ceph-mds.bfudz.asok 2/ D9 o3 _$ @% |
ceph-osd/ceph-mon/ceph-mds有一些自己的独特的注册函数,大家检索register_command可以检索的到这些独特的命令。
5 k4 u+ D$ w6 A* a( ?. {7 O. A/ _2 q+ I" V( d$ f& }

  y0 Y- d) l) `, }. G7 S7 @6 `这个AdminSocket 对应的线程,在common_init_finish函数中负责创建。
; V# o  M# a! p
/ w/ p: h5 Y1 {( X+ o7 e7 evoid common_init_finish(CephContext *cct)* M: n- m% }8 k# ^
{2 s0 P/ i" a* V) N6 a3 J" j/ |3 \
  ceph::crypto::init(cct);
! r0 d# t. I; o: h9 B  j0 I, u0 z  cct->start_service_thread();
6 ~4 S; v$ j( l# x  if (cct->_conf->lockdep) {; ]9 s* |5 H! ?+ B1 R( v1 v
    g_lockdep = true;: m1 q! V! W5 w$ h% L% X% n
    ldout(cct,0) << "lockdep is enabled" << dendl;
- D- P( s. u/ U: R5 x: l8 s+ c    lockdep_register_ceph_context(cct);( w- Z* b- f! [3 J! ^& o5 k
  }
8 K" i4 W8 k' r9 I3 ]}
0 x- o5 K# w: Z9 z! ?而在start_service_thread函数中会调用adminsocket的init函数:9 M1 }$ W, C; F0 I8 k  j) A

8 {  S+ g  G5 X$ V+ k' uvoid CephContext::start_service_thread()
6 R' p4 R2 w7 I. z2 [6 E{
9 g' z; b1 z  ?; W0 E  pthread_spin_lock(&_service_thread_lock);
: l/ T* }7 e3 r  m7 \  if (_service_thread) {
5 G9 d7 ~7 P5 m3 \    pthread_spin_unlock(&_service_thread_lock);
3 A2 q% I# p' R! R0 t    return;) z9 }/ }+ Q: b+ ^
  }) z, N7 d3 e% q4 M3 Y, u
  _service_thread = new CephContextServiceThread(this);+ p' d# _# m, d6 }
  _service_thread->create();
5 ]1 f* r6 ?3 |  pthread_spin_unlock(&_service_thread_lock);" ~8 {- [; C. }) O3 i5 C& \/ g
  // make logs flush on_exit()
( X) E+ j% p. d; P  if (_conf->log_flush_on_exit)
6 U7 s9 O" r- @# q5 Y# E    _log->set_flush_on_exit();) \7 a4 q* r% e7 I% ]
  // Trigger callbacks on any config observers that were waiting for
* q) l. z$ c+ n  // it to become safe to start threads.& x+ `7 B4 r' m9 q, L% ^" X
  _conf->set_val("internal_safe_to_start_threads", "true");) u7 b7 N( p6 H) z6 ~& s
  _conf->call_all_observers();
, x( N% z& s6 R: K6 e  // start admin socket
5 p* o% M( N3 _( s& t$ f  if (_conf->admin_socket.length())8 \8 O/ M4 w& H' f1 \; }9 P. h
    _admin_socket->init(_conf->admin_socket);
7 _5 s' K8 j& [$ L& O; T}
5 T, Z9 l+ C  x0 f" h1 E/ z! v' G4 _' B' f1 z- Q& _9 v! W
接下来可以分析下admin_socket的初始化函数init:' h6 }0 c0 u; `+ ^/ ^8 m, c
  S- n- v2 O2 b& q( d) C
bool AdminSocket::init(const std::string &path)6 i/ z7 j7 x! z+ k4 e1 x3 m$ `4 I
{2 D* r( S7 x% ^  d# B# i
  ldout(m_cct, 5) << "init " << path << dendl;8 c2 X1 @1 q4 Y/ p% H1 _& ]
  /* Set up things for the new thread */
. V. m; s% y4 z4 |  std::string err;
2 N# s  z" X, e& h1 R. j  int pipe_rd = -1, pipe_wr = -1;
6 v5 a! Y8 S0 N- A% d% {" Y" e1 s# C  err = create_shutdown_pipe(&pipe_rd, &pipe_wr);7 X) p8 {+ M4 L7 B/ a, c7 t
  if (!err.empty()) {
0 |( r! M4 I; f1 E. _+ ^7 M$ `" o7 S    lderr(m_cct) << "AdminSocketConfigObs::init: error: " << err << dendl;' a3 b, L% m( ]+ g# E! F! U
    return false;
; v, W) Y; U/ A) z7 i+ z  }9 {( }  R- f1 |1 [- K# {
  int sock_fd;
: R4 h" n7 i$ f$ L0 j  err = bind_and_listen(path, &sock_fd);4 Z4 a. r8 D% h. u/ i
  if (!err.empty()) {
8 d: |3 u% D9 a& m% O0 [7 S    lderr(m_cct) << "AdminSocketConfigObs::init: failed: " << err << dendl;& c" e8 E/ O& S3 I4 Y+ A6 b
    close(pipe_rd);
. i, @) {4 g, S- X8 t3 O    close(pipe_wr);
- [. b" V) u  t/ Q9 G3 Y    return false;; [& M  |( R8 m9 t. B, H: J5 B/ R
  }, F, d% i( N9 q4 x  k* C
  /* Create new thread */
! i" s, |. ^; u  m_sock_fd = sock_fd;2 |# K5 P  y0 [0 V
  m_shutdown_rd_fd = pipe_rd;: C( M3 B, G4 n% C
  m_shutdown_wr_fd = pipe_wr;/ ^. ]& Q) E1 S
  m_path = path;% g2 t# {0 h0 |- z# \, F9 y  L+ C0 s
  m_version_hook = new VersionHook;* u( D* ~, ]8 o; \( T" d9 w' a
  register_command("0", "0", m_version_hook, "");
; B4 m# t% U+ Q5 K2 c1 N2 n  register_command("version", "version", m_version_hook, "get ceph version");; e: h* P4 g" R) m# k7 L
  register_command("git_version", "git_version", m_version_hook, "get git sha1");
8 l" |8 Y" Q# d. _  m_help_hook = new HelpHook(this);
8 }! W$ t/ H5 p! y  register_command("help", "help", m_help_hook, "list available commands");9 s) s3 F3 M6 v1 ~1 s
  m_getdescs_hook = new GetdescsHook(this);, X3 L2 O! h4 ?" F& B
  register_command("get_command_descriptions", "get_command_descriptions",
' `& x2 _! E7 s; c, L' c  m_getdescs_hook, "list available commands");
$ k8 U, p4 f9 A) M  create();
* K& S- |8 R' l* o  add_cleanup_file(m_path.c_str());; x+ U: v- `5 L4 M) H3 P! v
  return true;/ h) p3 r/ ^* s, \
}7 e2 g: I- |7 n1 z3 f! R2 T

" ?' s: \+ s  q! H7 E首先是创建了管道,读取端的文件描述符记录在m_shutdown_rd_fd中,写入端的文件描述符记录在m_shutdown_wr_fd中。
2 ~: g# d% e) W) V: z从变量名字也可以看出,该文件描述符的作用是收取关闭信息。因为adminsocket一旦创建,必须能够通知到该线程及时退出。
. `% f! q' Y0 P0 z% G8 \" e退出的事情会写入管道的写入端,而线程会通过多路复用接口,监听读取端,一旦发现m_shutdown_rd_fd中读出内容,线程就知道,可以退出了。
0 J8 r* p: x; O5 m& p6 \* n按下不表。
7 v  }7 C2 }* S3 c( c  s3 N8 ]8 g" s; ]9 Z9 U
AdminSocket最重要的是监听发过来的请求,它是用socket来实现的,初始化在bind_and_listen 函数:
& c2 M  F: o. J4 b' }- x# F" _, c6 W7 A
std::string AdminSocket::bind_and_listen(const std::string &sock_path, int *fd)
) `. l" ^3 o& i{
" w' ?9 z" R/ z8 G; f# G  ldout(m_cct, 5) << "bind_and_listen " << sock_path << dendl;
: m* V5 R5 g; q
( t5 j9 P6 d+ A3 ~' a  struct sockaddr_un address;
" J2 A+ ]$ t5 l* u  if (sock_path.size() > sizeof(address.sun_path) - 1) {
$ p- B0 `9 \, i) `    ostringstream oss;
7 F: w9 C' W7 G' G' I% e. F8 L+ e    oss << "AdminSocket::bind_and_listen: "
- |  T0 R' H9 F* K, W6 V    << "The UNIX domain socket path " << sock_path << " is too long! The "; Z9 T% \0 i3 }6 T' ?
    << "maximum length on this system is "
2 R+ [% I" U0 S& _" ^    << (sizeof(address.sun_path) - 1);4 k/ b8 L2 m  ~. V, p
    return oss.str();
0 x" `1 |+ m# F$ i9 ]  }* o" y$ W) m8 P* V
  int sock_fd = socket(PF_UNIX, SOCK_STREAM, 0);
/ |# v) W* L2 R5 ?: S' S  if (sock_fd < 0) {# `0 d8 o. @5 a4 g$ c  F; i
    int err = errno;" {  R9 U3 E/ ?" f0 R
    ostringstream oss;
$ s& J+ x1 w! O5 ^9 d    oss << "AdminSocket::bind_and_listen: "+ s* N+ }7 U, c
    << "failed to create socket: " << cpp_strerror(err);& }( }" E2 j7 s' K
    return oss.str();: e- A) m) m# q# V) P
  }
1 }  N6 m' i) E* l9 ]) L" R/ A/ p  int r = fcntl(sock_fd, F_SETFD, FD_CLOEXEC);
* `3 F# [9 X1 R1 {) c- K  if (r < 0) {
$ a* M4 J* M& I6 I    r = errno;5 Y7 O8 }, j( @' u
    TEMP_FAILURE_RETRY(::close(sock_fd));5 {( V) L, M; ^0 s3 j
    ostringstream oss;- p& F. A0 I. [* A: h
    oss << "AdminSocket::bind_and_listen: failed to fcntl on socket: " << cpp_strerror(r);
- [; L( A/ n* v) F    return oss.str();
+ W' {+ q4 w" |/ r  }
. p% L* Z6 Z6 G: Q  memset(&address, 0, sizeof(struct sockaddr_un));% E6 R$ |, }: ^0 N1 O
  address.sun_family = AF_UNIX;) E5 l4 H8 C" }  H& ?0 a& \% T* z  d
  snprintf(address.sun_path, sizeof(address.sun_path),9 m1 v8 w% g1 f( H$ v* ~# \
     "%s", sock_path.c_str());1 l% a5 ]) N$ S
  if (bind(sock_fd, (struct sockaddr*)&address,
! R( o  {$ C  d8 Y3 g     sizeof(struct sockaddr_un)) != 0) {4 A) U. B; r, @  |
    int err = errno;
% ~9 J% v6 `6 }4 P  x: ?    if (err == EADDRINUSE) {
2 a! I% u8 o2 {& @1 Q      AdminSocketClient client(sock_path);
  w% M1 d% B5 ]. ~      bool ok;
7 x( @% Q" B4 R, `4 i* q      client.ping(&ok);
' R" _: m  m3 L5 n      if (ok) {9 h6 h" P* y/ e
    ldout(m_cct, 20) << "socket " << sock_path << " is in use" << dendl;
; F; \6 |/ k' b1 h: s9 p# m    err = EEXIST;
+ }! ?* |2 \+ H6 o1 S      } else {
8 X9 {) S/ Y& m* v7 m    ldout(m_cct, 20) << "unlink stale file " << sock_path << dendl;+ S: g! {; N' b6 J
    TEMP_FAILURE_RETRY(unlink(sock_path.c_str()));/ y# K+ l  d- c8 Q2 w% A
    if (bind(sock_fd, (struct sockaddr*)&address,- @/ c7 [, Y6 y  i  L, \
         sizeof(struct sockaddr_un)) == 0) {* Q8 j, [1 a/ N3 Z; c4 P
     err = 0;
" p5 X: D" U4 O3 \# Y7 x8 Q    } else {
. O5 W+ t8 C' k1 _7 v7 b* T     err = errno;* ]( w0 Z  H: ^# W7 [0 R
    }6 L( _6 B4 n& X3 f# i- X6 d+ `
      }( n( N2 X: q' S3 l0 _
    }1 E& o0 A1 V" i. O$ J
    if (err != 0) {
0 s8 E: i  {; }7 R  ?! m9 E- ]+ L) p/ d      ostringstream oss;
7 r4 o3 {1 l" t" }+ E      oss << "AdminSocket::bind_and_listen: "- k4 T) q& m2 |  z$ E
     << "failed to bind the UNIX domain socket to '" << sock_path  d8 T3 e* v1 r' R8 b
     << "': " << cpp_strerror(err);
1 \. ^: s( z  B2 c( I      close(sock_fd);; h3 J7 H  ?1 L9 A5 ?  d3 w
      return oss.str();
& }( [! X) P0 N9 G7 u7 l- K    }: J9 o! h  x! K& Y
  }4 q3 d) }* f0 ?. d# p0 L
  if (listen(sock_fd, 5) != 0) {
0 V2 U; T4 a8 y5 z    int err = errno;
5 L0 f; r* ^% r+ m4 l    ostringstream oss;
- s  D' }( A7 o, T# z" j2 W    oss << "AdminSocket::bind_and_listen: "
* `& s" S9 y' T3 u     << "failed to listen to socket: " << cpp_strerror(err);2 e! z% `6 z: h8 c% q" J4 I! ~
    close(sock_fd);' J. N) D  r% G8 j- p5 s& x
    TEMP_FAILURE_RETRY(unlink(sock_path.c_str()));
9 U) g0 z7 u" Z6 N' V; g    return oss.str();
, O5 B/ m3 g4 S( s; z( n  u  }6 m4 n0 |! D6 E3 g; C9 ]
  *fd = sock_fd;
8 A' ?  }! S( G: D- c9 [$ Q  return "";+ X. z% d/ h, h8 J
}" f! ]8 h6 R& r# N) `3 W, Z

9 V% u$ ?9 f- t9 F" B4 v  H这个函数做的事情并不难理解,做的事情比较老套:
& ~; k! [5 f3 l: a, n0 P1 Q1 创建一个socket,该函数需要一个入参,指定socket路径名:
7 {) Q3 ?- p& q2  bind( Y. K  t- P. @8 y
3 listen
" ]% {7 c7 B$ r而传入的路径名是这个9 ]) s. Y/ ^. j5 p
root@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok config get admin_socket7 L& E+ c) e8 S2 |1 T  U  a
{ "admin_socket": "\/var\/run\/ceph\/ceph-mds.bfudz.asok"}/ W( H+ c& O3 p& M8 v8 U: E$ l
socket建好之后,因为线程还没有创建,所以至今还没有accept接口 。然后我们再次回到init函数,该函数又注册了几个函数:, f7 H, @/ n/ D3 ?# z* n% B

( G# Z. ?2 c$ D6 y3 [m_version_hook = new VersionHook;$ |8 ]2 [* H6 O  u+ y$ `
  register_command("0", "0", m_version_hook, "");
$ w: i- q& z. Y" [# V  register_command("version", "version", m_version_hook, "get ceph version");
! y2 i: \+ D/ m  register_command("git_version", "git_version", m_version_hook, "get git sha1");
+ W$ _5 A' n' H6 n8 n4 V6 d1 i; t8 y2 n  m_help_hook = new HelpHook(this);! o+ u/ S. R- S1 @" A3 b' p
  register_command("help", "help", m_help_hook, "list available commands");
" H6 g) b, @, x4 x4 l3 z. z  m_getdescs_hook = new GetdescsHook(this);6 s6 Z( o- G/ h  W- v
  register_command("get_command_descriptions", "get_command_descriptions",
8 V6 i' R+ w8 p8 ?% `& y  m_getdescs_hook, "list available commands");
) M3 |4 ]8 Y6 \; k: g; c( g% {& P* F这几个函数的重要性并不大,基本是用来查版本信息的,如下所示,按下不表。7 P; y$ w! `, g+ A
; }" L; o- y* G: @. B2 t! h2 V
root@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok version, b3 ^* ^  V* B% X6 |3 c6 ^
{"version":"0.67.9-222-g014b35f"}! K# c( j8 \9 ?: v0 `0 @" A& q
root@test3:/var/run/ceph# ceph daemon /var/run/ceph/ceph-mds.bfudz.asok git_version+ Y/ O1 F' s) a
{"git_version":"014b35fc1ee0a1ad1f699a3705f3481a88614d36"}0 o) j# u4 R7 W% T
root@test3:/var/run/ceph#/ G4 K3 L: v) k0 [2 t- A
5 F* `/ c+ U+ Q
init函数最后调用了create函数。create函数是老朋友,前面分析Log的时候已经提到,对于Thread这个类,做的事情无非就是创建线程。关键内容是,线程执行的函数是哪个? & C7 x4 W* X2 x4 J% ]5 T
和Log一样,是entry函数。AdminSocket类也有entry函数,该函数是AdminSocket 对应线程指定的函数:/ w/ L2 O  C3 ~

9 P3 D1 M  p6 N6 k5 H# svoid* AdminSocket::entry()
* j: n% k- J/ c$ b" t5 I{
6 }' m5 Y; p. C$ J& s4 N' o8 F  ldout(m_cct, 5) << "entry start" << dendl;
7 R9 {. T" |% N/ t- a7 X  while (true) {
! p6 k" D* q* \    struct pollfd fds[2];& h% ]' d6 q+ [. b
    memset(fds, 0, sizeof(fds));
. N6 F" q: s) `4 O/ _4 W  h) L    fds[0].fd = m_sock_fd;
9 f: C+ _' F( Y* w& ~$ ?1 o    fds[0].events = POLLIN | POLLRDBAND;2 M! T7 E' A" b
    fds[1].fd = m_shutdown_rd_fd;
7 W! H; c. n. X3 G# i+ D) \    fds[1].events = POLLIN | POLLRDBAND;
3 U& |5 n: m; Y. B+ U% W4 A8 H    int ret = poll(fds, 2, -1);! Y& R' `6 v8 ^" R
    if (ret < 0) {
! L5 q& o) n+ e" o! a      int err = errno;. P. G9 Q& y5 h+ b1 I) M+ o/ x
      if (err == EINTR) {4 P* t! d# l# p& T+ D
continue;
/ \  H, w; N6 `# H; e      }
6 l7 E& A% f, X, r8 w9 u5 {      lderr(m_cct) << "AdminSocket: poll(2) error: '"
0 G* b0 ^) a1 L5 n  << cpp_strerror(err) << dendl;5 R' j; b6 f; ?; I
      return PFL_FAIL;! r+ E. t4 K  `+ g# ~. n" F
    }# s3 B0 d  j) t
    if (fds[0].revents & POLLIN) {& ~) |1 I' G  j7 J* B' ?' y9 v) U
      // Send out some data6 Y9 E5 i, T6 ^# Q
      do_accept();3 i& c) Z/ u( H
    }9 Y3 Q4 P8 `* y( x" u
    if (fds[1].revents & POLLIN) {
7 c. ~1 t! o4 X2 P  ~3 i0 ~8 q      // Parent wants us to shut down
6 t9 R' s* H; Z9 h3 _5 X' B      return PFL_SUCCESS;) Y0 N- H- T2 x) O: V
    }
6 {8 Y* o8 ]; e  D/ j$ I& i8 ]  ?  }
/ ~1 [8 d1 y$ S  ldout(m_cct, 5) << "entry exit" << dendl;
6 e( r% o8 w/ d0 \' [. `. u}
0 B( x  H$ ~7 h; V5 O# K4 ^( m+ H; d( ~
这个线程函数比较简单,它监听socket fd和管道的读取端。1 i7 a/ @  t4 v% n% ^2 h7 H. C
1 管道的读取端负责管理何时退出
4 e2 j* U- l% n6 U2 socket fd 负责监听用户发过来的指令。& B$ p3 V* Q4 ]! f2 [4 q

# v2 s3 T6 r9 u. `( H处理用户发过来的命令,是do_accept函数干的事情:
5 K' J: ?* z4 l  \1 g, P$ S
$ w  Z$ D( v* N2 X" b* E$ Q3 Abool AdminSocket::do_accept()
5 `% ~$ A) M, T{
5 {, Y% r" g) \  struct sockaddr_un address;
8 [: ?5 y+ W" _$ k  socklen_t address_length = sizeof(address);' q" M7 W; T6 T8 Y! m
  ldout(m_cct, 30) << "AdminSocket: calling accept" << dendl;2 c; s! T' ?7 Q& I" F
  int connection_fd = accept(m_sock_fd, (struct sockaddr*) &address,0 R  v% W% `$ T* y- Q
             &address_length);
- N/ Q% L- w# i) _; C  ldout(m_cct, 30) << "AdminSocket: finished accept" << dendl;
9 V  F" K. _0 x( T% o  if (connection_fd < 0) {
9 L4 M! ^+ ~0 u1 u    int err = errno;
$ u8 Z- E  A. T' f9 ^    lderr(m_cct) << "AdminSocket: do_accept error: '"/ ~% c5 Z* R: g$ e8 C# W" B4 x( I
             << cpp_strerror(err) << dendl;. d4 j) h# g- I2 n" j
    return false;
$ c0 U& a) `2 z" @% Y  }
# c7 [7 |. l7 e" f. y( l; J: _, p
+ L- e6 |  S3 Q, E& P) F$ _  char cmd[1024];
% q, r: B5 Q! x$ n+ j5 z  int pos = 0;
9 q; H5 _4 l) B( N5 z  string c;( J! _3 i" l/ c/ R+ a7 `. _" r5 B7 ?
  while (1) {
8 v0 g  s4 A; @* o0 @    int ret = safe_read(connection_fd, &cmd[pos], 1);2 }& g6 w% r  X/ E8 ^( L$ s9 s
    if (ret <= 0) {; A( Q6 s) x2 D
      lderr(m_cct) << "AdminSocket: error reading request code: "
) ]) C. D6 g2 f. q8 _: h# B" N         << cpp_strerror(ret) << dendl;
& d* K- l1 l6 M/ e( E- v: }/ A3 N      close(connection_fd);
% [/ Y$ }( b2 S8 P' i+ U5 g: K% P      return false;
! z' J1 {" o4 r9 `    }) ]: F  t& I! o- ^; w; Q# }
    //ldout(m_cct, 0) << "AdminSocket read byte " << (int)cmd[pos] << " pos " << pos << dendl;
4 G- t# f( n  T. E& p7 t    if (cmd[0] == '\0') {$ g* r1 X# x, d
      // old protocol: __be32
; [. H$ ?. {4 x* k1 ^' K) b      if (pos == 3 && cmd[0] == '\0') {8 X0 f. v' A7 i3 @$ D. v* h2 Y6 {1 F
    switch (cmd[3]) {
" w. i' h; R; [0 \    case 0:
  ~  G' g: }7 M: `8 \     c = "0";
2 k/ t" G. _6 @( y# ]     break;
; D" ?2 E0 I& m    case 1:
+ m1 P6 v9 C, n7 \6 ~3 I" {1 r     c = "perfcounters_dump";7 q! r$ a; ~7 D
     break;3 k+ D4 @0 a" [
    case 2:1 Z! {8 G2 @: ]& [; n% X1 K
     c = "perfcounters_schema";
/ J5 M+ |$ W) ^2 l     break;3 O, Q4 K$ t8 O( X% O' }6 ?
    default:- o6 M/ B1 t* p: c# b7 O( f
     c = "foo";2 J. q3 r5 q- d, T
     break;
+ M  X% m8 }; C2 O6 e0 y/ z  d: q    }
( X( c! I! x; A- O/ ]    break;- D9 U5 D2 ?! \# `) p! Y
      }
5 X/ c1 I% M; N1 s8 W: P2 k    } else {
* h' ~/ k# @: [/ V" ~      // new protocol: null or \n terminated string
6 R. V, [' ?: z% m! P( s      if (cmd[pos] == '\n' || cmd[pos] == '\0') {
4 N8 y1 Q1 ]4 |$ b# V    cmd[pos] = '\0';* {2 v) }9 a7 Q. W
    c = cmd;
0 t: z2 r. G! S% C9 l+ N$ Y% E    break;- e  v5 j& g' }9 L! {/ l4 C! o
      }% d6 L+ _& v% Y2 j9 ~% X8 n
    }
: q3 I# H! [9 ?& A! z  ]    pos++;2 t& C' j+ S/ Z; f8 w4 w3 Q
  }0 a  |, y) j2 n* P/ Q$ R

( [7 c, p. g4 v) O# \9 \$ _  bool rval = false;
/ D1 h" U0 I6 `, s! @+ m
# M  k% f2 g1 j3 ~) M2 U  map<string, cmd_vartype> cmdmap;
0 g2 X: b$ m# ]$ }1 x1 `, F  string format;
1 ?+ `0 `( b, J% P% d, n! k  vector<string> cmdvec;
2 |! b1 N' S8 D5 m  stringstream errss;% M9 C3 E- O- H4 n# D
  cmdvec.push_back(cmd);
1 L$ C; _# g% y2 E& Z5 z! h  if (!cmdmap_from_json(cmdvec, &cmdmap, errss)) {
! D: f- S( M% s8 J- n  A    ldout(m_cct, 0) << "AdminSocket: " << errss << dendl;  ^& E, b; |9 O0 W6 q
    return false;3 `% ]+ u2 T) |3 q
  }
9 @4 Y( D  f0 Y  cmd_getval(m_cct, cmdmap, "format", format);- r, o; Q. H* i% b* Y
  if (format != "json" && format != "json-pretty" &&7 ~8 h  G; K1 j; M1 }. i9 G
      format != "xml" && format != "xml-pretty")
# Q: R4 `0 o0 A% E! G( z9 {    format = "json-pretty";
* W: q  c& D7 w8 I' N1 _+ X) J$ e" k  cmd_getval(m_cct, cmdmap, "prefix", c);
- c8 X: q* g3 |3 E* x5 J- P8 P
, `% _$ u! i0 |6 r! I$ K, a) p4 `  string firstword;9 o3 t! I7 n+ k: O( C( Z6 C/ B& ]
  if (c.find(" ") == string::npos)
7 _- r& L3 s9 x. F! h" |    firstword = c;
% _* J$ X  c6 p" b" [  else
# @- _: P/ B8 j" G( Q% g- b) ~    firstword = c.substr(0, c.find(" "));
# `- o0 c/ F! a# g* i. X* z' }7 N) ]6 Y+ H: g! ]* y
  m_lock.Lock();
& Z* m- H; q0 [: B9 [  map<string,AdminSocketHook*>::iterator p;" X, E2 H* \3 C$ p( l  R& i6 d
  string match = c;$ ~  ?8 g3 b# c9 z
  while (match.size()) {
; |* F' U# C! p  F    p = m_hooks.find(match);. o" @4 j/ a# H) }! d
    if (p != m_hooks.end())
6 [* A, }+ X# D2 s5 U# A  Y      break;: P& |2 e! M4 F. X2 ^- r# E- o
   
2 O. c% ~* O$ q" C2 m& v    // drop right-most word
4 P2 o( B/ c/ h0 k6 {( I    size_t pos = match.rfind(' ');" S) C4 C3 |2 a6 S$ h" {  }
    if (pos == std::string::npos) {
( E4 _6 t' \% U/ n& U1 @      match.clear(); // we fail
0 l0 p# {; c  g; q      break;
! i( W' {* e) Q7 Q) S9 o    } else {
! Q; l5 @3 d) M1 A% ~      match.resize(pos);, V% r9 v4 O0 o- W8 w# W8 R
    }5 M& H/ `& x7 e# O+ d
  }& W' J/ U6 S, P3 Q( c4 O

* ?  h0 }  d) q( O/ W+ C  O  bufferlist out;" B; v$ t1 M2 o
  if (p == m_hooks.end()) {
! k0 z! @0 M9 W1 q1 S    lderr(m_cct) << "AdminSocket: request '" << c << "' not defined" << dendl;
! G) e  N5 I) x# C1 A$ _. g  } else {
7 w, S* I1 L" U1 @    string args;
7 Q* S7 f7 p; Y' X  w& F6 I  t    if (match != c)& d- q/ [$ R+ W1 E8 w
      args = c.substr(match.length() + 1);
9 d9 x9 k* Z: J* G' ^    bool success = p->second->call(match, cmdmap, format, out);
5 Q5 G, Y4 L# [6 ^7 I/ z* k1 n    if (!success) {' N+ v: I" Q1 v( q
      ldout(m_cct, 0) << "AdminSocket: request '" << match << "' args '" << args
( |* R: i6 U: t6 v         << "' to " << p->second << " failed" << dendl;( D4 @# M) g# B5 B5 g
      out.append("failed");" S( a' R% g4 ^% S$ z* X
    } else {' ^" r: Q4 z/ o( f8 {" V( L) I
      ldout(m_cct, 5) << "AdminSocket: request '" << match << "' '" << args6 P: W0 c2 |. x# I
         << "' to " << p->second
* M" v8 g$ |' {/ y, Q2 @: v. u! A         << " returned " << out.length() << " bytes" << dendl;
% q' j- T: F' ^    }' [# D3 p/ _5 f1 p
    uint32_t len = htonl(out.length());/ w9 s5 ?+ D2 ]) Y* k, x8 Z
    int ret = safe_write(connection_fd, &len, sizeof(len));& Q! w/ s! P4 ?% {
    if (ret < 0) {
3 @* N4 _/ j) \4 Y" i. i# K      lderr(m_cct) << "AdminSocket: error writing response length "
, i) z8 `( A: j: Y7 u( Z6 V         << cpp_strerror(ret) << dendl;9 X: g# M  p6 j  N
    } else {
9 `* n8 v6 d; A1 l' c- m9 s  G% l( R; ^      if (out.write_fd(connection_fd) >= 0)
1 w  `( l' n1 ^2 \: Q& _    rval = true;9 U$ ~: q# @9 U7 C& O/ ~
    }
" D+ [5 j% c+ W; V) L  }( d% K+ l# k7 I$ H; O2 d" @
  m_lock.Unlock();+ s+ P. e5 U* v1 `

6 N) z3 Q7 ~: J0 m  TEMP_FAILURE_RETRY(close(connection_fd));
6 F% w3 H! m7 U3 X' ~  ]  }* P  return rval;
0 g) G( y& b. ]) T6 {9 w# N7 W}
. J; _8 K3 [2 ]; Q
8 f: \- L$ g4 Q0 q这个函数有点长,但是并不复杂。简单说,如果有个client尝试 connect ,该线程就poll就会感知到,然后进入do_accept函数。
0 B! B( w: F" |do_accept首先执行accept,和client 搭上线,然后开始通信。
- Q) a5 F" y# s6 m: Z; ?: Z4 Q
6 b$ D% l/ s( A. J) |! Csafe_read负责 读取客户发过来的指令。前面已经提到过,AdminSocket支持的命令是有限的,初始化之前都已注册过了。
1 F2 D- h0 F) I: L5 G如果client 发来的指令时注册过的指令,就见招拆招,返回相应的结果给客户端。7 x1 D5 m5 E  ?' z/ d
! k0 I! o, x4 [& b& H% T+ u, x4 ]- L
每一个命令的字符串,都是和一个AdminSocketHook 的类型关联的,但是一个AdminSocketHook可以对应多个command2 _* c* v% \6 M0 k) [
2 l  _( h' w! J( P1 x
std::map<std::string,AdminSocketHook*> m_hooks: x( s( t, g8 V" K
比如说 config show  / config get   / perf dump对应的多事 CephContextHook,前面已经提到过。& {; M6 t/ {  t; t( g' k

/ T+ }5 J7 t, \( k' t) _  p) f见招拆招的函数,就记录在对应的Hook上:
" M% j! e) {6 m; O
% F2 R# [6 w- w" K5 e5 qclass CephContextHook : public AdminSocketHook {3 C' v0 a! P* b6 u+ b3 y
  CephContext *m_cct;
+ V1 H, H. f) gpublic:
1 P. P; o7 J% w/ F  CephContextHook(CephContext *cct) : m_cct(cct) {}
; W& S- G# X& o; l  bool call(std::string command, cmdmap_t& cmdmap, std::string format,  H( I( G! g( \. C* j' p8 h
   bufferlist& out) {
+ H0 a, v1 Q: A# D    m_cct->do_command(command, cmdmap, format, &out);
/ }6 a/ t6 ?0 T9 V/ S    return true;
* C. ^& S7 \) N5 Q- T! x  }$ D: q! f+ l$ o; H  Y& B$ R' f% l
};
$ U  z6 h) @! b' b. d( R8 a
  l6 E- ?8 U( f2 X8 y2 k5 H9 v9 Q9 \8 B8 K
do_accept函数中黄色的一行,具体是实现,就是对应Hook的call函数,对于CephContextHook,就是这个类的call方法。
$ ~" E1 D) u/ C/ L2 L- c
# `0 d: ~% e: P3 f4 d7 M. d' @( W下面我们看下CephContextHook的call方法:即它的do_command函数:1 }, Q  H, `  x. t

. q3 d1 p% O. J! p! s# Dvoid CephContext::do_command(std::string command, cmdmap_t& cmdmap,
" z; G0 j1 \0 k  [3 ~+ U             std::string format, bufferlist *out)
7 c( I" a# s( e' \; d5 F) z% g: y{3 t) Z# Z# |/ a/ N" Y- Y
  Formatter *f = new_formatter(format);
! H. ^* f) @, k' h  if (!f)
# C  B/ R. o6 B5 |( y# |) }    f = new_formatter("json-pretty");9 {6 d! Y* u, V# g8 |8 i1 v
  stringstream ss;
% u$ z) e) `2 e- S) }  T  for (cmdmap_t::iterator it = cmdmap.begin(); it != cmdmap.end(); ++it) {
' Z' t- u7 Z  i    if (it->first != "prefix") {
8 T% N* Y* O& r0 t; |      ss << it->first << ":" << cmd_vartype_stringify(it->second) << " ";
/ a; v$ b2 D; N    }* _* a5 b; D' M8 Y+ S
  }
" P/ ^7 n- \6 V, R" u7 e! ?  lgeneric_dout(this, 1) << "do_command '" << command << "' '"
- \! {& }2 v; w3 F  }4 `3 X$ X             << ss.str() << dendl;# C% I* Q: ~# }6 b; X6 M
  if (command == "perfcounters_dump" || command == "1" ||( x/ ^+ {+ N6 j: c" ~2 E" q2 W2 X
      command == "perf dump") {
) _8 D' m/ j* @1 {  w& R* U# f    _perf_counters_collection->dump_formatted(f, false);9 a# {3 ^: x6 m( ~6 |% Y/ ^' e
  }3 g8 M7 J2 u3 h  ~0 G& F
  else if (command == "perfcounters_schema" || command == "2" ||! s- K3 L) L3 R) u, {
    command == "perf schema") {
% F' N" {0 k* p2 b( d3 c' [- Q    _perf_counters_collection->dump_formatted(f, true);# ]+ b) {, B) H* [! I5 @
  }3 O- P5 l1 V. T  X
  else {0 v3 `& ]$ u8 n+ R
    f->open_object_section(command.c_str());: `" R2 z/ ?2 J' O; J+ M
    if (command == "config show") {. W7 @& v/ k' M+ ?5 n
      _conf->show_config(f);
) h+ D' ]: \5 [" L+ b4 p% G. e, |# n) G    }0 t/ Z/ L' I! Q' }5 c" M
    else if (command == "config set") {
. W1 L/ J' j/ r0 l# `& t. M, e      std::string var;
6 t) @$ n. f0 }      std::vector<std::string> val;
# b+ t7 o3 ]  z  }7 V- G% b
" H8 C6 b" D/ \% H( @+ ~      if (!(cmd_getval(this, cmdmap, "var", var)) ||
1 M8 F% O- h5 Y/ H' x7 W- k/ s4 e          !(cmd_getval(this, cmdmap, "val", val))) {
; [; s7 h6 e* q        f->dump_string("error", "syntax error: 'config set '");1 y' [" P4 u4 |5 U6 s6 F" a
      } else {7 \& C0 {2 l/ ~# M( e! Z
    // val may be multiple words6 }" _5 B, q! {) g
    string valstr = str_join(val, " ");4 l- ]+ O# U  \  P
        int r = _conf->set_val(var.c_str(), valstr.c_str());- k9 N: {2 b: y# i% L
        if (r < 0) {
1 C: k5 z# J7 {9 f; g. Y5 W3 h          f->dump_stream("error") << "error setting '" << var << "' to '" << valstr << "': " << cpp_strerror(r);! z6 y% a+ k# C7 s
        } else {
# K: O  ^7 R5 Y0 k* ]          ostringstream ss;
  S2 m2 G6 T: L& t          _conf->apply_changes(&ss);- \  w! J  b9 H  v% q; \) s! n
          f->dump_string("success", ss.str());
1 e+ F# V0 k( ]2 x        }
  P1 D7 k$ h" I* M  A9 r! Z      }
0 h+ k6 J' h. f    } else if (command == "config get") {0 E; t4 F' ^7 d  {  t8 l0 L
      std::string var;
* V8 |. g* q6 [      if (!cmd_getval(this, cmdmap, "var", var)) {
1 e( J% f, g7 b6 a6 e    f->dump_string("error", "syntax error: 'config get '");* P) O, m  l. Z$ z2 }5 Z
      } else {; Q4 ?8 q- Y& d1 m8 c
    char buf[4096];) u0 o( f: U2 o* M' p+ B4 R
    memset(buf, 0, sizeof(buf));4 O8 A( {/ m( }6 C6 B, Q9 @
    char *tmp = buf;4 a" r/ o. {, t# F
    int r = _conf->get_val(var.c_str(), &tmp, sizeof(buf));# v) X+ Y6 }, h
    if (r < 0) {* l4 a- f, @+ K! v+ n
     f->dump_stream("error") << "error getting '" << var << "': " << cpp_strerror(r);
( g$ X  D- H7 y, P$ B    } else {
7 f# d1 Y; z9 w0 W     f->dump_string(var.c_str(), buf);: [# u7 a5 @( [) i- n# p
    }
  N5 W2 t4 {0 y* b      }$ ?# n3 S$ @, b- \. e7 _3 I. V
    } else if (command == "log flush") {
. O' w3 V, |9 `$ p- H! x8 e: t& `+ [/ r      _log->flush();* P) n. r) J1 d: r
    }0 D  Q- Y% C) C  m* ^6 G
    else if (command == "log dump") {
* j! u  B# U6 X# g      _log->dump_recent();
# O* T9 i8 b) J. ^3 s/ c    }
5 N  ]+ ?# I, ]. |9 E- U    else if (command == "log reopen") {0 B2 v3 @8 N# r
      _log->reopen_log_file();" M/ t" u. e! ]5 L# t" e
    }" H: V- f/ Q, m9 z
    else {6 ?. \, F$ S0 `% I6 B* s- X
      assert(0 == "registered under wrong command?"); ; b# e3 X8 l  {$ a  Q. p0 x
    }' [: M/ K7 ?7 S
    f->close_section();
% k$ I, \  h* y& c5 q. j+ B  }# t' h% d& V" w7 p
  f->flush(*out);
6 [  ?5 Q2 a  I' D' i: B3 T  delete f;
# x1 t# R3 \+ w  lgeneric_dout(this, 1) << "do_command '" << command << "' '" << ss.str()
- j& t' l6 s7 v0 d9 O         << "result is " << out->length() << " bytes" << dendl;. o! k+ E( _$ q  }3 s$ g
};* d) n: ?  Z) D3 Z+ [
  毫不意外,前面注册的每一个方法,在do_command函数中都做了处理。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2026-6-12 01:05 , Processed in 0.020628 second(s), 23 queries .

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

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