diff options
author | Dan Williams <dan.j.williams@intel.com> | 2008-05-15 16:48:35 +1000 |
---|---|---|
committer | Neil Brown <neilb@suse.de> | 2008-05-15 16:48:35 +1000 |
commit | f7dd881f909a7bc552a6de3c1fc4920bb0bfdff2 (patch) | |
tree | 57f1a18231e53bff1c3244eed448fac1a356542f /msg.c | |
parent | 0fd5c350e55590d2adbbb6ca16ec86391abda14b (diff) | |
download | mdadm-f7dd881f909a7bc552a6de3c1fc4920bb0bfdff2.tar.gz |
handle Manage_subdevs() for 'external' arrays
From: Dan Williams <dan.j.williams@intel.com>
1/ Block attempts to add/remove devices from container members
2/ Forward add/remove requests to containers
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'msg.c')
-rw-r--r-- | msg.c | 249 |
1 files changed, 249 insertions, 0 deletions
@@ -0,0 +1,249 @@ +/* + * Copyright (C) 2008 Intel Corporation + * + * mdmon socket / message handling + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include "mdadm.h" + +enum tx_rx_state { + TX_RX_START, + TX_RX_SEQ, + TX_RX_NUM_BYTES, + TX_RX_BUF, + TX_RX_END, + TX_RX_SUCCESS, + TX_RX_ERR, +}; + +const int start_magic = 0x5a5aa5a5; +const int end_magic = 0xa5a55a5a; + +#define txrx(fd, buf, size, flags) (recv_send ? \ + recv(fd, buf, size, flags) : \ + send(fd, buf, size, flags)) + +/* non-blocking send/receive with n second timeout */ +static enum tx_rx_state +tx_rx_message(int fd, struct md_message *msg, int recv_send, int tmo) +{ + int d = recv_send ? 0 : start_magic; + int flags = recv_send ? 0 : MSG_NOSIGNAL; + enum tx_rx_state state = TX_RX_START; + void *buf = &d; + size_t size = sizeof(d); + off_t n = 0; + int rc; + int again; + + do { + again = 0; + rc = txrx(fd, buf + n, size - n, flags); + if (rc <= 0) { /* error */ + if (rc == -1 && errno == EAGAIN) + again = 1; + else + state = TX_RX_ERR; + } else if (rc + n == size) /* done */ + switch (state) { + case TX_RX_START: + if (recv_send && d != start_magic) + state = TX_RX_ERR; + else { + state = TX_RX_SEQ; + buf = &msg->seq; + size = sizeof(msg->seq); + n = 0; + } + break; + case TX_RX_SEQ: + state = TX_RX_NUM_BYTES; + buf = &msg->num_bytes; + size = sizeof(msg->num_bytes); + n = 0; + break; + case TX_RX_NUM_BYTES: + if (msg->num_bytes > + sizeof(union md_message_commands)) + state = TX_RX_ERR; + else if (recv_send && msg->num_bytes) { + msg->buf = malloc(msg->num_bytes); + if (!msg->buf) + state = TX_RX_ERR; + else { + state = TX_RX_BUF; + buf = msg->buf; + size = msg->num_bytes; + n = 0; + } + } else if (!recv_send && msg->num_bytes) { + state = TX_RX_BUF; + buf = msg->buf; + size = msg->num_bytes; + n = 0; + } else { + d = recv_send ? 0 : end_magic; + state = TX_RX_END; + buf = &d; + size = sizeof(d); + n = 0; + } + break; + case TX_RX_BUF: + d = recv_send ? 0 : end_magic; + state = TX_RX_END; + buf = &d; + size = sizeof(d); + n = 0; + break; + case TX_RX_END: + if (recv_send && d != end_magic) + state = TX_RX_ERR; + else + state = TX_RX_SUCCESS; + break; + case TX_RX_ERR: + case TX_RX_SUCCESS: + break; + } + else /* continue */ + n += rc; + + if (again) { + fd_set set; + struct timeval timeout = { tmo, 0 }; + struct timeval *ptmo = tmo ? &timeout : NULL; + + FD_ZERO(&set); + FD_SET(fd, &set); + + if (recv_send) + rc = select(fd + 1, &set, NULL, NULL, ptmo); + else + rc = select(fd + 1, NULL, &set, NULL, ptmo); + + if (rc <= 0) + state = TX_RX_ERR; + } + } while (state < TX_RX_SUCCESS); + + return state; +} + + +int receive_message(int fd, struct md_message *msg, int tmo) +{ + if (tx_rx_message(fd, msg, 1, tmo) == TX_RX_SUCCESS) + return 0; + else + return -1; +} + +int send_message(int fd, struct md_message *msg, int tmo) +{ + if (tx_rx_message(fd, msg, 0, tmo) == TX_RX_SUCCESS) + return 0; + else + return -1; +} + +int ack(int fd, int seq, int tmo) +{ + struct md_message msg = { .seq = seq, .num_bytes = 0 }; + + return send_message(fd, &msg, tmo); +} + +int nack(int fd, int err, int tmo) +{ + struct md_message msg = { .seq = err, .num_bytes = 0 }; + + return send_message(fd, &msg, tmo); +} + +int send_remove_device(int fd, dev_t rdev, int seq, int tmo) +{ + struct md_remove_device_cmd cmd = { .action = md_action_remove_device, + .rdev = rdev + }; + struct md_message msg = { .seq = seq, + .num_bytes = sizeof(cmd), + .buf = &cmd + }; + + return send_message(fd, &msg, tmo); +} + +int connect_monitor(char *devname) +{ + char path[100]; + int sfd; + long fl; + struct sockaddr_un addr; + + sprintf(path, "/var/run/mdadm/%s.sock", devname); + sfd = socket(PF_LOCAL, SOCK_STREAM, 0); + if (sfd < 0) + return -1; + + addr.sun_family = PF_LOCAL; + strcpy(addr.sun_path, path); + if (connect(sfd, &addr, sizeof(addr)) < 0) { + close(sfd); + return -1; + } + + fl = fcntl(sfd, F_GETFL, 0); + fl |= O_NONBLOCK; + fcntl(sfd, F_SETFL, fl); + + return sfd; +} + +int ping_monitor(char *devname) +{ + int sfd = connect_monitor(devname); + struct md_message msg; + int err = 0; + + if (sfd < 0) + return sfd; + + /* try to ping existing socket */ + if (ack(sfd, 0, 0) != 0) + err = -1; + + /* check the reply */ + if (!err && receive_message(sfd, &msg, 0) != 0) + err = -1; + + if (msg.seq != 0) + err = -1; + + close(sfd); + return err; +} |