#include "include/int_types.h"
#include "mon/MonClient.h"
-#include "mon/MonMap.h"
#include "common/config.h"
-#include "auth/KeyRing.h"
#include "common/errno.h"
#include "common/ceph_argparse.h"
#include "common/strtol.h"
#include "global/global_init.h"
#include "common/safe_io.h"
-#include "common/secret.h"
+#include "include/krbd.h"
#include "include/stringify.h"
#include "include/rados/librados.hpp"
#include "include/rbd/librbd.hpp"
#include "common/blkdev.h"
#include <boost/scoped_ptr.hpp>
-#include <dirent.h>
#include <errno.h>
#include <iostream>
#include <memory>
#include <sys/param.h>
#endif
-#include <blkid/blkid.h>
-
#define MAX_SECRET_LEN 1000
#define MAX_POOL_NAME_SIZE 128
return 0;
}
-static int do_kernel_add(const char *poolname, const char *imgname,
+static int do_kernel_map(const char *poolname, const char *imgname,
const char *snapname)
{
- MonMap monmap;
- int r = monmap.build_initial(g_ceph_context, cerr);
- if (r < 0)
- return r;
-
- map<string, entity_addr_t>::const_iterator it = monmap.mon_addr.begin();
+ struct krbd_ctx *krbd;
ostringstream oss;
- for (size_t i = 0; i < monmap.mon_addr.size(); ++i, ++it) {
- oss << it->second.addr;
- if (i + 1 < monmap.mon_addr.size())
- oss << ",";
- }
-
- const char *user = g_conf->name.get_id().c_str();
- oss << " name=" << user;
-
- char key_name[strlen(user) + strlen("client.") + 1];
- snprintf(key_name, sizeof(key_name), "client.%s", user);
+ char *devnode;
+ int r;
- KeyRing keyring;
- r = keyring.from_ceph_context(g_ceph_context);
- if (r == -ENOENT && !(g_conf->keyfile.length() ||
- g_conf->key.length()))
- r = 0;
- if (r < 0) {
- cerr << "rbd: failed to get secret: " << cpp_strerror(r) << std::endl;
+ r = krbd_create_from_context(g_ceph_context, &krbd);
+ if (r < 0)
return r;
- }
- CryptoKey secret;
- if (keyring.get_secret(g_conf->name, secret)) {
- string secret_str;
- secret.encode_base64(secret_str);
-
- r = set_kernel_secret(secret_str.c_str(), key_name);
- if (r >= 0) {
- if (r == 0)
- cerr << "rbd: warning: secret has length 0" << std::endl;
- oss << ",key=" << key_name;
- } else if (r == -ENODEV || r == -ENOSYS) {
- /* running against older kernel; fall back to secret= in options */
- oss << ",secret=" << secret_str;
- } else {
- cerr << "rbd: failed to add ceph secret key '" << key_name
- << "' to kernel: " << cpp_strerror(r) << std::endl;
- return r;
- }
- } else if (is_kernel_secret(key_name)) {
- oss << ",key=" << key_name;
- }
for (map<string, string>::const_iterator it = map_options.begin();
it != map_options.end();
if (it->first == "rw" && it->second == "rw")
continue;
- oss << "," << it->second;
- }
-
- oss << " " << poolname << " " << imgname;
-
- if (snapname) {
- oss << " " << snapname;
- }
-
- // modprobe the rbd module if /sys/bus/rbd doesn't exist
- struct stat sb;
- if ((stat("/sys/bus/rbd", &sb) < 0) || (!S_ISDIR(sb.st_mode))) {
- // turn on single-major device number allocation scheme if the
- // kernel supports it
- const char *cmd = "/sbin/modprobe rbd";
- r = system("/sbin/modinfo -F parm rbd | /bin/grep -q ^single_major:");
- if (r == 0) {
- cmd = "/sbin/modprobe rbd single_major=Y";
- } else if (r < 0) {
- cerr << "rbd: error executing modinfo as shell command!" << std::endl;
- }
-
- r = system(cmd);
- if (r) {
- if (r < 0)
- cerr << "rbd: error executing modprobe as shell command!" << std::endl;
- else
- cerr << "rbd: modprobe rbd failed! (" << r << ")" <<std::endl;
- return r;
- }
+ if (it != map_options.begin())
+ oss << ",";
+ oss << it->second;
}
- // 'add' interface is deprecated, use 'add_single_major' if it's
- // available
- //
- // ('add' and 'add_single_major' interfaces are identical, except
- // that if rbd kernel module is new enough and is configured to use
- // single-major scheme, 'add' is disabled in order to prevent old
- // userspace from doing weird things at unmap time)
- int fd = open("/sys/bus/rbd/add_single_major", O_WRONLY);
- if (fd < 0) {
- if (errno == ENOENT) {
- fd = open("/sys/bus/rbd/add", O_WRONLY);
- if (fd < 0) {
- r = -errno;
- if (r == -ENOENT) {
- cerr << "rbd: /sys/bus/rbd/add does not exist!" << std::endl
- << "Did you run 'modprobe rbd' or is your rbd module too old?"
- << std::endl;
- }
- return r;
- }
- } else {
- return -errno;
- }
- }
+ r = krbd_map(krbd, poolname, imgname, snapname, oss.str().c_str(), &devnode);
+ if (r < 0)
+ goto out;
- string add = oss.str();
- r = safe_write(fd, add.c_str(), add.size());
- close(fd);
-
- // let udevadm do its job before we return
- if (udevadm_settle) {
- int r = system("/sbin/udevadm settle");
- if (r) {
- if (r < 0)
- cerr << "rbd: error executing udevadm as shell command!" << std::endl;
- else
- cerr << "rbd: '/sbin/udevadm settle' failed! (" << r << ")" <<std::endl;
- return r;
- }
- }
+ cout << devnode << std::endl;
+ free(devnode);
+out:
+ krbd_destroy(krbd);
return r;
}
-static int read_file(const char *filename, char *buf, size_t bufsize)
-{
- int fd = open(filename, O_RDONLY);
- if (fd < 0)
- return -errno;
-
- int r = safe_read(fd, buf, bufsize);
- if (r < 0) {
- cerr << "rbd: could not read " << filename << ": "
- << cpp_strerror(-r) << std::endl;
- close(fd);
- return r;
- }
-
- char *end = buf;
- while (end < buf + bufsize && *end && *end != '\n') {
- end++;
- }
- *end = '\0';
-
- close(fd);
- return r;
-}
-
-void do_closedir(DIR *dp)
-{
- if (dp)
- closedir(dp);
-}
-
static int do_kernel_showmapped(Formatter *f)
{
+ struct krbd_ctx *krbd;
int r;
- bool have_output = false;
- TextTable tbl;
- const char *devices_path = "/sys/bus/rbd/devices";
- ceph::shared_ptr<DIR> device_dir(opendir(devices_path), do_closedir);
- if (!device_dir.get()) {
- r = -errno;
- cerr << "rbd: could not open " << devices_path << ": "
- << cpp_strerror(-r) << std::endl;
- return r;
- }
-
- struct dirent *dent;
- dent = readdir(device_dir.get());
- if (!dent) {
- r = -errno;
- cerr << "rbd: error reading " << devices_path << ": "
- << cpp_strerror(-r) << std::endl;
- return r;
- }
-
- if (f) {
- f->open_object_section("devices");
- } else {
- tbl.define_column("id", TextTable::LEFT, TextTable::LEFT);
- tbl.define_column("pool", TextTable::LEFT, TextTable::LEFT);
- tbl.define_column("image", TextTable::LEFT, TextTable::LEFT);
- tbl.define_column("snap", TextTable::LEFT, TextTable::LEFT);
- tbl.define_column("device", TextTable::LEFT, TextTable::LEFT);
- }
-
- do {
- if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
- continue;
-
- char fn[PATH_MAX];
-
- char dev[PATH_MAX];
- snprintf(dev, sizeof(dev), "/dev/rbd%s", dent->d_name);
-
- char name[RBD_MAX_IMAGE_NAME_SIZE];
- snprintf(fn, sizeof(fn), "%s/%s/name", devices_path, dent->d_name);
- r = read_file(fn, name, sizeof(name));
- if (r < 0) {
- cerr << "rbd: could not read name from " << fn << ": "
- << cpp_strerror(-r) << std::endl;
- continue;
- }
-
- char pool[4096];
- snprintf(fn, sizeof(fn), "%s/%s/pool", devices_path, dent->d_name);
- r = read_file(fn, pool, sizeof(pool));
- if (r < 0) {
- cerr << "rbd: could not read name from " << fn << ": "
- << cpp_strerror(-r) << std::endl;
- continue;
- }
-
- char snap[4096];
- snprintf(fn, sizeof(fn), "%s/%s/current_snap", devices_path, dent->d_name);
- r = read_file(fn, snap, sizeof(snap));
- if (r < 0) {
- cerr << "rbd: could not read name from " << fn << ": "
- << cpp_strerror(-r) << std::endl;
- continue;
- }
-
- if (f) {
- f->open_object_section(dent->d_name);
- f->dump_string("pool", pool);
- f->dump_string("name", name);
- f->dump_string("snap", snap);
- f->dump_string("device", dev);
- f->close_section();
- } else {
- tbl << dent->d_name << pool << name << snap << dev << TextTable::endrow;
- }
- have_output = true;
-
- } while ((dent = readdir(device_dir.get())));
-
- if (f) {
- f->close_section();
- f->flush(cout);
- } else {
- if (have_output)
- cout << tbl;
- }
-
- return 0;
-}
-
-static int get_rbd_seq(dev_t devno, string &seq)
-{
- // convert devno, which might be a partition major:minor pair, into
- // a whole disk major:minor pair
- dev_t wholediskno;
- int r = blkid_devno_to_wholedisk(devno, NULL, 0, &wholediskno);
- if (r) {
- cerr << "rbd: could not compute wholediskno: " << r << std::endl;
- // ignore the error: devno == wholediskno most of the time, and if
- // it turns out it's not we will fail with -ENOENT later anyway
- wholediskno = devno;
- }
-
- const char *devices_path = "/sys/bus/rbd/devices";
- DIR *device_dir = opendir(devices_path);
- if (!device_dir) {
- r = -errno;
- cerr << "rbd: could not open " << devices_path << ": " << cpp_strerror(-r)
- << std::endl;
- return r;
- }
-
- struct dirent *dent;
- dent = readdir(device_dir);
- if (!dent) {
- r = -errno;
- cerr << "Error reading " << devices_path << ": " << cpp_strerror(-r)
- << std::endl;
- closedir(device_dir);
+ r = krbd_create_from_context(g_ceph_context, &krbd);
+ if (r < 0)
return r;
- }
-
- int match_minor = -1;
- do {
- char fn[strlen(devices_path) + strlen(dent->d_name) + strlen("//major") + 1];
- char buf[32];
-
- if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
- continue;
-
- snprintf(fn, sizeof(fn), "%s/%s/major", devices_path, dent->d_name);
- r = read_file(fn, buf, sizeof(buf));
- if (r < 0) {
- cerr << "rbd: could not read major number from " << fn << ": "
- << cpp_strerror(-r) << std::endl;
- continue;
- }
- string err;
- int cur_major = strict_strtol(buf, 10, &err);
- if (!err.empty()) {
- cerr << err << std::endl;
- cerr << "rbd: could not parse major number read from " << fn << ": "
- << cpp_strerror(-r) << std::endl;
- continue;
- }
- if (cur_major != (int)major(wholediskno))
- continue;
-
- if (match_minor == -1) {
- // matching minors in addition to majors is not necessary unless
- // single-major scheme is turned on, but, if the kernel supports
- // it, do it anyway (blkid stuff above ensures that we always have
- // the correct minor to match with)
- struct stat sbuf;
- snprintf(fn, sizeof(fn), "%s/%s/minor", devices_path, dent->d_name);
- match_minor = (stat(fn, &sbuf) == 0);
- }
-
- if (match_minor == 1) {
- snprintf(fn, sizeof(fn), "%s/%s/minor", devices_path, dent->d_name);
- r = read_file(fn, buf, sizeof(buf));
- if (r < 0) {
- cerr << "rbd: could not read minor number from " << fn << ": "
- << cpp_strerror(-r) << std::endl;
- continue;
- }
- int cur_minor = strict_strtol(buf, 10, &err);
- if (!err.empty()) {
- cerr << err << std::endl;
- cerr << "rbd: could not parse minor number read from " << fn << ": "
- << cpp_strerror(-r) << std::endl;
- continue;
- }
- if (cur_minor != (int)minor(wholediskno))
- continue;
- } else {
- assert(match_minor == 0);
- }
- seq = string(dent->d_name);
- closedir(device_dir);
- return 0;
- } while ((dent = readdir(device_dir)));
+ r = krbd_showmapped(krbd, f);
- closedir(device_dir);
- return -ENOENT;
+ krbd_destroy(krbd);
+ return r;
}
-static int do_kernel_rm(const char *dev)
+static int do_kernel_unmap(const char *dev)
{
- struct stat sbuf;
- if (stat(dev, &sbuf) || !S_ISBLK(sbuf.st_mode)) {
- cerr << "rbd: " << dev << " is not a block device" << std::endl;
- return -EINVAL;
- }
+ struct krbd_ctx *krbd;
+ int r;
- string seq_num;
- int r = get_rbd_seq(sbuf.st_rdev, seq_num);
- if (r == -ENOENT) {
- cerr << "rbd: " << dev << " is not an rbd device" << std::endl;
- return -EINVAL;
- }
+ r = krbd_create_from_context(g_ceph_context, &krbd);
if (r < 0)
return r;
- // let udevadm do its job *before* we try to unmap
- if (udevadm_settle) {
- int r = system("/sbin/udevadm settle");
- if (r) {
- if (r < 0)
- cerr << "rbd: error executing udevadm as shell command!" << std::endl;
- else
- cerr << "rbd: '/sbin/udevadm settle' failed! (" << r << ")" <<std::endl;
- // ignore the error, though.
- }
- }
-
- // see comment in do_kernel_add(), same goes for 'remove' vs
- // 'remove_single_major'
- int fd = open("/sys/bus/rbd/remove_single_major", O_WRONLY);
- if (fd < 0) {
- if (errno == ENOENT) {
- fd = open("/sys/bus/rbd/remove", O_WRONLY);
- if (fd < 0)
- return -errno;
- } else {
- return -errno;
- }
- }
-
- r = safe_write(fd, seq_num.c_str(), seq_num.size());
- if (r < 0) {
- cerr << "rbd: failed to remove rbd device" << ": " << cpp_strerror(-r)
- << std::endl;
- close(fd);
- return r;
- }
-
- r = close(fd);
-
- // let udevadm finish, if present
- if (udevadm_settle){
- int r = system("/sbin/udevadm settle");
- if (r) {
- if (r < 0)
- cerr << "rbd: error executing udevadm as shell command!" << std::endl;
- else
- cerr << "rbd: '/sbin/udevadm settle' failed! (" << r << ")" <<std::endl;
- return r;
- }
- }
+ r = krbd_unmap(krbd, dev);
- if (r < 0)
- r = -errno;
+ krbd_destroy(krbd);
return r;
}
break;
case OPT_MAP:
- r = do_kernel_add(poolname, imgname, snapname);
+ r = do_kernel_map(poolname, imgname, snapname);
if (r < 0) {
- cerr << "rbd: add failed: " << cpp_strerror(-r) << std::endl;
+ cerr << "rbd: map failed: " << cpp_strerror(-r) << std::endl;
return -r;
}
break;
case OPT_UNMAP:
- r = do_kernel_rm(devpath);
+ r = do_kernel_unmap(devpath);
if (r < 0) {
- cerr << "rbd: remove failed: " << cpp_strerror(-r) << std::endl;
+ cerr << "rbd: unmap failed: " << cpp_strerror(-r) << std::endl;
return -r;
}
break;