diff options
Diffstat (limited to 'daemons/lvmlockd/lvmlock.c')
-rw-r--r-- | daemons/lvmlockd/lvmlock.c | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/daemons/lvmlockd/lvmlock.c b/daemons/lvmlockd/lvmlock.c new file mode 100644 index 000000000..d9472b685 --- /dev/null +++ b/daemons/lvmlockd/lvmlock.c @@ -0,0 +1,742 @@ +#define _GNU_SOURCE +#include "configure.h" +#include "lvmlockd-client.h" + +#include <stdio.h> +#include <stdint.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include <getopt.h> +#include <string.h> +#include <signal.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <sys/un.h> + +static int quit; +static int info; +static int dump; +static int wait_opt; +static int force_opt; +static int gl_enable; +static int gl_disable; +static int stop_lockspaces; +static char *able_vg_name; + +static int cmd_pipe[2]; +static int cmd_argc; +static char *cmd_name; +static char **cmd_argv; + +#define DUMP_SOCKET_NAME "lvmlockd-dump.sock" +#define DUMP_BUF_SIZE (1024 * 1024) +static char dump_buf[DUMP_BUF_SIZE]; +static int dump_len; +static struct sockaddr_un dump_addr; +static socklen_t dump_addrlen; + +daemon_handle _lvmlockd; + +#define log_debug(fmt, args...) \ +do { \ + printf(fmt "\n", ##args); \ +} while (0) + +#define log_error(fmt, args...) \ +do { \ + printf(fmt "\n", ##args); \ +} while (0) + +#define MAX_LINE 512 + +/* copied from lvmlockd-internal.h */ +#define MAX_NAME 64 +#define MAX_ARGS 64 + +/* + * lvmlockd dumps the client info before the lockspaces, + * so we can look up client info when printing lockspace info. + */ + +#define MAX_CLIENTS 100 + +struct client_info { + uint32_t client_id; + int pid; + char name[MAX_NAME+1]; +}; + +static struct client_info clients[MAX_CLIENTS]; +static int num_clients; + +static void save_client_info(char *line) +{ + uint32_t pid = 0; + int fd = 0; + int pi = 0; + uint32_t client_id = 0; + char name[MAX_NAME+1] = { 0 }; + + sscanf(line, "info=client pid=%u fd=%d pi=%d id=%u name=%s", + &pid, &fd, &pi, &client_id, name); + + clients[num_clients].client_id = client_id; + clients[num_clients].pid = pid; + strcpy(clients[num_clients].name, name); + num_clients++; +} + +static void find_client_info(uint32_t client_id, uint32_t *pid, char *cl_name) +{ + int i; + + for (i = 0; i < num_clients; i++) { + if (clients[i].client_id == client_id) { + *pid = clients[i].pid; + strcpy(cl_name, clients[i].name); + return; + } + } +} + +static void format_info_local_vg(char *line) +{ + char vg_name[MAX_NAME+1] = { 0 }; + char vg_uuid[MAX_NAME+1] = { 0 }; + char vg_sysid[MAX_NAME+1] = { 0 }; + + sscanf(line, "info=local_vg vg_name=%s vg_uuid=%s vg_sysid=%s", + vg_name, vg_uuid, vg_sysid); + + if (strlen(vg_sysid) == 1 && vg_sysid[0] == '.') + strcpy(vg_sysid, "none"); + + printf("VG %s system_id=%s %s\n", vg_name, vg_sysid, vg_uuid); +} + +static void format_info_ls(char *line) +{ + char ls_name[MAX_NAME+1] = { 0 }; + char vg_name[MAX_NAME+1] = { 0 }; + char vg_uuid[MAX_NAME+1] = { 0 }; + char vg_sysid[MAX_NAME+1] = { 0 }; + char lock_args[MAX_ARGS+1] = { 0 }; + char lock_type[MAX_NAME+1] = { 0 }; + + sscanf(line, "info=ls ls_name=%s vg_name=%s vg_uuid=%s vg_sysid=%s vg_args=%s lm_type=%s", + ls_name, vg_name, vg_uuid, vg_sysid, lock_args, lock_type); + + printf("\n"); + + printf("VG %s lock_type=%s %s\n", vg_name, lock_type, vg_uuid); + + printf("LS %s %s\n", lock_type, ls_name); +} + +static void format_info_ls_action(char *line) +{ + uint32_t client_id = 0; + char flags[MAX_NAME+1] = { 0 }; + char version[MAX_NAME+1] = { 0 }; + char op[MAX_NAME+1] = { 0 }; + uint32_t pid = 0; + char cl_name[MAX_NAME+1] = { 0 }; + + sscanf(line, "info=ls_action client_id=%u %s %s op=%s", + &client_id, flags, version, op); + + find_client_info(client_id, &pid, cl_name); + + printf("OP %s pid %u (%s)", op, pid, cl_name); +} + +static void format_info_r(char *line, char *r_name_out, char *r_type_out) +{ + char r_name[MAX_NAME+1] = { 0 }; + char r_type[4] = { 0 }; + char mode[4] = { 0 }; + char sh_count[MAX_NAME+1] = { 0 }; + uint32_t ver = 0; + + sscanf(line, "info=r name=%s type=%s mode=%s %s version=%u", + r_name, r_type, mode, sh_count, &ver); + + /* when mode is not un, wait and print each lk line */ + + if (strcmp(mode, "un")) { + strcpy(r_name_out, r_name); + strcpy(r_type_out, r_type); + return; + } + + /* when mode is un, there will be no lk lines, so print now */ + + if (!strcmp(r_type, "gl")) { + printf("LK GL un ver %4u\n", ver); + + } else if (!strcmp(r_type, "vg")) { + printf("LK VG un ver %4u\n", ver); + + } else if (!strcmp(r_type, "lv")) { + printf("LK LV un %s\n", r_name); + } +} + +static void format_info_lk(char *line, char *r_name, char *r_type) +{ + char mode[4] = { 0 }; + uint32_t ver = 0; + char flags[MAX_NAME+1] = { 0 }; + uint32_t client_id = 0; + uint32_t pid = 0; + char cl_name[MAX_NAME+1] = { 0 }; + + if (!r_name[0] || !r_type[0]) { + printf("format_info_lk error r_name %s r_type %s\n", r_name, r_type); + printf("%s\n", line); + return; + } + + sscanf(line, "info=lk mode=%s version=%u %s client_id=%u", + mode, &ver, flags, &client_id); + + find_client_info(client_id, &pid, cl_name); + + if (!strcmp(r_type, "gl")) { + printf("LK GL %s ver %4u pid %u (%s)\n", mode, ver, pid, cl_name); + + } else if (!strcmp(r_type, "vg")) { + printf("LK VG %s ver %4u pid %u (%s)\n", mode, ver, pid, cl_name); + + } else if (!strcmp(r_type, "lv")) { + printf("LK LV %s %s\n", mode, r_name); + } +} + +static void format_info_r_action(char *line, char *r_name, char *r_type) +{ + uint32_t client_id = 0; + char flags[MAX_NAME+1] = { 0 }; + char version[MAX_NAME+1] = { 0 }; + char op[MAX_NAME+1] = { 0 }; + char rt[4] = { 0 }; + char mode[4] = { 0 }; + char lm[MAX_NAME+1] = { 0 }; + char result[MAX_NAME+1] = { 0 }; + char lm_rv[MAX_NAME+1] = { 0 }; + uint32_t pid = 0; + char cl_name[MAX_NAME+1] = { 0 }; + + if (!r_name[0] || !r_type[0]) { + printf("format_info_r_action error r_name %s r_type %s\n", r_name, r_type); + printf("%s\n", line); + return; + } + + sscanf(line, "info=r_action client_id=%u %s %s op=%s rt=%s mode=%s %s %s %s", + &client_id, flags, version, op, rt, mode, lm, result, lm_rv); + + find_client_info(client_id, &pid, cl_name); + + if (strcmp(op, "lock")) { + printf("OP %s pid %u (%s)", op, pid, cl_name); + return; + } + + if (!strcmp(r_type, "gl")) { + printf("LW GL %s ver %4u pid %u (%s)\n", mode, 0, pid, cl_name); + + } else if (!strcmp(r_type, "vg")) { + printf("LW VG %s ver %4u pid %u (%s)\n", mode, 0, pid, cl_name); + + } else if (!strcmp(r_type, "lv")) { + printf("LW LV %s %s\n", mode, r_name); + } +} + +static void format_info_line(char *line) +{ + char r_name[MAX_NAME+1]; + char r_type[MAX_NAME+1]; + + if (!strncmp(line, "info=client ", strlen("info=client "))) { + save_client_info(line); + + } else if (!strncmp(line, "info=local_vg ", strlen("info=local_vg "))) { + format_info_local_vg(line); + + } else if (!strncmp(line, "info=ls ", strlen("info=ls "))) { + format_info_ls(line); + + } else if (!strncmp(line, "info=ls_action ", strlen("info=ls_action "))) { + format_info_ls_action(line); + + } else if (!strncmp(line, "info=r ", strlen("info=r "))) { + memset(r_name, 0, sizeof(r_name)); + memset(r_type, 0, sizeof(r_type)); + format_info_r(line, r_name, r_type); + + } else if (!strncmp(line, "info=lk ", strlen("info=lk "))) { + /* will use info from previous r */ + format_info_lk(line, r_name, r_type); + + } else if (!strncmp(line, "info=r_action ", strlen("info=r_action "))) { + /* will use info from previous r */ + format_info_r_action(line, r_name, r_type); + } else { + printf("UN %s\n", line); + } +} + +static void format_info(void) +{ + char line[MAX_LINE]; + int i, j; + + j = 0; + memset(line, 0, sizeof(line)); + + for (i = 0; i < dump_len; i++) { + line[j++] = dump_buf[i]; + + if ((line[j-1] == '\n') || (line[j-1] == '\0')) { + format_info_line(line); + j = 0; + memset(line, 0, sizeof(line)); + } + } +} + + +static daemon_reply _lvmlockd_send(const char *req_name, ...) +{ + va_list ap; + daemon_reply repl; + daemon_request req; + + req = daemon_request_make(req_name); + + va_start(ap, req_name); + daemon_request_extend_v(req, ap); + va_end(ap); + + repl = daemon_send(_lvmlockd, req); + + daemon_request_destroy(req); + + return repl; +} + +static int _lvmlockd_result(daemon_reply reply, int *result) +{ + int reply_result; + const char *reply_flags; + const char *lock_type; + + if (reply.error) { + log_error("lvmlockd_result reply error %d", reply.error); + return 0; + } + + if (strcmp(daemon_reply_str(reply, "response", ""), "OK")) { + log_error("lvmlockd_result bad response"); + return 0; + } + + /* FIXME: using -1000 is dumb */ + + reply_result = daemon_reply_int(reply, "op_result", -1000); + if (reply_result == -1000) { + log_error("lvmlockd_result no op_result"); + return 0; + } + + /* The lock_type that lvmlockd used for locking. */ + lock_type = daemon_reply_str(reply, "lock_type", "none"); + + *result = reply_result; + + reply_flags = daemon_reply_str(reply, "result_flags", NULL); + + log_debug("lvmlockd_result %d %s lm %s", reply_result, reply_flags, lock_type); + return 1; +} + +static int do_quit(void) +{ + daemon_reply reply; + int rv = 0; + + reply = daemon_send_simple(_lvmlockd, "quit", NULL); + + if (reply.error) { + log_error("reply error %d", reply.error); + rv = reply.error; + } + + daemon_reply_destroy(reply); + return rv; +} + +static int setup_dump_socket(void) +{ + int s, rv; + + s = socket(AF_LOCAL, SOCK_DGRAM, 0); + if (s < 0) + return s; + + memset(&dump_addr, 0, sizeof(dump_addr)); + dump_addr.sun_family = AF_LOCAL; + strcpy(&dump_addr.sun_path[1], DUMP_SOCKET_NAME); + dump_addrlen = sizeof(sa_family_t) + strlen(dump_addr.sun_path+1) + 1; + + rv = bind(s, (struct sockaddr *) &dump_addr, dump_addrlen); + if (rv < 0) + return rv; + + return s; +} + +static int do_dump(const char *req_name) +{ + daemon_reply reply; + int result; + int fd, rv; + + fd = setup_dump_socket(); + if (fd < 0) { + log_error("socket error %d", fd); + return fd; + } + + reply = daemon_send_simple(_lvmlockd, req_name, NULL); + + if (reply.error) { + log_error("reply error %d", reply.error); + rv = reply.error; + goto out; + } + + result = daemon_reply_int(reply, "result", 0); + dump_len = daemon_reply_int(reply, "dump_len", 0); + + daemon_reply_destroy(reply); + + if (result < 0) + log_error("result %d", result); + + if (!dump_len) + goto out; + + memset(dump_buf, 0, sizeof(dump_buf)); + + rv = recvfrom(fd, dump_buf, dump_len, MSG_WAITALL, + (struct sockaddr *)&dump_addr, &dump_addrlen); + if (rv < 0) { + log_error("recvfrom error %d %d", rv, errno); + rv = -errno; + goto out; + } + + rv = 0; + if ((info && dump) || !strcmp(req_name, "dump")) + printf("%s\n", dump_buf); + else + format_info(); +out: + close(fd); + return rv; +} + +static int do_able(const char *req_name) +{ + daemon_reply reply; + int result; + int rv; + + reply = _lvmlockd_send(req_name, + "cmd = %s", "lvmlock", + "pid = %d", getpid(), + "vg_name = %s", able_vg_name, + NULL); + + if (!_lvmlockd_result(reply, &result)) { + log_error("lvmlockd result %d", result); + rv = result; + } else { + rv = 0; + } + + daemon_reply_destroy(reply); + return rv; +} + +static int do_stop_lockspaces(void) +{ + daemon_reply reply; + char opts[32]; + int result; + int rv; + + memset(opts, 0, sizeof(opts)); + + if (wait_opt) + strcat(opts, "wait "); + if (force_opt) + strcat(opts, "force "); + + reply = _lvmlockd_send("stop_all", + "cmd = %s", "lvmlock", + "pid = %d", getpid(), + "opts = %s", opts[0] ? opts : "none", + NULL); + + if (!_lvmlockd_result(reply, &result)) { + log_error("lvmlockd result %d", result); + rv = result; + } else { + rv = 0; + } + + daemon_reply_destroy(reply); + return rv; +} + +static void print_usage(void) +{ + printf("lvmlock options\n"); + printf("Options:\n"); + printf("--help | -h\n"); + printf(" Show this help information.\n"); + printf("--quit | -q\n"); + printf(" Tell lvmlockd to quit.\n"); + printf("--info | -i\n"); + printf(" Print lock state information from lvmlockd.\n"); + printf("--dump | -d\n"); + printf(" Print log buffer from lvmlockd.\n"); + printf("--wait | -w 0|1\n"); + printf(" Wait option for other commands.\n"); + printf("--force | -f 0|1>\n"); + printf(" Force option for other commands.\n"); + printf("--stop-lockspaces | -S\n"); + printf(" Stop all lockspaces.\n"); + printf("--gl-enable <vg_name>\n"); + printf(" Tell lvmlockd to enable the global lock in a sanlock vg.\n"); + printf("--gl-disable <vg_name>\n"); + printf(" Tell lvmlockd to disable the global lock in a sanlock vg.\n"); +} + +static int read_options(int argc, char *argv[]) +{ + int option_index = 0; + int i, j, c, len; + + static struct option long_options[] = { + {"help", no_argument, 0, 'h' }, + {"quit", no_argument, 0, 'q' }, + {"info", no_argument, 0, 'i' }, + {"dump", no_argument, 0, 'd' }, + {"wait", required_argument, 0, 'w' }, + {"force", required_argument, 0, 'f' }, + {"gl-enable", required_argument, 0, 'E' }, + {"gl-disable", required_argument, 0, 'D' }, + {"stop-lockspaces", no_argument, 0, 'S' }, + {"sleep", required_argument, 0, 's' }, + {"command", required_argument, 0, 'c' }, + {0, 0, 0, 0 } + }; + + /* + if (argc == 1) { + print_usage(); + exit(0); + } + */ + + while (1) { + c = getopt_long(argc, argv, "hqidE:D:s:c:w:f:S", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + /* --help */ + print_usage(); + exit(0); + case 'q': + /* --quit */ + quit = 1; + break; + case 'i': + /* --info */ + info = 1; + break; + case 'd': + /* --dump */ + dump = 1; + break; + case 'w': + wait_opt = atoi(optarg); + break; + case 'E': + gl_enable = 1; + able_vg_name = strdup(optarg); + break; + case 'D': + gl_disable = 1; + able_vg_name = strdup(optarg); + break; + case 'S': + stop_lockspaces = 1; + break; + case 'c': + /* --command path args */ + cmd_name = strdup(optarg); + break; + default: + print_usage(); + exit(1); + } + + if (cmd_name) + break; + } + + if (cmd_name) { + /* + * optind is the index in argv of the first argv element that + * is not an option. + */ + + cmd_argc = argc - optind + 1; /* +1 for cmd_name */ + + len = (cmd_argc + 1) * sizeof(char *); /* +1 for final NULL */ + cmd_argv = malloc(len); + if (!cmd_argv) + return -ENOMEM; + memset(cmd_argv, 0, len); + + j = 0; + cmd_argv[j++] = cmd_name; + + for (i = optind; i < argc; i++) { + cmd_argv[j++] = strdup(argv[i]); + if (!cmd_argv[j-1]) + return -ENOMEM; + } + } + + return 0; +} + +static void run_command(void) +{ + char go[1]; + int rv; + + while (1) { + /* wait for parent to tell us to go */ + rv = read(cmd_pipe[0], go, 1); + if (rv == -1 && errno == EINTR) + continue; + if (rv == 1 && go[0] == 'g') + break; + else + exit(-1); + } + + execv(cmd_name, cmd_argv); + log_error("execv failed"); +} + +int main(int argc, char **argv) +{ + int status; + int pid = 0; + int rv = 0; + + rv = read_options(argc, argv); + if (rv < 0) + return rv; + + /* + * fork child for command before acquiring locks, + * exec command in child after acquiring locks, + * release locks after child exits. + */ + + if (cmd_name) { + if (pipe(cmd_pipe)) { + log_error("pipe error"); + return -1; + } + pid = fork(); + if (pid < 0) { + log_error("cannot fork"); + return -1; + } + if (!pid) { + run_command(); + return -1; + } + } + + _lvmlockd = lvmlockd_open(NULL); + + if (_lvmlockd.socket_fd < 0 || _lvmlockd.error) { + log_error("lvmlockd open error %d", _lvmlockd.error); + goto out_pid; + } + + if (quit) { + rv = do_quit(); + goto out; + } + + if (info) { + rv = do_dump("info"); + goto out; + } + + if (dump) { + rv = do_dump("dump"); + goto out; + } + + if (gl_enable) { + rv = do_able("enable_gl"); + goto out; + } + + if (gl_disable) { + rv = do_able("disable_gl"); + goto out; + } + + if (stop_lockspaces) { + rv = do_stop_lockspaces(); + goto out; + } + + if (pid) { + /* tell child to exec */ + write(cmd_pipe[1], "g", 1); + waitpid(pid, &status, 0); + pid = 0; + } +out: + lvmlockd_close(_lvmlockd); +out_pid: + if (pid) { + kill(pid, SIGKILL); + waitpid(pid, &status, 0); + } + + return rv; +} + |