LVM的libdaemon库实现了守护进程与命令工具之间使用UNIX本地套接字进行CS通信的基础模型。
报文
配置树结构
$ cat libdm/libdevmapper.h
...
typedef enum {
DM_CFG_INT,
DM_CFG_FLOAT,
DM_CFG_STRING,
DM_CFG_EMPTY_ARRAY
} dm_config_value_type_t;
struct dm_config_value {
dm_config_value_type_t type;
union {
int64_t i;
float f;
double d; /* Unused. */
const char *str;
} v;
struct dm_config_value *next; /* For arrays */
uint32_t format_flags;
};
struct dm_config_node {
const char *key;
struct dm_config_node *parent, *sib, *child;
struct dm_config_value *v;
int id;
};
struct dm_config_tree {
struct dm_config_node *root;
struct dm_config_tree *cascade;
struct dm_pool *mem;
void *custom;
};
...
请求和响应报文
服务端:
$ cat libdaemon/server/daemon-server.h
...
typedef struct {
struct dm_config_tree *cft;
struct buffer buffer;
} request;
typedef struct {
int error;
struct dm_config_tree *cft;
struct buffer buffer;
} response;
...
客户端:
$ cat libdaemon/client/daemon-client.h
...
typedef struct {
struct buffer buffer;
/*
* The request looks like this:
* request = "id"
* arg_foo = "something"
* arg_bar = 3
* arg_wibble {
* something_special = "here"
* amount = 75
* knobs = [ "twiddle", "tweak" ]
* }
*/
struct dm_config_tree *cft;
} daemon_request;
typedef struct {
int error; /* 0 for success */
struct buffer buffer;
struct dm_config_tree *cft; /* parsed reply, if available */
} daemon_reply;
...
服务端
守护服务状态
$ cat libdaemon/server/daemon-server.h
...
typedef struct daemon_state {
/*
* The maximal stack size for individual daemon threads. This is
* essential for daemons that need to be locked into memory, since
* pthread's default is 10M per thread.
*/
int thread_stack_size;
/* Flags & attributes affecting the behaviour of the daemon. */
unsigned avoid_oom:1;
unsigned foreground:1;
const char *name;
const char *pidfile;
const char *socket_path;
const char *protocol;
int protocol_version;
handle_request handler;
int (*daemon_init)(struct daemon_state *st);
int (*daemon_fini)(struct daemon_state *st);
int (*daemon_main)(struct daemon_state *st);
/* Global runtime info maintained by the framework. */
int socket_fd;
log_state *log;
struct thread_state *threads;
/* suport for shutdown on idle */
daemon_idle *idle;
void *private; /* the global daemon state */
} daemon_state;
...
守护服务启动、接管和停止
$ cat libdaemon/server/daemon-server.h
...
/*
* Start serving the requests. This does all the daemonisation, socket setup
* work and so on. This function takes over the process, and upon failure, it
* will terminate execution. It may be called at most once.
*/
void daemon_start(daemon_state s);
/*
* Take over from an already running daemon. This function handles connecting
* to the running daemon and telling it we are going to take over. The takeover
* request may be customised by passing in a non-NULL request.
*
* The takeover sequence: the old daemon stops accepting new clients, then it
* waits until all current client connections are closed. When that happens, it
* serializes its current state and sends that as a reply, which is then
* returned by this function (therefore, this function won't return until the
* previous instance has shut down).
*
* The daemon, after calling daemon_takeover is expected to set up its
* daemon_state using the reply from this function and call daemon_start as
* usual.
*/
daemon_reply daemon_takeover(daemon_info i, daemon_request r);
/* Call this to request a clean shutdown of the daemon. Async safe. */
void daemon_stop(void);
...
客户端句柄
每个客户端连接时都会创建一个新的线程和客户端句柄来处理与之对应的请求。
$ cat libdaemon/server/daemon-server.h
...
typedef struct {
int socket_fd; /* the fd we use to talk to the client */
pthread_t thread_id;
char *read_buf;
void *private; /* this holds per-client state */
} client_handle;
...
守护服务回调函数
$ cat libdaemon/server/daemon-server.h
...
/*
* The callback. Called once per request issued, in the respective client's
* thread. It is presented by a parsed request (in the form of a config tree).
* The output is a new config tree that is serialised and sent back to the
* client. The client blocks until the request processing is done and reply is
* sent.
*/
typedef response (*handle_request)(struct daemon_state s, client_handle h, request r);
...
响应请求
$ cat libdaemon/server/daemon-server.h
...
/*
* Craft a simple reply, without the need to construct a config_tree. See
* daemon_send_simple in daemon-client.h for the description of the parameters.
*/
response daemon_reply_simple(const char *id, ...);
static inline int daemon_request_int(request r, const char *path, int def) {
if (!r.cft)
return def;
return dm_config_find_int(r.cft->root, path, def);
}
static inline const char *daemon_request_str(request r, const char *path, const char *def) {
if (!r.cft)
return def;
return dm_config_find_str(r.cft->root, path, def);
}
...
客户端
守护服务句柄与信息
$ cat libdaemon/client/daemon-client.h
...
typedef struct {
int socket_fd; /* the fd we use to talk to the daemon */
const char *protocol;
int protocol_version; /* version of the protocol the daemon uses */
int error;
} daemon_handle;
typedef struct {
const char *path; /* the binary of the daemon */
const char *socket; /* path to the comms socket */
unsigned autostart:1; /* start the daemon if not running? */
/*
* If the following are not NULL/0, an attempt to talk to a daemon which
* uses a different protocol or version will fail.
*/
const char *protocol;
int protocol_version;
} daemon_info;
...
打开和关闭与守护服务的连接
$ cat libdaemon/client/daemon-client.h
...
/*
* Open the communication channel to the daemon. If the daemon is not running,
* it may be autostarted based on the binary path provided in the info (this
* will only happen if autostart is set to true). If the call fails for any
* reason, daemon_handle_valid(h) for the response will return false. Otherwise,
* the connection is good to start serving requests.
*/
daemon_handle daemon_open(daemon_info i);
...
/* Shut down the communication to the daemon. Compulsory. */
void daemon_close(daemon_handle h);
...
构造请求
辅助函数:
$ cat libdaemon/client/config-util.h
...
struct buffer {
int allocated;
int used;
char *mem;
};
int buffer_append_vf(struct buffer *buf, va_list ap);
int buffer_append_f(struct buffer *buf, ...);
int buffer_append(struct buffer *buf, const char *string);
void buffer_init(struct buffer *buf);
void buffer_destroy(struct buffer *buf);
int buffer_realloc(struct buffer *buf, int required);
int buffer_line(const char *line, void *baton);
int set_flag(struct dm_config_tree *cft, struct dm_config_node *parent,
const char *field, const char *flag, int want);
void chain_node(struct dm_config_node *cn,
struct dm_config_node *parent,
struct dm_config_node *pre_sib);
struct dm_config_node *make_config_node(struct dm_config_tree *cft,
const char *key,
struct dm_config_node *parent,
struct dm_config_node *pre_sib);
int compare_value(struct dm_config_value *a, struct dm_config_value *b);
int compare_config(struct dm_config_node *a, struct dm_config_node *b);
struct dm_config_node *make_text_node(struct dm_config_tree *cft,
const char *key,
const char *value,
struct dm_config_node *parent,
struct dm_config_node *pre_sib);
struct dm_config_node *make_int_node(struct dm_config_tree *cft,
const char *key,
int64_t value,
struct dm_config_node *parent,
struct dm_config_node *pre_sib);
struct dm_config_node *config_make_nodes_v(struct dm_config_tree *cft,
struct dm_config_node *parent,
struct dm_config_node *pre_sib,
va_list ap);
struct dm_config_node *config_make_nodes(struct dm_config_tree *cft,
struct dm_config_node *parent,
struct dm_config_node *pre_sib,
...);
...
构建及销毁请求和响应数据:
$ cat libdaemon/client/daemon-client.h
...
daemon_request daemon_request_make(const char *id);
int daemon_request_extend(daemon_request r, ...);
int daemon_request_extend_v(daemon_request r, va_list ap);
void daemon_request_destroy(daemon_request r);
void daemon_reply_destroy(daemon_reply r);
...
发送请求和等待响应
辅助函数:
$ cat libdaemon/client/daemon-io.h
...
int buffer_read(int fd, struct buffer *buffer);
int buffer_write(int fd, const struct buffer *buffer);
...
发送请求并等待响应:
$ cat libdaemon/client/daemon-client.h
...
/*
* Send a request to the daemon, waiting for the reply. All communication with
* the daemon is synchronous. The function handles the IO details and parses the
* response, handling common error conditions. See "daemon_reply" for details.
*
* In case the request contains a non-NULL buffer pointer, this buffer is sent
* *verbatim* to the server. In this case, the cft pointer may be NULL (but will
* be ignored even if non-NULL). If the buffer is NULL, the cft is required to
* be a valid pointer, and is used to build up the request.
*/
daemon_reply daemon_send(daemon_handle h, daemon_request r);
/*
* A simple interface to daemon_send. This function just takes the command id
* and possibly a list of parameters (of the form "name = %?", "value"). The
* type (string, integer) of the value is indicated by a character substituted
* for ? in %?: d for integer, s for string.
*/
daemon_reply daemon_send_simple(daemon_handle h, const char *id, ...);
daemon_reply daemon_send_simple_v(daemon_handle h, const char *id, va_list ap);
...
解析响应
$ cat libdaemon/client/daemon-client.h
...
static inline int64_t daemon_reply_int(daemon_reply r, const char *path, int64_t def)
{
return dm_config_find_int64(r.cft->root, path, def);
}
static inline const char *daemon_reply_str(daemon_reply r, const char *path, const char *def)
{
return dm_config_find_str_allow_empty(r.cft->root, path, def);
}
...