drbd-utils-8.9.10/0000755000175000017500000000000013027242657013566 5ustar apoikosapoikosdrbd-utils-8.9.10/Makefile.in0000644000175000017500000002027113026714246015632 0ustar apoikosapoikos# Makefile for drbd # # This file is part of DRBD by Philipp Reisner and Lars Ellenberg. # # Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. # Copyright (C) 2001-2008, Philipp Reisner . # Copyright (C) 2002-2008, Lars Ellenberg . # # drbd is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # drbd is distributed in the hope that 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 drbd # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. # # TODO move some of the more cryptic bash scriptlets here into scripts/* # and call those from here. -- lge # variables set by configure GIT = @GIT@ LN_S = @LN_S@ PREFIX = @prefix@ RPMBUILD = @RPMBUILD@ SED = @SED@ # features enabled or disabled by configure WITH_UDEV = @WITH_UDEV@ WITH_XEN = @WITH_XEN@ WITH_PACEMAKER = @WITH_PACEMAKER@ WITH_HEARTBEAT = @WITH_HEARTBEAT@ WITH_RGMANAGER = @WITH_RGMANAGER@ WITH_BASHCOMPLETION = @WITH_BASHCOMPLETION@ WITH_83_SUPPORT = @WITH_83_SUPPORT@ WITH_84_SUPPORT = @WITH_84_SUPPORT@ WITH_MANUAL = @WITH_MANUAL@ WITH_DRBDMON = @WITH_DRBDMON@ # for some reason some of the commands below only work correctly in bash, # and not in e.g. dash. I'm too lazy to fix it to be compatible. SHELL=/bin/bash TOOLS_DIRS = user/shared user/v9 user/v84 user/v83 scripts user/drbdmon DOC_DIRS = documentation/v83 documentation/v84 documentation/v9 SUBDIRS = $(TOOLS_DIRS) $(DOC_DIRS) REL_VERSION := $(shell $(SED) -ne '/^AC_INIT/{s/^[^,]*, *\([^,]*\) *,.*/\1/;p;q}' configure.ac) ifdef FORCE # # NOTE to generate a tgz even if too lazy to update the changelogs, # or to forcefully include the FIXME to be done: latest change date; # for now, include the git hash of the latest commit # in the tgz name: # make distclean doc tgz FORCE=1 # REL_VERSION := $(REL_VERSION)-$(shell $(GIT) rev-parse HEAD) endif DIST_VERSION := $(REL_VERSION) FDIST_VERSION := $(shell test -e .filelist && sed -ne 's,^drbd-utils-\([^/]*\)/.*,\1,p;q' < .filelist) ifeq ($(FDIST_VERSION),) FDIST_VERSION := $(DIST_VERSION) endif all-yes := check-submods configure tools all-$(WITH_MANUAL) += doc all: $(all-yes) .PHONY: all tools doc tools: @ set -e; for i in $(TOOLS_DIRS); do $(MAKE) -C $$i ; done @ echo -e "\n\tUserland tools build was successful." doc: $(MAKE) -C documentation/v9 doc ifeq ($(WITH_83_SUPPORT),yes) $(MAKE) -C documentation/v83 doc endif ifeq ($(WITH_84_SUPPORT),yes) $(MAKE) -C documentation/v84 doc endif # we cannot use 'git submodule foreach': # foreach only works if submodule already checked out .PHONY: check-submods check-submods: @if test -d .git && test -s .gitmodules; then \ for d in `grep "^\[submodule" .gitmodules | cut -f2 -d'"'`; do \ if [ ! "`ls -A $$d`" ]; then \ git submodule init; \ git submodule update; \ break; \ fi; \ done; \ fi .PHONY: doc-clean install install-tools clean distclean extra-clean uninstall doc-clean: $(MAKE) -C documentation/v9 doc-clean ifeq ($(WITH_83_SUPPORT),yes) $(MAKE) -C documentation/v83 doc-clean endif ifeq ($(WITH_84_SUPPORT),yes) $(MAKE) -C documentation/v84 doc-clean endif install-yes := install-tools install-$(WITH_MANUAL) += install-doc install: $(install-yes) install-tools: @ set -e; for i in $(patsubst drbd,,$(TOOLS_DIRS)); do $(MAKE) -C $$i install; done install-doc: @ set -e; for i in $(patsubst drbd,,$(DOC_DIRS)); do $(MAKE) -C $$i install; done .NOTPARALLEL clean: @ set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i clean; done rm -f *~ rm -rf dist distclean: @ set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i distclean; done rm -f *~ .filelist rm -rf dist rm -rf autom4te.cache rm -f config.log EXTRA_CLEAN += Makefile user/Makefile user/v83/Makefile user/v84/Makefile user/v9/Makefile scripts/Makefile user/drbdmon/Makefile EXTRA_CLEAN += documentation/Makefile documentation/v83/Makefile documentation/v84/Makefile documentation/v9/Makefile EXTRA_CLEAN += user/shared/config.h drbd.spec EXTRA_CLEAN += configure config.status extra-clean: distclean rm -f $(EXTRA_CLEAN) uninstall: @ set -e; for i in $(SUBDIRS); do $(MAKE) -C $$i uninstall; done .PHONY: check_changelogs_up2date check_changelogs_up2date: @ up2date=true; dver_re=$(DIST_VERSION); dver_re=$${dver_re//./\\.}; \ echo "checking for presence of $$dver_re in various changelog files"; \ in_changelog=$$(sed -n -e '0,/^%changelog/d' \ -e '/- '"$$dver_re"'-/p' < drbd.spec.in) ; \ if test -z "$$in_changelog" ; \ then \ echo -e "\n\t%changelog in drbd.spec.in needs update"; \ up2date=false; fi; \ if ! grep "^$$dver_re\>" >/dev/null 2>&1 ChangeLog; \ then \ echo -e "\n\tChangeLog needs update"; \ up2date=false; fi ; \ if ! grep "^AC_INIT(DRBD, $$dver_re" >/dev/null 2>&1 configure.ac; \ then \ echo -e "\n\tconfigure.ac needs update"; \ up2date=false; fi ; \ if ! grep "^drbd-utils ($$dver_re+linbit" >/dev/null 2>&1 debian/changelog; \ then \ echo -e "\n\tdebian/changelog needs update [ignored]\n"; \ : do not fail the build because of outdated debian/changelog ; fi ; \ $$up2date PNAME := drbd-utils .PHONY: .filelist .filelist: @set -e ; submodules=`$(GIT) submodule foreach --quiet 'echo $$path'`; \ $(GIT) ls-files | \ grep -vxF -e "$$submodules" | \ sed '$(if $(PRESERVE_DEBIAN),,/^debian/d);s#^#$(PNAME)-$(DIST_VERSION)/#' | \ grep -v "gitignore\|gitmodules" > .filelist @$(GIT) submodule foreach --quiet 'git ls-files | sed -e "s,^,$(PNAME)-$(DIST_VERSION)/$$path/,"' | \ grep -v "gitignore\|gitmodules" >> .filelist @[ -s .filelist ] # assert there is something in .filelist now @find documentation -name "[^.]*.[58]" -o -name "*.html" | \ sed "s/^/$(PNAME)-$(DIST_VERSION)\//" >> .filelist ; \ echo $(PNAME)-$(DIST_VERSION)/user/shared/drbd_buildtag.c >> .filelist ; \ echo $(PNAME)-$(DIST_VERSION)/user/shared/config.h.in >> .filelist ; \ echo $(PNAME)-$(DIST_VERSION)/.filelist >> .filelist ; \ echo $(PNAME)-$(DIST_VERSION)/configure >> .filelist ; \ echo "./.filelist updated." # tgz will no longer automatically update .filelist, # so the tgz and therefore rpm target will work within # an extracted tarball, too. # to generate a distribution tarball, use make tarball, # which will regenerate .filelist .PHONY: tgz tgz: test -e .filelist rm -f drbd-utils-$(FDIST_VERSION) $(LN_S) . drbd-utils-$(FDIST_VERSION) for f in $$(<.filelist) ; do [ -e $$f ] && continue ; echo missing: $$f ; exit 1; done grep debian .filelist >/dev/null 2>&1 && _DEB=-debian || _DEB="" ; \ test -n "$$KEEPNAME" && _DEB="" || :; \ tar --owner=0 --group=0 -czf - -T .filelist > drbd-utils-$(FDIST_VERSION)$$_DEB.tar.gz rm drbd-utils-$(FDIST_VERSION) ifeq ($(FORCE),) tgz: check_changelogs_up2date doc endif tools doc tgz: check-submods .PHONY: check_all_committed prepare_release tarball check_all_committed: @$(if $(FORCE),-,)modified=`$(GIT) ls-files -m -t`; \ if test -n "$$modified" ; then \ echo "$$modified"; \ false; \ fi prepare_release: $(MAKE) tarball $(MAKE) tarball PRESERVE_DEBIAN=1 debrelease: $(MAKE) tarball PRESERVE_DEBIAN=1 KEEPNAME=1 configure.ac: ; configure: configure.ac aclocal autoheader autoconf config.status: configure @printf "\nYou need to call ./configure with appropriate arguments (again).\n\n"; exit 1 tarball: check-submods check_all_committed distclean doc configure .filelist $(MAKE) tgz ifdef RPMBUILD drbd.spec: drbd.spec.in configure ./configure --enable-spec .PHONY: rpmprep rpmprep: tgz drbd.spec cp drbd-utils-$(FDIST_VERSION).tar.gz `rpm -E "%_sourcedir"` .PHONY: rpm rpm: rpmprep $(RPMBUILD) -bb \ $(RPMOPT) \ drbd.spec @echo "You have now:" ; find `rpm -E "%_rpmdir"` -name *.rpm .PHONY: srpm srpm: rpmprep $(RPMBUILD) -bs \ $(RPMOPT) \ drbd.spec @echo "You have now:" ; find `rpm -E "%_srcrpmdir"` -name *.src.rpm endif Makefile.in: ; Makefile: Makefile.in config.status ./config.status Makefile drbd-utils-8.9.10/documentation/0000755000175000017500000000000013027242657016437 5ustar apoikosapoikosdrbd-utils-8.9.10/documentation/aspell.en.per0000644000175000017500000000374212466702073021035 0ustar apoikosapoikospersonal_ws-1.1 en 285 ArbitraryCnt BIOs BLKFLSBUF BLKGETSIZE BLKSSZGET BarrierAck Bitmap's BrokenPipe ConnectedCnt ConnectedInd DRBD's DUnknown EAGAIN EBUSY EINVAL ENOMEM EOPNOTSUPP Ellenberg FIXME GBit GCs GPL GmbH HumanCnt IDE JFS KDIR KOBJ LANANA LBD LINBIT MDF NIC NICs NUL NUM NetworkFailure PausedSyncS PausedSyncT Philipp PingAck ProtocolError RAIDs RHEL RLE ReIsErFs Reisner SETLKW SIGALRM SVN SWAPSPACE StandAlone StartingSync StartingSyncS StartingSyncT SyncParam SyncSource SyncTarget SyncUUID TCQ TK TODO TearDown TimeoutCnt UI UUIDs UnknownMandatoryTag UpToDate VerifyS VerifyT WFBitMapS WFBitMapT WFConnection WFReportParams WFSyncUUID WantFullSync ack acked acks actlog addr adm al alg api argc args argv asbp asender asprintf bm bmbv bnum boolean bsize buildtag bvec cB calloc canonicalize cfg cgi chdir checksum cmd cmdname cn conf config conv cpu crypto cstate ctl degr dereference dev devfs devname diskless diskstats dont dopd drbd drbdX drbdadm drbdmeta drbdsetup drbdtool ds dstate dt endian endianness enums etext evictable exa extraversion fcntl fd fdopen fgets filesystem fprintf fs fstat fstype gc gcc gethostbyname gi goto haclient hacluster hdr hexdump hmac hostname http idx incon init inline io ip ipv irq kb kmalloc ko lastState len lge libdisk linux lld llu ln longjmp longoptions lookup lr lru lu lvm malloc md mem memalign memset metadata mkdir modprobe multihomed mutex netlink nodenames noheadings nop nosuffix nv ok online oopsie optind ord outdate outdating pagesize param parms pathname pid plaintext posix pre prepends pri printf proc pv pvs recurse recurses recv refcnt refcount reiser reiserfs resize resync resynced rr runlength sb sbin scmd sendpage sizeof sndbuf spinlock ssocks startup stderr stdout stonith str strtoll struct subcommand sublevel superblock suse symlinks syncer sys syscall sysconf tl toc tracepoint tri tty udev unconfigure unconfigured uniq urandom userland userspace usr uuid uuids varname vasprintf vmalloc wfc wget writeout xfs xfsprogs xml yylval drbd-utils-8.9.10/documentation/v83/0000755000175000017500000000000013027242657017057 5ustar apoikosapoikosdrbd-utils-8.9.10/documentation/v83/drbdmeta.xml0000644000175000017500000002205312466702073021364 0ustar apoikosapoikos 15 Oct 2008 DRBD 8.3.2 drbdmeta 8 System Administration drbdmeta DRBD's meta data management tool drbdmeta drbdmeta --force --ignore-sanity-checks device v06 minor v07 meta_dev index v08 meta_dev index command cmd args Description Drbdmeta is used to create, display and modify the contents of DRBD's meta data storage. Usually you do not want to use this command directly, but start it via the frontend drbdadm8. This command only works if the DRBD resource is currently down, or at least detached from its backing storage. The first parameter is the device node associated to the resource. With the second parameter you can select the version of the meta data. Currently all major DRBD releases (0.6, 0.7 and 8) are supported. Options --force drbdmeta--force All questions that get asked by drbdmeta are treated as if the user answered 'yes'. --ignore-sanity-checks drbdmeta--ignore-sanity-checks Some sanity checks cause drbdmeta to terminate. E.g. if a file system image would get destroyed by creating the meta data. By using that option you can force drbdmeta to ignore these checks. Commands create-md drbdmetacreate-md Create-md initializes the meta data storage. This needs to be done before a DRBD resource can be taken online for the first time. In case there is already a meta data signature of an older format in place, drbdmeta will ask you if it should convert the older format to the selected format. If you will use the resource before it is connected to its peer for the first time DRBD may perform better if you use the option. For DRBD versions of the peer use up to these values: <8.3.7 -> 4k, 8.3.8 -> 32k, 8.3.9 -> 128k, 8.4.0 -> 1M. get-gi drbdmetaget-gi Get-gi shows a short textual representation of the data generation identifier. In version 0.6 and 0.7 these are generation counters, while in version 8 it is a set of UUIDs. show-gi drbdmetashow-gi Show-gi prints a textual representation of the data generation identifiers including explanatory information. dump-md drbdmetadump-md Dumps the whole contents of the meta data storage including the stored bit-map and activity-log in a textual representation. outdate drbdmetaoutdate Sets the outdated flag in the meta data. This is used by the peer node when it wants to become primary, but cannot communicate with the DRBD stack on this host. dstate drbdmetadstate Prints the state of the data on the backing storage. The output is always followed by '/DUnknown' since drbdmeta only looks at the local meta data. check-resize drbdmetacheck-resize Examines the device size of a backing device, and it's last known device size, recorded in a file /var/lib/drbd/drbd-minor-??.lkbd. In case the size of the backing device changed, and the meta data can be found at the old position, it moves the meta data to the right position at the end of the block device. Expert's commands Drbdmeta allows you to modify the meta data as well. This is intentionally omitted for the command's usage output, since you should only use it if you really know what you are doing. By setting the generation identifiers to wrong values, you risk to overwrite your up-to-data data with an older version of your data. set-gi gi drbdmetaset-gi Set-gi allows you to set the generation identifier. Gi needs to be a generation counter for the 0.6 and 0.7 format, and a UUID set for 8.x. Specify it in the same way as get-gi shows it. restore-md dump_file drbdmetarestore-md Reads the dump_file and writes it to the meta data. Version This document was revised for version 8.3.2 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbdadm 8 drbd-utils-8.9.10/documentation/v83/Makefile.in0000644000175000017500000000675413012630471021125 0ustar apoikosapoikos# Makefile in documentation directory # # This file is part of DRBD by Philipp Reisner and Lars Ellenberg. # # drbd is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. # variables set by configure mandir = @mandir@ datarootdir = @datarootdir@ XSLTPROC = @XSLTPROC@ # features enabled or disabled by configure WITH_83_SUPPORT = @WITH_83_SUPPORT@ WITH_UDEV = @WITH_UDEV@ WITH_XEN = @WITH_XEN@ WITH_PACEMAKER = @WITH_PACEMAKER@ WITH_HEARTBEAT = @WITH_HEARTBEAT@ WITH_RGMANAGER = @WITH_RGMANAGER@ WITH_BASHCOMPLETION = @WITH_BASHCOMPLETION@ # variables meant to be overridden from the make command line DESTDIR ?= / # Needed for pattern substitution SHELL=/bin/bash MANPAGES := drbdsetup.8 drbd.conf.5 drbd.8 drbdadm.8 drbdmeta.8 ifeq ($(WITH_HEARTBEAT),yes) MANPAGES += drbddisk.8 endif SOURCES := $(wildcard *.xml) STYLESHEET_PREFIX ?= http://docbook.sourceforge.net/release/xsl/current MANPAGES_STYLESHEET ?= $(STYLESHEET_PREFIX)/manpages/docbook.xsl HTML_STYLESHEET ?= $(STYLESHEET_PREFIX)/xhtml/docbook.xsl FO_STYLESHEET ?= $(STYLESHEET_PREFIX)/fo/docbook.xsl XSLTPROC_OPTIONS ?= --xinclude XSLTPROC_MANPAGES_OPTIONS ?= $(XSLTPROC_OPTIONS) XSLTPROC_HTML_OPTIONS ?= $(XSLTPROC_OPTIONS) XSLTPROC_FO_OPTIONS ?= $(XSLTPROC_OPTIONS) make_doc := $(shell $(XSLTPROC) \ $(XSLTPROC_MANPAGES_OPTIONS) \ $(MANPAGES_STYLESHEET) < /dev/null > /dev/null 2>&1 && echo doc ) ifeq ($(make_doc),doc) all: doc else all: @echo "To (re)make the documentation: make doc" endif clean: @echo "To clean the documentation: make doc-clean" ifeq ($(WITH_83_SUPPORT),yes) doc: man else doc: endif doc-clean: distclean ####### Implicit rules .SUFFIXES: .sgml .5 .8 .html .pdf .ps %.5 %.8: %.xml $(XSLTPROC) \ $(XSLTPROC_MANPAGES_OPTIONS) \ $(MANPAGES_STYLESHEET) $< %.html: %.xml $(XSLTPROC) -o $@ \ $(XSLTPROC_HTML_OPTIONS) \ $(HTML_STYLESHEET) $< %.fo: %.xml $(XSLTPROC) -o $@ \ $(XSLTPROC_FO_OPTIONS) \ $(FO_STYLESHEET) $< distclean: rm -f *.[58] manpage.links manpage.refs *~ manpage.log rm -f *.ps.gz *.pdf *.ps *.html pod2htm* ####### man: $(MANPAGES) install: ifeq ($(WITH_83_SUPPORT),yes) @ok=true; for f in $(MANPAGES) ; \ do [ -e $$f ] || { echo $$f missing ; ok=false; } ; \ done ; $$ok set -e; for f in $(MANPAGES) ; do \ s=$${f##*.}; \ b=$${f%.[0-9]}; \ install -v -D -m 644 $$f $(DESTDIR)$(mandir)/man$$s/$$b-8.3.$$s ; \ done endif uninstall: ifeq ($(WITH_83_SUPPORT),yes) @ set -e; for f in $(MANPAGES) ; do \ s=$${f##*.}; \ b=$${f%.[0-9]}; \ rm -v $(DESTDIR)$(mandir)/man$$s/$$b-8.3.$$s ; \ done endif html: $(SOURCES:.xml=.html) pdf: $(SOURCES:.xml=.pdf) ps: $(SOURCES:.xml=.ps) ../../configure: @echo "please (re-)run ./autogen.sh with appropriate arguments"; exit 1 ../../config.status: ../../configure @echo "please (re-)run ./configure with appropriate arguments"; exit 1 Makefile.in: ; Makefile: Makefile.in ../../config.status cd ../.. && ./config.status documentation/v83/Makefile drbd-utils-8.9.10/documentation/v83/drbdadm.80000644000175000017500000002473113027211676020551 0ustar apoikosapoikos'\" t .\" Title: drbdadm .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 5 Dec 2008 .\" Manual: System Administration .\" Source: DRBD 8.3.2 .\" Language: English .\" .TH "DRBDADM" "8" "5 Dec 2008" "DRBD 8.3.2" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbdadm \- Administration tool for DRBD .SH "SYNOPSIS" .HP \w'\fBdrbdadm\fR\ 'u \fBdrbdadm\fR [\-d] [\-c\ {\fIfile\fR}] [\-t\ {\fIfile\fR}] [\-s\ {\fIcmd\fR}] [\-m\ {\fIcmd\fR}] [\-S] [\-h\ {\fIhost\fR}] [\-\-\ {\fIbackend\-options\fR}] {\fIcommand\fR} [all | \fIresource\fR...] .SH "DESCRIPTION" .PP \fBDrbdadm\fR is the high level tool of the DRBD program suite\&. \fBDrbdadm\fR is to \fBdrbdsetup\fR and \fBdrbdmeta\fR what \fBifup\fR/\fBifdown\fR is to \fBifconfig\fR\&. \fBDrbdadm\fR reads its configuration file and performs the specified commands by calling the \fBdrbdsetup\fR and/or the \fBdrbdmeta\fR program\&. .SH "OPTIONS" .PP \fB\-d\fR, \fB\-\-dry\-run\fR .RS 4 Just prints the calls of \fBdrbdsetup\fR to stdout, but does not run the commands\&. .RE .PP \fB\-c\fR, \fB\-\-config\-file\fR \fIfile\fR .RS 4 Specifies the configuration file drbdadm will use\&. If this parameter is not specified, drbdadm will look for \fB/etc/drbd\-83\&.conf\fR, \fB/etc/drbd\-08\&.conf\fR and \fB/etc/drbd\&.conf\fR\&. .RE .PP \fB\-t\fR, \fB\-\-config\-to\-test\fR \fIfile\fR .RS 4 Specifies an additional configuration file drbdadm to check\&. This option is only allowed with the dump and the sh\-nop commands\&. .RE .PP \fB\-s\fR, \fB\-\-drbdsetup\fR \fIfile\fR .RS 4 Specifies the full path to the \fBdrbdsetup\fR program\&. If this option is omitted, drbdadm will look for \fB/sbin/drbdsetup\fR and \fB\&./drbdsetup\fR\&. .RE .PP \fB\-m\fR, \fB\-\-drbdmeta\fR \fIfile\fR .RS 4 Specifies the full path to the \fBdrbdmeta\fR program\&. If this option is omitted, drbdadm will look for \fB/sbin/drbdmeta\fR and \fB\&./drbdmeta\fR\&. .RE .PP \fB\-S\fR, \fB\-\-stacked\fR .RS 4 Specifies that this command should be performed on a stacked resource\&. .RE .PP \fB\-P\fR, \fB\-\-peer\fR .RS 4 Specifies to which peer node to connect\&. Only necessary if there are more than two host sections in the resource you are working on\&. .RE .PP \fB\-\-\fR \fIbackend\-options\fR .RS 4 All options following the doubly hyphen are considered \fIbackend\-options\fR\&. These are passed through to the backend command\&. I\&.e\&. to \fBdrbdsetup\fR, \fBdrbdmeta\fR or \fBdrbd\-proxy\-ctl\fR\&. .RE .SH "COMMANDS" .PP attach .RS 4 Attaches a local backing block device to the DRBD resource\*(Aqs device\&. .RE .PP detach .RS 4 Removes the backing storage device from a DRBD resource\*(Aqs device\&. .RE .PP connect .RS 4 Sets up the network configuration of the resource\*(Aqs device\&. If the peer device is already configured, the two DRBD devices will connect\&. If there are more than two host sections in the resource you need to use the \fB\-\-peer\fR option to select the peer you want to connect to\&. .RE .PP disconnect .RS 4 Removes the network configuration from the resource\&. The device will then go into StandAlone state\&. .RE .PP syncer .RS 4 Loads the resynchronization parameters into the device\&. .RE .PP up .RS 4 Is a shortcut for attach and connect\&. .RE .PP down .RS 4 Is a shortcut for disconnect and detach\&. .RE .PP primary .RS 4 Promote the resource\*(Aqs device into primary role\&. You need to do this before any access to the device, such as creating or mounting a file system\&. .RE .PP secondary .RS 4 Brings the device back into secondary role\&. This is needed since in a connected DRBD device pair, only one of the two peers may have primary role (except if \fBallow\-two\-primaries\fR is explicitly set in the configuration file)\&. .RE .PP invalidate .RS 4 Forces DRBD to consider the data on the \fIlocal\fR backing storage device as out\-of\-sync\&. Therefore DRBD will copy each and every block from its peer, to bring the local storage device back in sync\&. To avoid races, you need an established replication link, or be disconnected Secondary\&. .RE .PP invalidate\-remote .RS 4 This command is similar to the invalidate command, however, the \fIpeer\*(Aqs\fR backing storage is invalidated and hence rewritten with the data of the local node\&. To avoid races, you need an established replication link, or be disconnected Primary\&. .RE .PP resize .RS 4 Causes DRBD to re\-examine all sizing constraints, and resize the resource\*(Aqs device accordingly\&. For example, if you increased the size of your backing storage devices (on both nodes, of course), then DRBD will adopt the new size after you called this command on one of your nodes\&. Since new storage space must be synchronised this command only works if there is at least one primary node present\&. .sp The \fB\-\-assume\-peer\-has\-space\fR allows you to resize a device which is currently not connected to the peer\&. Use with care, since if you do not resize the peer\*(Aqs disk as well, further connect attempts of the two will fail\&. .sp The \fB\-\-assume\-clean\fR allows you to resize an existing device and avoid syncing the new space\&. This is useful when adding addtional blank storage to your device\&. Example: .sp .if n \{\ .RS 4 .\} .nf # drbdadm \-\- \-\-assume\-clean resize r0 .fi .if n \{\ .RE .\} .sp .RE .PP check\-resize .RS 4 Calls drbdmeta to eventually move internal meta data\&. If the backing device was resized, while DRBD was not running, meta data has to be moved to the end of the device, so that the next \fBattach\fR command can succeed\&. .RE .PP create\-md .RS 4 Initializes the meta data storage\&. This needs to be done before a DRBD resource can be taken online for the first time\&. In case of issues with that command have a look at \fBdrbdmeta\fR(8) .RE .PP get\-gi .RS 4 Shows a short textual representation of the data generation identifiers\&. .RE .PP show\-gi .RS 4 Prints a textual representation of the data generation identifiers including explanatory information\&. .RE .PP dump\-md .RS 4 Dumps the whole contents of the meta data storage, including the stored bit\-map and activity\-log, in a textual representation\&. .RE .PP outdate .RS 4 Sets the outdated flag in the meta data\&. .RE .PP adjust .RS 4 Synchronizes the configuration of the device with your configuration file\&. You should always examine the output of the dry\-run mode before actually executing this command\&. .RE .PP wait\-connect .RS 4 Waits until the device is connected to its peer device\&. .RE .PP role .RS 4 Shows the current roles of the devices (local/peer)\&. E\&.g\&. Primary/Secondary .RE .PP state .RS 4 Deprecated alias for "role", see above\&. .RE .PP cstate .RS 4 Shows the current connection state of the devices\&. .RE .PP status .RS 4 Shows the current status of all devices defined in the current config file, in XML\-like format\&. Example output: .sp .if n \{\ .RS 4 .\} .nf .fi .if n \{\ .RE .\} .sp .RE .PP dump .RS 4 Just parse the configuration file and dump it to stdout\&. May be used to check the configuration file for syntactic correctness\&. .RE .PP outdate .RS 4 Used to mark the node\*(Aqs data as outdated\&. Usually used by the peer\*(Aqs fence\-peer handler\&. .RE .PP verify .RS 4 Starts online verify\&. During online verify, data on both nodes is compared for equality\&. See /proc/drbd for online verify progress\&. If out\-of\-sync blocks are found, they are \fInot\fR resynchronized automatically\&. To do that, \fBdisconnect\fR and \fBconnect\fR the resource when verification has completed\&. .sp See also the notes on data integrity on the drbd\&.conf manpage\&. .RE .PP pause\-sync .RS 4 Temporarily suspend an ongoing resynchronization by setting the local pause flag\&. Resync only progresses if neither the local nor the remote pause flag is set\&. It might be desirable to postpone DRBD\*(Aqs resynchronization until after any resynchronization of the backing storage\*(Aqs RAID setup\&. .RE .PP resume\-sync .RS 4 Unset the local sync pause flag\&. .RE .PP new\-current\-uuid .RS 4 Generates a new currend UUID and rotates all other UUID values\&. .sp This can be used to shorten the initial resync of a cluster\&. See the \fBdrbdsetup\fR manpage for a more details\&. .RE .PP dstate .RS 4 Show the current state of the backing storage devices\&. (local/peer) .RE .PP hidden\-commands .RS 4 Shows all commands undocumented on purpose\&. .RE .SH "VERSION" .sp This document was revised for version 8\&.3\&.2 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbd\fR(8), \fBdrbddisk\fR(8), \fBdrbdsetup\fR(8), \fBdrbdmeta\fR(8) and the \m[blue]\fBDRBD project web site\fR\m[]\&\s-2\u[1]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD project web site .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v83/drbddisk.80000644000175000017500000000524013027211701020721 0ustar apoikosapoikos'\" t .\" Title: drbddisk .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 15 Oct 2008 .\" Manual: System Administration .\" Source: DRBD 8.3.2 .\" Language: English .\" .TH "DRBDDISK" "8" "15 Oct 2008" "DRBD 8.3.2" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbddisk \- Script to mark devices as primary and mount file systems .SH "SYNOPSIS" .HP \w'\fB/etc/ha\&.d/resource\&.d/drbddisk\fR\ 'u \fB/etc/ha\&.d/resource\&.d/drbddisk\fR [\fIresource\fR] {{start}\ |\ {stop}\ |\ {status}} .SH "INTRODUCTION" .PP The \fB/etc/ha\&.d/resource\&.d/drbddisk\fR script brings the local device of \fIresource\fR into primary role\&. It is designed to be used by Heartbeat\&. .PP In order to use \fB/etc/ha\&.d/resource\&.d/drbddisk\fR you must define a resource, a host, and any other configuration options in the DRBD configuration file\&. See \fB/etc/drbd\&.conf\fR for details\&. If \fIresource\fR is omitted, then all of the resources listed in the config file are affected\&. .SH "VERSION" .sp This document was revised for version 8\&.0\&.14 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbd\fR(8), \fBdrbdsetup\fR(8)\fBdrbdadm\fR(8)\m[blue]\fBDRBD Homepage\fR\m[]\&\s-2\u[1]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD Homepage .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v83/drbd.conf.50000644000175000017500000013326413027211675021011 0ustar apoikosapoikos'\" t .\" Title: drbd.conf .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 5 Dec 2008 .\" Manual: Configuration Files .\" Source: DRBD 8.3.2 .\" Language: English .\" .TH "DRBD\&.CONF" "5" "5 Dec 2008" "DRBD 8.3.2" "Configuration Files" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbd.conf \- Configuration file for DRBD\*(Aqs devices .SH "INTRODUCTION" .PP The file \fB/etc/drbd\&.conf\fR is read by \fBdrbdadm\fR\&. .PP The file format was designed as to allow to have a verbatim copy of the file on both nodes of the cluster\&. It is highly recommended to do so in order to keep your configuration manageable\&. The file \fB/etc/drbd\&.conf\fR should be the same on both nodes of the cluster\&. Changes to \fB/etc/drbd\&.conf\fR do not apply immediately\&. .PP \fBExample\ \&1.\ \&A small drbd\&.conf file\fR .sp .if n \{\ .RS 4 .\} .nf global { usage\-count yes; } common { syncer { rate 10M; } } resource r0 { protocol C; net { cram\-hmac\-alg sha1; shared\-secret "FooFunFactory"; } on alice { device minor 1; disk /dev/sda7; address 10\&.1\&.1\&.31:7789; meta\-disk internal; } on bob { device minor 1; disk /dev/sda7; address 10\&.1\&.1\&.32:7789; meta\-disk internal; } } .fi .if n \{\ .RE .\} In this example, there is a single DRBD resource (called r0) which uses protocol C for the connection between its devices\&. The device which runs on host \fIalice\fR uses \fI/dev/drbd1\fR as devices for its application, and \fI/dev/sda7\fR as low\-level storage for the data\&. The IP addresses are used to specify the networking interfaces to be used\&. An eventually running resync process should use about 10MByte/second of IO bandwidth\&. .PP There may be multiple resource sections in a single drbd\&.conf file\&. For more examples, please have a look at the \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2\&. .SH "FILE FORMAT" .PP The file consists of sections and parameters\&. A section begins with a keyword, sometimes an additional name, and an opening brace (\(lq{\(rq)\&. A section ends with a closing brace (\(lq}\(rq\&. The braces enclose the parameters\&. .PP section [name] { parameter value; [\&.\&.\&.] } .PP A parameter starts with the identifier of the parameter followed by whitespace\&. Every subsequent character is considered as part of the parameter\*(Aqs value\&. A special case are Boolean parameters which consist only of the identifier\&. Parameters are terminated by a semicolon (\(lq;\(rq)\&. .PP Some parameter values have default units which might be overruled by K, M or G\&. These units are defined in the usual way (K = 2^10 = 1024, M = 1024 K, G = 1024 M)\&. .PP Comments may be placed into the configuration file and must begin with a hash sign (\(lq#\(rq)\&. Subsequent characters are ignored until the end of the line\&. .SS "Sections" .PP \fBskip\fR .RS 4 Comments out chunks of text, even spanning more than one line\&. Characters between the keyword \fBskip\fR and the opening brace (\(lq{\(rq) are ignored\&. Everything enclosed by the braces is skipped\&. This comes in handy, if you just want to comment out some \*(Aq\fBresource [name] {\&.\&.\&.}\fR\*(Aq section: just precede it with \*(Aq\(lqskip\(rq\*(Aq\&. .RE .PP \fBglobal\fR .RS 4 Configures some global parameters\&. Currently only \fBminor\-count\fR, \fBdialog\-refresh\fR, \fBdisable\-ip\-verification\fR and \fBusage\-count\fR are allowed here\&. You may only have one global section, preferably as the first section\&. .RE .PP \fBcommon\fR .RS 4 All resources inherit the options set in this section\&. The common section might have a \fBstartup\fR, a \fBsyncer\fR, a \fBhandlers\fR, a \fBnet\fR and a \fBdisk\fR section\&. .RE .PP \fBresource \fR\fB\fIname\fR\fR .RS 4 Configures a DRBD resource\&. Each resource section needs to have two (or more) \fBon \fR\fB\fIhost\fR\fR sections and may have a \fBstartup\fR, a \fBsyncer\fR, a \fBhandlers\fR, a \fBnet\fR and a \fBdisk\fR section\&. Required parameter in this section: \fBprotocol\fR\&. .RE .PP \fBon \fR\fB\fIhost\-name\fR\fR .RS 4 Carries the necessary configuration parameters for a DRBD device of the enclosing resource\&. \fIhost\-name\fR is mandatory and must match the Linux host name (uname \-n) of one of the nodes\&. You may list more than one host name here, in case you want to use the same parameters on several hosts (you\*(Aqd have to move the IP around usually)\&. Or you may list more than two such sections\&. .sp .if n \{\ .RS 4 .\} .nf resource r1 { protocol C; device minor 1; meta\-disk internal; on alice bob { address 10\&.2\&.2\&.100:7801; disk /dev/mapper/some\-san; } on charlie { address 10\&.2\&.2\&.101:7801; disk /dev/mapper/other\-san; } on daisy { address 10\&.2\&.2\&.103:7801; disk /dev/mapper/other\-san\-as\-seen\-from\-daisy; } } .fi .if n \{\ .RE .\} .sp See also the \fBfloating\fR section keyword\&. Required parameters in this section: \fBdevice\fR, \fBdisk\fR, \fBaddress\fR, \fBmeta\-disk\fR, \fBflexible\-meta\-disk\fR\&. .RE .PP \fBstacked\-on\-top\-of \fR\fB\fIresource\fR\fR .RS 4 For a stacked DRBD setup (3 or 4 nodes), a \fBstacked\-on\-top\-of\fR is used instead of an \fBon\fR section\&. Required parameters in this section: \fBdevice\fR and \fBaddress\fR\&. .RE .PP \fBfloating \fR\fB\fIAF addr:port\fR\fR .RS 4 Carries the necessary configuration parameters for a DRBD device of the enclosing resource\&. This section is very similar to the \fBon\fR section\&. The difference to the \fBon\fR section is that the matching of the host sections to machines is done by the IP\-address instead of the node name\&. Required parameters in this section: \fBdevice\fR, \fBdisk\fR, \fBmeta\-disk\fR, \fBflexible\-meta\-disk\fR, all of which \fImay\fR be inherited from the resource section, in which case you may shorten this section down to just the address identifier\&. .sp .if n \{\ .RS 4 .\} .nf resource r2 { protocol C; device minor 2; disk /dev/sda7; meta\-disk internal; # short form, device, disk and meta\-disk inherited floating 10\&.1\&.1\&.31:7802; # longer form, only device inherited floating 10\&.1\&.1\&.32:7802 { disk /dev/sdb; meta\-disk /dev/sdc8; } } .fi .if n \{\ .RE .\} .sp .RE .PP \fBdisk\fR .RS 4 This section is used to fine tune DRBD\*(Aqs properties in respect to the low level storage\&. Please refer to \fBdrbdsetup\fR(8) for detailed description of the parameters\&. Optional parameters: \fBon\-io\-error\fR, \fBsize\fR, \fBfencing\fR, \fBuse\-bmbv\fR, \fBno\-disk\-barrier\fR, \fBno\-disk\-flushes\fR, \fBno\-disk\-drain\fR, \fBno\-md\-flushes\fR, \fBmax\-bio\-bvecs\fR, \fBdisk\-timeout\fR\&. .RE .PP \fBnet\fR .RS 4 This section is used to fine tune DRBD\*(Aqs properties\&. Please refer to \fBdrbdsetup\fR(8) for a detailed description of this section\*(Aqs parameters\&. Optional parameters: \fBsndbuf\-size\fR, \fBrcvbuf\-size\fR, \fBtimeout\fR, \fBconnect\-int\fR, \fBping\-int\fR, \fBping\-timeout\fR, \fBmax\-buffers\fR, \fBmax\-epoch\-size\fR, \fBko\-count\fR, \fBallow\-two\-primaries\fR, \fBcram\-hmac\-alg\fR, \fBshared\-secret\fR, \fBafter\-sb\-0pri\fR, \fBafter\-sb\-1pri\fR, \fBafter\-sb\-2pri\fR, \fBdata\-integrity\-alg\fR, \fBno\-tcp\-cork\fR, \fBon\-congestion\fR, \fBcongestion\-fill\fR, \fBcongestion\-extents\fR .RE .PP \fBstartup\fR .RS 4 This section is used to fine tune DRBD\*(Aqs properties\&. Please refer to \fBdrbdsetup\fR(8) for a detailed description of this section\*(Aqs parameters\&. Optional parameters: \fBwfc\-timeout\fR, \fBdegr\-wfc\-timeout\fR, \fBoutdated\-wfc\-timeout\fR, \fBwait\-after\-sb\fR, \fBstacked\-timeouts\fR and \fBbecome\-primary\-on\fR\&. .RE .PP \fBsyncer\fR .RS 4 This section is used to fine tune the synchronization daemon for the device\&. Please refer to \fBdrbdsetup\fR(8) for a detailed description of this section\*(Aqs parameters\&. Optional parameters: \fBrate\fR, \fBafter\fR, \fBal\-extents\fR, \fBuse\-rle\fR, \fBcpu\-mask\fR, \fBverify\-alg\fR, \fBcsums\-alg\fR, \fBc\-plan\-ahead\fR, \fBc\-fill\-target\fR, \fBc\-delay\-target\fR, \fBc\-max\-rate\fR, \fBc\-min\-rate\fR and \fBon\-no\-data\-accessible\fR\&. .RE .PP \fBhandlers\fR .RS 4 In this section you can define handlers (executables) that are started by the DRBD system in response to certain events\&. Optional parameters: \fBpri\-on\-incon\-degr\fR, \fBpri\-lost\-after\-sb\fR, \fBpri\-lost\fR, \fBfence\-peer\fR (formerly oudate\-peer), \fBlocal\-io\-error\fR, \fBinitial\-split\-brain\fR, \fBsplit\-brain\fR, \fBbefore\-resync\-target\fR, \fBafter\-resync\-target\fR\&. .sp The interface is done via environment variables: .PP \fBDRBD_RESOURCE\fR .RS 4 is the name of the resource .RE .PP \fBDRBD_MINOR\fR .RS 4 is the minor number of the DRBD device, in decimal\&. .RE .PP \fBDRBD_CONF\fR .RS 4 is the path to the primary configuration file; if you split your configuration into multiple files (e\&.g\&. in \fB/etc/drbd\&.conf\&.d/\fR), this will not be helpful\&. .RE .PP \fBDRBD_PEER_AF\fR, \fBDRBD_PEER_ADDRESS\fR, \fBDRBD_PEERS\fR .RS 4 are the address family (e\&.g\&. \fBipv6\fR), the peer\*(Aqs address and hostnames\&. .RE .sp \fBDRBD_PEER\fR (note the singular form) is deprecated, and superseeded by DRBD_PEERS\&. .sp Please note that not all of these might be set for all handlers, and that some values might not be useable for a \fBfloating\fR definition\&. .RE .SS "Parameters" .PP \fBminor\-count \fR\fB\fIcount\fR\fR .RS 4 \fIcount\fR may be a number from 1 to 255\&. .sp Use \fIminor\-count\fR if you want to define massively more resources later without reloading the DRBD kernel module\&. Per default the module loads with 11 more resources than you have currently in your config but at least 32\&. .RE .PP \fBdialog\-refresh \fR\fB\fItime\fR\fR .RS 4 \fItime\fR may be 0 or a positive number\&. .sp The user dialog redraws the second count every \fItime\fR seconds (or does no redraws if \fItime\fR is 0)\&. The default value is 1\&. .RE .PP \fBdisable\-ip\-verification\fR .RS 4 Use \fIdisable\-ip\-verification\fR if, for some obscure reasons, drbdadm can/might not use \fBip\fR or \fBifconfig\fR to do a sanity check for the IP address\&. You can disable the IP verification with this option\&. .RE .PP \fBusage\-count \fR\fB\fIval\fR\fR .RS 4 Please participate in \m[blue]\fBDRBD\*(Aqs online usage counter\fR\m[]\&\s-2\u[2]\d\s+2\&. The most convenient way to do so is to set this option to \fByes\fR\&. Valid options are: \fByes\fR, \fBno\fR and \fBask\fR\&. .RE .PP \fBprotocol \fR\fB\fIprot\-id\fR\fR .RS 4 On the TCP/IP link the specified \fIprotocol\fR is used\&. Valid protocol specifiers are A, B, and C\&. .sp Protocol A: write IO is reported as completed, if it has reached local disk and local TCP send buffer\&. .sp Protocol B: write IO is reported as completed, if it has reached local disk and remote buffer cache\&. .sp Protocol C: write IO is reported as completed, if it has reached both local and remote disk\&. .RE .PP \fBdevice \fR\fB\fIname\fR\fR\fB minor \fR\fB\fInr\fR\fR .RS 4 The name of the block device node of the resource being described\&. You must use this device with your application (file system) and you must not use the low level block device which is specified with the \fBdisk\fR parameter\&. .sp One can ether omit the \fIname\fR or \fBminor\fR and the \fIminor number\fR\&. If you omit the \fIname\fR a default of /dev/drbd\fIminor\fR will be used\&. .sp Udev will create additional symlinks in /dev/drbd/by\-res and /dev/drbd/by\-disk\&. .RE .PP \fBdisk \fR\fB\fIname\fR\fR .RS 4 DRBD uses this block device to actually store and retrieve the data\&. Never access such a device while DRBD is running on top of it\&. This also holds true for \fBdumpe2fs\fR(8) and similar commands\&. .RE .PP \fBaddress \fR\fB\fIAF addr:port\fR\fR .RS 4 A resource needs one \fIIP\fR address per device, which is used to wait for incoming connections from the partner device respectively to reach the partner device\&. \fIAF\fR must be one of \fBipv4\fR, \fBipv6\fR, \fBssocks\fR or \fBsdp\fR (for compatibility reasons \fBsci\fR is an alias for \fBssocks\fR)\&. It may be omited for IPv4 addresses\&. The actual IPv6 address that follows the \fBipv6\fR keyword must be placed inside brackets: ipv6 [fd01:2345:6789:abcd::1]:7800\&. .sp Each DRBD resource needs a TCP \fIport\fR which is used to connect to the node\*(Aqs partner device\&. Two different DRBD resources may not use the same \fIaddr:port\fR combination on the same node\&. .RE .PP \fBmeta\-disk \fR\fB\fIinternal\fR\fR, \fBflexible\-meta\-disk \fR\fB\fIinternal\fR\fR, \fBmeta\-disk \fR\fB\fIdevice [index]\fR\fR, \fBflexible\-meta\-disk \fR\fB\fIdevice \fR\fR .RS 4 Internal means that the last part of the backing device is used to store the meta\-data\&. You must not use \fI[index]\fR with internal\&. Note: Regardless of whether you use the \fBmeta\-disk\fR or the \fBflexible\-meta\-disk\fR keyword, it will always be of the size needed for the remaining storage size\&. .sp You can use a single block \fIdevice\fR to store meta\-data of multiple DRBD devices\&. E\&.g\&. use meta\-disk /dev/sde6[0]; and meta\-disk /dev/sde6[1]; for two different resources\&. In this case the meta\-disk would need to be at least 256 MB in size\&. .sp With the \fBflexible\-meta\-disk\fR keyword you specify a block device as meta\-data storage\&. You usually use this with LVM, which allows you to have many variable sized block devices\&. The required size of the meta\-disk block device is 36kB + Backing\-Storage\-size / 32k\&. Round this number to the next 4kb boundary up and you have the exact size\&. Rule of the thumb: 32kByte per 1GByte of storage, round up to the next MB\&. .RE .PP \fBon\-io\-error \fR\fB\fIhandler\fR\fR .RS 4 \fIhandler\fR is taken, if the lower level device reports io\-errors to the upper layers\&. .sp \fIhandler\fR may be \fBpass_on\fR, \fBcall\-local\-io\-error\fR or \fBdetach\&.\fR .sp \fBpass_on\fR: The node downgrades the disk status to inconsistent, marks the erroneous block as inconsistent in the bitmap and retries the IO on the remote node\&. .sp \fBcall\-local\-io\-error\fR: Call the handler script \fBlocal\-io\-error\fR\&. .sp \fBdetach\fR: The node drops its low level device, and continues in diskless mode\&. .RE .PP \fBfencing \fR\fB\fIfencing_policy\fR\fR .RS 4 By \fBfencing\fR we understand preventive measures to avoid situations where both nodes are primary and disconnected (AKA split brain)\&. .sp Valid fencing policies are: .PP \fBdont\-care\fR .RS 4 This is the default policy\&. No fencing actions are taken\&. .RE .PP \fBresource\-only\fR .RS 4 If a node becomes a disconnected primary, it tries to fence the peer\*(Aqs disk\&. This is done by calling the \fBfence\-peer\fR handler\&. The handler is supposed to reach the other node over alternative communication paths and call \*(Aq\fBdrbdadm outdate res\fR\*(Aq there\&. .RE .PP \fBresource\-and\-stonith\fR .RS 4 If a node becomes a disconnected primary, it freezes all its IO operations and calls its fence\-peer handler\&. The fence\-peer handler is supposed to reach the peer over alternative communication paths and call \*(Aqdrbdadm outdate res\*(Aq there\&. In case it cannot reach the peer it should stonith the peer\&. IO is resumed as soon as the situation is resolved\&. In case your handler fails, you can resume IO with the \fBresume\-io\fR command\&. .RE .RE .PP \fBuse\-bmbv\fR .RS 4 In case the backing storage\*(Aqs driver has a merge_bvec_fn() function, DRBD has to pretend that it can only process IO requests in units not larger than 4KiB\&. (At the time of writing the only known drivers which have such a function are: md (software raid driver), dm (device mapper \- LVM) and DRBD itself)\&. .sp To get the best performance out of DRBD on top of software RAID (or any other driver with a merge_bvec_fn() function) you might enable this function, if you know for sure that the merge_bvec_fn() function will deliver the same results on all nodes of your cluster\&. I\&.e\&. the physical disks of the software RAID are of exactly the same type\&. \fIUse this option only if you know what you are doing\&.\fR .RE .PP \fBno\-disk\-barrier\fR, \fBno\-disk\-flushes\fR, \fBno\-disk\-drain\fR .RS 4 DRBD has four implementations to express write\-after\-write dependencies to its backing storage device\&. DRBD will use the first method that is supported by the backing storage device and that is not disabled by the user\&. .sp When selecting the method you should not only base your decision on the measurable performance\&. In case your backing storage device has a volatile write cache (plain disks, RAID of plain disks) you should use one of the first two\&. In case your backing storage device has battery\-backed write cache you may go with option 3\&. Option 4 (disable everything, use "none") \fIis dangerous\fR on most IO stacks, may result in write\-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles\&. \fIDo not use\fR \fBno\-disk\-drain\fR\&. .sp Unfortunately device mapper (LVM) might not support barriers\&. .sp The letter after "wo:" in /proc/drbd indicates with method is currently in use for a device: \fBb\fR, \fBf\fR, \fBd\fR, \fBn\fR\&. The implementations are: .PP barrier .RS 4 The first requires that the driver of the backing storage device support barriers (called \*(Aqtagged command queuing\*(Aq in SCSI and \*(Aqnative command queuing\*(Aq in SATA speak)\&. The use of this method can be disabled by the \fBno\-disk\-barrier\fR option\&. Note: Since Linux\-2\&.6\&.36 (or RHEL\*(Aqs 2\&.6\&.32) this method is disabled\&. .RE .PP flush .RS 4 The second requires that the backing device support disk flushes (called \*(Aqforce unit access\*(Aq in the drive vendors speak)\&. The use of this method can be disabled using the \fBno\-disk\-flushes\fR option\&. .RE .PP drain .RS 4 The third method is simply to let write requests drain before write requests of a new reordering domain are issued\&. This was the only implementation before 8\&.0\&.9\&. .RE .PP none .RS 4 The fourth method is to not express write\-after\-write dependencies to the backing store at all, by also specifying \fBno\-disk\-drain\fR\&. This \fIis dangerous\fR on most IO stacks, may result in write\-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles\&. \fIDo not use\fR \fBno\-disk\-drain\fR\&. .RE .RE .PP \fBno\-md\-flushes\fR .RS 4 Disables the use of disk flushes and barrier BIOs when accessing the meta data device\&. See the notes on \fBno\-disk\-flushes\fR\&. .RE .PP \fBmax\-bio\-bvecs\fR .RS 4 In some special circumstances the device mapper stack manages to pass BIOs to DRBD that violate the constraints that are set forth by DRBD\*(Aqs merge_bvec() function and which have more than one bvec\&. A known example is: phys\-disk \-> DRBD \-> LVM \-> Xen \-> misaligned partition (63) \-> DomU FS\&. Then you might see "bio would need to, but cannot, be split:" in the Dom0\*(Aqs kernel log\&. .sp The best workaround is to proper align the partition within the VM (E\&.g\&. start it at sector 1024)\&. This costs 480 KiB of storage\&. Unfortunately the default of most Linux partitioning tools is to start the first partition at an odd number (63)\&. Therefore most distribution\*(Aqs install helpers for virtual linux machines will end up with misaligned partitions\&. The second best workaround is to limit DRBD\*(Aqs max bvecs per BIO (= \fBmax\-bio\-bvecs\fR) to 1, but that might cost performance\&. .sp The default value of \fBmax\-bio\-bvecs\fR is 0, which means that there is no user imposed limitation\&. .RE .PP \fBdisk\-timeout\fR .RS 4 If the driver of the \fIlower_device\fR does not finish an IO request within \fIdisk_timeout\fR, DRBD considers the disk as failed\&. If DRBD is connected to a remote host, it will reissue local pending IO requests to the peer, and ship all new IO requests to the peer only\&. The disk state advances to diskless, as soon as the backing block device has finished all IO requests\&. .sp The default value of is 0, which means that no timeout is enforced\&. The default unit is 100ms\&. This option is available since 8\&.3\&.12\&. .RE .PP \fBsndbuf\-size \fR\fB\fIsize\fR\fR .RS 4 \fIsize\fR is the size of the TCP socket send buffer\&. The default value is 0, i\&.e\&. autotune\&. You can specify smaller or larger values\&. Larger values are appropriate for reasonable write throughput with protocol A over high latency networks\&. Values below 32K do not make sense\&. Since 8\&.0\&.13 resp\&. 8\&.2\&.7, setting the \fIsize\fR value to 0 means that the kernel should autotune this\&. .RE .PP \fBrcvbuf\-size \fR\fB\fIsize\fR\fR .RS 4 \fIsize\fR is the size of the TCP socket receive buffer\&. The default value is 0, i\&.e\&. autotune\&. You can specify smaller or larger values\&. Usually this should be left at its default\&. Setting the \fIsize\fR value to 0 means that the kernel should autotune this\&. .RE .PP \fBtimeout \fR\fB\fItime\fR\fR .RS 4 If the partner node fails to send an expected response packet within \fItime\fR tenths of a second, the partner node is considered dead and therefore the TCP/IP connection is abandoned\&. This must be lower than \fIconnect\-int\fR and \fIping\-int\fR\&. The default value is 60 = 6 seconds, the unit 0\&.1 seconds\&. .RE .PP \fBconnect\-int \fR\fB\fItime\fR\fR .RS 4 In case it is not possible to connect to the remote DRBD device immediately, DRBD keeps on trying to connect\&. With this option you can set the time between two retries\&. The default value is 10 seconds, the unit is 1 second\&. .RE .PP \fBping\-int \fR\fB\fItime\fR\fR .RS 4 If the TCP/IP connection linking a DRBD device pair is idle for more than \fItime\fR seconds, DRBD will generate a keep\-alive packet to check if its partner is still alive\&. The default is 10 seconds, the unit is 1 second\&. .RE .PP \fBping\-timeout \fR\fB\fItime\fR\fR .RS 4 The time the peer has time to answer to a keep\-alive packet\&. In case the peer\*(Aqs reply is not received within this time period, it is considered as dead\&. The default value is 500ms, the default unit are tenths of a second\&. .RE .PP \fBmax\-buffers \fR\fB\fInumber\fR\fR .RS 4 Limits the memory usage per DRBD minor device on the receiving side, or for internal buffers during resync or online\-verify\&. Unit is PAGE_SIZE, which is 4 KiB on most systems\&. The minimum possible setting is hard coded to 32 (=128 KiB)\&. These buffers are used to hold data blocks while they are written to/read from disk\&. To avoid possible distributed deadlocks on congestion, this setting is used as a throttle threshold rather than a hard limit\&. Once more than max\-buffers pages are in use, further allocation from this pool is throttled\&. You want to increase max\-buffers if you cannot saturate the IO backend on the receiving side\&. .RE .PP \fBko\-count \fR\fB\fInumber\fR\fR .RS 4 In case the secondary node fails to complete a single write request for \fIcount\fR times the \fItimeout\fR, it is expelled from the cluster\&. (I\&.e\&. the primary node goes into \fBStandAlone\fR mode\&.) To disable this feature, you should explicitly set it to 0; defaults may change between versions\&. .RE .PP \fBmax\-epoch\-size \fR\fB\fInumber\fR\fR .RS 4 The highest number of data blocks between two write barriers\&. If you set this smaller than 10, you might decrease your performance\&. .RE .PP \fBallow\-two\-primaries\fR .RS 4 With this option set you may assign the primary role to both nodes\&. You only should use this option if you use a shared storage file system on top of DRBD\&. At the time of writing the only ones are: OCFS2 and GFS\&. If you use this option with any other file system, you are going to crash your nodes and to corrupt your data! .RE .PP \fBunplug\-watermark \fR\fB\fInumber\fR\fR .RS 4 This setting has no effect with recent kernels that use explicit on\-stack plugging (upstream Linux kernel 2\&.6\&.39, distributions may have backported)\&. .sp When the number of pending write requests on the standby (secondary) node exceeds the \fBunplug\-watermark\fR, we trigger the request processing of our backing storage device\&. Some storage controllers deliver better performance with small values, others deliver best performance when the value is set to the same value as max\-buffers, yet others don\*(Aqt feel much effect at all\&. Minimum 16, default 128, maximum 131072\&. .RE .PP \fBcram\-hmac\-alg\fR .RS 4 You need to specify the HMAC algorithm to enable peer authentication at all\&. You are strongly encouraged to use peer authentication\&. The HMAC algorithm will be used for the challenge response authentication of the peer\&. You may specify any digest algorithm that is named in \fB/proc/crypto\fR\&. .RE .PP \fBshared\-secret\fR .RS 4 The shared secret used in peer authentication\&. May be up to 64 characters\&. Note that peer authentication is disabled as long as no \fBcram\-hmac\-alg\fR (see above) is specified\&. .RE .PP \fBafter\-sb\-0pri \fR \fIpolicy\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBdiscard\-younger\-primary\fR .RS 4 Auto sync from the node that was primary before the split\-brain situation happened\&. .RE .PP \fBdiscard\-older\-primary\fR .RS 4 Auto sync from the node that became primary as second during the split\-brain situation\&. .RE .PP \fBdiscard\-zero\-changes\fR .RS 4 In case one node did not write anything since the split brain became evident, sync from the node that wrote something to the node that did not write anything\&. In case none wrote anything this policy uses a random decision to perform a "resync" of 0 blocks\&. In case both have written something this policy disconnects the nodes\&. .RE .PP \fBdiscard\-least\-changes\fR .RS 4 Auto sync from the node that touched more blocks during the split brain situation\&. .RE .PP \fBdiscard\-node\-NODENAME\fR .RS 4 Auto sync to the named node\&. .RE .RE .PP \fBafter\-sb\-1pri \fR \fIpolicy\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBconsensus\fR .RS 4 Discard the version of the secondary if the outcome of the \fBafter\-sb\-0pri\fR algorithm would also destroy the current secondary\*(Aqs data\&. Otherwise disconnect\&. .RE .PP \fBviolently\-as0p\fR .RS 4 Always take the decision of the \fBafter\-sb\-0pri\fR algorithm, even if that causes an erratic change of the primary\*(Aqs view of the data\&. This is only useful if you use a one\-node FS (i\&.e\&. not OCFS2 or GFS) with the \fBallow\-two\-primaries\fR flag, \fIAND\fR if you really know what you are doing\&. This is \fIDANGEROUS and MAY CRASH YOUR MACHINE\fR if you have an FS mounted on the primary node\&. .RE .PP \fBdiscard\-secondary\fR .RS 4 Discard the secondary\*(Aqs version\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Always honor the outcome of the \fBafter\-sb\-0pri \fR algorithm\&. In case it decides the current secondary has the right data, it calls the "pri\-lost\-after\-sb" handler on the current primary\&. .RE .RE .PP \fBafter\-sb\-2pri \fR \fIpolicy\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBviolently\-as0p\fR .RS 4 Always take the decision of the \fBafter\-sb\-0pri\fR algorithm, even if that causes an erratic change of the primary\*(Aqs view of the data\&. This is only useful if you use a one\-node FS (i\&.e\&. not OCFS2 or GFS) with the \fBallow\-two\-primaries\fR flag, \fIAND\fR if you really know what you are doing\&. This is \fIDANGEROUS and MAY CRASH YOUR MACHINE\fR if you have an FS mounted on the primary node\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Call the "pri\-lost\-after\-sb" helper program on one of the machines\&. This program is expected to reboot the machine, i\&.e\&. make it secondary\&. .RE .RE .PP \fBalways\-asbp\fR .RS 4 Normally the automatic after\-split\-brain policies are only used if current states of the UUIDs do not indicate the presence of a third node\&. .sp With this option you request that the automatic after\-split\-brain policies are used as long as the data sets of the nodes are somehow related\&. This might cause a full sync, if the UUIDs indicate the presence of a third node\&. (Or double faults led to strange UUID sets\&.) .RE .PP \fBrr\-conflict \fR \fIpolicy\fR .RS 4 This option helps to solve the cases when the outcome of the resync decision is incompatible with the current role assignment in the cluster\&. .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBviolently\fR .RS 4 Sync to the primary node is allowed, violating the assumption that data on a block device are stable for one of the nodes\&. \fIDangerous, do not use\&.\fR .RE .PP \fBcall\-pri\-lost\fR .RS 4 Call the "pri\-lost" helper program on one of the machines\&. This program is expected to reboot the machine, i\&.e\&. make it secondary\&. .RE .RE .PP \fBdata\-integrity\-alg \fR \fIalg\fR .RS 4 DRBD can ensure the data integrity of the user\*(Aqs data on the network by comparing hash values\&. Normally this is ensured by the 16 bit checksums in the headers of TCP/IP packets\&. .sp This option can be set to any of the kernel\*(Aqs data digest algorithms\&. In a typical kernel configuration you should have at least one of \fBmd5\fR, \fBsha1\fR, and \fBcrc32c\fR available\&. By default this is not enabled\&. .sp See also the notes on data integrity\&. .RE .PP \fBno\-tcp\-cork\fR .RS 4 DRBD usually uses the TCP socket option TCP_CORK to hint to the network stack when it can expect more data, and when it should flush out what it has in its send queue\&. It turned out that there is at least one network stack that performs worse when one uses this hinting method\&. Therefore we introducted this option, which disables the setting and clearing of the TCP_CORK socket option by DRBD\&. .RE .PP \fBon\-congestion \fR\fB\fIcongestion_policy\fR\fR, \fBcongestion\-fill \fR\fB\fIfill_threshold\fR\fR, \fBcongestion\-extents \fR\fB\fIactive_extents_threshold\fR\fR .RS 4 By default DRBD blocks when the available TCP send queue becomes full\&. That means it will slow down the application that generates the write requests that cause DRBD to send more data down that TCP connection\&. .sp When DRBD is deployed with DRBD\-proxy it might be more desirable that DRBD goes into AHEAD/BEHIND mode shortly before the send queue becomes full\&. In AHEAD/BEHIND mode DRBD does no longer replicate data, but still keeps the connection open\&. .sp The advantage of the AHEAD/BEHIND mode is that the application is not slowed down, even if DRBD\-proxy\*(Aqs buffer is not sufficient to buffer all write requests\&. The downside is that the peer node falls behind, and that a resync will be necessary to bring it back into sync\&. During that resync the peer node will have an inconsistent disk\&. .sp Available \fIcongestion_policy\fRs are \fBblock\fR and \fBpull\-ahead\fR\&. The default is \fBblock\fR\&. \fIFill_threshold\fR might be in the range of 0 to 10GiBytes\&. The default is 0 which disables the check\&. \fIActive_extents_threshold\fR has the same limits as \fBal\-extents\fR\&. .sp The AHEAD/BEHIND mode and its settings are available since DRBD 8\&.3\&.10\&. .RE .PP \fBwfc\-timeout \fR\fB\fItime\fR\fR .RS 4 Wait for connection timeout\&. The init script \fBdrbd\fR(8) blocks the boot process until the DRBD resources are connected\&. When the cluster manager starts later, it does not see a resource with internal split\-brain\&. In case you want to limit the wait time, do it here\&. Default is 0, which means unlimited\&. The unit is seconds\&. .RE .PP \fBdegr\-wfc\-timeout \fR\fB\fItime\fR\fR .RS 4 Wait for connection timeout, if this node was a degraded cluster\&. In case a degraded cluster (= cluster with only one node left) is rebooted, this timeout value is used instead of wfc\-timeout, because the peer is less likely to show up in time, if it had been dead before\&. Value 0 means unlimited\&. .RE .PP \fBoutdated\-wfc\-timeout \fR\fB\fItime\fR\fR .RS 4 Wait for connection timeout, if the peer was outdated\&. In case a degraded cluster (= cluster with only one node left) with an outdated peer disk is rebooted, this timeout value is used instead of wfc\-timeout, because the peer is not allowed to become primary in the meantime\&. Value 0 means unlimited\&. .RE .PP \fBwait\-after\-sb\fR .RS 4 By setting this option you can make the init script to continue to wait even if the device pair had a split brain situation and therefore refuses to connect\&. .RE .PP \fBbecome\-primary\-on \fR\fB\fInode\-name\fR\fR .RS 4 Sets on which node the device should be promoted to primary role by the init script\&. The \fInode\-name\fR might either be a host name or the keyword \fBboth\fR\&. When this option is not set the devices stay in secondary role on both nodes\&. Usually one delegates the role assignment to a cluster manager (e\&.g\&. heartbeat)\&. .RE .PP \fBstacked\-timeouts\fR .RS 4 Usually \fBwfc\-timeout\fR and \fBdegr\-wfc\-timeout\fR are ignored for stacked devices, instead twice the amount of \fBconnect\-int\fR is used for the connection timeouts\&. With the \fBstacked\-timeouts\fR keyword you disable this, and force DRBD to mind the \fBwfc\-timeout\fR and \fBdegr\-wfc\-timeout\fR statements\&. Only do that if the peer of the stacked resource is usually not available or will usually not become primary\&. By using this option incorrectly, you run the risk of causing unexpected split brain\&. .RE .PP \fBrate \fR\fB\fIrate\fR\fR .RS 4 To ensure a smooth operation of the application on top of DRBD, it is possible to limit the bandwidth which may be used by background synchronizations\&. The default is 250 KB/sec, the default unit is KB/sec\&. Optional suffixes K, M, G are allowed\&. .RE .PP \fBuse\-rle\fR .RS 4 During resync\-handshake, the dirty\-bitmaps of the nodes are exchanged and merged (using bit\-or), so the nodes will have the same understanding of which blocks are dirty\&. On large devices, the fine grained dirty\-bitmap can become large as well, and the bitmap exchange can take quite some time on low\-bandwidth links\&. .sp Because the bitmap typically contains compact areas where all bits are unset (clean) or set (dirty), a simple run\-length encoding scheme can considerably reduce the network traffic necessary for the bitmap exchange\&. .sp For backward compatibilty reasons, and because on fast links this possibly does not improve transfer time but consumes cpu cycles, this defaults to off\&. .RE .PP \fBafter \fR\fB\fIres\-name\fR\fR .RS 4 By default, resynchronization of all devices would run in parallel\&. By defining a sync\-after dependency, the resynchronization of this resource will start only if the resource \fIres\-name\fR is already in connected state (i\&.e\&., has finished its resynchronization)\&. .RE .PP \fBal\-extents \fR\fB\fIextents\fR\fR .RS 4 DRBD automatically performs hot area detection\&. With this parameter you control how big the hot area (= active set) can get\&. Each extent marks 4M of the backing storage (= low\-level device)\&. In case a primary node leaves the cluster unexpectedly, the areas covered by the active set must be resynced upon rejoining of the failed node\&. The data structure is stored in the meta\-data area, therefore each change of the active set is a write operation to the meta\-data device\&. A higher number of extents gives longer resync times but less updates to the meta\-data\&. The default number of \fIextents\fR is 127\&. (Minimum: 7, Maximum: 3843) .RE .PP \fBverify\-alg \fR\fB\fIhash\-alg\fR\fR .RS 4 During online verification (as initiated by the \fBverify\fR sub\-command), rather than doing a bit\-wise comparison, DRBD applies a hash function to the contents of every block being verified, and compares that hash with the peer\&. This option defines the hash algorithm being used for that purpose\&. It can be set to any of the kernel\*(Aqs data digest algorithms\&. In a typical kernel configuration you should have at least one of \fBmd5\fR, \fBsha1\fR, and \fBcrc32c\fR available\&. By default this is not enabled; you must set this option explicitly in order to be able to use on\-line device verification\&. .sp See also the notes on data integrity\&. .RE .PP \fBcsums\-alg \fR\fB\fIhash\-alg\fR\fR .RS 4 A resync process sends all marked data blocks from the source to the destination node, as long as no \fBcsums\-alg\fR is given\&. When one is specified the resync process exchanges hash values of all marked blocks first, and sends only those data blocks that have different hash values\&. .sp This setting is useful for DRBD setups with low bandwidth links\&. During the restart of a crashed primary node, all blocks covered by the activity log are marked for resync\&. But a large part of those will actually be still in sync, therefore using \fBcsums\-alg\fR will lower the required bandwidth in exchange for CPU cycles\&. .RE .PP \fBc\-plan\-ahead \fR\fB\fIplan_time\fR\fR, \fBc\-fill\-target \fR\fB\fIfill_target\fR\fR, \fBc\-delay\-target \fR\fB\fIdelay_target\fR\fR, \fBc\-max\-rate \fR\fB\fImax_rate\fR\fR .RS 4 The dynamic resync speed controller gets enabled with setting \fIplan_time\fR to a positive value\&. It aims to fill the buffers along the data path with either a constant amount of data \fIfill_target\fR, or aims to have a constant delay time of \fIdelay_target\fR along the path\&. The controller has an upper bound of \fImax_rate\fR\&. .sp By \fIplan_time\fR the agility of the controller is configured\&. Higher values yield for slower/lower responses of the controller to deviation from the target value\&. It should be at least 5 times RTT\&. For regular data paths a \fIfill_target\fR in the area of 4k to 100k is appropriate\&. For a setup that contains drbd\-proxy it is advisable to use \fIdelay_target\fR instead\&. Only when \fIfill_target\fR is set to 0 the controller will use \fIdelay_target\fR\&. 5 times RTT is a reasonable starting value\&. \fIMax_rate\fR should be set to the bandwidth available between the DRBD\-hosts and the machines hosting DRBD\-proxy, or to the available disk\-bandwidth\&. .sp The default value of \fIplan_time\fR is 0, the default unit is 0\&.1 seconds\&. \fIFill_target\fR has 0 and sectors as default unit\&. \fIDelay_target\fR has 1 (100ms) and 0\&.1 as default unit\&. \fIMax_rate\fR has 10240 (100MiB/s) and KiB/s as default unit\&. .sp The dynamic resync speed controller and its settings are available since DRBD 8\&.3\&.9\&. .RE .PP \fBc\-min\-rate \fR\fB\fImin_rate\fR\fR .RS 4 A node that is primary and sync\-source has to schedule application IO requests and resync IO requests\&. The \fImin_rate\fR tells DRBD use only up to min_rate for resync IO and to dedicate all other available IO bandwidth to application requests\&. .sp Note: The value 0 has a special meaning\&. It disables the limitation of resync IO completely, which might slow down application IO considerably\&. Set it to a value of 1, if you prefer that resync IO never slows down application IO\&. .sp Note: Although the name might suggest that it is a lower bound for the dynamic resync speed controller, it is not\&. If the DRBD\-proxy buffer is full, the dynamic resync speed controller is free to lower the resync speed down to 0, completely independent of the \fBc\-min\-rate\fR setting\&. .sp \fIMin_rate\fR has 4096 (4MiB/s) and KiB/s as default unit\&. .RE .PP \fBon\-no\-data\-accessible \fR\fB\fIond\-policy\fR\fR .RS 4 This setting controls what happens to IO requests on a degraded, disk less node (I\&.e\&. no data store is reachable)\&. The available policies are \fBio\-error\fR and \fBsuspend\-io\fR\&. .sp If \fIond\-policy\fR is set to \fBsuspend\-io\fR you can either resume IO by attaching/connecting the last lost data storage, or by the \fBdrbdadm resume\-io \fR\fB\fIres\fR\fR command\&. The latter will result in IO errors of course\&. .sp The default is \fBio\-error\fR\&. This setting is available since DRBD 8\&.3\&.9\&. .RE .PP \fBcpu\-mask \fR\fB\fIcpu\-mask\fR\fR .RS 4 Sets the cpu\-affinity\-mask for DRBD\*(Aqs kernel threads of this device\&. The default value of \fIcpu\-mask\fR is 0, which means that DRBD\*(Aqs kernel threads should be spread over all CPUs of the machine\&. This value must be given in hexadecimal notation\&. If it is too big it will be truncated\&. .RE .PP \fBpri\-on\-incon\-degr \fR\fB\fIcmd\fR\fR .RS 4 This handler is called if the node is primary, degraded and if the local copy of the data is inconsistent\&. .RE .PP \fBpri\-lost\-after\-sb \fR\fB\fIcmd\fR\fR .RS 4 The node is currently primary, but lost the after\-split\-brain auto recovery procedure\&. As as consequence, it should be abandoned\&. .RE .PP \fBpri\-lost \fR\fB\fIcmd\fR\fR .RS 4 The node is currently primary, but DRBD\*(Aqs algorithm thinks that it should become sync target\&. As a consequence it should give up its primary role\&. .RE .PP \fBfence\-peer \fR\fB\fIcmd\fR\fR .RS 4 The handler is part of the \fBfencing\fR mechanism\&. This handler is called in case the node needs to fence the peer\*(Aqs disk\&. It should use other communication paths than DRBD\*(Aqs network link\&. .RE .PP \fBlocal\-io\-error \fR\fB\fIcmd\fR\fR .RS 4 DRBD got an IO error from the local IO subsystem\&. .RE .PP \fBinitial\-split\-brain \fR\fB\fIcmd\fR\fR .RS 4 DRBD has connected and detected a split brain situation\&. This handler can alert someone in all cases of split brain, not just those that go unresolved\&. .RE .PP \fBsplit\-brain \fR\fB\fIcmd\fR\fR .RS 4 DRBD detected a split brain situation but remains unresolved\&. Manual recovery is necessary\&. This handler should alert someone on duty\&. .RE .PP \fBbefore\-resync\-target \fR\fB\fIcmd\fR\fR .RS 4 DRBD calls this handler just before a resync begins on the node that becomes resync target\&. It might be used to take a snapshot of the backing block device\&. .RE .PP \fBafter\-resync\-target \fR\fB\fIcmd\fR\fR .RS 4 DRBD calls this handler just after a resync operation finished on the node whose disk just became consistent after being inconsistent for the duration of the resync\&. It might be used to remove a snapshot of the backing device that was created by the \fBbefore\-resync\-target\fR handler\&. .RE .SS "Other Keywords" .PP \fBinclude \fR\fB\fIfile\-pattern\fR\fR .RS 4 Include all files matching the wildcard pattern \fIfile\-pattern\fR\&. The \fBinclude\fR statement is only allowed on the top level, i\&.e\&. it is not allowed inside any section\&. .RE .SH "NOTES ON DATA INTEGRITY" .PP There are two independent methods in DRBD to ensure the integrity of the mirrored data\&. The online\-verify mechanism and the \fBdata\-integrity\-alg\fR of the \fBnetwork\fR section\&. .PP Both mechanisms might deliver false positives if the user of DRBD modifies the data which gets written to disk while the transfer goes on\&. This may happen for swap, or for certain append while global sync, or truncate/rewrite workloads, and not necessarily poses a problem for the integrity of the data\&. Usually when the initiator of the data transfer does this, it already knows that that data block will not be part of an on disk data structure, or will be resubmitted with correct data soon enough\&. .PP The \fBdata\-integrity\-alg\fR causes the receiving side to log an error about "Digest integrity check FAILED: Ns +x\en", where N is the sector offset, and x is the size of the requst in bytes\&. It will then disconnect, and reconnect, thus causing a quick resync\&. If the sending side at the same time detected a modification, it warns about "Digest mismatch, buffer modified by upper layers during write: Ns +x\en", which shows that this was a false positive\&. The sending side may detect these buffer modifications immediately after the unmodified data has been copied to the tcp buffers, in which case the receiving side won\*(Aqt notice it\&. .PP The most recent (2007) example of systematic corruption was an issue with the TCP offloading engine and the driver of a certain type of GBit NIC\&. The actual corruption happened on the DMA transfer from core memory to the card\&. Since the TCP checksum gets calculated on the card, this type of corruption stays undetected as long as you do not use either the online \fBverify\fR or the \fBdata\-integrity\-alg\fR\&. .PP We suggest to use the \fBdata\-integrity\-alg\fR only during a pre\-production phase due to its CPU costs\&. Further we suggest to do online \fBverify\fR runs regularly e\&.g\&. once a month during a low load period\&. .SH "VERSION" .sp This document was revised for version 8\&.3\&.2 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd\fR(8), \fBdrbddisk\fR(8), \fBdrbdsetup\fR(8), \fBdrbdadm\fR(8), \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2, \m[blue]\fBDRBD web site\fR\m[]\&\s-2\u[3]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD User's Guide .RS 4 \%http://www.drbd.org/users-guide/ .RE .IP " 2." 4 DRBD's online usage counter .RS 4 \%http://usage.drbd.org .RE .IP " 3." 4 DRBD web site .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v83/drbd.conf.xml0000644000175000017500000023722612504755522021453 0ustar apoikosapoikos 5 Dec 2008 DRBD 8.3.2 drbd.conf 5 Configuration Files drbd.conf Configuration file for DRBD's devices drbd.conf Introduction The file is read by . The file format was designed as to allow to have a verbatim copy of the file on both nodes of the cluster. It is highly recommended to do so in order to keep your configuration manageable. The file should be the same on both nodes of the cluster. Changes to do not apply immediately. A small drbd.conf fileglobal { usage-count yes; } common { syncer { rate 10M; } } resource r0 { protocol C; net { cram-hmac-alg sha1; shared-secret "FooFunFactory"; } on alice { device minor 1; disk /dev/sda7; address 10.1.1.31:7789; meta-disk internal; } on bob { device minor 1; disk /dev/sda7; address 10.1.1.32:7789; meta-disk internal; } } In this example, there is a single DRBD resource (called r0) which uses protocol C for the connection between its devices. The device which runs on host alice uses /dev/drbd1 as devices for its application, and /dev/sda7 as low-level storage for the data. The IP addresses are used to specify the networking interfaces to be used. An eventually running resync process should use about 10MByte/second of IO bandwidth. There may be multiple resource sections in a single drbd.conf file. For more examples, please have a look at the DRBD User's Guide. File Format The file consists of sections and parameters. A section begins with a keyword, sometimes an additional name, and an opening brace ({). A section ends with a closing brace (}. The braces enclose the parameters. section [name] { parameter value; [...] } A parameter starts with the identifier of the parameter followed by whitespace. Every subsequent character is considered as part of the parameter's value. A special case are Boolean parameters which consist only of the identifier. Parameters are terminated by a semicolon (;). Some parameter values have default units which might be overruled by K, M or G. These units are defined in the usual way (K = 2^10 = 1024, M = 1024 K, G = 1024 M). Comments may be placed into the configuration file and must begin with a hash sign (#). Subsequent characters are ignored until the end of the line. Sections drbd.confskip Comments out chunks of text, even spanning more than one line. Characters between the keyword and the opening brace ({) are ignored. Everything enclosed by the braces is skipped. This comes in handy, if you just want to comment out some '' section: just precede it with 'skip'. drbd.confglobal Configures some global parameters. Currently only , , and are allowed here. You may only have one global section, preferably as the first section. drbd.confcommon All resources inherit the options set in this section. The common section might have a , a , a , a and a section. drbd.confresource Configures a DRBD resource. Each resource section needs to have two (or more) sections and may have a , a , a , a and a section. Required parameter in this section: . drbd.confon Carries the necessary configuration parameters for a DRBD device of the enclosing resource. host-name is mandatory and must match the Linux host name (uname -n) of one of the nodes. You may list more than one host name here, in case you want to use the same parameters on several hosts (you'd have to move the IP around usually). Or you may list more than two such sections. resource r1 { protocol C; device minor 1; meta-disk internal; on alice bob { address 10.2.2.100:7801; disk /dev/mapper/some-san; } on charlie { address 10.2.2.101:7801; disk /dev/mapper/other-san; } on daisy { address 10.2.2.103:7801; disk /dev/mapper/other-san-as-seen-from-daisy; } } See also the section keyword. Required parameters in this section: , , , , . drbd.confstacked-on-top-of For a stacked DRBD setup (3 or 4 nodes), a is used instead of an section. Required parameters in this section: and . drbd.confon Carries the necessary configuration parameters for a DRBD device of the enclosing resource. This section is very similar to the section. The difference to the section is that the matching of the host sections to machines is done by the IP-address instead of the node name. Required parameters in this section: , , , , all of which may be inherited from the resource section, in which case you may shorten this section down to just the address identifier. resource r2 { protocol C; device minor 2; disk /dev/sda7; meta-disk internal; # short form, device, disk and meta-disk inherited floating 10.1.1.31:7802; # longer form, only device inherited floating 10.1.1.32:7802 { disk /dev/sdb; meta-disk /dev/sdc8; } } drbd.confdisk This section is used to fine tune DRBD's properties in respect to the low level storage. Please refer to drbdsetup8 for detailed description of the parameters. Optional parameters: , , , , , , , , , . drbd.confnet This section is used to fine tune DRBD's properties. Please refer to drbdsetup8 for a detailed description of this section's parameters. Optional parameters: , , , , , , , , , , , , , , , , , , , drbd.confstartup This section is used to fine tune DRBD's properties. Please refer to drbdsetup8 for a detailed description of this section's parameters. Optional parameters: , , , , and . drbd.confsyncer This section is used to fine tune the synchronization daemon for the device. Please refer to drbdsetup8 for a detailed description of this section's parameters. Optional parameters: , , , , , , , , , , , and . drbd.confhandlers In this section you can define handlers (executables) that are started by the DRBD system in response to certain events. Optional parameters: , , , (formerly oudate-peer), , , , , . The interface is done via environment variables: is the name of the resource is the minor number of the DRBD device, in decimal. is the path to the primary configuration file; if you split your configuration into multiple files (e.g. in ), this will not be helpful. , , are the address family (e.g. ), the peer's address and hostnames. (note the singular form) is deprecated, and superseeded by DRBD_PEERS. Please note that not all of these might be set for all handlers, and that some values might not be useable for a definition. Parameters drbd.confminor-count count may be a number from 1 to 255. Use minor-count if you want to define massively more resources later without reloading the DRBD kernel module. Per default the module loads with 11 more resources than you have currently in your config but at least 32. drbd.confdialog-refresh time may be 0 or a positive number. The user dialog redraws the second count every time seconds (or does no redraws if time is 0). The default value is 1. drbd.conf disable-ip-verification Use disable-ip-verification if, for some obscure reasons, drbdadm can/might not use or to do a sanity check for the IP address. You can disable the IP verification with this option. drbd.conf usage-count Please participate in DRBD's online usage counter. The most convenient way to do so is to set this option to . Valid options are: , and . drbd.conf protocol On the TCP/IP link the specified protocol is used. Valid protocol specifiers are A, B, and C. Protocol A: write IO is reported as completed, if it has reached local disk and local TCP send buffer. Protocol B: write IO is reported as completed, if it has reached local disk and remote buffer cache. Protocol C: write IO is reported as completed, if it has reached both local and remote disk. drbd.confdevice The name of the block device node of the resource being described. You must use this device with your application (file system) and you must not use the low level block device which is specified with the parameter. One can ether omit the name or and the minor number. If you omit the name a default of /dev/drbdminor will be used. Udev will create additional symlinks in /dev/drbd/by-res and /dev/drbd/by-disk. drbd.confdisk DRBD uses this block device to actually store and retrieve the data. Never access such a device while DRBD is running on top of it. This also holds true for dumpe2fs8 and similar commands. drbd.confaddress A resource needs one IP address per device, which is used to wait for incoming connections from the partner device respectively to reach the partner device. AF must be one of , , or (for compatibility reasons is an alias for ). It may be omited for IPv4 addresses. The actual IPv6 address that follows the keyword must be placed inside brackets: ipv6 [fd01:2345:6789:abcd::1]:7800. Each DRBD resource needs a TCP port which is used to connect to the node's partner device. Two different DRBD resources may not use the same addr:port combination on the same node. drbd.confmeta-diskdrbd.confflexible-meta-disk Internal means that the last part of the backing device is used to store the meta-data. You must not use [index] with internal. Note: Regardless of whether you use the or the keyword, it will always be of the size needed for the remaining storage size. You can use a single block device to store meta-data of multiple DRBD devices. E.g. use meta-disk /dev/sde6[0]; and meta-disk /dev/sde6[1]; for two different resources. In this case the meta-disk would need to be at least 256 MB in size. With the keyword you specify a block device as meta-data storage. You usually use this with LVM, which allows you to have many variable sized block devices. The required size of the meta-disk block device is 36kB + Backing-Storage-size / 32k. Round this number to the next 4kb boundary up and you have the exact size. Rule of the thumb: 32kByte per 1GByte of storage, round up to the next MB. drbd.confon-io-errorhandler is taken, if the lower level device reports io-errors to the upper layers. handler may be , or : The node downgrades the disk status to inconsistent, marks the erroneous block as inconsistent in the bitmap and retries the IO on the remote node. : Call the handler script . : The node drops its low level device, and continues in diskless mode. drbd.conffencing By we understand preventive measures to avoid situations where both nodes are primary and disconnected (AKA split brain). Valid fencing policies are: This is the default policy. No fencing actions are taken. If a node becomes a disconnected primary, it tries to fence the peer's disk. This is done by calling the handler. The handler is supposed to reach the other node over alternative communication paths and call '' there. If a node becomes a disconnected primary, it freezes all its IO operations and calls its fence-peer handler. The fence-peer handler is supposed to reach the peer over alternative communication paths and call 'drbdadm outdate res' there. In case it cannot reach the peer it should stonith the peer. IO is resumed as soon as the situation is resolved. In case your handler fails, you can resume IO with the command. drbd.conf use-bmbv In case the backing storage's driver has a merge_bvec_fn() function, DRBD has to pretend that it can only process IO requests in units not larger than 4KiB. (At the time of writing the only known drivers which have such a function are: md (software raid driver), dm (device mapper - LVM) and DRBD itself). To get the best performance out of DRBD on top of software RAID (or any other driver with a merge_bvec_fn() function) you might enable this function, if you know for sure that the merge_bvec_fn() function will deliver the same results on all nodes of your cluster. I.e. the physical disks of the software RAID are of exactly the same type. Use this option only if you know what you are doing. drbd.conf no-disk-barrier drbd.conf no-disk-flushes drbd.conf no-disk-drain DRBD has four implementations to express write-after-write dependencies to its backing storage device. DRBD will use the first method that is supported by the backing storage device and that is not disabled by the user. When selecting the method you should not only base your decision on the measurable performance. In case your backing storage device has a volatile write cache (plain disks, RAID of plain disks) you should use one of the first two. In case your backing storage device has battery-backed write cache you may go with option 3. Option 4 (disable everything, use "none") is dangerous on most IO stacks, may result in write-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles. Do not use . Unfortunately device mapper (LVM) might not support barriers. The letter after "wo:" in /proc/drbd indicates with method is currently in use for a device: , , , . The implementations are: barrier The first requires that the driver of the backing storage device support barriers (called 'tagged command queuing' in SCSI and 'native command queuing' in SATA speak). The use of this method can be disabled by the option. Note: Since Linux-2.6.36 (or RHEL's 2.6.32) this method is disabled. flush The second requires that the backing device support disk flushes (called 'force unit access' in the drive vendors speak). The use of this method can be disabled using the option. drain The third method is simply to let write requests drain before write requests of a new reordering domain are issued. This was the only implementation before 8.0.9. none The fourth method is to not express write-after-write dependencies to the backing store at all, by also specifying . This is dangerous on most IO stacks, may result in write-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles. Do not use . drbd.conf no-md-flushes Disables the use of disk flushes and barrier BIOs when accessing the meta data device. See the notes on . drbd.conf max-bio-bvecs In some special circumstances the device mapper stack manages to pass BIOs to DRBD that violate the constraints that are set forth by DRBD's merge_bvec() function and which have more than one bvec. A known example is: phys-disk -> DRBD -> LVM -> Xen -> misaligned partition (63) -> DomU FS. Then you might see "bio would need to, but cannot, be split:" in the Dom0's kernel log. The best workaround is to proper align the partition within the VM (E.g. start it at sector 1024). This costs 480 KiB of storage. Unfortunately the default of most Linux partitioning tools is to start the first partition at an odd number (63). Therefore most distribution's install helpers for virtual linux machines will end up with misaligned partitions. The second best workaround is to limit DRBD's max bvecs per BIO (= ) to 1, but that might cost performance. The default value of is 0, which means that there is no user imposed limitation. drbd.conf disk-timeout If the driver of the lower_device does not finish an IO request within disk_timeout, DRBD considers the disk as failed. If DRBD is connected to a remote host, it will reissue local pending IO requests to the peer, and ship all new IO requests to the peer only. The disk state advances to diskless, as soon as the backing block device has finished all IO requests. The default value of is 0, which means that no timeout is enforced. The default unit is 100ms. This option is available since 8.3.12. drbd.confsndbuf-size size is the size of the TCP socket send buffer. The default value is 0, i.e. autotune. You can specify smaller or larger values. Larger values are appropriate for reasonable write throughput with protocol A over high latency networks. Values below 32K do not make sense. Since 8.0.13 resp. 8.2.7, setting the size value to 0 means that the kernel should autotune this. drbd.confrcvbuf-size size is the size of the TCP socket receive buffer. The default value is 0, i.e. autotune. You can specify smaller or larger values. Usually this should be left at its default. Setting the size value to 0 means that the kernel should autotune this. drbd.conftimeout If the partner node fails to send an expected response packet within time tenths of a second, the partner node is considered dead and therefore the TCP/IP connection is abandoned. This must be lower than connect-int and ping-int. The default value is 60 = 6 seconds, the unit 0.1 seconds. drbd.confconnect-int In case it is not possible to connect to the remote DRBD device immediately, DRBD keeps on trying to connect. With this option you can set the time between two retries. The default value is 10 seconds, the unit is 1 second. drbd.confping-int If the TCP/IP connection linking a DRBD device pair is idle for more than time seconds, DRBD will generate a keep-alive packet to check if its partner is still alive. The default is 10 seconds, the unit is 1 second. drbd.confping-timeout The time the peer has time to answer to a keep-alive packet. In case the peer's reply is not received within this time period, it is considered as dead. The default value is 500ms, the default unit are tenths of a second. drbd.confmax-buffers Limits the memory usage per DRBD minor device on the receiving side, or for internal buffers during resync or online-verify. Unit is PAGE_SIZE, which is 4 KiB on most systems. The minimum possible setting is hard coded to 32 (=128 KiB). These buffers are used to hold data blocks while they are written to/read from disk. To avoid possible distributed deadlocks on congestion, this setting is used as a throttle threshold rather than a hard limit. Once more than max-buffers pages are in use, further allocation from this pool is throttled. You want to increase max-buffers if you cannot saturate the IO backend on the receiving side. drbd.confko-count In case the secondary node fails to complete a single write request for count times the timeout, it is expelled from the cluster. (I.e. the primary node goes into mode.) To disable this feature, you should explicitly set it to 0; defaults may change between versions. drbd.confmax-epoch-size The highest number of data blocks between two write barriers. If you set this smaller than 10, you might decrease your performance. drbd.confallow-two-primaries With this option set you may assign the primary role to both nodes. You only should use this option if you use a shared storage file system on top of DRBD. At the time of writing the only ones are: OCFS2 and GFS. If you use this option with any other file system, you are going to crash your nodes and to corrupt your data! drbd.conf unplug-watermark This setting has no effect with recent kernels that use explicit on-stack plugging (upstream Linux kernel 2.6.39, distributions may have backported). When the number of pending write requests on the standby (secondary) node exceeds the , we trigger the request processing of our backing storage device. Some storage controllers deliver better performance with small values, others deliver best performance when the value is set to the same value as max-buffers, yet others don't feel much effect at all. Minimum 16, default 128, maximum 131072. drbd.confcram-hmac-alg You need to specify the HMAC algorithm to enable peer authentication at all. You are strongly encouraged to use peer authentication. The HMAC algorithm will be used for the challenge response authentication of the peer. You may specify any digest algorithm that is named in . drbd.confshared-secret The shared secret used in peer authentication. May be up to 64 characters. Note that peer authentication is disabled as long as no (see above) is specified. policy drbd.conf after-sb-0pri possible policies are: No automatic resynchronization, simply disconnect. Auto sync from the node that was primary before the split-brain situation happened. Auto sync from the node that became primary as second during the split-brain situation. In case one node did not write anything since the split brain became evident, sync from the node that wrote something to the node that did not write anything. In case none wrote anything this policy uses a random decision to perform a "resync" of 0 blocks. In case both have written something this policy disconnects the nodes. Auto sync from the node that touched more blocks during the split brain situation. Auto sync to the named node. policy drbd.conf after-sb-1pri possible policies are: No automatic resynchronization, simply disconnect. Discard the version of the secondary if the outcome of the algorithm would also destroy the current secondary's data. Otherwise disconnect. Always take the decision of the algorithm, even if that causes an erratic change of the primary's view of the data. This is only useful if you use a one-node FS (i.e. not OCFS2 or GFS) with the flag, AND if you really know what you are doing. This is DANGEROUS and MAY CRASH YOUR MACHINE if you have an FS mounted on the primary node. Discard the secondary's version. Always honor the outcome of the algorithm. In case it decides the current secondary has the right data, it calls the "pri-lost-after-sb" handler on the current primary. policy drbd.conf after-sb-2pri possible policies are: No automatic resynchronization, simply disconnect. Always take the decision of the algorithm, even if that causes an erratic change of the primary's view of the data. This is only useful if you use a one-node FS (i.e. not OCFS2 or GFS) with the flag, AND if you really know what you are doing. This is DANGEROUS and MAY CRASH YOUR MACHINE if you have an FS mounted on the primary node. Call the "pri-lost-after-sb" helper program on one of the machines. This program is expected to reboot the machine, i.e. make it secondary. Normally the automatic after-split-brain policies are only used if current states of the UUIDs do not indicate the presence of a third node. With this option you request that the automatic after-split-brain policies are used as long as the data sets of the nodes are somehow related. This might cause a full sync, if the UUIDs indicate the presence of a third node. (Or double faults led to strange UUID sets.) policy drbd.conf rr-conflict This option helps to solve the cases when the outcome of the resync decision is incompatible with the current role assignment in the cluster. No automatic resynchronization, simply disconnect. Sync to the primary node is allowed, violating the assumption that data on a block device are stable for one of the nodes. Dangerous, do not use. Call the "pri-lost" helper program on one of the machines. This program is expected to reboot the machine, i.e. make it secondary. alg drbd.conf data-integrity-alg DRBD can ensure the data integrity of the user's data on the network by comparing hash values. Normally this is ensured by the 16 bit checksums in the headers of TCP/IP packets. This option can be set to any of the kernel's data digest algorithms. In a typical kernel configuration you should have at least one of , , and available. By default this is not enabled. See also the notes on data integrity. drbd.conf no-tcp-cork DRBD usually uses the TCP socket option TCP_CORK to hint to the network stack when it can expect more data, and when it should flush out what it has in its send queue. It turned out that there is at least one network stack that performs worse when one uses this hinting method. Therefore we introducted this option, which disables the setting and clearing of the TCP_CORK socket option by DRBD. By default DRBD blocks when the available TCP send queue becomes full. That means it will slow down the application that generates the write requests that cause DRBD to send more data down that TCP connection. When DRBD is deployed with DRBD-proxy it might be more desirable that DRBD goes into AHEAD/BEHIND mode shortly before the send queue becomes full. In AHEAD/BEHIND mode DRBD does no longer replicate data, but still keeps the connection open. The advantage of the AHEAD/BEHIND mode is that the application is not slowed down, even if DRBD-proxy's buffer is not sufficient to buffer all write requests. The downside is that the peer node falls behind, and that a resync will be necessary to bring it back into sync. During that resync the peer node will have an inconsistent disk. Available congestion_policys are and . The default is . Fill_threshold might be in the range of 0 to 10GiBytes. The default is 0 which disables the check. Active_extents_threshold has the same limits as . The AHEAD/BEHIND mode and its settings are available since DRBD 8.3.10. Wait for connection timeout. drbd.confwfc-timeout The init script drbd8 blocks the boot process until the DRBD resources are connected. When the cluster manager starts later, it does not see a resource with internal split-brain. In case you want to limit the wait time, do it here. Default is 0, which means unlimited. The unit is seconds. drbd.confdegr-wfc-timeout Wait for connection timeout, if this node was a degraded cluster. In case a degraded cluster (= cluster with only one node left) is rebooted, this timeout value is used instead of wfc-timeout, because the peer is less likely to show up in time, if it had been dead before. Value 0 means unlimited. drbd.confoutdated-wfc-timeout Wait for connection timeout, if the peer was outdated. In case a degraded cluster (= cluster with only one node left) with an outdated peer disk is rebooted, this timeout value is used instead of wfc-timeout, because the peer is not allowed to become primary in the meantime. Value 0 means unlimited. By setting this option you can make the init script to continue to wait even if the device pair had a split brain situation and therefore refuses to connect. Sets on which node the device should be promoted to primary role by the init script. The node-name might either be a host name or the keyword . When this option is not set the devices stay in secondary role on both nodes. Usually one delegates the role assignment to a cluster manager (e.g. heartbeat). Usually and are ignored for stacked devices, instead twice the amount of is used for the connection timeouts. With the keyword you disable this, and force DRBD to mind the and statements. Only do that if the peer of the stacked resource is usually not available or will usually not become primary. By using this option incorrectly, you run the risk of causing unexpected split brain. drbd.confrate To ensure a smooth operation of the application on top of DRBD, it is possible to limit the bandwidth which may be used by background synchronizations. The default is 250 KB/sec, the default unit is KB/sec. Optional suffixes K, M, G are allowed. drbd.confuse-rle During resync-handshake, the dirty-bitmaps of the nodes are exchanged and merged (using bit-or), so the nodes will have the same understanding of which blocks are dirty. On large devices, the fine grained dirty-bitmap can become large as well, and the bitmap exchange can take quite some time on low-bandwidth links. Because the bitmap typically contains compact areas where all bits are unset (clean) or set (dirty), a simple run-length encoding scheme can considerably reduce the network traffic necessary for the bitmap exchange. For backward compatibilty reasons, and because on fast links this possibly does not improve transfer time but consumes cpu cycles, this defaults to off. drbd.confafter By default, resynchronization of all devices would run in parallel. By defining a sync-after dependency, the resynchronization of this resource will start only if the resource res-name is already in connected state (i.e., has finished its resynchronization). drbd.confal-extents DRBD automatically performs hot area detection. With this parameter you control how big the hot area (= active set) can get. Each extent marks 4M of the backing storage (= low-level device). In case a primary node leaves the cluster unexpectedly, the areas covered by the active set must be resynced upon rejoining of the failed node. The data structure is stored in the meta-data area, therefore each change of the active set is a write operation to the meta-data device. A higher number of extents gives longer resync times but less updates to the meta-data. The default number of extents is 127. (Minimum: 7, Maximum: 3843) During online verification (as initiated by the verify sub-command), rather than doing a bit-wise comparison, DRBD applies a hash function to the contents of every block being verified, and compares that hash with the peer. This option defines the hash algorithm being used for that purpose. It can be set to any of the kernel's data digest algorithms. In a typical kernel configuration you should have at least one of , , and available. By default this is not enabled; you must set this option explicitly in order to be able to use on-line device verification. See also the notes on data integrity. A resync process sends all marked data blocks from the source to the destination node, as long as no is given. When one is specified the resync process exchanges hash values of all marked blocks first, and sends only those data blocks that have different hash values. This setting is useful for DRBD setups with low bandwidth links. During the restart of a crashed primary node, all blocks covered by the activity log are marked for resync. But a large part of those will actually be still in sync, therefore using will lower the required bandwidth in exchange for CPU cycles. The dynamic resync speed controller gets enabled with setting plan_time to a positive value. It aims to fill the buffers along the data path with either a constant amount of data fill_target, or aims to have a constant delay time of delay_target along the path. The controller has an upper bound of max_rate. By plan_time the agility of the controller is configured. Higher values yield for slower/lower responses of the controller to deviation from the target value. It should be at least 5 times RTT. For regular data paths a fill_target in the area of 4k to 100k is appropriate. For a setup that contains drbd-proxy it is advisable to use delay_target instead. Only when fill_target is set to 0 the controller will use delay_target. 5 times RTT is a reasonable starting value. Max_rate should be set to the bandwidth available between the DRBD-hosts and the machines hosting DRBD-proxy, or to the available disk-bandwidth. The default value of plan_time is 0, the default unit is 0.1 seconds. Fill_target has 0 and sectors as default unit. Delay_target has 1 (100ms) and 0.1 as default unit. Max_rate has 10240 (100MiB/s) and KiB/s as default unit. The dynamic resync speed controller and its settings are available since DRBD 8.3.9. A node that is primary and sync-source has to schedule application IO requests and resync IO requests. The min_rate tells DRBD use only up to min_rate for resync IO and to dedicate all other available IO bandwidth to application requests. Note: The value 0 has a special meaning. It disables the limitation of resync IO completely, which might slow down application IO considerably. Set it to a value of 1, if you prefer that resync IO never slows down application IO. Note: Although the name might suggest that it is a lower bound for the dynamic resync speed controller, it is not. If the DRBD-proxy buffer is full, the dynamic resync speed controller is free to lower the resync speed down to 0, completely independent of the setting. Min_rate has 4096 (4MiB/s) and KiB/s as default unit. This setting controls what happens to IO requests on a degraded, disk less node (I.e. no data store is reachable). The available policies are and . If ond-policy is set to you can either resume IO by attaching/connecting the last lost data storage, or by the drbdadm resume-io res command. The latter will result in IO errors of course. The default is . This setting is available since DRBD 8.3.9. drbd.confcpu-mask Sets the cpu-affinity-mask for DRBD's kernel threads of this device. The default value of cpu-mask is 0, which means that DRBD's kernel threads should be spread over all CPUs of the machine. This value must be given in hexadecimal notation. If it is too big it will be truncated. drbd.confpri-on-incon-degr This handler is called if the node is primary, degraded and if the local copy of the data is inconsistent. drbd.confpri-lost-after-sb The node is currently primary, but lost the after-split-brain auto recovery procedure. As as consequence, it should be abandoned. drbd.confpri-lost The node is currently primary, but DRBD's algorithm thinks that it should become sync target. As a consequence it should give up its primary role. drbd.conffence-peer The handler is part of the mechanism. This handler is called in case the node needs to fence the peer's disk. It should use other communication paths than DRBD's network link. drbd.conflocal-io-error DRBD got an IO error from the local IO subsystem. drbd.confinitial-split-brain DRBD has connected and detected a split brain situation. This handler can alert someone in all cases of split brain, not just those that go unresolved. drbd.confsplit-brain DRBD detected a split brain situation but remains unresolved. Manual recovery is necessary. This handler should alert someone on duty. drbd.confbefore-resync-target DRBD calls this handler just before a resync begins on the node that becomes resync target. It might be used to take a snapshot of the backing block device. drbd.confafter-resync-target DRBD calls this handler just after a resync operation finished on the node whose disk just became consistent after being inconsistent for the duration of the resync. It might be used to remove a snapshot of the backing device that was created by the handler. Other Keywords drbd.confinclude Include all files matching the wildcard pattern file-pattern. The statement is only allowed on the top level, i.e. it is not allowed inside any section. Notes on data integrity There are two independent methods in DRBD to ensure the integrity of the mirrored data. The online-verify mechanism and the of the section. Both mechanisms might deliver false positives if the user of DRBD modifies the data which gets written to disk while the transfer goes on. This may happen for swap, or for certain append while global sync, or truncate/rewrite workloads, and not necessarily poses a problem for the integrity of the data. Usually when the initiator of the data transfer does this, it already knows that that data block will not be part of an on disk data structure, or will be resubmitted with correct data soon enough. The causes the receiving side to log an error about "Digest integrity check FAILED: Ns +x\n", where N is the sector offset, and x is the size of the requst in bytes. It will then disconnect, and reconnect, thus causing a quick resync. If the sending side at the same time detected a modification, it warns about "Digest mismatch, buffer modified by upper layers during write: Ns +x\n", which shows that this was a false positive. The sending side may detect these buffer modifications immediately after the unmodified data has been copied to the tcp buffers, in which case the receiving side won't notice it. The most recent (2007) example of systematic corruption was an issue with the TCP offloading engine and the driver of a certain type of GBit NIC. The actual corruption happened on the DMA transfer from core memory to the card. Since the TCP checksum gets calculated on the card, this type of corruption stays undetected as long as you do not use either the online or the . We suggest to use the only during a pre-production phase due to its CPU costs. Further we suggest to do online runs regularly e.g. once a month during a low load period. Version This document was revised for version 8.3.2 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd8, drbddisk8, drbdsetup8, drbdadm8, DRBD User's Guide, DRBD web site drbd-utils-8.9.10/documentation/v83/drbd.80000644000175000017500000000545713027211676020073 0ustar apoikosapoikos'\" t .\" Title: drbd .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 15 Oct 2008 .\" Manual: System Administration .\" Source: DRBD 8.3.2 .\" Language: English .\" .TH "DRBD" "8" "15 Oct 2008" "DRBD 8.3.2" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbd \- The start and stop script for DRBD .SH "SYNOPSIS" .HP \w'\fB/etc/init\&.d/drbd\fR\ 'u \fB/etc/init\&.d/drbd\fR [\fIresource\fR] {{start}\ |\ {stop}\ |\ {status}\ |\ {reload}\ |\ {restart}\ |\ {force\-reload}} .SH "INTRODUCTION" .PP The \fB/etc/init\&.d/drbd\fR script is used to start and stop drbd on a system V style init system\&. .PP In order to use \fB/etc/init\&.d/drbd\fR you must define a resource, a host, and any other configuration options in the drbd configuration file\&. See \fB/etc/drbd\&.conf\fR for details\&. If \fIresource\fR is omitted, then all of the resources listed in the config file are configured\&. .PP This script might ask you \(lqDo you want to abort waiting for other server and make this one primary?\(rq .PP Only answer this question with \(lqyes\(rq if you are sure that it is impossible to repair the other node\&. .SH "VERSION" .sp This document was revised for version 8\&.3\&.2 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbddisk\fR(8), \fBdrbdsetup\fR(8)\fBdrbdadm\fR(8)\m[blue]\fBDRBD Homepage\fR\m[]\&\s-2\u[1]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD Homepage .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v83/drbdadm.xml0000644000175000017500000005132612466702073021204 0ustar apoikosapoikos 5 Dec 2008 DRBD 8.3.2 drbdadm 8 System Administration drbdadm Administration tool for DRBD drbdadm drbdadm -d -cfile -tfile -scmd -mcmd -S -hhost --backend-options command all resource Description is the high level tool of the DRBD program suite. is to and what / is to . reads its configuration file and performs the specified commands by calling the and/or the program. Options , Just prints the calls of to stdout, but does not run the commands. , file Specifies the configuration file drbdadm will use. If this parameter is not specified, drbdadm will look for , and . , file Specifies an additional configuration file drbdadm to check. This option is only allowed with the dump and the sh-nop commands. , file Specifies the full path to the program. If this option is omitted, drbdadm will look for and . , file Specifies the full path to the program. If this option is omitted, drbdadm will look for and . , Specifies that this command should be performed on a stacked resource. , Specifies to which peer node to connect. Only necessary if there are more than two host sections in the resource you are working on. backend-options All options following the doubly hyphen are considered backend-options. These are passed through to the backend command. I.e. to , or . Commands attach Attaches a local backing block device to the DRBD resource's device. detach drbdadmdetach Removes the backing storage device from a DRBD resource's device. connect drbdadmconnect Sets up the network configuration of the resource's device. If the peer device is already configured, the two DRBD devices will connect. If there are more than two host sections in the resource you need to use the option to select the peer you want to connect to. disconnect drbdadmdisconnect Removes the network configuration from the resource. The device will then go into StandAlone state. syncer drbdadmsyncer Loads the resynchronization parameters into the device. up drbdadmup Is a shortcut for attach and connect. down drbdadmdown Is a shortcut for disconnect and detach. primary drbdadmprimary Promote the resource's device into primary role. You need to do this before any access to the device, such as creating or mounting a file system. secondary drbdadmsecondary Brings the device back into secondary role. This is needed since in a connected DRBD device pair, only one of the two peers may have primary role (except if is explicitly set in the configuration file). invalidate drbdadminvalidate Forces DRBD to consider the data on the local backing storage device as out-of-sync. Therefore DRBD will copy each and every block from its peer, to bring the local storage device back in sync. To avoid races, you need an established replication link, or be disconnected Secondary. invalidate-remote drbdadminvalidate-remote This command is similar to the invalidate command, however, the peer's backing storage is invalidated and hence rewritten with the data of the local node. To avoid races, you need an established replication link, or be disconnected Primary. resize drbdadmresize Causes DRBD to re-examine all sizing constraints, and resize the resource's device accordingly. For example, if you increased the size of your backing storage devices (on both nodes, of course), then DRBD will adopt the new size after you called this command on one of your nodes. Since new storage space must be synchronised this command only works if there is at least one primary node present. The allows you to resize a device which is currently not connected to the peer. Use with care, since if you do not resize the peer's disk as well, further connect attempts of the two will fail. The allows you to resize an existing device and avoid syncing the new space. This is useful when adding addtional blank storage to your device. Example: # drbdadm -- --assume-clean resize r0 check-resize drbdadmcheck-resize Calls drbdmeta to eventually move internal meta data. If the backing device was resized, while DRBD was not running, meta data has to be moved to the end of the device, so that the next command can succeed. create-md drbdadmcreate-md Initializes the meta data storage. This needs to be done before a DRBD resource can be taken online for the first time. In case of issues with that command have a look at drbdmeta8 get-gi drbdadmget-gi Shows a short textual representation of the data generation identifiers. show-gi drbdadmshow-gi Prints a textual representation of the data generation identifiers including explanatory information. dump-md drbdadmdump-md Dumps the whole contents of the meta data storage, including the stored bit-map and activity-log, in a textual representation. outdate drbdadmoutdate Sets the outdated flag in the meta data. adjust drbdadmadjust Synchronizes the configuration of the device with your configuration file. You should always examine the output of the dry-run mode before actually executing this command. wait-connect drbdadmwait-connect Waits until the device is connected to its peer device. role drbdadmrole Shows the current roles of the devices (local/peer). E.g. Primary/Secondary state drbdadmstate Deprecated alias for "role", see above. cstate drbdadmcstate Shows the current connection state of the devices. status drbdadmstatus Shows the current status of all devices defined in the current config file, in XML-like format. Example output: <drbd-status version="8.3.2" api="88"> <resources config_file="/etc/drbd.conf"> <resource minor="0" name="s0" cs="SyncTarget" st1="Secondary" st2="Secondary" ds1="Inconsistent" ds2="UpToDate" resynced_precent="5.9" /> <resource minor="1" name="s1" cs="WFConnection" st1="Secondary" st2="Unknown" ds1="Inconsistent" ds2="Outdated" /> <resource minor="3" name="dummy" cs="Unconfigured" /> <!-- resource minor="4" name="scratch" not available or not yet created --> </resources> </drbd-status> dump drbdadmdump Just parse the configuration file and dump it to stdout. May be used to check the configuration file for syntactic correctness. outdate drbdadmoutdate Used to mark the node's data as outdated. Usually used by the peer's fence-peer handler. verify drbdadmverify Starts online verify. During online verify, data on both nodes is compared for equality. See /proc/drbd for online verify progress. If out-of-sync blocks are found, they are not resynchronized automatically. To do that, disconnect and connect the resource when verification has completed. See also the notes on data integrity on the drbd.conf manpage. pause-sync drbdadmpause-sync Temporarily suspend an ongoing resynchronization by setting the local pause flag. Resync only progresses if neither the local nor the remote pause flag is set. It might be desirable to postpone DRBD's resynchronization until after any resynchronization of the backing storage's RAID setup. resume-sync drbdadmresume-sync Unset the local sync pause flag. new-current-uuid drbdadmnew-current-uuid Generates a new currend UUID and rotates all other UUID values. This can be used to shorten the initial resync of a cluster. See the manpage for a more details. dstate drbdadmdstate Show the current state of the backing storage devices. (local/peer) hidden-commands Shows all commands undocumented on purpose. Version This document was revised for version 8.3.2 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf5, drbd8, drbddisk8, drbdsetup8, drbdmeta8 and the DRBD project web site drbd-utils-8.9.10/documentation/v83/drbd.xml0000644000175000017500000000670212466702073020520 0ustar apoikosapoikos drbd The start and stop script for DRBD DRBD 8.3.2 15 Oct 2008 drbd 8 System Administration /etc/init.d/drbd resource start stop status reload restart force-reload Introduction The script is used to start and stop drbd on a system V style init system. In order to use you must define a resource, a host, and any other configuration options in the drbd configuration file. See for details. If resource is omitted, then all of the resources listed in the config file are configured. This script might ask you Do you want to abort waiting for other server and make this one primary? Only answer this question with yes if you are sure that it is impossible to repair the other node. Version This document was revised for version 8.3.2 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf5, drbddisk8, drbdsetup8drbdadm8DRBD Homepage drbd-utils-8.9.10/documentation/v83/drbdsetup.80000644000175000017500000013166413027211674021152 0ustar apoikosapoikos'\" t .\" Title: drbdsetup .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 5 Dec 2008 .\" Manual: System Administration .\" Source: DRBD 8.3.2 .\" Language: English .\" .TH "DRBDSETUP" "8" "5 Dec 2008" "DRBD 8.3.2" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbdsetup \- Setup tool for DRBD .SH "SYNOPSIS" .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} disk {\fIlower_dev\fR} {\fImeta_data_dev\fR} {\fImeta_data_index\fR} [\-d\ {\fIsize\fR}] [\-e\ {\fIerr_handler\fR}] [\-f\ {\fIfencing_policy\fR}] [\-b] [\-t\ {\fIdisk_timeout\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} net [\fIaf:\fR] {\fIlocal_addr\fR} [\fI:port\fR] [\fIaf:\fR] {\fIremote_addr\fR} [\fI:port\fR] {\fIprotocol\fR} [\-c\ {\fItime\fR}] [\-i\ {\fItime\fR}] [\-t\ {\fIval\fR}] [\-S\ {\fIsize\fR}] [\-r\ {\fIsize\fR}] [\-k\ {\fIcount\fR}] [\-e\ {\fImax_epoch_size\fR}] [\-b\ {\fImax_buffers\fR}] [\-m] [\-a\ {\fIhash_alg\fR}] [\-x\ {\fIshared_secret\fR}] [\-A\ {\fIasb\-0p\-policy\fR}] [\-B\ {\fIasb\-1p\-policy\fR}] [\-C\ {\fIasb\-2p\-policy\fR}] [\-D] [\-R\ {\fIrole\-resync\-conflict\-policy\fR}] [\-p\ {\fIping_timeout\fR}] [\-u\ {\fIval\fR}] [\-d\ {\fIhash_alg\fR}] [\-o] [\-n] [\-g\ {\fIcongestion_policy\fR}] [\-f\ {\fIval\fR}] [\-h\ {\fIval\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} syncer [\-a\ {\fIdev_minor\fR}] [\-r\ {\fIrate\fR}] [\-e\ {\fIextents\fR}] [\-v\ {\fIverify\-hash\-alg\fR}] [\-c\ {\fIcpu\-mask\fR}] [\-C\ {\fIcsums\-hash\-alg\fR}] [\-R] [\-p\ {\fIplan_time\fR}] [\-s\ {\fIfill_target\fR}] [\-d\ {\fIdelay_target\fR}] [\-m\ {\fImax_rate\fR}] [\-n\ {\fIond\-policy\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} disconnect .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} detach [\-f] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} down .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} primary [\-f] [\-o] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} secondary .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} verify [\-s\ {\fIstart\-position\fR}] [\-S\ {\fIstop\-position\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} invalidate .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} invalidate\-remote .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} wait\-connect [\-t\ {\fIwfc_timeout\fR}] [\-d\ {\fIdegr_wfc_timeout\fR}] [\-o\ {\fIoutdated_wfc_timeout\fR}] [\-w] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} wait\-sync [\-t\ {\fIwfc_timeout\fR}] [\-d\ {\fIdegr_wfc_timeout\fR}] [\-o\ {\fIoutdated_wfc_timeout\fR}] [\-w] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} role .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} cstate .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} dstate .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} status .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} resize [\-d\ {\fIsize\fR}] [\-f\ {\fIassume\-peer\-has\-space\fR}] [\-c\ {\fIassume\-clean\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} check\-resize .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} pause\-sync .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} resume\-sync .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} outdate .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} show\-gi .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} get\-gi .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} show .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} suspend\-io .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} resume\-io .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} events [\-u] [\-a] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR {\fIdevice\fR} new\-current\-uuid [\-c] .SH "DESCRIPTION" .PP drbdsetup is used to associate DRBD devices with their backing block devices, to set up DRBD device pairs to mirror their backing block devices, and to inspect the configuration of running DRBD devices\&. .SH "NOTE" .PP drbdsetup is a low level tool of the DRBD program suite\&. It is used by the data disk and drbd scripts to communicate with the device driver\&. .SH "COMMANDS" .PP Each drbdsetup sub\-command might require arguments and bring its own set of options\&. All values have default units which might be overruled by K, M or G\&. These units are defined in the usual way (e\&.g\&. K = 2^10 = 1024)\&. .SS "Common options" .PP All drbdsetup sub\-commands accept these two options .PP \fB\-\-create\-device\fR .RS 4 In case the specified DRBD device (minor number) does not exist yet, create it implicitly\&. .RE .PP \fB\-\-set\-defaults\fR .RS 4 When \fB\-\-set\-defaults\fR is given on the command line, all options of the invoked sub\-command that are not explicitly set are reset to their default values\&. .RE .SS "disk" .PP Associates \fIdevice\fR with \fIlower_device\fR to store its data blocks on\&. The \fB\-d\fR (or \fB\-\-disk\-size\fR) should only be used if you wish not to use as much as possible from the backing block devices\&. If you do not use \fB\-d\fR, the \fIdevice\fR is only ready for use as soon as it was connected to its peer once\&. (See the \fBnet\fR command\&.) .PP \fB\-d\fR, \fB\-\-disk\-size \fR\fB\fIsize\fR\fR .RS 4 You can override DRBD\*(Aqs size determination method with this option\&. If you need to use the device before it was ever connected to its peer, use this option to pass the \fIsize\fR of the DRBD device to the driver\&. Default unit is sectors (1s = 512 bytes)\&. .sp If you use the \fIsize\fR parameter in drbd\&.conf, we strongly recommend to add an explicit unit postfix\&. drbdadm and drbdsetup used to have mismatching default units\&. .RE .PP \fB\-e\fR, \fB\-\-on\-io\-error \fR\fB\fIerr_handler\fR\fR .RS 4 If the driver of the \fIlower_device\fR reports an error to DRBD, DRBD will mark the disk as inconsistent, call a helper program, or detach the device from its backing storage and perform all further IO by requesting it from the peer\&. The valid \fIerr_handlers\fR are: \fBpass_on\fR, \fBcall\-local\-io\-error\fR and \fBdetach\fR\&. .RE .PP \fB\-f\fR, \fB\-\-fencing \fR\fB\fIfencing_policy\fR\fR .RS 4 Under \fBfencing\fR we understand preventive measures to avoid situations where both nodes are primary and disconnected (AKA split brain)\&. .sp Valid fencing policies are: .PP \fBdont\-care\fR .RS 4 This is the default policy\&. No fencing actions are done\&. .RE .PP \fBresource\-only\fR .RS 4 If a node becomes a disconnected primary, it tries to outdate the peer\*(Aqs disk\&. This is done by calling the fence\-peer handler\&. The handler is supposed to reach the other node over alternative communication paths and call \*(Aqdrbdadm outdate res\*(Aq there\&. .RE .PP \fBresource\-and\-stonith\fR .RS 4 If a node becomes a disconnected primary, it freezes all its IO operations and calls its fence\-peer handler\&. The fence\-peer handler is supposed to reach the peer over alternative communication paths and call \*(Aqdrbdadm outdate res\*(Aq there\&. In case it cannot reach the peer, it should stonith the peer\&. IO is resumed as soon as the situation is resolved\&. In case your handler fails, you can resume IO with the \fBresume\-io\fR command\&. .RE .RE .PP \fB\-b\fR, \fB\-\-use\-bmbv\fR .RS 4 In case the backing storage\*(Aqs driver has a merge_bvec_fn() function, DRBD has to pretend that it can only process IO requests in units not larger than 4 KiB\&. (At time of writing the only known drivers which have such a function are: md (software raid driver), dm (device mapper \- LVM) and DRBD itself) .sp To get best performance out of DRBD on top of software raid (or any other driver with a merge_bvec_fn() function) you might enable this option, if you know for sure that the merge_bvec_fn() function will deliver the same results on all nodes of your cluster\&. I\&.e\&. the physical disks of the software raid are exactly of the same type\&. USE THIS OPTION ONLY IF YOU KNOW WHAT YOU ARE DOING\&. .RE .PP \fB\-a\fR, \fB\-\-no\-disk\-barrier\fR, \fB\-i\fR, \fB\-\-no\-disk\-flushes\fR, \fB\-D\fR, \fB\-\-no\-disk\-drain\fR .RS 4 DRBD has four implementations to express write\-after\-write dependencies to its backing storage device\&. DRBD will use the first method that is supported by the backing storage device and that is not disabled by the user\&. .sp When selecting the method you should not only base your decision on the measurable performance\&. In case your backing storage device has a volatile write cache (plain disks, RAID of plain disks) you should use one of the first two\&. In case your backing storage device has battery\-backed write cache you may go with option 3\&. Option 4 (disable everything, use "none") \fIis dangerous\fR on most IO stacks, may result in write\-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles\&. \fIDo not use\fR \fBno\-disk\-drain\fR\&. .sp Unfortunately device mapper (LVM) might not support barriers\&. .sp The letter after "wo:" in /proc/drbd indicates with method is currently in use for a device: b, f, d, n\&. The implementations: .PP barrier .RS 4 The first requires that the driver of the backing storage device support barriers (called \*(Aqtagged command queuing\*(Aq in SCSI and \*(Aqnative command queuing\*(Aq in SATA speak)\&. The use of this method can be disabled by the \fB\-\-no\-disk\-barrier\fR option\&. Note: Since Linux\-2\&.6\&.36 (or RHEL\*(Aqs 2\&.6\&.32) this method is disabled\&. .RE .PP flush .RS 4 The second requires that the backing device support disk flushes (called \*(Aqforce unit access\*(Aq in the drive vendors speak)\&. The use of this method can be disabled using the \fB\-\-no\-disk\-flushes\fR option\&. .RE .PP drain .RS 4 The third method is simply to let write requests drain before write requests of a new reordering domain are issued\&. That was the only implementation before 8\&.0\&.9\&. .RE .PP none .RS 4 The fourth method is to not express write\-after\-write dependencies to the backing store at all, by also specifying \fB\-\-no\-disk\-drain\fR\&. This \fIis dangerous\fR on most IO stacks, may result in write\-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles\&. \fIDo not use\fR \fB\-\-no\-disk\-drain\fR\&. .RE .RE .PP \fB\-m\fR, \fB\-\-no\-md\-flushes\fR .RS 4 Disables the use of disk flushes and barrier BIOs when accessing the meta data device\&. See the notes on \fB\-\-no\-disk\-flushes\fR\&. .RE .PP \fB\-s\fR, \fB\-\-max\-bio\-bvecs\fR .RS 4 In some special circumstances the device mapper stack manages to pass BIOs to DRBD that violate the constraints that are set forth by DRBD\*(Aqs merge_bvec() function and which have more than one bvec\&. A known example is: phys\-disk \-> DRBD \-> LVM \-> Xen \-> missaligned partition (63) \-> DomU FS\&. Then you might see "bio would need to, but cannot, be split:" in the Dom0\*(Aqs kernel log\&. .sp The best workaround is to proper align the partition within the VM (E\&.g\&. start it at sector 1024)\&. That costs 480 KiB of storage\&. Unfortunately the default of most Linux partitioning tools is to start the first partition at an odd number (63)\&. Therefore most distributions install helpers for virtual linux machines will end up with missaligned partitions\&. The second best workaround is to limit DRBD\*(Aqs max bvecs per BIO (i\&.e\&., the \fBmax\-bio\-bvecs\fR option) to 1, but that might cost performance\&. .sp The default value of \fBmax\-bio\-bvecs\fR is 0, which means that there is no user imposed limitation\&. .RE .PP \fB\-t\fR, \fB\-\-disk\-timeout \fR\fB\fIdisk_timeout\fR\fR .RS 4 If the driver of the \fIlower_device\fR does not finish an IO request within \fIdisk_timeout\fR, DRBD considers the disk as failed\&. If DRBD is connected to a remote host, it will reissue local pending IO requests to the peer, and ship all new IO requests to the peer only\&. The disk state advances to diskless, as soon as the backing block device has finished all IO requests\&. .sp The default value of is 0, which means that no timeout is enforced\&. The default unit is 100ms\&. This option is available since 8\&.3\&.12\&. .RE .SS "net" .PP Sets up the \fIdevice\fR to listen on \fIaf:local_addr:port\fR for incoming connections and to try to connect to \fIaf:remote_addr:port\fR\&. If \fIport\fR is omitted, 7788 is used as default\&. If \fIaf\fR is omitted \fBipv4\fR gets used\&. Other supported address families are \fBipv6\fR, \fBssocks\fR for Dolphin Interconnect Solutions\*(Aq "super sockets" and \fBsdp\fR for Sockets Direct Protocol (Infiniband)\&. .PP On the TCP/IP link the specified \fIprotocol\fR is used\&. Valid protocol specifiers are A, B, and C\&. .PP Protocol A: write IO is reported as completed, if it has reached local disk and local TCP send buffer\&. .PP Protocol B: write IO is reported as completed, if it has reached local disk and remote buffer cache\&. .PP Protocol C: write IO is reported as completed, if it has reached both local and remote disk\&. .PP \fB\-c\fR, \fB\-\-connect\-int \fR\fB\fItime\fR\fR .RS 4 In case it is not possible to connect to the remote DRBD device immediately, DRBD keeps on trying to connect\&. With this option you can set the time between two retries\&. The default value is 10 seconds, the unit is 1 second\&. .RE .PP \fB\-i\fR, \fB\-\-ping\-int \fR\fB\fItime\fR\fR .RS 4 If the TCP/IP connection linking a DRBD device pair is idle for more than \fItime\fR seconds, DRBD will generate a keep\-alive packet to check if its partner is still alive\&. The default value is 10 seconds, the unit is 1 second\&. .RE .PP \fB\-t\fR, \fB\-\-timeout \fR\fB\fIval\fR\fR .RS 4 If the partner node fails to send an expected response packet within \fIval\fR tenths of a second, the partner node is considered dead and therefore the TCP/IP connection is abandoned\&. The default value is 60 (= 6 seconds)\&. .RE .PP \fB\-S\fR, \fB\-\-sndbuf\-size \fR\fB\fIsize\fR\fR .RS 4 The socket send buffer is used to store packets sent to the secondary node, which are not yet acknowledged (from a network point of view) by the secondary node\&. When using protocol A, it might be necessary to increase the size of this data structure in order to increase asynchronicity between primary and secondary nodes\&. But keep in mind that more asynchronicity is synonymous with more data loss in the case of a primary node failure\&. Since 8\&.0\&.13 resp\&. 8\&.2\&.7 setting the \fIsize\fR value to 0 means that the kernel should autotune this\&. The default \fIsize\fR is 0, i\&.e\&. autotune\&. .RE .PP \fB\-r\fR, \fB\-\-rcvbuf\-size \fR\fB\fIsize\fR\fR .RS 4 Packets received from the network are stored in the socket receive buffer first\&. From there they are consumed by DRBD\&. Before 8\&.3\&.2 the receive buffer\*(Aqs size was always set to the size of the socket send buffer\&. Since 8\&.3\&.2 they can be tuned independently\&. A value of 0 means that the kernel should autotune this\&. The default \fIsize\fR is 0, i\&.e\&. autotune\&. .RE .PP \fB\-k\fR, \fB\-\-ko\-count \fR\fB\fIcount\fR\fR .RS 4 In case the secondary node fails to complete a single write request for \fIcount\fR times the \fItimeout\fR, it is expelled from the cluster, i\&.e\&. the primary node goes into StandAlone mode\&. To disable this feature, you should explicitly set it to 0; defaults may change between versions\&. .RE .PP \fB\-e\fR, \fB\-\-max\-epoch\-size \fR\fB\fIval\fR\fR .RS 4 With this option the maximal number of write requests between two barriers is limited\&. Typically set to the same as \fB\-\-max\-buffers\fR, or the allowed maximum\&. Values smaller than 10 can lead to degraded performance\&. The default value is 2048\&. .RE .PP \fB\-b\fR, \fB\-\-max\-buffers \fR\fB\fIval\fR\fR .RS 4 With this option the maximal number of buffer pages allocated by DRBD\*(Aqs receiver thread is limited\&. Typically set to the same as \fB\-\-max\-epoch\-size\fR\&. Small values could lead to degraded performance\&. The default value is 2048, the minimum 32\&. Increase this if you cannot saturate the IO backend of the receiving side during linear write or during resync while otherwise idle\&. .sp See also \fBdrbd.conf\fR(5) .RE .PP \fB\-u\fR, \fB\-\-unplug\-watermark \fR\fB\fIval\fR\fR .RS 4 This setting has no effect with recent kernels that use explicit on\-stack plugging (upstream Linux kernel 2\&.6\&.39, distributions may have backported)\&. .sp When the number of pending write requests on the standby (secondary) node exceeds the unplug\-watermark, we trigger the request processing of our backing storage device\&. Some storage controllers deliver better performance with small values, others deliver best performance when the value is set to the same value as max\-buffers, yet others don\*(Aqt feel much effect at all\&. Minimum 16, default 128, maximum 131072\&. .RE .PP \fB\-m\fR, \fB\-\-allow\-two\-primaries \fR .RS 4 With this option set you may assign primary role to both nodes\&. You only should use this option if you use a shared storage file system on top of DRBD\&. At the time of writing the only ones are: OCFS2 and GFS\&. If you use this option with any other file system, you are going to crash your nodes and to corrupt your data! .RE .PP \fB\-a\fR, \fB\-\-cram\-hmac\-alg \fR\fIalg\fR .RS 4 You need to specify the HMAC algorithm to enable peer authentication at all\&. You are strongly encouraged to use peer authentication\&. The HMAC algorithm will be used for the challenge response authentication of the peer\&. You may specify any digest algorithm that is named in /proc/crypto\&. .RE .PP \fB\-x\fR, \fB\-\-shared\-secret \fR\fIsecret\fR .RS 4 The shared secret used in peer authentication\&. May be up to 64 characters\&. .RE .PP \fB\-A\fR, \fB\-\-after\-sb\-0pri \fR\fIasb\-0p\-policy\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBdiscard\-younger\-primary\fR .RS 4 Auto sync from the node that was primary before the split\-brain situation occurred\&. .RE .PP \fBdiscard\-older\-primary\fR .RS 4 Auto sync from the node that became primary as second during the split\-brain situation\&. .RE .PP \fBdiscard\-zero\-changes\fR .RS 4 In case one node did not write anything since the split brain became evident, sync from the node that wrote something to the node that did not write anything\&. In case none wrote anything this policy uses a random decision to perform a "resync" of 0 blocks\&. In case both have written something this policy disconnects the nodes\&. .RE .PP \fBdiscard\-least\-changes\fR .RS 4 Auto sync from the node that touched more blocks during the split brain situation\&. .RE .PP \fBdiscard\-node\-NODENAME\fR .RS 4 Auto sync to the named node\&. .RE .RE .PP \fB\-B\fR, \fB\-\-after\-sb\-1pri \fR\fIasb\-1p\-policy\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBconsensus\fR .RS 4 Discard the version of the secondary if the outcome of the \fBafter\-sb\-0pri\fR algorithm would also destroy the current secondary\*(Aqs data\&. Otherwise disconnect\&. .RE .PP \fBdiscard\-secondary\fR .RS 4 Discard the secondary\*(Aqs version\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Always honor the outcome of the \fBafter\-sb\-0pri \fR algorithm\&. In case it decides the current secondary has the correct data, call the \fBpri\-lost\-after\-sb\fR on the current primary\&. .RE .PP \fBviolently\-as0p\fR .RS 4 Always honor the outcome of the \fBafter\-sb\-0pri \fR algorithm\&. In case it decides the current secondary has the correct data, accept a possible instantaneous change of the primary\*(Aqs data\&. .RE .RE .PP \fB\-C\fR, \fB\-\-after\-sb\-2pri \fR\fIasb\-2p\-policy\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Always honor the outcome of the \fBafter\-sb\-0pri \fR algorithm\&. In case it decides the current secondary has the right data, call the \fBpri\-lost\-after\-sb\fR on the current primary\&. .RE .PP \fBviolently\-as0p\fR .RS 4 Always honor the outcome of the \fBafter\-sb\-0pri \fR algorithm\&. In case it decides the current secondary has the right data, accept a possible instantaneous change of the primary\*(Aqs data\&. .RE .RE .PP \fB\-P\fR, \fB\-\-always\-asbp\fR .RS 4 Normally the automatic after\-split\-brain policies are only used if current states of the UUIDs do not indicate the presence of a third node\&. .sp With this option you request that the automatic after\-split\-brain policies are used as long as the data sets of the nodes are somehow related\&. This might cause a full sync, if the UUIDs indicate the presence of a third node\&. (Or double faults have led to strange UUID sets\&.) .RE .PP \fB\-R\fR, \fB\-\-rr\-conflict \fR\fIrole\-resync\-conflict\-policy\fR .RS 4 This option sets DRBD\*(Aqs behavior when DRBD deduces from its meta data that a resynchronization is needed, and the SyncTarget node is already primary\&. The possible settings are: \fBdisconnect\fR, \fBcall\-pri\-lost\fR and \fBviolently\fR\&. While \fBdisconnect\fR speaks for itself, with the \fBcall\-pri\-lost\fR setting the \fBpri\-lost\fR handler is called which is expected to either change the role of the node to secondary, or remove the node from the cluster\&. The default is \fBdisconnect\fR\&. .sp With the \fBviolently\fR setting you allow DRBD to force a primary node into SyncTarget state\&. This means that the data exposed by DRBD changes to the SyncSource\*(Aqs version of the data instantaneously\&. USE THIS OPTION ONLY IF YOU KNOW WHAT YOU ARE DOING\&. .RE .PP \fB\-d\fR, \fB\-\-data\-integrity\-alg \fR\fIhash_alg\fR .RS 4 DRBD can ensure the data integrity of the user\*(Aqs data on the network by comparing hash values\&. Normally this is ensured by the 16 bit checksums in the headers of TCP/IP packets\&. This option can be set to any of the kernel\*(Aqs data digest algorithms\&. In a typical kernel configuration you should have at least one of \fBmd5\fR, \fBsha1\fR, and \fBcrc32c\fR available\&. By default this is not enabled\&. .sp See also the notes on data integrity on the drbd\&.conf manpage\&. .RE .PP \fB\-o\fR, \fB\-\-no\-tcp\-cork \fR .RS 4 DRBD usually uses the TCP socket option TCP_CORK to hint to the network stack when it can expect more data, and when it should flush out what it has in its send queue\&. There is at least one network stack that performs worse when one uses this hinting method\&. Therefore we introduced this option, which disable the setting and clearing of the TCP_CORK socket option by DRBD\&. .RE .PP \fB\-p\fR, \fB\-\-ping\-timeout \fR\fIping_timeout\fR .RS 4 The time the peer has to answer to a keep\-alive packet\&. In case the peer\*(Aqs reply is not received within this time period, it is considered dead\&. The default unit is tenths of a second, the default value is 5 (for half a second)\&. .RE .PP \fB\-D\fR, \fB\-\-discard\-my\-data \fR .RS 4 Use this option to manually recover from a split\-brain situation\&. In case you do not have any automatic after\-split\-brain policies selected, the nodes refuse to connect\&. By passing this option you make this node a sync target immediately after successful connect\&. .RE .PP \fB\-n\fR, \fB\-\-dry\-run \fR .RS 4 Causes DRBD to abort the connection process after the resync handshake, i\&.e\&. no resync gets performed\&. You can find out which resync DRBD would perform by looking at the kernel\*(Aqs log file\&. .RE .PP \fB\-g\fR, \fB\-\-on\-congestion \fR\fIcongestion_policy\fR, \fB\-f\fR, \fB\-\-congestion\-fill \fR\fIfill_threshold\fR, \fB\-h\fR, \fB\-\-congestion\-extents \fR\fIactive_extents_threshold\fR .RS 4 By default DRBD blocks when the available TCP send queue becomes full\&. That means it will slow down the application that generates the write requests that cause DRBD to send more data down that TCP connection\&. .sp When DRBD is deployed with DRBD\-proxy it might be more desirable that DRBD goes into AHEAD/BEHIND mode shortly before the send queue becomes full\&. In AHEAD/BEHIND mode DRBD does no longer replicate data, but still keeps the connection open\&. .sp The advantage of the AHEAD/BEHIND mode is that the application is not slowed down, even if DRBD\-proxy\*(Aqs buffer is not sufficient to buffer all write requests\&. The downside is that the peer node falls behind, and that a resync will be necessary to bring it back into sync\&. During that resync the peer node will have an inconsistent disk\&. .sp Available \fIcongestion_policy\fRs are \fBblock\fR and \fBpull\-ahead\fR\&. The default is \fBblock\fR\&. \fIFill_threshold\fR might be in the range of 0 to 10GiBytes\&. The default is 0 which disables the check\&. \fIActive_extents_threshold\fR has the same limits as \fBal\-extents\fR\&. .sp The AHEAD/BEHIND mode and its settings are available since DRBD 8\&.3\&.10\&. .RE .SS "syncer" .PP Changes the synchronization daemon parameters of \fIdevice\fR at runtime\&. .PP \fB\-r\fR, \fB\-\-rate \fR\fB\fIrate\fR\fR .RS 4 To ensure smooth operation of the application on top of DRBD, it is possible to limit the bandwidth that may be used by background synchronization\&. The default is 250 KiB/sec, the default unit is KiB/sec\&. .RE .PP \fB\-a\fR, \fB\-\-after \fR\fB\fIminor\fR\fR .RS 4 Start resync on this device only if the device with \fIminor\fR is already in connected state\&. Otherwise this device waits in SyncPause state\&. .RE .PP \fB\-e\fR, \fB\-\-al\-extents \fR\fB\fIextents\fR\fR .RS 4 DRBD automatically performs hot area detection\&. With this parameter you control how big the hot area (=active set) can get\&. Each extent marks 4M of the backing storage\&. In case a primary node leaves the cluster unexpectedly, the areas covered by the active set must be resynced upon rejoining of the failed node\&. The data structure is stored in the meta\-data area, therefore each change of the active set is a write operation to the meta\-data device\&. A higher number of extents gives longer resync times but less updates to the meta\-data\&. The default number of \fIextents\fR is 127\&. (Minimum: 7, Maximum: 3843) .RE .PP \fB\-v\fR, \fB\-\-verify\-alg \fR\fB\fIhash\-alg\fR\fR .RS 4 During online verification (as initiated by the \fBverify\fR sub\-command), rather than doing a bit\-wise comparison, DRBD applies a hash function to the contents of every block being verified, and compares that hash with the peer\&. This option defines the hash algorithm being used for that purpose\&. It can be set to any of the kernel\*(Aqs data digest algorithms\&. In a typical kernel configuration you should have at least one of \fBmd5\fR, \fBsha1\fR, and \fBcrc32c\fR available\&. By default this is not enabled; you must set this option explicitly in order to be able to use on\-line device verification\&. .sp See also the notes on data integrity on the drbd\&.conf manpage\&. .RE .PP \fB\-c\fR, \fB\-\-cpu\-mask \fR\fB\fIcpu\-mask\fR\fR .RS 4 Sets the cpu\-affinity\-mask for DRBD\*(Aqs kernel threads of this device\&. The default value of \fIcpu\-mask\fR is 0, which means that DRBD\*(Aqs kernel threads should be spread over all CPUs of the machine\&. This value must be given in hexadecimal notation\&. If it is too big it will be truncated\&. .RE .PP \fB\-C\fR, \fB\-\-csums\-alg \fR\fB\fIhash\-alg\fR\fR .RS 4 A resync process sends all marked data blocks form the source to the destination node, as long as no \fBcsums\-alg\fR is given\&. When one is specified the resync process exchanges hash values of all marked blocks first, and sends only those data blocks over, that have different hash values\&. .sp This setting is useful for DRBD setups with low bandwidth links\&. During the restart of a crashed primary node, all blocks covered by the activity log are marked for resync\&. But a large part of those will actually be still in sync, therefore using \fBcsums\-alg\fR will lower the required bandwidth in exchange for CPU cycles\&. .RE .PP \fB\-R\fR, \fB\-\-use\-rle\fR .RS 4 During resync\-handshake, the dirty\-bitmaps of the nodes are exchanged and merged (using bit\-or), so the nodes will have the same understanding of which blocks are dirty\&. On large devices, the fine grained dirty\-bitmap can become large as well, and the bitmap exchange can take quite some time on low\-bandwidth links\&. .sp Because the bitmap typically contains compact areas where all bits are unset (clean) or set (dirty), a simple run\-length encoding scheme can considerably reduce the network traffic necessary for the bitmap exchange\&. .sp For backward compatibilty reasons, and because on fast links this possibly does not improve transfer time but consumes cpu cycles, this defaults to off\&. .sp Introduced in 8\&.3\&.2\&. .RE .PP \fB\-p\fR, \fB\-\-c\-plan\-ahead \fR\fB\fIplan_time\fR\fR, \fB\-s\fR, \fB\-\-c\-fill\-target \fR\fB\fIfill_target\fR\fR, \fB\-d\fR, \fB\-\-c\-delay\-target \fR\fB\fIdelay_target\fR\fR, \fB\-M\fR, \fB\-\-c\-max\-rate \fR\fB\fImax_rate\fR\fR .RS 4 The dynamic resync speed controller gets enabled with setting \fIplan_time\fR to a positive value\&. It aims to fill the buffers along the data path with either a constant amount of data \fIfill_target\fR, or aims to have a constant delay time of \fIdelay_target\fR along the path\&. The controller has an upper bound of \fImax_rate\fR\&. .sp By \fIplan_time\fR the agility of the controller is configured\&. Higher values yield for slower/lower responses of the controller to deviation from the target value\&. It should be at least 5 times RTT\&. For regular data paths a \fIfill_target\fR in the area of 4k to 100k is appropriate\&. For a setup that contains drbd\-proxy it is advisable to use \fIdelay_target\fR instead\&. Only when \fIfill_target\fR is set to 0 the controller will use \fIdelay_target\fR\&. 5 times RTT is a reasonable starting value\&. \fIMax_rate\fR should be set to the bandwidth available between the DRBD\-hosts and the machines hosting DRBD\-proxy, or to the available disk\-bandwidth\&. .sp The default value of \fIplan_time\fR is 0, the default unit is 0\&.1 seconds\&. \fIFill_target\fR has 0 and sectors as default unit\&. \fIDelay_target\fR has 1 (100ms) and 0\&.1 as default unit\&. \fIMax_rate\fR has 10240 (100MiB/s) and KiB/s as default unit\&. .RE .PP \fB\-m\fR, \fB\-\-c\-min\-rate \fR\fB\fImin_rate\fR\fR .RS 4 We track the disk IO rate caused by the resync, so we can detect non\-resync IO on the lower level device\&. If the lower level device seems to be busy, and the current resync rate is above \fImin_rate\fR, we throttle the resync\&. .sp The default value of \fImin_rate\fR is 4M, the default unit is k\&. If you want to not throttle at all, set it to zero, if you want to throttle always, set it to one\&. .RE .PP \fB\-n\fR, \fB\-\-on\-no\-data\-accessible \fR\fB\fIond\-policy\fR\fR .RS 4 This setting controls what happens to IO requests on a degraded, disk less node (I\&.e\&. no data store is reachable)\&. The available policies are \fBio\-error\fR and \fBsuspend\-io\fR\&. .sp If \fIond\-policy\fR is set to \fBsuspend\-io\fR you can either resume IO by attaching/connecting the last lost data storage, or by the \fBdrbdadm resume\-io \fR\fB\fIres\fR\fR command\&. The latter will result in IO errors of course\&. .sp The default is \fBio\-error\fR\&. This setting is available since DRBD 8\&.3\&.9\&. .RE .SS "primary" .PP Sets the \fIdevice\fR into primary role\&. This means that applications (e\&.g\&. a file system) may open the \fIdevice\fR for read and write access\&. Data written to the \fIdevice\fR in primary role are mirrored to the device in secondary role\&. .PP Normally it is not possible to set both devices of a connected DRBD device pair to primary role\&. By using the \fB\-\-allow\-two\-primaries\fR option, you override this behavior and instruct DRBD to allow two primaries\&. .PP \fB\-o\fR, \fB\-\-overwrite\-data\-of\-peer\fR .RS 4 Alias for \-\-force\&. .RE .PP \fB\-f\fR, \fB\-\-force\fR .RS 4 Becoming primary fails if the local replica is not up\-to\-date\&. I\&.e\&. when it is inconsistent, outdated of consistent\&. By using this option you can force it into primary role anyway\&. USE THIS OPTION ONLY IF YOU KNOW WHAT YOU ARE DOING\&. .RE .SS "secondary" .PP Brings the \fIdevice\fR into secondary role\&. This operation fails as long as at least one application (or file system) has opened the device\&. .PP It is possible that both devices of a connected DRBD device pair are secondary\&. .SS "verify" .PP This initiates on\-line device verification\&. During on\-line verification, the contents of every block on the local node are compared to those on the peer node\&. Device verification progress can be monitored via /proc/drbd\&. Any blocks whose content differs from that of the corresponding block on the peer node will be marked out\-of\-sync in DRBD\*(Aqs on\-disk bitmap; they are \fInot\fR brought back in sync automatically\&. To do that, simply disconnect and reconnect the resource\&. .PP If on\-line verification is already in progress (and this node is "VerifyS"), this command silently "succeeds"\&. In this case, any start\-sector (see below) will be ignored, and any stop\-sector (see below) will be honored\&. This can be used to stop a running verify, or to update/shorten/extend the coverage of the currently running verify\&. .PP This command will fail if the \fIdevice\fR is not part of a connected device pair\&. .PP See also the notes on data integrity on the drbd\&.conf manpage\&. .PP \fB\-s\fR, \fB\-\-start \fR\fB\fIstart\-sector\fR\fR .RS 4 Since version 8\&.3\&.2, on\-line verification should resume from the last position after connection loss\&. It may also be started from an arbitrary position by setting this option\&. If you had reached some stop\-sector before, and you do not specify an explicit start\-sector, verify should resume from the previous stop\-sector\&. .sp Default unit is sectors\&. You may also specify a unit explicitly\&. The \fBstart\-sector\fR will be rounded down to a multiple of 8 sectors (4kB)\&. .RE .PP \fB\-S\fR, \fB\-\-stop \fR\fB\fIstop\-sector\fR\fR .RS 4 Since version 8\&.3\&.14, on\-line verification can be stopped before it reaches end\-of\-device\&. This can be .sp Default unit is sectors\&. You may also specify a unit explicitly\&. The \fBstop\-sector\fR may be updated by issuing an additional drbdsetup verify command on the same node while the verify is running\&. .RE .SS "invalidate" .PP This forces the local device of a pair of connected DRBD devices into SyncTarget state, which means that all data blocks of the device are copied over from the peer\&. .PP This command will fail if the \fIdevice\fR is not either part of a connected device pair, or disconnected Secondary\&. .SS "invalidate\-remote" .PP This forces the local device of a pair of connected DRBD devices into SyncSource state, which means that all data blocks of the device are copied to the peer\&. .PP On a disconnected Primary device, this will set all bits in the out of sync bitmap\&. As a side affect this suspends updates to the on disk activity log\&. Updates to the on disk activity log resume automatically when necessary\&. .SS "wait\-connect" .PP Returns as soon as the \fIdevice\fR can communicate with its partner device\&. .PP \fB\-t\fR, \fB\-\-wfc\-timeout \fR\fB\fIwfc_timeout\fR\fR, \fB\-d\fR, \fB\-\-degr\-wfc\-timeout \fR\fB\fIdegr_wfc_timeout\fR\fR, \fB\-o\fR, \fB\-\-outdated\-wfc\-timeout \fR\fB\fIoutdated_wfc_timeout\fR\fR, \fB\-w\fR, \fB\-\-wait\-after\-sb\fR .RS 4 This command will fail if the \fIdevice\fR cannot communicate with its partner for \fItimeout\fR seconds\&. If the peer was working before this node was rebooted, the \fIwfc_timeout\fR is used\&. If the peer was already down before this node was rebooted, the \fIdegr_wfc_timeout\fR is used\&. If the peer was sucessfully outdated before this node was rebooted the \fIoutdated_wfc_timeout\fR is used\&. The default value for all those timeout values is 0 which means to wait forever\&. In case the connection status goes down to StandAlone because the peer appeared but the devices had a split brain situation, the default for the command is to terminate\&. You can change this behavior with the \fB\-\-wait\-after\-sb\fR option\&. .RE .SS "wait\-sync" .PP Returns as soon as the \fIdevice\fR leaves any synchronization into connected state\&. The options are the same as with the \fIwait\-connect\fR command\&. .SS "disconnect" .PP Removes the information set by the \fBnet\fR command from the \fIdevice\fR\&. This means that the \fIdevice\fR goes into unconnected state and will no longer listen for incoming connections\&. .SS "detach" .PP Removes the information set by the \fBdisk\fR command from the \fIdevice\fR\&. This means that the \fIdevice\fR is detached from its backing storage device\&. .PP \fB\-f\fR, \fB\-\-force\fR .RS 4 A regular detach returns after the disk state finally reached diskless\&. As a consequence detaching from a frozen backing block device never terminates\&. .sp On the other hand A forced detach returns immediately\&. It allows you to detach DRBD from a frozen backing block device\&. Please note that the disk will be marked as failed until all pending IO requests where finished by the backing block device\&. .RE .SS "down" .PP Removes all configuration information from the \fIdevice\fR and forces it back to unconfigured state\&. .SS "role" .PP Shows the current roles of the \fIdevice\fR and its peer, as \fIlocal\fR/\fIpeer\fR\&. .SS "state" .PP Deprecated alias for "role" .SS "cstate" .PP Shows the current connection state of the \fIdevice\fR\&. .SS "dstate" .PP Shows the current states of the backing storage devices, as \fIlocal\fR/\fIpeer\fR\&. .SS "status" .PP Shows the current status of the device in XML\-like format\&. Example output: .sp .if n \{\ .RS 4 .\} .nf .fi .if n \{\ .RE .\} .sp .SS "resize" .PP This causes DRBD to reexamine the size of the \fIdevice\fR\*(Aqs backing storage device\&. To actually do online growing you need to extend the backing storages on both devices and call the \fBresize\fR command on one of your nodes\&. .PP The \fB\-\-assume\-peer\-has\-space\fR allows you to resize a device which is currently not connected to the peer\&. Use with care, since if you do not resize the peer\*(Aqs disk as well, further connect attempts of the two will fail\&. .PP When the \fB\-\-assume\-clean\fR option is given DRBD will skip the resync of the new storage\&. Only do this if you know that the new storage was initialized to the same content by other means\&. .SS "check\-resize" .PP To enable DRBD to detect offline resizing of backing devices this command may be used to record the current size of backing devices\&. The size is stored in files in /var/lib/drbd/ named drbd\-minor\-??\&.lkbd .PP This command is called by \fBdrbdadm resize \fR\fB\fIres\fR\fR after \fBdrbdsetup \fR\fB\fIdevice\fR\fR\fB resize\fR returned\&. .SS "pause\-sync" .PP Temporarily suspend an ongoing resynchronization by setting the local pause flag\&. Resync only progresses if neither the local nor the remote pause flag is set\&. It might be desirable to postpone DRBD\*(Aqs resynchronization after eventual resynchronization of the backing storage\*(Aqs RAID setup\&. .SS "resume\-sync" .PP Unset the local sync pause flag\&. .SS "outdate" .PP Mark the data on the local backing storage as outdated\&. An outdated device refuses to become primary\&. This is used in conjunction with \fBfencing\fR and by the peer\*(Aqs \fBfence\-peer\fR handler\&. .SS "show\-gi" .PP Displays the device\*(Aqs data generation identifiers verbosely\&. .SS "get\-gi" .PP Displays the device\*(Aqs data generation identifiers\&. .SS "show" .PP Shows all available configuration information of the \fIdevice\fR\&. .SS "suspend\-io" .PP This command is of no apparent use and just provided for the sake of completeness\&. .SS "resume\-io" .PP If the fence\-peer handler fails to stonith the peer node, and your \fBfencing\fR policy is set to resource\-and\-stonith, you can unfreeze IO operations with this command\&. .SS "events" .PP Displays every state change of DRBD and all calls to helper programs\&. This might be used to get notified of DRBD\*(Aqs state changes by piping the output to another program\&. .PP \fB\-a\fR, \fB\-\-all\-devices\fR .RS 4 Display the events of all DRBD minors\&. .RE .PP \fB\-u\fR, \fB\-\-unfiltered\fR .RS 4 This is a debugging aid that displays the content of all received netlink messages\&. .RE .SS "new\-current\-uuid" .PP Generates a new current UUID and rotates all other UUID values\&. This has at least two use cases, namely to skip the initial sync, and to reduce network bandwidth when starting in a single node configuration and then later (re\-)integrating a remote site\&. .PP Available option: .PP \fB\-c\fR, \fB\-\-clear\-bitmap\fR .RS 4 Clears the sync bitmap in addition to generating a new current UUID\&. .RE .PP This can be used to skip the initial sync, if you want to start from scratch\&. This use\-case does only work on "Just Created" meta data\&. Necessary steps: .sp .RS 4 .ie n \{\ \h'-04' 1.\h'+01'\c .\} .el \{\ .sp -1 .IP " 1." 4.2 .\} On \fIboth\fR nodes, initialize meta data and configure the device\&. .sp \fBdrbdadm \-\- \-\-force create\-md \fR\fB\fIres\fR\fR .RE .sp .RS 4 .ie n \{\ \h'-04' 2.\h'+01'\c .\} .el \{\ .sp -1 .IP " 2." 4.2 .\} They need to do the initial handshake, so they know their sizes\&. .sp \fBdrbdadm up \fR\fB\fIres\fR\fR .RE .sp .RS 4 .ie n \{\ \h'-04' 3.\h'+01'\c .\} .el \{\ .sp -1 .IP " 3." 4.2 .\} They are now Connected Secondary/Secondary Inconsistent/Inconsistent\&. Generate a new current\-uuid and clear the dirty bitmap\&. .sp \fBdrbdadm \-\- \-\-clear\-bitmap new\-current\-uuid \fR\fB\fIres\fR\fR .RE .sp .RS 4 .ie n \{\ \h'-04' 4.\h'+01'\c .\} .el \{\ .sp -1 .IP " 4." 4.2 .\} They are now Connected Secondary/Secondary UpToDate/UpToDate\&. Make one side primary and create a file system\&. .sp \fBdrbdadm primary \fR\fB\fIres\fR\fR .sp \fBmkfs \-t \fR\fB\fIfs\-type\fR\fR\fB $(drbdadm sh\-dev \fR\fB\fIres\fR\fR\fB)\fR .RE .PP One obvious side\-effect is that the replica is full of old garbage (unless you made them identical using other means), so any online\-verify is expected to find any number of out\-of\-sync blocks\&. .PP \fIYou must not use this on pre\-existing data!\fR Even though it may appear to work at first glance, once you switch to the other node, your data is toast, as it never got replicated\&. So \fIdo not leave out the mkfs\fR (or equivalent)\&. .PP This can also be used to shorten the initial resync of a cluster where the second node is added after the first node is gone into production, by means of disk shipping\&. This use\-case works on disconnected devices only, the device may be in primary or secondary role\&. .PP The necessary steps on the current active server are: .sp .RS 4 .ie n \{\ \h'-04' 1.\h'+01'\c .\} .el \{\ .sp -1 .IP " 1." 4.2 .\} \fBdrbdsetup \fR\fB\fIdevice\fR\fR\fB new\-current\-uuid \-\-clear\-bitmap\fR .RE .sp .RS 4 .ie n \{\ \h'-04' 2.\h'+01'\c .\} .el \{\ .sp -1 .IP " 2." 4.2 .\} Take the copy of the current active server\&. E\&.g\&. by pulling a disk out of the RAID1 controller, or by copying with dd\&. You need to copy the actual data, and the meta data\&. .RE .sp .RS 4 .ie n \{\ \h'-04' 3.\h'+01'\c .\} .el \{\ .sp -1 .IP " 3." 4.2 .\} \fBdrbdsetup \fR\fB\fIdevice\fR\fR\fB new\-current\-uuid\fR .RE .sp Now add the disk to the new secondary node, and join it to the cluster\&. You will get a resync of that parts that were changed since the first call to \fBdrbdsetup\fR in step 1\&. .SH "EXAMPLES" .PP For examples, please have a look at the \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2\&. .SH "VERSION" .sp This document was revised for version 8\&.3\&.2 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbd\fR(8), \fBdrbddisk\fR(8), \fBdrbdadm\fR(8), \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2, \m[blue]\fBDRBD web site\fR\m[]\&\s-2\u[2]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD User's Guide .RS 4 \%http://www.drbd.org/users-guide/ .RE .IP " 2." 4 DRBD web site .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v83/drbddisk.xml0000644000175000017500000000615012466702073021370 0ustar apoikosapoikos drbddisk Script to mark devices as primary and mount file systems 15 Oct 2008 DRBD 8.3.2 drbddisk 8 System Administration /etc/ha.d/resource.d/drbddisk resource start stop status Introduction The script brings the local device of resource into primary role. It is designed to be used by Heartbeat. In order to use you must define a resource, a host, and any other configuration options in the DRBD configuration file. See for details. If resource is omitted, then all of the resources listed in the config file are affected. Version This document was revised for version 8.0.14 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf5, drbd8, drbdsetup8drbdadm8DRBD Homepage drbd-utils-8.9.10/documentation/v83/drbdmeta.80000644000175000017500000001301213027211700020710 0ustar apoikosapoikos'\" t .\" Title: drbdmeta .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 15 Oct 2008 .\" Manual: System Administration .\" Source: DRBD 8.3.2 .\" Language: English .\" .TH "DRBDMETA" "8" "15 Oct 2008" "DRBD 8.3.2" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbdmeta \- DRBD\*(Aqs meta data management tool .SH "SYNOPSIS" .HP \w'\fBdrbdmeta\fR\ 'u \fBdrbdmeta\fR [\-\-force] [\-\-ignore\-sanity\-checks] {\fIdevice\fR} {v06\ \fIminor\fR | v07\ \fImeta_dev\ index\fR | v08\ \fImeta_dev\ index\fR} {\fIcommand\fR} [\fIcmd\ args\fR...] .SH "DESCRIPTION" .PP Drbdmeta is used to create, display and modify the contents of DRBD\*(Aqs meta data storage\&. Usually you do not want to use this command directly, but start it via the frontend \fBdrbdadm\fR(8)\&. .PP This command only works if the DRBD resource is currently down, or at least detached from its backing storage\&. The first parameter is the device node associated to the resource\&. With the second parameter you can select the version of the meta data\&. Currently all major DRBD releases (0\&.6, 0\&.7 and 8) are supported\&. .SH "OPTIONS" .PP \-\-force .RS 4 All questions that get asked by drbdmeta are treated as if the user answered \*(Aqyes\*(Aq\&. .RE .PP \-\-ignore\-sanity\-checks .RS 4 Some sanity checks cause drbdmeta to terminate\&. E\&.g\&. if a file system image would get destroyed by creating the meta data\&. By using that option you can force drbdmeta to ignore these checks\&. .RE .SH "COMMANDS" .PP create\-md \fB\-\-peer\-max\-bio\-size \fR\fB\fIval\fR\fR .RS 4 Create\-md initializes the meta data storage\&. This needs to be done before a DRBD resource can be taken online for the first time\&. In case there is already a meta data signature of an older format in place, drbdmeta will ask you if it should convert the older format to the selected format\&. .sp If you will use the resource before it is connected to its peer for the first time DRBD may perform better if you use the \fB\-\-peer\-max\-bio\-size\fR option\&. For DRBD versions of the peer use up to these values: <8\&.3\&.7 \-> 4k, 8\&.3\&.8 \-> 32k, 8\&.3\&.9 \-> 128k, 8\&.4\&.0 \-> 1M\&. .RE .PP get\-gi .RS 4 Get\-gi shows a short textual representation of the data generation identifier\&. In version 0\&.6 and 0\&.7 these are generation counters, while in version 8 it is a set of UUIDs\&. .RE .PP show\-gi .RS 4 Show\-gi prints a textual representation of the data generation identifiers including explanatory information\&. .RE .PP dump\-md .RS 4 Dumps the whole contents of the meta data storage including the stored bit\-map and activity\-log in a textual representation\&. .RE .PP outdate .RS 4 Sets the outdated flag in the meta data\&. This is used by the peer node when it wants to become primary, but cannot communicate with the DRBD stack on this host\&. .RE .PP dstate .RS 4 Prints the state of the data on the backing storage\&. The output is always followed by \*(Aq/DUnknown\*(Aq since drbdmeta only looks at the local meta data\&. .RE .PP check\-resize .RS 4 Examines the device size of a backing device, and it\*(Aqs last known device size, recorded in a file /var/lib/drbd/drbd\-minor\-??\&.lkbd\&. In case the size of the backing device changed, and the meta data can be found at the old position, it moves the meta data to the right position at the end of the block device\&. .RE .SH "EXPERT\*(AQS COMMANDS" .PP Drbdmeta allows you to modify the meta data as well\&. This is intentionally omitted for the command\*(Aqs usage output, since you should only use it if you really know what you are doing\&. By setting the generation identifiers to wrong values, you risk to overwrite your up\-to\-data data with an older version of your data\&. .PP set\-gi \fIgi\fR .RS 4 Set\-gi allows you to set the generation identifier\&. \fIGi\fR needs to be a generation counter for the 0\&.6 and 0\&.7 format, and a UUID set for 8\&.x\&. Specify it in the same way as get\-gi shows it\&. .RE .PP restore\-md \fIdump_file\fR .RS 4 Reads the \fIdump_file\fR and writes it to the meta data\&. .RE .SH "VERSION" .sp This document was revised for version 8\&.3\&.2 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbdadm\fR(8) drbd-utils-8.9.10/documentation/v83/drbdsetup.xml0000644000175000017500000024364712504755522021614 0ustar apoikosapoikos 5 Dec 2008 DRBD 8.3.2 drbdsetup 8 System Administration drbdsetup Setup tool for DRBD drbdsetup drbdsetup device disk lower_dev meta_data_dev meta_data_index -dsize -eerr_handler -ffencing_policy -b -tdisk_timeout drbdsetup device net af: local_addr :port af: remote_addr :port protocol -ctime -itime -tval -Ssize -rsize -kcount -emax_epoch_size -bmax_buffers -m -ahash_alg -xshared_secret -Aasb-0p-policy -Basb-1p-policy -Casb-2p-policy -D -Rrole-resync-conflict-policy -pping_timeout -uval -dhash_alg -o -n -gcongestion_policy -fval -hval drbdsetup device syncer -adev_minor -rrate -eextents -vverify-hash-alg -ccpu-mask -Ccsums-hash-alg -R -pplan_time -sfill_target -ddelay_target -mmax_rate -nond-policy drbdsetup device disconnect drbdsetup device detach -f drbdsetup device down drbdsetup device primary -f -o drbdsetup device secondary drbdsetup device verify -sstart-position -Sstop-position drbdsetup device invalidate drbdsetup device invalidate-remote drbdsetup device wait-connect -twfc_timeout -ddegr_wfc_timeout -ooutdated_wfc_timeout -w drbdsetup device wait-sync -twfc_timeout -ddegr_wfc_timeout -ooutdated_wfc_timeout -w drbdsetup device role drbdsetup device cstate drbdsetup device dstate drbdsetup device status drbdsetup device resize -dsize -fassume-peer-has-space -cassume-clean drbdsetup device check-resize drbdsetup device pause-sync drbdsetup device resume-sync drbdsetup device outdate drbdsetup device show-gi drbdsetup device get-gi drbdsetup device show drbdsetup device suspend-io drbdsetup device resume-io drbdsetup device events -u -a drbdsetup device new-current-uuid -c Description drbdsetup is used to associate DRBD devices with their backing block devices, to set up DRBD device pairs to mirror their backing block devices, and to inspect the configuration of running DRBD devices. Note drbdsetup is a low level tool of the DRBD program suite. It is used by the data disk and drbd scripts to communicate with the device driver. Commands Each drbdsetup sub-command might require arguments and bring its own set of options. All values have default units which might be overruled by K, M or G. These units are defined in the usual way (e.g. K = 2^10 = 1024). Common options All drbdsetup sub-commands accept these two options In case the specified DRBD device (minor number) does not exist yet, create it implicitly. When is given on the command line, all options of the invoked sub-command that are not explicitly set are reset to their default values. disk drbdsetup disk Associates device with lower_device to store its data blocks on. The (or ) should only be used if you wish not to use as much as possible from the backing block devices. If you do not use , the device is only ready for use as soon as it was connected to its peer once. (See the command.) , You can override DRBD's size determination method with this option. If you need to use the device before it was ever connected to its peer, use this option to pass the size of the DRBD device to the driver. Default unit is sectors (1s = 512 bytes). If you use the size parameter in drbd.conf, we strongly recommend to add an explicit unit postfix. drbdadm and drbdsetup used to have mismatching default units. , If the driver of the lower_device reports an error to DRBD, DRBD will mark the disk as inconsistent, call a helper program, or detach the device from its backing storage and perform all further IO by requesting it from the peer. The valid err_handlers are: , and . , Under we understand preventive measures to avoid situations where both nodes are primary and disconnected (AKA split brain). Valid fencing policies are: This is the default policy. No fencing actions are done. If a node becomes a disconnected primary, it tries to outdate the peer's disk. This is done by calling the fence-peer handler. The handler is supposed to reach the other node over alternative communication paths and call 'drbdadm outdate res' there. If a node becomes a disconnected primary, it freezes all its IO operations and calls its fence-peer handler. The fence-peer handler is supposed to reach the peer over alternative communication paths and call 'drbdadm outdate res' there. In case it cannot reach the peer, it should stonith the peer. IO is resumed as soon as the situation is resolved. In case your handler fails, you can resume IO with the command. , In case the backing storage's driver has a merge_bvec_fn() function, DRBD has to pretend that it can only process IO requests in units not larger than 4 KiB. (At time of writing the only known drivers which have such a function are: md (software raid driver), dm (device mapper - LVM) and DRBD itself) To get best performance out of DRBD on top of software raid (or any other driver with a merge_bvec_fn() function) you might enable this option, if you know for sure that the merge_bvec_fn() function will deliver the same results on all nodes of your cluster. I.e. the physical disks of the software raid are exactly of the same type. USE THIS OPTION ONLY IF YOU KNOW WHAT YOU ARE DOING. , , , DRBD has four implementations to express write-after-write dependencies to its backing storage device. DRBD will use the first method that is supported by the backing storage device and that is not disabled by the user. When selecting the method you should not only base your decision on the measurable performance. In case your backing storage device has a volatile write cache (plain disks, RAID of plain disks) you should use one of the first two. In case your backing storage device has battery-backed write cache you may go with option 3. Option 4 (disable everything, use "none") is dangerous on most IO stacks, may result in write-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles. Do not use . Unfortunately device mapper (LVM) might not support barriers. The letter after "wo:" in /proc/drbd indicates with method is currently in use for a device: b, f, d, n. The implementations: barrier The first requires that the driver of the backing storage device support barriers (called 'tagged command queuing' in SCSI and 'native command queuing' in SATA speak). The use of this method can be disabled by the option. Note: Since Linux-2.6.36 (or RHEL's 2.6.32) this method is disabled. flush The second requires that the backing device support disk flushes (called 'force unit access' in the drive vendors speak). The use of this method can be disabled using the option. drain The third method is simply to let write requests drain before write requests of a new reordering domain are issued. That was the only implementation before 8.0.9. none The fourth method is to not express write-after-write dependencies to the backing store at all, by also specifying . This is dangerous on most IO stacks, may result in write-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles. Do not use . , Disables the use of disk flushes and barrier BIOs when accessing the meta data device. See the notes on . , In some special circumstances the device mapper stack manages to pass BIOs to DRBD that violate the constraints that are set forth by DRBD's merge_bvec() function and which have more than one bvec. A known example is: phys-disk -> DRBD -> LVM -> Xen -> missaligned partition (63) -> DomU FS. Then you might see "bio would need to, but cannot, be split:" in the Dom0's kernel log. The best workaround is to proper align the partition within the VM (E.g. start it at sector 1024). That costs 480 KiB of storage. Unfortunately the default of most Linux partitioning tools is to start the first partition at an odd number (63). Therefore most distributions install helpers for virtual linux machines will end up with missaligned partitions. The second best workaround is to limit DRBD's max bvecs per BIO (i.e., the option) to 1, but that might cost performance. The default value of is 0, which means that there is no user imposed limitation. , If the driver of the lower_device does not finish an IO request within disk_timeout, DRBD considers the disk as failed. If DRBD is connected to a remote host, it will reissue local pending IO requests to the peer, and ship all new IO requests to the peer only. The disk state advances to diskless, as soon as the backing block device has finished all IO requests. The default value of is 0, which means that no timeout is enforced. The default unit is 100ms. This option is available since 8.3.12. net drbdsetup net Sets up the device to listen on af:local_addr:port for incoming connections and to try to connect to af:remote_addr:port. If port is omitted, 7788 is used as default. If af is omitted gets used. Other supported address families are , for Dolphin Interconnect Solutions' "super sockets" and for Sockets Direct Protocol (Infiniband). On the TCP/IP link the specified protocol is used. Valid protocol specifiers are A, B, and C. Protocol A: write IO is reported as completed, if it has reached local disk and local TCP send buffer. Protocol B: write IO is reported as completed, if it has reached local disk and remote buffer cache. Protocol C: write IO is reported as completed, if it has reached both local and remote disk. , In case it is not possible to connect to the remote DRBD device immediately, DRBD keeps on trying to connect. With this option you can set the time between two retries. The default value is 10 seconds, the unit is 1 second. , If the TCP/IP connection linking a DRBD device pair is idle for more than time seconds, DRBD will generate a keep-alive packet to check if its partner is still alive. The default value is 10 seconds, the unit is 1 second. , If the partner node fails to send an expected response packet within val tenths of a second, the partner node is considered dead and therefore the TCP/IP connection is abandoned. The default value is 60 (= 6 seconds). , The socket send buffer is used to store packets sent to the secondary node, which are not yet acknowledged (from a network point of view) by the secondary node. When using protocol A, it might be necessary to increase the size of this data structure in order to increase asynchronicity between primary and secondary nodes. But keep in mind that more asynchronicity is synonymous with more data loss in the case of a primary node failure. Since 8.0.13 resp. 8.2.7 setting the size value to 0 means that the kernel should autotune this. The default size is 0, i.e. autotune. , Packets received from the network are stored in the socket receive buffer first. From there they are consumed by DRBD. Before 8.3.2 the receive buffer's size was always set to the size of the socket send buffer. Since 8.3.2 they can be tuned independently. A value of 0 means that the kernel should autotune this. The default size is 0, i.e. autotune. , In case the secondary node fails to complete a single write request for count times the timeout, it is expelled from the cluster, i.e. the primary node goes into StandAlone mode. To disable this feature, you should explicitly set it to 0; defaults may change between versions. , With this option the maximal number of write requests between two barriers is limited. Typically set to the same as , or the allowed maximum. Values smaller than 10 can lead to degraded performance. The default value is 2048. , With this option the maximal number of buffer pages allocated by DRBD's receiver thread is limited. Typically set to the same as . Small values could lead to degraded performance. The default value is 2048, the minimum 32. Increase this if you cannot saturate the IO backend of the receiving side during linear write or during resync while otherwise idle. See also drbd.conf5 , This setting has no effect with recent kernels that use explicit on-stack plugging (upstream Linux kernel 2.6.39, distributions may have backported). When the number of pending write requests on the standby (secondary) node exceeds the unplug-watermark, we trigger the request processing of our backing storage device. Some storage controllers deliver better performance with small values, others deliver best performance when the value is set to the same value as max-buffers, yet others don't feel much effect at all. Minimum 16, default 128, maximum 131072. , With this option set you may assign primary role to both nodes. You only should use this option if you use a shared storage file system on top of DRBD. At the time of writing the only ones are: OCFS2 and GFS. If you use this option with any other file system, you are going to crash your nodes and to corrupt your data! , alg You need to specify the HMAC algorithm to enable peer authentication at all. You are strongly encouraged to use peer authentication. The HMAC algorithm will be used for the challenge response authentication of the peer. You may specify any digest algorithm that is named in /proc/crypto. , secret The shared secret used in peer authentication. May be up to 64 characters. , asb-0p-policy possible policies are: No automatic resynchronization, simply disconnect. Auto sync from the node that was primary before the split-brain situation occurred. Auto sync from the node that became primary as second during the split-brain situation. In case one node did not write anything since the split brain became evident, sync from the node that wrote something to the node that did not write anything. In case none wrote anything this policy uses a random decision to perform a "resync" of 0 blocks. In case both have written something this policy disconnects the nodes. Auto sync from the node that touched more blocks during the split brain situation. Auto sync to the named node. , asb-1p-policy possible policies are: No automatic resynchronization, simply disconnect. Discard the version of the secondary if the outcome of the algorithm would also destroy the current secondary's data. Otherwise disconnect. Discard the secondary's version. Always honor the outcome of the algorithm. In case it decides the current secondary has the correct data, call the on the current primary. Always honor the outcome of the algorithm. In case it decides the current secondary has the correct data, accept a possible instantaneous change of the primary's data. , asb-2p-policy possible policies are: No automatic resynchronization, simply disconnect. Always honor the outcome of the algorithm. In case it decides the current secondary has the right data, call the on the current primary. Always honor the outcome of the algorithm. In case it decides the current secondary has the right data, accept a possible instantaneous change of the primary's data. , Normally the automatic after-split-brain policies are only used if current states of the UUIDs do not indicate the presence of a third node. With this option you request that the automatic after-split-brain policies are used as long as the data sets of the nodes are somehow related. This might cause a full sync, if the UUIDs indicate the presence of a third node. (Or double faults have led to strange UUID sets.) , role-resync-conflict-policy This option sets DRBD's behavior when DRBD deduces from its meta data that a resynchronization is needed, and the SyncTarget node is already primary. The possible settings are: , and . While speaks for itself, with the setting the handler is called which is expected to either change the role of the node to secondary, or remove the node from the cluster. The default is . With the setting you allow DRBD to force a primary node into SyncTarget state. This means that the data exposed by DRBD changes to the SyncSource's version of the data instantaneously. USE THIS OPTION ONLY IF YOU KNOW WHAT YOU ARE DOING. , hash_alg DRBD can ensure the data integrity of the user's data on the network by comparing hash values. Normally this is ensured by the 16 bit checksums in the headers of TCP/IP packets. This option can be set to any of the kernel's data digest algorithms. In a typical kernel configuration you should have at least one of , , and available. By default this is not enabled. See also the notes on data integrity on the drbd.conf manpage. , DRBD usually uses the TCP socket option TCP_CORK to hint to the network stack when it can expect more data, and when it should flush out what it has in its send queue. There is at least one network stack that performs worse when one uses this hinting method. Therefore we introduced this option, which disable the setting and clearing of the TCP_CORK socket option by DRBD. , ping_timeout The time the peer has to answer to a keep-alive packet. In case the peer's reply is not received within this time period, it is considered dead. The default unit is tenths of a second, the default value is 5 (for half a second). , Use this option to manually recover from a split-brain situation. In case you do not have any automatic after-split-brain policies selected, the nodes refuse to connect. By passing this option you make this node a sync target immediately after successful connect. , Causes DRBD to abort the connection process after the resync handshake, i.e. no resync gets performed. You can find out which resync DRBD would perform by looking at the kernel's log file. , congestion_policy , fill_threshold , active_extents_threshold By default DRBD blocks when the available TCP send queue becomes full. That means it will slow down the application that generates the write requests that cause DRBD to send more data down that TCP connection. When DRBD is deployed with DRBD-proxy it might be more desirable that DRBD goes into AHEAD/BEHIND mode shortly before the send queue becomes full. In AHEAD/BEHIND mode DRBD does no longer replicate data, but still keeps the connection open. The advantage of the AHEAD/BEHIND mode is that the application is not slowed down, even if DRBD-proxy's buffer is not sufficient to buffer all write requests. The downside is that the peer node falls behind, and that a resync will be necessary to bring it back into sync. During that resync the peer node will have an inconsistent disk. Available congestion_policys are and . The default is . Fill_threshold might be in the range of 0 to 10GiBytes. The default is 0 which disables the check. Active_extents_threshold has the same limits as . The AHEAD/BEHIND mode and its settings are available since DRBD 8.3.10. syncer drbdsetup syncer Changes the synchronization daemon parameters of device at runtime. , To ensure smooth operation of the application on top of DRBD, it is possible to limit the bandwidth that may be used by background synchronization. The default is 250 KiB/sec, the default unit is KiB/sec. , Start resync on this device only if the device with minor is already in connected state. Otherwise this device waits in SyncPause state. , DRBD automatically performs hot area detection. With this parameter you control how big the hot area (=active set) can get. Each extent marks 4M of the backing storage. In case a primary node leaves the cluster unexpectedly, the areas covered by the active set must be resynced upon rejoining of the failed node. The data structure is stored in the meta-data area, therefore each change of the active set is a write operation to the meta-data device. A higher number of extents gives longer resync times but less updates to the meta-data. The default number of extents is 127. (Minimum: 7, Maximum: 3843) , During online verification (as initiated by the verify sub-command), rather than doing a bit-wise comparison, DRBD applies a hash function to the contents of every block being verified, and compares that hash with the peer. This option defines the hash algorithm being used for that purpose. It can be set to any of the kernel's data digest algorithms. In a typical kernel configuration you should have at least one of , , and available. By default this is not enabled; you must set this option explicitly in order to be able to use on-line device verification. See also the notes on data integrity on the drbd.conf manpage. , Sets the cpu-affinity-mask for DRBD's kernel threads of this device. The default value of cpu-mask is 0, which means that DRBD's kernel threads should be spread over all CPUs of the machine. This value must be given in hexadecimal notation. If it is too big it will be truncated. , A resync process sends all marked data blocks form the source to the destination node, as long as no is given. When one is specified the resync process exchanges hash values of all marked blocks first, and sends only those data blocks over, that have different hash values. This setting is useful for DRBD setups with low bandwidth links. During the restart of a crashed primary node, all blocks covered by the activity log are marked for resync. But a large part of those will actually be still in sync, therefore using will lower the required bandwidth in exchange for CPU cycles. , During resync-handshake, the dirty-bitmaps of the nodes are exchanged and merged (using bit-or), so the nodes will have the same understanding of which blocks are dirty. On large devices, the fine grained dirty-bitmap can become large as well, and the bitmap exchange can take quite some time on low-bandwidth links. Because the bitmap typically contains compact areas where all bits are unset (clean) or set (dirty), a simple run-length encoding scheme can considerably reduce the network traffic necessary for the bitmap exchange. For backward compatibilty reasons, and because on fast links this possibly does not improve transfer time but consumes cpu cycles, this defaults to off. Introduced in 8.3.2. , , , , The dynamic resync speed controller gets enabled with setting plan_time to a positive value. It aims to fill the buffers along the data path with either a constant amount of data fill_target, or aims to have a constant delay time of delay_target along the path. The controller has an upper bound of max_rate. By plan_time the agility of the controller is configured. Higher values yield for slower/lower responses of the controller to deviation from the target value. It should be at least 5 times RTT. For regular data paths a fill_target in the area of 4k to 100k is appropriate. For a setup that contains drbd-proxy it is advisable to use delay_target instead. Only when fill_target is set to 0 the controller will use delay_target. 5 times RTT is a reasonable starting value. Max_rate should be set to the bandwidth available between the DRBD-hosts and the machines hosting DRBD-proxy, or to the available disk-bandwidth. The default value of plan_time is 0, the default unit is 0.1 seconds. Fill_target has 0 and sectors as default unit. Delay_target has 1 (100ms) and 0.1 as default unit. Max_rate has 10240 (100MiB/s) and KiB/s as default unit. , We track the disk IO rate caused by the resync, so we can detect non-resync IO on the lower level device. If the lower level device seems to be busy, and the current resync rate is above min_rate, we throttle the resync. The default value of min_rate is 4M, the default unit is k. If you want to not throttle at all, set it to zero, if you want to throttle always, set it to one. , This setting controls what happens to IO requests on a degraded, disk less node (I.e. no data store is reachable). The available policies are and . If ond-policy is set to you can either resume IO by attaching/connecting the last lost data storage, or by the drbdadm resume-io res command. The latter will result in IO errors of course. The default is . This setting is available since DRBD 8.3.9. primary drbdsetup primary Sets the device into primary role. This means that applications (e.g. a file system) may open the device for read and write access. Data written to the device in primary role are mirrored to the device in secondary role. Normally it is not possible to set both devices of a connected DRBD device pair to primary role. By using the option, you override this behavior and instruct DRBD to allow two primaries. , Alias for --force. , Becoming primary fails if the local replica is not up-to-date. I.e. when it is inconsistent, outdated of consistent. By using this option you can force it into primary role anyway. USE THIS OPTION ONLY IF YOU KNOW WHAT YOU ARE DOING. secondary drbdsetup secondary Brings the device into secondary role. This operation fails as long as at least one application (or file system) has opened the device. It is possible that both devices of a connected DRBD device pair are secondary. verify drbdsetup verify This initiates on-line device verification. During on-line verification, the contents of every block on the local node are compared to those on the peer node. Device verification progress can be monitored via /proc/drbd. Any blocks whose content differs from that of the corresponding block on the peer node will be marked out-of-sync in DRBD's on-disk bitmap; they are not brought back in sync automatically. To do that, simply disconnect and reconnect the resource. If on-line verification is already in progress (and this node is "VerifyS"), this command silently "succeeds". In this case, any start-sector (see below) will be ignored, and any stop-sector (see below) will be honored. This can be used to stop a running verify, or to update/shorten/extend the coverage of the currently running verify. This command will fail if the device is not part of a connected device pair. See also the notes on data integrity on the drbd.conf manpage. , Since version 8.3.2, on-line verification should resume from the last position after connection loss. It may also be started from an arbitrary position by setting this option. If you had reached some stop-sector before, and you do not specify an explicit start-sector, verify should resume from the previous stop-sector. Default unit is sectors. You may also specify a unit explicitly. The will be rounded down to a multiple of 8 sectors (4kB). , Since version 8.3.14, on-line verification can be stopped before it reaches end-of-device. This can be Default unit is sectors. You may also specify a unit explicitly. The may be updated by issuing an additional drbdsetup verify command on the same node while the verify is running. invalidate drbdsetup invalidate This forces the local device of a pair of connected DRBD devices into SyncTarget state, which means that all data blocks of the device are copied over from the peer. This command will fail if the device is not either part of a connected device pair, or disconnected Secondary. invalidate-remote drbdsetup invalidate-remote This forces the local device of a pair of connected DRBD devices into SyncSource state, which means that all data blocks of the device are copied to the peer. On a disconnected Primary device, this will set all bits in the out of sync bitmap. As a side affect this suspends updates to the on disk activity log. Updates to the on disk activity log resume automatically when necessary. wait-connect drbdsetup wait-connect Returns as soon as the device can communicate with its partner device. , , , , This command will fail if the device cannot communicate with its partner for timeout seconds. If the peer was working before this node was rebooted, the wfc_timeout is used. If the peer was already down before this node was rebooted, the degr_wfc_timeout is used. If the peer was sucessfully outdated before this node was rebooted the outdated_wfc_timeout is used. The default value for all those timeout values is 0 which means to wait forever. In case the connection status goes down to StandAlone because the peer appeared but the devices had a split brain situation, the default for the command is to terminate. You can change this behavior with the option. wait-sync drbdsetup wait-sync Returns as soon as the device leaves any synchronization into connected state. The options are the same as with the wait-connect command. disconnect drbdsetup disconnect Removes the information set by the command from the device. This means that the device goes into unconnected state and will no longer listen for incoming connections. detach drbdsetup detach Removes the information set by the command from the device. This means that the device is detached from its backing storage device. , A regular detach returns after the disk state finally reached diskless. As a consequence detaching from a frozen backing block device never terminates. On the other hand A forced detach returns immediately. It allows you to detach DRBD from a frozen backing block device. Please note that the disk will be marked as failed until all pending IO requests where finished by the backing block device. down drbdsetup down Removes all configuration information from the device and forces it back to unconfigured state. role drbdsetup role Shows the current roles of the device and its peer, as local/peer. state drbdsetup state Deprecated alias for "role" cstate drbdsetup cstate Shows the current connection state of the device. dstate drbdsetup dstate Shows the current states of the backing storage devices, as local/peer. status drbdsetup status Shows the current status of the device in XML-like format. Example output: <resource minor="0" name="s0" cs="SyncTarget" st1="Secondary" st2="Secondary" ds1="Inconsistent" ds2="UpToDate" resynced_precent="5.9" /> resize drbdsetup resize This causes DRBD to reexamine the size of the device's backing storage device. To actually do online growing you need to extend the backing storages on both devices and call the command on one of your nodes. The allows you to resize a device which is currently not connected to the peer. Use with care, since if you do not resize the peer's disk as well, further connect attempts of the two will fail. When the option is given DRBD will skip the resync of the new storage. Only do this if you know that the new storage was initialized to the same content by other means. check-resize drbdsetup check-resize To enable DRBD to detect offline resizing of backing devices this command may be used to record the current size of backing devices. The size is stored in files in /var/lib/drbd/ named drbd-minor-??.lkbd This command is called by drbdadm resize res after drbdsetup device resize returned. pause-sync drbdsetup pause-sync Temporarily suspend an ongoing resynchronization by setting the local pause flag. Resync only progresses if neither the local nor the remote pause flag is set. It might be desirable to postpone DRBD's resynchronization after eventual resynchronization of the backing storage's RAID setup. resume-sync drbdsetup resume-sync Unset the local sync pause flag. outdate drbdsetup outdate Mark the data on the local backing storage as outdated. An outdated device refuses to become primary. This is used in conjunction with and by the peer's handler. show-gi drbdsetup show-gi Displays the device's data generation identifiers verbosely. get-gi drbdsetup get-gi Displays the device's data generation identifiers. show drbdsetup show Shows all available configuration information of the device. suspend-io drbdsetup suspend-io This command is of no apparent use and just provided for the sake of completeness. resume-io drbdsetup resume-io If the fence-peer handler fails to stonith the peer node, and your policy is set to resource-and-stonith, you can unfreeze IO operations with this command. events drbdsetup events Displays every state change of DRBD and all calls to helper programs. This might be used to get notified of DRBD's state changes by piping the output to another program. , Display the events of all DRBD minors. , This is a debugging aid that displays the content of all received netlink messages. new-current-uuid drbdsetup new-current-uuid Generates a new current UUID and rotates all other UUID values. This has at least two use cases, namely to skip the initial sync, and to reduce network bandwidth when starting in a single node configuration and then later (re-)integrating a remote site. Available option: , Clears the sync bitmap in addition to generating a new current UUID. This can be used to skip the initial sync, if you want to start from scratch. This use-case does only work on "Just Created" meta data. Necessary steps: On both nodes, initialize meta data and configure the device. drbdadm -- --force create-md res They need to do the initial handshake, so they know their sizes. drbdadm up res They are now Connected Secondary/Secondary Inconsistent/Inconsistent. Generate a new current-uuid and clear the dirty bitmap. drbdadm -- --clear-bitmap new-current-uuid res They are now Connected Secondary/Secondary UpToDate/UpToDate. Make one side primary and create a file system. drbdadm primary resmkfs -t fs-type $(drbdadm sh-dev res) One obvious side-effect is that the replica is full of old garbage (unless you made them identical using other means), so any online-verify is expected to find any number of out-of-sync blocks. You must not use this on pre-existing data! Even though it may appear to work at first glance, once you switch to the other node, your data is toast, as it never got replicated. So do not leave out the mkfs (or equivalent). This can also be used to shorten the initial resync of a cluster where the second node is added after the first node is gone into production, by means of disk shipping. This use-case works on disconnected devices only, the device may be in primary or secondary role. The necessary steps on the current active server are: drbdsetup device new-current-uuid --clear-bitmap Take the copy of the current active server. E.g. by pulling a disk out of the RAID1 controller, or by copying with dd. You need to copy the actual data, and the meta data. drbdsetup device new-current-uuid Now add the disk to the new secondary node, and join it to the cluster. You will get a resync of that parts that were changed since the first call to drbdsetup in step 1. Examples For examples, please have a look at the DRBD User's Guide. Version This document was revised for version 8.3.2 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf5, drbd8, drbddisk8, drbdadm8, DRBD User's Guide, DRBD web site drbd-utils-8.9.10/documentation/fencing-by-constraints.txt0000644000175000017500000001002712466702073023565 0ustar apoikosapoikos# vim: set foldenable foldmethod=indent sw=4 ts=8 : # Copyright 2013 Linbit HA Solutions GmbH # Lars Ellenberg @ linbit.com TODO: someone convert this into proper ascii doc please ;-) ... and draw some pictures ... How crm-fence-peer.sh, pacemaker, and the OCF Linbit DRBD resource agent are supposed to work together. Two node cluster is the trickier one, because it has not real quorum. Relative Timeouts --dc-timeout > dead-time resp. stonith-timeout if stonith enabled, --timeout >= --dc-timeout if no stonith, then timeout may be small. Pacemaker operations timeouts monitor and promote action timeout > max(dc_timeout, timeout) Node reboot, possibly because of crash or stonith due to communication loss no peer reachable [no delay] crm may decide to elect itself, shoot the peer, and start services. If DRBD peer disk state is known Outdated or worse, DRBD will switch itself to UpToDate, allowing it to be promoted, without further fencing actions. If DRBD peer disk state is DUnknown, DRBD will be only Consistent. In case crm decides to promote this instance, the fence-peer callback runs, finds the peer "unreachable", finds itself Consistent only, does NOT set any constraint, and DRBD refuses to be promoted. CRM will now try in an endless loop to promote this instance. Avoid this by adding param adjust_master_score="0 10 1000 10000" to the DRBD resource definition. no replication link CRM can see both nodes. [delay: crmadmin -S $peer] If currently both nodes are Secondary Consistent, CRM will decide to promote one instance. The fence-peer callback will find the other node still reachable after timeout, and set the constraint. If there is already one Primary, and this is a node rejoining the cluster, there should already be a constraint preventing this node from being promoted. Only Replication link breaks during normal operation Single Primary [delay: crmadmin -S $peer] fence-peer callback finds DC, crmadmin -S confirms peer still "reachable", and sets contraint. Dual Primary both fence-peer callbacks find DC, both see node_state "reachable", optionaly delay for --network-hickup timeout, and if DRBD is still disconnected, both try to set the constraint. Only one succeeds. The loser should probably commit suicide, to reduce the overall recovery time. --suicide-on-failure-if-primary Node crash surviving node is Secondary, [no delay] If not DC, triggers DC election, elects itself. Is DC now. If stonith enabled, shoots the peer. Promotes this node. During promotion, fenc-peer callback finds a DC, and a node_state "unreachable", so sets the constraint "immediately". surviving node is Primary (DC) [delay up to timeout] If stonith enabled, shoots the peer. fence-peer callback finds DC, after some time sees node_state "unreachable", or times out while node_state is still "reachable". Either way still sets the constraint. surviving node is Primary (not DC) [delay up to mac(dc_timeout,timeout)] fence-peer callback loops trying to contact DC. eventually this node is elected DC. If stonith enabled, shoots the peer. Fence-peer callback either times out while no DC is available, thus fails. Make sure you chose a suitable --dc-timeout. Or it finds the other node "unreachable", and sets the constraint. Total communication loss To the single node, this looks like node crash, so see above. The difference is the potential of data divergence. If DRBD was configured for "fencing resource-and-stonith", IO on any Primary is frozen while the fence-peer callback runs. If stonith is enabled, timeouts should be selected so that we are shot while waiting for the DC to confirm node_state "unreachable" of the peer, thus combined with freezing IO, no harmful data diversion can happen at this time. If there is no stonith enabled, data divergence is unavoidable. ==> Multi-Primary *requires* both node level fencing (stonith) AND drbd resource level fencing Again: Multi-Primary REQUIRES stonith enabled and working. drbd-utils-8.9.10/documentation/v84/0000755000175000017500000000000013027242657017060 5ustar apoikosapoikosdrbd-utils-8.9.10/documentation/v84/drbdmeta.xml0000644000175000017500000002416312466702073021371 0ustar apoikosapoikos 15 Oct 2008 DRBD 8.3.2 drbdmeta 8 System Administration drbdmeta DRBD's meta data management tool drbdmeta drbdmeta --force --ignore-sanity-checks device v06 minor v07 meta_dev index v08 meta_dev index command cmd args Description Drbdmeta is used to create, display and modify the contents of DRBD's meta data storage. Usually you do not want to use this command directly, but start it via the frontend drbdadm8. This command only works if the DRBD resource is currently down, or at least detached from its backing storage. The first parameter is the device node associated to the resource. With the second parameter you can select the version of the meta data. Currently all major DRBD releases (0.6, 0.7 and 8) are supported. Options --force drbdmeta--force All questions that get asked by drbdmeta are treated as if the user answered 'yes'. --ignore-sanity-checks drbdmeta--ignore-sanity-checks Some sanity checks cause drbdmeta to terminate. E.g. if a file system image would get destroyed by creating the meta data. By using that option you can force drbdmeta to ignore these checks. Commands create-md drbdmetacreate-md Create-md initializes the meta data storage. This needs to be done before a DRBD resource can be taken online for the first time. In case there is already a meta data signature of an older format in place, drbdmeta will ask you if it should convert the older format to the selected format. If you will use the resource before it is connected to its peer for the first time DRBD may perform better if you use the option. For DRBD versions of the peer use up to these values: <8.3.7 -> 4k, 8.3.8 -> 32k, 8.3.9 -> 128k, 8.4.0 -> 1M. If you want to use more than 6433 activity log extents, or live on top of a spriped RAID, you may specify the number of stripes (, default 1), and the stripe size (, default 32). To just use a larger linear on-disk ring-buffer, leave the number of stripes at 1, and increase the size only: drbdmeta 0 v08 /dev/vg23/lv42 internal create-md --al-stripe-size 1M To avoid a single "spindle" from becoming a bottleneck, increase the number of stripes, to achieve an interleaved layout of the on-disk activity-log transactions. What you give as "stripe-size" should be what is a.k.a. "chunk size" or "granularity" or "strip unit": the minimum skip to the next "spindle". drbdmeta 0 v08 /dev/vg23/lv42 internal create-md --al-stripes 7 --al-stripe-size 64k get-gi drbdmetaget-gi Get-gi shows a short textual representation of the data generation identifier. In version 0.6 and 0.7 these are generation counters, while in version 8 it is a set of UUIDs. show-gi drbdmetashow-gi Show-gi prints a textual representation of the data generation identifiers including explanatory information. dump-md drbdmetadump-md Dumps the whole contents of the meta data storage including the stored bit-map and activity-log in a textual representation. outdate drbdmetaoutdate Sets the outdated flag in the meta data. This is used by the peer node when it wants to become primary, but cannot communicate with the DRBD stack on this host. dstate drbdmetadstate Prints the state of the data on the backing storage. The output is always followed by '/DUnknown' since drbdmeta only looks at the local meta data. check-resize drbdmetacheck-resize Examines the device size of a backing device, and it's last known device size, recorded in a file /var/lib/drbd/drbd-minor-??.lkbd. In case the size of the backing device changed, and the meta data can be found at the old position, it moves the meta data to the right position at the end of the block device. Expert's commands Drbdmeta allows you to modify the meta data as well. This is intentionally omitted for the command's usage output, since you should only use it if you really know what you are doing. By setting the generation identifiers to wrong values, you risk to overwrite your up-to-data data with an older version of your data. set-gi gi drbdmetaset-gi Set-gi allows you to set the generation identifier. Gi needs to be a generation counter for the 0.6 and 0.7 format, and a UUID set for 8.x. Specify it in the same way as get-gi shows it. restore-md dump_file drbdmetarestore-md Reads the dump_file and writes it to the meta data. Version This document was revised for version 8.3.2 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbdadm 8 drbd-utils-8.9.10/documentation/v84/Makefile.in0000644000175000017500000001202013012630471021105 0ustar apoikosapoikos# Makefile in documentation directory # # This file is part of DRBD by Philipp Reisner and Lars Ellenberg. # # drbd is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. # variables set by configure mandir = @mandir@ datarootdir = @datarootdir@ XSLTPROC = @XSLTPROC@ # features enabled or disabled by configure WITH_83_SUPPORT = @WITH_83_SUPPORT@ WITH_84_SUPPORT = @WITH_84_SUPPORT@ WITH_UDEV = @WITH_UDEV@ WITH_XEN = @WITH_XEN@ WITH_PACEMAKER = @WITH_PACEMAKER@ WITH_HEARTBEAT = @WITH_HEARTBEAT@ WITH_RGMANAGER = @WITH_RGMANAGER@ WITH_BASHCOMPLETION = @WITH_BASHCOMPLETION@ # variables meant to be overridden from the make command line DESTDIR ?= / # Needed for pattern substitution SHELL=/bin/bash MANPAGES := drbdsetup.8 drbd.conf.5 drbd.8 drbdadm.8 drbdmeta.8 ifeq ($(WITH_HEARTBEAT),yes) MANPAGES += drbddisk.8 endif STYLESHEET_PREFIX ?= http://docbook.sourceforge.net/release/xsl/current MANPAGES_STYLESHEET ?= $(STYLESHEET_PREFIX)/manpages/docbook.xsl HTML_STYLESHEET ?= $(STYLESHEET_PREFIX)/xhtml/docbook.xsl FO_STYLESHEET ?= $(STYLESHEET_PREFIX)/fo/docbook.xsl XSLTPROC_OPTIONS ?= --xinclude XSLTPROC_OPTIONS += --stringparam variablelist.term.break.after 1 #XSLTPROC_OPTIONS += --stringparam variablelist.term.separator "" XSLTPROC_MANPAGES_OPTIONS ?= $(XSLTPROC_OPTIONS) XSLTPROC_HTML_OPTIONS ?= $(XSLTPROC_OPTIONS) XSLTPROC_FO_OPTIONS ?= $(XSLTPROC_OPTIONS) DRBDSETUP_CMDS = new-resource new-minor del-resource del-minor DRBDSETUP_CMDS += attach connect disk-options net-options resource-options DRBDSETUP_CMDS += disconnect detach primary secondary verify invalidate invalidate-remote DRBDSETUP_CMDS += down wait-connect wait-sync role cstate dstate DRBDSETUP_CMDS += resize check-resize pause-sync resume-sync DRBDSETUP_CMDS += outdate show-gi get-gi show events events2 DRBDSETUP_CMDS += status suspend-io resume-io new-current-uuid make_doc := $(shell $(XSLTPROC) \ $(XSLTPROC_MANPAGES_OPTIONS) \ $(MANPAGES_STYLESHEET) < /dev/null > /dev/null 2>&1 && echo doc ) ifeq ($(make_doc),doc) all: doc else all: @echo "To (re)make the documentation: make doc" endif clean: @echo "To clean the documentation: make doc-clean" .PHONY: all clean doc man doc-clean distclean .PHONY: install uninstall html pdf ps ifeq ($(WITH_84_SUPPORT),yes) doc: man else doc: endif doc-clean: distclean ####### Implicit rules .SUFFIXES: .sgml .5 .8 .html .pdf .ps %.5 %.8: %.xml $(XSLTPROC) \ $(XSLTPROC_MANPAGES_OPTIONS) \ $(MANPAGES_STYLESHEET) $< %.html: %.xml $(XSLTPROC) -o $@ \ $(XSLTPROC_HTML_OPTIONS) \ $(HTML_STYLESHEET) $< %.fo: %.xml $(XSLTPROC) -o $@ \ $(XSLTPROC_FO_OPTIONS) \ $(FO_STYLESHEET) $< ../../user/v84/drbdsetup.o: FORCE $(MAKE) -C $(@D) drbdsetup-84 .PHONY: FORCE FORCE: # Don't try to re-make files tracked in git FILES_IN_GIT := # $(shell git ls-files) FILES_IN_GIT += Makefile.in drbd.conf.xml drbd.xml drbdadm.xml drbddisk.xml FILES_IN_GIT += drbdmeta.xml drbdsetup.xml xml-usage-to-docbook.xsl $(FILES_IN_GIT): ; drbdsetup_X.xml := $(patsubst %,drbdsetup_%.xml,$(DRBDSETUP_CMDS)) drbdsetup_xml-help_X.xml := $(patsubst %,drbdsetup_xml-help_%.xml,$(DRBDSETUP_CMDS)) $(drbdsetup_xml-help_X.xml): ../../user/v84/drbdsetup.o $(drbdsetup_X.xml): xml-usage-to-docbook.xsl drbdsetup_xml-help_%.xml: ../../user/v84/drbdsetup-84 xml-help $* > $@ drbdsetup_%.xml: drbdsetup_xml-help_%.xml $(XSLTPROC) -o $@ xml-usage-to-docbook.xsl $< distclean: ifeq ($(make_doc),doc) rm -f *.[58] manpage.links manpage.refs *~ manpage.log endif rm -f *.ps.gz *.pdf *.ps *.html pod2htm* rm -f drbdsetup_*.xml ####### man: $(MANPAGES) install: ifeq ($(WITH_84_SUPPORT),yes) @ok=true; for f in $(MANPAGES) ; \ do [ -e $$f ] || { echo $$f missing ; ok=false; } ; \ done ; $$ok set -e; for f in $(MANPAGES) ; do \ s=$${f##*.}; \ b=$${f%.[0-9]}; \ install -v -D -m 644 $$f $(DESTDIR)$(mandir)/man$$s/$$b-8.4.$$s ; \ done endif uninstall: ifeq ($(WITH_84_SUPPORT),yes) @ set -e; for f in $(MANPAGES) ; do \ s=$${f##*.}; \ b=$${f%.[0-9]}; \ rm -vf $(DESTDIR)$(mandir)/man$$s/$$b-8.4.$$s ; \ done endif html: $(MANPAGES:.8=.html) pdf: $(MANPAGES:.8=.pdf) ps: $(MANPAGES:.8=.ps) drbdsetup.8: drbdsetup.xml $(drbdsetup_X.xml) .PHONY: install uninstall clean distclean ../../configure: @echo "please (re-)run ./autogen.sh with appropriate arguments"; exit 1 ../../config.status: ../../configure @echo "please (re-)run ./configure with appropriate arguments"; exit 1 Makefile: Makefile.in ../../config.status cd ../.. && ./config.status documentation/v84/Makefile drbd-utils-8.9.10/documentation/v84/drbdadm.80000644000175000017500000002564713027211740020551 0ustar apoikosapoikos'\" t .\" Title: drbdadm .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 6 May 2011 .\" Manual: System Administration .\" Source: DRBD 8.4.0 .\" Language: English .\" .TH "DRBDADM" "8" "6 May 2011" "DRBD 8.4.0" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbdadm \- Administration tool for DRBD .SH "SYNOPSIS" .HP \w'\fBdrbdadm\fR\ 'u \fBdrbdadm\fR [\-d] [\-c\ {\fIfile\fR}] [\-t\ {\fIfile\fR}] [\-s\ {\fIcmd\fR}] [\-m\ {\fIcmd\fR}] [\-S] [\-h\ {\fIhost\fR}] [\-\-\ {\fIbackend\-options\fR}] {\fIcommand\fR} [{all} | {\fIresource\fR\fI[/volume>]\fR...}] .SH "DESCRIPTION" .PP \fBDrbdadm\fR is the high level tool of the DRBD program suite\&. \fBDrbdadm\fR is to \fBdrbdsetup\fR and \fBdrbdmeta\fR what \fBifup\fR/\fBifdown\fR is to \fBifconfig\fR\&. \fBDrbdadm\fR reads its configuration file and performs the specified commands by calling the \fBdrbdsetup\fR and/or the \fBdrbdmeta\fR program\&. .PP \fBDrbdadm\fR can operate on whole resources or on individual volumes in a resource\&. The sub commands: \fBattach\fR, \fBdetach\fR, \fBprimary\fR, \fBsecondary\fR, \fBinvalidate\fR, \fBinvalidate\-remote\fR, \fBoutdate\fR, \fBresize\fR, \fBverify\fR, \fBpause\-sync\fR, \fBresume\-sync\fR, \fBrole\fR, \fBcsytate\fR, \fBdstate\fR, \fBcreate\-md\fR, \fBshow\-gi\fR, \fBget\-gi\fR, \fBdump\-md\fR, \fBwipe\-md\fR work on whole resources and on individual volumes\&. .PP Resource level only commands are: \fBconnect\fR, \fBdisconnect\fR, \fBup\fR, \fBdown\fR, \fBwait\-connect\fR and \fBdump\fR\&. .SH "OPTIONS" .PP \fB\-d\fR, \fB\-\-dry\-run\fR .RS 4 Just prints the calls of \fBdrbdsetup\fR to stdout, but does not run the commands\&. .RE .PP \fB\-c\fR, \fB\-\-config\-file\fR \fIfile\fR .RS 4 Specifies the configuration file drbdadm will use\&. If this parameter is not specified, drbdadm will look for \fB/etc/drbd\-84\&.conf\fR, \fB/etc/drbd\-83\&.conf\fR, \fB/etc/drbd\-08\&.conf\fR and \fB/etc/drbd\&.conf\fR\&. .RE .PP \fB\-t\fR, \fB\-\-config\-to\-test\fR \fIfile\fR .RS 4 Specifies an additional configuration file drbdadm to check\&. This option is only allowed with the dump and the sh\-nop commands\&. .RE .PP \fB\-s\fR, \fB\-\-drbdsetup\fR \fIfile\fR .RS 4 Specifies the full path to the \fBdrbdsetup\fR program\&. If this option is omitted, drbdadm will look for it beneath itself first, and then in the PATH\&. .RE .PP \fB\-m\fR, \fB\-\-drbdmeta\fR \fIfile\fR .RS 4 Specifies the full path to the \fBdrbdmeta\fR program\&. If this option is omitted, drbdadm will look for it beneath itself first, and then in the PATH\&. .RE .PP \fB\-S\fR, \fB\-\-stacked\fR .RS 4 Specifies that this command should be performed on a stacked resource\&. .RE .PP \fB\-P\fR, \fB\-\-peer\fR .RS 4 Specifies to which peer node to connect\&. Only necessary if there are more than two host sections in the resource you are working on\&. .RE .PP \fB\-\-\fR \fIbackend\-options\fR .RS 4 All options following the doubly hyphen are considered \fIbackend\-options\fR\&. These are passed through to the backend command\&. I\&.e\&. to \fBdrbdsetup\fR, \fBdrbdmeta\fR or \fBdrbd\-proxy\-ctl\fR\&. .RE .SH "COMMANDS" .PP attach .RS 4 Attaches a local backing block device to the DRBD resource\*(Aqs device\&. .RE .PP detach .RS 4 Removes the backing storage device from a DRBD resource\*(Aqs device\&. .RE .PP connect .RS 4 Sets up the network configuration of the resource\*(Aqs device\&. If the peer device is already configured, the two DRBD devices will connect\&. If there are more than two host sections in the resource you need to use the \fB\-\-peer\fR option to select the peer you want to connect to\&. .RE .PP disconnect .RS 4 Removes the network configuration from the resource\&. The device will then go into StandAlone state\&. .RE .PP syncer .RS 4 Loads the resynchronization parameters into the device\&. .RE .PP up .RS 4 Is a shortcut for attach and connect\&. .RE .PP down .RS 4 Is a shortcut for disconnect and detach\&. .RE .PP primary .RS 4 Promote the resource\*(Aqs device into primary role\&. You need to do this before any access to the device, such as creating or mounting a file system\&. .RE .PP secondary .RS 4 Brings the device back into secondary role\&. This is needed since in a connected DRBD device pair, only one of the two peers may have primary role (except if \fBallow\-two\-primaries\fR is explicitly set in the configuration file)\&. .RE .PP invalidate .RS 4 Forces DRBD to consider the data on the \fIlocal\fR backing storage device as out\-of\-sync\&. Therefore DRBD will copy each and every block from its peer, to bring the local storage device back in sync\&. To avoid races, you need an established replication link, or be disconnected Secondary\&. .RE .PP invalidate\-remote .RS 4 This command is similar to the invalidate command, however, the \fIpeer\*(Aqs\fR backing storage is invalidated and hence rewritten with the data of the local node\&. To avoid races, you need an established replication link, or be disconnected Primary\&. .RE .PP resize .RS 4 Causes DRBD to re\-examine all sizing constraints, and resize the resource\*(Aqs device accordingly\&. For example, if you increased the size of your backing storage devices (on both nodes, of course), then DRBD will adopt the new size after you called this command on one of your nodes\&. Since new storage space must be synchronised this command only works if there is at least one primary node present\&. .sp The \fB\-\-size\fR option can be used to online shrink the usable size of a drbd device\&. It\*(Aqs the users responsibility to make sure that a file system on the device is not truncated by that operation\&. .sp The \fB\-\-assume\-peer\-has\-space\fR allows you to resize a device which is currently not connected to the peer\&. Use with care, since if you do not resize the peer\*(Aqs disk as well, further connect attempts of the two will fail\&. .sp The \fB\-\-assume\-clean\fR allows you to resize an existing device and avoid syncing the new space\&. This is useful when adding addtional blank storage to your device\&. Example: .sp .if n \{\ .RS 4 .\} .nf # drbdadm \-\- \-\-assume\-clean resize r0 .fi .if n \{\ .RE .\} .sp The options \fB\-\-al\-stripes\fR and \fB\-\-al\-stripe\-size\-kB\fR may be used to change the layout of the activity log online\&. In case of internal meta data this may invovle shrinking the user visible size at the same time (unsing the \fB\-\-size\fR) or increasing the avalable space on the backing devices\&. .RE .PP check\-resize .RS 4 Calls drbdmeta to eventually move internal meta data\&. If the backing device was resized, while DRBD was not running, meta data has to be moved to the end of the device, so that the next \fBattach\fR command can succeed\&. .RE .PP create\-md .RS 4 Initializes the meta data storage\&. This needs to be done before a DRBD resource can be taken online for the first time\&. In case of issues with that command have a look at \fBdrbdmeta\fR(8) .RE .PP get\-gi .RS 4 Shows a short textual representation of the data generation identifiers\&. .RE .PP show\-gi .RS 4 Prints a textual representation of the data generation identifiers including explanatory information\&. .RE .PP dump\-md .RS 4 Dumps the whole contents of the meta data storage, including the stored bit\-map and activity\-log, in a textual representation\&. .RE .PP outdate .RS 4 Sets the outdated flag in the meta data\&. .RE .PP adjust .RS 4 Synchronizes the configuration of the device with your configuration file\&. You should always examine the output of the dry\-run mode before actually executing this command\&. .RE .PP wait\-connect .RS 4 Waits until the device is connected to its peer device\&. .RE .PP role .RS 4 Shows the current roles of the devices (local/peer)\&. E\&.g\&. Primary/Secondary .RE .PP state .RS 4 Deprecated alias for "role", see above\&. .RE .PP cstate .RS 4 Shows the current connection state of the devices\&. .RE .PP dump .RS 4 Just parse the configuration file and dump it to stdout\&. May be used to check the configuration file for syntactic correctness\&. .RE .PP outdate .RS 4 Used to mark the node\*(Aqs data as outdated\&. Usually used by the peer\*(Aqs fence\-peer handler\&. .RE .PP verify .RS 4 Starts online verify\&. During online verify, data on both nodes is compared for equality\&. See /proc/drbd for online verify progress\&. If out\-of\-sync blocks are found, they are \fInot\fR resynchronized automatically\&. To do that, \fBdisconnect\fR and \fBconnect\fR the resource when verification has completed\&. .sp See also the notes on data integrity on the drbd\&.conf manpage\&. .RE .PP pause\-sync .RS 4 Temporarily suspend an ongoing resynchronization by setting the local pause flag\&. Resync only progresses if neither the local nor the remote pause flag is set\&. It might be desirable to postpone DRBD\*(Aqs resynchronization until after any resynchronization of the backing storage\*(Aqs RAID setup\&. .RE .PP resume\-sync .RS 4 Unset the local sync pause flag\&. .RE .PP new\-current\-uuid .RS 4 Generates a new currend UUID and rotates all other UUID values\&. .sp This can be used to shorten the initial resync of a cluster\&. See the \fBdrbdsetup\fR manpage for a more details\&. .RE .PP dstate .RS 4 Show the current state of the backing storage devices\&. (local/peer) .RE .PP hidden\-commands .RS 4 Shows all commands undocumented on purpose\&. .RE .SH "VERSION" .sp This document was revised for version 8\&.4\&.0 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2011 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbd\fR(8), \fBdrbddisk\fR(8), \fBdrbdsetup\fR(8), \fBdrbdmeta\fR(8) and the \m[blue]\fBDRBD project web site\fR\m[]\&\s-2\u[1]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD project web site .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v84/drbddisk.80000644000175000017500000000524013027211741020726 0ustar apoikosapoikos'\" t .\" Title: drbddisk .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 15 Oct 2008 .\" Manual: System Administration .\" Source: DRBD 8.3.2 .\" Language: English .\" .TH "DRBDDISK" "8" "15 Oct 2008" "DRBD 8.3.2" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbddisk \- Script to mark devices as primary and mount file systems .SH "SYNOPSIS" .HP \w'\fB/etc/ha\&.d/resource\&.d/drbddisk\fR\ 'u \fB/etc/ha\&.d/resource\&.d/drbddisk\fR [\fIresource\fR] {{start}\ |\ {stop}\ |\ {status}} .SH "INTRODUCTION" .PP The \fB/etc/ha\&.d/resource\&.d/drbddisk\fR script brings the local device of \fIresource\fR into primary role\&. It is designed to be used by Heartbeat\&. .PP In order to use \fB/etc/ha\&.d/resource\&.d/drbddisk\fR you must define a resource, a host, and any other configuration options in the DRBD configuration file\&. See \fB/etc/drbd\&.conf\fR for details\&. If \fIresource\fR is omitted, then all of the resources listed in the config file are affected\&. .SH "VERSION" .sp This document was revised for version 8\&.0\&.14 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbd\fR(8), \fBdrbdsetup\fR(8)\fBdrbdadm\fR(8)\m[blue]\fBDRBD Homepage\fR\m[]\&\s-2\u[1]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD Homepage .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v84/drbd.conf.50000644000175000017500000015165513027211737021015 0ustar apoikosapoikos'\" t .\" Title: drbd.conf .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 6 May 2011 .\" Manual: Configuration Files .\" Source: DRBD 8.4.0 .\" Language: English .\" .TH "DRBD\&.CONF" "5" "6 May 2011" "DRBD 8.4.0" "Configuration Files" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbd.conf \- Configuration file for DRBD\*(Aqs devices .SH "INTRODUCTION" .PP The file \fB/etc/drbd\&.conf\fR is read by \fBdrbdadm\fR\&. .PP The file format was designed as to allow to have a verbatim copy of the file on both nodes of the cluster\&. It is highly recommended to do so in order to keep your configuration manageable\&. The file \fB/etc/drbd\&.conf\fR should be the same on both nodes of the cluster\&. Changes to \fB/etc/drbd\&.conf\fR do not apply immediately\&. .PP By convention the main config contains two include statements\&. The first one includes the file \fB/etc/drbd\&.d/global_common\&.conf\fR, the second one all file with a \fB\&.res\fR suffix\&. .PP .PP \fBExample\ \&1.\ \&A small example\&.res file\fR .sp .if n \{\ .RS 4 .\} .nf resource r0 { net { protocol C; cram\-hmac\-alg sha1; shared\-secret "FooFunFactory"; } disk { resync\-rate 10M; } on alice { volume 0 { device minor 1; disk /dev/sda7; meta\-disk internal; } address 10\&.1\&.1\&.31:7789; } on bob { volume 0 { device minor 1; disk /dev/sda7; meta\-disk internal; } address 10\&.1\&.1\&.32:7789; } } .fi .if n \{\ .RE .\}In this example, there is a single DRBD resource (called r0) which uses protocol C for the connection between its devices\&. It contains a single volume which runs on host \fIalice\fR uses \fI/dev/drbd1\fR as devices for its application, and \fI/dev/sda7\fR as low\-level storage for the data\&. The IP addresses are used to specify the networking interfaces to be used\&. An eventually running resync process should use about 10MByte/second of IO bandwidth\&. This sync\-rate statement is valid for volume 0, but would also be valid for further volumes\&. In this example it assigns full 10MByte/second to each volume\&. .PP There may be multiple resource sections in a single drbd\&.conf file\&. For more examples, please have a look at the \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2\&. .SH "FILE FORMAT" .PP The file consists of sections and parameters\&. A section begins with a keyword, sometimes an additional name, and an opening brace (\(lq{\(rq)\&. A section ends with a closing brace (\(lq}\(rq\&. The braces enclose the parameters\&. .PP section [name] { parameter value; [\&.\&.\&.] } .PP A parameter starts with the identifier of the parameter followed by whitespace\&. Every subsequent character is considered as part of the parameter\*(Aqs value\&. A special case are Boolean parameters which consist only of the identifier\&. Parameters are terminated by a semicolon (\(lq;\(rq)\&. .PP Some parameter values have default units which might be overruled by K, M or G\&. These units are defined in the usual way (K = 2^10 = 1024, M = 1024 K, G = 1024 M)\&. .PP Comments may be placed into the configuration file and must begin with a hash sign (\(lq#\(rq)\&. Subsequent characters are ignored until the end of the line\&. .SS "Sections" .PP \fBskip\fR .RS 4 Comments out chunks of text, even spanning more than one line\&. Characters between the keyword \fBskip\fR and the opening brace (\(lq{\(rq) are ignored\&. Everything enclosed by the braces is skipped\&. This comes in handy, if you just want to comment out some \*(Aq\fBresource [name] {\&.\&.\&.}\fR\*(Aq section: just precede it with \*(Aq\fBskip\fR\*(Aq\&. .RE .PP \fBglobal\fR .RS 4 Configures some global parameters\&. Currently only \fBminor\-count\fR, \fBdialog\-refresh\fR, \fBdisable\-ip\-verification\fR and \fBusage\-count\fR are allowed here\&. You may only have one global section, preferably as the first section\&. .RE .PP \fBcommon\fR .RS 4 All resources inherit the options set in this section\&. The common section might have a \fBstartup\fR, a \fBoptions\fR, a \fBhandlers\fR, a \fBnet\fR and a \fBdisk\fR section\&. .RE .PP \fBresource \fR\fB\fIname\fR\fR .RS 4 Configures a DRBD resource\&. Each resource section needs to have two (or more) \fBon \fR\fB\fIhost\fR\fR sections and may have a \fBstartup\fR, a \fBoptions\fR, a \fBhandlers\fR, a \fBnet\fR and a \fBdisk\fR section\&. It might contain \fBvolume\fRs sections\&. .RE .PP \fBon \fR\fB\fIhost\-name\fR\fR .RS 4 Carries the necessary configuration parameters for a DRBD device of the enclosing resource\&. \fIhost\-name\fR is mandatory and must match the Linux host name (uname \-n) of one of the nodes\&. You may list more than one host name here, in case you want to use the same parameters on several hosts (you\*(Aqd have to move the IP around usually)\&. Or you may list more than two such sections\&. .sp .if n \{\ .RS 4 .\} .nf resource r1 { protocol C; device minor 1; meta\-disk internal; on alice bob { address 10\&.2\&.2\&.100:7801; disk /dev/mapper/some\-san; } on charlie { address 10\&.2\&.2\&.101:7801; disk /dev/mapper/other\-san; } on daisy { address 10\&.2\&.2\&.103:7801; disk /dev/mapper/other\-san\-as\-seen\-from\-daisy; } } .fi .if n \{\ .RE .\} .sp See also the \fBfloating\fR section keyword\&. Required statements in this section: \fBaddress\fR and \fBvolume\fR\&. Note for backward compatibility and convenience it is valid to embed the statements of a single volume directly into the host section\&. .RE .PP \fBvolume \fR\fB\fIvnr\fR\fR .RS 4 Defines a volume within a connection\&. The minor numbers of a replicated volume might be different on different hosts, the volume number (\fIvnr\fR) is what groups them together\&. Required parameters in this section: \fBdevice\fR, \fBdisk\fR, \fBmeta\-disk\fR\&. .RE .PP \fBstacked\-on\-top\-of \fR\fB\fIresource\fR\fR .RS 4 For a stacked DRBD setup (3 or 4 nodes), a \fBstacked\-on\-top\-of\fR is used instead of an \fBon\fR section\&. Required parameters in this section: \fBdevice\fR and \fBaddress\fR\&. .RE .PP \fBfloating \fR\fB\fIAF addr:port\fR\fR .RS 4 Carries the necessary configuration parameters for a DRBD device of the enclosing resource\&. This section is very similar to the \fBon\fR section\&. The difference to the \fBon\fR section is that the matching of the host sections to machines is done by the IP\-address instead of the node name\&. Required parameters in this section: \fBdevice\fR, \fBdisk\fR, \fBmeta\-disk\fR, all of which \fImay\fR be inherited from the resource section, in which case you may shorten this section down to just the address identifier\&. .sp .if n \{\ .RS 4 .\} .nf resource r2 { protocol C; device minor 2; disk /dev/sda7; meta\-disk internal; # short form, device, disk and meta\-disk inherited floating 10\&.1\&.1\&.31:7802; # longer form, only device inherited floating 10\&.1\&.1\&.32:7802 { disk /dev/sdb; meta\-disk /dev/sdc8; } } .fi .if n \{\ .RE .\} .RE .PP \fBdisk\fR .RS 4 This section is used to fine tune DRBD\*(Aqs properties in respect to the low level storage\&. Please refer to \fBdrbdsetup\fR(8) for detailed description of the parameters\&. Optional parameters: \fBon\-io\-error\fR, \fBsize\fR, \fBfencing\fR, \fBdisk\-barrier\fR, \fBdisk\-flushes\fR, \fBdisk\-drain\fR, \fBmd\-flushes\fR, \fBmax\-bio\-bvecs\fR, \fBresync\-rate\fR, \fBresync\-after\fR, \fBal\-extents\fR, \fBal\-updates\fR, \fBc\-plan\-ahead\fR, \fBc\-fill\-target\fR, \fBc\-delay\-target\fR, \fBc\-max\-rate\fR, \fBc\-min\-rate\fR, \fBdisk\-timeout\fR, \fBdiscard\-zeroes\-if\-aligned\fR, \fBrs\-discard\-granularity\fR, \fBread\-balancing\fR\&. .RE .PP \fBnet\fR .RS 4 This section is used to fine tune DRBD\*(Aqs properties\&. Please refer to \fBdrbdsetup\fR(8) for a detailed description of this section\*(Aqs parameters\&. Optional parameters: \fBprotocol\fR, \fBsndbuf\-size\fR, \fBrcvbuf\-size\fR, \fBtimeout\fR, \fBconnect\-int\fR, \fBping\-int\fR, \fBping\-timeout\fR, \fBmax\-buffers\fR, \fBmax\-epoch\-size\fR, \fBko\-count\fR, \fBallow\-two\-primaries\fR, \fBcram\-hmac\-alg\fR, \fBshared\-secret\fR, \fBafter\-sb\-0pri\fR, \fBafter\-sb\-1pri\fR, \fBafter\-sb\-2pri\fR, \fBdata\-integrity\-alg\fR, \fBno\-tcp\-cork\fR, \fBon\-congestion\fR, \fBcongestion\-fill\fR, \fBcongestion\-extents\fR, \fBverify\-alg\fR, \fBuse\-rle\fR, \fBcsums\-alg\fR, \fBsocket\-check\-timeout\fR\&. .RE .PP \fBstartup\fR .RS 4 This section is used to fine tune DRBD\*(Aqs properties\&. Please refer to \fBdrbdsetup\fR(8) for a detailed description of this section\*(Aqs parameters\&. Optional parameters: \fBwfc\-timeout\fR, \fBdegr\-wfc\-timeout\fR, \fBoutdated\-wfc\-timeout\fR, \fBwait\-after\-sb\fR, \fBstacked\-timeouts\fR and \fBbecome\-primary\-on\fR\&. .RE .PP \fBoptions\fR .RS 4 This section is used to fine tune the behaviour of the resource object\&. Please refer to \fBdrbdsetup\fR(8) for a detailed description of this section\*(Aqs parameters\&. Optional parameters: \fBcpu\-mask\fR, and \fBon\-no\-data\-accessible\fR\&. .RE .PP \fBhandlers\fR .RS 4 In this section you can define handlers (executables) that are started by the DRBD system in response to certain events\&. Optional parameters: \fBpri\-on\-incon\-degr\fR, \fBpri\-lost\-after\-sb\fR, \fBpri\-lost\fR, \fBfence\-peer\fR (formerly oudate\-peer), \fBlocal\-io\-error\fR, \fBinitial\-split\-brain\fR, \fBsplit\-brain\fR, \fBbefore\-resync\-target\fR, \fBafter\-resync\-target\fR\&. .sp The interface is done via environment variables: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBDRBD_RESOURCE\fR is the name of the resource .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBDRBD_MINOR\fR is the minor number of the DRBD device, in decimal\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBDRBD_CONF\fR is the path to the primary configuration file; if you split your configuration into multiple files (e\&.g\&. in \fB/etc/drbd\&.conf\&.d/\fR), this will not be helpful\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} \fBDRBD_PEER_AF\fR , \fBDRBD_PEER_ADDRESS\fR , \fBDRBD_PEERS\fR are the address family (e\&.g\&. \fBipv6\fR), the peer\*(Aqs address and hostnames\&. .RE .sp \fBDRBD_PEER\fR is deprecated\&. .sp Please note that not all of these might be set for all handlers, and that some values might not be useable for a \fBfloating\fR definition\&. .RE .SS "Parameters" .PP \fBminor\-count \fR\fB\fIcount\fR\fR .RS 4 \fIcount\fR may be a number from 1 to 1048575\&. .sp \fIMinor\-count\fR is a sizing hint for DRBD\&. It helps to right\-size various memory pools\&. It should be set in the in the same order of magnitude than the actual number of minors you use\&. Per default the module loads with 11 more resources than you have currently in your config but at least 32\&. .RE .PP \fBdialog\-refresh \fR\fB\fItime\fR\fR .RS 4 \fItime\fR may be 0 or a positive number\&. .sp The user dialog redraws the second count every \fItime\fR seconds (or does no redraws if \fItime\fR is 0)\&. The default value is 1\&. .RE .PP \fBdisable\-ip\-verification\fR .RS 4 Use \fIdisable\-ip\-verification\fR if, for some obscure reasons, drbdadm can/might not use \fBip\fR or \fBifconfig\fR to do a sanity check for the IP address\&. You can disable the IP verification with this option\&. .RE .PP \fBusage\-count \fR\fB\fIval\fR\fR .RS 4 Please participate in \m[blue]\fBDRBD\*(Aqs online usage counter\fR\m[]\&\s-2\u[2]\d\s+2\&. The most convenient way to do so is to set this option to \fByes\fR\&. Valid options are: \fByes\fR, \fBno\fR and \fBask\fR\&. .RE .PP \fBprotocol \fR\fB\fIprot\-id\fR\fR .RS 4 On the TCP/IP link the specified \fIprotocol\fR is used\&. Valid protocol specifiers are A, B, and C\&. .sp Protocol A: write IO is reported as completed, if it has reached local disk and local TCP send buffer\&. .sp Protocol B: write IO is reported as completed, if it has reached local disk and remote buffer cache\&. .sp Protocol C: write IO is reported as completed, if it has reached both local and remote disk\&. .RE .PP \fBdevice \fR\fB\fIname\fR\fR\fB minor \fR\fB\fInr\fR\fR .RS 4 The name of the block device node of the resource being described\&. You must use this device with your application (file system) and you must not use the low level block device which is specified with the \fBdisk\fR parameter\&. .sp One can ether omit the \fIname\fR or \fBminor\fR and the \fIminor number\fR\&. If you omit the \fIname\fR a default of /dev/drbd\fIminor\fR will be used\&. .sp Udev will create additional symlinks in /dev/drbd/by\-res and /dev/drbd/by\-disk\&. .RE .PP \fBdisk \fR\fB\fIname\fR\fR .RS 4 DRBD uses this block device to actually store and retrieve the data\&. Never access such a device while DRBD is running on top of it\&. This also holds true for \fBdumpe2fs\fR(8) and similar commands\&. .RE .PP \fBaddress \fR\fB\fIAF addr:port\fR\fR .RS 4 A resource needs one \fIIP\fR address per device, which is used to wait for incoming connections from the partner device respectively to reach the partner device\&. \fIAF\fR must be one of \fBipv4\fR, \fBipv6\fR, \fBssocks\fR or \fBsdp\fR (for compatibility reasons \fBsci\fR is an alias for \fBssocks\fR)\&. It may be omited for IPv4 addresses\&. The actual IPv6 address that follows the \fBipv6\fR keyword must be placed inside brackets: ipv6 [fd01:2345:6789:abcd::1]:7800\&. .sp Each DRBD resource needs a TCP \fIport\fR which is used to connect to the node\*(Aqs partner device\&. Two different DRBD resources may not use the same \fIaddr:port\fR combination on the same node\&. .RE .PP \fBmeta\-disk internal\fR, .br \fBmeta\-disk \fR\fB\fIdevice\fR\fR, .br \fBmeta\-disk \fR\fB\fIdevice\fR\fR\fB [\fR\fB\fIindex\fR\fR\fB]\fR .RS 4 Internal means that the last part of the backing device is used to store the meta\-data\&. The size of the meta\-data is computed based on the size of the device\&. .sp When a \fIdevice\fR is specified, either with or without an \fIindex\fR, DRBD stores the meta\-data on this device\&. Without \fIindex\fR, the size of the meta\-data is determined by the size of the data device\&. This is usually used with LVM, which allows to have many variable sized block devices\&. The meta\-data size is 36kB + Backing\-Storage\-size / 32k, rounded up to the next 4kb boundary\&. (Rule of the thumb: 32kByte per 1GByte of storage, rounded up to the next MB\&.) .sp When an \fIindex\fR is specified, each index number refers to a fixed slot of meta\-data of 128 MB, which allows a maximum data size of 4 TiB\&. This way, multiple DBRD devices can share the same meta\-data device\&. For example, if /dev/sde6[0] and /dev/sde6[1] are used, /dev/sde6 must be at least 256 MB big\&. Because of the hard size limit, use of meta\-disk indexes is discouraged\&. .RE .PP \fBon\-io\-error \fR\fB\fIhandler\fR\fR .RS 4 \fIhandler\fR is taken, if the lower level device reports io\-errors to the upper layers\&. .sp \fIhandler\fR may be \fBpass_on\fR, \fBcall\-local\-io\-error\fR or \fBdetach\&.\fR .sp \fBpass_on\fR: The node downgrades the disk status to inconsistent, marks the erroneous block as inconsistent in the bitmap and retries the IO on the remote node\&. .sp \fBcall\-local\-io\-error\fR: Call the handler script \fBlocal\-io\-error\fR\&. .sp \fBdetach\fR: The node drops its low level device, and continues in diskless mode\&. .RE .PP \fBfencing \fR\fB\fIfencing_policy\fR\fR .RS 4 By \fBfencing\fR we understand preventive measures to avoid situations where both nodes are primary and disconnected (AKA split brain)\&. .sp Valid fencing policies are: .PP \fBdont\-care\fR .RS 4 This is the default policy\&. No fencing actions are taken\&. .RE .PP \fBresource\-only\fR .RS 4 If a node becomes a disconnected primary, it tries to fence the peer\*(Aqs disk\&. This is done by calling the \fBfence\-peer\fR handler\&. The handler is supposed to reach the other node over alternative communication paths and call \*(Aq\fBdrbdadm outdate res\fR\*(Aq there\&. .RE .PP \fBresource\-and\-stonith\fR .RS 4 If a node becomes a disconnected primary, it freezes all its IO operations and calls its fence\-peer handler\&. The fence\-peer handler is supposed to reach the peer over alternative communication paths and call \*(Aqdrbdadm outdate res\*(Aq there\&. In case it cannot reach the peer it should stonith the peer\&. IO is resumed as soon as the situation is resolved\&. In case your handler fails, you can resume IO with the \fBresume\-io\fR command\&. .RE .RE .PP \fBdisk\-barrier\fR, .br \fBdisk\-flushes\fR, .br \fBdisk\-drain\fR .RS 4 DRBD has four implementations to express write\-after\-write dependencies to its backing storage device\&. DRBD will use the first method that is supported by the backing storage device and that is not disabled\&. By default the \fIflush\fR method is used\&. .sp Since drbd\-8\&.4\&.2 \fBdisk\-barrier\fR is disabled by default because since linux\-2\&.6\&.36 (or 2\&.6\&.32 RHEL6) there is no reliable way to determine if queuing of IO\-barriers works\&. \fIDangerous\fR only enable if you are told so by one that knows for sure\&. .sp When selecting the method you should not only base your decision on the measurable performance\&. In case your backing storage device has a volatile write cache (plain disks, RAID of plain disks) you should use one of the first two\&. In case your backing storage device has battery\-backed write cache you may go with option 3\&. Option 4 (disable everything, use "none") \fIis dangerous\fR on most IO stacks, may result in write\-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles\&. \fIDo not use\fR \fBno\-disk\-drain\fR\&. .sp Unfortunately device mapper (LVM) might not support barriers\&. .sp The letter after "wo:" in /proc/drbd indicates with method is currently in use for a device: \fBb\fR, \fBf\fR, \fBd\fR, \fBn\fR\&. The implementations are: .PP barrier .RS 4 The first requires that the driver of the backing storage device support barriers (called \*(Aqtagged command queuing\*(Aq in SCSI and \*(Aqnative command queuing\*(Aq in SATA speak)\&. The use of this method can be enabled by setting the \fBdisk\-barrier\fR options to \fByes\fR\&. .RE .PP flush .RS 4 The second requires that the backing device support disk flushes (called \*(Aqforce unit access\*(Aq in the drive vendors speak)\&. The use of this method can be disabled setting \fBdisk\-flushes\fR to \fBno\fR\&. .RE .PP drain .RS 4 The third method is simply to let write requests drain before write requests of a new reordering domain are issued\&. This was the only implementation before 8\&.0\&.9\&. .RE .PP none .RS 4 The fourth method is to not express write\-after\-write dependencies to the backing store at all, by also specifying \fBno\-disk\-drain\fR\&. This \fIis dangerous\fR on most IO stacks, may result in write\-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles\&. \fIDo not use\fR \fBno\-disk\-drain\fR\&. .RE .RE .PP \fBmd\-flushes\fR .RS 4 Disables the use of disk flushes and barrier BIOs when accessing the meta data device\&. See the notes on \fBdisk\-flushes\fR\&. .RE .PP \fBmax\-bio\-bvecs\fR .RS 4 In some special circumstances the device mapper stack manages to pass BIOs to DRBD that violate the constraints that are set forth by DRBD\*(Aqs merge_bvec() function and which have more than one bvec\&. A known example is: phys\-disk \-> DRBD \-> LVM \-> Xen \-> misaligned partition (63) \-> DomU FS\&. Then you might see "bio would need to, but cannot, be split:" in the Dom0\*(Aqs kernel log\&. .sp The best workaround is to proper align the partition within the VM (E\&.g\&. start it at sector 1024)\&. This costs 480 KiB of storage\&. Unfortunately the default of most Linux partitioning tools is to start the first partition at an odd number (63)\&. Therefore most distribution\*(Aqs install helpers for virtual linux machines will end up with misaligned partitions\&. The second best workaround is to limit DRBD\*(Aqs max bvecs per BIO (= \fBmax\-bio\-bvecs\fR) to 1, but that might cost performance\&. .sp The default value of \fBmax\-bio\-bvecs\fR is 0, which means that there is no user imposed limitation\&. .RE .PP \fBdisk\-timeout\fR .RS 4 If the lower\-level device on which a DRBD device stores its data does not finish an I/O request within the defined \fBdisk\-timeout\fR, DRBD treats this as a failure\&. The lower\-level device is detached, and the device\*(Aqs disk state advances to Diskless\&. If DRBD is connected to one or more peers, the failed request is passed on to one of them\&. .sp This option is \fIdangerous and may lead to kernel panic!\fR .sp "Aborting" requests, or force\-detaching the disk, is intended for completely blocked/hung local backing devices which do no longer complete requests at all, not even do error completions\&. In this situation, usually a hard\-reset and failover is the only way out\&. .sp By "aborting", basically faking a local error\-completion, we allow for a more graceful swichover by cleanly migrating services\&. Still the affected node has to be rebooted "soon"\&. .sp By completing these requests, we allow the upper layers to re\-use the associated data pages\&. .sp If later the local backing device "recovers", and now DMAs some data from disk into the original request pages, in the best case it will just put random data into unused pages; but typically it will corrupt meanwhile completely unrelated data, causing all sorts of damage\&. .sp Which means delayed successful completion, especially for READ requests, is a reason to panic()\&. We assume that a delayed *error* completion is OK, though we still will complain noisily about it\&. .sp The default value of \fBdisk\-timeout\fR is 0, which stands for an infinite timeout\&. Timeouts are specified in units of 0\&.1 seconds\&. This option is available since DRBD 8\&.3\&.12\&. .RE .PP \fBdiscard\-zeroes\-if\-aligned \fR\fB{yes | no}\fR .RS 4 There are several aspects to discard/trim/unmap support on linux block devices\&. Even if discard is supported in general, it may fail silently, or may partially ignore discard requests\&. Devices also announce whether reading from unmapped blocks returns defined data (usually zeroes), or undefined data (possibly old data, possibly garbage)\&. .sp If on different nodes, DRBD is backed by devices with differing discard characteristics, discards may lead to data divergence (old data or garbage left over on one backend, zeroes due to unmapped areas on the other backend)\&. Online verify would now potentially report tons of spurious differences\&. While probably harmless for most use cases (fstrim on a file system), DRBD cannot have that\&. .sp To play safe, we have to disable discard support, if our local backend (on a Primary) does not support "discard_zeroes_data=true"\&. We also have to translate discards to explicit zero\-out on the receiving side, unless the receiving side (Secondary) supports "discard_zeroes_data=true", thereby allocating areas what were supposed to be unmapped\&. .sp There are some devices (notably the LVM/DM thin provisioning) that are capable of discard, but announce discard_zeroes_data=false\&. In the case of DM\-thin, discards aligned to the chunk size will be unmapped, and reading from unmapped sectors will return zeroes\&. However, unaligned partial head or tail areas of discard requests will be silently ignored\&. .sp If we now add a helper to explicitly zero\-out these unaligned partial areas, while passing on the discard of the aligned full chunks, we effectively achieve discard_zeroes_data=true on such devices\&. .sp Setting \fBdiscard\-zeroes\-if\-aligned\fR to \fByes\fR will allow DRBD to use discards, and to announce discard_zeroes_data=true, even on backends that announce discard_zeroes_data=false\&. .sp Setting \fBdiscard\-zeroes\-if\-aligned\fR to \fBno\fR will cause DRBD to always fall\-back to zero\-out on the receiving side, and to not even announce discard capabilities on the Primary, if the respective backend announces discard_zeroes_data=false\&. .sp We used to ignore the discard_zeroes_data setting completely\&. To not break established and expected behaviour, and suddenly cause fstrim on thin\-provisioned LVs to run out\-of\-space instead of freeing up space, the default value is \fByes\fR\&. .sp This option is available since 8\&.4\&.7\&. .RE .PP \fBread\-balancing \fR\fB\fImethod\fR\fR .RS 4 The supported \fImethods\fR for load balancing of read requests are \fBprefer\-local\fR, \fBprefer\-remote\fR, \fBround\-robin\fR, \fBleast\-pending\fR, \fBwhen\-congested\-remote\fR, \fB32K\-striping\fR, \fB64K\-striping\fR, \fB128K\-striping\fR, \fB256K\-striping\fR, \fB512K\-striping\fR and \fB1M\-striping\fR\&. .sp The default value of is \fBprefer\-local\fR\&. This option is available since 8\&.4\&.1\&. .RE .PP \fBrs\-discard\-granularity \fR\fB\fIbyte\fR\fR .RS 4 When \fBrs\-discard\-granularity\fR is set to a non zero, positive value then DRBD tries to do a resync operation in requests of this size\&. In case such a block contains only zero bytes on the sync source node, the sync target node will issue a discard/trim/unmap command for the area\&. .sp The value is constrained by the discard granularity of the backing block device\&. In case \fBrs\-discard\-granularity\fR is not a multiplier of the discard granularity of the backing block device DRBD rounds it up\&. The feature only gets active if the backing block device reads back zeroes after a discard command\&. .sp The default value of is 0\&. This option is available since 8\&.4\&.7\&. .RE .PP \fBsndbuf\-size \fR\fB\fIsize\fR\fR .RS 4 \fIsize\fR is the size of the TCP socket send buffer\&. The default value is 0, i\&.e\&. autotune\&. You can specify smaller or larger values\&. Larger values are appropriate for reasonable write throughput with protocol A over high latency networks\&. Values below 32K do not make sense\&. Since 8\&.0\&.13 resp\&. 8\&.2\&.7, setting the \fIsize\fR value to 0 means that the kernel should autotune this\&. .RE .PP \fBrcvbuf\-size \fR\fB\fIsize\fR\fR .RS 4 \fIsize\fR is the size of the TCP socket receive buffer\&. The default value is 0, i\&.e\&. autotune\&. You can specify smaller or larger values\&. Usually this should be left at its default\&. Setting the \fIsize\fR value to 0 means that the kernel should autotune this\&. .RE .PP \fBtimeout \fR\fB\fItime\fR\fR .RS 4 If the partner node fails to send an expected response packet within \fItime\fR tenths of a second, the partner node is considered dead and therefore the TCP/IP connection is abandoned\&. This must be lower than \fIconnect\-int\fR and \fIping\-int\fR\&. The default value is 60 = 6 seconds, the unit 0\&.1 seconds\&. .RE .PP \fBconnect\-int \fR\fB\fItime\fR\fR .RS 4 In case it is not possible to connect to the remote DRBD device immediately, DRBD keeps on trying to connect\&. With this option you can set the time between two retries\&. The default value is 10 seconds, the unit is 1 second\&. .RE .PP \fBping\-int \fR\fB\fItime\fR\fR .RS 4 If the TCP/IP connection linking a DRBD device pair is idle for more than \fItime\fR seconds, DRBD will generate a keep\-alive packet to check if its partner is still alive\&. The default is 10 seconds, the unit is 1 second\&. .RE .PP \fBping\-timeout \fR\fB\fItime\fR\fR .RS 4 The time the peer has time to answer to a keep\-alive packet\&. In case the peer\*(Aqs reply is not received within this time period, it is considered as dead\&. The default value is 500ms, the default unit are tenths of a second\&. .RE .PP \fBmax\-buffers \fR\fB\fInumber\fR\fR .RS 4 Limits the memory usage per DRBD minor device on the receiving side, or for internal buffers during resync or online\-verify\&. Unit is PAGE_SIZE, which is 4 KiB on most systems\&. The minimum possible setting is hard coded to 32 (=128 KiB)\&. These buffers are used to hold data blocks while they are written to/read from disk\&. To avoid possible distributed deadlocks on congestion, this setting is used as a throttle threshold rather than a hard limit\&. Once more than max\-buffers pages are in use, further allocation from this pool is throttled\&. You want to increase max\-buffers if you cannot saturate the IO backend on the receiving side\&. .RE .PP \fBko\-count \fR\fB\fInumber\fR\fR .RS 4 In case the secondary node fails to complete a single write request for \fIcount\fR times the \fItimeout\fR, it is expelled from the cluster\&. (I\&.e\&. the primary node will kill and restart the connection\&.) To disable this feature, you should explicitly set it to 0; defaults may change between versions\&. .RE .PP \fBmax\-epoch\-size \fR\fB\fInumber\fR\fR .RS 4 The highest number of data blocks between two write barriers\&. If you set this smaller than 10, you might decrease your performance\&. .RE .PP \fBallow\-two\-primaries\fR .RS 4 With this option set you may assign the primary role to both nodes\&. You only should use this option if you use a shared storage file system on top of DRBD\&. At the time of writing the only ones are: OCFS2 and GFS\&. If you use this option with any other file system, you are going to crash your nodes and to corrupt your data! .RE .PP \fBunplug\-watermark \fR\fB\fInumber\fR\fR .RS 4 This setting has no effect with recent kernels that use explicit on\-stack plugging (upstream Linux kernel 2\&.6\&.39, distributions may have backported)\&. .sp When the number of pending write requests on the standby (secondary) node exceeds the \fBunplug\-watermark\fR, we trigger the request processing of our backing storage device\&. Some storage controllers deliver better performance with small values, others deliver best performance when the value is set to the same value as max\-buffers, yet others don\*(Aqt feel much effect at all\&. Minimum 16, default 128, maximum 131072\&. .RE .PP \fBcram\-hmac\-alg\fR .RS 4 You need to specify the HMAC algorithm to enable peer authentication at all\&. You are strongly encouraged to use peer authentication\&. The HMAC algorithm will be used for the challenge response authentication of the peer\&. You may specify any digest algorithm that is named in \fB/proc/crypto\fR\&. .RE .PP \fBshared\-secret\fR .RS 4 The shared secret used in peer authentication\&. May be up to 64 characters\&. Note that peer authentication is disabled as long as no \fBcram\-hmac\-alg\fR (see above) is specified\&. .RE .PP \fBafter\-sb\-0pri \fR \fIpolicy\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBdiscard\-younger\-primary\fR .RS 4 Auto sync from the node that was primary before the split\-brain situation happened\&. .RE .PP \fBdiscard\-older\-primary\fR .RS 4 Auto sync from the node that became primary as second during the split\-brain situation\&. .RE .PP \fBdiscard\-zero\-changes\fR .RS 4 In case one node did not write anything since the split brain became evident, sync from the node that wrote something to the node that did not write anything\&. In case none wrote anything this policy uses a random decision to perform a "resync" of 0 blocks\&. In case both have written something this policy disconnects the nodes\&. .RE .PP \fBdiscard\-least\-changes\fR .RS 4 Auto sync from the node that touched more blocks during the split brain situation\&. .RE .PP \fBdiscard\-node\-NODENAME\fR .RS 4 Auto sync to the named node\&. .RE .RE .PP \fBafter\-sb\-1pri \fR \fIpolicy\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBconsensus\fR .RS 4 Discard the version of the secondary if the outcome of the \fBafter\-sb\-0pri\fR algorithm would also destroy the current secondary\*(Aqs data\&. Otherwise disconnect\&. .RE .PP \fBviolently\-as0p\fR .RS 4 Always take the decision of the \fBafter\-sb\-0pri\fR algorithm, even if that causes an erratic change of the primary\*(Aqs view of the data\&. This is only useful if you use a one\-node FS (i\&.e\&. not OCFS2 or GFS) with the \fBallow\-two\-primaries\fR flag, \fIAND\fR if you really know what you are doing\&. This is \fIDANGEROUS and MAY CRASH YOUR MACHINE\fR if you have an FS mounted on the primary node\&. .RE .PP \fBdiscard\-secondary\fR .RS 4 Discard the secondary\*(Aqs version\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Always honor the outcome of the \fBafter\-sb\-0pri \fR algorithm\&. In case it decides the current secondary has the right data, it calls the "pri\-lost\-after\-sb" handler on the current primary\&. .RE .RE .PP \fBafter\-sb\-2pri \fR \fIpolicy\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBviolently\-as0p\fR .RS 4 Always take the decision of the \fBafter\-sb\-0pri\fR algorithm, even if that causes an erratic change of the primary\*(Aqs view of the data\&. This is only useful if you use a one\-node FS (i\&.e\&. not OCFS2 or GFS) with the \fBallow\-two\-primaries\fR flag, \fIAND\fR if you really know what you are doing\&. This is \fIDANGEROUS and MAY CRASH YOUR MACHINE\fR if you have an FS mounted on the primary node\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Call the "pri\-lost\-after\-sb" helper program on one of the machines\&. This program is expected to reboot the machine, i\&.e\&. make it secondary\&. .RE .RE .PP \fBalways\-asbp\fR .RS 4 Normally the automatic after\-split\-brain policies are only used if current states of the UUIDs do not indicate the presence of a third node\&. .sp With this option you request that the automatic after\-split\-brain policies are used as long as the data sets of the nodes are somehow related\&. This might cause a full sync, if the UUIDs indicate the presence of a third node\&. (Or double faults led to strange UUID sets\&.) .RE .PP \fBrr\-conflict \fR \fIpolicy\fR .RS 4 This option helps to solve the cases when the outcome of the resync decision is incompatible with the current role assignment in the cluster\&. .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBviolently\fR .RS 4 Sync to the primary node is allowed, violating the assumption that data on a block device are stable for one of the nodes\&. \fIDangerous, do not use\&.\fR .RE .PP \fBcall\-pri\-lost\fR .RS 4 Call the \fBpri\-lost\-after\-sb\fR helper program on one of the machines unless that machine can demote to secondary\&. The helper program is expected to reboot the machine, which brings the node into a secondary role\&. Which machine runs the helper program is determined by the \fBafter\-sb\-0pri\fR strategy\&. .RE .RE .PP \fBdata\-integrity\-alg \fR \fIalg\fR .RS 4 DRBD can ensure the data integrity of the user\*(Aqs data on the network by comparing hash values\&. Normally this is ensured by the 16 bit checksums in the headers of TCP/IP packets\&. .sp This option can be set to any of the kernel\*(Aqs data digest algorithms\&. In a typical kernel configuration you should have at least one of \fBmd5\fR, \fBsha1\fR, and \fBcrc32c\fR available\&. By default this is not enabled\&. .sp See also the notes on data integrity\&. .RE .PP \fBtcp\-cork\fR .RS 4 DRBD usually uses the TCP socket option TCP_CORK to hint to the network stack when it can expect more data, and when it should flush out what it has in its send queue\&. It turned out that there is at least one network stack that performs worse when one uses this hinting method\&. Therefore we introducted this option\&. By setting \fBtcp\-cork\fR to \fBno\fR you can disable the setting and clearing of the TCP_CORK socket option by DRBD\&. .RE .PP \fBon\-congestion \fR\fB\fIcongestion_policy\fR\fR, .br \fBcongestion\-fill \fR\fB\fIfill_threshold\fR\fR, .br \fBcongestion\-extents \fR\fB\fIactive_extents_threshold\fR\fR .RS 4 By default DRBD blocks when the available TCP send queue becomes full\&. That means it will slow down the application that generates the write requests that cause DRBD to send more data down that TCP connection\&. .sp When DRBD is deployed with DRBD\-proxy it might be more desirable that DRBD goes into AHEAD/BEHIND mode shortly before the send queue becomes full\&. In AHEAD/BEHIND mode DRBD does no longer replicate data, but still keeps the connection open\&. .sp The advantage of the AHEAD/BEHIND mode is that the application is not slowed down, even if DRBD\-proxy\*(Aqs buffer is not sufficient to buffer all write requests\&. The downside is that the peer node falls behind, and that a resync will be necessary to bring it back into sync\&. During that resync the peer node will have an inconsistent disk\&. .sp Available \fIcongestion_policy\fRs are \fBblock\fR and \fBpull\-ahead\fR\&. The default is \fBblock\fR\&. \fIFill_threshold\fR might be in the range of 0 to 10GiBytes\&. The default is 0 which disables the check\&. \fIActive_extents_threshold\fR has the same limits as \fBal\-extents\fR\&. .sp The AHEAD/BEHIND mode and its settings are available since DRBD 8\&.3\&.10\&. .RE .PP \fBwfc\-timeout \fR\fB\fItime\fR\fR .RS 4 Wait for connection timeout\&. The init script \fBdrbd\fR(8) blocks the boot process until the DRBD resources are connected\&. When the cluster manager starts later, it does not see a resource with internal split\-brain\&. In case you want to limit the wait time, do it here\&. Default is 0, which means unlimited\&. The unit is seconds\&. .RE .PP \fBdegr\-wfc\-timeout \fR\fB\fItime\fR\fR .RS 4 Wait for connection timeout, if this node was a degraded cluster\&. In case a degraded cluster (= cluster with only one node left) is rebooted, this timeout value is used instead of wfc\-timeout, because the peer is less likely to show up in time, if it had been dead before\&. Value 0 means unlimited\&. .RE .PP \fBoutdated\-wfc\-timeout \fR\fB\fItime\fR\fR .RS 4 Wait for connection timeout, if the peer was outdated\&. In case a degraded cluster (= cluster with only one node left) with an outdated peer disk is rebooted, this timeout value is used instead of wfc\-timeout, because the peer is not allowed to become primary in the meantime\&. Value 0 means unlimited\&. .RE .PP \fBwait\-after\-sb\fR .RS 4 By setting this option you can make the init script to continue to wait even if the device pair had a split brain situation and therefore refuses to connect\&. .RE .PP \fBbecome\-primary\-on \fR\fB\fInode\-name\fR\fR .RS 4 Sets on which node the device should be promoted to primary role by the init script\&. The \fInode\-name\fR might either be a host name or the keyword \fBboth\fR\&. When this option is not set the devices stay in secondary role on both nodes\&. Usually one delegates the role assignment to a cluster manager (e\&.g\&. heartbeat)\&. .RE .PP \fBstacked\-timeouts\fR .RS 4 Usually \fBwfc\-timeout\fR and \fBdegr\-wfc\-timeout\fR are ignored for stacked devices, instead twice the amount of \fBconnect\-int\fR is used for the connection timeouts\&. With the \fBstacked\-timeouts\fR keyword you disable this, and force DRBD to mind the \fBwfc\-timeout\fR and \fBdegr\-wfc\-timeout\fR statements\&. Only do that if the peer of the stacked resource is usually not available or will usually not become primary\&. By using this option incorrectly, you run the risk of causing unexpected split brain\&. .RE .PP \fBresync\-rate \fR\fB\fIrate\fR\fR .RS 4 To ensure a smooth operation of the application on top of DRBD, it is possible to limit the bandwidth which may be used by background synchronizations\&. The default is 250 KB/sec, the default unit is KB/sec\&. Optional suffixes K, M, G are allowed\&. .RE .PP \fBuse\-rle\fR .RS 4 During resync\-handshake, the dirty\-bitmaps of the nodes are exchanged and merged (using bit\-or), so the nodes will have the same understanding of which blocks are dirty\&. On large devices, the fine grained dirty\-bitmap can become large as well, and the bitmap exchange can take quite some time on low\-bandwidth links\&. .sp Because the bitmap typically contains compact areas where all bits are unset (clean) or set (dirty), a simple run\-length encoding scheme can considerably reduce the network traffic necessary for the bitmap exchange\&. .sp For backward compatibilty reasons, and because on fast links this possibly does not improve transfer time but consumes cpu cycles, this defaults to off\&. .RE .PP \fBsocket\-check\-timeout \fR\fB\fIvalue\fR\fR .RS 4 In setups involving a DRBD\-proxy and connections that experience a lot of buffer\-bloat it might be necessary to set \fBping\-timeout\fR to an unusual high value\&. By default DRBD uses the same value to wait if a newly established TCP\-connection is stable\&. Since the DRBD\-proxy is usually located in the same data center such a long wait time may hinder DRBD\*(Aqs connect process\&. .sp In such setups \fBsocket\-check\-timeout\fR should be set to at least to the round trip time between DRBD and DRBD\-proxy\&. I\&.e\&. in most cases to 1\&. .sp The default unit is tenths of a second, the default value is 0 (which causes DRBD to use the value of \fBping\-timeout\fR instead)\&. Introduced in 8\&.4\&.5\&. .RE .PP \fBresync\-after \fR\fB\fIres\-name\fR\fR .RS 4 By default, resynchronization of all devices would run in parallel\&. By defining a resync\-after dependency, the resynchronization of this resource will start only if the resource \fIres\-name\fR is already in connected state (i\&.e\&., has finished its resynchronization)\&. .RE .PP \fBal\-extents \fR\fB\fIextents\fR\fR .RS 4 DRBD automatically performs hot area detection\&. With this parameter you control how big the hot area (= active set) can get\&. Each extent marks 4M of the backing storage (= low\-level device)\&. In case a primary node leaves the cluster unexpectedly, the areas covered by the active set must be resynced upon rejoining of the failed node\&. The data structure is stored in the meta\-data area, therefore each change of the active set is a write operation to the meta\-data device\&. A higher number of extents gives longer resync times but less updates to the meta\-data\&. The default number of \fIextents\fR is 1237\&. (Minimum: 7, Maximum: 65534) .sp Note that the effective maximum may be smaller, depending on how you created the device meta data, see also \fBdrbdmeta\fR(8)\&. The effective maximum is 919 * (available on\-disk activity\-log ring\-buffer area/4kB \-1), the default 32kB ring\-buffer effects a maximum of 6433 (covers more than 25 GiB of data)\&. We recommend to keep this well within the amount your backend storage and replication link are able to resync inside of about 5 minutes\&. .RE .PP \fBal\-updates \fR\fB{yes | no}\fR .RS 4 DRBD\*(Aqs activity log transaction writing makes it possible, that after the crash of a primary node a partial (bit\-map based) resync is sufficient to bring the node back to up\-to\-date\&. Setting \fBal\-updates\fR to \fBno\fR might increase normal operation performance but causes DRBD to do a full resync when a crashed primary gets reconnected\&. The default value is \fByes\fR\&. .RE .PP \fBverify\-alg \fR\fB\fIhash\-alg\fR\fR .RS 4 During online verification (as initiated by the \fBverify\fR sub\-command), rather than doing a bit\-wise comparison, DRBD applies a hash function to the contents of every block being verified, and compares that hash with the peer\&. This option defines the hash algorithm being used for that purpose\&. It can be set to any of the kernel\*(Aqs data digest algorithms\&. In a typical kernel configuration you should have at least one of \fBmd5\fR, \fBsha1\fR, and \fBcrc32c\fR available\&. By default this is not enabled; you must set this option explicitly in order to be able to use on\-line device verification\&. .sp See also the notes on data integrity\&. .RE .PP \fBcsums\-alg \fR\fB\fIhash\-alg\fR\fR .RS 4 A resync process sends all marked data blocks from the source to the destination node, as long as no \fBcsums\-alg\fR is given\&. When one is specified the resync process exchanges hash values of all marked blocks first, and sends only those data blocks that have different hash values\&. .sp This setting is useful for DRBD setups with low bandwidth links\&. During the restart of a crashed primary node, all blocks covered by the activity log are marked for resync\&. But a large part of those will actually be still in sync, therefore using \fBcsums\-alg\fR will lower the required bandwidth in exchange for CPU cycles\&. .RE .PP \fBc\-plan\-ahead \fR\fB\fIplan_time\fR\fR, .br \fBc\-fill\-target \fR\fB\fIfill_target\fR\fR, .br \fBc\-delay\-target \fR\fB\fIdelay_target\fR\fR, .br \fBc\-max\-rate \fR\fB\fImax_rate\fR\fR .RS 4 The dynamic resync speed controller gets enabled with setting \fIplan_time\fR to a positive value\&. It aims to fill the buffers along the data path with either a constant amount of data \fIfill_target\fR, or aims to have a constant delay time of \fIdelay_target\fR along the path\&. The controller has an upper bound of \fImax_rate\fR\&. .sp By \fIplan_time\fR the agility of the controller is configured\&. Higher values yield for slower/lower responses of the controller to deviation from the target value\&. It should be at least 5 times RTT\&. For regular data paths a \fIfill_target\fR in the area of 4k to 100k is appropriate\&. For a setup that contains drbd\-proxy it is advisable to use \fIdelay_target\fR instead\&. Only when \fIfill_target\fR is set to 0 the controller will use \fIdelay_target\fR\&. 5 times RTT is a reasonable starting value\&. \fIMax_rate\fR should be set to the bandwidth available between the DRBD\-hosts and the machines hosting DRBD\-proxy, or to the available disk\-bandwidth\&. .sp The default value of \fIplan_time\fR is 0, the default unit is 0\&.1 seconds\&. \fIFill_target\fR has 0 and sectors as default unit\&. \fIDelay_target\fR has 1 (100ms) and 0\&.1 as default unit\&. \fIMax_rate\fR has 10240 (100MiB/s) and KiB/s as default unit\&. .sp The dynamic resync speed controller and its settings are available since DRBD 8\&.3\&.9\&. .RE .PP \fBc\-min\-rate \fR\fB\fImin_rate\fR\fR .RS 4 A node that is primary and sync\-source has to schedule application IO requests and resync IO requests\&. The \fImin_rate\fR tells DRBD use only up to min_rate for resync IO and to dedicate all other available IO bandwidth to application requests\&. .sp Note: The value 0 has a special meaning\&. It disables the limitation of resync IO completely, which might slow down application IO considerably\&. Set it to a value of 1, if you prefer that resync IO never slows down application IO\&. .sp Note: Although the name might suggest that it is a lower bound for the dynamic resync speed controller, it is not\&. If the DRBD\-proxy buffer is full, the dynamic resync speed controller is free to lower the resync speed down to 0, completely independent of the \fBc\-min\-rate\fR setting\&. .sp \fIMin_rate\fR has 4096 (4MiB/s) and KiB/s as default unit\&. .RE .PP \fBon\-no\-data\-accessible \fR\fB\fIond\-policy\fR\fR .RS 4 This setting controls what happens to IO requests on a degraded, disk less node (I\&.e\&. no data store is reachable)\&. The available policies are \fBio\-error\fR and \fBsuspend\-io\fR\&. .sp If \fIond\-policy\fR is set to \fBsuspend\-io\fR you can either resume IO by attaching/connecting the last lost data storage, or by the \fBdrbdadm resume\-io \fR\fB\fIres\fR\fR command\&. The latter will result in IO errors of course\&. .sp The default is \fBio\-error\fR\&. This setting is available since DRBD 8\&.3\&.9\&. .RE .PP \fBcpu\-mask \fR\fB\fIcpu\-mask\fR\fR .RS 4 Sets the cpu\-affinity\-mask for DRBD\*(Aqs kernel threads of this device\&. The default value of \fIcpu\-mask\fR is 0, which means that DRBD\*(Aqs kernel threads should be spread over all CPUs of the machine\&. This value must be given in hexadecimal notation\&. If it is too big it will be truncated\&. .RE .PP \fBpri\-on\-incon\-degr \fR\fB\fIcmd\fR\fR .RS 4 This handler is called if the node is primary, degraded and if the local copy of the data is inconsistent\&. .RE .PP \fBpri\-lost\-after\-sb \fR\fB\fIcmd\fR\fR .RS 4 The node is currently primary, but lost the after\-split\-brain auto recovery procedure\&. As as consequence, it should be abandoned\&. .RE .PP \fBpri\-lost \fR\fB\fIcmd\fR\fR .RS 4 The node is currently primary, but DRBD\*(Aqs algorithm thinks that it should become sync target\&. As a consequence it should give up its primary role\&. .RE .PP \fBfence\-peer \fR\fB\fIcmd\fR\fR .RS 4 The handler is part of the \fBfencing\fR mechanism\&. This handler is called in case the node needs to fence the peer\*(Aqs disk\&. It should use other communication paths than DRBD\*(Aqs network link\&. .RE .PP \fBlocal\-io\-error \fR\fB\fIcmd\fR\fR .RS 4 DRBD got an IO error from the local IO subsystem\&. .RE .PP \fBinitial\-split\-brain \fR\fB\fIcmd\fR\fR .RS 4 DRBD has connected and detected a split brain situation\&. This handler can alert someone in all cases of split brain, not just those that go unresolved\&. .RE .PP \fBsplit\-brain \fR\fB\fIcmd\fR\fR .RS 4 DRBD detected a split brain situation but remains unresolved\&. Manual recovery is necessary\&. This handler should alert someone on duty\&. .RE .PP \fBbefore\-resync\-target \fR\fB\fIcmd\fR\fR .RS 4 DRBD calls this handler just before a resync begins on the node that becomes resync target\&. It might be used to take a snapshot of the backing block device\&. .RE .PP \fBafter\-resync\-target \fR\fB\fIcmd\fR\fR .RS 4 DRBD calls this handler just after a resync operation finished on the node whose disk just became consistent after being inconsistent for the duration of the resync\&. It might be used to remove a snapshot of the backing device that was created by the \fBbefore\-resync\-target\fR handler\&. .RE .SS "Other Keywords" .PP \fBinclude \fR\fB\fIfile\-pattern\fR\fR .RS 4 Include all files matching the wildcard pattern \fIfile\-pattern\fR\&. The \fBinclude\fR statement is only allowed on the top level, i\&.e\&. it is not allowed inside any section\&. .RE .SH "NOTES ON DATA INTEGRITY" .PP There are two independent methods in DRBD to ensure the integrity of the mirrored data\&. The online\-verify mechanism and the \fBdata\-integrity\-alg\fR of the \fBnetwork\fR section\&. .PP Both mechanisms might deliver false positives if the user of DRBD modifies the data which gets written to disk while the transfer goes on\&. This may happen for swap, or for certain append while global sync, or truncate/rewrite workloads, and not necessarily poses a problem for the integrity of the data\&. Usually when the initiator of the data transfer does this, it already knows that that data block will not be part of an on disk data structure, or will be resubmitted with correct data soon enough\&. .PP The \fBdata\-integrity\-alg\fR causes the receiving side to log an error about "Digest integrity check FAILED: Ns +x\en", where N is the sector offset, and x is the size of the request in bytes\&. It will then disconnect, and reconnect, thus causing a quick resync\&. If the sending side at the same time detected a modification, it warns about "Digest mismatch, buffer modified by upper layers during write: Ns +x\en", which shows that this was a false positive\&. The sending side may detect these buffer modifications immediately after the unmodified data has been copied to the tcp buffers, in which case the receiving side won\*(Aqt notice it\&. .PP The most recent (2007) example of systematic corruption was an issue with the TCP offloading engine and the driver of a certain type of GBit NIC\&. The actual corruption happened on the DMA transfer from core memory to the card\&. Since the TCP checksum gets calculated on the card, this type of corruption stays undetected as long as you do not use either the online \fBverify\fR or the \fBdata\-integrity\-alg\fR\&. .PP We suggest to use the \fBdata\-integrity\-alg\fR only during a pre\-production phase due to its CPU costs\&. Further we suggest to do online \fBverify\fR runs regularly e\&.g\&. once a month during a low load period\&. .SH "VERSION" .sp This document was revised for version 8\&.4\&.0 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd\fR(8), \fBdrbddisk\fR(8), \fBdrbdsetup\fR(8), \fBdrbdmeta\fR(8), \fBdrbdadm\fR(8), \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2, \m[blue]\fBDRBD web site\fR\m[]\&\s-2\u[3]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD User's Guide .RS 4 \%http://www.drbd.org/users-guide/ .RE .IP " 2." 4 DRBD's online usage counter .RS 4 \%http://usage.drbd.org .RE .IP " 3." 4 DRBD web site .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v84/drbd.conf.xml0000644000175000017500000027274312654402323021451 0ustar apoikosapoikos 6 May 2011 DRBD 8.4.0 drbd.conf 5 Configuration Files drbd.conf Configuration file for DRBD's devices drbd.conf Introduction The file is read by . The file format was designed as to allow to have a verbatim copy of the file on both nodes of the cluster. It is highly recommended to do so in order to keep your configuration manageable. The file should be the same on both nodes of the cluster. Changes to do not apply immediately. By convention the main config contains two include statements. The first one includes the file , the second one all file with a suffix. A small example.res file resource r0 { net { protocol C; cram-hmac-alg sha1; shared-secret "FooFunFactory"; } disk { resync-rate 10M; } on alice { volume 0 { device minor 1; disk /dev/sda7; meta-disk internal; } address 10.1.1.31:7789; } on bob { volume 0 { device minor 1; disk /dev/sda7; meta-disk internal; } address 10.1.1.32:7789; } } In this example, there is a single DRBD resource (called r0) which uses protocol C for the connection between its devices. It contains a single volume which runs on host alice uses /dev/drbd1 as devices for its application, and /dev/sda7 as low-level storage for the data. The IP addresses are used to specify the networking interfaces to be used. An eventually running resync process should use about 10MByte/second of IO bandwidth. This sync-rate statement is valid for volume 0, but would also be valid for further volumes. In this example it assigns full 10MByte/second to each volume. There may be multiple resource sections in a single drbd.conf file. For more examples, please have a look at the DRBD User's Guide. File Format The file consists of sections and parameters. A section begins with a keyword, sometimes an additional name, and an opening brace ({). A section ends with a closing brace (}. The braces enclose the parameters. section [name] { parameter value; [...] } A parameter starts with the identifier of the parameter followed by whitespace. Every subsequent character is considered as part of the parameter's value. A special case are Boolean parameters which consist only of the identifier. Parameters are terminated by a semicolon (;). Some parameter values have default units which might be overruled by K, M or G. These units are defined in the usual way (K = 2^10 = 1024, M = 1024 K, G = 1024 M). Comments may be placed into the configuration file and must begin with a hash sign (#). Subsequent characters are ignored until the end of the line. Sections drbd.conf skip Comments out chunks of text, even spanning more than one line. Characters between the keyword and the opening brace ({) are ignored. Everything enclosed by the braces is skipped. This comes in handy, if you just want to comment out some '' section: just precede it with ''. drbd.conf global Configures some global parameters. Currently only , , and are allowed here. You may only have one global section, preferably as the first section. drbd.conf common All resources inherit the options set in this section. The common section might have a , a , a , a and a section. drbd.conf resource Configures a DRBD resource. Each resource section needs to have two (or more) sections and may have a , a , a , a and a section. It might contain s sections. drbd.conf on Carries the necessary configuration parameters for a DRBD device of the enclosing resource. host-name is mandatory and must match the Linux host name (uname -n) of one of the nodes. You may list more than one host name here, in case you want to use the same parameters on several hosts (you'd have to move the IP around usually). Or you may list more than two such sections. resource r1 { protocol C; device minor 1; meta-disk internal; on alice bob { address 10.2.2.100:7801; disk /dev/mapper/some-san; } on charlie { address 10.2.2.101:7801; disk /dev/mapper/other-san; } on daisy { address 10.2.2.103:7801; disk /dev/mapper/other-san-as-seen-from-daisy; } } See also the section keyword. Required statements in this section: and . Note for backward compatibility and convenience it is valid to embed the statements of a single volume directly into the host section. drbd.conf volume Defines a volume within a connection. The minor numbers of a replicated volume might be different on different hosts, the volume number (vnr) is what groups them together. Required parameters in this section: , , . drbd.conf stacked-on-top-of For a stacked DRBD setup (3 or 4 nodes), a is used instead of an section. Required parameters in this section: and . drbd.conf on Carries the necessary configuration parameters for a DRBD device of the enclosing resource. This section is very similar to the section. The difference to the section is that the matching of the host sections to machines is done by the IP-address instead of the node name. Required parameters in this section: , , , all of which may be inherited from the resource section, in which case you may shorten this section down to just the address identifier. resource r2 { protocol C; device minor 2; disk /dev/sda7; meta-disk internal; # short form, device, disk and meta-disk inherited floating 10.1.1.31:7802; # longer form, only device inherited floating 10.1.1.32:7802 { disk /dev/sdb; meta-disk /dev/sdc8; } } drbd.conf disk This section is used to fine tune DRBD's properties in respect to the low level storage. Please refer to drbdsetup 8 for detailed description of the parameters. Optional parameters: , , , , , , , , , , , , , , , , , , , , . drbd.conf net This section is used to fine tune DRBD's properties. Please refer to drbdsetup 8 for a detailed description of this section's parameters. Optional parameters: , , , , , , , , , , , , , , , , , , , , , , , , . drbd.conf startup This section is used to fine tune DRBD's properties. Please refer to drbdsetup 8 for a detailed description of this section's parameters. Optional parameters: , , , , and . drbd.conf options This section is used to fine tune the behaviour of the resource object. Please refer to drbdsetup 8 for a detailed description of this section's parameters. Optional parameters: , and . drbd.conf handlers In this section you can define handlers (executables) that are started by the DRBD system in response to certain events. Optional parameters: , , , (formerly oudate-peer), , , , , . The interface is done via environment variables: is the name of the resource is the minor number of the DRBD device, in decimal. is the path to the primary configuration file; if you split your configuration into multiple files (e.g. in ), this will not be helpful. , , are the address family (e.g. ), the peer's address and hostnames. is deprecated. Please note that not all of these might be set for all handlers, and that some values might not be useable for a definition. Parameters drbd.conf minor-count count may be a number from 1 to 1048575. Minor-count is a sizing hint for DRBD. It helps to right-size various memory pools. It should be set in the in the same order of magnitude than the actual number of minors you use. Per default the module loads with 11 more resources than you have currently in your config but at least 32. drbd.conf dialog-refresh time may be 0 or a positive number. The user dialog redraws the second count every time seconds (or does no redraws if time is 0). The default value is 1. drbd.conf disable-ip-verification Use disable-ip-verification if, for some obscure reasons, drbdadm can/might not use or to do a sanity check for the IP address. You can disable the IP verification with this option. drbd.conf usage-count Please participate in DRBD's online usage counter. The most convenient way to do so is to set this option to . Valid options are: , and . drbd.conf protocol On the TCP/IP link the specified protocol is used. Valid protocol specifiers are A, B, and C. Protocol A: write IO is reported as completed, if it has reached local disk and local TCP send buffer. Protocol B: write IO is reported as completed, if it has reached local disk and remote buffer cache. Protocol C: write IO is reported as completed, if it has reached both local and remote disk. drbd.conf device The name of the block device node of the resource being described. You must use this device with your application (file system) and you must not use the low level block device which is specified with the parameter. One can ether omit the name or and the minor number. If you omit the name a default of /dev/drbdminor will be used. Udev will create additional symlinks in /dev/drbd/by-res and /dev/drbd/by-disk. drbd.conf disk DRBD uses this block device to actually store and retrieve the data. Never access such a device while DRBD is running on top of it. This also holds true for dumpe2fs 8 and similar commands. drbd.conf address A resource needs one IP address per device, which is used to wait for incoming connections from the partner device respectively to reach the partner device. AF must be one of , , or (for compatibility reasons is an alias for ). It may be omited for IPv4 addresses. The actual IPv6 address that follows the keyword must be placed inside brackets: ipv6 [fd01:2345:6789:abcd::1]:7800. Each DRBD resource needs a TCP port which is used to connect to the node's partner device. Two different DRBD resources may not use the same addr:port combination on the same node. drbd.conf meta-disk Internal means that the last part of the backing device is used to store the meta-data. The size of the meta-data is computed based on the size of the device. When a device is specified, either with or without an index, DRBD stores the meta-data on this device. Without index, the size of the meta-data is determined by the size of the data device. This is usually used with LVM, which allows to have many variable sized block devices. The meta-data size is 36kB + Backing-Storage-size / 32k, rounded up to the next 4kb boundary. (Rule of the thumb: 32kByte per 1GByte of storage, rounded up to the next MB.) When an index is specified, each index number refers to a fixed slot of meta-data of 128 MB, which allows a maximum data size of 4 TiB. This way, multiple DBRD devices can share the same meta-data device. For example, if /dev/sde6[0] and /dev/sde6[1] are used, /dev/sde6 must be at least 256 MB big. Because of the hard size limit, use of meta-disk indexes is discouraged. drbd.conf on-io-error handler is taken, if the lower level device reports io-errors to the upper layers. handler may be , or : The node downgrades the disk status to inconsistent, marks the erroneous block as inconsistent in the bitmap and retries the IO on the remote node. : Call the handler script . : The node drops its low level device, and continues in diskless mode. drbd.conf fencing By we understand preventive measures to avoid situations where both nodes are primary and disconnected (AKA split brain). Valid fencing policies are: This is the default policy. No fencing actions are taken. If a node becomes a disconnected primary, it tries to fence the peer's disk. This is done by calling the handler. The handler is supposed to reach the other node over alternative communication paths and call '' there. If a node becomes a disconnected primary, it freezes all its IO operations and calls its fence-peer handler. The fence-peer handler is supposed to reach the peer over alternative communication paths and call 'drbdadm outdate res' there. In case it cannot reach the peer it should stonith the peer. IO is resumed as soon as the situation is resolved. In case your handler fails, you can resume IO with the command. drbd.conf disk-barrier drbd.conf disk-flushes drbd.conf disk-drain DRBD has four implementations to express write-after-write dependencies to its backing storage device. DRBD will use the first method that is supported by the backing storage device and that is not disabled. By default the flush method is used. Since drbd-8.4.2 is disabled by default because since linux-2.6.36 (or 2.6.32 RHEL6) there is no reliable way to determine if queuing of IO-barriers works. Dangerous only enable if you are told so by one that knows for sure. When selecting the method you should not only base your decision on the measurable performance. In case your backing storage device has a volatile write cache (plain disks, RAID of plain disks) you should use one of the first two. In case your backing storage device has battery-backed write cache you may go with option 3. Option 4 (disable everything, use "none") is dangerous on most IO stacks, may result in write-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles. Do not use . Unfortunately device mapper (LVM) might not support barriers. The letter after "wo:" in /proc/drbd indicates with method is currently in use for a device: , , , . The implementations are: barrier The first requires that the driver of the backing storage device support barriers (called 'tagged command queuing' in SCSI and 'native command queuing' in SATA speak). The use of this method can be enabled by setting the options to . flush The second requires that the backing device support disk flushes (called 'force unit access' in the drive vendors speak). The use of this method can be disabled setting to . drain The third method is simply to let write requests drain before write requests of a new reordering domain are issued. This was the only implementation before 8.0.9. none The fourth method is to not express write-after-write dependencies to the backing store at all, by also specifying . This is dangerous on most IO stacks, may result in write-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles. Do not use . drbd.conf md-flushes Disables the use of disk flushes and barrier BIOs when accessing the meta data device. See the notes on . drbd.conf max-bio-bvecs In some special circumstances the device mapper stack manages to pass BIOs to DRBD that violate the constraints that are set forth by DRBD's merge_bvec() function and which have more than one bvec. A known example is: phys-disk -> DRBD -> LVM -> Xen -> misaligned partition (63) -> DomU FS. Then you might see "bio would need to, but cannot, be split:" in the Dom0's kernel log. The best workaround is to proper align the partition within the VM (E.g. start it at sector 1024). This costs 480 KiB of storage. Unfortunately the default of most Linux partitioning tools is to start the first partition at an odd number (63). Therefore most distribution's install helpers for virtual linux machines will end up with misaligned partitions. The second best workaround is to limit DRBD's max bvecs per BIO (= ) to 1, but that might cost performance. The default value of is 0, which means that there is no user imposed limitation. drbd.conf disk-timeout If the lower-level device on which a DRBD device stores its data does not finish an I/O request within the defined , DRBD treats this as a failure. The lower-level device is detached, and the device's disk state advances to Diskless. If DRBD is connected to one or more peers, the failed request is passed on to one of them. This option is dangerous and may lead to kernel panic! "Aborting" requests, or force-detaching the disk, is intended for completely blocked/hung local backing devices which do no longer complete requests at all, not even do error completions. In this situation, usually a hard-reset and failover is the only way out. By "aborting", basically faking a local error-completion, we allow for a more graceful swichover by cleanly migrating services. Still the affected node has to be rebooted "soon". By completing these requests, we allow the upper layers to re-use the associated data pages. If later the local backing device "recovers", and now DMAs some data from disk into the original request pages, in the best case it will just put random data into unused pages; but typically it will corrupt meanwhile completely unrelated data, causing all sorts of damage. Which means delayed successful completion, especially for READ requests, is a reason to panic(). We assume that a delayed *error* completion is OK, though we still will complain noisily about it. The default value of is 0, which stands for an infinite timeout. Timeouts are specified in units of 0.1 seconds. This option is available since DRBD 8.3.12. drbd.conf discard-zeroes-if-aligned There are several aspects to discard/trim/unmap support on linux block devices. Even if discard is supported in general, it may fail silently, or may partially ignore discard requests. Devices also announce whether reading from unmapped blocks returns defined data (usually zeroes), or undefined data (possibly old data, possibly garbage). If on different nodes, DRBD is backed by devices with differing discard characteristics, discards may lead to data divergence (old data or garbage left over on one backend, zeroes due to unmapped areas on the other backend). Online verify would now potentially report tons of spurious differences. While probably harmless for most use cases (fstrim on a file system), DRBD cannot have that. To play safe, we have to disable discard support, if our local backend (on a Primary) does not support "discard_zeroes_data=true". We also have to translate discards to explicit zero-out on the receiving side, unless the receiving side (Secondary) supports "discard_zeroes_data=true", thereby allocating areas what were supposed to be unmapped. There are some devices (notably the LVM/DM thin provisioning) that are capable of discard, but announce discard_zeroes_data=false. In the case of DM-thin, discards aligned to the chunk size will be unmapped, and reading from unmapped sectors will return zeroes. However, unaligned partial head or tail areas of discard requests will be silently ignored. If we now add a helper to explicitly zero-out these unaligned partial areas, while passing on the discard of the aligned full chunks, we effectively achieve discard_zeroes_data=true on such devices. Setting to will allow DRBD to use discards, and to announce discard_zeroes_data=true, even on backends that announce discard_zeroes_data=false. Setting to will cause DRBD to always fall-back to zero-out on the receiving side, and to not even announce discard capabilities on the Primary, if the respective backend announces discard_zeroes_data=false. We used to ignore the discard_zeroes_data setting completely. To not break established and expected behaviour, and suddenly cause fstrim on thin-provisioned LVs to run out-of-space instead of freeing up space, the default value is . This option is available since 8.4.7. drbd.conf read-balancing The supported methods for load balancing of read requests are , , , , , , , , , and . The default value of is . This option is available since 8.4.1. drbd.conf rs-discard-granularity When is set to a non zero, positive value then DRBD tries to do a resync operation in requests of this size. In case such a block contains only zero bytes on the sync source node, the sync target node will issue a discard/trim/unmap command for the area. The value is constrained by the discard granularity of the backing block device. In case is not a multiplier of the discard granularity of the backing block device DRBD rounds it up. The feature only gets active if the backing block device reads back zeroes after a discard command. The default value of is 0. This option is available since 8.4.7. drbd.conf sndbuf-size size is the size of the TCP socket send buffer. The default value is 0, i.e. autotune. You can specify smaller or larger values. Larger values are appropriate for reasonable write throughput with protocol A over high latency networks. Values below 32K do not make sense. Since 8.0.13 resp. 8.2.7, setting the size value to 0 means that the kernel should autotune this. drbd.conf rcvbuf-size size is the size of the TCP socket receive buffer. The default value is 0, i.e. autotune. You can specify smaller or larger values. Usually this should be left at its default. Setting the size value to 0 means that the kernel should autotune this. drbd.conf timeout If the partner node fails to send an expected response packet within time tenths of a second, the partner node is considered dead and therefore the TCP/IP connection is abandoned. This must be lower than connect-int and ping-int. The default value is 60 = 6 seconds, the unit 0.1 seconds. drbd.conf connect-int In case it is not possible to connect to the remote DRBD device immediately, DRBD keeps on trying to connect. With this option you can set the time between two retries. The default value is 10 seconds, the unit is 1 second. drbd.conf ping-int If the TCP/IP connection linking a DRBD device pair is idle for more than time seconds, DRBD will generate a keep-alive packet to check if its partner is still alive. The default is 10 seconds, the unit is 1 second. drbd.conf ping-timeout The time the peer has time to answer to a keep-alive packet. In case the peer's reply is not received within this time period, it is considered as dead. The default value is 500ms, the default unit are tenths of a second. drbd.conf max-buffers Limits the memory usage per DRBD minor device on the receiving side, or for internal buffers during resync or online-verify. Unit is PAGE_SIZE, which is 4 KiB on most systems. The minimum possible setting is hard coded to 32 (=128 KiB). These buffers are used to hold data blocks while they are written to/read from disk. To avoid possible distributed deadlocks on congestion, this setting is used as a throttle threshold rather than a hard limit. Once more than max-buffers pages are in use, further allocation from this pool is throttled. You want to increase max-buffers if you cannot saturate the IO backend on the receiving side. drbd.conf ko-count In case the secondary node fails to complete a single write request for count times the timeout, it is expelled from the cluster. (I.e. the primary node will kill and restart the connection.) To disable this feature, you should explicitly set it to 0; defaults may change between versions. drbd.conf max-epoch-size The highest number of data blocks between two write barriers. If you set this smaller than 10, you might decrease your performance. drbd.conf allow-two-primaries With this option set you may assign the primary role to both nodes. You only should use this option if you use a shared storage file system on top of DRBD. At the time of writing the only ones are: OCFS2 and GFS. If you use this option with any other file system, you are going to crash your nodes and to corrupt your data! drbd.conf unplug-watermark This setting has no effect with recent kernels that use explicit on-stack plugging (upstream Linux kernel 2.6.39, distributions may have backported). When the number of pending write requests on the standby (secondary) node exceeds the , we trigger the request processing of our backing storage device. Some storage controllers deliver better performance with small values, others deliver best performance when the value is set to the same value as max-buffers, yet others don't feel much effect at all. Minimum 16, default 128, maximum 131072. drbd.conf cram-hmac-alg You need to specify the HMAC algorithm to enable peer authentication at all. You are strongly encouraged to use peer authentication. The HMAC algorithm will be used for the challenge response authentication of the peer. You may specify any digest algorithm that is named in . drbd.conf shared-secret The shared secret used in peer authentication. May be up to 64 characters. Note that peer authentication is disabled as long as no (see above) is specified. policy drbd.conf after-sb-0pri possible policies are: No automatic resynchronization, simply disconnect. Auto sync from the node that was primary before the split-brain situation happened. Auto sync from the node that became primary as second during the split-brain situation. In case one node did not write anything since the split brain became evident, sync from the node that wrote something to the node that did not write anything. In case none wrote anything this policy uses a random decision to perform a "resync" of 0 blocks. In case both have written something this policy disconnects the nodes. Auto sync from the node that touched more blocks during the split brain situation. Auto sync to the named node. policy drbd.conf after-sb-1pri possible policies are: No automatic resynchronization, simply disconnect. Discard the version of the secondary if the outcome of the algorithm would also destroy the current secondary's data. Otherwise disconnect. Always take the decision of the algorithm, even if that causes an erratic change of the primary's view of the data. This is only useful if you use a one-node FS (i.e. not OCFS2 or GFS) with the flag, AND if you really know what you are doing. This is DANGEROUS and MAY CRASH YOUR MACHINE if you have an FS mounted on the primary node. Discard the secondary's version. Always honor the outcome of the algorithm. In case it decides the current secondary has the right data, it calls the "pri-lost-after-sb" handler on the current primary. policy drbd.conf after-sb-2pri possible policies are: No automatic resynchronization, simply disconnect. Always take the decision of the algorithm, even if that causes an erratic change of the primary's view of the data. This is only useful if you use a one-node FS (i.e. not OCFS2 or GFS) with the flag, AND if you really know what you are doing. This is DANGEROUS and MAY CRASH YOUR MACHINE if you have an FS mounted on the primary node. Call the "pri-lost-after-sb" helper program on one of the machines. This program is expected to reboot the machine, i.e. make it secondary. Normally the automatic after-split-brain policies are only used if current states of the UUIDs do not indicate the presence of a third node. With this option you request that the automatic after-split-brain policies are used as long as the data sets of the nodes are somehow related. This might cause a full sync, if the UUIDs indicate the presence of a third node. (Or double faults led to strange UUID sets.) policy drbd.conf rr-conflict This option helps to solve the cases when the outcome of the resync decision is incompatible with the current role assignment in the cluster. No automatic resynchronization, simply disconnect. Sync to the primary node is allowed, violating the assumption that data on a block device are stable for one of the nodes. Dangerous, do not use. Call the helper program on one of the machines unless that machine can demote to secondary. The helper program is expected to reboot the machine, which brings the node into a secondary role. Which machine runs the helper program is determined by the strategy. alg drbd.conf data-integrity-alg DRBD can ensure the data integrity of the user's data on the network by comparing hash values. Normally this is ensured by the 16 bit checksums in the headers of TCP/IP packets. This option can be set to any of the kernel's data digest algorithms. In a typical kernel configuration you should have at least one of , , and available. By default this is not enabled. See also the notes on data integrity. drbd.conf tcp-cork DRBD usually uses the TCP socket option TCP_CORK to hint to the network stack when it can expect more data, and when it should flush out what it has in its send queue. It turned out that there is at least one network stack that performs worse when one uses this hinting method. Therefore we introducted this option. By setting to you can disable the setting and clearing of the TCP_CORK socket option by DRBD. By default DRBD blocks when the available TCP send queue becomes full. That means it will slow down the application that generates the write requests that cause DRBD to send more data down that TCP connection. When DRBD is deployed with DRBD-proxy it might be more desirable that DRBD goes into AHEAD/BEHIND mode shortly before the send queue becomes full. In AHEAD/BEHIND mode DRBD does no longer replicate data, but still keeps the connection open. The advantage of the AHEAD/BEHIND mode is that the application is not slowed down, even if DRBD-proxy's buffer is not sufficient to buffer all write requests. The downside is that the peer node falls behind, and that a resync will be necessary to bring it back into sync. During that resync the peer node will have an inconsistent disk. Available congestion_policys are and . The default is . Fill_threshold might be in the range of 0 to 10GiBytes. The default is 0 which disables the check. Active_extents_threshold has the same limits as . The AHEAD/BEHIND mode and its settings are available since DRBD 8.3.10. Wait for connection timeout. drbd.conf wfc-timeout The init script drbd 8 blocks the boot process until the DRBD resources are connected. When the cluster manager starts later, it does not see a resource with internal split-brain. In case you want to limit the wait time, do it here. Default is 0, which means unlimited. The unit is seconds. drbd.conf degr-wfc-timeout Wait for connection timeout, if this node was a degraded cluster. In case a degraded cluster (= cluster with only one node left) is rebooted, this timeout value is used instead of wfc-timeout, because the peer is less likely to show up in time, if it had been dead before. Value 0 means unlimited. drbd.conf outdated-wfc-timeout Wait for connection timeout, if the peer was outdated. In case a degraded cluster (= cluster with only one node left) with an outdated peer disk is rebooted, this timeout value is used instead of wfc-timeout, because the peer is not allowed to become primary in the meantime. Value 0 means unlimited. By setting this option you can make the init script to continue to wait even if the device pair had a split brain situation and therefore refuses to connect. Sets on which node the device should be promoted to primary role by the init script. The node-name might either be a host name or the keyword . When this option is not set the devices stay in secondary role on both nodes. Usually one delegates the role assignment to a cluster manager (e.g. heartbeat). Usually and are ignored for stacked devices, instead twice the amount of is used for the connection timeouts. With the keyword you disable this, and force DRBD to mind the and statements. Only do that if the peer of the stacked resource is usually not available or will usually not become primary. By using this option incorrectly, you run the risk of causing unexpected split brain. drbd.conf resync-rate To ensure a smooth operation of the application on top of DRBD, it is possible to limit the bandwidth which may be used by background synchronizations. The default is 250 KB/sec, the default unit is KB/sec. Optional suffixes K, M, G are allowed. drbd.conf use-rle During resync-handshake, the dirty-bitmaps of the nodes are exchanged and merged (using bit-or), so the nodes will have the same understanding of which blocks are dirty. On large devices, the fine grained dirty-bitmap can become large as well, and the bitmap exchange can take quite some time on low-bandwidth links. Because the bitmap typically contains compact areas where all bits are unset (clean) or set (dirty), a simple run-length encoding scheme can considerably reduce the network traffic necessary for the bitmap exchange. For backward compatibilty reasons, and because on fast links this possibly does not improve transfer time but consumes cpu cycles, this defaults to off. drbd.conf socket-check-timeout In setups involving a DRBD-proxy and connections that experience a lot of buffer-bloat it might be necessary to set to an unusual high value. By default DRBD uses the same value to wait if a newly established TCP-connection is stable. Since the DRBD-proxy is usually located in the same data center such a long wait time may hinder DRBD's connect process. In such setups should be set to at least to the round trip time between DRBD and DRBD-proxy. I.e. in most cases to 1. The default unit is tenths of a second, the default value is 0 (which causes DRBD to use the value of instead). Introduced in 8.4.5. drbd.conf resync-after By default, resynchronization of all devices would run in parallel. By defining a resync-after dependency, the resynchronization of this resource will start only if the resource res-name is already in connected state (i.e., has finished its resynchronization). drbd.conf al-extents DRBD automatically performs hot area detection. With this parameter you control how big the hot area (= active set) can get. Each extent marks 4M of the backing storage (= low-level device). In case a primary node leaves the cluster unexpectedly, the areas covered by the active set must be resynced upon rejoining of the failed node. The data structure is stored in the meta-data area, therefore each change of the active set is a write operation to the meta-data device. A higher number of extents gives longer resync times but less updates to the meta-data. The default number of extents is 1237. (Minimum: 7, Maximum: 65534) Note that the effective maximum may be smaller, depending on how you created the device meta data, see also drbdmeta8. The effective maximum is 919 * (available on-disk activity-log ring-buffer area/4kB -1), the default 32kB ring-buffer effects a maximum of 6433 (covers more than 25 GiB of data). We recommend to keep this well within the amount your backend storage and replication link are able to resync inside of about 5 minutes. drbd.conf al-updates DRBD's activity log transaction writing makes it possible, that after the crash of a primary node a partial (bit-map based) resync is sufficient to bring the node back to up-to-date. Setting to might increase normal operation performance but causes DRBD to do a full resync when a crashed primary gets reconnected. The default value is . During online verification (as initiated by the verify sub-command), rather than doing a bit-wise comparison, DRBD applies a hash function to the contents of every block being verified, and compares that hash with the peer. This option defines the hash algorithm being used for that purpose. It can be set to any of the kernel's data digest algorithms. In a typical kernel configuration you should have at least one of , , and available. By default this is not enabled; you must set this option explicitly in order to be able to use on-line device verification. See also the notes on data integrity. A resync process sends all marked data blocks from the source to the destination node, as long as no is given. When one is specified the resync process exchanges hash values of all marked blocks first, and sends only those data blocks that have different hash values. This setting is useful for DRBD setups with low bandwidth links. During the restart of a crashed primary node, all blocks covered by the activity log are marked for resync. But a large part of those will actually be still in sync, therefore using will lower the required bandwidth in exchange for CPU cycles. The dynamic resync speed controller gets enabled with setting plan_time to a positive value. It aims to fill the buffers along the data path with either a constant amount of data fill_target, or aims to have a constant delay time of delay_target along the path. The controller has an upper bound of max_rate. By plan_time the agility of the controller is configured. Higher values yield for slower/lower responses of the controller to deviation from the target value. It should be at least 5 times RTT. For regular data paths a fill_target in the area of 4k to 100k is appropriate. For a setup that contains drbd-proxy it is advisable to use delay_target instead. Only when fill_target is set to 0 the controller will use delay_target. 5 times RTT is a reasonable starting value. Max_rate should be set to the bandwidth available between the DRBD-hosts and the machines hosting DRBD-proxy, or to the available disk-bandwidth. The default value of plan_time is 0, the default unit is 0.1 seconds. Fill_target has 0 and sectors as default unit. Delay_target has 1 (100ms) and 0.1 as default unit. Max_rate has 10240 (100MiB/s) and KiB/s as default unit. The dynamic resync speed controller and its settings are available since DRBD 8.3.9. A node that is primary and sync-source has to schedule application IO requests and resync IO requests. The min_rate tells DRBD use only up to min_rate for resync IO and to dedicate all other available IO bandwidth to application requests. Note: The value 0 has a special meaning. It disables the limitation of resync IO completely, which might slow down application IO considerably. Set it to a value of 1, if you prefer that resync IO never slows down application IO. Note: Although the name might suggest that it is a lower bound for the dynamic resync speed controller, it is not. If the DRBD-proxy buffer is full, the dynamic resync speed controller is free to lower the resync speed down to 0, completely independent of the setting. Min_rate has 4096 (4MiB/s) and KiB/s as default unit. This setting controls what happens to IO requests on a degraded, disk less node (I.e. no data store is reachable). The available policies are and . If ond-policy is set to you can either resume IO by attaching/connecting the last lost data storage, or by the drbdadm resume-io res command. The latter will result in IO errors of course. The default is . This setting is available since DRBD 8.3.9. drbd.conf cpu-mask Sets the cpu-affinity-mask for DRBD's kernel threads of this device. The default value of cpu-mask is 0, which means that DRBD's kernel threads should be spread over all CPUs of the machine. This value must be given in hexadecimal notation. If it is too big it will be truncated. drbd.conf pri-on-incon-degr This handler is called if the node is primary, degraded and if the local copy of the data is inconsistent. drbd.conf pri-lost-after-sb The node is currently primary, but lost the after-split-brain auto recovery procedure. As as consequence, it should be abandoned. drbd.conf pri-lost The node is currently primary, but DRBD's algorithm thinks that it should become sync target. As a consequence it should give up its primary role. drbd.conf fence-peer The handler is part of the mechanism. This handler is called in case the node needs to fence the peer's disk. It should use other communication paths than DRBD's network link. drbd.conf local-io-error DRBD got an IO error from the local IO subsystem. drbd.conf initial-split-brain DRBD has connected and detected a split brain situation. This handler can alert someone in all cases of split brain, not just those that go unresolved. drbd.conf split-brain DRBD detected a split brain situation but remains unresolved. Manual recovery is necessary. This handler should alert someone on duty. drbd.conf before-resync-target DRBD calls this handler just before a resync begins on the node that becomes resync target. It might be used to take a snapshot of the backing block device. drbd.conf after-resync-target DRBD calls this handler just after a resync operation finished on the node whose disk just became consistent after being inconsistent for the duration of the resync. It might be used to remove a snapshot of the backing device that was created by the handler. Other Keywords drbd.conf include Include all files matching the wildcard pattern file-pattern. The statement is only allowed on the top level, i.e. it is not allowed inside any section. Notes on data integrity There are two independent methods in DRBD to ensure the integrity of the mirrored data. The online-verify mechanism and the of the section. Both mechanisms might deliver false positives if the user of DRBD modifies the data which gets written to disk while the transfer goes on. This may happen for swap, or for certain append while global sync, or truncate/rewrite workloads, and not necessarily poses a problem for the integrity of the data. Usually when the initiator of the data transfer does this, it already knows that that data block will not be part of an on disk data structure, or will be resubmitted with correct data soon enough. The causes the receiving side to log an error about "Digest integrity check FAILED: Ns +x\n", where N is the sector offset, and x is the size of the request in bytes. It will then disconnect, and reconnect, thus causing a quick resync. If the sending side at the same time detected a modification, it warns about "Digest mismatch, buffer modified by upper layers during write: Ns +x\n", which shows that this was a false positive. The sending side may detect these buffer modifications immediately after the unmodified data has been copied to the tcp buffers, in which case the receiving side won't notice it. The most recent (2007) example of systematic corruption was an issue with the TCP offloading engine and the driver of a certain type of GBit NIC. The actual corruption happened on the DMA transfer from core memory to the card. Since the TCP checksum gets calculated on the card, this type of corruption stays undetected as long as you do not use either the online or the . We suggest to use the only during a pre-production phase due to its CPU costs. Further we suggest to do online runs regularly e.g. once a month during a low load period. Version This document was revised for version 8.4.0 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd8, drbddisk8, drbdsetup8, drbdmeta8, drbdadm8, DRBD User's Guide, DRBD web site drbd-utils-8.9.10/documentation/v84/xml-usage-to-docbook.xsl0000644000175000017500000000323412466702073023551 0ustar apoikosapoikos drbdsetup -- val -- -- drbd-utils-8.9.10/documentation/v84/drbd.80000644000175000017500000000545713027211740020064 0ustar apoikosapoikos'\" t .\" Title: drbd .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 15 Oct 2008 .\" Manual: System Administration .\" Source: DRBD 8.3.2 .\" Language: English .\" .TH "DRBD" "8" "15 Oct 2008" "DRBD 8.3.2" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbd \- The start and stop script for DRBD .SH "SYNOPSIS" .HP \w'\fB/etc/init\&.d/drbd\fR\ 'u \fB/etc/init\&.d/drbd\fR [\fIresource\fR] {{start}\ |\ {stop}\ |\ {status}\ |\ {reload}\ |\ {restart}\ |\ {force\-reload}} .SH "INTRODUCTION" .PP The \fB/etc/init\&.d/drbd\fR script is used to start and stop drbd on a system V style init system\&. .PP In order to use \fB/etc/init\&.d/drbd\fR you must define a resource, a host, and any other configuration options in the drbd configuration file\&. See \fB/etc/drbd\&.conf\fR for details\&. If \fIresource\fR is omitted, then all of the resources listed in the config file are configured\&. .PP This script might ask you \(lqDo you want to abort waiting for other server and make this one primary?\(rq .PP Only answer this question with \(lqyes\(rq if you are sure that it is impossible to repair the other node\&. .SH "VERSION" .sp This document was revised for version 8\&.3\&.2 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbddisk\fR(8), \fBdrbdsetup\fR(8)\fBdrbdadm\fR(8)\m[blue]\fBDRBD Homepage\fR\m[]\&\s-2\u[1]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD Homepage .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v84/drbdadm.xml0000644000175000017500000005472012466702073021206 0ustar apoikosapoikos 6 May 2011 DRBD 8.4.0 drbdadm 8 System Administration drbdadm Administration tool for DRBD drbdadm drbdadm -d -cfile -tfile -scmd -mcmd -S -hhost --backend-options command all resource/volume> Description is the high level tool of the DRBD program suite. is to and what / is to . reads its configuration file and performs the specified commands by calling the and/or the program. can operate on whole resources or on individual volumes in a resource. The sub commands: , , , , , , , , , , , , , , , , , , work on whole resources and on individual volumes. Resource level only commands are: , , , , and . Options , Just prints the calls of to stdout, but does not run the commands. , file Specifies the configuration file drbdadm will use. If this parameter is not specified, drbdadm will look for , , and . , file Specifies an additional configuration file drbdadm to check. This option is only allowed with the dump and the sh-nop commands. , file Specifies the full path to the program. If this option is omitted, drbdadm will look for it beneath itself first, and then in the PATH. , file Specifies the full path to the program. If this option is omitted, drbdadm will look for it beneath itself first, and then in the PATH. , Specifies that this command should be performed on a stacked resource. , Specifies to which peer node to connect. Only necessary if there are more than two host sections in the resource you are working on. backend-options All options following the doubly hyphen are considered backend-options. These are passed through to the backend command. I.e. to , or . Commands attach Attaches a local backing block device to the DRBD resource's device. detach drbdadm detach Removes the backing storage device from a DRBD resource's device. connect drbdadm connect Sets up the network configuration of the resource's device. If the peer device is already configured, the two DRBD devices will connect. If there are more than two host sections in the resource you need to use the option to select the peer you want to connect to. disconnect drbdadm disconnect Removes the network configuration from the resource. The device will then go into StandAlone state. syncer drbdadm syncer Loads the resynchronization parameters into the device. up drbdadm up Is a shortcut for attach and connect. down drbdadm down Is a shortcut for disconnect and detach. primary drbdadm primary Promote the resource's device into primary role. You need to do this before any access to the device, such as creating or mounting a file system. secondary drbdadm secondary Brings the device back into secondary role. This is needed since in a connected DRBD device pair, only one of the two peers may have primary role (except if is explicitly set in the configuration file). invalidate drbdadm invalidate Forces DRBD to consider the data on the local backing storage device as out-of-sync. Therefore DRBD will copy each and every block from its peer, to bring the local storage device back in sync. To avoid races, you need an established replication link, or be disconnected Secondary. invalidate-remote drbdadm invalidate-remote This command is similar to the invalidate command, however, the peer's backing storage is invalidated and hence rewritten with the data of the local node. To avoid races, you need an established replication link, or be disconnected Primary. resize drbdadm resize Causes DRBD to re-examine all sizing constraints, and resize the resource's device accordingly. For example, if you increased the size of your backing storage devices (on both nodes, of course), then DRBD will adopt the new size after you called this command on one of your nodes. Since new storage space must be synchronised this command only works if there is at least one primary node present. The option can be used to online shrink the usable size of a drbd device. It's the users responsibility to make sure that a file system on the device is not truncated by that operation. The allows you to resize a device which is currently not connected to the peer. Use with care, since if you do not resize the peer's disk as well, further connect attempts of the two will fail. The allows you to resize an existing device and avoid syncing the new space. This is useful when adding addtional blank storage to your device. Example: # drbdadm -- --assume-clean resize r0 The options and may be used to change the layout of the activity log online. In case of internal meta data this may invovle shrinking the user visible size at the same time (unsing the ) or increasing the avalable space on the backing devices. check-resize drbdadm check-resize Calls drbdmeta to eventually move internal meta data. If the backing device was resized, while DRBD was not running, meta data has to be moved to the end of the device, so that the next command can succeed. create-md drbdadm create-md Initializes the meta data storage. This needs to be done before a DRBD resource can be taken online for the first time. In case of issues with that command have a look at drbdmeta 8 get-gi drbdadm get-gi Shows a short textual representation of the data generation identifiers. show-gi drbdadm show-gi Prints a textual representation of the data generation identifiers including explanatory information. dump-md drbdadm dump-md Dumps the whole contents of the meta data storage, including the stored bit-map and activity-log, in a textual representation. outdate drbdadm outdate Sets the outdated flag in the meta data. adjust drbdadm adjust Synchronizes the configuration of the device with your configuration file. You should always examine the output of the dry-run mode before actually executing this command. wait-connect drbdadm wait-connect Waits until the device is connected to its peer device. role drbdadm role Shows the current roles of the devices (local/peer). E.g. Primary/Secondary state drbdadm state Deprecated alias for "role", see above. cstate drbdadm cstate Shows the current connection state of the devices. dump drbdadm dump Just parse the configuration file and dump it to stdout. May be used to check the configuration file for syntactic correctness. outdate drbdadm outdate Used to mark the node's data as outdated. Usually used by the peer's fence-peer handler. verify drbdadm verify Starts online verify. During online verify, data on both nodes is compared for equality. See /proc/drbd for online verify progress. If out-of-sync blocks are found, they are not resynchronized automatically. To do that, disconnect and connect the resource when verification has completed. See also the notes on data integrity on the drbd.conf manpage. pause-sync drbdadm pause-sync Temporarily suspend an ongoing resynchronization by setting the local pause flag. Resync only progresses if neither the local nor the remote pause flag is set. It might be desirable to postpone DRBD's resynchronization until after any resynchronization of the backing storage's RAID setup. resume-sync drbdadm resume-sync Unset the local sync pause flag. new-current-uuid drbdadm new-current-uuid Generates a new currend UUID and rotates all other UUID values. This can be used to shorten the initial resync of a cluster. See the manpage for a more details. dstate drbdadm dstate Show the current state of the backing storage devices. (local/peer) hidden-commands Shows all commands undocumented on purpose. Version This document was revised for version 8.4.0 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2011 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf 5 , drbd 8 , drbddisk 8 , drbdsetup 8 , drbdmeta 8 and the DRBD project web site drbd-utils-8.9.10/documentation/v84/drbd.xml0000644000175000017500000000670212466702073020521 0ustar apoikosapoikos drbd The start and stop script for DRBD DRBD 8.3.2 15 Oct 2008 drbd 8 System Administration /etc/init.d/drbd resource start stop status reload restart force-reload Introduction The script is used to start and stop drbd on a system V style init system. In order to use you must define a resource, a host, and any other configuration options in the drbd configuration file. See for details. If resource is omitted, then all of the resources listed in the config file are configured. This script might ask you Do you want to abort waiting for other server and make this one primary? Only answer this question with yes if you are sure that it is impossible to repair the other node. Version This document was revised for version 8.3.2 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf5, drbddisk8, drbdsetup8drbdadm8DRBD Homepage drbd-utils-8.9.10/documentation/v84/drbdsetup.80000644000175000017500000016176413027211737021157 0ustar apoikosapoikos'\" t .\" Title: drbdsetup .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 6 May 2011 .\" Manual: System Administration .\" Source: DRBD 8.4.0 .\" Language: English .\" .TH "DRBDSETUP" "8" "6 May 2011" "DRBD 8.4.0" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbdsetup \- Setup tool for DRBD .SH "SYNOPSIS" .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR new\-resource \fIresource\fR [\-\-cpu\-mask\ {\fIval\fR}] [\-\-on\-no\-data\-accessible\ {io\-error\ |\ suspend\-io}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR new\-minor \fIresource\fR \fIminor\fR \fIvolume\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR del\-resource \fIresource\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR del\-minor \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR attach \fIminor\fR \fIlower_dev\fR \fImeta_data_dev\fR \fImeta_data_index\fR [\-\-size\ {\fIval\fR}] [\-\-max\-bio\-bvecs\ {\fIval\fR}] [\-\-on\-io\-error\ {pass_on\ |\ call\-local\-io\-error\ |\ detach}] [\-\-fencing\ {dont\-care\ |\ resource\-only\ |\ resource\-and\-stonith}] [\-\-disk\-barrier] [\-\-disk\-flushes] [\-\-disk\-drain] [\-\-md\-flushes] [\-\-resync\-rate\ {\fIval\fR}] [\-\-resync\-after\ {\fIval\fR}] [\-\-al\-extents\ {\fIval\fR}] [\-\-al\-updates] [\-\-discard\-zeroes\-if\-aligned] [\-\-c\-plan\-ahead\ {\fIval\fR}] [\-\-c\-delay\-target\ {\fIval\fR}] [\-\-c\-fill\-target\ {\fIval\fR}] [\-\-c\-max\-rate\ {\fIval\fR}] [\-\-c\-min\-rate\ {\fIval\fR}] [\-\-disk\-timeout\ {\fIval\fR}] [\-\-read\-balancing\ {prefer\-local\ |\ prefer\-remote\ |\ round\-robin\ |\ least\-pending\ |\ when\-congested\-remote\ |\ 32K\-striping\ |\ 64K\-striping\ |\ 128K\-striping\ |\ 256K\-striping\ |\ 512K\-striping\ |\ 1M\-striping}] [\-\-rs\-discard\-granularity\ {\fIval\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR connect \fIresource\fR \fIlocal_addr\fR \fIremote_addr\fR [\-\-tentative] [\-\-discard\-my\-data] [\-\-protocol\ {A\ |\ B\ |\ C}] [\-\-timeout\ {\fIval\fR}] [\-\-max\-epoch\-size\ {\fIval\fR}] [\-\-max\-buffers\ {\fIval\fR}] [\-\-unplug\-watermark\ {\fIval\fR}] [\-\-connect\-int\ {\fIval\fR}] [\-\-ping\-int\ {\fIval\fR}] [\-\-sndbuf\-size\ {\fIval\fR}] [\-\-rcvbuf\-size\ {\fIval\fR}] [\-\-ko\-count\ {\fIval\fR}] [\-\-allow\-two\-primaries] [\-\-cram\-hmac\-alg\ {\fIval\fR}] [\-\-shared\-secret\ {\fIval\fR}] [\-\-after\-sb\-0pri\ {disconnect\ |\ discard\-younger\-primary\ |\ discard\-older\-primary\ |\ discard\-zero\-changes\ |\ discard\-least\-changes\ |\ discard\-local\ |\ discard\-remote}] [\-\-after\-sb\-1pri\ {disconnect\ |\ consensus\ |\ discard\-secondary\ |\ call\-pri\-lost\-after\-sb\ |\ violently\-as0p}] [\-\-after\-sb\-2pri\ {disconnect\ |\ call\-pri\-lost\-after\-sb\ |\ violently\-as0p}] [\-\-always\-asbp] [\-\-rr\-conflict\ {disconnect\ |\ call\-pri\-lost\ |\ violently}] [\-\-ping\-timeout\ {\fIval\fR}] [\-\-data\-integrity\-alg\ {\fIval\fR}] [\-\-tcp\-cork] [\-\-on\-congestion\ {block\ |\ pull\-ahead\ |\ disconnect}] [\-\-congestion\-fill\ {\fIval\fR}] [\-\-congestion\-extents\ {\fIval\fR}] [\-\-csums\-alg\ {\fIval\fR}] [\-\-csums\-after\-crash\-only] [\-\-verify\-alg\ {\fIval\fR}] [\-\-use\-rle] [\-\-socket\-check\-timeout\ {\fIval\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR disk\-options \fIminor\fR [\-\-on\-io\-error\ {pass_on\ |\ call\-local\-io\-error\ |\ detach}] [\-\-fencing\ {dont\-care\ |\ resource\-only\ |\ resource\-and\-stonith}] [\-\-disk\-barrier] [\-\-disk\-flushes] [\-\-disk\-drain] [\-\-md\-flushes] [\-\-resync\-rate\ {\fIval\fR}] [\-\-resync\-after\ {\fIval\fR}] [\-\-al\-extents\ {\fIval\fR}] [\-\-al\-updates] [\-\-discard\-zeroes\-if\-aligned] [\-\-c\-plan\-ahead\ {\fIval\fR}] [\-\-c\-delay\-target\ {\fIval\fR}] [\-\-c\-fill\-target\ {\fIval\fR}] [\-\-c\-max\-rate\ {\fIval\fR}] [\-\-c\-min\-rate\ {\fIval\fR}] [\-\-disk\-timeout\ {\fIval\fR}] [\-\-read\-balancing\ {prefer\-local\ |\ prefer\-remote\ |\ round\-robin\ |\ least\-pending\ |\ when\-congested\-remote\ |\ 32K\-striping\ |\ 64K\-striping\ |\ 128K\-striping\ |\ 256K\-striping\ |\ 512K\-striping\ |\ 1M\-striping}] [\-\-rs\-discard\-granularity\ {\fIval\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR net\-options \fIlocal_addr\fR \fIremote_addr\fR [\-\-protocol\ {A\ |\ B\ |\ C}] [\-\-timeout\ {\fIval\fR}] [\-\-max\-epoch\-size\ {\fIval\fR}] [\-\-max\-buffers\ {\fIval\fR}] [\-\-unplug\-watermark\ {\fIval\fR}] [\-\-connect\-int\ {\fIval\fR}] [\-\-ping\-int\ {\fIval\fR}] [\-\-sndbuf\-size\ {\fIval\fR}] [\-\-rcvbuf\-size\ {\fIval\fR}] [\-\-ko\-count\ {\fIval\fR}] [\-\-allow\-two\-primaries] [\-\-cram\-hmac\-alg\ {\fIval\fR}] [\-\-shared\-secret\ {\fIval\fR}] [\-\-after\-sb\-0pri\ {disconnect\ |\ discard\-younger\-primary\ |\ discard\-older\-primary\ |\ discard\-zero\-changes\ |\ discard\-least\-changes\ |\ discard\-local\ |\ discard\-remote}] [\-\-after\-sb\-1pri\ {disconnect\ |\ consensus\ |\ discard\-secondary\ |\ call\-pri\-lost\-after\-sb\ |\ violently\-as0p}] [\-\-after\-sb\-2pri\ {disconnect\ |\ call\-pri\-lost\-after\-sb\ |\ violently\-as0p}] [\-\-always\-asbp] [\-\-rr\-conflict\ {disconnect\ |\ call\-pri\-lost\ |\ violently}] [\-\-ping\-timeout\ {\fIval\fR}] [\-\-data\-integrity\-alg\ {\fIval\fR}] [\-\-tcp\-cork] [\-\-on\-congestion\ {block\ |\ pull\-ahead\ |\ disconnect}] [\-\-congestion\-fill\ {\fIval\fR}] [\-\-congestion\-extents\ {\fIval\fR}] [\-\-csums\-alg\ {\fIval\fR}] [\-\-csums\-after\-crash\-only] [\-\-verify\-alg\ {\fIval\fR}] [\-\-use\-rle] [\-\-socket\-check\-timeout\ {\fIval\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR resource\-options \fIresource\fR [\-\-cpu\-mask\ {\fIval\fR}] [\-\-on\-no\-data\-accessible\ {io\-error\ |\ suspend\-io}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR disconnect \fIlocal_addr\fR \fIremote_addr\fR [\-\-force] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR detach \fIminor\fR [\-\-force] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR primary \fIminor\fR [\-\-force] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR secondary \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR down \fIresource\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR verify \fIminor\fR [\-\-start\ {\fIval\fR}] [\-\-stop\ {\fIval\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR invalidate \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR invalidate\-remote \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR wait\-connect \fIminor\fR [\-\-wfc\-timeout\ {\fIval\fR}] [\-\-degr\-wfc\-timeout\ {\fIval\fR}] [\-\-outdated\-wfc\-timeout\ {\fIval\fR}] [\-\-wait\-after\-sb\ {\fIval\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR wait\-sync \fIminor\fR [\-\-wfc\-timeout\ {\fIval\fR}] [\-\-degr\-wfc\-timeout\ {\fIval\fR}] [\-\-outdated\-wfc\-timeout\ {\fIval\fR}] [\-\-wait\-after\-sb\ {\fIval\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR role \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR cstate \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR dstate \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR resize \fIminor\fR [\-\-size\ {\fIval\fR}] [\-\-assume\-peer\-has\-space] [\-\-assume\-clean] [\-\-al\-stripes\ {\fIval\fR}] [\-\-al\-stripe\-size\-kB\ {\fIval\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR check\-resize \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR pause\-sync \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR resume\-sync \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR outdate \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR show\-gi \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR get\-gi \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR show {\fIresource\fR | \fIminor\fR | \fIall\fR} .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR suspend\-io \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR resume\-io \fIminor\fR .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR status {\fIresource\fR | \fIall\fR} [\-\-color\ {\fIval\fR}] .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR events2 {\fIresource\fR | \fIall\fR} .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR events {\fIresource\fR | \fIminor\fR | \fIall\fR} .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR new\-current\-uuid \fIminor\fR [\-\-clear\-bitmap] .SH "DESCRIPTION" .PP drbdsetup is used to associate DRBD devices with their backing block devices, to set up DRBD device pairs to mirror their backing block devices, and to inspect the configuration of running DRBD devices\&. .SH "NOTE" .PP drbdsetup is a low level tool of the DRBD program suite\&. It is used by the data disk and drbd scripts to communicate with the device driver\&. .SH "COMMANDS" .PP Each drbdsetup sub\-command might require arguments and bring its own set of options\&. All values have default units which might be overruled by K, M or G\&. These units are defined in the usual way (e\&.g\&. K = 2^10 = 1024)\&. .SS "Common options" .PP All drbdsetup sub\-commands accept these two options .PP \fB\-\-create\-device\fR .RS 4 In case the specified DRBD device (minor number) does not exist yet, create it implicitly\&. .RE .SS "new\-resource" .PP Resources are the primary objects of any DRBD configuration\&. A resource must be created with the \fBnew\-resource\fR command before any volumes or minor devices can be created\&. Connections are referenced by name\&. .SS "new\-minor" .PP A \fIminor\fR is used as a synonym for replicated block device\&. It is represented in the /dev/ directory by a block device\&. It is the application\*(Aqs interface to the DRBD\-replicated block devices\&. These block devices get addressed by their minor numbers on the drbdsetup commandline\&. .PP A pair of replicated block devices may have different minor numbers on the two machines\&. They are associated by a common \fIvolume\-number\fR\&. Volume numbers are local to each connection\&. Minor numbers are global on one node\&. .SS "del\-resource" .PP Destroys a resource object\&. This is only possible if the resource has no volumes\&. .SS "del\-minor" .PP Minors can only be destroyed if its disk is detached\&. .SS "attach, disk\-options" .PP Attach associates \fIdevice\fR with \fIlower_device\fR to store its data blocks on\&. The \fB\-d\fR (or \fB\-\-disk\-size\fR) should only be used if you wish not to use as much as possible from the backing block devices\&. If you do not use \fB\-d\fR, the \fIdevice\fR is only ready for use as soon as it was connected to its peer once\&. (See the \fBnet\fR command\&.) .PP With the disk\-options command it is possible to change the options of a minor while it is attached\&. .PP \fB\-\-disk\-size \fR\fB\fIsize\fR\fR .RS 4 You can override DRBD\*(Aqs size determination method with this option\&. If you need to use the device before it was ever connected to its peer, use this option to pass the \fIsize\fR of the DRBD device to the driver\&. Default unit is sectors (1s = 512 bytes)\&. .sp If you use the \fIsize\fR parameter in drbd\&.conf, we strongly recommend to add an explicit unit postfix\&. drbdadm and drbdsetup used to have mismatching default units\&. .RE .PP \fB\-\-on\-io\-error \fR\fB\fIerr_handler\fR\fR .RS 4 If the driver of the \fIlower_device\fR reports an error to DRBD, DRBD will mark the disk as inconsistent, call a helper program, or detach the device from its backing storage and perform all further IO by requesting it from the peer\&. The valid \fIerr_handlers\fR are: \fBpass_on\fR, \fBcall\-local\-io\-error\fR and \fBdetach\fR\&. .RE .PP \fB\-\-fencing \fR\fB\fIfencing_policy\fR\fR .RS 4 Under \fBfencing\fR we understand preventive measures to avoid situations where both nodes are primary and disconnected (AKA split brain)\&. .sp Valid fencing policies are: .PP \fBdont\-care\fR .RS 4 This is the default policy\&. No fencing actions are done\&. .RE .PP \fBresource\-only\fR .RS 4 If a node becomes a disconnected primary, it tries to outdate the peer\*(Aqs disk\&. This is done by calling the fence\-peer handler\&. The handler is supposed to reach the other node over alternative communication paths and call \*(Aqdrbdadm outdate res\*(Aq there\&. .RE .PP \fBresource\-and\-stonith\fR .RS 4 If a node becomes a disconnected primary, it freezes all its IO operations and calls its fence\-peer handler\&. The fence\-peer handler is supposed to reach the peer over alternative communication paths and call \*(Aqdrbdadm outdate res\*(Aq there\&. In case it cannot reach the peer, it should stonith the peer\&. IO is resumed as soon as the situation is resolved\&. In case your handler fails, you can resume IO with the \fBresume\-io\fR command\&. .RE .RE .PP \fB\-\-disk\-barrier\fR, .br \fB\-\-disk\-flushes\fR, .br \fB\-\-disk\-drain\fR .RS 4 DRBD has four implementations to express write\-after\-write dependencies to its backing storage device\&. DRBD will use the first method that is supported by the backing storage device and that is not disabled\&. By default the \fIflush\fR method is used\&. .sp Since drbd\-8\&.4\&.2 \fBdisk\-barrier\fR is disabled by default because since linux\-2\&.6\&.36 (or 2\&.6\&.32 RHEL6) there is no reliable way to determine if queuing of IO\-barriers works\&. \fIDangerous\fR only enable if you are told so by one that knows for sure\&. .sp When selecting the method you should not only base your decision on the measurable performance\&. In case your backing storage device has a volatile write cache (plain disks, RAID of plain disks) you should use one of the first two\&. In case your backing storage device has battery\-backed write cache you may go with option 3\&. Option 4 (disable everything, use "none") \fIis dangerous\fR on most IO stacks, may result in write\-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles\&. \fIDo not use\fR \fBno\-disk\-drain\fR\&. .sp Unfortunately device mapper (LVM) might not support barriers\&. .sp The letter after "wo:" in /proc/drbd indicates with method is currently in use for a device: b, f, d, n\&. The implementations: .PP barrier .RS 4 The first requires that the driver of the backing storage device support barriers (called \*(Aqtagged command queuing\*(Aq in SCSI and \*(Aqnative command queuing\*(Aq in SATA speak)\&. The use of this method can be enabled by setting the \fBdisk\-barrier\fR options to \fByes\fR\&. .RE .PP flush .RS 4 The second requires that the backing device support disk flushes (called \*(Aqforce unit access\*(Aq in the drive vendors speak)\&. The use of this method can be disabled setting \fBdisk\-flushes\fR to \fBno\fR\&. .RE .PP drain .RS 4 The third method is simply to let write requests drain before write requests of a new reordering domain are issued\&. That was the only implementation before 8\&.0\&.9\&. .RE .PP none .RS 4 The fourth method is to not express write\-after\-write dependencies to the backing store at all, by also specifying \fB\-\-no\-disk\-drain\fR\&. This \fIis dangerous\fR on most IO stacks, may result in write\-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles\&. \fIDo not use\fR \fB\-\-no\-disk\-drain\fR\&. .RE .RE .PP \fB\-\-md\-flushes\fR .RS 4 Disables the use of disk flushes and barrier BIOs when accessing the meta data device\&. See the notes on \fB\-\-disk\-flushes\fR\&. .RE .PP \fB\-\-max\-bio\-bvecs\fR .RS 4 In some special circumstances the device mapper stack manages to pass BIOs to DRBD that violate the constraints that are set forth by DRBD\*(Aqs merge_bvec() function and which have more than one bvec\&. A known example is: phys\-disk \-> DRBD \-> LVM \-> Xen \-> missaligned partition (63) \-> DomU FS\&. Then you might see "bio would need to, but cannot, be split:" in the Dom0\*(Aqs kernel log\&. .sp The best workaround is to proper align the partition within the VM (E\&.g\&. start it at sector 1024)\&. That costs 480 KiB of storage\&. Unfortunately the default of most Linux partitioning tools is to start the first partition at an odd number (63)\&. Therefore most distributions install helpers for virtual linux machines will end up with missaligned partitions\&. The second best workaround is to limit DRBD\*(Aqs max bvecs per BIO (i\&.e\&., the \fBmax\-bio\-bvecs\fR option) to 1, but that might cost performance\&. .sp The default value of \fBmax\-bio\-bvecs\fR is 0, which means that there is no user imposed limitation\&. .RE .PP \fB\-\-resync\-rate \fR\fB\fIrate\fR\fR .RS 4 To ensure smooth operation of the application on top of DRBD, it is possible to limit the bandwidth that may be used by background synchronization\&. The default is 250 KiB/sec, the default unit is KiB/sec\&. .RE .PP \fB\-\-resync\-after \fR\fB\fIminor\fR\fR .RS 4 Start resync on this device only if the device with \fIminor\fR is already in connected state\&. Otherwise this device waits in SyncPause state\&. .RE .PP \fB\-\-al\-extents \fR\fB\fIextents\fR\fR .RS 4 DRBD automatically performs hot area detection\&. With this parameter you control how big the hot area (=active set) can get\&. Each extent marks 4M of the backing storage\&. In case a primary node leaves the cluster unexpectedly, the areas covered by the active set must be resynced upon rejoining of the failed node\&. The data structure is stored in the meta\-data area, therefore each change of the active set is a write operation to the meta\-data device\&. A higher number of extents gives longer resync times but less updates to the meta\-data\&. The default number of \fIextents\fR is 1237\&. (Minimum: 7, Maximum: 65534) .sp See also \fBdrbd.conf\fR(5) and \fBdrbdmeta\fR(8) for additional limitations and necessary preparation\&. .RE .PP \fB\-\-al\-updates \fR\fB{yes | no}\fR .RS 4 DRBD\*(Aqs activity log transaction writing makes it possible, that after the crash of a primary node a partial (bit\-map based) resync is sufficient to bring the node back to up\-to\-date\&. Setting \fBal\-updates\fR to \fBno\fR might increase normal operation performance but causes DRBD to do a full resync when a crashed primary gets reconnected\&. The default value is \fByes\fR\&. .RE .PP \fB\-\-c\-plan\-ahead \fR\fB\fIplan_time\fR\fR, .br \fB\-\-c\-fill\-target \fR\fB\fIfill_target\fR\fR, .br \fB\-\-c\-delay\-target \fR\fB\fIdelay_target\fR\fR, .br \fB\-\-c\-max\-rate \fR\fB\fImax_rate\fR\fR .RS 4 The dynamic resync speed controller gets enabled with setting \fIplan_time\fR to a positive value\&. It aims to fill the buffers along the data path with either a constant amount of data \fIfill_target\fR, or aims to have a constant delay time of \fIdelay_target\fR along the path\&. The controller has an upper bound of \fImax_rate\fR\&. .sp By \fIplan_time\fR the agility of the controller is configured\&. Higher values yield for slower/lower responses of the controller to deviation from the target value\&. It should be at least 5 times RTT\&. For regular data paths a \fIfill_target\fR in the area of 4k to 100k is appropriate\&. For a setup that contains drbd\-proxy it is advisable to use \fIdelay_target\fR instead\&. Only when \fIfill_target\fR is set to 0 the controller will use \fIdelay_target\fR\&. 5 times RTT is a reasonable starting value\&. \fIMax_rate\fR should be set to the bandwidth available between the DRBD\-hosts and the machines hosting DRBD\-proxy, or to the available disk\-bandwidth\&. .sp The default value of \fIplan_time\fR is 0, the default unit is 0\&.1 seconds\&. \fIFill_target\fR has 0 and sectors as default unit\&. \fIDelay_target\fR has 1 (100ms) and 0\&.1 as default unit\&. \fIMax_rate\fR has 10240 (100MiB/s) and KiB/s as default unit\&. .RE .PP \fB\-\-c\-min\-rate \fR\fB\fImin_rate\fR\fR .RS 4 We track the disk IO rate caused by the resync, so we can detect non\-resync IO on the lower level device\&. If the lower level device seems to be busy, and the current resync rate is above \fImin_rate\fR, we throttle the resync\&. .sp The default value of \fImin_rate\fR is 4M, the default unit is k\&. If you want to not throttle at all, set it to zero, if you want to throttle always, set it to one\&. .RE .PP \fB\-t\fR, \fB\-\-disk\-timeout \fR\fB\fIdisk_timeout\fR\fR .RS 4 If the lower\-level device on which a DRBD device stores its data does not finish an I/O request within the defined \fBdisk\-timeout\fR, DRBD treats this as a failure\&. The lower\-level device is detached, and the device\*(Aqs disk state advances to Diskless\&. If DRBD is connected to one or more peers, the failed request is passed on to one of them\&. .sp This option is \fIdangerous and may lead to kernel panic!\fR .sp "Aborting" requests, or force\-detaching the disk, is intended for completely blocked/hung local backing devices which do no longer complete requests at all, not even do error completions\&. In this situation, usually a hard\-reset and failover is the only way out\&. .sp By "aborting", basically faking a local error\-completion, we allow for a more graceful swichover by cleanly migrating services\&. Still the affected node has to be rebooted "soon"\&. .sp By completing these requests, we allow the upper layers to re\-use the associated data pages\&. .sp If later the local backing device "recovers", and now DMAs some data from disk into the original request pages, in the best case it will just put random data into unused pages; but typically it will corrupt meanwhile completely unrelated data, causing all sorts of damage\&. .sp Which means delayed successful completion, especially for READ requests, is a reason to panic()\&. We assume that a delayed *error* completion is OK, though we still will complain noisily about it\&. .sp The default value of \fBdisk\-timeout\fR is 0, which stands for an infinite timeout\&. Timeouts are specified in units of 0\&.1 seconds\&. This option is available since DRBD 8\&.3\&.12\&. .RE .PP \fB\-\-discard\-zeroes\-if\-aligned \fR\fB{yes | no}\fR .RS 4 Setting \fBdiscard\-zeroes\-if\-aligned\fR to \fBno\fR will cause DRBD to always fall\-back to zero\-out on the receiving side, and to not even announce discard capabilities on the Primary, if the respective backend announces discard_zeroes_data=false\&. .sp Setting \fBdiscards\-zeroes\-if\-aligned\fR to \fByes\fR will allow DRBD to use discards, and to announce discard_zeroes=true, even on backends that announce discard_zeroes_data=false\&. .sp We used to ignore the discard_zeroes_data setting completely\&. To not break established and expected behaviour, the default value is \fByes\fR\&. .sp This option is available since 8\&.4\&.7\&. See also \fBdrbd.conf\fR(5)\&. .RE .PP \fB\-\-read\-balancing \fR\fB\fImethod\fR\fR .RS 4 The supported \fImethods\fR for load balancing of read requests are \fBprefer\-local\fR, \fBprefer\-remote\fR, \fBround\-robin\fR, \fBleast\-pending\fR and \fBwhen\-congested\-remote\fR, \fB32K\-striping\fR, \fB64K\-striping\fR, \fB128K\-striping\fR, \fB256K\-striping\fR, \fB512K\-striping\fR and \fB1M\-striping\fR\&. .sp The default value of is \fBprefer\-local\fR\&. This option is available since 8\&.4\&.1\&. .RE .PP \fB\-\-rs\-discard\-granularity \fR\fB\fIbytes\fR\fR .RS 4 When \fBrs\-discard\-granularity\fR is set to a non zero, positive value then DRBD tries to do a resync operation in requests of this size\&. In case such a block contains only zero bytes on the sync source node, the sync target node will issue a discard/trim/unmap command for the area\&. .sp The value is constrained by the discard granularity of the backing block device\&. In case \fBrs\-discard\-granularity\fR is not a multiplier of the discard granularity of the backing block device DRBD rounds it up\&. The feature only gets active if the backing block device reads back zeroes after a discard command\&. .sp The default value of is 0\&. This option is available since 8\&.4\&.7\&. .RE .SS "connect, net\-options" .PP Connect sets up the \fIdevice\fR to listen on \fIaf:local_addr:port\fR for incoming connections and to try to connect to \fIaf:remote_addr:port\fR\&. If \fIport\fR is omitted, 7788 is used as default\&. If \fIaf\fR is omitted \fBipv4\fR gets used\&. Other supported address families are \fBipv6\fR, \fBssocks\fR for Dolphin Interconnect Solutions\*(Aq "super sockets" and \fBsdp\fR for Sockets Direct Protocol (Infiniband)\&. .PP The net\-options command allows you to change options while the connection is established\&. .PP \fB\-\-protocol \fR\fB\fIprotocol\fR\fR .RS 4 On the TCP/IP link the specified \fIprotocol\fR is used\&. Valid protocol specifiers are A, B, and C\&. .sp Protocol A: write IO is reported as completed, if it has reached local disk and local TCP send buffer\&. .sp Protocol B: write IO is reported as completed, if it has reached local disk and remote buffer cache\&. .sp Protocol C: write IO is reported as completed, if it has reached both local and remote disk\&. .RE .PP \fB\-\-connect\-int \fR\fB\fItime\fR\fR .RS 4 In case it is not possible to connect to the remote DRBD device immediately, DRBD keeps on trying to connect\&. With this option you can set the time between two retries\&. The default value is 10\&. The unit is seconds\&. .RE .PP \fB\-\-ping\-int \fR\fB\fItime\fR\fR .RS 4 If the TCP/IP connection linking a DRBD device pair is idle for more than \fItime\fR seconds, DRBD will generate a keep\-alive packet to check if its partner is still alive\&. The default value is 10\&. The unit is seconds\&. .RE .PP \fB\-\-timeout \fR\fB\fIval\fR\fR .RS 4 If the partner node fails to send an expected response packet within \fIval\fR tenths of a second, the partner node is considered dead and therefore the TCP/IP connection is abandoned\&. The default value is 60 (= 6 seconds)\&. .RE .PP \fB\-\-sndbuf\-size \fR\fB\fIsize\fR\fR .RS 4 The socket send buffer is used to store packets sent to the secondary node, which are not yet acknowledged (from a network point of view) by the secondary node\&. When using protocol A, it might be necessary to increase the size of this data structure in order to increase asynchronicity between primary and secondary nodes\&. But keep in mind that more asynchronicity is synonymous with more data loss in the case of a primary node failure\&. Since 8\&.0\&.13 resp\&. 8\&.2\&.7 setting the \fIsize\fR value to 0 means that the kernel should autotune this\&. The default \fIsize\fR is 0, i\&.e\&. autotune\&. .RE .PP \fB\-\-rcvbuf\-size \fR\fB\fIsize\fR\fR .RS 4 Packets received from the network are stored in the socket receive buffer first\&. From there they are consumed by DRBD\&. Before 8\&.3\&.2 the receive buffer\*(Aqs size was always set to the size of the socket send buffer\&. Since 8\&.3\&.2 they can be tuned independently\&. A value of 0 means that the kernel should autotune this\&. The default \fIsize\fR is 0, i\&.e\&. autotune\&. .RE .PP \fB\-\-ko\-count \fR\fB\fIcount\fR\fR .RS 4 In case the secondary node fails to complete a single write request for \fIcount\fR times the \fItimeout\fR, it is expelled from the cluster, i\&.e\&. the primary node goes into StandAlone mode\&. To disable this feature, you should explicitly set it to 0; defaults may change between versions\&. .RE .PP \fB\-\-max\-epoch\-size \fR\fB\fIval\fR\fR .RS 4 With this option the maximal number of write requests between two barriers is limited\&. Typically set to the same as \fB\-\-max\-buffers\fR, or the allowed maximum\&. Values smaller than 10 can lead to degraded performance\&. The default value is 2048\&. .RE .PP \fB\-\-max\-buffers \fR\fB\fIval\fR\fR .RS 4 With this option the maximal number of buffer pages allocated by DRBD\*(Aqs receiver thread is limited\&. Typically set to the same as \fB\-\-max\-epoch\-size\fR\&. Small values could lead to degraded performance\&. The default value is 2048, the minimum 32\&. Increase this if you cannot saturate the IO backend of the receiving side during linear write or during resync while otherwise idle\&. .sp See also \fBdrbd.conf\fR(5) .RE .PP \fB\-\-unplug\-watermark \fR\fB\fIval\fR\fR .RS 4 This setting has no effect with recent kernels that use explicit on\-stack plugging (upstream Linux kernel 2\&.6\&.39, distributions may have backported)\&. .sp When the number of pending write requests on the standby (secondary) node exceeds the unplug\-watermark, we trigger the request processing of our backing storage device\&. Some storage controllers deliver better performance with small values, others deliver best performance when the value is set to the same value as max\-buffers, yet others don\*(Aqt feel much effect at all\&. Minimum 16, default 128, maximum 131072\&. .RE .PP \fB\-\-allow\-two\-primaries \fR .RS 4 With this option set you may assign primary role to both nodes\&. You only should use this option if you use a shared storage file system on top of DRBD\&. At the time of writing the only ones are: OCFS2 and GFS\&. If you use this option with any other file system, you are going to crash your nodes and to corrupt your data! .RE .PP \fB\-\-cram\-hmac\-alg \fR\fB\fIalg\fR\fR .RS 4 You need to specify the HMAC algorithm to enable peer authentication at all\&. You are strongly encouraged to use peer authentication\&. The HMAC algorithm will be used for the challenge response authentication of the peer\&. You may specify any digest algorithm that is named in /proc/crypto\&. .RE .PP \fB\-\-shared\-secret \fR\fB\fIsecret\fR\fR .RS 4 The shared secret used in peer authentication\&. May be up to 64 characters\&. .RE .PP \fB\-\-after\-sb\-0pri \fR\fB\fIasb\-0p\-policy\fR\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBdiscard\-younger\-primary\fR .RS 4 Auto sync from the node that was primary before the split\-brain situation occurred\&. .RE .PP \fBdiscard\-older\-primary\fR .RS 4 Auto sync from the node that became primary as second during the split\-brain situation\&. .RE .PP \fBdiscard\-zero\-changes\fR .RS 4 In case one node did not write anything since the split brain became evident, sync from the node that wrote something to the node that did not write anything\&. In case none wrote anything this policy uses a random decision to perform a "resync" of 0 blocks\&. In case both have written something this policy disconnects the nodes\&. .RE .PP \fBdiscard\-least\-changes\fR .RS 4 Auto sync from the node that touched more blocks during the split brain situation\&. .RE .PP \fBdiscard\-node\-NODENAME\fR .RS 4 Auto sync to the named node\&. .RE .RE .PP \fB\-\-after\-sb\-1pri \fR\fB\fIasb\-1p\-policy\fR\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBconsensus\fR .RS 4 Discard the version of the secondary if the outcome of the \fBafter\-sb\-0pri\fR algorithm would also destroy the current secondary\*(Aqs data\&. Otherwise disconnect\&. .RE .PP \fBdiscard\-secondary\fR .RS 4 Discard the secondary\*(Aqs version\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Always honor the outcome of the \fBafter\-sb\-0pri \fR algorithm\&. In case it decides the current secondary has the correct data, call the \fBpri\-lost\-after\-sb\fR on the current primary\&. .RE .PP \fBviolently\-as0p\fR .RS 4 Always honor the outcome of the \fBafter\-sb\-0pri \fR algorithm\&. In case it decides the current secondary has the correct data, accept a possible instantaneous change of the primary\*(Aqs data\&. .RE .RE .PP \fB\-\-after\-sb\-2pri \fR\fB\fIasb\-2p\-policy\fR\fR .RS 4 possible policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Always honor the outcome of the \fBafter\-sb\-0pri \fR algorithm\&. In case it decides the current secondary has the right data, call the \fBpri\-lost\-after\-sb\fR on the current primary\&. .RE .PP \fBviolently\-as0p\fR .RS 4 Always honor the outcome of the \fBafter\-sb\-0pri \fR algorithm\&. In case it decides the current secondary has the right data, accept a possible instantaneous change of the primary\*(Aqs data\&. .RE .RE .PP \fB\-\-always\-asbp\fR .RS 4 Normally the automatic after\-split\-brain policies are only used if current states of the UUIDs do not indicate the presence of a third node\&. .sp With this option you request that the automatic after\-split\-brain policies are used as long as the data sets of the nodes are somehow related\&. This might cause a full sync, if the UUIDs indicate the presence of a third node\&. (Or double faults have led to strange UUID sets\&.) .RE .PP \fB\-\-rr\-conflict \fR\fB\fIrole\-resync\-conflict\-policy\fR\fR .RS 4 This option sets DRBD\*(Aqs behavior when DRBD deduces from its meta data that a resynchronization is needed, and the SyncTarget node is already primary\&. The possible settings are: \fBdisconnect\fR, \fBcall\-pri\-lost\fR and \fBviolently\fR\&. While \fBdisconnect\fR speaks for itself, with the \fBcall\-pri\-lost\fR setting the \fBpri\-lost\fR handler is called which is expected to either change the role of the node to secondary, or remove the node from the cluster\&. The default is \fBdisconnect\fR\&. .sp With the \fBviolently\fR setting you allow DRBD to force a primary node into SyncTarget state\&. This means that the data exposed by DRBD changes to the SyncSource\*(Aqs version of the data instantaneously\&. USE THIS OPTION ONLY IF YOU KNOW WHAT YOU ARE DOING\&. .RE .PP \fB\-\-data\-integrity\-alg \fR\fB\fIhash_alg\fR\fR .RS 4 DRBD can ensure the data integrity of the user\*(Aqs data on the network by comparing hash values\&. Normally this is ensured by the 16 bit checksums in the headers of TCP/IP packets\&. This option can be set to any of the kernel\*(Aqs data digest algorithms\&. In a typical kernel configuration you should have at least one of \fBmd5\fR, \fBsha1\fR, and \fBcrc32c\fR available\&. By default this is not enabled\&. .sp See also the notes on data integrity on the drbd\&.conf manpage\&. .RE .PP \fB\-\-no\-tcp\-cork\fR .RS 4 DRBD usually uses the TCP socket option TCP_CORK to hint to the network stack when it can expect more data, and when it should flush out what it has in its send queue\&. There is at least one network stack that performs worse when one uses this hinting method\&. Therefore we introduced this option, which disable the setting and clearing of the TCP_CORK socket option by DRBD\&. .RE .PP \fB\-\-ping\-timeout \fR\fB\fIping_timeout\fR\fR .RS 4 The time the peer has to answer to a keep\-alive packet\&. In case the peer\*(Aqs reply is not received within this time period, it is considered dead\&. The default unit is tenths of a second, the default value is 5 (for half a second)\&. .RE .PP \fB\-\-discard\-my\-data\fR .RS 4 Use this option to manually recover from a split\-brain situation\&. In case you do not have any automatic after\-split\-brain policies selected, the nodes refuse to connect\&. By passing this option you make this node a sync target immediately after successful connect\&. .RE .PP \fB\-\-tentative\fR .RS 4 Causes DRBD to abort the connection process after the resync handshake, i\&.e\&. no resync gets performed\&. You can find out which resync DRBD would perform by looking at the kernel\*(Aqs log file\&. .RE .PP \fB\-\-on\-congestion \fR\fB\fIcongestion_policy\fR\fR, .br \fB\-\-congestion\-fill \fR\fB\fIfill_threshold\fR\fR, .br \fB\-\-congestion\-extents \fR\fB\fIactive_extents_threshold\fR\fR .RS 4 By default DRBD blocks when the available TCP send queue becomes full\&. That means it will slow down the application that generates the write requests that cause DRBD to send more data down that TCP connection\&. .sp When DRBD is deployed with DRBD\-proxy it might be more desirable that DRBD goes into AHEAD/BEHIND mode shortly before the send queue becomes full\&. In AHEAD/BEHIND mode DRBD does no longer replicate data, but still keeps the connection open\&. .sp The advantage of the AHEAD/BEHIND mode is that the application is not slowed down, even if DRBD\-proxy\*(Aqs buffer is not sufficient to buffer all write requests\&. The downside is that the peer node falls behind, and that a resync will be necessary to bring it back into sync\&. During that resync the peer node will have an inconsistent disk\&. .sp Available \fIcongestion_policy\fRs are \fBblock\fR and \fBpull\-ahead\fR\&. The default is \fBblock\fR\&. \fIFill_threshold\fR might be in the range of 0 to 10GiBytes\&. The default is 0 which disables the check\&. \fIActive_extents_threshold\fR has the same limits as \fBal\-extents\fR\&. .sp The AHEAD/BEHIND mode and its settings are available since DRBD 8\&.3\&.10\&. .RE .PP \fB\-\-verify\-alg \fR\fB\fIhash\-alg\fR\fR .RS 4 During online verification (as initiated by the \fBverify\fR sub\-command), rather than doing a bit\-wise comparison, DRBD applies a hash function to the contents of every block being verified, and compares that hash with the peer\&. This option defines the hash algorithm being used for that purpose\&. It can be set to any of the kernel\*(Aqs data digest algorithms\&. In a typical kernel configuration you should have at least one of \fBmd5\fR, \fBsha1\fR, and \fBcrc32c\fR available\&. By default this is not enabled; you must set this option explicitly in order to be able to use on\-line device verification\&. .sp See also the notes on data integrity on the drbd\&.conf manpage\&. .RE .PP \fB\-\-csums\-alg \fR\fB\fIhash\-alg\fR\fR .RS 4 A resync process sends all marked data blocks form the source to the destination node, as long as no \fBcsums\-alg\fR is given\&. When one is specified the resync process exchanges hash values of all marked blocks first, and sends only those data blocks over, that have different hash values\&. .sp This setting is useful for DRBD setups with low bandwidth links\&. During the restart of a crashed primary node, all blocks covered by the activity log are marked for resync\&. But a large part of those will actually be still in sync, therefore using \fBcsums\-alg\fR will lower the required bandwidth in exchange for CPU cycles\&. .RE .PP \fB\-\-use\-rle\fR .RS 4 During resync\-handshake, the dirty\-bitmaps of the nodes are exchanged and merged (using bit\-or), so the nodes will have the same understanding of which blocks are dirty\&. On large devices, the fine grained dirty\-bitmap can become large as well, and the bitmap exchange can take quite some time on low\-bandwidth links\&. .sp Because the bitmap typically contains compact areas where all bits are unset (clean) or set (dirty), a simple run\-length encoding scheme can considerably reduce the network traffic necessary for the bitmap exchange\&. .sp For backward compatibility reasons, and because on fast links this possibly does not improve transfer time but consumes cpu cycles, this defaults to off\&. .sp Introduced in 8\&.3\&.2\&. .RE .PP \fB\-\-socket\-check\-timeout\fR .RS 4 In setups involving a DRBD\-proxy and connections that experience a lot of buffer\-bloat it might be necessary to set \fBping\-timeout\fR to an unusual high value\&. By default DRBD uses the same value to wait if a newly established TCP\-connection is stable\&. Since the DRBD\-proxy is usually located in the same data center such a long wait time may hinder DRBD\*(Aqs connect process\&. .sp In such setups \fBsocket\-check\-timeout\fR should be set to at least to the round trip time between DRBD and DRBD\-proxy\&. I\&.e\&. in most cases to 1\&. .sp The default unit is tenths of a second, the default value is 0 (which causes DRBD to use the value of \fBping\-timeout\fR instead)\&. Introduced in 8\&.4\&.5\&. .RE .SS "resource\-options" .PP Changes the options of the resource at runtime\&. .PP \fB\-\-cpu\-mask \fR\fB\fIcpu\-mask\fR\fR .RS 4 Sets the cpu\-affinity\-mask for DRBD\*(Aqs kernel threads of this device\&. The default value of \fIcpu\-mask\fR is 0, which means that DRBD\*(Aqs kernel threads should be spread over all CPUs of the machine\&. This value must be given in hexadecimal notation\&. If it is too big it will be truncated\&. .RE .PP \fB\-\-on\-no\-data\-accessible \fR\fB\fIond\-policy\fR\fR .RS 4 This setting controls what happens to IO requests on a degraded, disk less node (I\&.e\&. no data store is reachable)\&. The available policies are \fBio\-error\fR and \fBsuspend\-io\fR\&. .sp If \fIond\-policy\fR is set to \fBsuspend\-io\fR you can either resume IO by attaching/connecting the last lost data storage, or by the \fBdrbdadm resume\-io \fR\fB\fIres\fR\fR command\&. The latter will result in IO errors of course\&. .sp The default is \fBio\-error\fR\&. This setting is available since DRBD 8\&.3\&.9\&. .RE .SS "primary" .PP Sets the \fIdevice\fR into primary role\&. This means that applications (e\&.g\&. a file system) may open the \fIdevice\fR for read and write access\&. Data written to the \fIdevice\fR in primary role are mirrored to the device in secondary role\&. .PP Normally it is not possible to set both devices of a connected DRBD device pair to primary role\&. By using the \fB\-\-allow\-two\-primaries\fR option, you override this behavior and instruct DRBD to allow two primaries\&. .PP \fB\-\-overwrite\-data\-of\-peer\fR .RS 4 Alias for \-\-force\&. .RE .PP \fB\-\-force\fR .RS 4 Becoming primary fails if the local replica is not up\-to\-date\&. I\&.e\&. when it is inconsistent, outdated of consistent\&. By using this option you can force it into primary role anyway\&. USE THIS OPTION ONLY IF YOU KNOW WHAT YOU ARE DOING\&. .RE .SS "secondary" .PP Brings the \fIdevice\fR into secondary role\&. This operation fails as long as at least one application (or file system) has opened the device\&. .PP It is possible that both devices of a connected DRBD device pair are secondary\&. .SS "verify" .PP This initiates on\-line device verification\&. During on\-line verification, the contents of every block on the local node are compared to those on the peer node\&. Device verification progress can be monitored via /proc/drbd\&. Any blocks whose content differs from that of the corresponding block on the peer node will be marked out\-of\-sync in DRBD\*(Aqs on\-disk bitmap; they are \fInot\fR brought back in sync automatically\&. To do that, simply disconnect and reconnect the resource\&. .PP If on\-line verification is already in progress (and this node is "VerifyS"), this command silently "succeeds"\&. In this case, any start\-sector (see below) will be ignored, and any stop\-sector (see below) will be honored\&. This can be used to stop a running verify, or to update/shorten/extend the coverage of the currently running verify\&. .PP This command will fail if the \fIdevice\fR is not part of a connected device pair\&. .PP See also the notes on data integrity on the drbd\&.conf manpage\&. .PP \fB\-\-start \fR\fB\fIstart\-sector\fR\fR .RS 4 Since version 8\&.3\&.2, on\-line verification should resume from the last position after connection loss\&. It may also be started from an arbitrary position by setting this option\&. If you had reached some stop\-sector before, and you do not specify an explicit start\-sector, verify should resume from the previous stop\-sector\&. .sp Default unit is sectors\&. You may also specify a unit explicitly\&. The \fBstart\-sector\fR will be rounded down to a multiple of 8 sectors (4kB)\&. .RE .PP \fB\-S\fR, \fB\-\-stop \fR\fB\fIstop\-sector\fR\fR .RS 4 Since version 8\&.3\&.14, on\-line verification can be stopped before it reaches end\-of\-device\&. .sp Default unit is sectors\&. You may also specify a unit explicitly\&. The \fBstop\-sector\fR may be updated by issuing an additional drbdsetup verify command on the same node while the verify is running\&. This can be used to stop a running verify, or to update/shorten/extend the coverage of the currently running verify\&. .RE .SS "invalidate" .PP This forces the local device of a pair of connected DRBD devices into SyncTarget state, which means that all data blocks of the device are copied over from the peer\&. .PP This command will fail if the \fIdevice\fR is not either part of a connected device pair, or disconnected Secondary\&. .SS "invalidate\-remote" .PP This forces the local device of a pair of connected DRBD devices into SyncSource state, which means that all data blocks of the device are copied to the peer\&. .PP On a disconnected Primary device, this will set all bits in the out of sync bitmap\&. As a side affect this suspends updates to the on disk activity log\&. Updates to the on disk activity log resume automatically when necessary\&. .SS "wait\-connect" .PP Returns as soon as the \fIdevice\fR can communicate with its partner device\&. .PP \fB\-\-wfc\-timeout \fR\fB\fIwfc_timeout\fR\fR, .br \fB\-\-degr\-wfc\-timeout \fR\fB\fIdegr_wfc_timeout\fR\fR, .br \fB\-\-outdated\-wfc\-timeout \fR\fB\fIoutdated_wfc_timeout\fR\fR, .br \fB\-\-wait\-after\-sb\fR .RS 4 This command will fail if the \fIdevice\fR cannot communicate with its partner for \fItimeout\fR seconds\&. If the peer was working before this node was rebooted, the \fIwfc_timeout\fR is used\&. If the peer was already down before this node was rebooted, the \fIdegr_wfc_timeout\fR is used\&. If the peer was successfully outdated before this node was rebooted the \fIoutdated_wfc_timeout\fR is used\&. The default value for all those timeout values is 0 which means to wait forever\&. The unit is seconds\&. In case the connection status goes down to StandAlone because the peer appeared but the devices had a split brain situation, the default for the command is to terminate\&. You can change this behavior with the \fB\-\-wait\-after\-sb\fR option\&. .RE .SS "wait\-sync" .PP Returns as soon as the \fIdevice\fR leaves any synchronization into connected state\&. The options are the same as with the \fIwait\-connect\fR command\&. .SS "disconnect" .PP Removes the information set by the \fBnet\fR command from the \fIdevice\fR\&. This means that the \fIdevice\fR goes into unconnected state and will no longer listen for incoming connections\&. .SS "detach" .PP Removes the information set by the \fBdisk\fR command from the \fIdevice\fR\&. This means that the \fIdevice\fR is detached from its backing storage device\&. .PP \fB\-f\fR, \fB\-\-force\fR .RS 4 A regular detach returns after the disk state finally reached diskless\&. As a consequence detaching from a frozen backing block device never terminates\&. .sp On the other hand A forced detach returns immediately\&. It allows you to detach DRBD from a frozen backing block device\&. Please note that the disk will be marked as failed until all pending IO requests where finished by the backing block device\&. .RE .SS "down" .PP Removes all configuration information from the \fIdevice\fR and forces it back to unconfigured state\&. .SS "role" .PP Shows the current roles of the \fIdevice\fR and its peer, as \fIlocal\fR/\fIpeer\fR\&. .SS "state" .PP Deprecated alias for "role" .SS "cstate" .PP Shows the current connection state of the \fIdevice\fR\&. .SS "dstate" .PP Shows the current states of the backing storage devices, as \fIlocal\fR/\fIpeer\fR\&. .SS "resize" .PP This causes DRBD to reexamine the size of the \fIdevice\fR\*(Aqs backing storage device\&. To actually do online growing you need to extend the backing storages on both devices and call the \fBresize\fR command on one of your nodes\&. .PP The \fB\-\-size\fR option can be used to online shrink the usable size of a drbd device\&. It\*(Aqs the users responsibility to make sure that a file system on the device is not truncated by that operation\&. .PP The \fB\-\-assume\-peer\-has\-space\fR allows you to resize a device which is currently not connected to the peer\&. Use with care, since if you do not resize the peer\*(Aqs disk as well, further connect attempts of the two will fail\&. .PP When the \fB\-\-assume\-clean\fR option is given DRBD will skip the resync of the new storage\&. Only do this if you know that the new storage was initialized to the same content by other means\&. .PP The options \fB\-\-al\-stripes\fR and \fB\-\-al\-stripe\-size\-kB\fR may be used to change the layout of the activity log online\&. In case of internal meta data this may invovle shrinking the user visible size at the same time (unsing the \fB\-\-size\fR) or increasing the avalable space on the backing devices\&. .SS "check\-resize" .PP To enable DRBD to detect offline resizing of backing devices this command may be used to record the current size of backing devices\&. The size is stored in files in /var/lib/drbd/ named drbd\-minor\-??\&.lkbd .PP This command is called by \fBdrbdadm resize \fR\fB\fIres\fR\fR after \fBdrbdsetup \fR\fB\fIdevice\fR\fR\fB resize\fR returned\&. .SS "pause\-sync" .PP Temporarily suspend an ongoing resynchronization by setting the local pause flag\&. Resync only progresses if neither the local nor the remote pause flag is set\&. It might be desirable to postpone DRBD\*(Aqs resynchronization after eventual resynchronization of the backing storage\*(Aqs RAID setup\&. .SS "resume\-sync" .PP Unset the local sync pause flag\&. .SS "outdate" .PP Mark the data on the local backing storage as outdated\&. An outdated device refuses to become primary\&. This is used in conjunction with \fBfencing\fR and by the peer\*(Aqs \fBfence\-peer\fR handler\&. .SS "show\-gi" .PP Displays the device\*(Aqs data generation identifiers verbosely\&. .SS "get\-gi" .PP Displays the device\*(Aqs data generation identifiers\&. .SS "show" .PP Shows all available configuration information of a resource, or of all resources\&. Available options: .PP \fB\-\-show\-defaults\fR .RS 4 Show all configuration parameters, even the ones with default values\&. Normally, parameters with default values are not shown\&. .RE .SS "suspend\-io" .PP This command is of no apparent use and just provided for the sake of completeness\&. .SS "resume\-io" .PP If the fence\-peer handler fails to stonith the peer node, and your \fBfencing\fR policy is set to resource\-and\-stonith, you can unfreeze IO operations with this command\&. .SS "status" .PP Show the status of a resource, or of all resources\&. The output consists of one paragraph for each configured resource\&. Each paragraph contains one line for each resource, followed by one line for each device, and one line for each connection\&. The device and connection lines are indented\&. The connection lines are followed by one line for each peer device; these lines are indented against the connection line\&. .PP Long lines are wrapped around at terminal width, and indented to indicate how the lines belongs together\&. Available options: .PP \fB\-\-verbose\fR .RS 4 Include more information in the output even when it is likely redundant or irrelevant\&. .RE .PP \fB\-\-statistics\fR .RS 4 Include data transfer statistics in the output\&. .RE .PP \fB\-\-color=\fR\fB{always | auto | never}\fR\fB \fR .RS 4 Colorize the output\&. With \fB\-\-color=auto\fR, \fBdrbdsetup\fR emits color codes only when standard output is connected to a terminal\&. .RE .PP For example, the non\-verbose output for a resource with only one connection and only one volume could look like this: .sp .if n \{\ .RS 4 .\} .nf fs\-backoffice role:Primary disk:UpToDate peer role:Secondary replication:Established peer\-disk:UpToDate .fi .if n \{\ .RE .\} .PP With the \fB\-\-verbose\fR \fB\-\-statistics\fR options, the same resource could be reported as: .sp .if n \{\ .RS 4 .\} .nf fs\-data role:Primary suspended:no write\-ordering:drain volume:0 minor:1 disk:UpToDate size:10616472 read:134465 written:144800 al\-writes:18 bm\-writes:0 upper\-pending:0 lower\-pending:0 al\-suspended:no blocked:no peer connection:Connected role:Secondary congested:no volume:0 replication:Established peer\-disk:UpToDate resync\-suspended:no received:122596 sent:22204 out\-of\-sync:0 pending:0 unacked:0 .fi .if n \{\ .RE .\} .sp .SS "events2" .PP Show the current state of all configured DRBD objects, followed by all changes to the state\&. .PP The output format is meant to be human as well as machine readable\&. Each line starts with the event number, which is followed by an asterisk if the event continues in the next line\&. The second word in each line indicates the kind of event: \fBexists\fR for an existing object; \fBcreate\fR, \fBdestroy\fR, and \fBchange\fR if an object is created, destroyed, or changed; or \fBcall\fR or \fBresponse\fR if an event handler is called or it returns\&. The third word indicates the object the event applies to: \fBresource\fR, \fBdevice\fR, \fBconnection\fR, \fBpeer\-device\fR, \fBhelper\fR, or a dash (\fB\-\fR) to indicate that the current state has been dumped completely\&. .PP The remaining words identify the object and describe the state that he object is in\&. Available options: .PP \fB\-\-now\fR .RS 4 Terminate after reporting the current state\&. The default is to continuously listen and report state changes\&. .RE .PP \fB\-\-statistics\fR .RS 4 Include statistics in the output\&. .RE .SS "events" .PP Deprecated\&. If possible, change to the events2 subcommand instead\&. .PP Displays every state change of DRBD and all calls to helper programs\&. This might be used to get notified of DRBD\*(Aqs state changes by piping the output to another program\&. .PP \fB\-\-all\-devices\fR .RS 4 Display the events of all DRBD minors\&. .RE .PP \fB\-\-unfiltered\fR .RS 4 This is a debugging aid that displays the content of all received netlink messages\&. .RE .SS "new\-current\-uuid" .PP Generates a new current UUID and rotates all other UUID values\&. This has at least two use cases, namely to skip the initial sync, and to reduce network bandwidth when starting in a single node configuration and then later (re\-)integrating a remote site\&. .PP Available option: .PP \fB\-\-clear\-bitmap\fR .RS 4 Clears the sync bitmap in addition to generating a new current UUID\&. .RE .PP This can be used to skip the initial sync, if you want to start from scratch\&. This use\-case does only work on "Just Created" meta data\&. Necessary steps: .sp .RS 4 .ie n \{\ \h'-04' 1.\h'+01'\c .\} .el \{\ .sp -1 .IP " 1." 4.2 .\} On \fIboth\fR nodes, initialize meta data and configure the device\&. .sp \fBdrbdadm \-\- \-\-force create\-md \fR\fB\fIres\fR\fR .RE .sp .RS 4 .ie n \{\ \h'-04' 2.\h'+01'\c .\} .el \{\ .sp -1 .IP " 2." 4.2 .\} They need to do the initial handshake, so they know their sizes\&. .sp \fBdrbdadm up \fR\fB\fIres\fR\fR .RE .sp .RS 4 .ie n \{\ \h'-04' 3.\h'+01'\c .\} .el \{\ .sp -1 .IP " 3." 4.2 .\} They are now Connected Secondary/Secondary Inconsistent/Inconsistent\&. Generate a new current\-uuid and clear the dirty bitmap\&. .sp \fBdrbdadm new\-current\-uuid \-\-clear\-bitmap \fR\fB\fIres\fR\fR .RE .sp .RS 4 .ie n \{\ \h'-04' 4.\h'+01'\c .\} .el \{\ .sp -1 .IP " 4." 4.2 .\} They are now Connected Secondary/Secondary UpToDate/UpToDate\&. Make one side primary and create a file system\&. .sp \fBdrbdadm primary \fR\fB\fIres\fR\fR .sp \fBmkfs \-t \fR\fB\fIfs\-type\fR\fR\fB $(drbdadm sh\-dev \fR\fB\fIres\fR\fR\fB)\fR .RE .PP One obvious side\-effect is that the replica is full of old garbage (unless you made them identical using other means), so any online\-verify is expected to find any number of out\-of\-sync blocks\&. .PP \fIYou must not use this on pre\-existing data!\fR Even though it may appear to work at first glance, once you switch to the other node, your data is toast, as it never got replicated\&. So \fIdo not leave out the mkfs\fR (or equivalent)\&. .PP This can also be used to shorten the initial resync of a cluster where the second node is added after the first node is gone into production, by means of disk shipping\&. This use\-case works on disconnected devices only, the device may be in primary or secondary role\&. .PP The necessary steps on the current active server are: .sp .RS 4 .ie n \{\ \h'-04' 1.\h'+01'\c .\} .el \{\ .sp -1 .IP " 1." 4.2 .\} \fBdrbdsetup new\-current\-uuid \-\-clear\-bitmap \fR\fB\fIminor\fR\fR\fB \fR .RE .sp .RS 4 .ie n \{\ \h'-04' 2.\h'+01'\c .\} .el \{\ .sp -1 .IP " 2." 4.2 .\} Take the copy of the current active server\&. E\&.g\&. by pulling a disk out of the RAID1 controller, or by copying with dd\&. You need to copy the actual data, and the meta data\&. .RE .sp .RS 4 .ie n \{\ \h'-04' 3.\h'+01'\c .\} .el \{\ .sp -1 .IP " 3." 4.2 .\} \fBdrbdsetup new\-current\-uuid \fR\fB\fIminor\fR\fR\fB \fR .RE .sp Now add the disk to the new secondary node, and join it to the cluster\&. You will get a resync of that parts that were changed since the first call to \fBdrbdsetup\fR in step 1\&. .SH "EXAMPLES" .PP For examples, please have a look at the \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2\&. .SH "VERSION" .sp This document was revised for version 8\&.3\&.2 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbd\fR(8), \fBdrbddisk\fR(8), \fBdrbdadm\fR(8), \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2, \m[blue]\fBDRBD web site\fR\m[]\&\s-2\u[2]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD User's Guide .RS 4 \%http://www.drbd.org/users-guide/ .RE .IP " 2." 4 DRBD web site .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v84/drbddisk.xml0000644000175000017500000000615012466702073021371 0ustar apoikosapoikos drbddisk Script to mark devices as primary and mount file systems 15 Oct 2008 DRBD 8.3.2 drbddisk 8 System Administration /etc/ha.d/resource.d/drbddisk resource start stop status Introduction The script brings the local device of resource into primary role. It is designed to be used by Heartbeat. In order to use you must define a resource, a host, and any other configuration options in the DRBD configuration file. See for details. If resource is omitted, then all of the resources listed in the config file are affected. Version This document was revised for version 8.0.14 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf5, drbd8, drbdsetup8drbdadm8DRBD Homepage drbd-utils-8.9.10/documentation/v84/drbdmeta.80000644000175000017500000001466413027211740020733 0ustar apoikosapoikos'\" t .\" Title: drbdmeta .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 15 Oct 2008 .\" Manual: System Administration .\" Source: DRBD 8.3.2 .\" Language: English .\" .TH "DRBDMETA" "8" "15 Oct 2008" "DRBD 8.3.2" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbdmeta \- DRBD\*(Aqs meta data management tool .SH "SYNOPSIS" .HP \w'\fBdrbdmeta\fR\ 'u \fBdrbdmeta\fR [\-\-force] [\-\-ignore\-sanity\-checks] {\fIdevice\fR} {v06\ \fIminor\fR | v07\ \fImeta_dev\ index\fR | v08\ \fImeta_dev\ index\fR} {\fIcommand\fR} [\fIcmd\ args\fR...] .SH "DESCRIPTION" .PP Drbdmeta is used to create, display and modify the contents of DRBD\*(Aqs meta data storage\&. Usually you do not want to use this command directly, but start it via the frontend \fBdrbdadm\fR(8)\&. .PP This command only works if the DRBD resource is currently down, or at least detached from its backing storage\&. The first parameter is the device node associated to the resource\&. With the second parameter you can select the version of the meta data\&. Currently all major DRBD releases (0\&.6, 0\&.7 and 8) are supported\&. .SH "OPTIONS" .PP \-\-force .RS 4 All questions that get asked by drbdmeta are treated as if the user answered \*(Aqyes\*(Aq\&. .RE .PP \-\-ignore\-sanity\-checks .RS 4 Some sanity checks cause drbdmeta to terminate\&. E\&.g\&. if a file system image would get destroyed by creating the meta data\&. By using that option you can force drbdmeta to ignore these checks\&. .RE .SH "COMMANDS" .PP create\-md \fB\-\-peer\-max\-bio\-size \fR\fB\fIval\fR\fR \fB\-\-al\-stripes \fR\fB\fIval\fR\fR \fB\-\-al\-stripe\-size\-kB \fR\fB\fIval\fR\fR .RS 4 Create\-md initializes the meta data storage\&. This needs to be done before a DRBD resource can be taken online for the first time\&. In case there is already a meta data signature of an older format in place, drbdmeta will ask you if it should convert the older format to the selected format\&. .sp If you will use the resource before it is connected to its peer for the first time DRBD may perform better if you use the \fB\-\-peer\-max\-bio\-size\fR option\&. For DRBD versions of the peer use up to these values: <8\&.3\&.7 \-> 4k, 8\&.3\&.8 \-> 32k, 8\&.3\&.9 \-> 128k, 8\&.4\&.0 \-> 1M\&. .sp If you want to use more than 6433 activity log extents, or live on top of a spriped RAID, you may specify the number of stripes (\fB\-\-al\-stripes\fR, default 1), and the stripe size (\fB\-\-al\-stripe\-size\-kB\fR, default 32)\&. To just use a larger linear on\-disk ring\-buffer, leave the number of stripes at 1, and increase the size only: \fBdrbdmeta 0 v08 /dev/vg23/lv42 internal create\-md \-\-al\-stripe\-size 1M\fR .sp To avoid a single "spindle" from becoming a bottleneck, increase the number of stripes, to achieve an interleaved layout of the on\-disk activity\-log transactions\&. What you give as "stripe\-size" should be what is a\&.k\&.a\&. "chunk size" or "granularity" or "strip unit": the minimum skip to the next "spindle"\&. \fBdrbdmeta 0 v08 /dev/vg23/lv42 internal create\-md \-\-al\-stripes 7 \-\-al\-stripe\-size 64k\fR .RE .PP get\-gi .RS 4 Get\-gi shows a short textual representation of the data generation identifier\&. In version 0\&.6 and 0\&.7 these are generation counters, while in version 8 it is a set of UUIDs\&. .RE .PP show\-gi .RS 4 Show\-gi prints a textual representation of the data generation identifiers including explanatory information\&. .RE .PP dump\-md .RS 4 Dumps the whole contents of the meta data storage including the stored bit\-map and activity\-log in a textual representation\&. .RE .PP outdate .RS 4 Sets the outdated flag in the meta data\&. This is used by the peer node when it wants to become primary, but cannot communicate with the DRBD stack on this host\&. .RE .PP dstate .RS 4 Prints the state of the data on the backing storage\&. The output is always followed by \*(Aq/DUnknown\*(Aq since drbdmeta only looks at the local meta data\&. .RE .PP check\-resize .RS 4 Examines the device size of a backing device, and it\*(Aqs last known device size, recorded in a file /var/lib/drbd/drbd\-minor\-??\&.lkbd\&. In case the size of the backing device changed, and the meta data can be found at the old position, it moves the meta data to the right position at the end of the block device\&. .RE .SH "EXPERT\*(AQS COMMANDS" .PP Drbdmeta allows you to modify the meta data as well\&. This is intentionally omitted for the command\*(Aqs usage output, since you should only use it if you really know what you are doing\&. By setting the generation identifiers to wrong values, you risk to overwrite your up\-to\-data data with an older version of your data\&. .PP set\-gi \fIgi\fR .RS 4 Set\-gi allows you to set the generation identifier\&. \fIGi\fR needs to be a generation counter for the 0\&.6 and 0\&.7 format, and a UUID set for 8\&.x\&. Specify it in the same way as get\-gi shows it\&. .RE .PP restore\-md \fIdump_file\fR .RS 4 Reads the \fIdump_file\fR and writes it to the meta data\&. .RE .SH "VERSION" .sp This document was revised for version 8\&.3\&.2 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbdadm\fR(8) drbd-utils-8.9.10/documentation/v84/drbdsetup.xml0000644000175000017500000024333112645466445021614 0ustar apoikosapoikos 6 May 2011 DRBD 8.4.0 drbdsetup 8 System Administration drbdsetup Setup tool for DRBD drbdsetup Description drbdsetup is used to associate DRBD devices with their backing block devices, to set up DRBD device pairs to mirror their backing block devices, and to inspect the configuration of running DRBD devices. Note drbdsetup is a low level tool of the DRBD program suite. It is used by the data disk and drbd scripts to communicate with the device driver. Commands Each drbdsetup sub-command might require arguments and bring its own set of options. All values have default units which might be overruled by K, M or G. These units are defined in the usual way (e.g. K = 2^10 = 1024). Common options All drbdsetup sub-commands accept these two options In case the specified DRBD device (minor number) does not exist yet, create it implicitly. new-resource Resources are the primary objects of any DRBD configuration. A resource must be created with the command before any volumes or minor devices can be created. Connections are referenced by name. new-minor A minor is used as a synonym for replicated block device. It is represented in the /dev/ directory by a block device. It is the application's interface to the DRBD-replicated block devices. These block devices get addressed by their minor numbers on the drbdsetup commandline. A pair of replicated block devices may have different minor numbers on the two machines. They are associated by a common volume-number. Volume numbers are local to each connection. Minor numbers are global on one node. del-resource Destroys a resource object. This is only possible if the resource has no volumes. del-minor Minors can only be destroyed if its disk is detached. attach, disk-options drbdsetup disk Attach associates device with lower_device to store its data blocks on. The (or ) should only be used if you wish not to use as much as possible from the backing block devices. If you do not use , the device is only ready for use as soon as it was connected to its peer once. (See the command.) With the disk-options command it is possible to change the options of a minor while it is attached. You can override DRBD's size determination method with this option. If you need to use the device before it was ever connected to its peer, use this option to pass the size of the DRBD device to the driver. Default unit is sectors (1s = 512 bytes). If you use the size parameter in drbd.conf, we strongly recommend to add an explicit unit postfix. drbdadm and drbdsetup used to have mismatching default units. If the driver of the lower_device reports an error to DRBD, DRBD will mark the disk as inconsistent, call a helper program, or detach the device from its backing storage and perform all further IO by requesting it from the peer. The valid err_handlers are: , and . Under we understand preventive measures to avoid situations where both nodes are primary and disconnected (AKA split brain). Valid fencing policies are: This is the default policy. No fencing actions are done. If a node becomes a disconnected primary, it tries to outdate the peer's disk. This is done by calling the fence-peer handler. The handler is supposed to reach the other node over alternative communication paths and call 'drbdadm outdate res' there. If a node becomes a disconnected primary, it freezes all its IO operations and calls its fence-peer handler. The fence-peer handler is supposed to reach the peer over alternative communication paths and call 'drbdadm outdate res' there. In case it cannot reach the peer, it should stonith the peer. IO is resumed as soon as the situation is resolved. In case your handler fails, you can resume IO with the command. DRBD has four implementations to express write-after-write dependencies to its backing storage device. DRBD will use the first method that is supported by the backing storage device and that is not disabled. By default the flush method is used. Since drbd-8.4.2 is disabled by default because since linux-2.6.36 (or 2.6.32 RHEL6) there is no reliable way to determine if queuing of IO-barriers works. Dangerous only enable if you are told so by one that knows for sure. When selecting the method you should not only base your decision on the measurable performance. In case your backing storage device has a volatile write cache (plain disks, RAID of plain disks) you should use one of the first two. In case your backing storage device has battery-backed write cache you may go with option 3. Option 4 (disable everything, use "none") is dangerous on most IO stacks, may result in write-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles. Do not use . Unfortunately device mapper (LVM) might not support barriers. The letter after "wo:" in /proc/drbd indicates with method is currently in use for a device: b, f, d, n. The implementations: barrier The first requires that the driver of the backing storage device support barriers (called 'tagged command queuing' in SCSI and 'native command queuing' in SATA speak). The use of this method can be enabled by setting the options to . flush The second requires that the backing device support disk flushes (called 'force unit access' in the drive vendors speak). The use of this method can be disabled setting to . drain The third method is simply to let write requests drain before write requests of a new reordering domain are issued. That was the only implementation before 8.0.9. none The fourth method is to not express write-after-write dependencies to the backing store at all, by also specifying . This is dangerous on most IO stacks, may result in write-reordering, and if so, can theoretically be the reason for data corruption, or disturb the DRBD protocol, causing spurious disconnect/reconnect cycles. Do not use . Disables the use of disk flushes and barrier BIOs when accessing the meta data device. See the notes on . In some special circumstances the device mapper stack manages to pass BIOs to DRBD that violate the constraints that are set forth by DRBD's merge_bvec() function and which have more than one bvec. A known example is: phys-disk -> DRBD -> LVM -> Xen -> missaligned partition (63) -> DomU FS. Then you might see "bio would need to, but cannot, be split:" in the Dom0's kernel log. The best workaround is to proper align the partition within the VM (E.g. start it at sector 1024). That costs 480 KiB of storage. Unfortunately the default of most Linux partitioning tools is to start the first partition at an odd number (63). Therefore most distributions install helpers for virtual linux machines will end up with missaligned partitions. The second best workaround is to limit DRBD's max bvecs per BIO (i.e., the option) to 1, but that might cost performance. The default value of is 0, which means that there is no user imposed limitation. To ensure smooth operation of the application on top of DRBD, it is possible to limit the bandwidth that may be used by background synchronization. The default is 250 KiB/sec, the default unit is KiB/sec. Start resync on this device only if the device with minor is already in connected state. Otherwise this device waits in SyncPause state. DRBD automatically performs hot area detection. With this parameter you control how big the hot area (=active set) can get. Each extent marks 4M of the backing storage. In case a primary node leaves the cluster unexpectedly, the areas covered by the active set must be resynced upon rejoining of the failed node. The data structure is stored in the meta-data area, therefore each change of the active set is a write operation to the meta-data device. A higher number of extents gives longer resync times but less updates to the meta-data. The default number of extents is 1237. (Minimum: 7, Maximum: 65534) See also drbd.conf5 and drbdmeta8 for additional limitations and necessary preparation. DRBD's activity log transaction writing makes it possible, that after the crash of a primary node a partial (bit-map based) resync is sufficient to bring the node back to up-to-date. Setting to might increase normal operation performance but causes DRBD to do a full resync when a crashed primary gets reconnected. The default value is . The dynamic resync speed controller gets enabled with setting plan_time to a positive value. It aims to fill the buffers along the data path with either a constant amount of data fill_target, or aims to have a constant delay time of delay_target along the path. The controller has an upper bound of max_rate. By plan_time the agility of the controller is configured. Higher values yield for slower/lower responses of the controller to deviation from the target value. It should be at least 5 times RTT. For regular data paths a fill_target in the area of 4k to 100k is appropriate. For a setup that contains drbd-proxy it is advisable to use delay_target instead. Only when fill_target is set to 0 the controller will use delay_target. 5 times RTT is a reasonable starting value. Max_rate should be set to the bandwidth available between the DRBD-hosts and the machines hosting DRBD-proxy, or to the available disk-bandwidth. The default value of plan_time is 0, the default unit is 0.1 seconds. Fill_target has 0 and sectors as default unit. Delay_target has 1 (100ms) and 0.1 as default unit. Max_rate has 10240 (100MiB/s) and KiB/s as default unit. We track the disk IO rate caused by the resync, so we can detect non-resync IO on the lower level device. If the lower level device seems to be busy, and the current resync rate is above min_rate, we throttle the resync. The default value of min_rate is 4M, the default unit is k. If you want to not throttle at all, set it to zero, if you want to throttle always, set it to one. , If the lower-level device on which a DRBD device stores its data does not finish an I/O request within the defined , DRBD treats this as a failure. The lower-level device is detached, and the device's disk state advances to Diskless. If DRBD is connected to one or more peers, the failed request is passed on to one of them. This option is dangerous and may lead to kernel panic! "Aborting" requests, or force-detaching the disk, is intended for completely blocked/hung local backing devices which do no longer complete requests at all, not even do error completions. In this situation, usually a hard-reset and failover is the only way out. By "aborting", basically faking a local error-completion, we allow for a more graceful swichover by cleanly migrating services. Still the affected node has to be rebooted "soon". By completing these requests, we allow the upper layers to re-use the associated data pages. If later the local backing device "recovers", and now DMAs some data from disk into the original request pages, in the best case it will just put random data into unused pages; but typically it will corrupt meanwhile completely unrelated data, causing all sorts of damage. Which means delayed successful completion, especially for READ requests, is a reason to panic(). We assume that a delayed *error* completion is OK, though we still will complain noisily about it. The default value of is 0, which stands for an infinite timeout. Timeouts are specified in units of 0.1 seconds. This option is available since DRBD 8.3.12. Setting to will cause DRBD to always fall-back to zero-out on the receiving side, and to not even announce discard capabilities on the Primary, if the respective backend announces discard_zeroes_data=false. Setting to will allow DRBD to use discards, and to announce discard_zeroes=true, even on backends that announce discard_zeroes_data=false. We used to ignore the discard_zeroes_data setting completely. To not break established and expected behaviour, the default value is . This option is available since 8.4.7. See also drbd.conf5. The supported methods for load balancing of read requests are , , , and , , , , , and . The default value of is . This option is available since 8.4.1. When is set to a non zero, positive value then DRBD tries to do a resync operation in requests of this size. In case such a block contains only zero bytes on the sync source node, the sync target node will issue a discard/trim/unmap command for the area. The value is constrained by the discard granularity of the backing block device. In case is not a multiplier of the discard granularity of the backing block device DRBD rounds it up. The feature only gets active if the backing block device reads back zeroes after a discard command. The default value of is 0. This option is available since 8.4.7. connect, net-options drbdsetup net Connect sets up the device to listen on af:local_addr:port for incoming connections and to try to connect to af:remote_addr:port. If port is omitted, 7788 is used as default. If af is omitted gets used. Other supported address families are , for Dolphin Interconnect Solutions' "super sockets" and for Sockets Direct Protocol (Infiniband). The net-options command allows you to change options while the connection is established. On the TCP/IP link the specified protocol is used. Valid protocol specifiers are A, B, and C. Protocol A: write IO is reported as completed, if it has reached local disk and local TCP send buffer. Protocol B: write IO is reported as completed, if it has reached local disk and remote buffer cache. Protocol C: write IO is reported as completed, if it has reached both local and remote disk. In case it is not possible to connect to the remote DRBD device immediately, DRBD keeps on trying to connect. With this option you can set the time between two retries. The default value is 10. The unit is seconds. If the TCP/IP connection linking a DRBD device pair is idle for more than time seconds, DRBD will generate a keep-alive packet to check if its partner is still alive. The default value is 10. The unit is seconds. If the partner node fails to send an expected response packet within val tenths of a second, the partner node is considered dead and therefore the TCP/IP connection is abandoned. The default value is 60 (= 6 seconds). The socket send buffer is used to store packets sent to the secondary node, which are not yet acknowledged (from a network point of view) by the secondary node. When using protocol A, it might be necessary to increase the size of this data structure in order to increase asynchronicity between primary and secondary nodes. But keep in mind that more asynchronicity is synonymous with more data loss in the case of a primary node failure. Since 8.0.13 resp. 8.2.7 setting the size value to 0 means that the kernel should autotune this. The default size is 0, i.e. autotune. Packets received from the network are stored in the socket receive buffer first. From there they are consumed by DRBD. Before 8.3.2 the receive buffer's size was always set to the size of the socket send buffer. Since 8.3.2 they can be tuned independently. A value of 0 means that the kernel should autotune this. The default size is 0, i.e. autotune. In case the secondary node fails to complete a single write request for count times the timeout, it is expelled from the cluster, i.e. the primary node goes into StandAlone mode. To disable this feature, you should explicitly set it to 0; defaults may change between versions. With this option the maximal number of write requests between two barriers is limited. Typically set to the same as , or the allowed maximum. Values smaller than 10 can lead to degraded performance. The default value is 2048. With this option the maximal number of buffer pages allocated by DRBD's receiver thread is limited. Typically set to the same as . Small values could lead to degraded performance. The default value is 2048, the minimum 32. Increase this if you cannot saturate the IO backend of the receiving side during linear write or during resync while otherwise idle. See also drbd.conf5 This setting has no effect with recent kernels that use explicit on-stack plugging (upstream Linux kernel 2.6.39, distributions may have backported). When the number of pending write requests on the standby (secondary) node exceeds the unplug-watermark, we trigger the request processing of our backing storage device. Some storage controllers deliver better performance with small values, others deliver best performance when the value is set to the same value as max-buffers, yet others don't feel much effect at all. Minimum 16, default 128, maximum 131072. With this option set you may assign primary role to both nodes. You only should use this option if you use a shared storage file system on top of DRBD. At the time of writing the only ones are: OCFS2 and GFS. If you use this option with any other file system, you are going to crash your nodes and to corrupt your data! You need to specify the HMAC algorithm to enable peer authentication at all. You are strongly encouraged to use peer authentication. The HMAC algorithm will be used for the challenge response authentication of the peer. You may specify any digest algorithm that is named in /proc/crypto. The shared secret used in peer authentication. May be up to 64 characters. possible policies are: No automatic resynchronization, simply disconnect. Auto sync from the node that was primary before the split-brain situation occurred. Auto sync from the node that became primary as second during the split-brain situation. In case one node did not write anything since the split brain became evident, sync from the node that wrote something to the node that did not write anything. In case none wrote anything this policy uses a random decision to perform a "resync" of 0 blocks. In case both have written something this policy disconnects the nodes. Auto sync from the node that touched more blocks during the split brain situation. Auto sync to the named node. possible policies are: No automatic resynchronization, simply disconnect. Discard the version of the secondary if the outcome of the algorithm would also destroy the current secondary's data. Otherwise disconnect. Discard the secondary's version. Always honor the outcome of the algorithm. In case it decides the current secondary has the correct data, call the on the current primary. Always honor the outcome of the algorithm. In case it decides the current secondary has the correct data, accept a possible instantaneous change of the primary's data. possible policies are: No automatic resynchronization, simply disconnect. Always honor the outcome of the algorithm. In case it decides the current secondary has the right data, call the on the current primary. Always honor the outcome of the algorithm. In case it decides the current secondary has the right data, accept a possible instantaneous change of the primary's data. Normally the automatic after-split-brain policies are only used if current states of the UUIDs do not indicate the presence of a third node. With this option you request that the automatic after-split-brain policies are used as long as the data sets of the nodes are somehow related. This might cause a full sync, if the UUIDs indicate the presence of a third node. (Or double faults have led to strange UUID sets.) This option sets DRBD's behavior when DRBD deduces from its meta data that a resynchronization is needed, and the SyncTarget node is already primary. The possible settings are: , and . While speaks for itself, with the setting the handler is called which is expected to either change the role of the node to secondary, or remove the node from the cluster. The default is . With the setting you allow DRBD to force a primary node into SyncTarget state. This means that the data exposed by DRBD changes to the SyncSource's version of the data instantaneously. USE THIS OPTION ONLY IF YOU KNOW WHAT YOU ARE DOING. DRBD can ensure the data integrity of the user's data on the network by comparing hash values. Normally this is ensured by the 16 bit checksums in the headers of TCP/IP packets. This option can be set to any of the kernel's data digest algorithms. In a typical kernel configuration you should have at least one of , , and available. By default this is not enabled. See also the notes on data integrity on the drbd.conf manpage. DRBD usually uses the TCP socket option TCP_CORK to hint to the network stack when it can expect more data, and when it should flush out what it has in its send queue. There is at least one network stack that performs worse when one uses this hinting method. Therefore we introduced this option, which disable the setting and clearing of the TCP_CORK socket option by DRBD. The time the peer has to answer to a keep-alive packet. In case the peer's reply is not received within this time period, it is considered dead. The default unit is tenths of a second, the default value is 5 (for half a second). Use this option to manually recover from a split-brain situation. In case you do not have any automatic after-split-brain policies selected, the nodes refuse to connect. By passing this option you make this node a sync target immediately after successful connect. Causes DRBD to abort the connection process after the resync handshake, i.e. no resync gets performed. You can find out which resync DRBD would perform by looking at the kernel's log file. By default DRBD blocks when the available TCP send queue becomes full. That means it will slow down the application that generates the write requests that cause DRBD to send more data down that TCP connection. When DRBD is deployed with DRBD-proxy it might be more desirable that DRBD goes into AHEAD/BEHIND mode shortly before the send queue becomes full. In AHEAD/BEHIND mode DRBD does no longer replicate data, but still keeps the connection open. The advantage of the AHEAD/BEHIND mode is that the application is not slowed down, even if DRBD-proxy's buffer is not sufficient to buffer all write requests. The downside is that the peer node falls behind, and that a resync will be necessary to bring it back into sync. During that resync the peer node will have an inconsistent disk. Available congestion_policys are and . The default is . Fill_threshold might be in the range of 0 to 10GiBytes. The default is 0 which disables the check. Active_extents_threshold has the same limits as . The AHEAD/BEHIND mode and its settings are available since DRBD 8.3.10. During online verification (as initiated by the verify sub-command), rather than doing a bit-wise comparison, DRBD applies a hash function to the contents of every block being verified, and compares that hash with the peer. This option defines the hash algorithm being used for that purpose. It can be set to any of the kernel's data digest algorithms. In a typical kernel configuration you should have at least one of , , and available. By default this is not enabled; you must set this option explicitly in order to be able to use on-line device verification. See also the notes on data integrity on the drbd.conf manpage. A resync process sends all marked data blocks form the source to the destination node, as long as no is given. When one is specified the resync process exchanges hash values of all marked blocks first, and sends only those data blocks over, that have different hash values. This setting is useful for DRBD setups with low bandwidth links. During the restart of a crashed primary node, all blocks covered by the activity log are marked for resync. But a large part of those will actually be still in sync, therefore using will lower the required bandwidth in exchange for CPU cycles. During resync-handshake, the dirty-bitmaps of the nodes are exchanged and merged (using bit-or), so the nodes will have the same understanding of which blocks are dirty. On large devices, the fine grained dirty-bitmap can become large as well, and the bitmap exchange can take quite some time on low-bandwidth links. Because the bitmap typically contains compact areas where all bits are unset (clean) or set (dirty), a simple run-length encoding scheme can considerably reduce the network traffic necessary for the bitmap exchange. For backward compatibility reasons, and because on fast links this possibly does not improve transfer time but consumes cpu cycles, this defaults to off. Introduced in 8.3.2. In setups involving a DRBD-proxy and connections that experience a lot of buffer-bloat it might be necessary to set to an unusual high value. By default DRBD uses the same value to wait if a newly established TCP-connection is stable. Since the DRBD-proxy is usually located in the same data center such a long wait time may hinder DRBD's connect process. In such setups should be set to at least to the round trip time between DRBD and DRBD-proxy. I.e. in most cases to 1. The default unit is tenths of a second, the default value is 0 (which causes DRBD to use the value of instead). Introduced in 8.4.5. resource-options drbdsetup resource-options Changes the options of the resource at runtime. Sets the cpu-affinity-mask for DRBD's kernel threads of this device. The default value of cpu-mask is 0, which means that DRBD's kernel threads should be spread over all CPUs of the machine. This value must be given in hexadecimal notation. If it is too big it will be truncated. This setting controls what happens to IO requests on a degraded, disk less node (I.e. no data store is reachable). The available policies are and . If ond-policy is set to you can either resume IO by attaching/connecting the last lost data storage, or by the drbdadm resume-io res command. The latter will result in IO errors of course. The default is . This setting is available since DRBD 8.3.9. primary drbdsetup primary Sets the device into primary role. This means that applications (e.g. a file system) may open the device for read and write access. Data written to the device in primary role are mirrored to the device in secondary role. Normally it is not possible to set both devices of a connected DRBD device pair to primary role. By using the option, you override this behavior and instruct DRBD to allow two primaries. Alias for --force. Becoming primary fails if the local replica is not up-to-date. I.e. when it is inconsistent, outdated of consistent. By using this option you can force it into primary role anyway. USE THIS OPTION ONLY IF YOU KNOW WHAT YOU ARE DOING. secondary drbdsetup secondary Brings the device into secondary role. This operation fails as long as at least one application (or file system) has opened the device. It is possible that both devices of a connected DRBD device pair are secondary. verify drbdsetup verify This initiates on-line device verification. During on-line verification, the contents of every block on the local node are compared to those on the peer node. Device verification progress can be monitored via /proc/drbd. Any blocks whose content differs from that of the corresponding block on the peer node will be marked out-of-sync in DRBD's on-disk bitmap; they are not brought back in sync automatically. To do that, simply disconnect and reconnect the resource. If on-line verification is already in progress (and this node is "VerifyS"), this command silently "succeeds". In this case, any start-sector (see below) will be ignored, and any stop-sector (see below) will be honored. This can be used to stop a running verify, or to update/shorten/extend the coverage of the currently running verify. This command will fail if the device is not part of a connected device pair. See also the notes on data integrity on the drbd.conf manpage. Since version 8.3.2, on-line verification should resume from the last position after connection loss. It may also be started from an arbitrary position by setting this option. If you had reached some stop-sector before, and you do not specify an explicit start-sector, verify should resume from the previous stop-sector. Default unit is sectors. You may also specify a unit explicitly. The will be rounded down to a multiple of 8 sectors (4kB). , Since version 8.3.14, on-line verification can be stopped before it reaches end-of-device. Default unit is sectors. You may also specify a unit explicitly. The may be updated by issuing an additional drbdsetup verify command on the same node while the verify is running. This can be used to stop a running verify, or to update/shorten/extend the coverage of the currently running verify. invalidate drbdsetup invalidate This forces the local device of a pair of connected DRBD devices into SyncTarget state, which means that all data blocks of the device are copied over from the peer. This command will fail if the device is not either part of a connected device pair, or disconnected Secondary. invalidate-remote drbdsetup invalidate-remote This forces the local device of a pair of connected DRBD devices into SyncSource state, which means that all data blocks of the device are copied to the peer. On a disconnected Primary device, this will set all bits in the out of sync bitmap. As a side affect this suspends updates to the on disk activity log. Updates to the on disk activity log resume automatically when necessary. wait-connect drbdsetup wait-connect Returns as soon as the device can communicate with its partner device. This command will fail if the device cannot communicate with its partner for timeout seconds. If the peer was working before this node was rebooted, the wfc_timeout is used. If the peer was already down before this node was rebooted, the degr_wfc_timeout is used. If the peer was successfully outdated before this node was rebooted the outdated_wfc_timeout is used. The default value for all those timeout values is 0 which means to wait forever. The unit is seconds. In case the connection status goes down to StandAlone because the peer appeared but the devices had a split brain situation, the default for the command is to terminate. You can change this behavior with the option. wait-sync drbdsetup wait-sync Returns as soon as the device leaves any synchronization into connected state. The options are the same as with the wait-connect command. disconnect drbdsetup disconnect Removes the information set by the command from the device. This means that the device goes into unconnected state and will no longer listen for incoming connections. detach drbdsetup detach Removes the information set by the command from the device. This means that the device is detached from its backing storage device. , A regular detach returns after the disk state finally reached diskless. As a consequence detaching from a frozen backing block device never terminates. On the other hand A forced detach returns immediately. It allows you to detach DRBD from a frozen backing block device. Please note that the disk will be marked as failed until all pending IO requests where finished by the backing block device. down drbdsetup down Removes all configuration information from the device and forces it back to unconfigured state. role drbdsetup role Shows the current roles of the device and its peer, as local/peer. state drbdsetup state Deprecated alias for "role" cstate drbdsetup cstate Shows the current connection state of the device. dstate drbdsetup dstate Shows the current states of the backing storage devices, as local/peer. resize drbdsetup resize This causes DRBD to reexamine the size of the device's backing storage device. To actually do online growing you need to extend the backing storages on both devices and call the command on one of your nodes. The option can be used to online shrink the usable size of a drbd device. It's the users responsibility to make sure that a file system on the device is not truncated by that operation. The allows you to resize a device which is currently not connected to the peer. Use with care, since if you do not resize the peer's disk as well, further connect attempts of the two will fail. When the option is given DRBD will skip the resync of the new storage. Only do this if you know that the new storage was initialized to the same content by other means. The options and may be used to change the layout of the activity log online. In case of internal meta data this may invovle shrinking the user visible size at the same time (unsing the ) or increasing the avalable space on the backing devices. check-resize drbdsetup check-resize To enable DRBD to detect offline resizing of backing devices this command may be used to record the current size of backing devices. The size is stored in files in /var/lib/drbd/ named drbd-minor-??.lkbd This command is called by drbdadm resize res after drbdsetup device resize returned. pause-sync drbdsetup pause-sync Temporarily suspend an ongoing resynchronization by setting the local pause flag. Resync only progresses if neither the local nor the remote pause flag is set. It might be desirable to postpone DRBD's resynchronization after eventual resynchronization of the backing storage's RAID setup. resume-sync drbdsetup resume-sync Unset the local sync pause flag. outdate drbdsetup outdate Mark the data on the local backing storage as outdated. An outdated device refuses to become primary. This is used in conjunction with and by the peer's handler. show-gi drbdsetup show-gi Displays the device's data generation identifiers verbosely. get-gi drbdsetup get-gi Displays the device's data generation identifiers. show drbdsetup show Shows all available configuration information of a resource, or of all resources. Available options: Show all configuration parameters, even the ones with default values. Normally, parameters with default values are not shown. suspend-io drbdsetup suspend-io This command is of no apparent use and just provided for the sake of completeness. resume-io drbdsetup resume-io If the fence-peer handler fails to stonith the peer node, and your policy is set to resource-and-stonith, you can unfreeze IO operations with this command. status drbdsetup status Show the status of a resource, or of all resources. The output consists of one paragraph for each configured resource. Each paragraph contains one line for each resource, followed by one line for each device, and one line for each connection. The device and connection lines are indented. The connection lines are followed by one line for each peer device; these lines are indented against the connection line. Long lines are wrapped around at terminal width, and indented to indicate how the lines belongs together. Available options: Include more information in the output even when it is likely redundant or irrelevant. Include data transfer statistics in the output. Colorize the output. With , emits color codes only when standard output is connected to a terminal. For example, the non-verbose output for a resource with only one connection and only one volume could look like this: fs-backoffice role:Primary disk:UpToDate peer role:Secondary replication:Established peer-disk:UpToDate With the options, the same resource could be reported as: fs-data role:Primary suspended:no write-ordering:drain volume:0 minor:1 disk:UpToDate size:10616472 read:134465 written:144800 al-writes:18 bm-writes:0 upper-pending:0 lower-pending:0 al-suspended:no blocked:no peer connection:Connected role:Secondary congested:no volume:0 replication:Established peer-disk:UpToDate resync-suspended:no received:122596 sent:22204 out-of-sync:0 pending:0 unacked:0 events2 drbdsetup events2 Show the current state of all configured DRBD objects, followed by all changes to the state. The output format is meant to be human as well as machine readable. Each line starts with the event number, which is followed by an asterisk if the event continues in the next line. The second word in each line indicates the kind of event: for an existing object; , , and if an object is created, destroyed, or changed; or or if an event handler is called or it returns. The third word indicates the object the event applies to: , , , , , or a dash () to indicate that the current state has been dumped completely. The remaining words identify the object and describe the state that he object is in. Available options: Terminate after reporting the current state. The default is to continuously listen and report state changes. Include statistics in the output. events drbdsetup events Deprecated. If possible, change to the events2 subcommand instead. Displays every state change of DRBD and all calls to helper programs. This might be used to get notified of DRBD's state changes by piping the output to another program. Display the events of all DRBD minors. This is a debugging aid that displays the content of all received netlink messages. new-current-uuid drbdsetup new-current-uuid Generates a new current UUID and rotates all other UUID values. This has at least two use cases, namely to skip the initial sync, and to reduce network bandwidth when starting in a single node configuration and then later (re-)integrating a remote site. Available option: Clears the sync bitmap in addition to generating a new current UUID. This can be used to skip the initial sync, if you want to start from scratch. This use-case does only work on "Just Created" meta data. Necessary steps: On both nodes, initialize meta data and configure the device. drbdadm -- --force create-md res They need to do the initial handshake, so they know their sizes. drbdadm up res They are now Connected Secondary/Secondary Inconsistent/Inconsistent. Generate a new current-uuid and clear the dirty bitmap. drbdadm new-current-uuid --clear-bitmap res They are now Connected Secondary/Secondary UpToDate/UpToDate. Make one side primary and create a file system. drbdadm primary res mkfs -t fs-type $(drbdadm sh-dev res) One obvious side-effect is that the replica is full of old garbage (unless you made them identical using other means), so any online-verify is expected to find any number of out-of-sync blocks. You must not use this on pre-existing data! Even though it may appear to work at first glance, once you switch to the other node, your data is toast, as it never got replicated. So do not leave out the mkfs (or equivalent). This can also be used to shorten the initial resync of a cluster where the second node is added after the first node is gone into production, by means of disk shipping. This use-case works on disconnected devices only, the device may be in primary or secondary role. The necessary steps on the current active server are: drbdsetup new-current-uuid --clear-bitmap minor Take the copy of the current active server. E.g. by pulling a disk out of the RAID1 controller, or by copying with dd. You need to copy the actual data, and the meta data. drbdsetup new-current-uuid minor Now add the disk to the new secondary node, and join it to the cluster. You will get a resync of that parts that were changed since the first call to drbdsetup in step 1. Examples For examples, please have a look at the DRBD User's Guide. Version This document was revised for version 8.3.2 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf 5 , drbd 8 , drbddisk 8 , drbdadm 8 , DRBD User's Guide, DRBD web site drbd-utils-8.9.10/documentation/v9/0000755000175000017500000000000013027242657016775 5ustar apoikosapoikosdrbd-utils-8.9.10/documentation/v9/drbdmeta.xml0000644000175000017500000003237312466702073021310 0ustar apoikosapoikos 6 December 2012 DRBD 9.0.0 drbdmeta 8 System Administration drbdmeta Manipulate the DRBD on-disk metadata drbdmeta drbdmeta --force --ignore-sanity-checks device v06 minor v07 meta_dev index v08 meta_dev index v09 meta_dev index command cmd args Description The utility is used for creating, displaying, and modifying DRBD's on-disk metadata. Users usually interact with the utility, which provides a more high-level interface to DRBD than . (See 's option to see how uses .) This utility can only be used on devices which are not currently in use by the kernel. The first argument (device) specifies the drbd device associated with a volume, or - if no device is associated with that volume. If the drbd device is specified, the utility makes sure that the drbd device does not currently have a volume attached to prevent meta-data of an active volume from being destroyed. The second argument specifies the metadata version to use (v06, v07, v08, v09). In most metadata versions, the third argument (meta_dev) specifies the device which contains the metadata; this argument can be the same as device. The fourth argument (index) can be one of the keywords (for internal metadata), (in v07 for variable-sized metadata; v07 otherwise defaults to fixed-size internal metadata), (for variable-sized external metadata), or a numeric matadata index (for fixed-size external metadata). See the parameter in drbd.conf 5 . Options --force drbdmeta--force Assume yes as the answer to all questions drbdmeta would ask. --ignore-sanity-checks drbdmeta--ignore-sanity-checks Normally, performs some sanity checks before writing to the metadata device: for example, if the device appears to contain a file system, it refuses to destroy the file system by writing into it. Use this option to ignore these checks. Commands val (metadata versions v06, v07, and v08) number-of-bitmap-slots val val val (metadata version v09) drbdmetacreate-md Initialize the metadata. This is necessary before a DRBD resource can be attached. If finds an older version of DRBD metadata on the device, it asks if the format should be converted. When calls 's command for a device, it sets the number-of-bitmap-slots argument to the number of peers in the resource. To reserve additional bitmap slots (which allows to add more peers in the future), call directly instead. When a device is used before being connected to its peers the first time, DRBD assumes that peers can only handle 4 KiB requests by default. The option allows to set more optimistic values; use this if the versions of DRBD that this device will connect to are known. DRBD supports a maximum bio size of 32 KiB since version 8.3.8, of 128 KiB since version 8.3.9, and of 1 MiB since version 8.4.0. If you want to use more than 6433 activity log extents, or live on top of a spriped RAID, you may specify the number of stripes (, default 1), and the stripe size (, default 32). To just use a larger linear on-disk ring-buffer, leave the number of stripes at 1, and increase the size only: drbdmeta 0 v08 /dev/vg23/lv42 internal create-md --al-stripe-size 1M To avoid a single "spindle" from becoming a bottleneck, increase the number of stripes, to achieve an interleaved layout of the on-disk activity-log transactions. What you give as "stripe-size" should be what is a.k.a. "chunk size" or "granularity" or "strip unit": the minimum skip to the next "spindle". drbdmeta 0 v08 /dev/vg23/lv42 internal create-md --al-stripes 7 --al-stripe-size 64 id drbdmetaget-gi Show the data generation identifiers for a device on a particular connection. DRBD version 9.0.0 and beyond support multiple peers; use the node-id option to define which peer's data generation identifiers to show. id drbdmetashow-gi Similar to , but with explanatory information. drbdmetadump-md Dump the metadata of a device in text form, including the bitmap and activity log. Mark the data on a lower-level device as outdated. See drbdsetup 8 for details. Show the current disk state of a lower-level device. drbdmetacheck-resize Examine the device size of a lower-level device and its last known device size (saved in by ). For internal metadata, if the size of the lower-level device has changed and the metadata can be found at the previous position, move the metadata to the new position at the end of the block device. drbdmetaapply-al Apply the activity log of the specified device. This is necessary before the device can be attached by the kernel again. Expert commands The utility can be used to fine tune metdata. Please note that this can lead to destroyed metadata or even silent data corruption; use with great care only. gi id drbdmetaset-gi Set the generation identifiers. The gi argument is a generation counter for the v06 and v07 formats, and a set of UUIDs for v08 and beyond. Accepts the same syntax as in the output. DRBD version 9.0.0 and beyond support multiple peers; use the --node-id option to define which peer's data generation identifiers to set. dump_file drbdmetarestore-md Replace the metadata on the device with the contents of dump_file. The dump file format is defined by the output of the command. Version This document was revised for version 9.0.0 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2008,2012 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbdadm 8 drbd.conf 5 drbd-utils-8.9.10/documentation/v9/Makefile.in0000644000175000017500000001351513016771234021043 0ustar apoikosapoikos# Makefile in documentation directory # # This file is part of DRBD by Philipp Reisner and Lars Ellenberg. # # drbd is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. # variables set by configure mandir = @mandir@ datarootdir = @datarootdir@ XSLTPROC = @XSLTPROC@ # features enabled or disabled by configure WITH_83_SUPPORT = @WITH_83_SUPPORT@ WITH_84_SUPPORT = @WITH_84_SUPPORT@ WITH_UDEV = @WITH_UDEV@ WITH_XEN = @WITH_XEN@ WITH_PACEMAKER = @WITH_PACEMAKER@ WITH_RGMANAGER = @WITH_RGMANAGER@ WITH_BASHCOMPLETION = @WITH_BASHCOMPLETION@ WITH_DRBDMON=@WITH_DRBDMON@ # variables meant to be overridden from the make command line DESTDIR ?= / CREATE_MAN_LINK ?= yes MANPAGES := drbdsetup.8 drbd.conf.5 drbd.8 drbdadm.8 drbdmeta.8 MANPAGES += drbd-overview.8 ifeq ($(WITH_DRBDMON), yes) MANPAGES += drbdmon.8 endif STYLESHEET_PREFIX ?= http://docbook.sourceforge.net/release/xsl/current MANPAGES_STYLESHEET ?= $(STYLESHEET_PREFIX)/manpages/docbook.xsl HTML_STYLESHEET ?= $(STYLESHEET_PREFIX)/xhtml/docbook.xsl FO_STYLESHEET ?= $(STYLESHEET_PREFIX)/fo/docbook.xsl XSLTPROC_OPTIONS ?= --xinclude XSLTPROC_OPTIONS += --stringparam variablelist.term.break.after 1 #XSLTPROC_OPTIONS += --stringparam variablelist.term.separator "" XSLTPROC_MANPAGES_OPTIONS ?= $(XSLTPROC_OPTIONS) XSLTPROC_HTML_OPTIONS ?= $(XSLTPROC_OPTIONS) XSLTPROC_FO_OPTIONS ?= $(XSLTPROC_OPTIONS) DRBDSETUP_CMDS = new-resource new-minor del-resource del-minor DRBDSETUP_CMDS += new-peer new-path del-peer del-path DRBDSETUP_CMDS += attach connect disk-options net-options resource-options peer-device-options DRBDSETUP_CMDS += disconnect detach primary secondary verify invalidate invalidate-remote DRBDSETUP_CMDS += down role cstate dstate DRBDSETUP_CMDS += resize check-resize pause-sync resume-sync DRBDSETUP_CMDS += outdate show-gi get-gi show events2 DRBDSETUP_CMDS += status suspend-io resume-io new-current-uuid DRBDSETUP_CMDS += wait-connect-volume wait-connect-connection wait-connect-resource DRBDSETUP_CMDS += wait-sync-volume wait-sync-connection wait-sync-resource DRBDSETUP_CMDS += forget-peer make_doc := $(shell $(XSLTPROC) \ $(XSLTPROC_MANPAGES_OPTIONS) \ $(MANPAGES_STYLESHEET) < /dev/null > /dev/null 2>&1 && echo doc ) ifeq ($(make_doc),doc) all: doc else all: @echo "To (re)make the documentation: make doc" endif clean: @echo "To clean the documentation: make doc-clean" .PHONY: all clean doc man doc-clean distclean .PHONY: install uninstall html pdf ps doc: man ORIG_VERSION=9.0 ifeq ($(WITH_84_SUPPORT),yes) MAN_LINK=8.4 else MAN_LINK=$(ORIG_VERSION) endif doc-clean: distclean ####### Implicit rules .SUFFIXES: .sgml .5 .8 .html .pdf .ps %.5 %.8: %.xml $(XSLTPROC) \ $(XSLTPROC_MANPAGES_OPTIONS) \ $(MANPAGES_STYLESHEET) $< %.html: %.xml $(XSLTPROC) -o $@ \ $(XSLTPROC_HTML_OPTIONS) \ $(HTML_STYLESHEET) $< %.fo: %.xml $(XSLTPROC) -o $@ \ $(XSLTPROC_FO_OPTIONS) \ $(FO_STYLESHEET) $< ../../user/v9/drbdsetup.o: FORCE $(MAKE) -C $(@D) drbdsetup .PHONY: FORCE FORCE: # Don't try to re-make files tracked in git FILES_IN_GIT := # $(shell git ls-files) FILES_IN_GIT += Makefile.in drbd-overview.xml drbd.conf.xml.in drbd.conf.xsl FILES_IN_GIT += drbd.xml drbdadm.xml drbdmeta.xml drbdsetup-options.xml FILES_IN_GIT += drbdsetup.xml.in drbdsetup.xsl xml-usage-to-docbook.xsl $(FILES_IN_GIT): ; drbdsetup_X.xml := $(patsubst %,drbdsetup_%.xml,$(DRBDSETUP_CMDS)) drbdsetup_xml-help_X.xml := $(patsubst %,drbdsetup_xml-help_%.xml,$(DRBDSETUP_CMDS)) $(drbdsetup_xml-help_X.xml): ../../user/v9/drbdsetup.o $(drbdsetup_X.xml): xml-usage-to-docbook.xsl drbdsetup_xml-help_%.xml: ../../user/v9/drbdsetup xml-help $* > $@ drbdsetup_%.xml: drbdsetup_xml-help_%.xml $(XSLTPROC) -o $@ xml-usage-to-docbook.xsl $< drbd.conf.xml: drbd.conf.xml.in drbd.conf.xsl drbdsetup-options.xml $(XSLTPROC) -o $@ drbd.conf.xsl $< drbdsetup.xml: drbdsetup.xml.in drbdsetup.xsl drbdsetup-options.xml $(XSLTPROC) -o $@ drbdsetup.xsl $< distclean: ifeq ($(make_doc),doc) rm -f *.[58] manpage.links manpage.refs *~ manpage.log endif rm -f *.ps.gz *.pdf *.ps *.html pod2htm* rm -f drbdsetup_*.xml rm -f drbd.conf.xml drbdsetup.xml .PRECIOUS: drbd.conf.xml drbdsetup.xml ####### man: $(MANPAGES) install: @ok=true; for f in $(MANPAGES) ; \ do [ -e $$f ] || { echo $$f missing ; ok=false; } ; \ done ; $$ok set -e; for f in $(MANPAGES) ; do \ s=$${f##*.}; \ b=$${f%.[0-9]}; \ install -v -D -m 644 $$f $(DESTDIR)$(mandir)/man$$s/$$b-$(ORIG_VERSION).$$s ; \ if [ "$(CREATE_MAN_LINK)" = "yes" ]; then \ ML=$(MAN_LINK); \ if [ $$f = "drbdmon.8" ]; then ML=$(ORIG_VERSION); fi ; \ ln -sf $$b-$$ML.$$s $(DESTDIR)$(mandir)/man$$s/$$f ; \ fi \ done uninstall: @ set -e; for f in $(MANPAGES) ; do \ s=$${f##*.}; \ b=$${f%.[0-9]}; \ rm -vf $(DESTDIR)$(mandir)/man$$s/$$b-$(ORIG_VERSION).$$s ; \ rm -vf $(DESTDIR)$(mandir)/man$$s/$$f ; \ done html: $(MANPAGES:.8=.html) pdf: $(MANPAGES:.8=.pdf) ps: $(MANPAGES:.8=.ps) drbdsetup.8: drbdsetup.xml $(drbdsetup_X.xml) .PHONY: install uninstall clean distclean ../../configure: @echo "please (re-)run ./autogen.sh with appropriate arguments"; exit 1 ../../config.status: ../../configure @echo "please (re-)run ./configure with appropriate arguments"; exit 1 Makefile: Makefile.in ../../config.status cd ../.. && ./config.status documentation/v9/Makefile drbd-utils-8.9.10/documentation/v9/drbdadm.80000644000175000017500000002677113027211670020467 0ustar apoikosapoikos'\" t .\" Title: drbdadm .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 6 December 2012 .\" Manual: System Administration .\" Source: DRBD 9.0.0 .\" Language: English .\" .TH "DRBDADM" "8" "6 December 2012" "DRBD 9.0.0" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbdadm \- Utility for DRBD administration .SH "SYNOPSIS" .HP \w'\fBdrbdadm\fR\ 'u \fBdrbdadm\fR [options...] [\-\-\ [\fIbackend\-options\fR...]] {\fIcommand\fR} {\fIcontext\fR...} .SH "DESCRIPTION" .PP The \fBdrbdadm\fR utility is used for managing DRBD based on its configuration files, see \fBdrbd.conf\fR(5)\&. It translates high\-level commands into one or more lower\-level commands for the \fBdrbdsetup\fR and \fBdrbdmeta\fR utilities, which control the kernel module and manipulate the on\-disk metadata\&. .PP Depending on the command, the \fBdrbdadm\fR utility operates on one or more resources, devices, connections, or peer devices\&. The following command contexts are defined: .PP .PP \fIresource\fR .RS 4 A resource specified by name, or the keyword \fBall\fR for all defined resources\&. .RE .PP \fIdevice\fR .RS 4 A device, specified by minor number (\fBminor\-\fR\fIminornumber\fR, e\&.g\&. \fBminor\-\fR\fI0\fR) or by resource and volume number (\fIresource\fR/\fIvolume\fR)\&. If only a \fIresource\fR is specified, the command iterates over all devices of that resource\&. .RE .PP \fIconnection\fR .RS 4 A connection, specified by resource and connection name (\fIresource\fR:\fIconnection\-name\fR)\&. If only a \fIresource\fR is specified, the command iterates over all connections of that resource\&. .RE .PP \fIpeer_device\fR .RS 4 A peer device, specified by resource, connection name, and volume number (\fIresource\fR:\fIconnection\-name\fR/\fIvolume\fR)\&. If only a \fIresource\fR, \fIdevice\fR, or \fIconnection\fR is specified, the command iterates over all peer devices of that resource, device, or connection\&. .RE .PP All options following a double\-dash are passed through to the lower\-level utilities as specified\&. In addition, \fBdrbdadm\fR understands most of the options of \fBdrbdsetup\fR, and will pass them through even without the double\-dash\&. .SH "OPTIONS" .PP \fB\-d\fR, \fB\-\-dry\-run\fR .RS 4 Show which commands \fBdrbdadm\fR would execute instead of actually executing them (for example, \fBdrbdadm \-d up \fR\fB\fIresource\fR\fR)\&. This can be a useful way to learn how \fBdrbdsetup\fR and \fBdrbdmeta\fR are used\&. .RE .PP \fB\-c\fR, \fB\-\-config\-file\fR \fIfile\fR .RS 4 Use an alternative configuration file\&. By default, \fBdrbdadm\fR will use the the first of the following files that exists: \fB/etc/drbd\-90\&.conf\fR, \fB/etc/drbd\-84\&.conf\fR, \fB/etc/drbd\-83\&.conf\fR, \fB/etc/drbd\-82\&.conf\fR, \fB/etc/drbd\-08\&.conf\fR, \fB/etc/drbd\&.conf\fR\&. .RE .PP \fB\-t\fR, \fB\-\-config\-to\-test\fR \fIfile\fR .RS 4 Check an additional configuration file\&. This option is only allowed with the dump and the sh\-nop commands\&. .RE .PP \fB\-s\fR, \fB\-\-drbdsetup\fR \fIfile\fR .RS 4 Specifies the full path to the \fBdrbdsetup\fR program\&. If this option is omitted, drbdadm will look for it beneath itself first, and then in the PATH\&. .RE .PP \fB\-m\fR, \fB\-\-drbdmeta\fR \fIfile\fR .RS 4 Specifies the full path to the \fBdrbdmeta\fR program\&. If this option is omitted, drbdadm will look for it beneath itself first, and then in the PATH\&. .RE .PP \fB\-S\fR, \fB\-\-stacked\fR .RS 4 Perform the command on a stacked resource\&. .RE .SH "COMMANDS" .PP adjust {\fIresource\fR} .RS 4 Adjust the configuration of the kernel module so that it matches the configuration files\&. The result should be the same as when stopping and restarting all resources (\fBdrbdadm down all\fR followed by \fBdrbdadm up all\fR), but without the interruptions\&. .sp Note that the adjust command can misinterpret the configuration change in some cases\&. To be safe, check what the command would do (with the \fB\-\-dry\-run\fR option) before running the actual command\&. .RE .PP adjust\-with\-progress {\fIresource\fR} .RS 4 The same as \fBadjust\fR, but with some more information about the command\*(Aqs progress\&. .RE .PP apply\-al {\fIdevice\fR} .RS 4 Apply the activity log of the specified device\&. See \fBdrbdmeta\fR(8) for details\&. .RE .PP attach {\fIdevice\fR} .RS 4 Attach a lower\-level device to an existing replicated device\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP check\-resize {\fIdevice\fR} .RS 4 Call drbdmeta to eventually move internal meta data\&. If the backing device was resized, while DRBD was not running, meta data has to be moved to the end of the device, so that the next \fBattach\fR command can succeed\&. .RE .PP connect {\fIconnection\fR} .RS 4 Activate an exisiting connection to a peer\&. The connection needs to be created first with the \fBnew\-peer\fR command, and have at least one path created with the \fBnew\-path\fR command\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP create\-md {\fIdevice\fR} .RS 4 Initialize the metadata of a device\&. This is necessary before a device can be attached; see \fBdrbdmeta\fR(8) for details\&. .RE .PP cstate {\fIconnection\fR} .RS 4 Show the current state of a connection\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP detach {\fIdevice\fR} .RS 4 Detach the lower\-level device of a replicated device\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP disconnect {\fIconnection\fR} .RS 4 Remove a connection to a peer host\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP disk\-options {\fIdevice\fR} .RS 4 Cange the disk options of an attached lower\-level device\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP down {\fIresource\fR} .RS 4 Take a resource down by removing all volumes, connections, and the resource itself\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP dstate {\fIdevice\fR} .RS 4 Show the current disk state of a lower\-level device\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP dump {\fIresource\fR} .RS 4 Parse the configuration file and dump it to stdout\&. This will fail if the configuration file is syntactically incorrect\&. .RE .PP dump\-md {\fIdevice\fR} .RS 4 Dump the metadata of a device in text form, including the bitmap and activity log\&. See \fBdrbdmeta\fR(8) for details\&. .RE .PP get\-gi {\fIpeer_device\fR} .RS 4 Show the data generation identifiers for a device on a particular connection\&. Uses \fBdrbdsetup\fR for attached devices and \fBdrbdmeta\fR for unattached devices\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP hidden\-commands .RS 4 Shows all commands which are not explicitly documented\&. .RE .PP invalidate {\fIpeer_device\fR} .RS 4 Replace the local data of a device with that of a peer\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP invalidate\-remote {\fIpeer_device\fR} .RS 4 Replace a peer device\*(Aqs data of a resource with the local data\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP net\-options {\fIconnection\fR} .RS 4 Change the network options of an existing connection\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP new\-current\-uuid {\fIdevice\fR} .RS 4 Generate a new currend UUID\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP outdate {\fIdevice\fR} .RS 4 Mark the data on a lower\-level device as outdated\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP pause\-sync {\fIpeer_device\fR} .RS 4 Stop resynchronizing between a local and a peer device by setting the local pause flag\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP primary {\fIresource\fR} .RS 4 Change the role of a node in a resource to primary\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP resize {\fIdevice\fR} .RS 4 Resize the lower\-level devices of a replicated device on all nodes\&. This combines the \fBcheck\-resize\fR and \fBresize\fR lower\-level commands; see \fBdrbdsetup\fR(8) for details\&. .RE .PP resource\-options {\fIresource\fR} .RS 4 Change the resource options of an existing resource\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP resume\-sync {\fIpeer_device\fR} .RS 4 Allow resynchronization to resume by clearing the local sync pause flag\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP role {\fIresource\fR} .RS 4 Show the current role of a resource\&. .RE .PP secondary {\fIresource\fR} .RS 4 Change the role of a node in a resource to secondary\&. This command fails if the replicated device is in use\&. .RE .PP show\-gi {\fIpeer_device\fR} .RS 4 Show the data generation identifiers for a device on a particular connection\&. In addition, explain the output\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP state {\fIresource\fR} .RS 4 This is an alias for \fBdrbdsetup role\fR\&. Deprecated\&. .RE .PP up {\fIresource\fR} .RS 4 Bring up a resource by applying the activity log of all volumes, creating the resource, creating the replicated devices, attaching the lower\-level devices, and connecting to all peers\&. See the \fBapply\-al\fR drbdmeta command and the \fBnew\-resource\fR, \fBnew\-device\fR, \fBnew\-minor\fR, \fBattach\fR, and \fBconnect\fR drbdsetup commands\&. .RE .PP verify {\fIpeer_device\fR} .RS 4 Start online verification, change which part of the device will be verified, or stop online verification\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP wait\-connect {[\fIdevice\fR] | [\fIconnection\fR] | [\fIresource\fR]} .RS 4 Wait until a device on a peer, all devices over a connection, or all devices on all peers are visible\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP wait\-sync {[\fIdevice\fR] | [\fIconnection\fR] | [\fIresource\fR]} .RS 4 Wait until a device is connected and has finished eventual resync operation\&. Also available on connection and resource level\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP wipe\-md {\fIdevice\fR} .RS 4 Wipe out the DRBD metadata of a device\&. See \fBdrbdmeta\fR(8) for details\&. .RE .PP forget\-peer {\fIconnection\fR} .RS 4 Completely remove any reference to a unconnected peer from meta\-data\&. See \fBdrbdmeta\fR(8) for details\&. .RE .SH "VERSION" .sp This document was revised for version 9\&.0\&.0 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2012 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbd\fR(8), \fBdrbddisk\fR(8), \fBdrbdsetup\fR(8), \fBdrbdmeta\fR(8) and the \m[blue]\fBDRBD project web site\fR\m[]\&\s-2\u[1]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD project web site .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v9/drbdsetup.xsl0000644000175000017500000000233012466702073021516 0ustar apoikosapoikos drbd-utils-8.9.10/documentation/v9/drbd-overview.xml0000644000175000017500000000537312466702073022305 0ustar apoikosapoikos drbd-overview Overview of all configured DRBD resources DRBD 9.0.0 24 June 2014 drbd-overview 8 System Administration drbd-overview Description drbd-overview offers an overview of the state of all configured DRBD resources. It provides information about the connection status of each DRBD resource. It will also use auxiliary information provided by df1, lvm8, xm1 and virsh1 for DRBD resources used as mounted filesystems, LVM physical volumes, Xen domain backing disks and libvirt instance disks respectively. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Initial man-page contributed by Apollon Oikonomopoulos apoikos@gmail.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2008-2014 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf5, drbdsetup8, drbdadm8, DRBD Homepage drbd-utils-8.9.10/documentation/v9/drbdsetup.xml.in0000644000175000017500000012667313002133622022117 0ustar apoikosapoikos ]> &drbdsetup_options; 3 December 2011 DRBD 9.0.0 drbdsetup 8 System Administration drbdsetup Configure the DRBD kernel module drbdsetup drbdsetup command argument option Description The drbdsetup utility serves to configure the DRBD kernel module and to show its current configuration. Users usually interact with the drbdadm utility, which provides a more high-level interface to DRBD than drbdsetup. (See 's option to see how uses .) Some option arguments have a default scale which applies when a plain number is specified (for example Kilo, or 1024 times the numeric value). Such default scales can be overridden by using a suffix (for example, M for Mega). The common suffixes K = 2^10 = 1024, M = 1024 K, and G = 1024 M are supported. Commands drbdsetup disk The command attaches a lower-level device to an existing replicated device. The command changes the disk options of an attached lower-level device. In either case, the replicated device must have been created with drbdsetup new-minor. Both commands refer to the replicated device by its minor number. lower_dev is the name of the lower-level device. meta_data_dev is the name of the device containing the metadata, and may be the same as lower_dev. meta_data_index is either a numeric metadata index, or the keyword for internal metadata, or the keyword for variable-size external metadata. Available options: drbdsetup peer-device-options These are options that affect the peer's device. drbdsetup check-resize Remember the current size of the lower-level device of the specified replicated device. Used by drbdadm. The size information is stored in file /var/lib/drbd/drbd-minor-minor.lkbd. drbdsetup net The command creates a connection within a resource. The resource must have been created with drbdsetup new-resource. The command changes the network options of an existing connection. Before a connection can be activated with the command, at least one path need to added with the command. Available options: drbdsetup net The command creates a path within a connection. The connection must have been created with drbdsetup new-peer. Local_addr and remote_addr refer to the local and remote protocol, network address, and port in the format address-family:address:port. The address families , , (Dolphin Interconnect Solutions' "super sockets"), (Infiniband Sockets Direct Protocol), and are supported ( is an alias for ). If no address family is specified, is assumed. For all address families except , the address uses IPv4 address notation (for example, 1.2.3.4). For , the address is enclosed in brackets and uses IPv6 address notation (for example, [fd01:2345:6789:abcd::1]). The port defaults to 7788. drbdsetup net The command activates a connection. That means that the DRBD driver will bind and listen on all local addresses of the connection-'s paths. It will begin to try to establish one or more paths of the connection. Available options: drbdsetup net The command removes a connection from a resource. drbdsetup net The command removes a path from a connection. Please not that it fails if the path is necessary to keep a connected connection in tact. In order to remove all paths, disconnect the connection first. drbdsetup cstate Show the current state of a connection. The connection is identified by the node-id of the peer; see the drbdsetup connect command. Remove a replicated device. No lower-level device may be attached; see drbdsetup detach. Remove a resource. All volumes and connections must be removed first (drbdsetup del-minor, drbdsetup disconnect). Alternatively, drbdsetup down can be used to remove a resource together with all its volumes and connections. drbdsetup detach Detach the lower-level device of a replicated device. Available options: Force the detach and return immediately. This puts the lower-level device into failed state until all pending I/O has completed, and then detaches the device. Any I/O not yet submitted to the lower-level device (for example, because I/O on the device was suspended) is assumed to have failed. drbdsetup disconnect Remove a connection to a peer host. The connection is identified by the node-id of the peer; see the drbdsetup connect command. drbdsetup down Take a resource down by removing all volumes, connections, and the resource itself. drbdsetup dstate Show the current disk state of a lower-level device. drbdsetup events2 Show the current state of all configured DRBD objects, followed by all changes to the state. The output format is meant to be human as well as machine readable. The line starts with a word that indicates the kind of event: for an existing object; , , and if an object is created, destroyed, or changed; or or if an event handler is called or it returns. The second word indicates the object the event applies to: , , , , , or a dash () to indicate that the current state has been dumped completely. The remaining words identify the object and describe the state that he object is in. Available options: Terminate after reporting the current state. The default is to continuously listen and report state changes. Include statistics in the output. drbdsetup get-gi Show the data generation identifiers for a device on a particular connection. The device is identified by its volume number. The connection is identified by its endpoints; see the drbdsetup connect command. The output consists of the current UUID, bitmap UUID, and the first two history UUIDS, folowed by a set of flags. The current UUID and history UUIDs are device specific; the bitmap UUID and flags are peer device specific. This command only shows the first two history UUIDs. Internally, DRBD maintains one history UUID for each possible peer device. drbdsetup invalidate Replace the local data of a device with that of a peer. All the local data will be marked out-of-sync, and a resync with the specified peer device will be initialted. drbdsetup invalidate-remote Replace a peer device's data of a resource with the local data. The peer device's data will be marked out-of-sync, and a resync from the local node to the specified peer will be initiated. drbdsetup new-current-uuid Generate a new current UUID and rotates all other UUID values. This has at least two use cases, namely to skip the initial sync, and to reduce network bandwidth when starting in a single node configuration and then later (re-)integrating a remote site. Available option: Clears the sync bitmap in addition to generating a new current UUID. This can be used to skip the initial sync, if you want to start from scratch. This use-case does only work on "Just Created" meta data. Necessary steps: On both nodes, initialize meta data and configure the device. drbdadm create-md --force res They need to do the initial handshake, so they know their sizes. drbdadm up res They are now Connected Secondary/Secondary Inconsistent/Inconsistent. Generate a new current-uuid and clear the dirty bitmap. drbdadm --clear-bitmap new-current-uuid res They are now Connected Secondary/Secondary UpToDate/UpToDate. Make one side primary and create a file system. drbdadm primary res mkfs -t fs-type $(drbdadm sh-dev res) One obvious side-effect is that the replica is full of old garbage (unless you made them identical using other means), so any online-verify is expected to find any number of out-of-sync blocks. You must not use this on pre-existing data! Even though it may appear to work at first glance, once you switch to the other node, your data is toast, as it never got replicated. So do not leave out the mkfs (or equivalent). This can also be used to shorten the initial resync of a cluster where the second node is added after the first node is gone into production, by means of disk shipping. This use-case works on disconnected devices only, the device may be in primary or secondary role. The necessary steps on the current active server are: drbdsetup new-current-uuid --clear-bitmap minor Take the copy of the current active server. E.g. by pulling a disk out of the RAID1 controller, or by copying with dd. You need to copy the actual data, and the meta data. drbdsetup new-current-uuid minor Now add the disk to the new secondary node, and join it to the cluster. You will get a resync of that parts that were changed since the first call to drbdsetup in step 1. Create a new replicated device within a resource. The command creates a block device inode for the replicated device (by default, /dev/drbdminor). The volume number identifies the device within the resource. The command creates a new resource. The command changes the resource options of an existing resource. Available options: drbdsetup outdate Mark the data on a lower-level device as outdated. This is used for fencing, and prevents the resource the device is part of from becoming primary in the future. See the disk option. drbdsetup pause-sync Stop resynchronizing between a local and a peer device by setting the local pause flag. The resync can only resume if the pause flags on both sides of a connection are cleared. drbdsetup primary Change the role of a node in a resource to primary. This allows the replicated devices in this resource to be mounted or opened for writing. Available options: This option is an alias for the option. Force the resource to become primary even if some devices are not guaranteed to have up-to-date data. This option is used to turn one of the nodes in a newly created cluster into the primary node, or when manually recovering from a disaster. Note that this can lead to split-brain scenarios. Also, when forcefully turning an inconsistent device into an up-to-date device, it is highly recommended to use any integrity checks available (such as a filesystem check) to make sure that the device can at least be used without crashing the system. Note that DRBD usually only allows one node in a cluster to be in primary role at any time; this allows DRBD to coordinate access to the devices in a resource across nodes. The network option changes this; in that case, a mechanism outside of DRBD needs to coordinate device access. drbdsetup resize Reexamine the size of the lower-level devices of a replicated device on all nodes. This command is called after the lower-level devices on all nodes have been grown to adjust the size of the replicated device. Available options: Resize the device even if some of the peer devices are not connected at the moment. DRBD will try to resize the peer devices when they next connect. It will refuse to connect to a peer device which is too small. Do not resynchronize the added disk space; instead, assume that it is identical on all nodes. This option can be used when the disk space is uninitialized and differences do not matter, or when it is known to be identical on all nodes. See the drbdsetup verify command. This option can be used to online shrink the usable size of a drbd device. It's the users responsibility to make sure that a file system on the device is not truncated by that operation. These options may be used to change the layout of the activity log online. In case of internal meta data this may invovle shrinking the user visible size at the same time (unsing the ) or increasing the avalable space on the backing devices. drbdsetup resume-io Resume I/O on a replicated device. See the net option. drbdsetup resume-sync Allow resynchronization to resume by clearing the local sync pause flag. drbdsetup role Show the current role of a resource. drbdsetup secondary Change the role of a node in a resource to secondary. This command fails if the replicated device is in use. drbdsetup show Show the current configuration of a resource, or of all resources. Available options: Show all configuration parameters, even the ones with default values. Normally, parameters with default values are not shown. drbdsetup show-gi Show the data generation identifiers for a device on a particular connection. In addition, explain the output. The output otherwise is the same as in the drbdsetup get-gi command. drbdsetupstate drbdsetup state This is an alias for drbdsetup role. Deprecated. drbdsetup status Show the status of a resource, or of all resources. The output consists of one paragraph for each configured resource. Each paragraph contains one line for each resource, followed by one line for each device, and one line for each connection. The device and connection lines are indented. The connection lines are followed by one line for each peer device; these lines are indented against the connection line. Long lines are wrapped around at terminal width, and indented to indicate how the lines belongs together. Available options: Include more information in the output even when it is likely redundant or irrelevant. Include data transfer statistics in the output. Colorize the output. With , emits color codes only when standard output is connected to a terminal. For example, the non-verbose output for a resource with only one connection and only one volume could look like this: drbd0 role:Primary disk:UpToDate host2.example.com role:Secondary disk:UpToDate With the option, the same resource could be reported as: drbd0 node-id:1 role:Primary suspended:no volume:0 minor:1 disk:UpToDate blocked:no host2.example.com local:ipv4:192.168.123.4:7788 peer:ipv4:192.168.123.2:7788 node-id:0 connection:WFReportParams role:Secondary congested:no volume:0 replication:Connected disk:UpToDate resync-suspended:no drbdsetup suspend-io Suspend I/O on a replicated device. It is not usually necessary to use this command. drbdsetup verify Start online verification, change which part of the device will be verified, or stop online verification. The command requires the specified peer to be connected. Online verification compares each disk block on the local and peer node. Blocks which differ between the nodes are marked as out-of-sync, but they are not automatically brought back into sync. To bring them into sync, the resource must be disconnected and reconnected. Progress can be monitored in the output of drbdsetup status --statistics. Available options: Define where online verification should start. This parameter is ignored if online verification is already in progress. If the start parameter is not specified, online verification will continue where it was interrupted (if the connection to the peer was lost while verifying), after the previous stop sector (if the previous online verification has finished), or at the beginning of the device (if the end of the device was reached, or online verify has not run before). The position on disk is specified in disk sectors (512 bytes) by default. Define where online verification should stop. If online verification is already in progress, the stop position of the active online verification process is changed. Use this to stop online verification. The position on disk is specified in disk sectors (512 bytes) by default. Also see the notes on data integrity in the drbd.conf 5 manual page. drbdsetup wait-connect-volume wait-connect-connection wait-connect-resource drbdsetup wait-sync-volume wait-sync-connection wait-sync-resource The commands waits until a device on a peer is visible. The commands waits until a device on a peer is up to date. Available options for both commands: drbdsetup forget-peer The command removes all traces of a peer node from the meta-data. It frees a bitmap slot in the meta-data and make it avalable for futher bitmap slot allocation in case a so-far never seen node connects. The connection must be taken down before this command may be used. In case the peer re-connects at a later point a bit-map based resync will be turned into a full-sync. Examples Please see the DRBD User's Guide for examples. Version This document was revised for version 9.0.0 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2012 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf 5 , drbd 8 , drbddisk 8 , drbdadm 8 , DRBD User's Guide, DRBD Web Site drbd-utils-8.9.10/documentation/v9/drbd.conf.50000644000175000017500000017132313027211667020726 0ustar apoikosapoikos'\" t .\" Title: drbd.conf .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 3 December 2012 .\" Manual: Configuration Files .\" Source: DRBD 9.0.0 .\" Language: English .\" .TH "DRBD\&.CONF" "5" "3 December 2012" "DRBD 9.0.0" "Configuration Files" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbd.conf \- DRBD Configuration Files .SH "INTRODUCTION" .PP DRBD implements block devices which replicate their data to all nodes of a cluster\&. The actual data and associated metadata are usually stored redundantly on "ordinary" block devices on each cluster node\&. .PP Replicated block devices are called \fB/dev/drbd\fR\fB\fIminor\fR\fR by default\&. They are grouped into resources, with one or more devices per resource\&. Replication among the devices in a resource takes place in chronological order\&. With DRBD, we refer to the devices inside a resource as \fIvolumes\fR\&. .PP In DRBD 9, a resource can be replicated between two or more cluster nodes\&. The connections between cluster nodes are point\-to\-point links, and use TCP or a TCP\-like protocol\&. All nodes must be directly connected\&. .PP DRBD consists of low\-level user\-space components which interact with the kernel and perform basic operations (\fBdrbdsetup\fR, \fBdrbdmeta\fR), a high\-level user\-space component which understands and processes the DRBD configuration and translates it into basic operations of the low\-level components (\fBdrbdadm\fR), and a kernel component\&. .PP The default DRBD configuration consists of \fB/etc/drbd\&.conf\fR and of additional files included from there, usually \fBglobal_common\&.conf\fR and all \fB\fI*\fR\fR\fB\&.res\fR files inside \fB/etc/drbd\&.d/\fR\&. It has turned out to be useful to define each resource in a separate \fB\fI*\fR\fR\fB\&.res\fR file\&. .PP The configuration files are designed so that each cluster node can contain an identical copy of the entire cluster configuration\&. The host name of each node determines which parts of the configuration apply (\fBuname \-n\fR)\&. It is highly recommended to keep the cluster configuration on all nodes in sync by manually copying it to all nodes, or by automating the process with \fBcsync2\fR or a similar tool\&. .SH "EXAMPLE CONFIGURATION FILE" .PP .if n \{\ .RS 4 .\} .nf resource r0 { net { cram\-hmac\-alg sha1; shared\-secret "FooFunFactory"; } volume 0 { device /dev/drbd1; disk /dev/sda7; meta\-disk internal; } on alice { node\-id 0; address 10\&.1\&.1\&.31:7000; } on bob { node\-id 1; address 10\&.1\&.1\&.32:7000; } connection { host alice port 7000; host bob port 7000; net { protocol C; } } } .fi .if n \{\ .RE .\} .sp This example defines a resource \fBr0\fR which contains a single replicated device with volume number 0\&. The resource is replicated among hosts \fBalice\fR and \fBbob\fR, which have the IPv4 addresses \fB10\&.1\&.1\&.31\fR and \fB10\&.1\&.1\&.32\fR and the node identifiers 0 and 1, respectively\&. On both hosts, the replicated device is called \fB/dev/drbd1\fR, and the actual data and metadata are stored on the lower\-level device \fB/dev/sda7\fR\&. The connection between the hosts uses protocol C\&. .PP Please refer to the \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2 for more examples\&. .SH "FILE FORMAT" .PP DRBD configuration files consist of sections, which contain other sections and parameters depending on the section types\&. Each section consists of one or more keywords, sometimes a section name, an opening brace (\(lq{\(rq), the section\*(Aqs contents, and a closing brace (\(lq}\(rq)\&. Parameters inside a section consist of a keyword, followed by one or more keywords or values, and a semicolon (\(lq;\(rq)\&. .PP Some parameter values have a default scale which applies when a plain number is specified (for example Kilo, or 1024 times the numeric value)\&. Such default scales can be overridden by using a suffix (for example, \fBM\fR for Mega)\&. The common suffixes \fBK\fR = 2^10 = 1024, \fBM\fR = 1024 K, and \fBG\fR = 1024 M are supported\&. .PP Comments start with a hash sign (\(lq#\(rq) and extend to the end of the line\&. In addition, any section can be prefixed with the keyword \fBskip\fR, which causes the section and any sub\-sections to be ignored\&. .PP Additional files can be included with the \fBinclude \fR\fB\fIfile\-pattern\fR\fR statement (see \fBglob\fR(7) for the expressions supported in \fIfile\-pattern\fR)\&. Include statements are only allowed outside of sections\&. .PP The following sections are defined (indentation indicates in which context): .sp .if n \{\ .RS 4 .\} .nf common [disk] [handlers] [net] [options] [startup] global resource connection path net connection\-mesh net [disk] floating handlers [net] on volume disk [disk] options stacked\-on\-top\-of startup .fi .if n \{\ .RE .\} .sp Sections in brackets affect other parts of the configuration: inside the \fBcommon\fR section, they apply to all resources\&. A \fBdisk\fR section inside a \fBresource\fR or \fBon\fR section applies to all volumes of that resource, and a \fBnet\fR section inside a \fBresource\fR section applies to all connections of that resource\&. This allows to avoid repeating identical options for each resource, connection, or volume\&. Options can be overridden in a more specific \fBresource\fR, \fBconnection\fR, \fBon\fR, or \fBvolume\fR section\&. .SS "Sections" .PP \fBcommon\fR .RS 4 This section can contain each a \fBdisk\fR, \fBhandlers\fR, \fBnet\fR, \fBoptions\fR, and \fBstartup\fR section\&. All resources inherit the parameters in these sections as their default values\&. .RE .PP \fBconnection \fR\fB\fI[name]\fR\fR .RS 4 Define a connection between two hosts\&. This section must contain two \fBhost\fR parameters or multiple \fBpath sections\fR\&. The optional \fIname\fR is used to refer to the connection in the system log and in other messages\&. If no name is specified, the peer\*(Aqs host name is used instead\&. .RE .PP \fBpath\fR .RS 4 Define a path between two hosts\&. This section must contain two \fBhost\fR parameters\&. .RE .PP \fBconnection\-mesh\fR .RS 4 Define a connection mesh between multiple hosts\&. This section must contain a \fBhosts\fR parameter, which has the host names as arguments\&. This section is a shortcut to define many connections which share the same network options\&. .RE .PP \fBdisk\fR .RS 4 Define parameters for a volume\&. All parameters in this section are optional\&. .RE .PP \fBfloating \fR\fB\fI[address\-family]\fR\fR\fB \fR\fB\fIaddr\fR\fR\fB:\fR\fB\fIport\fR\fR .RS 4 Like the \fBon\fR section, except that instead of the host name a network address is used to determine if it matches a \fBfloating\fR section\&. .sp The \fBnode\-id\fR parameter in this section is required\&. If the \fBaddress\fR parameter is not provided, no connections to peers will be created by default\&. The \fBdevice\fR, \fBdisk\fR, and \fBmeta\-disk\fR parameters must be defined in, or inherited by, this section\&. .RE .PP \fBglobal\fR .RS 4 Define some global parameters\&. All parameters in this section are optional\&. Only one \fBglobal\fR section is allowed in the configuration\&. .RE .PP \fBhandlers\fR .RS 4 Define handlers to be invoked when certain events occur\&. The kernel passes the resource name in the first command\-line argument and sets the following environment variables depending on the event\*(Aqs context: .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} For events related to a particular device: the device\*(Aqs minor number in \fBDRBD_MINOR\fR, the device\*(Aqs volume number in \fBDRBD_VOLUME\fR\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} For events related to a particular device on a particular peer: the connection endpoints in \fBDRBD_MY_ADDRESS\fR, \fBDRBD_MY_AF\fR, \fBDRBD_PEER_ADDRESS\fR, and \fBDRBD_PEER_AF\fR; the device\*(Aqs local minor number in \fBDRBD_MINOR\fR, and the device\*(Aqs volume number in \fBDRBD_VOLUME\fR\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} For events related to a particular connection: the connection endpoints in \fBDRBD_MY_ADDRESS\fR, \fBDRBD_MY_AF\fR, \fBDRBD_PEER_ADDRESS\fR, and \fBDRBD_PEER_AF\fR; and, for each device defined for that connection: the device\*(Aqs minor number in \fBDRBD_MINOR_\fR\fB\fIvolume\-number\fR\fR\&. .RE .sp .RS 4 .ie n \{\ \h'-04'\(bu\h'+03'\c .\} .el \{\ .sp -1 .IP \(bu 2.3 .\} For events that identify a device, if a lower\-level device is attached, the lower\-level device\*(Aqs device name is passed in \fBDRBD_BACKING_DEV\fR (or \fBDRBD_BACKING_DEV_\fR\fB\fIvolume\-number\fR\fR)\&. .RE .sp All parameters in this section are optional\&. Only a single handler can be defined for each event; if no handler is defined, nothing will happen\&. .RE .PP \fBnet\fR .RS 4 Define parameters for a connection\&. All parameters in this section are optional\&. .RE .PP \fBon\fR \fB\fIhost\-name\fR\fR \fI[\&.\&.\&.]\fR .RS 4 Define the properties of a resource on a particular host or set of hosts\&. Specifying more than one host name can make sense in a setup with IP address failover, for example\&. The \fIhost\-name\fR argument must match the Linux host name (\fBuname \-n\fR)\&. .sp Usually contains or inherits at least one \fBvolume\fR section\&. The \fBnode\-id\fR and \fBaddress\fR parameters must be defined in this section\&. The \fBdevice\fR, \fBdisk\fR, and \fBmeta\-disk\fR parameters must be defined in, or inherited by, this section\&. .sp A normal configuration file contains two or more \fBon\fR sections for each resource\&. Also see the \fBfloating\fR section\&. .RE .PP \fBoptions\fR .RS 4 Define parameters for a resource\&. All parameters in this section are optional\&. .RE .PP \fBresource\fR \fB\fIname\fR\fR .RS 4 Define a resource\&. Usually contains at least two \fBon\fR sections and at least one \fBconnection\fR section\&. .RE .PP \fBstacked\-on\-top\-of \fR\fB\fIresource\fR\fR .RS 4 Used instead of an \fBon\fR section for configuring a stacked resource with three to four nodes\&. .sp Starting with DRBD 9, stacking is deprecated\&. It is advised to use resources which are replicated among more than two nodes instead\&. .RE .PP \fBstartup\fR .RS 4 The parameters in this section determine the behavior of a resource at startup time\&. .RE .PP \fBvolume\fR \fB\fIvolume\-number\fR\fR .RS 4 Define a volume within a resource\&. The volume numbers in the various \fBvolume\fR sections of a resource define which devices on which hosts form a replicated device\&. .RE .SS "Section connection Parameters" .PP \fBhost \fR\fB\fIname\fR\fR [\fBaddress \fR\fB[address\-family]\fR\fB \fR\fB\fIaddress\fR\fR] [\fBport \fR\fB\fIport\-number\fR\fR] .RS 4 Defines an endpoint for a connection\&. Each \fBhost\fR statement refers to an \fBon\fR section in a resource\&. If a port number is defined, this endpoint will use the specified port instead of the port defined in the \fBon\fR section\&. Each \fBconnection\fR section must contain exactly two \fBhost\fR parameters\&. Instead of two \fBhost\fR parameters the connection may contain multiple \fBpath\fR sections\&. .RE .SS "Section path Parameters" .PP \fBhost \fR\fB\fIname\fR\fR [\fBaddress \fR\fB[address\-family]\fR\fB \fR\fB\fIaddress\fR\fR] [\fBport \fR\fB\fIport\-number\fR\fR] .RS 4 Defines an endpoint for a connection\&. Each \fBhost\fR statement refers to an \fBon\fR section in a resource\&. If a port number is defined, this endpoint will use the specified port instead of the port defined in the \fBon\fR section\&. Each \fBpath\fR section must contain exactly two \fBhost\fR parameters\&. .RE .SS "Section connection\-mesh Parameters" .PP \fBhosts \fR\fB\fIname\fR...\fR .RS 4 Defines all nodes of a mesh\&. Each \fB\fIname\fR\fR refers to an \fBon\fR section in a resource\&. The port that is defined in the \fBon\fR section will be used\&. .RE .SS "Section disk Parameters" .PP \fBal\-extents \fR\fB\fIextents\fR\fR .RS 4 DRBD automatically maintains a "hot" or "active" disk area likely to be written to again soon based on the recent write activity\&. The "active" disk area can be written to immediately, while "inactive" disk areas must be "activated" first, which requires a meta\-data write\&. We also refer to this active disk area as the "activity log"\&. .sp The activity log saves meta\-data writes, but the whole log must be resynced upon recovery of a failed node\&. The size of the activity log is a major factor of how long a resync will take and how fast a replicated disk will become consistent after a crash\&. .sp The activity log consists of a number of 4\-Megabyte segments; the \fIal\-extents\fR parameter determines how many of those segments can be active at the same time\&. The default value for \fIal\-extents\fR is 1237, with a minimum of 7 and a maximum of 65536\&. .sp Note that the effective maximum may be smaller, depending on how you created the device meta data, see also \fBdrbdmeta\fR(8) The effective maximum is 919 * (available on\-disk activity\-log ring\-buffer area/4kB \-1), the default 32kB ring\-buffer effects a maximum of 6433 (covers more than 25 GiB of data) We recommend to keep this well within the amount your backend storage and replication link are able to resync inside of about 5 minutes\&. .RE .PP \fBal\-updates \fR\fB{yes | no}\fR\fB \fR .RS 4 With this parameter, the activity log can be turned off entirely (see the \fBal\-extents\fR parameter)\&. This will speed up writes because fewer meta\-data writes will be necessary, but the entire device needs to be resynchronized opon recovery of a failed primary node\&. The default value for \fBal\-updates\fR is \fByes\fR\&. .RE .PP \fBc\-delay\-target \fR\fB\fIdelay_target\fR\fR, .br \fBc\-fill\-target \fR\fB\fIfill_target\fR\fR, .br \fBc\-max\-rate \fR\fB\fImax_rate\fR\fR, .br \fBc\-plan\-ahead \fR\fB\fIplan_time\fR\fR .RS 4 Dynamically control the resync speed\&. This mechanism is enabled by setting the \fBc\-plan\-ahead\fR parameter to a positive value\&. The goal is to either fill the buffers along the data path with a defined amount of data if \fBc\-fill\-target\fR is defined, or to have a defined delay along the path if \fBc\-delay\-target\fR is defined\&. The maximum bandwidth is limited by the \fBc\-max\-rate\fR parameter\&. .sp The \fBc\-plan\-ahead\fR parameter defines how fast drbd adapts to changes in the resync speed\&. It should be set to five times the network round\-trip time or more\&. Common values for \fBc\-fill\-target\fR for "normal" data paths range from 4K to 100K\&. If drbd\-proxy is used, it is advised to use \fBc\-delay\-target\fR instead of \fBc\-fill\-target\fR\&. The \fBc\-delay\-target\fR parameter is used if the \fBc\-fill\-target\fR parameter is undefined or set to 0\&. The \fBc\-delay\-target\fR parameter should be set to five times the network round\-trip time or more\&. The \fBc\-max\-rate\fR option should be set to either the bandwidth available between the DRBD\-hosts and the machines hosting DRBD\-proxy, or to the available disk bandwidth\&. .sp The default values of these parameters are: \fBc\-plan\-ahead\fR = 20 (in units of 0\&.1 seconds), \fBc\-fill\-target\fR = 0 (in units of sectors), \fBc\-delay\-target\fR = 1 (in units of 0\&.1 seconds), and \fBc\-max\-rate\fR = 102400 (in units of KiB/s)\&. .sp Dynamic resync speed control is available since DRBD 8\&.3\&.9\&. .RE .PP \fBc\-min\-rate \fR\fB\fImin_rate\fR\fR .RS 4 A node which is primary and sync\-source has to schedule application I/O requests and resync I/O requests\&. The \fBc\-min\-rate\fR parameter limits how much bandwidth is available for resync I/O; the remaining bandwidth is used for application I/O\&. .sp A \fBc\-min\-rate\fR value of 0 means that there is no limit on the resync I/O bandwidth\&. This can slow down application I/O significantly\&. Use a value of 1 (1 KiB/s) for the lowest possible resync rate\&. .sp The default value of \fBc\-min\-rate\fR is 4096, in units of KiB/s\&. .RE .PP \fBdisk\-barrier\fR, .br \fBdisk\-flushes\fR, .br \fBdisk\-drain\fR .RS 4 DRBD has three methods of handling the ordering of dependent write requests: .PP \fBdisk\-barrier\fR .RS 4 Use disk barriers to make sure that requests are written to disk in the right order\&. Barriers ensure that all requests submitted before a barrier make it to the disk before any requests submitted after the barrier\&. This is implemented using \*(Aqtagged command queuing\*(Aq on SCSI devices and \*(Aqnative command queuing\*(Aq on SATA devices\&. Only some devices and device stacks support this method\&. The device mapper (LVM) only supports barriers in some configurations\&. .sp Note that on systems which do not support disk barriers, enabling this option can lead to data loss or corruption\&. Until DRBD 8\&.4\&.1, \fBdisk\-barrier\fR was turned on if the I/O stack below DRBD did support barriers\&. Kernels since linux\-2\&.6\&.36 (or 2\&.6\&.32 RHEL6) no longer allow to detect if barriers are supported\&. Since drbd\-8\&.4\&.2, this option is off by default and needs to be enabled explicitly\&. .RE .PP \fBdisk\-flushes\fR .RS 4 Use disk flushes between dependent write requests, also referred to as \*(Aqforce unit access\*(Aq by drive vendors\&. This forces all data to disk\&. This option is enabled by default\&. .RE .PP \fBdisk\-drain\fR .RS 4 Wait for the request queue to "drain" (that is, wait for the requests to finish) before submitting a dependent write request\&. This method requires that requests are stable on disk when they finish\&. Before DRBD 8\&.0\&.9, this was the only method implemented\&. This option is enabled by default\&. Do not disable in production environments\&. .RE .sp From these three methods, drbd will use the first that is enabled and supported by the backing storage device\&. If all three of these options are turned off, DRBD will submit write requests without bothering about dependencies\&. Depending on the I/O stack, write requests can be reordered, and they can be submitted in a different order on different cluster nodes\&. This can result in data loss or corruption\&. Therefore, turning off all three methods of controlling write ordering is strongly discouraged\&. .sp A general guideline for configuring write ordering is to use disk barriers or disk flushes when using ordinary disks (or an ordinary disk array) with a volatile write cache\&. On storage without cache or with a battery backed write cache, disk draining can be a reasonable choice\&. .RE .PP \fBdisk\-timeout\fR .RS 4 If the lower\-level device on which a DRBD device stores its data does not finish an I/O request within the defined \fBdisk\-timeout\fR, DRBD treats this as a failure\&. The lower\-level device is detached, and the device\*(Aqs disk state advances to Diskless\&. If DRBD is connected to one or more peers, the failed request is passed on to one of them\&. .sp This option is \fIdangerous and may lead to kernel panic!\fR .sp "Aborting" requests, or force\-detaching the disk, is intended for completely blocked/hung local backing devices which do no longer complete requests at all, not even do error completions\&. In this situation, usually a hard\-reset and failover is the only way out\&. .sp By "aborting", basically faking a local error\-completion, we allow for a more graceful swichover by cleanly migrating services\&. Still the affected node has to be rebooted "soon"\&. .sp By completing these requests, we allow the upper layers to re\-use the associated data pages\&. .sp If later the local backing device "recovers", and now DMAs some data from disk into the original request pages, in the best case it will just put random data into unused pages; but typically it will corrupt meanwhile completely unrelated data, causing all sorts of damage\&. .sp Which means delayed successful completion, especially for READ requests, is a reason to panic()\&. We assume that a delayed *error* completion is OK, though we still will complain noisily about it\&. .sp The default value of \fBdisk\-timeout\fR is 0, which stands for an infinite timeout\&. Timeouts are specified in units of 0\&.1 seconds\&. This option is available since DRBD 8\&.3\&.12\&. .RE .PP \fBmd\-flushes\fR .RS 4 Enable disk flushes and disk barriers on the meta\-data device\&. This option is enabled by default\&. See the \fBdisk\-flushes\fR parameter\&. .RE .PP \fBon\-io\-error \fR\fB\fIhandler\fR\fR .RS 4 Configure how DRBD reacts to I/O errors on a lower\-level device\&. The following policies are defined: .PP \fBpass_on\fR .RS 4 Change the disk status to Inconsistent, mark the failed block as inconsistent in the bitmap, and retry the I/O operation on a remote cluster node\&. .RE .PP \fBcall\-local\-io\-error\fR .RS 4 Call the \fBlocal\-io\-error\fR handler (see the \fBhandlers\fR section)\&. .RE .PP \fBdetach\fR .RS 4 Detach the lower\-level device and continue in diskless mode\&. .RE .sp .RE .PP \fBread\-balancing \fR\fB\fIpolicy\fR\fR .RS 4 Distribute read requests among cluster nodes as defined by \fIpolicy\fR\&. The supported policies are \fBprefer\-local\fR (the default), \fBprefer\-remote\fR, \fBround\-robin\fR, \fBleast\-pending\fR, \fBwhen\-congested\-remote\fR, \fB32K\-striping\fR, \fB64K\-striping\fR, \fB128K\-striping\fR, \fB256K\-striping\fR, \fB512K\-striping\fR and \fB1M\-striping\fR\&. .sp This option is available since DRBD 8\&.4\&.1\&. .RE .PP \fBresync\-after \fR\fB\fIres\-name\fR\fR\fB/\fR\fB\fIvolume\fR\fR .RS 4 Define that a device should only resynchronize after the specified other device\&. By default, no order between devices is defined, and all devices will resynchronize in parallel\&. Depending on the configuration of the lower\-level devices, and the available network and disk bandwidth, this can slow down the overall resync process\&. This option can be used to form a chain or tree of dependencies among devices\&. .RE .PP \fBresync\-rate \fR\fB\fIrate\fR\fR .RS 4 Define how much bandwidth DRBD may use for resynchronizing\&. DRBD allows "normal" application I/O even during a resync\&. If the resync takes up too much bandwidth, application I/O can become very slow\&. This parameter allows to avoid that\&. Please note this is option only works when the dynamic resync controller is disabled\&. .RE .PP \fBrs\-discard\-granularity \fR\fB\fIbyte\fR\fR .RS 4 When \fBrs\-discard\-granularity\fR is set to a non zero, positive value then DRBD tries to do a resync operation in requests of this size\&. In case such a block contains only zero bytes on the sync source node, the sync target node will issue a discard/trim/unmap command for the area\&. .sp The value is constrained by the discard granularity of the backing block device\&. In case \fBrs\-discard\-granularity\fR is not a multiplier of the discard granularity of the backing block device DRBD rounds it up\&. The feature only gets active if the backing block device reads back zeroes after a discard command\&. .sp The default value of is 0\&. This option is available since 8\&.4\&.7\&. .RE .PP \fBdiscard\-zeroes\-if\-aligned \fR\fB{yes | no}\fR .RS 4 There are several aspects to discard/trim/unmap support on linux block devices\&. Even if discard is supported in general, it may fail silently, or may partially ignore discard requests\&. Devices also announce whether reading from unmapped blocks returns defined data (usually zeroes), or undefined data (possibly old data, possibly garbage)\&. .sp If on different nodes, DRBD is backed by devices with differing discard characteristics, discards may lead to data divergence (old data or garbage left over on one backend, zeroes due to unmapped areas on the other backend)\&. Online verify would now potentially report tons of spurious differences\&. While probably harmless for most use cases (fstrim on a file system), DRBD cannot have that\&. .sp To play safe, we have to disable discard support, if our local backend (on a Primary) does not support "discard_zeroes_data=true"\&. We also have to translate discards to explicit zero\-out on the receiving side, unless the receiving side (Secondary) supports "discard_zeroes_data=true", thereby allocating areas what were supposed to be unmapped\&. .sp There are some devices (notably the LVM/DM thin provisioning) that are capable of discard, but announce discard_zeroes_data=false\&. In the case of DM\-thin, discards aligned to the chunk size will be unmapped, and reading from unmapped sectors will return zeroes\&. However, unaligned partial head or tail areas of discard requests will be silently ignored\&. .sp If we now add a helper to explicitly zero\-out these unaligned partial areas, while passing on the discard of the aligned full chunks, we effectively achieve discard_zeroes_data=true on such devices\&. .sp Setting \fBdiscard\-zeroes\-if\-aligned\fR to \fByes\fR will allow DRBD to use discards, and to announce discard_zeroes_data=true, even on backends that announce discard_zeroes_data=false\&. .sp Setting \fBdiscard\-zeroes\-if\-aligned\fR to \fBno\fR will cause DRBD to always fall\-back to zero\-out on the receiving side, and to not even announce discard capabilities on the Primary, if the respective backend announces discard_zeroes_data=false\&. .sp We used to ignore the discard_zeroes_data setting completely\&. To not break established and expected behaviour, and suddenly cause fstrim on thin\-provisioned LVs to run out\-of\-space instead of freeing up space, the default value is \fByes\fR\&. .sp This option is available since 8\&.4\&.7\&. .RE .SS "Section global Parameters" .PP \fBdialog\-refresh \fR\fB\fItime\fR\fR .RS 4 The DRBD init script can be used to configure and start DRBD devices, which can involve waiting for other cluster nodes\&. While waiting, the init script shows the remaining waiting time\&. The \fBdialog\-refresh\fR defines the number of seconds between updates of that countdown\&. The default value is 1; a value of 0 turns off the countdown\&. .RE .PP \fBdisable\-ip\-verification\fR .RS 4 Normally, DRBD verifies that the IP addresses in the configuration match the host names\&. Use the \fBdisable\-ip\-verification\fR parameter to disable these checks\&. .RE .PP \fBusage\-count \fR\fB{yes | no | ask}\fR\fB \fR .RS 4 A explained on DRBD\*(Aqs \m[blue]\fBOnline Usage Counter\fR\m[]\&\s-2\u[2]\d\s+2 web page, DRBD includes a mechanism for anonymously counting how many installations are using which versions of DRBD\&. The results are available on the web page for anyone to see\&. .sp This parameter defines if a cluster node participates in the usage counter; the supported values are \fByes\fR, \fBno\fR, and \fBask\fR (ask the user, the default)\&. .sp We would like to ask users to participate in the online usage counter as this provides us valuable feedback for steering the development of DRBD\&. .RE .SS "Section handlers Parameters" .PP \fBafter\-resync\-target \fR\fB\fIcmd\fR\fR .RS 4 Called on a resync target when a node state changes from \fBInconsistent\fR to \fBConsistent\fR when a resync finishes\&. This handler can be used for removing the snapshot created in the \fBbefore\-resync\-target\fR handler\&. .RE .PP \fBbefore\-resync\-target \fR\fB\fIcmd\fR\fR .RS 4 Called on a resync target before a resync begins\&. This handler can be used for creating a snapshot of the lower\-level device for the duration of the resync: if the resync source becomes unavailable during a resync, reverting to the snapshot can restore a consistent state\&. .RE .PP \fBfence\-peer \fR\fB\fIcmd\fR\fR .RS 4 Called when a node should fence a resource on a particular peer\&. The handler should not use the same communication path that DRBD uses for talking to the peer\&. .RE .PP \fBunfence\-peer \fR\fB\fIcmd\fR\fR .RS 4 Called when a node should remove fencing constraints from other nodes\&. .RE .PP \fBinitial\-split\-brain \fR\fB\fIcmd\fR\fR .RS 4 Called when DRBD connects to a peer and detects that the peer is in a split\-brain state with the local node\&. This handler is also called for split\-brain scenarios which will be resolved automatically\&. .RE .PP \fBlocal\-io\-error \fR\fB\fIcmd\fR\fR .RS 4 Called when an I/O error occurs on a lower\-level device\&. .RE .PP \fBpri\-lost \fR\fB\fIcmd\fR\fR .RS 4 The local node is currently primary, but DRBD believes that it should become a sync target\&. The node should give up its primary role\&. .RE .PP \fBpri\-lost\-after\-sb \fR\fB\fIcmd\fR\fR .RS 4 The local node is currently primary, but it has lost the after\-split\-brain auto recovery procedure\&. The node should be abandoned\&. .RE .PP \fBpri\-on\-incon\-degr \fR\fB\fIcmd\fR\fR .RS 4 The local node is primary, and neither the local lower\-level device nor a lower\-level device on a peer is up to date\&. (The primary has no device to read from or to write to\&.) .RE .PP \fBsplit\-brain \fR\fB\fIcmd\fR\fR .RS 4 DRBD has detected a split\-brain situation which could not be resolved automatically\&. Manual recovery is necessary\&. This handler can be used to call for administrator attention\&. .RE .SS "Section net Parameters" .PP \fBafter\-sb\-0pri \fR\fB\fIpolicy\fR\fR .RS 4 Define how to react if a split\-brain scenario is detected and none of the two nodes is in primary role\&. (We detect split\-brain scenarios when two nodes connect; split\-brain decisions are always between two nodes\&.) The defined policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization; simply disconnect\&. .RE .PP \fBdiscard\-younger\-primary\fR, .br \fBdiscard\-older\-primary\fR .RS 4 Resynchronize from the node which became primary first (\fBdiscard\-younger\-primary\fR) or last (\fBdiscard\-older\-primary\fR)\&. If both nodes became primary independently, the \fBdiscard\-least\-changes\fR policy is used\&. .RE .PP \fBdiscard\-zero\-changes\fR .RS 4 If only one of the nodes wrote data since the split brain situation was detected, resynchronize from this node to the other\&. If both nodes wrote data, disconnect\&. .RE .PP \fBdiscard\-least\-changes\fR .RS 4 Resynchronize from the node with more modified blocks\&. .RE .PP \fBdiscard\-node\-\fR\fB\fInodename\fR\fR .RS 4 Always resynchronize to the named node\&. .RE .RE .PP \fBafter\-sb\-1pri \fR\fB\fIpolicy\fR\fR .RS 4 Define how to react if a split\-brain scenario is detected, with one node in primary role and one node in secondary role\&. (We detect split\-brain scenarios when two nodes connect, so split\-brain decisions are always among two nodes\&.) The defined policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBconsensus\fR .RS 4 Discard the data on the secondary node if the \fBafter\-sb\-0pri\fR algorithm would also discard the data on the secondary node\&. Otherwise, disconnect\&. .RE .PP \fBviolently\-as0p\fR .RS 4 Always take the decision of the \fBafter\-sb\-0pri\fR algorithm, even if it causes an erratic change of the primary\*(Aqs view of the data\&. This is only useful if a single\-node file system (i\&.e\&., not OCFS2 or GFS) with the \fBallow\-two\-primaries\fR flag is used\&. This option can cause the primary node to crash, and should not be used\&. .RE .PP \fBdiscard\-secondary\fR .RS 4 Discard the data on the secondary node\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Always take the decision of the \fBafter\-sb\-0pri\fR algorithm\&. If the decision is to discard the data on the primary node, call the \fBpri\-lost\-after\-sb\fR handler on the primary node\&. .RE .RE .PP \fBafter\-sb\-2pri \fR\fB\fIpolicy\fR\fR .RS 4 Define how to react if a split\-brain scenario is detected and both nodes are in primary role\&. (We detect split\-brain scenarios when two nodes connect, so split\-brain decisions are always among two nodes\&.) The defined policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBviolently\-as0p\fR .RS 4 See the \fBviolently\-as0p\fR policy for \fBafter\-sb\-1pri\fR\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Call the \fBpri\-lost\-after\-sb\fR helper program on one of the machines unless that machine can demote to secondary\&. The helper program is expected to reboot the machine, which brings the node into a secondary role\&. Which machine runs the helper program is determined by the \fBafter\-sb\-0pri\fR strategy\&. .RE .RE .PP \fBallow\-two\-primaries\fR .RS 4 The most common way to configure DRBD devices is to allow only one node to be primary (and thus writable) at a time\&. .sp In some scenarios it is preferable to allow two nodes to be primary at once; a mechanism outside of DRBD then must make sure that writes to the shared, replicated device happen in a coordinated way\&. This can be done with a shared\-storage cluster file system like OCFS2 and GFS, or with virtual machine images and a virtual machine manager that can migrate virtual machines between physical machines\&. .sp The \fBallow\-two\-primaries\fR parameter tells DRBD to allow two nodes to be primary at the same time\&. Never enable this option when using a non\-distributed file system; otherwise, data corruption and node crashes will result! .RE .PP \fBalways\-asbp\fR .RS 4 Normally the automatic after\-split\-brain policies are only used if current states of the UUIDs do not indicate the presence of a third node\&. .sp With this option you request that the automatic after\-split\-brain policies are used as long as the data sets of the nodes are somehow related\&. This might cause a full sync, if the UUIDs indicate the presence of a third node\&. (Or double faults led to strange UUID sets\&.) .RE .PP \fBconnect\-int \fR\fB\fItime\fR\fR .RS 4 As soon as a connection between two nodes is configured with \fBdrbdsetup connect\fR, DRBD immediately tries to establish the connection\&. If this fails, DRBD waits for \fBconnect\-int\fR seconds and then repeats\&. The default value of \fBconnect\-int\fR is 10 seconds\&. .RE .PP \fBcram\-hmac\-alg \fR\fB\fIhash\-algorithm\fR\fR .RS 4 Configure the hash\-based message authentication code (HMAC) or secure hash algorithm to use for peer authentication\&. The kernel supports a number of different algorithms, some of which may be loadable as kernel modules\&. See the shash algorithms listed in /proc/crypto\&. By default, \fBcram\-hmac\-alg\fR is unset\&. Peer authentication also requires a \fBshared\-secret\fR to be configured\&. .RE .PP \fBcsums\-alg \fR\fB\fIhash\-algorithm\fR\fR .RS 4 Normally, when two nodes resynchronize, the sync target requests a piece of out\-of\-sync data from the sync source, and the sync source sends the data\&. With many usage patterns, a significant number of those blocks will actually be identical\&. .sp When a \fBcsums\-alg\fR algorithm is specified, when requesting a piece of out\-of\-sync data, the sync target also sends along a hash of the data it currently has\&. The sync source compares this hash with its own version of the data\&. It sends the sync target the new data if the hashes differ, and tells it that the data are the same otherwise\&. This reduces the network bandwidth required, at the cost of higher cpu utilization and possibly increased I/O on the sync target\&. .sp The \fBcsums\-alg\fR can be set to one of the secure hash algorithms supported by the kernel; see the shash algorithms listed in /proc/crypto\&. By default, \fBcsums\-alg\fR is unset\&. .RE .PP \fBcsums\-after\-crash\-only\fR .RS 4 Enabling this option (and csums\-alg, above) makes it possible to use the checksum based resync only for the first resync after primary crash, but not for later "network hickups"\&. .sp In most cases, block that are marked as need\-to\-be\-resynced are in fact changed, so calculating checksums, and both reading and writing the blocks on the resync target is all effective overhead\&. .sp The advantage of checksum based resync is mostly after primary crash recovery, where the recovery marked larger areas (those covered by the activity log) as need\-to\-be\-resynced, just in case\&. Introduced in 8\&.4\&.5\&. .RE .PP \fBdata\-integrity\-alg \fR \fIalg\fR .RS 4 DRBD normally relies on the data integrity checks built into the TCP/IP protocol, but if a data integrity algorithm is configured, it will additionally use this algorithm to make sure that the data received over the network match what the sender has sent\&. If a data integrity error is detected, DRBD will close the network connection and reconnect, which will trigger a resync\&. .sp The \fBdata\-integrity\-alg\fR can be set to one of the secure hash algorithms supported by the kernel; see the shash algorithms listed in /proc/crypto\&. By default, this mechanism is turned off\&. .sp Because of the CPU overhead involved, we recommend not to use this option in production environments\&. Also see the notes on data integrity below\&. .RE .PP \fBfencing \fR\fB\fIfencing_policy\fR\fR .RS 4 \fBFencing\fR is a preventive measure to avoid situations where both nodes are primary and disconnected\&. This is also known as a split\-brain situation\&. DRBD supports the following fencing policies: .PP \fBdont\-care\fR .RS 4 No fencing actions are taken\&. This is the default policy\&. .RE .PP \fBresource\-only\fR .RS 4 If a node becomes a disconnected primary, it tries to fence the peer\&. This is done by calling the \fBfence\-peer\fR handler\&. The handler is supposed to reach the peer over an alternative communication path and call \*(Aq\fBdrbdadm outdate minor\fR\*(Aq there\&. .RE .PP \fBresource\-and\-stonith\fR .RS 4 If a node becomes a disconnected primary, it freezes all its IO operations and calls its fence\-peer handler\&. The fence\-peer handler is supposed to reach the peer over an alternative communication path and call \*(Aq\fBdrbdadm outdate minor\fR\*(Aq there\&. In case it cannot do that, it should stonith the peer\&. IO is resumed as soon as the situation is resolved\&. In case the fence\-peer handler fails, I/O can be resumed manually with \*(Aq\fBdrbdadm resume\-io\fR\*(Aq\&. .RE .RE .PP \fBko\-count \fR\fB\fInumber\fR\fR .RS 4 If a secondary node fails to complete a write request in \fBko\-count\fR times the \fBtimeout\fR parameter, it is excluded from the cluster\&. The primary node then sets the connection to this secondary node to Standalone\&. To disable this feature, you should explicitly set it to 0; defaults may change between versions\&. .RE .PP \fBmax\-buffers \fR\fB\fInumber\fR\fR .RS 4 Limits the memory usage per DRBD minor device on the receiving side, or for internal buffers during resync or online\-verify\&. Unit is PAGE_SIZE, which is 4 KiB on most systems\&. The minimum possible setting is hard coded to 32 (=128 KiB)\&. These buffers are used to hold data blocks while they are written to/read from disk\&. To avoid possible distributed deadlocks on congestion, this setting is used as a throttle threshold rather than a hard limit\&. Once more than max\-buffers pages are in use, further allocation from this pool is throttled\&. You want to increase max\-buffers if you cannot saturate the IO backend on the receiving side\&. .RE .PP \fBmax\-epoch\-size \fR\fB\fInumber\fR\fR .RS 4 Define the maximum number of write requests DRBD may issue before issuing a write barrier\&. The default value is 2048, with a minimum of 1 and a maximum of 20000\&. Setting this parameter to a value below 10 is likely to decrease performance\&. .RE .PP \fBon\-congestion \fR\fB\fIpolicy\fR\fR, .br \fBcongestion\-fill \fR\fB\fIthreshold\fR\fR, .br \fBcongestion\-extents \fR\fB\fIthreshold\fR\fR .RS 4 By default, DRBD blocks when the TCP send queue is full\&. This prevents applications from generating further write requests until more buffer space becomes available again\&. .sp When DRBD is used together with DRBD\-proxy, it can be better to use the \fBpull\-ahead\fR \fBon\-congestion\fR policy, which can switch DRBD into ahead/behind mode before the send queue is full\&. DRBD then records the differences between itself and the peer in its bitmap, but it no longer replicates them to the peer\&. When enough buffer space becomes available again, the node resynchronizes with the peer and switches back to normal replication\&. .sp This has the advantage of not blocking application I/O even when the queues fill up, and the disadvantage that peer nodes can fall behind much further\&. Also, while resynchronizing, peer nodes will become inconsistent\&. .sp The available congestion policies are \fBblock\fR (the default) and \fBpull\-ahead\fR\&. The \fBcongestion\-fill\fR parameter defines how much data is allowed to be "in flight" in this connection\&. The default value is 0, which disables this mechanism of congestion control, with a maximum of 10 GiBytes\&. The \fBcongestion\-extents\fR parameter defines how many bitmap extents may be active before switching into ahead/behind mode, with the same default and limits as the \fBal\-extents\fR parameter\&. The \fBcongestion\-extents\fR parameter is effective only when set to a value smaller than \fBal\-extents\fR\&. .sp Ahead/behind mode is available since DRBD 8\&.3\&.10\&. .RE .PP \fBping\-int \fR\fB\fIinterval\fR\fR .RS 4 When the TCP/IP connection to a peer is idle for more than \fBping\-int\fR seconds, DRBD will send a keep\-alive packet to make sure that a failed peer or network connection is detected reasonably soon\&. The default value is 10 seconds, with a minimum of 1 and a maximum of 120 seconds\&. The unit is seconds\&. .RE .PP \fBping\-timeout \fR\fB\fItimeout\fR\fR .RS 4 Define the timeout for replies to keep\-alive packets\&. If the peer does not reply within \fBping\-timeout\fR, DRBD will close and try to reestablish the connection\&. The default value is 0\&.5 seconds, with a minimum of 0\&.1 seconds and a maximum of 3 seconds\&. The unit is tenths of a second\&. .RE .PP \fBsocket\-check\-timeout \fR\fB\fItimeout\fR\fR .RS 4 In setups involving a DRBD\-proxy and connections that experience a lot of buffer\-bloat it might be necessary to set \fBping\-timeout\fR to an unusual high value\&. By default DRBD uses the same value to wait if a newly established TCP\-connection is stable\&. Since the DRBD\-proxy is usually located in the same data center such a long wait time may hinder DRBD\*(Aqs connect process\&. .sp In such setups \fBsocket\-check\-timeout\fR should be set to at least to the round trip time between DRBD and DRBD\-proxy\&. I\&.e\&. in most cases to 1\&. .sp The default unit is tenths of a second, the default value is 0 (which causes DRBD to use the value of \fBping\-timeout\fR instead)\&. Introduced in 8\&.4\&.5\&. .RE .PP \fBprotocol \fR\fB\fIname\fR\fR .RS 4 Use the specified protocol on this connection\&. The supported protocols are: .PP \fBA\fR .RS 4 Writes to the DRBD device complete as soon as they have reached the local disk and the TCP/IP send buffer\&. .RE .PP \fBB\fR .RS 4 Writes to the DRBD device complete as soon as they have reached the local disk, and all peers have acknowledged the receipt of the write requests\&. .RE .PP \fBC\fR .RS 4 Writes to the DRBD device complete as soon as they have reached the local and all remote disks\&. .RE .sp .RE .PP \fBrcvbuf\-size \fR\fB\fIsize\fR\fR .RS 4 Configure the size of the TCP/IP receive buffer\&. A value of 0 (the default) causes the buffer size to adjust dynamically\&. This parameter usually does not need to be set, but it can be set to a value up to 10 MiB\&. The default unit is bytes\&. .RE .PP \fBrr\-conflict\fR \fIpolicy\fR .RS 4 This option helps to solve the cases when the outcome of the resync decision is incompatible with the current role assignment in the cluster\&. The defined policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBviolently\fR .RS 4 Resync to the primary node is allowed, violating the assumption that data on a block device are stable for one of the nodes\&. \fIDo not use this option, it is dangerous\&.\fR .RE .PP \fBcall\-pri\-lost\fR .RS 4 Call the \fBpri\-lost\fR handler on one of the machines\&. The handler is expected to reboot the machine, which puts it into secondary role\&. .RE .RE .PP \fBshared\-secret \fR\fB\fIsecret\fR\fR .RS 4 Configure the shared secret used for peer authentication\&. The secret is a string of up to 64 characters\&. Peer authentication also requires the \fBcram\-hmac\-alg\fR parameter to be set\&. .RE .PP \fBsndbuf\-size \fR\fB\fIsize\fR\fR .RS 4 Configure the size of the TCP/IP send buffer\&. Since DRBD 8\&.0\&.13 / 8\&.2\&.7, a value of 0 (the default) causes the buffer size to adjust dynamically\&. Values below 32 KiB are harmful to the throughput on this connection\&. Large buffer sizes can be useful especially when protocol A is used over high\-latency networks; the maximum value supported is 10 MiB\&. .RE .PP \fBtcp\-cork\fR .RS 4 By default, DRBD uses the TCP_CORK socket option to prevent the kernel from sending partial messages; this results in fewer and bigger packets on the network\&. Some network stacks can perform worse with this optimization\&. On these, the \fBtcp\-cork\fR parameter can be used to turn this optimization off\&. .RE .PP \fBtimeout \fR\fB\fItime\fR\fR .RS 4 Define the timeout for replies over the network: if a peer node does not send an expected reply within the specified \fBtimeout\fR, it is considered dead and the TCP/IP connection is closed\&. The timeout value must be lower than \fBconnect\-int\fR and lower than \fBping\-int\fR\&. The default is 6 seconds; the value is specified in tenths of a second\&. .RE .PP \fBuse\-rle\fR .RS 4 Each replicated device on a cluster node has a separate bitmap for each of its peer devices\&. The bitmaps are used for tracking the differences between the local and peer device: depending on the cluster state, a disk range can be marked as different from the peer in the device\*(Aqs bitmap, in the peer device\*(Aqs bitmap, or in both bitmaps\&. When two cluster nodes connect, they exchange each other\*(Aqs bitmaps, and they each compute the union of the local and peer bitmap to determine the overall differences\&. .sp Bitmaps of very large devices are also relatively large, but they usually compress very well using run\-length encoding\&. This can save time and bandwidth for the bitmap transfers\&. .sp The \fBuse\-rle\fR parameter determines if run\-length encoding should be used\&. It is on by default since DRBD 8\&.4\&.0\&. .RE .PP \fBverify\-alg \fR\fB\fIhash\-algorithm\fR\fR .RS 4 Online verification (\fBdrbdadm verify\fR) computes and compares checksums of disk blocks (i\&.e\&., hash values) in order to detect if they differ\&. The \fBverify\-alg\fR parameter determines which algorithm to use for these checksums\&. It must be set to one of the secure hash algorithms supported by the kernel before online verify can be used; see the shash algorithms listed in /proc/crypto\&. .sp We recommend to schedule online verifications regularly during low\-load periods, for example once a month\&. Also see the notes on data integrity below\&. .RE .SS "Section on Parameters" .PP \fBaddress \fR\fB\fI[address\-family]\fR\fR\fB \fR\fB\fIaddress\fR\fR\fB:\fR\fB\fIport\fR\fR .RS 4 Defines the address family, address, and port of a connection endpoint\&. .sp The address families \fBipv4\fR, \fBipv6\fR, \fBssocks\fR (Dolphin Interconnect Solutions\*(Aq "super sockets"), \fBsdp\fR (Infiniband Sockets Direct Protocol), and \fBsci\fR are supported (\fBsci\fR is an alias for \fBssocks\fR)\&. If no address family is specified, \fBipv4\fR is assumed\&. For all address families except \fBipv6\fR, the address is specified in IPV4 address notation (for example, 1\&.2\&.3\&.4)\&. For \fBipv6\fR, the address is enclosed in brackets and uses IPv6 address notation (for example, [fd01:2345:6789:abcd::1])\&. The port is always specified as a decimal number from 1 to 65535\&. .sp On each host, the port numbers must be unique for each address; ports cannot be shared\&. .RE .PP \fBnode\-id \fR\fB\fIvalue\fR\fR .RS 4 Defines the unique node identifier for a node in the cluster\&. Node identifiers are used to identify individual nodes in the network protocol, and to assign bitmap slots to nodes in the metadata\&. .sp Node identifiers can only be reasssigned in a cluster when the cluster is down\&. It is essential that the node identifiers in the configuration and in the device metadata are changed consistently on all hosts\&. To change the metadata, dump the current state with \fBdrbdmeta dump\-md\fR, adjust the bitmap slot assignment, and update the metadata with \fBdrbdmeta restore\-md\fR\&. .sp The \fBnode\-id\fR parameter exists since DRBD 9\&. Its value ranges from 0 to 16; there is no default\&. .RE .SS "Section options Parameters (Resource Options)" .PP \fBauto\-promote \fR\fB\fIbool\-value\fR\fR .RS 4 A resource must be promoted to primary role before any of its devices can be mounted or opened for writing\&. .sp Before DRBD 9, this could only be done explicitly ("drbdadm primary")\&. Since DRBD 9, the \fBauto\-promote\fR parameter allows to automatically promote a resource to primary role when one of its devices is mounted or opened for writing\&. As soon as all devices are unmounted or closed with no more remaining users, the role of the resource changes back to secondary\&. .sp Automatic promotion only succeeds if the cluster state allows it (that is, if an explicit \fBdrbdadm primary\fR command would succeed)\&. Otherwise, mounting or opening the device fails as it already did before DRBD 9: the \fBmount\fR(2) system call fails with errno set to EROFS (Read\-only file system); the \fBopen\fR(2) system call fails with errno set to EMEDIUMTYPE (wrong medium type)\&. .sp Irrespective of the \fBauto\-promote\fR parameter, if a device is promoted explicitly (\fBdrbdadm primary\fR), it also needs to be demoted explicitly (\fBdrbdadm secondary\fR)\&. .sp The \fBauto\-promote\fR parameter is available since DRBD 9\&.0\&.0, and defaults to \fByes\fR\&. .RE .PP \fBcpu\-mask \fR\fB\fIcpu\-mask\fR\fR .RS 4 Set the cpu affinity mask for DRBD kernel threads\&. The cpu mask is specified as a hexadecimal number\&. The default value is 0, which lets the scheduler decide which kernel threads run on which CPUs\&. CPU numbers in \fBcpu\-mask\fR which do not exist in the system are ignored\&. .RE .PP \fBon\-no\-data\-accessible \fR\fB\fIpolicy\fR\fR .RS 4 Determine how to deal with I/O requests when the requested data is not available locally or remotely (for example, when all disks have failed)\&. The defined policies are: .PP \fBio\-error\fR .RS 4 System calls fail with errno set to EIO\&. .RE .PP \fBsuspend\-io\fR .RS 4 The resource suspends I/O\&. I/O can be resumed by (re)attaching the lower\-level device, by connecting to a peer which has access to the data, or by forcing DRBD to resume I/O with \fBdrbdadm resume\-io \fR\fB\fIres\fR\fR\&. When no data is available, forcing I/O to resume will result in the same behavior as the \fBio\-error\fR policy\&. .RE .sp This setting is available since DRBD 8\&.3\&.9; the default policy is \fBio\-error\fR\&. .RE .PP \fBpeer\-ack\-window \fR\fB\fIvalue\fR\fR .RS 4 On each node and for each device, DRBD maintains a bitmap of the differences between the local and remote data for each peer device\&. For example, in a three\-node setup (nodes A, B, C) each with a single device, every node maintains one bitmap for each of its peers\&. .sp When nodes receive write requests, they know how to update the bitmaps for the writing node, but not how to update the bitmaps between themselves\&. In this example, when a write request propagates from node A to B and C, nodes B and C know that they have the same data as node A, but not whether or not they both have the same data\&. .sp As a remedy, the writing node occasionally sends peer\-ack packets to its peers which tell them which state they are in relative to each other\&. .sp The \fBpeer\-ack\-window\fR parameter specifies how much data a primary node may send before sending a peer\-ack packet\&. A low value causes increased network traffic; a high value causes less network traffic but higher memory consumption on secondary nodes and higher resync times between the secondary nodes after primary node failures\&. (Note: peer\-ack packets may be sent due to other reasons as well, e\&.g\&. membership changes or expiry of the \fBpeer\-ack\-delay\fR timer\&.) .sp The default value for \fBpeer\-ack\-window\fR is 2 MiB, the default unit is sectors\&. This option is available since 9\&.0\&.0\&. .RE .PP \fBpeer\-ack\-delay \fR\fB\fIexpiry\-time\fR\fR .RS 4 If after the last finished write request no new write request gets issued for \fIexpiry\-time\fR, then a peer\-ack packet is sent\&. If a new write request is issued before the timer expires, the timer gets reset to \fIexpiry\-time\fR\&. (Note: peer\-ack packets may be sent due to other reasons as well, e\&.g\&. membership changes or the \fBpeer\-ack\-window\fR option\&.) .sp This parameter may influence resync behavior on remote nodes\&. Peer nodes need to wait until they receive an peer\-ack for releasing a lock on an AL\-extent\&. Resync operations between peers may need to wait for for these locks\&. .sp The default value for \fBpeer\-ack\-delay\fR is 100 milliseconds, the default unit is milliseconds\&. This option is available since 9\&.0\&.0\&. .RE .SS "Section startup Parameters" .PP The parameters in this section define the behavior of DRBD at system startup time, in the DRBD init script\&. They have no effect once the system is up and running\&. .PP \fBdegr\-wfc\-timeout \fR\fB\fItimeout\fR\fR .RS 4 Define how long to wait until all peers are connected in case the cluster consisted of a single node only when the system went down\&. This parameter is usually set to a value smaller than \fBwfc\-timeout\fR\&. The assumption here is that peers which were unreachable before a reboot are less likely to be be reachable after the reboot, so waiting is less likely to help\&. .sp The timeout is specified in seconds\&. The default value is 0, which stands for an infinite timeout\&. Also see the \fBwfc\-timeout\fR parameter\&. .RE .PP \fBoutdated\-wfc\-timeout \fR\fB\fItimeout\fR\fR .RS 4 Define how long to wait until all peers are connected if all peers were outdated when the system went down\&. This parameter is usually set to a value smaller than \fBwfc\-timeout\fR\&. The assumption here is that an outdated peer cannot have become primary in the meantime, so we don\*(Aqt need to wait for it as long as for a node which was alive before\&. .sp The timeout is specified in seconds\&. The default value is 0, which stands for an infinite timeout\&. Also see the \fBwfc\-timeout\fR parameter\&. .RE .PP \fBstacked\-timeouts\fR .RS 4 On stacked devices, the \fBwfc\-timeout\fR and \fBdegr\-wfc\-timeout\fR parameters in the configuration are usually ignored, and both timeouts are set to twice the \fBconnect\-int\fR timeout\&. The \fBstacked\-timeouts\fR parameter tells DRBD to use the \fBwfc\-timeout\fR and \fBdegr\-wfc\-timeout\fR parameters as defined in the configuration, even on stacked devices\&. Only use this parameter if the peer of the stacked resource is usually not available, or will not become primary\&. Incorrect use of this parameter can lead to unexpected split\-brain scenarios\&. .RE .PP \fBwait\-after\-sb\fR .RS 4 This parameter causes DRBD to continue waiting in the init script even when a split\-brain situation has been detected, and the nodes therefore refuse to connect to each other\&. .RE .PP \fBwfc\-timeout \fR\fB\fItimeout\fR\fR .RS 4 Define how long the init script waits until all peers are connected\&. This can be useful in combination with a cluster manager which cannot manage DRBD resources: when the cluster manager starts, the DRBD resources will already be up and running\&. With a more capable cluster manager such as Pacemaker, it makes more sense to let the cluster manager control DRBD resources\&. The timeout is specified in seconds\&. The default value is 0, which stands for an infinite timeout\&. Also see the \fBdegr\-wfc\-timeout\fR parameter\&. .RE .SS "Section volume Parameters" .PP \fBdevice /dev/drbd\fR\fB\fIminor\-number\fR\fR .RS 4 Define the device name and minor number of a replicated block device\&. This is the device that applications are supposed to access; in most cases, the device is not used directly, but as a file system\&. This parameter is required and the standard device naming convention is assumed\&. .sp In addition to this device, udev will create \fB/dev/drbd/by\-res/\fR\fB\fIresource\fR\fR\fB/\fR\fB\fIvolume\fR\fR and \fB/dev/drbd/by\-disk/\fR\fB\fIlower\-level\-device\fR\fR symlinks to the device\&. .RE .PP \fBdisk\fR {[disk] | \fBnone\fR} .RS 4 Define the lower\-level block device that DRBD will use for storing the actual data\&. While the replicated drbd device is configured, the lower\-level device must not be used directly\&. Even read\-only access with tools like \fBdumpe2fs\fR(8) and similar is not allowed\&. The keyword \fBnone\fR specifies that no lower\-level block device is configured; this also overrides inheritance of the lower\-level device\&. .RE .PP \fBmeta\-disk internal\fR, .br \fBmeta\-disk \fR\fB\fIdevice\fR\fR, .br \fBmeta\-disk \fR\fB\fIdevice\fR\fR\fB [\fR\fB\fIindex\fR\fR\fB]\fR .RS 4 Define where the metadata of a replicated block device resides: it can be \fBinternal\fR, meaning that the lower\-level device contains both the data and the metadata, or on a separate device\&. .sp When the \fIindex\fR form of this parameter is used, multiple replicated devices can share the same metadata device, each using a separate index\&. Each index occupies 128 MiB of data, which corresponds to a replicated device size of at most 4 TiB with two cluster nodes\&. We recommend not to share metadata devices anymore, and to instead use the lvm volume manager for creating metadata devices as needed\&. .sp When the \fIindex\fR form of this parameter is not used, the size of the lower\-level device determines the size of the metadata\&. The size needed is 36 KiB + (size of lower\-level device) / 32K * (number of nodes \- 1)\&. If the metadata device is bigger than that, the extra space is not used\&. .sp This parameter is required if a \fBdisk\fR other than \fBnone\fR is specified, and ignored if \fBdisk\fR is set to \fBnone\fR\&. A \fBmeta\-disk\fR parameter without a \fBdisk\fR parameter is not allowed\&. .RE .SH "NOTES ON DATA INTEGRITY" .PP DRBD supports two different mechanisms for data integrity checking: first, the \fBdata\-integrity\-alg\fR network parameter allows to add a checksum to the data sent over the network\&. Second, the online verification mechanism (\fBdrbdadm verify\fR and the \fBverify\-alg\fR parameter) allows to check for differences in the on\-disk data\&. .PP Both mechanisms can produce false positives if the data is modified during I/O (i\&.e\&., while it is being sent over the network or written to disk)\&. This does not always indicate a problem: for example, some file systems and applications do modify data under I/O for certain operations\&. Swap space can also undergo changes while under I/O\&. .PP Network data integrity checking tries to identify data modification during I/O by verifying the checksums on the sender side after sending the data\&. If it detects a mismatch, it logs an error\&. The receiver also logs an error when it detects a mismatch\&. Thus, an error logged only on the receiver side indicates an error on the network, and an error logged on both sides indicates data modification under I/O\&. .PP The most recent example of systematic data corruption was identified as a bug in the TCP offloading engine and driver of a certain type of GBit NIC in 2007: the data corruption happened on the DMA transfer from core memory to the card\&. Because the TCP checksum were calculated on the card, the TCP/IP protocol checksums did not reveal this problem\&. .SH "VERSION" .sp This document was revised for version 9\&.0\&.0 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2012 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd\fR(8), \fBdrbddisk\fR(8), \fBdrbdsetup\fR(8), \fBdrbdadm\fR(8), \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2, \m[blue]\fBDRBD Web Site\fR\m[]\&\s-2\u[3]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD User's Guide .RS 4 \%http://www.drbd.org/users-guide/ .RE .IP " 2." 4 Online Usage Counter .RS 4 \%http://usage.drbd.org .RE .IP " 3." 4 DRBD Web Site .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v9/xml-usage-to-docbook.xsl0000644000175000017500000000175612466702073023475 0ustar apoikosapoikos drbdsetup drbd-utils-8.9.10/documentation/v9/drbd.80000644000175000017500000000526513027211670020000 0ustar apoikosapoikos'\" t .\" Title: drbd .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 24 June 2014 .\" Manual: System Administration .\" Source: DRBD 9.0.0 .\" Language: English .\" .TH "DRBD" "8" "24 June 2014" "DRBD 9.0.0" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbd \- The start and stop script for DRBD .SH "SYNOPSIS" .HP \w'\fB/etc/init\&.d/drbd\fR\ 'u \fB/etc/init\&.d/drbd\fR {start | stop | status | reload | restart | force\-reload} .SH "INTRODUCTION" .PP The \fB/etc/init\&.d/drbd\fR script is used to start and stop drbd on a system V style init system\&. .PP When using a cluster resource manger such as Pacemaker, DRBD should usually \fInot\fR be started by the init system, but should typically be exclusively controlled by the cluster manager\&. You should not use, and disable, the init script in this case\&. \fBchmod \-x /etc/init\&.d/drbd\fR has proven most effective for this\&. .PP In order to use \fB/etc/init\&.d/drbd\fR, define a drbd configuration\&. See \fBdrbd.conf\fR(5) for details\&. .SH "VERSION" .sp This document was revised for version 9\&.0\&.0 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2014 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbdsetup\fR(8), \fBdrbdadm\fR(8), \m[blue]\fBDRBD Homepage\fR\m[]\&\s-2\u[1]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD Homepage .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v9/drbd-overview.80000644000175000017500000000477113027211672021647 0ustar apoikosapoikos'\" t .\" Title: drbd-overview .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 24 June 2014 .\" Manual: System Administration .\" Source: DRBD 9.0.0 .\" Language: English .\" .TH "DRBD\-OVERVIEW" "8" "24 June 2014" "DRBD 9.0.0" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbd-overview \- Overview of all configured DRBD resources .SH "SYNOPSIS" .HP \w'\fBdrbd\-overview\fR\ 'u \fBdrbd\-overview\fR .SH "DESCRIPTION" .PP drbd\-overview offers an overview of the state of all configured DRBD resources\&. It provides information about the connection status of each DRBD resource\&. It will also use auxiliary information provided by \fBdf\fR(1), \fBlvm\fR(8), \fBxm\fR(1) and \fBvirsh\fR(1) for DRBD resources used as mounted filesystems, LVM physical volumes, Xen domain backing disks and libvirt instance disks respectively\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. Initial man\-page contributed by Apollon Oikonomopoulos \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2008\-2014 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbdsetup\fR(8), \fBdrbdadm\fR(8), \m[blue]\fBDRBD Homepage\fR\m[]\&\s-2\u[1]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD Homepage .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v9/drbdmon.xml0000644000175000017500000001113613012630471021133 0ustar apoikosapoikos 9 November 2016 DRBD 9.0.0 drbdmon 8 System Administration drbdmon Monitor DRBD resources realtime drbdmon drbdmon options --backend-options command context Description The utility is used for monitoring volumes and connections realtime, as defined in DRBD resource files. See drbd.conf 5 for more information. Options , Show which commands would execute instead of actually executing them (for example, drbdmon -d up resource). This can be a useful way to learn how drbdsetup and drbdmeta are used. Use only ascii characters (no Unicode). Do not display the drbdmon header line Do not display the hotkeys line. Commands c device Clear screen. r Repaint display. q Quit. Version This document was revised for version 9.0.0 of the DRBD distribution. Author Written by Robert Altnoeder robert.altnoeder@linbit.com and Sam Leonard sam@linbit.com Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2016 LINBIT Information Technologies, Robert Altnoeder, Sam Leonard. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf 5 , drbd 8 , drbddisk 8 , drbdsetup 8 , drbdmeta 8 and the DRBD project web site drbd-utils-8.9.10/documentation/v9/drbd.conf.xml.in0000644000175000017500000012425213002133622021751 0ustar apoikosapoikos ]> &drbdsetup_options; 3 December 2012 DRBD 9.0.0 drbd.conf 5 Configuration Files drbd.conf DRBD Configuration Files drbd.conf Introduction DRBD implements block devices which replicate their data to all nodes of a cluster. The actual data and associated metadata are usually stored redundantly on "ordinary" block devices on each cluster node. Replicated block devices are called by default. They are grouped into resources, with one or more devices per resource. Replication among the devices in a resource takes place in chronological order. With DRBD, we refer to the devices inside a resource as volumes. In DRBD 9, a resource can be replicated between two or more cluster nodes. The connections between cluster nodes are point-to-point links, and use TCP or a TCP-like protocol. All nodes must be directly connected. DRBD consists of low-level user-space components which interact with the kernel and perform basic operations (, ), a high-level user-space component which understands and processes the DRBD configuration and translates it into basic operations of the low-level components (), and a kernel component. The default DRBD configuration consists of and of additional files included from there, usually and all files inside . It has turned out to be useful to define each resource in a separate file. The configuration files are designed so that each cluster node can contain an identical copy of the entire cluster configuration. The host name of each node determines which parts of the configuration apply (). It is highly recommended to keep the cluster configuration on all nodes in sync by manually copying it to all nodes, or by automating the process with or a similar tool. Example Configuration File resource r0 { net { cram-hmac-alg sha1; shared-secret "FooFunFactory"; } volume 0 { device /dev/drbd1; disk /dev/sda7; meta-disk internal; } on alice { node-id 0; address 10.1.1.31:7000; } on bob { node-id 1; address 10.1.1.32:7000; } connection { host alice port 7000; host bob port 7000; net { protocol C; } } } This example defines a resource which contains a single replicated device with volume number 0. The resource is replicated among hosts and , which have the IPv4 addresses and and the node identifiers 0 and 1, respectively. On both hosts, the replicated device is called , and the actual data and metadata are stored on the lower-level device . The connection between the hosts uses protocol C. Please refer to the DRBD User's Guide for more examples. File Format DRBD configuration files consist of sections, which contain other sections and parameters depending on the section types. Each section consists of one or more keywords, sometimes a section name, an opening brace ({), the section's contents, and a closing brace (}). Parameters inside a section consist of a keyword, followed by one or more keywords or values, and a semicolon (;). Some parameter values have a default scale which applies when a plain number is specified (for example Kilo, or 1024 times the numeric value). Such default scales can be overridden by using a suffix (for example, for Mega). The common suffixes = 2^10 = 1024, = 1024 K, and = 1024 M are supported. Comments start with a hash sign (#) and extend to the end of the line. In addition, any section can be prefixed with the keyword , which causes the section and any sub-sections to be ignored. Additional files can be included with the statement (see glob7 for the expressions supported in file-pattern). Include statements are only allowed outside of sections. The following sections are defined (indentation indicates in which context): common [disk] [handlers] [net] [options] [startup] global resource connection path net connection-mesh net [disk] floating handlers [net] on volume disk [disk] options stacked-on-top-of startup Sections in brackets affect other parts of the configuration: inside the section, they apply to all resources. A section inside a or section applies to all volumes of that resource, and a section inside a section applies to all connections of that resource. This allows to avoid repeating identical options for each resource, connection, or volume. Options can be overridden in a more specific , , , or section. Sections drbd.conf common This section can contain each a , , , , and section. All resources inherit the parameters in these sections as their default values. drbd.conf connection Define a connection between two hosts. This section must contain two parameters or multiple . The optional name is used to refer to the connection in the system log and in other messages. If no name is specified, the peer's host name is used instead. drbd.conf path Define a path between two hosts. This section must contain two parameters. drbd.conf connection-mesh Define a connection mesh between multiple hosts. This section must contain a parameter, which has the host names as arguments. This section is a shortcut to define many connections which share the same network options. drbd.conf disk Define parameters for a volume. All parameters in this section are optional. drbd.conf floating Like the section, except that instead of the host name a network address is used to determine if it matches a section. The parameter in this section is required. If the parameter is not provided, no connections to peers will be created by default. The , , and parameters must be defined in, or inherited by, this section. drbd.conf global Define some global parameters. All parameters in this section are optional. Only one section is allowed in the configuration. drbd.conf handlers Define handlers to be invoked when certain events occur. The kernel passes the resource name in the first command-line argument and sets the following environment variables depending on the event's context: For events related to a particular device: the device's minor number in , the device's volume number in . For events related to a particular device on a particular peer: the connection endpoints in , , , and ; the device's local minor number in , and the device's volume number in . For events related to a particular connection: the connection endpoints in , , , and ; and, for each device defined for that connection: the device's minor number in . For events that identify a device, if a lower-level device is attached, the lower-level device's device name is passed in (or ). All parameters in this section are optional. Only a single handler can be defined for each event; if no handler is defined, nothing will happen. drbd.conf net Define parameters for a connection. All parameters in this section are optional. ... drbd.conf on Define the properties of a resource on a particular host or set of hosts. Specifying more than one host name can make sense in a setup with IP address failover, for example. The host-name argument must match the Linux host name (). Usually contains or inherits at least one section. The and parameters must be defined in this section. The , , and parameters must be defined in, or inherited by, this section. A normal configuration file contains two or more sections for each resource. Also see the section. drbd.conf options Define parameters for a resource. All parameters in this section are optional. drbd.conf resource Define a resource. Usually contains at least two sections and at least one section. drbd.conf stacked-on-top-of Used instead of an section for configuring a stacked resource with three to four nodes. Starting with DRBD 9, stacking is deprecated. It is advised to use resources which are replicated among more than two nodes instead. drbd.conf startup The parameters in this section determine the behavior of a resource at startup time. drbd.conf volume Define a volume within a resource. The volume numbers in the various sections of a resource define which devices on which hosts form a replicated device. Section <option>connection</option> Parameters drbd.conf host Defines an endpoint for a connection. Each statement refers to an section in a resource. If a port number is defined, this endpoint will use the specified port instead of the port defined in the section. Each section must contain exactly two parameters. Instead of two parameters the connection may contain multiple sections. Section <option>path</option> Parameters drbd.conf host Defines an endpoint for a connection. Each statement refers to an section in a resource. If a port number is defined, this endpoint will use the specified port instead of the port defined in the section. Each section must contain exactly two parameters. Section <option>connection-mesh</option> Parameters drbd.conf host Defines all nodes of a mesh. Each refers to an section in a resource. The port that is defined in the section will be used. Section <option>disk</option> Parameters Section <option>global</option> Parameters Section <option>handlers</option> Parameters drbd.conf after-resync-target Called on a resync target when a node state changes from to when a resync finishes. This handler can be used for removing the snapshot created in the handler. drbd.conf before-resync-target Called on a resync target before a resync begins. This handler can be used for creating a snapshot of the lower-level device for the duration of the resync: if the resync source becomes unavailable during a resync, reverting to the snapshot can restore a consistent state. drbd.conf fence-peer Called when a node should fence a resource on a particular peer. The handler should not use the same communication path that DRBD uses for talking to the peer. drbd.conf unfence-peer Called when a node should remove fencing constraints from other nodes. drbd.conf initial-split-brain Called when DRBD connects to a peer and detects that the peer is in a split-brain state with the local node. This handler is also called for split-brain scenarios which will be resolved automatically. drbd.conf local-io-error Called when an I/O error occurs on a lower-level device. drbd.conf pri-lost The local node is currently primary, but DRBD believes that it should become a sync target. The node should give up its primary role. drbd.conf pri-lost-after-sb The local node is currently primary, but it has lost the after-split-brain auto recovery procedure. The node should be abandoned. drbd.conf pri-on-incon-degr The local node is primary, and neither the local lower-level device nor a lower-level device on a peer is up to date. (The primary has no device to read from or to write to.) drbd.conf split-brain DRBD has detected a split-brain situation which could not be resolved automatically. Manual recovery is necessary. This handler can be used to call for administrator attention. Section <option>net</option> Parameters Section <option>on</option> Parameters drbd.conf address Defines the address family, address, and port of a connection endpoint. The address families , , (Dolphin Interconnect Solutions' "super sockets"), (Infiniband Sockets Direct Protocol), and are supported ( is an alias for ). If no address family is specified, is assumed. For all address families except , the address is specified in IPV4 address notation (for example, 1.2.3.4). For , the address is enclosed in brackets and uses IPv6 address notation (for example, [fd01:2345:6789:abcd::1]). The port is always specified as a decimal number from 1 to 65535. On each host, the port numbers must be unique for each address; ports cannot be shared. drbd.conf node-id Defines the unique node identifier for a node in the cluster. Node identifiers are used to identify individual nodes in the network protocol, and to assign bitmap slots to nodes in the metadata. Node identifiers can only be reasssigned in a cluster when the cluster is down. It is essential that the node identifiers in the configuration and in the device metadata are changed consistently on all hosts. To change the metadata, dump the current state with drbdmeta dump-md, adjust the bitmap slot assignment, and update the metadata with drbdmeta restore-md. The parameter exists since DRBD 9. Its value ranges from 0 to 16; there is no default. Section <option>options</option> Parameters (Resource Options) Section <option>startup</option> Parameters The parameters in this section define the behavior of DRBD at system startup time, in the DRBD init script. They have no effect once the system is up and running. On stacked devices, the and parameters in the configuration are usually ignored, and both timeouts are set to twice the timeout. The parameter tells DRBD to use the and parameters as defined in the configuration, even on stacked devices. Only use this parameter if the peer of the stacked resource is usually not available, or will not become primary. Incorrect use of this parameter can lead to unexpected split-brain scenarios. Section <option>volume</option> Parameters drbd.conf device Define the device name and minor number of a replicated block device. This is the device that applications are supposed to access; in most cases, the device is not used directly, but as a file system. This parameter is required and the standard device naming convention is assumed. In addition to this device, udev will create and symlinks to the device. disk drbd.conf disk Define the lower-level block device that DRBD will use for storing the actual data. While the replicated drbd device is configured, the lower-level device must not be used directly. Even read-only access with tools like dumpe2fs 8 and similar is not allowed. The keyword specifies that no lower-level block device is configured; this also overrides inheritance of the lower-level device. drbd.conf meta-disk Define where the metadata of a replicated block device resides: it can be , meaning that the lower-level device contains both the data and the metadata, or on a separate device. When the index form of this parameter is used, multiple replicated devices can share the same metadata device, each using a separate index. Each index occupies 128 MiB of data, which corresponds to a replicated device size of at most 4 TiB with two cluster nodes. We recommend not to share metadata devices anymore, and to instead use the lvm volume manager for creating metadata devices as needed. When the index form of this parameter is not used, the size of the lower-level device determines the size of the metadata. The size needed is 36 KiB + (size of lower-level device) / 32K * (number of nodes - 1). If the metadata device is bigger than that, the extra space is not used. This parameter is required if a other than is specified, and ignored if is set to . A parameter without a parameter is not allowed. Notes on data integrity DRBD supports two different mechanisms for data integrity checking: first, the network parameter allows to add a checksum to the data sent over the network. Second, the online verification mechanism (drbdadm verify and the parameter) allows to check for differences in the on-disk data. Both mechanisms can produce false positives if the data is modified during I/O (i.e., while it is being sent over the network or written to disk). This does not always indicate a problem: for example, some file systems and applications do modify data under I/O for certain operations. Swap space can also undergo changes while under I/O. Network data integrity checking tries to identify data modification during I/O by verifying the checksums on the sender side after sending the data. If it detects a mismatch, it logs an error. The receiver also logs an error when it detects a mismatch. Thus, an error logged only on the receiver side indicates an error on the network, and an error logged on both sides indicates data modification under I/O. The most recent example of systematic data corruption was identified as a bug in the TCP offloading engine and driver of a certain type of GBit NIC in 2007: the data corruption happened on the DMA transfer from core memory to the card. Because the TCP checksum were calculated on the card, the TCP/IP protocol checksums did not reveal this problem. Version This document was revised for version 9.0.0 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2012 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd 8 , drbddisk 8 , drbdsetup 8 , drbdadm 8 , DRBD User's Guide, DRBD Web Site drbd-utils-8.9.10/documentation/v9/drbdadm.xml0000644000175000017500000007151412736712235021124 0ustar apoikosapoikos 6 December 2012 DRBD 9.0.0 drbdadm 8 System Administration drbdadm Utility for DRBD administration drbdadm drbdadm options --backend-options command context Description The utility is used for managing DRBD based on its configuration files, see drbd.conf 5 . It translates high-level commands into one or more lower-level commands for the and utilities, which control the kernel module and manipulate the on-disk metadata. Depending on the command, the utility operates on one or more resources, devices, connections, or peer devices. The following command contexts are defined: resource A resource specified by name, or the keyword for all defined resources. device A device, specified by minor number (minornumber, e.g. 0) or by resource and volume number (resource/volume). If only a resource is specified, the command iterates over all devices of that resource. connection A connection, specified by resource and connection name (resource:connection-name). If only a resource is specified, the command iterates over all connections of that resource. peer_device A peer device, specified by resource, connection name, and volume number (resource:connection-name/volume). If only a resource, device, or connection is specified, the command iterates over all peer devices of that resource, device, or connection. All options following a double-dash are passed through to the lower-level utilities as specified. In addition, understands most of the options of , and will pass them through even without the double-dash. Options , Show which commands would execute instead of actually executing them (for example, drbdadm -d up resource). This can be a useful way to learn how drbdsetup and drbdmeta are used. , file Use an alternative configuration file. By default, will use the the first of the following files that exists: , , , , , . , file Check an additional configuration file. This option is only allowed with the dump and the sh-nop commands. , file Specifies the full path to the program. If this option is omitted, drbdadm will look for it beneath itself first, and then in the PATH. , file Specifies the full path to the program. If this option is omitted, drbdadm will look for it beneath itself first, and then in the PATH. , Perform the command on a stacked resource. Commands adjust resource drbdadm adjust Adjust the configuration of the kernel module so that it matches the configuration files. The result should be the same as when stopping and restarting all resources (drbdadm down all followed by drbdadm up all), but without the interruptions. Note that the adjust command can misinterpret the configuration change in some cases. To be safe, check what the command would do (with the option) before running the actual command. adjust-with-progress resource drbdadm adjust-with-progress The same as , but with some more information about the command's progress. apply-al device drbdadm apply-al Apply the activity log of the specified device. See drbdmeta 8 for details. attach device Attach a lower-level device to an existing replicated device. See drbdsetup 8 for details. check-resize device drbdadm check-resize Call drbdmeta to eventually move internal meta data. If the backing device was resized, while DRBD was not running, meta data has to be moved to the end of the device, so that the next command can succeed. connect connection drbdadm connect Activate an exisiting connection to a peer. The connection needs to be created first with the command, and have at least one path created with the command. See drbdsetup 8 for details. create-md device drbdadm create-md Initialize the metadata of a device. This is necessary before a device can be attached; see drbdmeta 8 for details. cstate connection drbdadm cstate Show the current state of a connection. See drbdsetup 8 for details. detach device drbdadm detach Detach the lower-level device of a replicated device. See drbdsetup 8 for details. disconnect connection drbdadm disconnect Remove a connection to a peer host. See drbdsetup 8 for details. disk-options device drbdadm disk-options Cange the disk options of an attached lower-level device. See drbdsetup 8 for details. down resource drbdadm down Take a resource down by removing all volumes, connections, and the resource itself. See drbdsetup 8 for details. dstate device drbdadm dstate Show the current disk state of a lower-level device. See drbdsetup 8 for details. dump resource drbdadm dump Parse the configuration file and dump it to stdout. This will fail if the configuration file is syntactically incorrect. dump-md device drbdadm dump-md Dump the metadata of a device in text form, including the bitmap and activity log. See drbdmeta 8 for details. get-gi peer_device drbdadm get-gi Show the data generation identifiers for a device on a particular connection. Uses for attached devices and for unattached devices. See drbdsetup 8 for details. hidden-commands Shows all commands which are not explicitly documented. invalidate peer_device drbdadm invalidate Replace the local data of a device with that of a peer. See drbdsetup 8 for details. invalidate-remote peer_device drbdadm invalidate-remote Replace a peer device's data of a resource with the local data. See drbdsetup 8 for details. net-options connection drbdadm net-options Change the network options of an existing connection. See drbdsetup 8 for details. new-current-uuid device drbdadm new-current-uuid Generate a new currend UUID. See drbdsetup 8 for details. outdate device drbdadm outdate Mark the data on a lower-level device as outdated. See drbdsetup 8 for details. pause-sync peer_device drbdadm pause-sync Stop resynchronizing between a local and a peer device by setting the local pause flag. See drbdsetup 8 for details. primary resource drbdadm primary Change the role of a node in a resource to primary. See drbdsetup 8 for details. resize device drbdadm resize Resize the lower-level devices of a replicated device on all nodes. This combines the and lower-level commands; see drbdsetup 8 for details. resource-options resource drbdadm resource-options Change the resource options of an existing resource. See drbdsetup 8 for details. resume-sync peer_device drbdadm resume-sync Allow resynchronization to resume by clearing the local sync pause flag. See drbdsetup 8 for details. role resource drbdadm role Show the current role of a resource. secondary resource drbdadm secondary Change the role of a node in a resource to secondary. This command fails if the replicated device is in use. show-gi peer_device drbdadm show-gi Show the data generation identifiers for a device on a particular connection. In addition, explain the output. See drbdsetup 8 for details. state resource drbdadm state This is an alias for drbdsetup role. Deprecated. up resource drbdadm up Bring up a resource by applying the activity log of all volumes, creating the resource, creating the replicated devices, attaching the lower-level devices, and connecting to all peers. See the drbdmeta command and the , , , , and drbdsetup commands. verify peer_device drbdadm verify Start online verification, change which part of the device will be verified, or stop online verification. See drbdsetup 8 for details. wait-connect device connection resource drbdadm wait-connect Wait until a device on a peer, all devices over a connection, or all devices on all peers are visible. See drbdsetup 8 for details. wait-sync device connection resource drbdadm wait-sync Wait until a device is connected and has finished eventual resync operation. Also available on connection and resource level. See drbdsetup 8 for details. wipe-md device drbdadm wipe-md Wipe out the DRBD metadata of a device. See drbdmeta 8 for details. forget-peer connection drbdadm forget-peer Completely remove any reference to a unconnected peer from meta-data. See drbdmeta 8 for details. Version This document was revised for version 9.0.0 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2012 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf 5 , drbd 8 , drbddisk 8 , drbdsetup 8 , drbdmeta 8 and the DRBD project web site drbd-utils-8.9.10/documentation/v9/drbd.xml0000644000175000017500000000617312466702073020440 0ustar apoikosapoikos drbd The start and stop script for DRBD DRBD 9.0.0 24 June 2014 drbd 8 System Administration /etc/init.d/drbd start stop status reload restart force-reload Introduction The /etc/init.d/drbd script is used to start and stop drbd on a system V style init system. When using a cluster resource manger such as Pacemaker, DRBD should usually not be started by the init system, but should typically be exclusively controlled by the cluster manager. You should not use, and disable, the init script in this case. chmod -x /etc/init.d/drbd has proven most effective for this. In order to use /etc/init.d/drbd, define a drbd configuration. See drbd.conf5 for details. Version This document was revised for version 9.0.0 of the DRBD distribution. Author Written by Philipp Reisner philipp.reisner@linbit.com and Lars Ellenberg lars.ellenberg@linbit.com. Reporting Bugs Report bugs to drbd-user@lists.linbit.com. Copyright Copyright 2001-2014 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See Also drbd.conf5, drbdsetup8, drbdadm8, DRBD Homepage drbd-utils-8.9.10/documentation/v9/drbdsetup.80000644000175000017500000017273213027211666021072 0ustar apoikosapoikos'\" t .\" Title: drbdsetup .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 3 December 2011 .\" Manual: System Administration .\" Source: DRBD 9.0.0 .\" Language: English .\" .TH "DRBDSETUP" "8" "3 December 2011" "DRBD 9.0.0" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbdsetup \- Configure the DRBD kernel module .SH "SYNOPSIS" .HP \w'\fBdrbdsetup\fR\ 'u \fBdrbdsetup\fR command {argument...} [option...] .SH "DESCRIPTION" .PP The \fBdrbdsetup\fR utility serves to configure the DRBD kernel module and to show its current configuration\&. Users usually interact with the \fBdrbdadm\fR utility, which provides a more high\-level interface to DRBD than \fBdrbdsetup\fR\&. (See \fBdrbdadm\fR\*(Aqs \fB\-\-dry\-run\fR option to see how \fBdrbdadm\fR uses \fBdrbdsetup\fR\&.) .PP Some option arguments have a default scale which applies when a plain number is specified (for example Kilo, or 1024 times the numeric value)\&. Such default scales can be overridden by using a suffix (for example, M for Mega)\&. The common suffixes K = 2^10 = 1024, M = 1024 K, and G = 1024 M are supported\&. .SH "COMMANDS" .PP \fBdrbdsetup\fR attach \fIminor\fR \fIlower_dev\fR \fImeta_data_dev\fR \fImeta_data_index\fR, .br \fBdrbdsetup\fR disk\-options \fIminor\fR .RS 4 The \fBattach\fR command attaches a lower\-level device to an existing replicated device\&. The \fBdisk\-options\fR command changes the disk options of an attached lower\-level device\&. In either case, the replicated device must have been created with \fBdrbdsetup new\-minor\fR\&. .sp Both commands refer to the replicated device by its \fIminor\fR number\&. \fIlower_dev\fR is the name of the lower\-level device\&. \fImeta_data_dev\fR is the name of the device containing the metadata, and may be the same as \fIlower_dev\fR\&. \fImeta_data_index\fR is either a numeric metadata index, or the keyword \fBinternal\fR for internal metadata, or the keyword \fBflexible\fR for variable\-size external metadata\&. Available options: .PP \fB\-\-al\-extents \fR\fB\fIextents\fR\fR .RS 4 DRBD automatically maintains a "hot" or "active" disk area likely to be written to again soon based on the recent write activity\&. The "active" disk area can be written to immediately, while "inactive" disk areas must be "activated" first, which requires a meta\-data write\&. We also refer to this active disk area as the "activity log"\&. .sp The activity log saves meta\-data writes, but the whole log must be resynced upon recovery of a failed node\&. The size of the activity log is a major factor of how long a resync will take and how fast a replicated disk will become consistent after a crash\&. .sp The activity log consists of a number of 4\-Megabyte segments; the \fIal\-extents\fR parameter determines how many of those segments can be active at the same time\&. The default value for \fIal\-extents\fR is 1237, with a minimum of 7 and a maximum of 65536\&. .sp Note that the effective maximum may be smaller, depending on how you created the device meta data, see also \fBdrbdmeta\fR(8) The effective maximum is 919 * (available on\-disk activity\-log ring\-buffer area/4kB \-1), the default 32kB ring\-buffer effects a maximum of 6433 (covers more than 25 GiB of data) We recommend to keep this well within the amount your backend storage and replication link are able to resync inside of about 5 minutes\&. .RE .PP \fB\-\-al\-updates \fR\fB{yes | no}\fR\fB \fR .RS 4 With this parameter, the activity log can be turned off entirely (see the \fBal\-extents\fR parameter)\&. This will speed up writes because fewer meta\-data writes will be necessary, but the entire device needs to be resynchronized opon recovery of a failed primary node\&. The default value for \fBal\-updates\fR is \fByes\fR\&. .RE .PP \fB\-\-disk\-barrier\fR, .br \fB\-\-disk\-flushes\fR, .br \fB\-\-disk\-drain\fR .RS 4 DRBD has three methods of handling the ordering of dependent write requests: .PP \fBdisk\-barrier\fR .RS 4 Use disk barriers to make sure that requests are written to disk in the right order\&. Barriers ensure that all requests submitted before a barrier make it to the disk before any requests submitted after the barrier\&. This is implemented using \*(Aqtagged command queuing\*(Aq on SCSI devices and \*(Aqnative command queuing\*(Aq on SATA devices\&. Only some devices and device stacks support this method\&. The device mapper (LVM) only supports barriers in some configurations\&. .sp Note that on systems which do not support disk barriers, enabling this option can lead to data loss or corruption\&. Until DRBD 8\&.4\&.1, \fBdisk\-barrier\fR was turned on if the I/O stack below DRBD did support barriers\&. Kernels since linux\-2\&.6\&.36 (or 2\&.6\&.32 RHEL6) no longer allow to detect if barriers are supported\&. Since drbd\-8\&.4\&.2, this option is off by default and needs to be enabled explicitly\&. .RE .PP \fBdisk\-flushes\fR .RS 4 Use disk flushes between dependent write requests, also referred to as \*(Aqforce unit access\*(Aq by drive vendors\&. This forces all data to disk\&. This option is enabled by default\&. .RE .PP \fBdisk\-drain\fR .RS 4 Wait for the request queue to "drain" (that is, wait for the requests to finish) before submitting a dependent write request\&. This method requires that requests are stable on disk when they finish\&. Before DRBD 8\&.0\&.9, this was the only method implemented\&. This option is enabled by default\&. Do not disable in production environments\&. .RE .sp From these three methods, drbd will use the first that is enabled and supported by the backing storage device\&. If all three of these options are turned off, DRBD will submit write requests without bothering about dependencies\&. Depending on the I/O stack, write requests can be reordered, and they can be submitted in a different order on different cluster nodes\&. This can result in data loss or corruption\&. Therefore, turning off all three methods of controlling write ordering is strongly discouraged\&. .sp A general guideline for configuring write ordering is to use disk barriers or disk flushes when using ordinary disks (or an ordinary disk array) with a volatile write cache\&. On storage without cache or with a battery backed write cache, disk draining can be a reasonable choice\&. .RE .PP \fB\-\-disk\-timeout\fR .RS 4 If the lower\-level device on which a DRBD device stores its data does not finish an I/O request within the defined \fBdisk\-timeout\fR, DRBD treats this as a failure\&. The lower\-level device is detached, and the device\*(Aqs disk state advances to Diskless\&. If DRBD is connected to one or more peers, the failed request is passed on to one of them\&. .sp This option is \fIdangerous and may lead to kernel panic!\fR .sp "Aborting" requests, or force\-detaching the disk, is intended for completely blocked/hung local backing devices which do no longer complete requests at all, not even do error completions\&. In this situation, usually a hard\-reset and failover is the only way out\&. .sp By "aborting", basically faking a local error\-completion, we allow for a more graceful swichover by cleanly migrating services\&. Still the affected node has to be rebooted "soon"\&. .sp By completing these requests, we allow the upper layers to re\-use the associated data pages\&. .sp If later the local backing device "recovers", and now DMAs some data from disk into the original request pages, in the best case it will just put random data into unused pages; but typically it will corrupt meanwhile completely unrelated data, causing all sorts of damage\&. .sp Which means delayed successful completion, especially for READ requests, is a reason to panic()\&. We assume that a delayed *error* completion is OK, though we still will complain noisily about it\&. .sp The default value of \fBdisk\-timeout\fR is 0, which stands for an infinite timeout\&. Timeouts are specified in units of 0\&.1 seconds\&. This option is available since DRBD 8\&.3\&.12\&. .RE .PP \fB\-\-md\-flushes\fR .RS 4 Enable disk flushes and disk barriers on the meta\-data device\&. This option is enabled by default\&. See the \fBdisk\-flushes\fR parameter\&. .RE .PP \fB\-\-on\-io\-error \fR\fB\fIhandler\fR\fR .RS 4 Configure how DRBD reacts to I/O errors on a lower\-level device\&. The following policies are defined: .PP \fBpass_on\fR .RS 4 Change the disk status to Inconsistent, mark the failed block as inconsistent in the bitmap, and retry the I/O operation on a remote cluster node\&. .RE .PP \fBcall\-local\-io\-error\fR .RS 4 Call the \fBlocal\-io\-error\fR handler (see the \fBhandlers\fR section)\&. .RE .PP \fBdetach\fR .RS 4 Detach the lower\-level device and continue in diskless mode\&. .RE .sp .RE .PP \fB\-\-read\-balancing \fR\fB\fIpolicy\fR\fR .RS 4 Distribute read requests among cluster nodes as defined by \fIpolicy\fR\&. The supported policies are \fBprefer\-local\fR (the default), \fBprefer\-remote\fR, \fBround\-robin\fR, \fBleast\-pending\fR, \fBwhen\-congested\-remote\fR, \fB32K\-striping\fR, \fB64K\-striping\fR, \fB128K\-striping\fR, \fB256K\-striping\fR, \fB512K\-striping\fR and \fB1M\-striping\fR\&. .sp This option is available since DRBD 8\&.4\&.1\&. .RE .PP \fBresync\-after \fR\fB\fIminor\fR\fR .RS 4 Define that a device should only resynchronize after the specified other device\&. By default, no order between devices is defined, and all devices will resynchronize in parallel\&. Depending on the configuration of the lower\-level devices, and the available network and disk bandwidth, this can slow down the overall resync process\&. This option can be used to form a chain or tree of dependencies among devices\&. .RE .PP \fB\-\-size \fR\fB\fIsize\fR\fR .RS 4 Specify the size of the lower\-level device explicitly instead of determining it automatically\&. The device size must be determined once and is remembered for the lifetime of the device\&. In order to determine it automatically, all the lower\-level devices on all nodes must be attached, and all nodes must be connected\&. If the size is specified explicitly, this is not necessary\&. The \fBsize\fR value is assumed to be in units of sectors (512 bytes) by default\&. .RE .PP \fB\-\-discard\-zeroes\-if\-aligned \fR\fB{yes | no}\fR .RS 4 There are several aspects to discard/trim/unmap support on linux block devices\&. Even if discard is supported in general, it may fail silently, or may partially ignore discard requests\&. Devices also announce whether reading from unmapped blocks returns defined data (usually zeroes), or undefined data (possibly old data, possibly garbage)\&. .sp If on different nodes, DRBD is backed by devices with differing discard characteristics, discards may lead to data divergence (old data or garbage left over on one backend, zeroes due to unmapped areas on the other backend)\&. Online verify would now potentially report tons of spurious differences\&. While probably harmless for most use cases (fstrim on a file system), DRBD cannot have that\&. .sp To play safe, we have to disable discard support, if our local backend (on a Primary) does not support "discard_zeroes_data=true"\&. We also have to translate discards to explicit zero\-out on the receiving side, unless the receiving side (Secondary) supports "discard_zeroes_data=true", thereby allocating areas what were supposed to be unmapped\&. .sp There are some devices (notably the LVM/DM thin provisioning) that are capable of discard, but announce discard_zeroes_data=false\&. In the case of DM\-thin, discards aligned to the chunk size will be unmapped, and reading from unmapped sectors will return zeroes\&. However, unaligned partial head or tail areas of discard requests will be silently ignored\&. .sp If we now add a helper to explicitly zero\-out these unaligned partial areas, while passing on the discard of the aligned full chunks, we effectively achieve discard_zeroes_data=true on such devices\&. .sp Setting \fBdiscard\-zeroes\-if\-aligned\fR to \fByes\fR will allow DRBD to use discards, and to announce discard_zeroes_data=true, even on backends that announce discard_zeroes_data=false\&. .sp Setting \fBdiscard\-zeroes\-if\-aligned\fR to \fBno\fR will cause DRBD to always fall\-back to zero\-out on the receiving side, and to not even announce discard capabilities on the Primary, if the respective backend announces discard_zeroes_data=false\&. .sp We used to ignore the discard_zeroes_data setting completely\&. To not break established and expected behaviour, and suddenly cause fstrim on thin\-provisioned LVs to run out\-of\-space instead of freeing up space, the default value is \fByes\fR\&. .sp This option is available since 8\&.4\&.7\&. .RE .PP \fB\-\-rs\-discard\-granularity \fR\fB\fIbyte\fR\fR .RS 4 When \fBrs\-discard\-granularity\fR is set to a non zero, positive value then DRBD tries to do a resync operation in requests of this size\&. In case such a block contains only zero bytes on the sync source node, the sync target node will issue a discard/trim/unmap command for the area\&. .sp The value is constrained by the discard granularity of the backing block device\&. In case \fBrs\-discard\-granularity\fR is not a multiplier of the discard granularity of the backing block device DRBD rounds it up\&. The feature only gets active if the backing block device reads back zeroes after a discard command\&. .sp The default value of is 0\&. This option is available since 8\&.4\&.7\&. .RE .RE .PP \fBdrbdsetup\fR peer\-device\-options \fIresource\fR \fIpeer_node_id\fR \fIvolume\fR .RS 4 These are options that affect the \fIpeer\fR\*(Aqs device\&. .PP \fB\-\-c\-delay\-target \fR\fB\fIdelay_target\fR\fR, .br \fB\-\-c\-fill\-target \fR\fB\fIfill_target\fR\fR, .br \fB\-\-c\-max\-rate \fR\fB\fImax_rate\fR\fR, .br \fB\-\-c\-plan\-ahead \fR\fB\fIplan_time\fR\fR .RS 4 Dynamically control the resync speed\&. This mechanism is enabled by setting the \fBc\-plan\-ahead\fR parameter to a positive value\&. The goal is to either fill the buffers along the data path with a defined amount of data if \fBc\-fill\-target\fR is defined, or to have a defined delay along the path if \fBc\-delay\-target\fR is defined\&. The maximum bandwidth is limited by the \fBc\-max\-rate\fR parameter\&. .sp The \fBc\-plan\-ahead\fR parameter defines how fast drbd adapts to changes in the resync speed\&. It should be set to five times the network round\-trip time or more\&. Common values for \fBc\-fill\-target\fR for "normal" data paths range from 4K to 100K\&. If drbd\-proxy is used, it is advised to use \fBc\-delay\-target\fR instead of \fBc\-fill\-target\fR\&. The \fBc\-delay\-target\fR parameter is used if the \fBc\-fill\-target\fR parameter is undefined or set to 0\&. The \fBc\-delay\-target\fR parameter should be set to five times the network round\-trip time or more\&. The \fBc\-max\-rate\fR option should be set to either the bandwidth available between the DRBD\-hosts and the machines hosting DRBD\-proxy, or to the available disk bandwidth\&. .sp The default values of these parameters are: \fBc\-plan\-ahead\fR = 20 (in units of 0\&.1 seconds), \fBc\-fill\-target\fR = 0 (in units of sectors), \fBc\-delay\-target\fR = 1 (in units of 0\&.1 seconds), and \fBc\-max\-rate\fR = 102400 (in units of KiB/s)\&. .sp Dynamic resync speed control is available since DRBD 8\&.3\&.9\&. .RE .PP .RS 4 .RE .PP .RS 4 .RE .PP \fB\-\-c\-min\-rate \fR\fB\fImin_rate\fR\fR .RS 4 A node which is primary and sync\-source has to schedule application I/O requests and resync I/O requests\&. The \fBc\-min\-rate\fR parameter limits how much bandwidth is available for resync I/O; the remaining bandwidth is used for application I/O\&. .sp A \fBc\-min\-rate\fR value of 0 means that there is no limit on the resync I/O bandwidth\&. This can slow down application I/O significantly\&. Use a value of 1 (1 KiB/s) for the lowest possible resync rate\&. .sp The default value of \fBc\-min\-rate\fR is 4096, in units of KiB/s\&. .RE .PP .RS 4 .RE .PP \fB\-\-resync\-rate \fR\fB\fIrate\fR\fR .RS 4 Define how much bandwidth DRBD may use for resynchronizing\&. DRBD allows "normal" application I/O even during a resync\&. If the resync takes up too much bandwidth, application I/O can become very slow\&. This parameter allows to avoid that\&. Please note this is option only works when the dynamic resync controller is disabled\&. .RE .RE .PP \fBdrbdsetup\fR check\-resize \fIminor\fR .RS 4 Remember the current size of the lower\-level device of the specified replicated device\&. Used by drbdadm\&. The size information is stored in file /var/lib/drbd/drbd\-minor\-\fIminor\fR\&.lkbd\&. .RE .PP \fBdrbdsetup\fR new\-peer \fIresource\fR \fIpeer_node_id\fR, .br \fBdrbdsetup\fR net\-options \fIresource\fR \fIpeer_node_id\fR .RS 4 The \fBnew\-peer\fR command creates a connection within a \fIresource\fR\&. The resource must have been created with \fBdrbdsetup new\-resource\fR\&. The \fBnet\-options\fR command changes the network options of an existing connection\&. Before a connection can be activated with the \fBconnect\fR command, at least one path need to added with the \fBnew\-path\fR command\&. Available options: .PP \fB\-\-after\-sb\-0pri \fR\fB\fIpolicy\fR\fR .RS 4 Define how to react if a split\-brain scenario is detected and none of the two nodes is in primary role\&. (We detect split\-brain scenarios when two nodes connect; split\-brain decisions are always between two nodes\&.) The defined policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization; simply disconnect\&. .RE .PP \fBdiscard\-younger\-primary\fR, .br \fBdiscard\-older\-primary\fR .RS 4 Resynchronize from the node which became primary first (\fBdiscard\-younger\-primary\fR) or last (\fBdiscard\-older\-primary\fR)\&. If both nodes became primary independently, the \fBdiscard\-least\-changes\fR policy is used\&. .RE .PP \fBdiscard\-zero\-changes\fR .RS 4 If only one of the nodes wrote data since the split brain situation was detected, resynchronize from this node to the other\&. If both nodes wrote data, disconnect\&. .RE .PP \fBdiscard\-least\-changes\fR .RS 4 Resynchronize from the node with more modified blocks\&. .RE .PP \fBdiscard\-node\-\fR\fB\fInodename\fR\fR .RS 4 Always resynchronize to the named node\&. .RE .RE .PP \fB\-\-after\-sb\-1pri \fR\fB\fIpolicy\fR\fR .RS 4 Define how to react if a split\-brain scenario is detected, with one node in primary role and one node in secondary role\&. (We detect split\-brain scenarios when two nodes connect, so split\-brain decisions are always among two nodes\&.) The defined policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBconsensus\fR .RS 4 Discard the data on the secondary node if the \fBafter\-sb\-0pri\fR algorithm would also discard the data on the secondary node\&. Otherwise, disconnect\&. .RE .PP \fBviolently\-as0p\fR .RS 4 Always take the decision of the \fBafter\-sb\-0pri\fR algorithm, even if it causes an erratic change of the primary\*(Aqs view of the data\&. This is only useful if a single\-node file system (i\&.e\&., not OCFS2 or GFS) with the \fBallow\-two\-primaries\fR flag is used\&. This option can cause the primary node to crash, and should not be used\&. .RE .PP \fBdiscard\-secondary\fR .RS 4 Discard the data on the secondary node\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Always take the decision of the \fBafter\-sb\-0pri\fR algorithm\&. If the decision is to discard the data on the primary node, call the \fBpri\-lost\-after\-sb\fR handler on the primary node\&. .RE .RE .PP \fB\-\-after\-sb\-2pri \fR\fB\fIpolicy\fR\fR .RS 4 Define how to react if a split\-brain scenario is detected and both nodes are in primary role\&. (We detect split\-brain scenarios when two nodes connect, so split\-brain decisions are always among two nodes\&.) The defined policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBviolently\-as0p\fR .RS 4 See the \fBviolently\-as0p\fR policy for \fBafter\-sb\-1pri\fR\&. .RE .PP \fBcall\-pri\-lost\-after\-sb\fR .RS 4 Call the \fBpri\-lost\-after\-sb\fR helper program on one of the machines unless that machine can demote to secondary\&. The helper program is expected to reboot the machine, which brings the node into a secondary role\&. Which machine runs the helper program is determined by the \fBafter\-sb\-0pri\fR strategy\&. .RE .RE .PP \fB\-\-allow\-two\-primaries\fR .RS 4 The most common way to configure DRBD devices is to allow only one node to be primary (and thus writable) at a time\&. .sp In some scenarios it is preferable to allow two nodes to be primary at once; a mechanism outside of DRBD then must make sure that writes to the shared, replicated device happen in a coordinated way\&. This can be done with a shared\-storage cluster file system like OCFS2 and GFS, or with virtual machine images and a virtual machine manager that can migrate virtual machines between physical machines\&. .sp The \fBallow\-two\-primaries\fR parameter tells DRBD to allow two nodes to be primary at the same time\&. Never enable this option when using a non\-distributed file system; otherwise, data corruption and node crashes will result! .RE .PP \fB\-\-always\-asbp\fR .RS 4 Normally the automatic after\-split\-brain policies are only used if current states of the UUIDs do not indicate the presence of a third node\&. .sp With this option you request that the automatic after\-split\-brain policies are used as long as the data sets of the nodes are somehow related\&. This might cause a full sync, if the UUIDs indicate the presence of a third node\&. (Or double faults led to strange UUID sets\&.) .RE .PP \fB\-\-connect\-int \fR\fB\fItime\fR\fR .RS 4 As soon as a connection between two nodes is configured with \fBdrbdsetup connect\fR, DRBD immediately tries to establish the connection\&. If this fails, DRBD waits for \fBconnect\-int\fR seconds and then repeats\&. The default value of \fBconnect\-int\fR is 10 seconds\&. .RE .PP \fB\-\-cram\-hmac\-alg \fR\fB\fIhash\-algorithm\fR\fR .RS 4 Configure the hash\-based message authentication code (HMAC) or secure hash algorithm to use for peer authentication\&. The kernel supports a number of different algorithms, some of which may be loadable as kernel modules\&. See the shash algorithms listed in /proc/crypto\&. By default, \fBcram\-hmac\-alg\fR is unset\&. Peer authentication also requires a \fBshared\-secret\fR to be configured\&. .RE .PP \fB\-\-csums\-alg \fR\fB\fIhash\-algorithm\fR\fR .RS 4 Normally, when two nodes resynchronize, the sync target requests a piece of out\-of\-sync data from the sync source, and the sync source sends the data\&. With many usage patterns, a significant number of those blocks will actually be identical\&. .sp When a \fBcsums\-alg\fR algorithm is specified, when requesting a piece of out\-of\-sync data, the sync target also sends along a hash of the data it currently has\&. The sync source compares this hash with its own version of the data\&. It sends the sync target the new data if the hashes differ, and tells it that the data are the same otherwise\&. This reduces the network bandwidth required, at the cost of higher cpu utilization and possibly increased I/O on the sync target\&. .sp The \fBcsums\-alg\fR can be set to one of the secure hash algorithms supported by the kernel; see the shash algorithms listed in /proc/crypto\&. By default, \fBcsums\-alg\fR is unset\&. .RE .PP \fB\-\-csums\-after\-crash\-only\fR .RS 4 Enabling this option (and csums\-alg, above) makes it possible to use the checksum based resync only for the first resync after primary crash, but not for later "network hickups"\&. .sp In most cases, block that are marked as need\-to\-be\-resynced are in fact changed, so calculating checksums, and both reading and writing the blocks on the resync target is all effective overhead\&. .sp The advantage of checksum based resync is mostly after primary crash recovery, where the recovery marked larger areas (those covered by the activity log) as need\-to\-be\-resynced, just in case\&. Introduced in 8\&.4\&.5\&. .RE .PP \fB\-\-data\-integrity\-alg \fR \fIalg\fR .RS 4 DRBD normally relies on the data integrity checks built into the TCP/IP protocol, but if a data integrity algorithm is configured, it will additionally use this algorithm to make sure that the data received over the network match what the sender has sent\&. If a data integrity error is detected, DRBD will close the network connection and reconnect, which will trigger a resync\&. .sp The \fBdata\-integrity\-alg\fR can be set to one of the secure hash algorithms supported by the kernel; see the shash algorithms listed in /proc/crypto\&. By default, this mechanism is turned off\&. .sp Because of the CPU overhead involved, we recommend not to use this option in production environments\&. Also see the notes on data integrity below\&. .RE .PP \fB\-\-fencing \fR\fB\fIfencing_policy\fR\fR .RS 4 \fBFencing\fR is a preventive measure to avoid situations where both nodes are primary and disconnected\&. This is also known as a split\-brain situation\&. DRBD supports the following fencing policies: .PP \fBdont\-care\fR .RS 4 No fencing actions are taken\&. This is the default policy\&. .RE .PP \fBresource\-only\fR .RS 4 If a node becomes a disconnected primary, it tries to fence the peer\&. This is done by calling the \fBfence\-peer\fR handler\&. The handler is supposed to reach the peer over an alternative communication path and call \*(Aq\fBdrbdadm outdate minor\fR\*(Aq there\&. .RE .PP \fBresource\-and\-stonith\fR .RS 4 If a node becomes a disconnected primary, it freezes all its IO operations and calls its fence\-peer handler\&. The fence\-peer handler is supposed to reach the peer over an alternative communication path and call \*(Aq\fBdrbdadm outdate minor\fR\*(Aq there\&. In case it cannot do that, it should stonith the peer\&. IO is resumed as soon as the situation is resolved\&. In case the fence\-peer handler fails, I/O can be resumed manually with \*(Aq\fBdrbdadm resume\-io\fR\*(Aq\&. .RE .RE .PP \fB\-\-ko\-count \fR\fB\fInumber\fR\fR .RS 4 If a secondary node fails to complete a write request in \fBko\-count\fR times the \fBtimeout\fR parameter, it is excluded from the cluster\&. The primary node then sets the connection to this secondary node to Standalone\&. To disable this feature, you should explicitly set it to 0; defaults may change between versions\&. .RE .PP \fB\-\-max\-buffers \fR\fB\fInumber\fR\fR .RS 4 Limits the memory usage per DRBD minor device on the receiving side, or for internal buffers during resync or online\-verify\&. Unit is PAGE_SIZE, which is 4 KiB on most systems\&. The minimum possible setting is hard coded to 32 (=128 KiB)\&. These buffers are used to hold data blocks while they are written to/read from disk\&. To avoid possible distributed deadlocks on congestion, this setting is used as a throttle threshold rather than a hard limit\&. Once more than max\-buffers pages are in use, further allocation from this pool is throttled\&. You want to increase max\-buffers if you cannot saturate the IO backend on the receiving side\&. .RE .PP \fB\-\-max\-epoch\-size \fR\fB\fInumber\fR\fR .RS 4 Define the maximum number of write requests DRBD may issue before issuing a write barrier\&. The default value is 2048, with a minimum of 1 and a maximum of 20000\&. Setting this parameter to a value below 10 is likely to decrease performance\&. .RE .PP \fB\-\-on\-congestion \fR\fB\fIpolicy\fR\fR, .br \fB\-\-congestion\-fill \fR\fB\fIthreshold\fR\fR, .br \fB\-\-congestion\-extents \fR\fB\fIthreshold\fR\fR .RS 4 By default, DRBD blocks when the TCP send queue is full\&. This prevents applications from generating further write requests until more buffer space becomes available again\&. .sp When DRBD is used together with DRBD\-proxy, it can be better to use the \fBpull\-ahead\fR \fBon\-congestion\fR policy, which can switch DRBD into ahead/behind mode before the send queue is full\&. DRBD then records the differences between itself and the peer in its bitmap, but it no longer replicates them to the peer\&. When enough buffer space becomes available again, the node resynchronizes with the peer and switches back to normal replication\&. .sp This has the advantage of not blocking application I/O even when the queues fill up, and the disadvantage that peer nodes can fall behind much further\&. Also, while resynchronizing, peer nodes will become inconsistent\&. .sp The available congestion policies are \fBblock\fR (the default) and \fBpull\-ahead\fR\&. The \fBcongestion\-fill\fR parameter defines how much data is allowed to be "in flight" in this connection\&. The default value is 0, which disables this mechanism of congestion control, with a maximum of 10 GiBytes\&. The \fBcongestion\-extents\fR parameter defines how many bitmap extents may be active before switching into ahead/behind mode, with the same default and limits as the \fBal\-extents\fR parameter\&. The \fBcongestion\-extents\fR parameter is effective only when set to a value smaller than \fBal\-extents\fR\&. .sp Ahead/behind mode is available since DRBD 8\&.3\&.10\&. .RE .PP \fB\-\-ping\-int \fR\fB\fIinterval\fR\fR .RS 4 When the TCP/IP connection to a peer is idle for more than \fBping\-int\fR seconds, DRBD will send a keep\-alive packet to make sure that a failed peer or network connection is detected reasonably soon\&. The default value is 10 seconds, with a minimum of 1 and a maximum of 120 seconds\&. The unit is seconds\&. .RE .PP \fB\-\-ping\-timeout \fR\fB\fItimeout\fR\fR .RS 4 Define the timeout for replies to keep\-alive packets\&. If the peer does not reply within \fBping\-timeout\fR, DRBD will close and try to reestablish the connection\&. The default value is 0\&.5 seconds, with a minimum of 0\&.1 seconds and a maximum of 3 seconds\&. The unit is tenths of a second\&. .RE .PP \fB\-\-socket\-check\-timeout \fR\fB\fItimeout\fR\fR .RS 4 In setups involving a DRBD\-proxy and connections that experience a lot of buffer\-bloat it might be necessary to set \fBping\-timeout\fR to an unusual high value\&. By default DRBD uses the same value to wait if a newly established TCP\-connection is stable\&. Since the DRBD\-proxy is usually located in the same data center such a long wait time may hinder DRBD\*(Aqs connect process\&. .sp In such setups \fBsocket\-check\-timeout\fR should be set to at least to the round trip time between DRBD and DRBD\-proxy\&. I\&.e\&. in most cases to 1\&. .sp The default unit is tenths of a second, the default value is 0 (which causes DRBD to use the value of \fBping\-timeout\fR instead)\&. Introduced in 8\&.4\&.5\&. .RE .PP \fB\-\-protocol \fR\fB\fIname\fR\fR .RS 4 Use the specified protocol on this connection\&. The supported protocols are: .PP \fBA\fR .RS 4 Writes to the DRBD device complete as soon as they have reached the local disk and the TCP/IP send buffer\&. .RE .PP \fBB\fR .RS 4 Writes to the DRBD device complete as soon as they have reached the local disk, and all peers have acknowledged the receipt of the write requests\&. .RE .PP \fBC\fR .RS 4 Writes to the DRBD device complete as soon as they have reached the local and all remote disks\&. .RE .sp .RE .PP \fB\-\-rcvbuf\-size \fR\fB\fIsize\fR\fR .RS 4 Configure the size of the TCP/IP receive buffer\&. A value of 0 (the default) causes the buffer size to adjust dynamically\&. This parameter usually does not need to be set, but it can be set to a value up to 10 MiB\&. The default unit is bytes\&. .RE .PP \fB\-\-rr\-conflict\fR \fIpolicy\fR .RS 4 This option helps to solve the cases when the outcome of the resync decision is incompatible with the current role assignment in the cluster\&. The defined policies are: .PP \fBdisconnect\fR .RS 4 No automatic resynchronization, simply disconnect\&. .RE .PP \fBviolently\fR .RS 4 Resync to the primary node is allowed, violating the assumption that data on a block device are stable for one of the nodes\&. \fIDo not use this option, it is dangerous\&.\fR .RE .PP \fBcall\-pri\-lost\fR .RS 4 Call the \fBpri\-lost\fR handler on one of the machines\&. The handler is expected to reboot the machine, which puts it into secondary role\&. .RE .RE .PP \fB\-\-shared\-secret \fR\fB\fIsecret\fR\fR .RS 4 Configure the shared secret used for peer authentication\&. The secret is a string of up to 64 characters\&. Peer authentication also requires the \fBcram\-hmac\-alg\fR parameter to be set\&. .RE .PP \fB\-\-sndbuf\-size \fR\fB\fIsize\fR\fR .RS 4 Configure the size of the TCP/IP send buffer\&. Since DRBD 8\&.0\&.13 / 8\&.2\&.7, a value of 0 (the default) causes the buffer size to adjust dynamically\&. Values below 32 KiB are harmful to the throughput on this connection\&. Large buffer sizes can be useful especially when protocol A is used over high\-latency networks; the maximum value supported is 10 MiB\&. .RE .PP \fB\-\-tcp\-cork\fR .RS 4 By default, DRBD uses the TCP_CORK socket option to prevent the kernel from sending partial messages; this results in fewer and bigger packets on the network\&. Some network stacks can perform worse with this optimization\&. On these, the \fBtcp\-cork\fR parameter can be used to turn this optimization off\&. .RE .PP \fB\-\-timeout \fR\fB\fItime\fR\fR .RS 4 Define the timeout for replies over the network: if a peer node does not send an expected reply within the specified \fBtimeout\fR, it is considered dead and the TCP/IP connection is closed\&. The timeout value must be lower than \fBconnect\-int\fR and lower than \fBping\-int\fR\&. The default is 6 seconds; the value is specified in tenths of a second\&. .RE .PP \fB\-\-use\-rle\fR .RS 4 Each replicated device on a cluster node has a separate bitmap for each of its peer devices\&. The bitmaps are used for tracking the differences between the local and peer device: depending on the cluster state, a disk range can be marked as different from the peer in the device\*(Aqs bitmap, in the peer device\*(Aqs bitmap, or in both bitmaps\&. When two cluster nodes connect, they exchange each other\*(Aqs bitmaps, and they each compute the union of the local and peer bitmap to determine the overall differences\&. .sp Bitmaps of very large devices are also relatively large, but they usually compress very well using run\-length encoding\&. This can save time and bandwidth for the bitmap transfers\&. .sp The \fBuse\-rle\fR parameter determines if run\-length encoding should be used\&. It is on by default since DRBD 8\&.4\&.0\&. .RE .PP \fB\-\-verify\-alg \fR\fB\fIhash\-algorithm\fR\fR .RS 4 Online verification (\fBdrbdadm verify\fR) computes and compares checksums of disk blocks (i\&.e\&., hash values) in order to detect if they differ\&. The \fBverify\-alg\fR parameter determines which algorithm to use for these checksums\&. It must be set to one of the secure hash algorithms supported by the kernel before online verify can be used; see the shash algorithms listed in /proc/crypto\&. .sp We recommend to schedule online verifications regularly during low\-load periods, for example once a month\&. Also see the notes on data integrity below\&. .RE .RE .PP \fBdrbdsetup\fR new\-path \fIresource\fR \fIpeer_node_id\fR \fIlocal\-addr\fR \fIremote\-addr\fR .RS 4 The \fBnew\-path\fR command creates a path within a \fIconnection\fR\&. The connection must have been created with \fBdrbdsetup new\-peer\fR\&. \fILocal_addr\fR and \fIremote_addr\fR refer to the local and remote protocol, network address, and port in the format [\fIaddress\-family\fR:]\fIaddress\fR[:\fIport\fR]\&. The address families \fBipv4\fR, \fBipv6\fR, \fBssocks\fR (Dolphin Interconnect Solutions\*(Aq "super sockets"), \fBsdp\fR (Infiniband Sockets Direct Protocol), and \fBsci\fR are supported (\fBsci\fR is an alias for \fBssocks\fR)\&. If no address family is specified, \fBipv4\fR is assumed\&. For all address families except \fBipv6\fR, the \fIaddress\fR uses IPv4 address notation (for example, 1\&.2\&.3\&.4)\&. For \fBipv6\fR, the address is enclosed in brackets and uses IPv6 address notation (for example, [fd01:2345:6789:abcd::1])\&. The \fIport\fR defaults to 7788\&. .RE .PP \fBdrbdsetup\fR connect \fIresource\fR \fIpeer_node_id\fR .RS 4 The \fBconnect\fR command activates a connection\&. That means that the DRBD driver will bind and listen on all local addresses of the connection\-\*(Aqs paths\&. It will begin to try to establish one or more paths of the connection\&. Available options: .PP \fB\-\-tentative\fR .RS 4 Only determine if a connection to the peer can be established and if a resync is necessary (and in which direction) without actually establishing the connection or starting the resync\&. Check the system log to see what DRBD would do without the \fB\-\-tentative\fR option\&. .RE .PP \fB\-\-discard\-my\-data\fR .RS 4 Discard the local data and resynchronize with the peer that has the most up\-to\-data data\&. Use this option to manually recover from a split\-brain situation\&. .RE .RE .PP \fBdrbdsetup\fR del\-peer \fIresource\fR \fIpeer_node_id\fR .RS 4 The \fBdel\-peer\fR command removes a connection from a \fIresource\fR\&. .RE .PP \fBdrbdsetup\fR del\-path \fIresource\fR \fIpeer_node_id\fR \fIlocal\-addr\fR \fIremote\-addr\fR .RS 4 The \fBdel\-path\fR command removes a path from a \fIconnection\fR\&. Please not that it fails if the path is necessary to keep a connected connection in tact\&. In order to remove all paths, disconnect the connection first\&. .RE .PP \fBdrbdsetup\fR cstate \fIresource\fR \fIpeer_node_id\fR .RS 4 Show the current state of a connection\&. The connection is identified by the node\-id of the peer; see the \fBdrbdsetup connect\fR command\&. .RE .PP \fBdrbdsetup\fR del\-minor \fIminor\fR .RS 4 Remove a replicated device\&. No lower\-level device may be attached; see \fBdrbdsetup detach\fR\&. .RE .PP \fBdrbdsetup\fR del\-resource \fIresource\fR .RS 4 Remove a resource\&. All volumes and connections must be removed first (\fBdrbdsetup del\-minor\fR, \fBdrbdsetup disconnect\fR)\&. Alternatively, \fBdrbdsetup down\fR can be used to remove a resource together with all its volumes and connections\&. .RE .PP \fBdrbdsetup\fR detach \fIminor\fR .RS 4 Detach the lower\-level device of a replicated device\&. Available options: .PP \fB\-\-force\fR .RS 4 Force the detach and return immediately\&. This puts the lower\-level device into failed state until all pending I/O has completed, and then detaches the device\&. Any I/O not yet submitted to the lower\-level device (for example, because I/O on the device was suspended) is assumed to have failed\&. .RE .sp .RE .PP \fBdrbdsetup\fR disconnect \fIresource\fR \fIpeer_node_id\fR .RS 4 Remove a connection to a peer host\&. The connection is identified by the node\-id of the peer; see the \fBdrbdsetup connect\fR command\&. .RE .PP \fBdrbdsetup\fR down {\fIresource\fR | \fIall\fR} .RS 4 Take a resource down by removing all volumes, connections, and the resource itself\&. .RE .PP \fBdrbdsetup\fR dstate \fIminor\fR .RS 4 Show the current disk state of a lower\-level device\&. .RE .PP \fBdrbdsetup\fR events2 {\fIresource\fR | \fIall\fR} .RS 4 Show the current state of all configured DRBD objects, followed by all changes to the state\&. .sp The output format is meant to be human as well as machine readable\&. The line starts with a word that indicates the kind of event: \fBexists\fR for an existing object; \fBcreate\fR, \fBdestroy\fR, and \fBchange\fR if an object is created, destroyed, or changed; or \fBcall\fR or \fBresponse\fR if an event handler is called or it returns\&. The second word indicates the object the event applies to: \fBresource\fR, \fBdevice\fR, \fBconnection\fR, \fBpeer\-device\fR, \fBhelper\fR, or a dash (\fB\-\fR) to indicate that the current state has been dumped completely\&. .sp The remaining words identify the object and describe the state that he object is in\&. Available options: .PP \fB\-\-now\fR .RS 4 Terminate after reporting the current state\&. The default is to continuously listen and report state changes\&. .RE .PP \fB\-\-statistics\fR .RS 4 Include statistics in the output\&. .RE .sp .RE .PP \fBdrbdsetup\fR get\-gi \fIresource\fR \fIpeer_node_id\fR \fIvolume\fR .RS 4 Show the data generation identifiers for a device on a particular connection\&. The device is identified by its volume number\&. The connection is identified by its endpoints; see the \fBdrbdsetup connect\fR command\&. .sp The output consists of the current UUID, bitmap UUID, and the first two history UUIDS, folowed by a set of flags\&. The current UUID and history UUIDs are device specific; the bitmap UUID and flags are peer device specific\&. This command only shows the first two history UUIDs\&. Internally, DRBD maintains one history UUID for each possible peer device\&. .RE .PP \fBdrbdsetup\fR invalidate \fIminor\fR .RS 4 Replace the local data of a device with that of a peer\&. All the local data will be marked out\-of\-sync, and a resync with the specified peer device will be initialted\&. .RE .PP \fBdrbdsetup\fR invalidate\-remote \fIresource\fR \fIpeer_node_id\fR \fIvolume\fR .RS 4 Replace a peer device\*(Aqs data of a resource with the local data\&. The peer device\*(Aqs data will be marked out\-of\-sync, and a resync from the local node to the specified peer will be initiated\&. .RE .PP \fBdrbdsetup\fR new\-current\-uuid \fIminor\fR .RS 4 Generate a new current UUID and rotates all other UUID values\&. This has at least two use cases, namely to skip the initial sync, and to reduce network bandwidth when starting in a single node configuration and then later (re\-)integrating a remote site\&. .sp Available option: .PP \fB\-\-clear\-bitmap\fR .RS 4 Clears the sync bitmap in addition to generating a new current UUID\&. .RE .sp This can be used to skip the initial sync, if you want to start from scratch\&. This use\-case does only work on "Just Created" meta data\&. Necessary steps: .sp .RS 4 .ie n \{\ \h'-04' 1.\h'+01'\c .\} .el \{\ .sp -1 .IP " 1." 4.2 .\} On \fIboth\fR nodes, initialize meta data and configure the device\&. .sp \fBdrbdadm create\-md \-\-force \fR\fB\fIres\fR\fR .RE .sp .RS 4 .ie n \{\ \h'-04' 2.\h'+01'\c .\} .el \{\ .sp -1 .IP " 2." 4.2 .\} They need to do the initial handshake, so they know their sizes\&. .sp \fBdrbdadm up \fR\fB\fIres\fR\fR .RE .sp .RS 4 .ie n \{\ \h'-04' 3.\h'+01'\c .\} .el \{\ .sp -1 .IP " 3." 4.2 .\} They are now Connected Secondary/Secondary Inconsistent/Inconsistent\&. Generate a new current\-uuid and clear the dirty bitmap\&. .sp \fBdrbdadm \-\-clear\-bitmap new\-current\-uuid \fR\fB\fIres\fR\fR .RE .sp .RS 4 .ie n \{\ \h'-04' 4.\h'+01'\c .\} .el \{\ .sp -1 .IP " 4." 4.2 .\} They are now Connected Secondary/Secondary UpToDate/UpToDate\&. Make one side primary and create a file system\&. .sp \fBdrbdadm primary \fR\fB\fIres\fR\fR .sp \fBmkfs \-t \fR\fB\fIfs\-type\fR\fR\fB $(drbdadm sh\-dev \fR\fB\fIres\fR\fR\fB)\fR .RE .sp One obvious side\-effect is that the replica is full of old garbage (unless you made them identical using other means), so any online\-verify is expected to find any number of out\-of\-sync blocks\&. .sp \fIYou must not use this on pre\-existing data!\fR Even though it may appear to work at first glance, once you switch to the other node, your data is toast, as it never got replicated\&. So \fIdo not leave out the mkfs\fR (or equivalent)\&. .sp This can also be used to shorten the initial resync of a cluster where the second node is added after the first node is gone into production, by means of disk shipping\&. This use\-case works on disconnected devices only, the device may be in primary or secondary role\&. .sp The necessary steps on the current active server are: .sp .RS 4 .ie n \{\ \h'-04' 1.\h'+01'\c .\} .el \{\ .sp -1 .IP " 1." 4.2 .\} \fBdrbdsetup new\-current\-uuid \-\-clear\-bitmap \fR\fB\fIminor\fR\fR\fB \fR .RE .sp .RS 4 .ie n \{\ \h'-04' 2.\h'+01'\c .\} .el \{\ .sp -1 .IP " 2." 4.2 .\} Take the copy of the current active server\&. E\&.g\&. by pulling a disk out of the RAID1 controller, or by copying with dd\&. You need to copy the actual data, and the meta data\&. .RE .sp .RS 4 .ie n \{\ \h'-04' 3.\h'+01'\c .\} .el \{\ .sp -1 .IP " 3." 4.2 .\} \fBdrbdsetup new\-current\-uuid \fR\fB\fIminor\fR\fR\fB \fR .RE .sp Now add the disk to the new secondary node, and join it to the cluster\&. You will get a resync of that parts that were changed since the first call to \fBdrbdsetup\fR in step 1\&. .RE .PP \fBdrbdsetup\fR new\-minor \fIresource\fR \fIminor\fR \fIvolume\fR .RS 4 Create a new replicated device within a resource\&. The command creates a block device inode for the replicated device (by default, /dev/drbd\fIminor\fR)\&. The \fIvolume\fR number identifies the device within the \fIresource\fR\&. .RE .PP \fBdrbdsetup\fR new\-resource \fIresource\fR \fInode_id\fR, .br \fBdrbdsetup\fR resource\-options \fIresource\fR .RS 4 The \fBnew\-resource\fR command creates a new resource\&. The \fBresource\-options\fR command changes the resource options of an existing resource\&. Available options: .PP \fB\-\-auto\-promote \fR\fB\fIbool\-value\fR\fR .RS 4 A resource must be promoted to primary role before any of its devices can be mounted or opened for writing\&. .sp Before DRBD 9, this could only be done explicitly ("drbdadm primary")\&. Since DRBD 9, the \fBauto\-promote\fR parameter allows to automatically promote a resource to primary role when one of its devices is mounted or opened for writing\&. As soon as all devices are unmounted or closed with no more remaining users, the role of the resource changes back to secondary\&. .sp Automatic promotion only succeeds if the cluster state allows it (that is, if an explicit \fBdrbdadm primary\fR command would succeed)\&. Otherwise, mounting or opening the device fails as it already did before DRBD 9: the \fBmount\fR(2) system call fails with errno set to EROFS (Read\-only file system); the \fBopen\fR(2) system call fails with errno set to EMEDIUMTYPE (wrong medium type)\&. .sp Irrespective of the \fBauto\-promote\fR parameter, if a device is promoted explicitly (\fBdrbdadm primary\fR), it also needs to be demoted explicitly (\fBdrbdadm secondary\fR)\&. .sp The \fBauto\-promote\fR parameter is available since DRBD 9\&.0\&.0, and defaults to \fByes\fR\&. .RE .PP \fB\-\-cpu\-mask \fR\fB\fIcpu\-mask\fR\fR .RS 4 Set the cpu affinity mask for DRBD kernel threads\&. The cpu mask is specified as a hexadecimal number\&. The default value is 0, which lets the scheduler decide which kernel threads run on which CPUs\&. CPU numbers in \fBcpu\-mask\fR which do not exist in the system are ignored\&. .RE .PP \fB\-\-on\-no\-data\-accessible \fR\fB\fIpolicy\fR\fR .RS 4 Determine how to deal with I/O requests when the requested data is not available locally or remotely (for example, when all disks have failed)\&. The defined policies are: .PP \fBio\-error\fR .RS 4 System calls fail with errno set to EIO\&. .RE .PP \fBsuspend\-io\fR .RS 4 The resource suspends I/O\&. I/O can be resumed by (re)attaching the lower\-level device, by connecting to a peer which has access to the data, or by forcing DRBD to resume I/O with \fBdrbdadm resume\-io \fR\fB\fIres\fR\fR\&. When no data is available, forcing I/O to resume will result in the same behavior as the \fBio\-error\fR policy\&. .RE .sp This setting is available since DRBD 8\&.3\&.9; the default policy is \fBio\-error\fR\&. .RE .PP \fB\-\-peer\-ack\-window \fR\fB\fIvalue\fR\fR .RS 4 On each node and for each device, DRBD maintains a bitmap of the differences between the local and remote data for each peer device\&. For example, in a three\-node setup (nodes A, B, C) each with a single device, every node maintains one bitmap for each of its peers\&. .sp When nodes receive write requests, they know how to update the bitmaps for the writing node, but not how to update the bitmaps between themselves\&. In this example, when a write request propagates from node A to B and C, nodes B and C know that they have the same data as node A, but not whether or not they both have the same data\&. .sp As a remedy, the writing node occasionally sends peer\-ack packets to its peers which tell them which state they are in relative to each other\&. .sp The \fBpeer\-ack\-window\fR parameter specifies how much data a primary node may send before sending a peer\-ack packet\&. A low value causes increased network traffic; a high value causes less network traffic but higher memory consumption on secondary nodes and higher resync times between the secondary nodes after primary node failures\&. (Note: peer\-ack packets may be sent due to other reasons as well, e\&.g\&. membership changes or expiry of the \fBpeer\-ack\-delay\fR timer\&.) .sp The default value for \fBpeer\-ack\-window\fR is 2 MiB, the default unit is sectors\&. This option is available since 9\&.0\&.0\&. .RE .PP \fB\-\-peer\-ack\-delay \fR\fB\fIexpiry\-time\fR\fR .RS 4 If after the last finished write request no new write request gets issued for \fIexpiry\-time\fR, then a peer\-ack packet is sent\&. If a new write request is issued before the timer expires, the timer gets reset to \fIexpiry\-time\fR\&. (Note: peer\-ack packets may be sent due to other reasons as well, e\&.g\&. membership changes or the \fBpeer\-ack\-window\fR option\&.) .sp This parameter may influence resync behavior on remote nodes\&. Peer nodes need to wait until they receive an peer\-ack for releasing a lock on an AL\-extent\&. Resync operations between peers may need to wait for for these locks\&. .sp The default value for \fBpeer\-ack\-delay\fR is 100 milliseconds, the default unit is milliseconds\&. This option is available since 9\&.0\&.0\&. .RE .sp .RE .PP \fBdrbdsetup\fR outdate \fIminor\fR .RS 4 Mark the data on a lower\-level device as outdated\&. This is used for fencing, and prevents the resource the device is part of from becoming primary in the future\&. See the \fB\-\-fencing\fR disk option\&. .RE .PP \fBdrbdsetup\fR pause\-sync \fIresource\fR \fIpeer_node_id\fR \fIvolume\fR .RS 4 Stop resynchronizing between a local and a peer device by setting the local pause flag\&. The resync can only resume if the pause flags on both sides of a connection are cleared\&. .RE .PP \fBdrbdsetup\fR primary \fIresource\fR .RS 4 Change the role of a node in a resource to primary\&. This allows the replicated devices in this resource to be mounted or opened for writing\&. Available options: .PP \fB\-\-overwrite\-data\-of\-peer\fR .RS 4 This option is an alias for the \fB\-\-force\fR option\&. .RE .PP \fB\-\-force\fR .RS 4 Force the resource to become primary even if some devices are not guaranteed to have up\-to\-date data\&. This option is used to turn one of the nodes in a newly created cluster into the primary node, or when manually recovering from a disaster\&. .sp Note that this can lead to split\-brain scenarios\&. Also, when forcefully turning an inconsistent device into an up\-to\-date device, it is highly recommended to use any integrity checks available (such as a filesystem check) to make sure that the device can at least be used without crashing the system\&. .RE .sp Note that DRBD usually only allows one node in a cluster to be in primary role at any time; this allows DRBD to coordinate access to the devices in a resource across nodes\&. The \fB\-\-allow\-two\-primaries\fR network option changes this; in that case, a mechanism outside of DRBD needs to coordinate device access\&. .RE .PP \fBdrbdsetup\fR resize \fIminor\fR .RS 4 Reexamine the size of the lower\-level devices of a replicated device on all nodes\&. This command is called after the lower\-level devices on all nodes have been grown to adjust the size of the replicated device\&. Available options: .PP \fB\-\-assume\-peer\-has\-space\fR .RS 4 Resize the device even if some of the peer devices are not connected at the moment\&. DRBD will try to resize the peer devices when they next connect\&. It will refuse to connect to a peer device which is too small\&. .RE .PP \fB\-\-assume\-clean\fR .RS 4 Do not resynchronize the added disk space; instead, assume that it is identical on all nodes\&. This option can be used when the disk space is uninitialized and differences do not matter, or when it is known to be identical on all nodes\&. See the \fBdrbdsetup verify\fR command\&. .RE .PP \fB\-\-size \fR\fB\fIval\fR\fR .RS 4 This option can be used to online shrink the usable size of a drbd device\&. It\*(Aqs the users responsibility to make sure that a file system on the device is not truncated by that operation\&. .RE .PP \fB\-\-al\-stripes \fR\fB\fIval\fR\fR \fB\-\-al\-stripes \fR\fB\fIval\fR\fR .RS 4 These options may be used to change the layout of the activity log online\&. In case of internal meta data this may invovle shrinking the user visible size at the same time (unsing the \fB\-\-size\fR) or increasing the avalable space on the backing devices\&. .RE .sp .RE .PP \fBdrbdsetup\fR resume\-io \fIminor\fR .RS 4 Resume I/O on a replicated device\&. See the \fB\-\-fencing\fR net option\&. .RE .PP \fBdrbdsetup\fR resume\-sync \fIresource\fR \fIpeer_node_id\fR \fIvolume\fR .RS 4 Allow resynchronization to resume by clearing the local sync pause flag\&. .RE .PP \fBdrbdsetup\fR role \fIresource\fR .RS 4 Show the current role of a resource\&. .RE .PP \fBdrbdsetup\fR secondary \fIresource\fR .RS 4 Change the role of a node in a resource to secondary\&. This command fails if the replicated device is in use\&. .RE .PP \fBdrbdsetup\fR show {\fIresource\fR | \fIall\fR} .RS 4 Show the current configuration of a resource, or of all resources\&. Available options: .PP \fB\-\-show\-defaults\fR .RS 4 Show all configuration parameters, even the ones with default values\&. Normally, parameters with default values are not shown\&. .RE .sp .RE .PP \fBdrbdsetup\fR show\-gi \fIresource\fR \fIpeer_node_id\fR \fIvolume\fR .RS 4 Show the data generation identifiers for a device on a particular connection\&. In addition, explain the output\&. The output otherwise is the same as in the \fBdrbdsetup get\-gi\fR command\&. .RE .PP \fBdrbdsetup\fR state .RS 4 This is an alias for \fBdrbdsetup role\fR\&. Deprecated\&. .RE .PP \fBdrbdsetup\fR status {\fIresource\fR | \fIall\fR} .RS 4 Show the status of a resource, or of all resources\&. The output consists of one paragraph for each configured resource\&. Each paragraph contains one line for each resource, followed by one line for each device, and one line for each connection\&. The device and connection lines are indented\&. The connection lines are followed by one line for each peer device; these lines are indented against the connection line\&. .sp Long lines are wrapped around at terminal width, and indented to indicate how the lines belongs together\&. Available options: .PP \fB\-\-verbose\fR .RS 4 Include more information in the output even when it is likely redundant or irrelevant\&. .RE .PP \fB\-\-statistics\fR .RS 4 Include data transfer statistics in the output\&. .RE .PP \fB\-\-color=\fR\fB{always | auto | never}\fR\fB \fR .RS 4 Colorize the output\&. With \fB\-\-color=auto\fR, \fBdrbdsetup\fR emits color codes only when standard output is connected to a terminal\&. .RE .sp For example, the non\-verbose output for a resource with only one connection and only one volume could look like this: .sp .if n \{\ .RS 4 .\} .nf drbd0 role:Primary disk:UpToDate host2\&.example\&.com role:Secondary disk:UpToDate .fi .if n \{\ .RE .\} .sp With the \fB\-\-verbose\fR option, the same resource could be reported as: .sp .if n \{\ .RS 4 .\} .nf drbd0 node\-id:1 role:Primary suspended:no volume:0 minor:1 disk:UpToDate blocked:no host2\&.example\&.com local:ipv4:192\&.168\&.123\&.4:7788 peer:ipv4:192\&.168\&.123\&.2:7788 node\-id:0 connection:WFReportParams role:Secondary congested:no volume:0 replication:Connected disk:UpToDate resync\-suspended:no .fi .if n \{\ .RE .\} .sp .RE .PP \fBdrbdsetup\fR suspend\-io \fIminor\fR .RS 4 Suspend I/O on a replicated device\&. It is not usually necessary to use this command\&. .RE .PP \fBdrbdsetup\fR verify \fIresource\fR \fIpeer_node_id\fR \fIvolume\fR .RS 4 Start online verification, change which part of the device will be verified, or stop online verification\&. The command requires the specified peer to be connected\&. .sp Online verification compares each disk block on the local and peer node\&. Blocks which differ between the nodes are marked as out\-of\-sync, but they are \fInot\fR automatically brought back into sync\&. To bring them into sync, the resource must be disconnected and reconnected\&. Progress can be monitored in the output of \fBdrbdsetup status \-\-statistics\fR\&. Available options: .PP \fB\-\-start \fR\fB\fIposition\fR\fR .RS 4 Define where online verification should start\&. This parameter is ignored if online verification is already in progress\&. If the start parameter is not specified, online verification will continue where it was interrupted (if the connection to the peer was lost while verifying), after the previous stop sector (if the previous online verification has finished), or at the beginning of the device (if the end of the device was reached, or online verify has not run before)\&. .sp The position on disk is specified in disk sectors (512 bytes) by default\&. .RE .PP \fB\-\-stop \fR\fB\fIposition\fR\fR .RS 4 Define where online verification should stop\&. If online verification is already in progress, the stop position of the active online verification process is changed\&. Use this to stop online verification\&. .sp The position on disk is specified in disk sectors (512 bytes) by default\&. .RE .sp Also see the notes on data integrity in the \fBdrbd.conf\fR(5) manual page\&. .RE .PP \fBdrbdsetup\fR wait\-connect\-volume \fIresource\fR \fIpeer_node_id\fR \fIvolume\fR, .br \fBdrbdsetup\fR wait\-connect\-connection \fIresource\fR \fIpeer_node_id\fR, .br \fBdrbdsetup\fR wait\-connect\-resource \fIresource\fR, .br \fBdrbdsetup\fR wait\-sync\-volume \fIresource\fR \fIpeer_node_id\fR \fIvolume\fR, .br \fBdrbdsetup\fR wait\-sync\-connection \fIresource\fR \fIpeer_node_id\fR, .br \fBdrbdsetup\fR wait\-sync\-resource \fIresource\fR .RS 4 The \fBwait\-connect\-*\fR commands waits until a device on a peer is visible\&. The \fBwait\-sync\-*\fR commands waits until a device on a peer is up to date\&. Available options for both commands: .PP \fB\-\-degr\-wfc\-timeout \fR\fB\fItimeout\fR\fR .RS 4 Define how long to wait until all peers are connected in case the cluster consisted of a single node only when the system went down\&. This parameter is usually set to a value smaller than \fBwfc\-timeout\fR\&. The assumption here is that peers which were unreachable before a reboot are less likely to be be reachable after the reboot, so waiting is less likely to help\&. .sp The timeout is specified in seconds\&. The default value is 0, which stands for an infinite timeout\&. Also see the \fBwfc\-timeout\fR parameter\&. .RE .PP \fB\-\-outdated\-wfc\-timeout \fR\fB\fItimeout\fR\fR .RS 4 Define how long to wait until all peers are connected if all peers were outdated when the system went down\&. This parameter is usually set to a value smaller than \fBwfc\-timeout\fR\&. The assumption here is that an outdated peer cannot have become primary in the meantime, so we don\*(Aqt need to wait for it as long as for a node which was alive before\&. .sp The timeout is specified in seconds\&. The default value is 0, which stands for an infinite timeout\&. Also see the \fBwfc\-timeout\fR parameter\&. .RE .PP \fB\-\-wait\-after\-sb\fR .RS 4 This parameter causes DRBD to continue waiting in the init script even when a split\-brain situation has been detected, and the nodes therefore refuse to connect to each other\&. .RE .PP \fB\-\-wfc\-timeout \fR\fB\fItimeout\fR\fR .RS 4 Define how long the init script waits until all peers are connected\&. This can be useful in combination with a cluster manager which cannot manage DRBD resources: when the cluster manager starts, the DRBD resources will already be up and running\&. With a more capable cluster manager such as Pacemaker, it makes more sense to let the cluster manager control DRBD resources\&. The timeout is specified in seconds\&. The default value is 0, which stands for an infinite timeout\&. Also see the \fBdegr\-wfc\-timeout\fR parameter\&. .RE .sp .RE .PP \fBdrbdsetup\fR forget\-peer \fIresource\fR \fIpeer_node_id\fR .RS 4 The \fBforget\-peer\fR command removes all traces of a peer node from the meta\-data\&. It frees a bitmap slot in the meta\-data and make it avalable for futher bitmap slot allocation in case a so\-far never seen node connects\&. .sp The connection must be taken down before this command may be used\&. In case the peer re\-connects at a later point a bit\-map based resync will be turned into a full\-sync\&. .RE .SH "EXAMPLES" .PP Please see the \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2 for examples\&. .SH "VERSION" .sp This document was revised for version 9\&.0\&.0 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2012 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbd.conf\fR(5), \fBdrbd\fR(8), \fBdrbddisk\fR(8), \fBdrbdadm\fR(8), \m[blue]\fBDRBD User\*(Aqs Guide\fR\m[]\&\s-2\u[1]\d\s+2, \m[blue]\fBDRBD Web Site\fR\m[]\&\s-2\u[2]\d\s+2 .SH "NOTES" .IP " 1." 4 DRBD User's Guide .RS 4 \%http://www.drbd.org/users-guide/ .RE .IP " 2." 4 DRBD Web Site .RS 4 \%http://www.drbd.org/ .RE drbd-utils-8.9.10/documentation/v9/drbdsetup-options.xml0000644000175000017500000017743313002133622023203 0ustar apoikosapoikos drbd.conf al-extents DRBD automatically maintains a "hot" or "active" disk area likely to be written to again soon based on the recent write activity. The "active" disk area can be written to immediately, while "inactive" disk areas must be "activated" first, which requires a meta-data write. We also refer to this active disk area as the "activity log". The activity log saves meta-data writes, but the whole log must be resynced upon recovery of a failed node. The size of the activity log is a major factor of how long a resync will take and how fast a replicated disk will become consistent after a crash. The activity log consists of a number of 4-Megabyte segments; the al-extents parameter determines how many of those segments can be active at the same time. The default value for al-extents is 1237, with a minimum of 7 and a maximum of 65536. Note that the effective maximum may be smaller, depending on how you created the device meta data, see also drbdmeta8 The effective maximum is 919 * (available on-disk activity-log ring-buffer area/4kB -1), the default 32kB ring-buffer effects a maximum of 6433 (covers more than 25 GiB of data) We recommend to keep this well within the amount your backend storage and replication link are able to resync inside of about 5 minutes. drbd.conf al-updates With this parameter, the activity log can be turned off entirely (see the parameter). This will speed up writes because fewer meta-data writes will be necessary, but the entire device needs to be resynchronized opon recovery of a failed primary node. The default value for is . Dynamically control the resync speed. This mechanism is enabled by setting the parameter to a positive value. The goal is to either fill the buffers along the data path with a defined amount of data if is defined, or to have a defined delay along the path if is defined. The maximum bandwidth is limited by the parameter. The parameter defines how fast drbd adapts to changes in the resync speed. It should be set to five times the network round-trip time or more. Common values for for "normal" data paths range from 4K to 100K. If drbd-proxy is used, it is advised to use instead of . The parameter is used if the parameter is undefined or set to 0. The parameter should be set to five times the network round-trip time or more. The option should be set to either the bandwidth available between the DRBD-hosts and the machines hosting DRBD-proxy, or to the available disk bandwidth. The default values of these parameters are: = 20 (in units of 0.1 seconds), = 0 (in units of sectors), = 1 (in units of 0.1 seconds), and = 102400 (in units of KiB/s). Dynamic resync speed control is available since DRBD 8.3.9. A node which is primary and sync-source has to schedule application I/O requests and resync I/O requests. The parameter limits how much bandwidth is available for resync I/O; the remaining bandwidth is used for application I/O. A value of 0 means that there is no limit on the resync I/O bandwidth. This can slow down application I/O significantly. Use a value of 1 (1 KiB/s) for the lowest possible resync rate. The default value of is 4096, in units of KiB/s. drbd.conf disk-barrier drbd.conf disk-flushes drbd.conf disk-drain DRBD has three methods of handling the ordering of dependent write requests: Use disk barriers to make sure that requests are written to disk in the right order. Barriers ensure that all requests submitted before a barrier make it to the disk before any requests submitted after the barrier. This is implemented using 'tagged command queuing' on SCSI devices and 'native command queuing' on SATA devices. Only some devices and device stacks support this method. The device mapper (LVM) only supports barriers in some configurations. Note that on systems which do not support disk barriers, enabling this option can lead to data loss or corruption. Until DRBD 8.4.1, was turned on if the I/O stack below DRBD did support barriers. Kernels since linux-2.6.36 (or 2.6.32 RHEL6) no longer allow to detect if barriers are supported. Since drbd-8.4.2, this option is off by default and needs to be enabled explicitly. Use disk flushes between dependent write requests, also referred to as 'force unit access' by drive vendors. This forces all data to disk. This option is enabled by default. Wait for the request queue to "drain" (that is, wait for the requests to finish) before submitting a dependent write request. This method requires that requests are stable on disk when they finish. Before DRBD 8.0.9, this was the only method implemented. This option is enabled by default. Do not disable in production environments. From these three methods, drbd will use the first that is enabled and supported by the backing storage device. If all three of these options are turned off, DRBD will submit write requests without bothering about dependencies. Depending on the I/O stack, write requests can be reordered, and they can be submitted in a different order on different cluster nodes. This can result in data loss or corruption. Therefore, turning off all three methods of controlling write ordering is strongly discouraged. A general guideline for configuring write ordering is to use disk barriers or disk flushes when using ordinary disks (or an ordinary disk array) with a volatile write cache. On storage without cache or with a battery backed write cache, disk draining can be a reasonable choice. drbd.conf disk-timeout If the lower-level device on which a DRBD device stores its data does not finish an I/O request within the defined , DRBD treats this as a failure. The lower-level device is detached, and the device's disk state advances to Diskless. If DRBD is connected to one or more peers, the failed request is passed on to one of them. This option is dangerous and may lead to kernel panic! "Aborting" requests, or force-detaching the disk, is intended for completely blocked/hung local backing devices which do no longer complete requests at all, not even do error completions. In this situation, usually a hard-reset and failover is the only way out. By "aborting", basically faking a local error-completion, we allow for a more graceful swichover by cleanly migrating services. Still the affected node has to be rebooted "soon". By completing these requests, we allow the upper layers to re-use the associated data pages. If later the local backing device "recovers", and now DMAs some data from disk into the original request pages, in the best case it will just put random data into unused pages; but typically it will corrupt meanwhile completely unrelated data, causing all sorts of damage. Which means delayed successful completion, especially for READ requests, is a reason to panic(). We assume that a delayed *error* completion is OK, though we still will complain noisily about it. The default value of is 0, which stands for an infinite timeout. Timeouts are specified in units of 0.1 seconds. This option is available since DRBD 8.3.12. drbd.conf fencing is a preventive measure to avoid situations where both nodes are primary and disconnected. This is also known as a split-brain situation. DRBD supports the following fencing policies: No fencing actions are taken. This is the default policy. If a node becomes a disconnected primary, it tries to fence the peer. This is done by calling the handler. The handler is supposed to reach the peer over an alternative communication path and call '' there. If a node becomes a disconnected primary, it freezes all its IO operations and calls its fence-peer handler. The fence-peer handler is supposed to reach the peer over an alternative communication path and call '' there. In case it cannot do that, it should stonith the peer. IO is resumed as soon as the situation is resolved. In case the fence-peer handler fails, I/O can be resumed manually with ''. drbd.conf md-flushes Enable disk flushes and disk barriers on the meta-data device. This option is enabled by default. See the parameter. drbd.conf on-io-error Configure how DRBD reacts to I/O errors on a lower-level device. The following policies are defined: Change the disk status to Inconsistent, mark the failed block as inconsistent in the bitmap, and retry the I/O operation on a remote cluster node. Call the handler (see the section). Detach the lower-level device and continue in diskless mode. drbd.conf read-balancing Distribute read requests among cluster nodes as defined by policy. The supported policies are (the default), , , , , , , , , and . This option is available since DRBD 8.4.1. drbd.conf discard-zeroes-if-aligned There are several aspects to discard/trim/unmap support on linux block devices. Even if discard is supported in general, it may fail silently, or may partially ignore discard requests. Devices also announce whether reading from unmapped blocks returns defined data (usually zeroes), or undefined data (possibly old data, possibly garbage). If on different nodes, DRBD is backed by devices with differing discard characteristics, discards may lead to data divergence (old data or garbage left over on one backend, zeroes due to unmapped areas on the other backend). Online verify would now potentially report tons of spurious differences. While probably harmless for most use cases (fstrim on a file system), DRBD cannot have that. To play safe, we have to disable discard support, if our local backend (on a Primary) does not support "discard_zeroes_data=true". We also have to translate discards to explicit zero-out on the receiving side, unless the receiving side (Secondary) supports "discard_zeroes_data=true", thereby allocating areas what were supposed to be unmapped. There are some devices (notably the LVM/DM thin provisioning) that are capable of discard, but announce discard_zeroes_data=false. In the case of DM-thin, discards aligned to the chunk size will be unmapped, and reading from unmapped sectors will return zeroes. However, unaligned partial head or tail areas of discard requests will be silently ignored. If we now add a helper to explicitly zero-out these unaligned partial areas, while passing on the discard of the aligned full chunks, we effectively achieve discard_zeroes_data=true on such devices. Setting to will allow DRBD to use discards, and to announce discard_zeroes_data=true, even on backends that announce discard_zeroes_data=false. Setting to will cause DRBD to always fall-back to zero-out on the receiving side, and to not even announce discard capabilities on the Primary, if the respective backend announces discard_zeroes_data=false. We used to ignore the discard_zeroes_data setting completely. To not break established and expected behaviour, and suddenly cause fstrim on thin-provisioned LVs to run out-of-space instead of freeing up space, the default value is . This option is available since 8.4.7. drbd.conf rs-discard-granularity When is set to a non zero, positive value then DRBD tries to do a resync operation in requests of this size. In case such a block contains only zero bytes on the sync source node, the sync target node will issue a discard/trim/unmap command for the area. The value is constrained by the discard granularity of the backing block device. In case is not a multiplier of the discard granularity of the backing block device DRBD rounds it up. The feature only gets active if the backing block device reads back zeroes after a discard command. The default value of is 0. This option is available since 8.4.7. drbd.conf resync-after Define that a device should only resynchronize after the specified other device. By default, no order between devices is defined, and all devices will resynchronize in parallel. Depending on the configuration of the lower-level devices, and the available network and disk bandwidth, this can slow down the overall resync process. This option can be used to form a chain or tree of dependencies among devices. drbd.conf resync-rate Define how much bandwidth DRBD may use for resynchronizing. DRBD allows "normal" application I/O even during a resync. If the resync takes up too much bandwidth, application I/O can become very slow. This parameter allows to avoid that. Please note this is option only works when the dynamic resync controller is disabled. Specify the size of the lower-level device explicitly instead of determining it automatically. The device size must be determined once and is remembered for the lifetime of the device. In order to determine it automatically, all the lower-level devices on all nodes must be attached, and all nodes must be connected. If the size is specified explicitly, this is not necessary. The value is assumed to be in units of sectors (512 bytes) by default. drbd.conf dialog-refresh The DRBD init script can be used to configure and start DRBD devices, which can involve waiting for other cluster nodes. While waiting, the init script shows the remaining waiting time. The defines the number of seconds between updates of that countdown. The default value is 1; a value of 0 turns off the countdown. drbd.conf disable-ip-verification Normally, DRBD verifies that the IP addresses in the configuration match the host names. Use the parameter to disable these checks. drbd.conf usage-count A explained on DRBD's Online Usage Counter web page, DRBD includes a mechanism for anonymously counting how many installations are using which versions of DRBD. The results are available on the web page for anyone to see. This parameter defines if a cluster node participates in the usage counter; the supported values are , , and (ask the user, the default). We would like to ask users to participate in the online usage counter as this provides us valuable feedback for steering the development of DRBD. drbd.conf after-sb-0pri Define how to react if a split-brain scenario is detected and none of the two nodes is in primary role. (We detect split-brain scenarios when two nodes connect; split-brain decisions are always between two nodes.) The defined policies are: No automatic resynchronization; simply disconnect. Resynchronize from the node which became primary first () or last (). If both nodes became primary independently, the policy is used. If only one of the nodes wrote data since the split brain situation was detected, resynchronize from this node to the other. If both nodes wrote data, disconnect. Resynchronize from the node with more modified blocks. Always resynchronize to the named node. drbd.conf after-sb-1pri Define how to react if a split-brain scenario is detected, with one node in primary role and one node in secondary role. (We detect split-brain scenarios when two nodes connect, so split-brain decisions are always among two nodes.) The defined policies are: No automatic resynchronization, simply disconnect. Discard the data on the secondary node if the algorithm would also discard the data on the secondary node. Otherwise, disconnect. Always take the decision of the algorithm, even if it causes an erratic change of the primary's view of the data. This is only useful if a single-node file system (i.e., not OCFS2 or GFS) with the flag is used. This option can cause the primary node to crash, and should not be used. Discard the data on the secondary node. Always take the decision of the algorithm. If the decision is to discard the data on the primary node, call the handler on the primary node. drbd.conf after-sb-2pri Define how to react if a split-brain scenario is detected and both nodes are in primary role. (We detect split-brain scenarios when two nodes connect, so split-brain decisions are always among two nodes.) The defined policies are: No automatic resynchronization, simply disconnect. See the policy for . Call the helper program on one of the machines unless that machine can demote to secondary. The helper program is expected to reboot the machine, which brings the node into a secondary role. Which machine runs the helper program is determined by the strategy. drbd.conf allow-two-primaries The most common way to configure DRBD devices is to allow only one node to be primary (and thus writable) at a time. In some scenarios it is preferable to allow two nodes to be primary at once; a mechanism outside of DRBD then must make sure that writes to the shared, replicated device happen in a coordinated way. This can be done with a shared-storage cluster file system like OCFS2 and GFS, or with virtual machine images and a virtual machine manager that can migrate virtual machines between physical machines. The parameter tells DRBD to allow two nodes to be primary at the same time. Never enable this option when using a non-distributed file system; otherwise, data corruption and node crashes will result! Normally the automatic after-split-brain policies are only used if current states of the UUIDs do not indicate the presence of a third node. With this option you request that the automatic after-split-brain policies are used as long as the data sets of the nodes are somehow related. This might cause a full sync, if the UUIDs indicate the presence of a third node. (Or double faults led to strange UUID sets.) drbd.conf connect-int As soon as a connection between two nodes is configured with drbdsetup connect, DRBD immediately tries to establish the connection. If this fails, DRBD waits for seconds and then repeats. The default value of is 10 seconds. drbd.conf cram-hmac-alg Configure the hash-based message authentication code (HMAC) or secure hash algorithm to use for peer authentication. The kernel supports a number of different algorithms, some of which may be loadable as kernel modules. See the shash algorithms listed in /proc/crypto. By default, is unset. Peer authentication also requires a to be configured. drbd.conf csums-alg Normally, when two nodes resynchronize, the sync target requests a piece of out-of-sync data from the sync source, and the sync source sends the data. With many usage patterns, a significant number of those blocks will actually be identical. When a algorithm is specified, when requesting a piece of out-of-sync data, the sync target also sends along a hash of the data it currently has. The sync source compares this hash with its own version of the data. It sends the sync target the new data if the hashes differ, and tells it that the data are the same otherwise. This reduces the network bandwidth required, at the cost of higher cpu utilization and possibly increased I/O on the sync target. The can be set to one of the secure hash algorithms supported by the kernel; see the shash algorithms listed in /proc/crypto. By default, is unset. drbd.conf csums-after-crash-only Enabling this option (and csums-alg, above) makes it possible to use the checksum based resync only for the first resync after primary crash, but not for later "network hickups". In most cases, block that are marked as need-to-be-resynced are in fact changed, so calculating checksums, and both reading and writing the blocks on the resync target is all effective overhead. The advantage of checksum based resync is mostly after primary crash recovery, where the recovery marked larger areas (those covered by the activity log) as need-to-be-resynced, just in case. Introduced in 8.4.5. alg drbd.conf data-integrity-alg DRBD normally relies on the data integrity checks built into the TCP/IP protocol, but if a data integrity algorithm is configured, it will additionally use this algorithm to make sure that the data received over the network match what the sender has sent. If a data integrity error is detected, DRBD will close the network connection and reconnect, which will trigger a resync. The can be set to one of the secure hash algorithms supported by the kernel; see the shash algorithms listed in /proc/crypto. By default, this mechanism is turned off. Because of the CPU overhead involved, we recommend not to use this option in production environments. Also see the notes on data integrity below. drbd.conf ko-count If a secondary node fails to complete a write request in times the parameter, it is excluded from the cluster. The primary node then sets the connection to this secondary node to Standalone. To disable this feature, you should explicitly set it to 0; defaults may change between versions. drbd.conf max-buffers Limits the memory usage per DRBD minor device on the receiving side, or for internal buffers during resync or online-verify. Unit is PAGE_SIZE, which is 4 KiB on most systems. The minimum possible setting is hard coded to 32 (=128 KiB). These buffers are used to hold data blocks while they are written to/read from disk. To avoid possible distributed deadlocks on congestion, this setting is used as a throttle threshold rather than a hard limit. Once more than max-buffers pages are in use, further allocation from this pool is throttled. You want to increase max-buffers if you cannot saturate the IO backend on the receiving side. drbd.conf max-epoch-size Define the maximum number of write requests DRBD may issue before issuing a write barrier. The default value is 2048, with a minimum of 1 and a maximum of 20000. Setting this parameter to a value below 10 is likely to decrease performance. By default, DRBD blocks when the TCP send queue is full. This prevents applications from generating further write requests until more buffer space becomes available again. When DRBD is used together with DRBD-proxy, it can be better to use the policy, which can switch DRBD into ahead/behind mode before the send queue is full. DRBD then records the differences between itself and the peer in its bitmap, but it no longer replicates them to the peer. When enough buffer space becomes available again, the node resynchronizes with the peer and switches back to normal replication. This has the advantage of not blocking application I/O even when the queues fill up, and the disadvantage that peer nodes can fall behind much further. Also, while resynchronizing, peer nodes will become inconsistent. The available congestion policies are (the default) and . The parameter defines how much data is allowed to be "in flight" in this connection. The default value is 0, which disables this mechanism of congestion control, with a maximum of 10 GiBytes. The parameter defines how many bitmap extents may be active before switching into ahead/behind mode, with the same default and limits as the parameter. The parameter is effective only when set to a value smaller than . Ahead/behind mode is available since DRBD 8.3.10. drbd.conf ping-int When the TCP/IP connection to a peer is idle for more than seconds, DRBD will send a keep-alive packet to make sure that a failed peer or network connection is detected reasonably soon. The default value is 10 seconds, with a minimum of 1 and a maximum of 120 seconds. The unit is seconds. drbd.conf ping-timeout Define the timeout for replies to keep-alive packets. If the peer does not reply within , DRBD will close and try to reestablish the connection. The default value is 0.5 seconds, with a minimum of 0.1 seconds and a maximum of 3 seconds. The unit is tenths of a second. drbd.conf socket-check-timeout In setups involving a DRBD-proxy and connections that experience a lot of buffer-bloat it might be necessary to set to an unusual high value. By default DRBD uses the same value to wait if a newly established TCP-connection is stable. Since the DRBD-proxy is usually located in the same data center such a long wait time may hinder DRBD's connect process. In such setups should be set to at least to the round trip time between DRBD and DRBD-proxy. I.e. in most cases to 1. The default unit is tenths of a second, the default value is 0 (which causes DRBD to use the value of instead). Introduced in 8.4.5. drbd.conf protocol Use the specified protocol on this connection. The supported protocols are: Writes to the DRBD device complete as soon as they have reached the local disk and the TCP/IP send buffer. Writes to the DRBD device complete as soon as they have reached the local disk, and all peers have acknowledged the receipt of the write requests. Writes to the DRBD device complete as soon as they have reached the local and all remote disks. drbd.conf rcvbuf-size Configure the size of the TCP/IP receive buffer. A value of 0 (the default) causes the buffer size to adjust dynamically. This parameter usually does not need to be set, but it can be set to a value up to 10 MiB. The default unit is bytes. policy drbd.conf rr-conflict This option helps to solve the cases when the outcome of the resync decision is incompatible with the current role assignment in the cluster. The defined policies are: No automatic resynchronization, simply disconnect. Resync to the primary node is allowed, violating the assumption that data on a block device are stable for one of the nodes. Do not use this option, it is dangerous. Call the handler on one of the machines. The handler is expected to reboot the machine, which puts it into secondary role. drbd.conf shared-secret Configure the shared secret used for peer authentication. The secret is a string of up to 64 characters. Peer authentication also requires the parameter to be set. drbd.conf sndbuf-size Configure the size of the TCP/IP send buffer. Since DRBD 8.0.13 / 8.2.7, a value of 0 (the default) causes the buffer size to adjust dynamically. Values below 32 KiB are harmful to the throughput on this connection. Large buffer sizes can be useful especially when protocol A is used over high-latency networks; the maximum value supported is 10 MiB. drbd.conf tcp-cork By default, DRBD uses the TCP_CORK socket option to prevent the kernel from sending partial messages; this results in fewer and bigger packets on the network. Some network stacks can perform worse with this optimization. On these, the parameter can be used to turn this optimization off. drbd.conf timeout Define the timeout for replies over the network: if a peer node does not send an expected reply within the specified , it is considered dead and the TCP/IP connection is closed. The timeout value must be lower than and lower than . The default is 6 seconds; the value is specified in tenths of a second. drbd.conf use-rle Each replicated device on a cluster node has a separate bitmap for each of its peer devices. The bitmaps are used for tracking the differences between the local and peer device: depending on the cluster state, a disk range can be marked as different from the peer in the device's bitmap, in the peer device's bitmap, or in both bitmaps. When two cluster nodes connect, they exchange each other's bitmaps, and they each compute the union of the local and peer bitmap to determine the overall differences. Bitmaps of very large devices are also relatively large, but they usually compress very well using run-length encoding. This can save time and bandwidth for the bitmap transfers. The parameter determines if run-length encoding should be used. It is on by default since DRBD 8.4.0. Online verification (drbdadm verify) computes and compares checksums of disk blocks (i.e., hash values) in order to detect if they differ. The parameter determines which algorithm to use for these checksums. It must be set to one of the secure hash algorithms supported by the kernel before online verify can be used; see the shash algorithms listed in /proc/crypto. We recommend to schedule online verifications regularly during low-load periods, for example once a month. Also see the notes on data integrity below. Discard the local data and resynchronize with the peer that has the most up-to-data data. Use this option to manually recover from a split-brain situation. Only determine if a connection to the peer can be established and if a resync is necessary (and in which direction) without actually establishing the connection or starting the resync. Check the system log to see what DRBD would do without the option. drbd.conf auto-promote A resource must be promoted to primary role before any of its devices can be mounted or opened for writing. Before DRBD 9, this could only be done explicitly ("drbdadm primary"). Since DRBD 9, the parameter allows to automatically promote a resource to primary role when one of its devices is mounted or opened for writing. As soon as all devices are unmounted or closed with no more remaining users, the role of the resource changes back to secondary. Automatic promotion only succeeds if the cluster state allows it (that is, if an explicit drbdadm primary command would succeed). Otherwise, mounting or opening the device fails as it already did before DRBD 9: the mount2 system call fails with errno set to EROFS (Read-only file system); the open2 system call fails with errno set to EMEDIUMTYPE (wrong medium type). Irrespective of the parameter, if a device is promoted explicitly (drbdadm primary), it also needs to be demoted explicitly (drbdadm secondary). The parameter is available since DRBD 9.0.0, and defaults to yes. drbd.conf cpu-mask Set the cpu affinity mask for DRBD kernel threads. The cpu mask is specified as a hexadecimal number. The default value is 0, which lets the scheduler decide which kernel threads run on which CPUs. CPU numbers in which do not exist in the system are ignored. Determine how to deal with I/O requests when the requested data is not available locally or remotely (for example, when all disks have failed). The defined policies are: System calls fail with errno set to EIO. The resource suspends I/O. I/O can be resumed by (re)attaching the lower-level device, by connecting to a peer which has access to the data, or by forcing DRBD to resume I/O with drbdadm resume-io res. When no data is available, forcing I/O to resume will result in the same behavior as the policy. This setting is available since DRBD 8.3.9; the default policy is . drbd.conf peer-ack-window On each node and for each device, DRBD maintains a bitmap of the differences between the local and remote data for each peer device. For example, in a three-node setup (nodes A, B, C) each with a single device, every node maintains one bitmap for each of its peers. When nodes receive write requests, they know how to update the bitmaps for the writing node, but not how to update the bitmaps between themselves. In this example, when a write request propagates from node A to B and C, nodes B and C know that they have the same data as node A, but not whether or not they both have the same data. As a remedy, the writing node occasionally sends peer-ack packets to its peers which tell them which state they are in relative to each other. The parameter specifies how much data a primary node may send before sending a peer-ack packet. A low value causes increased network traffic; a high value causes less network traffic but higher memory consumption on secondary nodes and higher resync times between the secondary nodes after primary node failures. (Note: peer-ack packets may be sent due to other reasons as well, e.g. membership changes or expiry of the timer.) The default value for is 2 MiB, the default unit is sectors. This option is available since 9.0.0. drbd.conf peer-ack-delay If after the last finished write request no new write request gets issued for expiry-time, then a peer-ack packet is sent. If a new write request is issued before the timer expires, the timer gets reset to expiry-time. (Note: peer-ack packets may be sent due to other reasons as well, e.g. membership changes or the option.) This parameter may influence resync behavior on remote nodes. Peer nodes need to wait until they receive an peer-ack for releasing a lock on an AL-extent. Resync operations between peers may need to wait for for these locks. The default value for is 100 milliseconds, the default unit is milliseconds. This option is available since 9.0.0. drbd.conf degr-wfc-timeout Define how long to wait until all peers are connected in case the cluster consisted of a single node only when the system went down. This parameter is usually set to a value smaller than . The assumption here is that peers which were unreachable before a reboot are less likely to be be reachable after the reboot, so waiting is less likely to help. The timeout is specified in seconds. The default value is 0, which stands for an infinite timeout. Also see the parameter. drbd.conf outdated-wfc-timeout Define how long to wait until all peers are connected if all peers were outdated when the system went down. This parameter is usually set to a value smaller than . The assumption here is that an outdated peer cannot have become primary in the meantime, so we don't need to wait for it as long as for a node which was alive before. The timeout is specified in seconds. The default value is 0, which stands for an infinite timeout. Also see the parameter. This parameter causes DRBD to continue waiting in the init script even when a split-brain situation has been detected, and the nodes therefore refuse to connect to each other. drbd.conf wfc-timeout Define how long the init script waits until all peers are connected. This can be useful in combination with a cluster manager which cannot manage DRBD resources: when the cluster manager starts, the DRBD resources will already be up and running. With a more capable cluster manager such as Pacemaker, it makes more sense to let the cluster manager control DRBD resources. The timeout is specified in seconds. The default value is 0, which stands for an infinite timeout. Also see the parameter. drbd-utils-8.9.10/documentation/v9/drbdmeta.80000644000175000017500000002020213027211670020633 0ustar apoikosapoikos'\" t .\" Title: drbdmeta .\" Author: [see the "Author" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 .\" Date: 6 December 2012 .\" Manual: System Administration .\" Source: DRBD 9.0.0 .\" Language: English .\" .TH "DRBDMETA" "8" "6 December 2012" "DRBD 9.0.0" "System Administration" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .\" http://bugs.debian.org/507673 .\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html .\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- .\" disable hyphenation .nh .\" disable justification (adjust text to left margin only) .ad l .\" ----------------------------------------------------------------- .\" * MAIN CONTENT STARTS HERE * .\" ----------------------------------------------------------------- .SH "NAME" drbdmeta \- Manipulate the DRBD on\-disk metadata .SH "SYNOPSIS" .HP \w'\fBdrbdmeta\fR\ 'u \fBdrbdmeta\fR [\-\-force] [\-\-ignore\-sanity\-checks] {\fIdevice\fR} {v06\ \fIminor\fR | v07\ \fImeta_dev\ index\fR | v08\ \fImeta_dev\ index\fR | v09\ \fImeta_dev\ index\fR} {\fIcommand\fR} [\fIcmd\ args\fR...] .SH "DESCRIPTION" .PP The \fBdrbdmeta\fR utility is used for creating, displaying, and modifying DRBD\*(Aqs on\-disk metadata\&. Users usually interact with the \fBdrbdadm\fR utility, which provides a more high\-level interface to DRBD than \fBdrbdmeta\fR\&. (See \fBdrbdadm\fR\*(Aqs \fB\-\-dry\-run\fR option to see how \fBdrbdadm\fR uses \fBdrbdmeta\fR\&.) .PP This utility can only be used on devices which are not currently in use by the kernel\&. .PP The first argument (\fIdevice\fR) specifies the drbd device associated with a volume, or \(lq\-\(rq if no device is associated with that volume\&. If the drbd device is specified, the \fBdrbdmeta\fR utility makes sure that the drbd device does not currently have a volume attached to prevent meta\-data of an active volume from being destroyed\&. .PP The second argument specifies the metadata version to use (v06, v07, v08, v09)\&. In most metadata versions, the third argument (\fImeta_dev\fR) specifies the device which contains the metadata; this argument can be the same as \fIdevice\fR\&. The fourth argument (\fIindex\fR) can be one of the keywords \fBinternal\fR (for internal metadata), \fBflex\-internal\fR (in v07 for variable\-sized metadata; v07 otherwise defaults to fixed\-size internal metadata), \fBflex\-external\fR (for variable\-sized external metadata), or a numeric matadata index (for fixed\-size external metadata)\&. See the \fBmeta\-disk\fR parameter in \fBdrbd.conf\fR(5)\&. .SH "OPTIONS" .PP \-\-force .RS 4 Assume yes as the answer to all questions drbdmeta would ask\&. .RE .PP \-\-ignore\-sanity\-checks .RS 4 Normally, \fBdrbdmeta\fR performs some sanity checks before writing to the metadata device: for example, if the device appears to contain a file system, it refuses to destroy the file system by writing into it\&. Use this option to ignore these checks\&. .RE .SH "COMMANDS" .PP \fBcreate\-md\fR [\fB\-\-peer\-max\-bio\-size=\fR\fIval\fR] (metadata versions v06, v07, and v08), .br \fBcreate\-md\fR {number\-of\-bitmap\-slots} [\fB\-\-peer\-max\-bio\-size=\fR\fIval\fR] [\fB\-\-al\-stripes=\fR\fIval\fR] [\fB\-\-al\-stripe\-size\-kB=\fR\fIval\fR] (metadata version v09) .RS 4 Initialize the metadata\&. This is necessary before a DRBD resource can be attached\&. If \fBdrbdmeta\fR finds an older version of DRBD metadata on the device, it asks if the format should be converted\&. .sp When \fBdrbdadm\fR calls \fBdrbdmeta\fR\*(Aqs \fBcreate\-md\fR command for a device, it sets the \fInumber\-of\-bitmap\-slots\fR argument to the number of peers in the resource\&. To reserve additional bitmap slots (which allows to add more peers in the future), call \fBdrbdmeta\fR directly instead\&. .sp When a device is used before being connected to its peers the first time, DRBD assumes that peers can only handle 4 KiB requests by default\&. The \fB\-\-peer\-max\-bio\-size\fR option allows to set more optimistic values; use this if the versions of DRBD that this device will connect to are known\&. DRBD supports a maximum bio size of 32 KiB since version 8\&.3\&.8, of 128 KiB since version 8\&.3\&.9, and of 1 MiB since version 8\&.4\&.0\&. .sp If you want to use more than 6433 activity log extents, or live on top of a spriped RAID, you may specify the number of stripes (\fB\-\-al\-stripes\fR, default 1), and the stripe size (\fB\-\-al\-stripe\-size\-kB\fR, default 32)\&. To just use a larger linear on\-disk ring\-buffer, leave the number of stripes at 1, and increase the size only: \fBdrbdmeta 0 v08 /dev/vg23/lv42 internal create\-md \-\-al\-stripe\-size 1M\fR .sp To avoid a single "spindle" from becoming a bottleneck, increase the number of stripes, to achieve an interleaved layout of the on\-disk activity\-log transactions\&. What you give as "stripe\-size" should be what is a\&.k\&.a\&. "chunk size" or "granularity" or "strip unit": the minimum skip to the next "spindle"\&. \fBdrbdmeta 0 v08 /dev/vg23/lv42 internal create\-md \-\-al\-stripes 7 \-\-al\-stripe\-size 64\fR .RE .PP \fBget\-gi\fR [\fB\-\-node\-id=\fR\fIid\fR] .RS 4 Show the data generation identifiers for a device on a particular connection\&. DRBD version 9\&.0\&.0 and beyond support multiple peers; use the \fInode\-id\fR option to define which peer\*(Aqs data generation identifiers to show\&. .RE .PP \fBshow\-gi\fR [\fB\-\-node\-id=\fR\fIid\fR] .RS 4 Similar to \fBget\-gi\fR, but with explanatory information\&. .RE .PP \fBdump\-md\fR .RS 4 Dump the metadata of a device in text form, including the bitmap and activity log\&. .RE .PP \fBoutdate\fR .RS 4 Mark the data on a lower\-level device as outdated\&. See \fBdrbdsetup\fR(8) for details\&. .RE .PP \fBdstate\fR .RS 4 Show the current disk state of a lower\-level device\&. .RE .PP \fBcheck\-resize\fR .RS 4 Examine the device size of a lower\-level device and its last known device size (saved in \fB/var/lib/drbd/drbd\-minor\-\fR\fB\fIminor\fR\fR\fB\&.lkbd\fR by \fBdrbdsetup check\-resize\fR)\&. For internal metadata, if the size of the lower\-level device has changed and the metadata can be found at the previous position, move the metadata to the new position at the end of the block device\&. .RE .PP \fBapply\-al\fR .RS 4 Apply the activity log of the specified device\&. This is necessary before the device can be attached by the kernel again\&. .RE .SH "EXPERT COMMANDS" .PP The \fBdrbdmeta\fR utility can be used to fine tune metdata\&. Please note that this can lead to destroyed metadata or even silent data corruption; use with great care only\&. .PP \fBset\-gi\fR \fIgi\fR [\fB\-\-node\-id=\fR\fIid\fR] .RS 4 Set the generation identifiers\&. The \fIgi\fR argument is a generation counter for the v06 and v07 formats, and a set of UUIDs for v08 and beyond\&. Accepts the same syntax as in the \fBget\-gi\fR output\&. DRBD version 9\&.0\&.0 and beyond support multiple peers; use the \fI\-\-node\-id\fR option to define which peer\*(Aqs data generation identifiers to set\&. .RE .PP \fBrestore\-md\fR \fIdump_file\fR .RS 4 Replace the metadata on the device with the contents of \fIdump_file\fR\&. The dump file format is defined by the output of the \fBdump\-md\fR command\&. .RE .SH "VERSION" .sp This document was revised for version 9\&.0\&.0 of the DRBD distribution\&. .SH "AUTHOR" .sp Written by Philipp Reisner and Lars Ellenberg \&. .SH "REPORTING BUGS" .sp Report bugs to \&. .SH "COPYRIGHT" .sp Copyright 2001\-2008,2012 LINBIT Information Technologies, Philipp Reisner, Lars Ellenberg\&. This is free software; see the source for copying conditions\&. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE\&. .SH "SEE ALSO" .PP \fBdrbdadm\fR(8) \fBdrbd.conf\fR(5) drbd-utils-8.9.10/documentation/v9/drbd.conf.xsl0000644000175000017500000000165112466702073021366 0ustar apoikosapoikos drbd-utils-8.9.10/.filelist0000644000175000017500000002445513027211741015402 0ustar apoikosapoikosdrbd-utils-8.9.10/COPYING drbd-utils-8.9.10/ChangeLog drbd-utils-8.9.10/Makefile.in drbd-utils-8.9.10/README drbd-utils-8.9.10/autogen.sh drbd-utils-8.9.10/configure.ac drbd-utils-8.9.10/documentation/aspell.en.per drbd-utils-8.9.10/documentation/fencing-by-constraints.txt drbd-utils-8.9.10/documentation/v83/Makefile.in drbd-utils-8.9.10/documentation/v83/drbd.conf.xml drbd-utils-8.9.10/documentation/v83/drbd.xml drbd-utils-8.9.10/documentation/v83/drbdadm.xml drbd-utils-8.9.10/documentation/v83/drbddisk.xml drbd-utils-8.9.10/documentation/v83/drbdmeta.xml drbd-utils-8.9.10/documentation/v83/drbdsetup.xml drbd-utils-8.9.10/documentation/v84/Makefile.in drbd-utils-8.9.10/documentation/v84/drbd.conf.xml drbd-utils-8.9.10/documentation/v84/drbd.xml drbd-utils-8.9.10/documentation/v84/drbdadm.xml drbd-utils-8.9.10/documentation/v84/drbddisk.xml drbd-utils-8.9.10/documentation/v84/drbdmeta.xml drbd-utils-8.9.10/documentation/v84/drbdsetup.xml drbd-utils-8.9.10/documentation/v84/xml-usage-to-docbook.xsl drbd-utils-8.9.10/documentation/v9/Makefile.in drbd-utils-8.9.10/documentation/v9/drbd-overview.xml drbd-utils-8.9.10/documentation/v9/drbd.conf.xml.in drbd-utils-8.9.10/documentation/v9/drbd.conf.xsl drbd-utils-8.9.10/documentation/v9/drbd.xml drbd-utils-8.9.10/documentation/v9/drbdadm.xml drbd-utils-8.9.10/documentation/v9/drbdmeta.xml drbd-utils-8.9.10/documentation/v9/drbdmon.xml drbd-utils-8.9.10/documentation/v9/drbdsetup-options.xml drbd-utils-8.9.10/documentation/v9/drbdsetup.xml.in drbd-utils-8.9.10/documentation/v9/drbdsetup.xsl drbd-utils-8.9.10/documentation/v9/xml-usage-to-docbook.xsl drbd-utils-8.9.10/drbd.spec.in drbd-utils-8.9.10/m4/AX_CXX_COMPILE_STDXX.m4 drbd-utils-8.9.10/m4/AX_CXX_COMPILE_STDXX_11.m4 drbd-utils-8.9.10/scripts/Makefile.in drbd-utils-8.9.10/scripts/README drbd-utils-8.9.10/scripts/README.rhcs_fence drbd-utils-8.9.10/scripts/block-drbd drbd-utils-8.9.10/scripts/crm-fence-peer.sh drbd-utils-8.9.10/scripts/drbd drbd-utils-8.9.10/scripts/drbd-overview.pl drbd-utils-8.9.10/scripts/drbd.conf drbd-utils-8.9.10/scripts/drbd.conf.example drbd-utils-8.9.10/scripts/drbd.lang drbd-utils-8.9.10/scripts/drbd.metadata.rhcs drbd-utils-8.9.10/scripts/drbd.ocf drbd-utils-8.9.10/scripts/drbd.rules.in drbd-utils-8.9.10/scripts/drbd.service drbd-utils-8.9.10/scripts/drbd.sh.rhcs drbd-utils-8.9.10/scripts/drbd.tmpfiles.conf drbd-utils-8.9.10/scripts/drbdadm.bash_completion drbd-utils-8.9.10/scripts/drbddisk drbd-utils-8.9.10/scripts/drbdupper drbd-utils-8.9.10/scripts/get_uts_release.sh drbd-utils-8.9.10/scripts/global_common.conf drbd-utils-8.9.10/scripts/notify.sh drbd-utils-8.9.10/scripts/outdate-peer.sh drbd-utils-8.9.10/scripts/pretty-proc-drbd.sh drbd-utils-8.9.10/scripts/rhcs_fence drbd-utils-8.9.10/scripts/snapshot-resync-target-lvm.sh drbd-utils-8.9.10/scripts/stonith_admin-fence-peer.sh drbd-utils-8.9.10/scripts/unsnapshot-resync-target-lvm.sh drbd-utils-8.9.10/user/drbdmon/Args.cpp drbd-utils-8.9.10/user/drbdmon/Args.h drbd-utils-8.9.10/user/drbdmon/CompactDisplay.cpp drbd-utils-8.9.10/user/drbdmon/CompactDisplay.h drbd-utils-8.9.10/user/drbdmon/ConfigOption.cpp drbd-utils-8.9.10/user/drbdmon/ConfigOption.h drbd-utils-8.9.10/user/drbdmon/Configurable.h drbd-utils-8.9.10/user/drbdmon/Configurator.h drbd-utils-8.9.10/user/drbdmon/DebugDisplay.cpp drbd-utils-8.9.10/user/drbdmon/DebugDisplay.h drbd-utils-8.9.10/user/drbdmon/Display.cpp drbd-utils-8.9.10/user/drbdmon/Display.h drbd-utils-8.9.10/user/drbdmon/DrbdConnection.cpp drbd-utils-8.9.10/user/drbdmon/DrbdConnection.h drbd-utils-8.9.10/user/drbdmon/DrbdMon.cpp drbd-utils-8.9.10/user/drbdmon/DrbdMon.h drbd-utils-8.9.10/user/drbdmon/DrbdResource.cpp drbd-utils-8.9.10/user/drbdmon/DrbdResource.h drbd-utils-8.9.10/user/drbdmon/DrbdRole.cpp drbd-utils-8.9.10/user/drbdmon/DrbdRole.h drbd-utils-8.9.10/user/drbdmon/DrbdVolume.cpp drbd-utils-8.9.10/user/drbdmon/DrbdVolume.h drbd-utils-8.9.10/user/drbdmon/EventsIo.cpp drbd-utils-8.9.10/user/drbdmon/EventsIo.h drbd-utils-8.9.10/user/drbdmon/EventsSourceSpawner.cpp drbd-utils-8.9.10/user/drbdmon/EventsSourceSpawner.h drbd-utils-8.9.10/user/drbdmon/GenericDisplay.h drbd-utils-8.9.10/user/drbdmon/Makefile.in drbd-utils-8.9.10/user/drbdmon/MessageLog.cpp drbd-utils-8.9.10/user/drbdmon/MessageLog.h drbd-utils-8.9.10/user/drbdmon/StateFlags.cpp drbd-utils-8.9.10/user/drbdmon/StateFlags.h drbd-utils-8.9.10/user/drbdmon/StringTokenizer.cpp drbd-utils-8.9.10/user/drbdmon/StringTokenizer.h drbd-utils-8.9.10/user/drbdmon/VolumesContainer.cpp drbd-utils-8.9.10/user/drbdmon/VolumesContainer.h drbd-utils-8.9.10/user/drbdmon/comparators.cpp drbd-utils-8.9.10/user/drbdmon/comparators.h drbd-utils-8.9.10/user/drbdmon/cppdsaext/src/BSearch.h drbd-utils-8.9.10/user/drbdmon/cppdsaext/src/QTree.h drbd-utils-8.9.10/user/drbdmon/cppdsaext/src/VList.h drbd-utils-8.9.10/user/drbdmon/cppdsaext/src/VMap.h drbd-utils-8.9.10/user/drbdmon/cppdsaext/src/dsaext.cpp drbd-utils-8.9.10/user/drbdmon/cppdsaext/src/dsaext.h drbd-utils-8.9.10/user/drbdmon/drbdmon_main.cpp drbd-utils-8.9.10/user/drbdmon/exceptions.h drbd-utils-8.9.10/user/drbdmon/map_types.h drbd-utils-8.9.10/user/drbdmon/test.cpp drbd-utils-8.9.10/user/drbdmon/utils.cpp drbd-utils-8.9.10/user/drbdmon/utils.h drbd-utils-8.9.10/user/linux/drbd_config.h drbd-utils-8.9.10/user/shared/Makefile.in drbd-utils-8.9.10/user/shared/drbd_endian.h drbd-utils-8.9.10/user/shared/drbdmeta.c drbd-utils-8.9.10/user/shared/drbdmeta_parser.h drbd-utils-8.9.10/user/shared/drbdmeta_scanner.fl drbd-utils-8.9.10/user/shared/libgenl.c drbd-utils-8.9.10/user/shared/libgenl.h drbd-utils-8.9.10/user/shared/shared_main.c drbd-utils-8.9.10/user/shared/shared_main.h drbd-utils-8.9.10/user/shared/shared_parser.c drbd-utils-8.9.10/user/shared/shared_parser.h drbd-utils-8.9.10/user/shared/shared_tool.c drbd-utils-8.9.10/user/shared/shared_tool.h drbd-utils-8.9.10/user/shared/wrap_printf.c drbd-utils-8.9.10/user/shared/wrap_printf.h drbd-utils-8.9.10/user/shared_prereqs.mk drbd-utils-8.9.10/user/v83/Makefile.in drbd-utils-8.9.10/user/v83/drbd_strings.c drbd-utils-8.9.10/user/v83/drbdadm.h drbd-utils-8.9.10/user/v83/drbdadm_adjust.c drbd-utils-8.9.10/user/v83/drbdadm_main.c drbd-utils-8.9.10/user/v83/drbdadm_minor_table.c drbd-utils-8.9.10/user/v83/drbdadm_parser.c drbd-utils-8.9.10/user/v83/drbdadm_parser.h drbd-utils-8.9.10/user/v83/drbdadm_scanner.fl drbd-utils-8.9.10/user/v83/drbdadm_usage_cnt.c drbd-utils-8.9.10/user/v83/drbdsetup.c drbd-utils-8.9.10/user/v83/drbdtool_common.c drbd-utils-8.9.10/user/v83/drbdtool_common.h drbd-utils-8.9.10/user/v83/linux/drbd.h drbd-utils-8.9.10/user/v83/linux/drbd_config.h drbd-utils-8.9.10/user/v83/linux/drbd_limits.h drbd-utils-8.9.10/user/v83/linux/drbd_nl.h drbd-utils-8.9.10/user/v83/linux/drbd_tag_magic.h drbd-utils-8.9.10/user/v83/unaligned.h drbd-utils-8.9.10/user/v84/Makefile.in drbd-utils-8.9.10/user/v84/config_flags.c drbd-utils-8.9.10/user/v84/config_flags.h drbd-utils-8.9.10/user/v84/drbd_nla.c drbd-utils-8.9.10/user/v84/drbd_nla.h drbd-utils-8.9.10/user/v84/drbd_strings.c drbd-utils-8.9.10/user/v84/drbd_strings.h drbd-utils-8.9.10/user/v84/drbdadm.h drbd-utils-8.9.10/user/v84/drbdadm_adjust.c drbd-utils-8.9.10/user/v84/drbdadm_main.c drbd-utils-8.9.10/user/v84/drbdadm_parser.c drbd-utils-8.9.10/user/v84/drbdadm_parser.h drbd-utils-8.9.10/user/v84/drbdadm_scanner.fl drbd-utils-8.9.10/user/v84/drbdadm_usage_cnt.c drbd-utils-8.9.10/user/v84/drbdsetup.c drbd-utils-8.9.10/user/v84/drbdsetup_colors.c drbd-utils-8.9.10/user/v84/drbdsetup_colors.h drbd-utils-8.9.10/user/v84/drbdtool_common.c drbd-utils-8.9.10/user/v84/drbdtool_common.h drbd-utils-8.9.10/user/v84/linux/drbd.h drbd-utils-8.9.10/user/v84/linux/drbd_config.h drbd-utils-8.9.10/user/v84/linux/drbd_genl.h drbd-utils-8.9.10/user/v84/linux/drbd_genl_api.h drbd-utils-8.9.10/user/v84/linux/drbd_limits.h drbd-utils-8.9.10/user/v84/linux/genl_magic_func.h drbd-utils-8.9.10/user/v84/linux/genl_magic_struct.h drbd-utils-8.9.10/user/v84/registry.c drbd-utils-8.9.10/user/v84/registry.h drbd-utils-8.9.10/user/v9/Makefile.in drbd-utils-8.9.10/user/v9/config_flags.c drbd-utils-8.9.10/user/v9/config_flags.h drbd-utils-8.9.10/user/v9/drbd_nla.c drbd-utils-8.9.10/user/v9/drbd_nla.h drbd-utils-8.9.10/user/v9/drbdadm.h drbd-utils-8.9.10/user/v9/drbdadm_adjust.c drbd-utils-8.9.10/user/v9/drbdadm_dump.c drbd-utils-8.9.10/user/v9/drbdadm_dump.h drbd-utils-8.9.10/user/v9/drbdadm_main.c drbd-utils-8.9.10/user/v9/drbdadm_parser.c drbd-utils-8.9.10/user/v9/drbdadm_parser.h drbd-utils-8.9.10/user/v9/drbdadm_postparse.c drbd-utils-8.9.10/user/v9/drbdadm_scanner.fl drbd-utils-8.9.10/user/v9/drbdadm_usage_cnt.c drbd-utils-8.9.10/user/v9/drbdsetup.c drbd-utils-8.9.10/user/v9/drbdsetup_colors.c drbd-utils-8.9.10/user/v9/drbdsetup_colors.h drbd-utils-8.9.10/user/v9/drbdtool_common.c drbd-utils-8.9.10/user/v9/drbdtool_common.h drbd-utils-8.9.10/user/v9/registry.c drbd-utils-8.9.10/user/v9/registry.h drbd-utils-8.9.10/user/v9/sys_queue.h drbd-utils-8.9.10/drbd-headers/compat.h drbd-utils-8.9.10/drbd-headers/drbd_meta_data.h drbd-utils-8.9.10/drbd-headers/drbd_protocol.h drbd-utils-8.9.10/drbd-headers/drbd_strings.c drbd-utils-8.9.10/drbd-headers/drbd_strings.h drbd-utils-8.9.10/drbd-headers/drbd_transport.h drbd-utils-8.9.10/drbd-headers/linux/drbd.h drbd-utils-8.9.10/drbd-headers/linux/drbd_genl.h drbd-utils-8.9.10/drbd-headers/linux/drbd_genl_api.h drbd-utils-8.9.10/drbd-headers/linux/drbd_limits.h drbd-utils-8.9.10/drbd-headers/linux/genl_magic_func.h drbd-utils-8.9.10/drbd-headers/linux/genl_magic_struct.h drbd-utils-8.9.10/documentation/v83/drbdsetup.8 drbd-utils-8.9.10/documentation/v83/drbd.8 drbd-utils-8.9.10/documentation/v83/drbdmeta.8 drbd-utils-8.9.10/documentation/v83/drbd.conf.5 drbd-utils-8.9.10/documentation/v83/drbdadm.8 drbd-utils-8.9.10/documentation/v83/drbddisk.8 drbd-utils-8.9.10/documentation/v84/drbd.8 drbd-utils-8.9.10/documentation/v84/drbdadm.8 drbd-utils-8.9.10/documentation/v84/drbdsetup.8 drbd-utils-8.9.10/documentation/v84/drbdmeta.8 drbd-utils-8.9.10/documentation/v84/drbd.conf.5 drbd-utils-8.9.10/documentation/v84/drbddisk.8 drbd-utils-8.9.10/documentation/v9/drbd.conf.5 drbd-utils-8.9.10/documentation/v9/drbd.8 drbd-utils-8.9.10/documentation/v9/drbdsetup.8 drbd-utils-8.9.10/documentation/v9/drbdadm.8 drbd-utils-8.9.10/documentation/v9/drbdmeta.8 drbd-utils-8.9.10/documentation/v9/drbd-overview.8 drbd-utils-8.9.10/user/shared/drbd_buildtag.c drbd-utils-8.9.10/user/shared/config.h.in drbd-utils-8.9.10/.filelist drbd-utils-8.9.10/configure drbd-utils-8.9.10/README0000644000175000017500000000065112466702073014447 0ustar apoikosapoikos DRBD ====== by Philipp Reisner and Lars Ellenberg LINBIT Information Technologies Reference documentation is included in the documentation directory. Please refer to the web pages at http://www.drbd.org/ http://www.drbd.org/docs/introduction/ to find maintained information. drbd-utils-8.9.10/ChangeLog0000644000175000017500000001617113027210656015340 0ustar apoikosapoikosLatest: ------ For even more detail, use "git log" or visit http://git.drbd.org/. 8.9.10 -------- * new drbdmon tool, which provides a compact overview about resources. In contrast to drbdsetup status it provides live monitoring. This requires a C++11 compiler. * drbdadm parser got substantially faster for thousands of resources * send SIGKILL to childs if parent (drbdadm) dies. 8.9.9 -------- * fix --skip-disk/--skip-net when used with more than one resource * events2 of v8.4: Fake peer-node-ids to make it in-line with v9 * fix drbdadm crashes in set-gi and new-current-uuid * new peer_device option (--bitmap=no) to indicate that for this peer no bitmap slot should be allocated in the meta-data; This is used by all peers of a (configured) diskless node; saves bitmap-slots, thus allows you to use smaller max-peers numbers, thus saves memory and CPU resources * removed sh-status from v9 8.9.8 -------- * fix a lot of regressions from allowing to pass command options on the drbdadm adjust commandline. (filtering of backend options) * wait-for-* return success if there are no peers * naturally align 64 bit attributes in gennetlink packets * improve systemd unit-file to wait for network-online.target * return success for a resize to the same size even if a resync does not allow as resize at this point in time * improve sh-nop with --config-to-test with new --config-to-exclude and correct exit code * implemented --json for drbdsetup status 8.9.7 -------- * allow to pass peer device options on the drbdadm command-line * fix drbdadm net-options by not passing the transport (the transport of a connection can not be changed while the connection is online) * options passed on the drbdadm command-line now overrule settings from the configuration file when calling drbdsetup * new resource option max-io-depth (v9 only) * allow partial adjust by --skip-disk and/or --skip-net * support for a new meta-data flag that helps resize operations * workaround for sysfs entries and del_gendisk and add_disk; in case drbdsetup sees those sysfs entries it exits immediately, that prevents half initialized drbd devices that can not be removed * drbdadm resize waits until new new size is user visible * support for the reload operation in the OCF resource agent * drbdadm's parser now reports too long strings with a meaningful error message * increased the size of the uniqueness hash-table, to support up to to 1000 resources 8.9.6 -------- * Call "drbdsetup resize" only as often as necessary on "drbdadm resize" * Disconnect connection first on single path deletion from connection * Add unfence-peer handler * Fix "drbdadm adjust" for proxy configurations 8.9.5 -------- * add support for new disk option 'rs-discard-granularity' (module v8.4.7) * add support for new disk option 'discard-zeroes-if-aligned' (module v8.4.7) * add support for v9 "path" commands * doc improvements/corrections * improvements to drbd ocf resource agent and pacemaker "constraint based fencing" (crm-fence-peer.sh) * drbd-overview improvements 8.9.4 -------- * Add an autoconf switch for building without man pages * Fix compatibility with the musl C library * Introduce the concept of paths within a connection to the config file; add support for paths to drbdadm adjust * Allow multiple connection-mesh statements within one resource * Document peer-device-options on the drbd-9.0 man pages * increase the lower boundary for al-extents from 7 to 67; drbdadm clamps al-extents to 67 implicitly if you configures something lower than 67 * Document the events2 command on the drbd-8.4 man page * Fix environment variables for handlers 8.9.3 -------- * Support for the new new-peer, add-path, connect, del-path, disconnect and del-peer commands of drbd-9.0.0; drbdadm support for the new commands * New configuration file directive template-file; with that a dedicated file for the common section of a resource can be specified * Rewrite the parser for configure options; reuse the data structures describing drbdsetup options * No longer try to set peer-device-options with the connect or attach commands; Only do it with the up and attach commands * Fixed issues with stderr messages might go into arbitrary FDs * Allow recursive includes; drbdadm includes each config file only once * Fix parsing cut-off proxy sections * When converting 8.4 to 9.0 meta-data produce meta-data the drbd9 kernel driver will accept * Obey max-peers for all volumes when creating meta-data * Do not re-register at usage.drbd.org when the module is not loaded when drbdadm is invoked 8.9.2 -------- * change systemd unit file: basically just call the init script * make some previously hardcoded timeouts configurable * drbdadm: New command peer-device-options * drbd 9: Move max_buffers to net_conf * drbd 9: Log errors to syslog if stderr is not available * init script: fixes for stacked resources * fix regression corner cases in bitmap size calculation * allow create-md to initialize peer-max-bio-size to 1M * drbd 9: make transport selectable * fix aggregating drbdsetup / drbdmeta exit statii * some documentation fixes (content and build) * added direct-connect command * incompatible drbd-9 metadata format change. use: node_id as index for peer_md instead of the bitmap_index * drbdadm/meta/usage_cnt: ensure output is visible * drbdsetup: fix arguments for all commands expecting a peer_device * exit codes: redefine E_USAGE to 1 (not 3) * some build changes * init script: on start, first try to load the module * drbdsetup events2: Improve how timestamps are assigned * udev rules (symlinks in /dev/by-res and by-disk) got fixed * Fixed upper limit for drbd-8.4 activity log entries * many fixes to drbdadm adjust and proxy commands for drbd-9 * rhcs_fence: Do not invoke fence agents in parallel, rewrite in bash * drbdsetup events2 is now also available in drbd-8.4 (backported from drbd-9) * reorganized the repository have common code for drbd-9, drbd-8.4 and drbd-8.3 only once * Fix drbd.ocf for resources without volume 0 8.9.1 -------- * add DRBD systemd service * new configuration options socket-check-timeout and csums-after-crash-only * update xen block-drbd helper: allow for type "phy" * update udev rules and move from /etc/ to /lib/ * fix various regressions/fallout from the kernel/userland package split, re-add scripts drbddisk and drbdupper, re-add "become-on-primary" feature to init script * crm-fence-peer.sh: improve detection of "clean down" * updated fencing scripts stonith_admin-fence-peer.sh and rhcs_fence * improved "proxy" configuration section parsing * added manpage for drbd-overview, minor review of man pages * build: various changes in configure.ac, makefiles, debian/* and spec file 8.9.0 -------- * Initial release of unified drbd-utils * Supports drbd drivers 8.3, 8.4 and 9.0 * Ships with man page links defaulting to 8.4 * Fixed offline resizing in drbdmeta; A regression that causes data loss, since meta-data was written with wrong offset. The regression was introduced with 8.4.3 drbd-utils-8.9.10/COPYING0000644000175000017500000004310612466702073014624 0ustar apoikosapoikos GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. drbd-utils-8.9.10/configure0000755000175000017500000051476713027211614015506 0ustar apoikosapoikos#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for DRBD 8.9.10. # # Report bugs to . # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org and $0: drbd-dev@lists.linbit.com about your system, including $0: any error possibly output before this message. Then $0: install a modern shell, or manually run the script $0: under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='DRBD' PACKAGE_TARNAME='drbd' PACKAGE_VERSION='8.9.10' PACKAGE_STRING='DRBD 8.9.10' PACKAGE_BUGREPORT='drbd-dev@lists.linbit.com' PACKAGE_URL='' ac_subst_vars='LTLIBOBJS LIBOBJS DRBD_CONFIG_DIR DRBD_LOCK_DIR DRBD_RUN_DIR DRBD_LIB_DIR RPM_REQ_CHKCONFIG_PREUN RPM_REQ_CHKCONFIG_POST RPM_REQ_XEN RPM_REQ_BASH_COMPLETION RPM_REQ_HEARTBEAT RPM_REQ_PACEMAKER RPM_SUBPACKAGE_NOARCH RPM_BUILDREQ_DEFAULT RPM_DIST_TAG UDEV_RULE_SUFFIX BASH_COMPLETION_SUFFIX INITDIR DISTRO HAVE_CXX11 ac_ct_CXX CXXFLAGS CXX UDEVINFO UDEVADM DPKG_BUILDPACKAGE GIT TAR XSLTPROC RPMBUILD FLEX GREP SED LN_S OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC WITH_DRBDMON initscripttype udevrulesdir udevdir tmpfilesdir systemdunitdir WITH_MANUAL WITH_BASHCOMPLETION WITH_RGMANAGER WITH_HEARTBEAT WITH_PACEMAKER WITH_XEN WITH_UDEV WITH_84_SUPPORT WITH_83_SUPPORT target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking with_83support with_84support with_udev with_xen with_pacemaker with_heartbeat with_rgmanager with_bashcompletion with_distro with_initdir with_noarchsubpkg enable_spec with_manual with_systemdunitdir with_tmpfilesdir with_initscripttype with_drbdmon ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CXX CXXFLAGS CCC' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures DRBD 8.9.10 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/drbd] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of DRBD 8.9.10:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-spec Rather than creating Makefiles, create an RPM spec file only Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --without-83support Do not include support for drbd driver/module <= 8.3 --without-84support Do not include support for drbd driver/module 8.4 --with-udev Enable udev integration --with-xen Enable Xen integration --with-pacemaker Enable Pacemaker integration --with-heartbeat Enable Heartbeat v1 haresources integration scripts --with-rgmanager Enable Red Hat Cluster Suite integration --with-bashcompletion Enable programmable bash completion --with-distro Configure for a specific distribution (supported values: generic, redhat, suse, debian; default is to autodetect) --with-initdir Override directory for init scripts (default is distribution-specific) --with-noarchsubpkg Build subpackages that support it for the "noarch" architecture (makes sense only with --enable-spec, supported by RPM from 4.6.0 forward) --without-manual Do not include manual pages --with-systemdunitdir=DIR Directory for systemd service files [Auto] --with-tmpfilesdir=DIR install configuration files for management of volatile files and directories in DIR [[PREFIX/lib/tmpfiles.d]] --with-initscripttype=INIT_SCRIPT_TYPE Type of init script to install (sysv|systemd|both|none). [auto] --without-drbdmon Do not include the DRBD resource reporting utility Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CXX C++ compiler command CXXFLAGS C++ compiler flags Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF DRBD configure 8.9.10 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_cxx_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_cxx_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_cxx_try_compile cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by DRBD $as_me 8.9.10, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test "$prefix" = "NONE"; then prefix=$ac_default_prefix fi exec_prefix=$prefix prefix="`eval echo ${prefix}`" exec_prefix="`eval echo ${exec_prefix}`" bindir="`eval echo ${bindir}`" sbindir="`eval echo ${sbindir}`" libexecdir="`eval echo ${libexecdir}`" datarootdir="`eval echo ${datarootdir}`" datadir="`eval echo ${datadir}`" sysconfdir="`eval echo ${sysconfdir}`" sharedstatedir="`eval echo ${sharedstatedir}`" localstatedir="`eval echo ${localstatedir}`" libdir="`eval echo ${libdir}`" includedir="`eval echo ${includedir}`" oldincludedir="`eval echo ${oldincludedir}`" infodir="`eval echo ${infodir}`" mandir="`eval echo ${mandir}`" docdir="`eval echo ${docdir}`" WITH_83_SUPPORT=yes WITH_84_SUPPORT=yes WITH_UDEV=yes WITH_XEN=yes WITH_PACEMAKER=yes WITH_HEARTBEAT=yes WITH_RGMANAGER=no WITH_BASHCOMPLETION=yes WITH_NOARCH_SUBPACKAGES=no WITH_MANUAL=yes WITH_DRBDMON=yes # Check whether --with-83support was given. if test "${with_83support+set}" = set; then : withval=$with_83support; WITH_83_SUPPORT=$withval fi # Check whether --with-84support was given. if test "${with_84support+set}" = set; then : withval=$with_84support; WITH_84_SUPPORT=$withval fi # Check whether --with-udev was given. if test "${with_udev+set}" = set; then : withval=$with_udev; WITH_UDEV=$withval fi # Check whether --with-xen was given. if test "${with_xen+set}" = set; then : withval=$with_xen; WITH_XEN=$withval fi # Check whether --with-pacemaker was given. if test "${with_pacemaker+set}" = set; then : withval=$with_pacemaker; WITH_PACEMAKER=$withval fi # Check whether --with-heartbeat was given. if test "${with_heartbeat+set}" = set; then : withval=$with_heartbeat; WITH_HEARTBEAT=$withval fi # Check whether --with-rgmanager was given. if test "${with_rgmanager+set}" = set; then : withval=$with_rgmanager; WITH_RGMANAGER=$withval fi # Check whether --with-bashcompletion was given. if test "${with_bashcompletion+set}" = set; then : withval=$with_bashcompletion; WITH_BASHCOMPLETION=$withval fi # Check whether --with-distro was given. if test "${with_distro+set}" = set; then : withval=$with_distro; DISTRO=$withval fi # Check whether --with-initdir was given. if test "${with_initdir+set}" = set; then : withval=$with_initdir; INITDIR=$withval fi # Check whether --with-noarchsubpkg was given. if test "${with_noarchsubpkg+set}" = set; then : withval=$with_noarchsubpkg; WITH_NOARCH_SUBPACKAGES=$withval fi # Check whether --enable-spec was given. if test "${enable_spec+set}" = set; then : enableval=$enable_spec; SPECMODE=$enableval else SPECMODE="" fi # Check whether --with-manual was given. if test "${with_manual+set}" = set; then : withval=$with_manual; WITH_MANUAL=$withval fi PKG_PROG_PKG_CONFIG # Check whether --with-systemdunitdir was given. if test "${with_systemdunitdir+set}" = set; then : withval=$with_systemdunitdir; WITH_SYSTEMD_UNIT_DIR=$withval fi if test x"$with_systemdunitdir" = x || \ test x"$with_systemdunitdir" = xyes ; then if test x"$PKG_CONFIG" != x; then systemdunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) fi if test x"$systemdunitdir" = x; then { $as_echo "$as_me:${as_lineno-$LINENO}: Could not detect systemd unit directory" >&5 $as_echo "$as_me: Could not detect systemd unit directory" >&6;} fi else systemdunitdir=$with_systemdunitdir fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using systemd unit directory: $systemdunitdir" >&5 $as_echo "Using systemd unit directory: $systemdunitdir" >&6; } # Check whether --with-tmpfilesdir was given. if test "${with_tmpfilesdir+set}" = set; then : withval=$with_tmpfilesdir; tmpfilesdir=$withval else tmpfilesdir='${prefix}/lib/tmpfiles.d' fi # set default early default_udevdir=/lib/udev if test x"$with_udev" = x || \ test x"$with_udev" = xyes ; then if test x"$PKG_CONFIG" != x; then udevdir=$($PKG_CONFIG --variable=udevdir udev) fi if test x"$udevdir" = x; then { $as_echo "$as_me:${as_lineno-$LINENO}: Could not detect udev rules directory, using default" >&5 $as_echo "$as_me: Could not detect udev rules directory, using default" >&6;} udevdir=$default_udevdir fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using udev rules directory: $udevdir" >&5 $as_echo "Using udev rules directory: $udevdir" >&6; } else udevdir=$default_udevdir fi udevrulesdir=$udevdir/rules.d # Check whether --with-initscripttype was given. if test "${with_initscripttype+set}" = set; then : withval=$with_initscripttype; fi case "$with_initscripttype" in "") if grep -ql systemd /sbin/init ; then initscripttype=systemd else initscripttype=sysv fi ;; sysv|systemd|both|none) initscripttype=$with_initscripttype ;; *) as_fn_error $? "Illegal value -$with_initscripttype- for option --with-initscripttype" "$LINENO" 5 ;; esac # Check whether --with-drbdmon was given. if test "${with_drbdmon+set}" = set; then : withval=$with_drbdmon; WITH_DRBDMON=$withval fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 $as_echo_n "checking whether ln -s works... " >&6; } LN_S=$as_ln_s if test "$LN_S" = "ln -s"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 $as_echo "no, using $LN_S" >&6; } fi # Extract the first word of "sed", so it can be a program name with args. set dummy sed; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_SED+:} false; then : $as_echo_n "(cached) " >&6 else case $SED in [\\/]* | ?:[\\/]*) ac_cv_path_SED="$SED" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_SED="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi SED=$ac_cv_path_SED if test -n "$SED"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SED" >&5 $as_echo "$SED" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "grep", so it can be a program name with args. set dummy grep; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else case $GREP in [\\/]* | ?:[\\/]*) ac_cv_path_GREP="$GREP" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_GREP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi GREP=$ac_cv_path_GREP if test -n "$GREP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GREP" >&5 $as_echo "$GREP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "flex", so it can be a program name with args. set dummy flex; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_FLEX+:} false; then : $as_echo_n "(cached) " >&6 else case $FLEX in [\\/]* | ?:[\\/]*) ac_cv_path_FLEX="$FLEX" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_FLEX="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi FLEX=$ac_cv_path_FLEX if test -n "$FLEX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $FLEX" >&5 $as_echo "$FLEX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "rpmbuild", so it can be a program name with args. set dummy rpmbuild; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_RPMBUILD+:} false; then : $as_echo_n "(cached) " >&6 else case $RPMBUILD in [\\/]* | ?:[\\/]*) ac_cv_path_RPMBUILD="$RPMBUILD" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_RPMBUILD="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi RPMBUILD=$ac_cv_path_RPMBUILD if test -n "$RPMBUILD"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RPMBUILD" >&5 $as_echo "$RPMBUILD" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "xsltproc", so it can be a program name with args. set dummy xsltproc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_XSLTPROC+:} false; then : $as_echo_n "(cached) " >&6 else case $XSLTPROC in [\\/]* | ?:[\\/]*) ac_cv_path_XSLTPROC="$XSLTPROC" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_XSLTPROC="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi XSLTPROC=$ac_cv_path_XSLTPROC if test -n "$XSLTPROC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XSLTPROC" >&5 $as_echo "$XSLTPROC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "tar", so it can be a program name with args. set dummy tar; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_TAR+:} false; then : $as_echo_n "(cached) " >&6 else case $TAR in [\\/]* | ?:[\\/]*) ac_cv_path_TAR="$TAR" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_TAR="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi TAR=$ac_cv_path_TAR if test -n "$TAR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $TAR" >&5 $as_echo "$TAR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "git", so it can be a program name with args. set dummy git; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_GIT+:} false; then : $as_echo_n "(cached) " >&6 else case $GIT in [\\/]* | ?:[\\/]*) ac_cv_path_GIT="$GIT" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_GIT="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi GIT=$ac_cv_path_GIT if test -n "$GIT"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GIT" >&5 $as_echo "$GIT" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "dpkg-buildpackage", so it can be a program name with args. set dummy dpkg-buildpackage; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_DPKG_BUILDPACKAGE+:} false; then : $as_echo_n "(cached) " >&6 else case $DPKG_BUILDPACKAGE in [\\/]* | ?:[\\/]*) ac_cv_path_DPKG_BUILDPACKAGE="$DPKG_BUILDPACKAGE" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_DPKG_BUILDPACKAGE="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi DPKG_BUILDPACKAGE=$ac_cv_path_DPKG_BUILDPACKAGE if test -n "$DPKG_BUILDPACKAGE"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DPKG_BUILDPACKAGE" >&5 $as_echo "$DPKG_BUILDPACKAGE" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "udevadm", so it can be a program name with args. set dummy udevadm; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_UDEVADM+:} false; then : $as_echo_n "(cached) " >&6 else case $UDEVADM in [\\/]* | ?:[\\/]*) ac_cv_path_UDEVADM="$UDEVADM" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /sbin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_UDEVADM="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_UDEVADM" && ac_cv_path_UDEVADM="false" ;; esac fi UDEVADM=$ac_cv_path_UDEVADM if test -n "$UDEVADM"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $UDEVADM" >&5 $as_echo "$UDEVADM" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi # Extract the first word of "udevinfo", so it can be a program name with args. set dummy udevinfo; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_UDEVINFO+:} false; then : $as_echo_n "(cached) " >&6 else case $UDEVINFO in [\\/]* | ?:[\\/]*) ac_cv_path_UDEVINFO="$UDEVINFO" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in /sbin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_UDEVINFO="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_UDEVINFO" && ac_cv_path_UDEVINFO="false" ;; esac fi UDEVINFO=$ac_cv_path_UDEVINFO if test -n "$UDEVINFO"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $UDEVINFO" >&5 $as_echo "$UDEVINFO" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test -z "$CC"; then as_fn_error $? "Cannot build utils without a C compiler." "$LINENO" 5 fi if test -z $FLEX; then as_fn_error $? "Cannot build utils without flex." "$LINENO" 5 fi if [ $WITH_DRBDMON == "yes" ] ; then ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu if test -z "$CXX"; then if test -n "$CCC"; then CXX=$CCC else if test -n "$ac_tool_prefix"; then for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 $as_echo "$CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CXX" && break done fi if test -z "$CXX"; then ac_ct_CXX=$CXX for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CXX+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CXX="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 $as_echo "$ac_ct_CXX" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CXX" && break done if test "x$ac_ct_CXX" = x; then CXX="g++" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX fi fi fi fi # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 $as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } if ${ac_cv_cxx_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 $as_echo "$ac_cv_cxx_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GXX=yes else GXX= fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 $as_echo_n "checking whether $CXX accepts -g... " >&6; } if ${ac_cv_prog_cxx_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else CXXFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : else ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 $as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then if test "$GXX" = yes; then CXXFLAGS="-g -O2" else CXXFLAGS="-g" fi else if test "$GXX" = yes; then CXXFLAGS="-O2" else CXXFLAGS= fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ax_cxx_compile_cxx11_required=false ac_ext=cpp ac_cpp='$CXXCPP $CPPFLAGS' ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_cxx_compiler_gnu ac_success=no { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5 $as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; } if ${ax_cv_cxx_compile_cxx11+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = (){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = (int i, int j){ return i + j; }(1, 2); auto b = () -> int { return '0'; }(); auto c = =(){ return a + b; }(); auto d = &(){ return c; }(); auto e = a, &b(int x) mutable { const auto identity = (int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = (){ return 0; }; const auto unary = (int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = (nullary_t f){ return f(); }; const auto higher2nd = unary(nullary_t f1){ return unary, f1(unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : ax_cv_cxx_compile_cxx11=yes else ax_cv_cxx_compile_cxx11=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5 $as_echo "$ax_cv_cxx_compile_cxx11" >&6; } if test x$ax_cv_cxx_compile_cxx11 = xyes; then ac_success=yes fi if test x$ac_success = xno; then for switch in -std=gnu++11 -std=gnu++0x; do cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 $as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } if eval \${$cachevar+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_CXX="$CXX" CXX="$CXX $switch" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = (){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = (int i, int j){ return i + j; }(1, 2); auto b = () -> int { return '0'; }(); auto c = =(){ return a + b; }(); auto d = &(){ return c; }(); auto e = a, &b(int x) mutable { const auto identity = (int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = (){ return 0; }; const auto unary = (int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = (nullary_t f){ return f(); }; const auto higher2nd = unary(nullary_t f1){ return unary, f1(unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval $cachevar=yes else eval $cachevar=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXX="$ac_save_CXX" fi eval ac_res=\$$cachevar { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi if test x$ac_success = xno; then for switch in -std=c++11 -std=c++0x +std=c++11 "-h std=c++11"; do cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 $as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } if eval \${$cachevar+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_CXX="$CXX" CXX="$CXX $switch" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ // If the compiler admits that it is not ready for C++11, why torture it? // Hopefully, this will speed up the test. #ifndef __cplusplus #error "This is not a C++ compiler" #elif __cplusplus < 201103L #error "This is not a C++11 compiler" #else namespace cxx11 { namespace test_static_assert { template struct check { static_assert(sizeof(int) <= sizeof(T), "not big enough"); }; } namespace test_final_override { struct Base { virtual void f() {} }; struct Derived : public Base { virtual void f() override {} }; } namespace test_double_right_angle_brackets { template < typename T > struct check {}; typedef check single_type; typedef check> double_type; typedef check>> triple_type; typedef check>>> quadruple_type; } namespace test_decltype { int f() { int a = 1; decltype(a) b = 2; return a + b; } } namespace test_type_deduction { template < typename T1, typename T2 > struct is_same { static const bool value = false; }; template < typename T > struct is_same { static const bool value = true; }; template < typename T1, typename T2 > auto add(T1 a1, T2 a2) -> decltype(a1 + a2) { return a1 + a2; } int test(const int c, volatile int v) { static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == false, ""); auto ac = c; auto av = v; auto sumi = ac + av + 'x'; auto sumf = ac + av + 1.0; static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == true, ""); static_assert(is_same::value == false, ""); static_assert(is_same::value == true, ""); return (sumf > 0.0) ? sumi : add(c, v); } } namespace test_noexcept { int f() { return 0; } int g() noexcept { return 0; } static_assert(noexcept(f()) == false, ""); static_assert(noexcept(g()) == true, ""); } namespace test_constexpr { template < typename CharT > unsigned long constexpr strlen_c_r(const CharT *const s, const unsigned long acc) noexcept { return *s ? strlen_c_r(s + 1, acc + 1) : acc; } template < typename CharT > unsigned long constexpr strlen_c(const CharT *const s) noexcept { return strlen_c_r(s, 0UL); } static_assert(strlen_c("") == 0UL, ""); static_assert(strlen_c("1") == 1UL, ""); static_assert(strlen_c("example") == 7UL, ""); static_assert(strlen_c("another\0example") == 7UL, ""); } namespace test_rvalue_references { template < int N > struct answer { static constexpr int value = N; }; answer<1> f(int&) { return answer<1>(); } answer<2> f(const int&) { return answer<2>(); } answer<3> f(int&&) { return answer<3>(); } void test() { int i = 0; const int c = 0; static_assert(decltype(f(i))::value == 1, ""); static_assert(decltype(f(c))::value == 2, ""); static_assert(decltype(f(0))::value == 3, ""); } } namespace test_uniform_initialization { struct test { static const int zero {}; static const int one {1}; }; static_assert(test::zero == 0, ""); static_assert(test::one == 1, ""); } namespace test_lambdas { void test1() { auto lambda1 = (){}; auto lambda2 = lambda1; lambda1(); lambda2(); } int test2() { auto a = (int i, int j){ return i + j; }(1, 2); auto b = () -> int { return '0'; }(); auto c = =(){ return a + b; }(); auto d = &(){ return c; }(); auto e = a, &b(int x) mutable { const auto identity = (int y){ return y; }; for (auto i = 0; i < a; ++i) a += b--; return x + identity(a + b); }(0); return a + b + c + d + e; } int test3() { const auto nullary = (){ return 0; }; const auto unary = (int x){ return x; }; using nullary_t = decltype(nullary); using unary_t = decltype(unary); const auto higher1st = (nullary_t f){ return f(); }; const auto higher2nd = unary(nullary_t f1){ return unary, f1(unary_t f2){ return f2(unary(f1())); }; }; return higher1st(nullary) + higher2nd(nullary)(unary); } } namespace test_variadic_templates { template struct sum; template struct sum { static constexpr auto value = N0 + sum::value; }; template <> struct sum<> { static constexpr auto value = 0; }; static_assert(sum<>::value == 0, ""); static_assert(sum<1>::value == 1, ""); static_assert(sum<23>::value == 23, ""); static_assert(sum<1, 2>::value == 3, ""); static_assert(sum<5, 5, 11>::value == 21, ""); static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); } // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function // because of this. namespace test_template_alias_sfinae { struct foo {}; template using member = typename T::member_type; template void func(...) {} template void func(member*) {} void test(); void test() { func(0); } } } // namespace cxx11 #endif // __cplusplus >= 201103L _ACEOF if ac_fn_cxx_try_compile "$LINENO"; then : eval $cachevar=yes else eval $cachevar=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CXX="$ac_save_CXX" fi eval ac_res=\$$cachevar { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } if eval test x\$$cachevar = xyes; then CXX="$CXX $switch" if test -n "$CXXCPP" ; then CXXCPP="$CXXCPP $switch" fi ac_success=yes break fi done fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test x$ax_cxx_compile_cxx11_required = xtrue; then if test x$ac_success = xno; then as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5 fi fi if test x$ac_success = xno; then HAVE_CXX11=0 { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5 $as_echo "$as_me: No compiler with C++11 support was found" >&6;} else HAVE_CXX11=1 $as_echo "#define HAVE_CXX11 1" >>confdefs.h fi if test -z "$ac_ct_CXX" -o "$HAVE_CXX11" = "0"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: No C++11 compiler found, disabling drbdmon build." >&5 $as_echo "$as_me: WARNING: No C++11 compiler found, disabling drbdmon build." >&2;} WITH_DRBDMON=no fi fi if test -z $RPMBUILD; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: No rpmbuild found, building RPM packages is disabled." >&5 $as_echo "$as_me: WARNING: No rpmbuild found, building RPM packages is disabled." >&2;} fi if test -z $DPKG_BUILDPACKAGE; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: No dpkg-buildpackage found, building Debian packages is disabled." >&5 $as_echo "$as_me: WARNING: No dpkg-buildpackage found, building Debian packages is disabled." >&2;} fi if test -z $XSLTPROC; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cannot build man pages without xsltproc. You may safely ignore this warning when building from a tarball." >&5 $as_echo "$as_me: WARNING: Cannot build man pages without xsltproc. You may safely ignore this warning when building from a tarball." >&2;} XSLTPROC=xsltproc fi if test -z $GIT; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Cannot update buildtag without git. You may safely ignore this warning when building from a tarball." >&5 $as_echo "$as_me: WARNING: Cannot update buildtag without git. You may safely ignore this warning when building from a tarball." >&2;} fi if test $UDEVADM = false && test $UDEVINFO = false; then if test "$WITH_UDEV" = "yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: udev support enabled, but neither udevadm nor udevinfo found on this system." >&5 $as_echo "$as_me: WARNING: udev support enabled, but neither udevadm nor udevinfo found on this system." >&2;} fi fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include static unsigned int dummy = CTRL_CMD_DELMCAST_GRP; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : $as_echo "#define HAVE_CTRL_CMD_DELMCAST_GRP 1" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext BASH_COMPLETION_SUFFIX="" UDEV_RULE_SUFFIX="" RPM_DIST_TAG="" RPM_BUILDREQ_DEFAULT="gcc flex glibc-devel make" RPM_REQ_DRBDMON="" RPM_SUBPACKAGE_NOARCH="" RPM_REQ_PACEMAKER="" RPM_REQ_HEARTBEAT="" RPM_REQ_BASH_COMPLETION="" RPM_REQ_XEN="" RPM_REQ_CHKCONFIG_POST="" RPM_REQ_CHKCONFIG_PREUN="" if test -z $DISTRO; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for /etc/redhat-release" >&5 $as_echo_n "checking for /etc/redhat-release... " >&6; } if ${ac_cv_file__etc_redhat_release+:} false; then : $as_echo_n "(cached) " >&6 else test "$cross_compiling" = yes && as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "/etc/redhat-release"; then ac_cv_file__etc_redhat_release=yes else ac_cv_file__etc_redhat_release=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_file__etc_redhat_release" >&5 $as_echo "$ac_cv_file__etc_redhat_release" >&6; } if test "x$ac_cv_file__etc_redhat_release" = xyes; then : DISTRO="redhat" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for /etc/debian_version" >&5 $as_echo_n "checking for /etc/debian_version... " >&6; } if ${ac_cv_file__etc_debian_version+:} false; then : $as_echo_n "(cached) " >&6 else test "$cross_compiling" = yes && as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "/etc/debian_version"; then ac_cv_file__etc_debian_version=yes else ac_cv_file__etc_debian_version=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_file__etc_debian_version" >&5 $as_echo "$ac_cv_file__etc_debian_version" >&6; } if test "x$ac_cv_file__etc_debian_version" = xyes; then : DISTRO="debian" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for /etc/SuSE-release" >&5 $as_echo_n "checking for /etc/SuSE-release... " >&6; } if ${ac_cv_file__etc_SuSE_release+:} false; then : $as_echo_n "(cached) " >&6 else test "$cross_compiling" = yes && as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "/etc/SuSE-release"; then ac_cv_file__etc_SuSE_release=yes else ac_cv_file__etc_SuSE_release=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_file__etc_SuSE_release" >&5 $as_echo "$ac_cv_file__etc_SuSE_release" >&6; } if test "x$ac_cv_file__etc_SuSE_release" = xyes; then : DISTRO="suse" fi fi case "$DISTRO" in redhat) test -z $INITDIR && INITDIR="$sysconfdir/rc.d/init.d" RPM_DIST_TAG="%{?dist}" RPM_BUILDREQ_DEFAULT="flex" RPM_REQ_CHKCONFIG_POST="Requires(post): chkconfig" RPM_REQ_CHKCONFIG_PREUN="Requires(preun): chkconfig" { $as_echo "$as_me:${as_lineno-$LINENO}: configured for Red Hat (includes Fedora, RHEL, CentOS)." >&5 $as_echo "$as_me: configured for Red Hat (includes Fedora, RHEL, CentOS)." >&6;} { $as_echo "$as_me:${as_lineno-$LINENO}: checking for /etc/fedora-release" >&5 $as_echo_n "checking for /etc/fedora-release... " >&6; } if ${ac_cv_file__etc_fedora_release+:} false; then : $as_echo_n "(cached) " >&6 else test "$cross_compiling" = yes && as_fn_error $? "cannot check for file existence when cross compiling" "$LINENO" 5 if test -r "/etc/fedora-release"; then ac_cv_file__etc_fedora_release=yes else ac_cv_file__etc_fedora_release=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_file__etc_fedora_release" >&5 $as_echo "$ac_cv_file__etc_fedora_release" >&6; } if test "x$ac_cv_file__etc_fedora_release" = xyes; then : SUB_DISTRO="fedora" else SUB_DISTRO="RHEL" fi if test "$SUB_DISTRO" = "fedora"; then # pacemaker, heartbeat and bash-completion are not available in RHEL # Xen: Be relaxed on RHEL (hassle free update). Be strict on Fedora RPM_REQ_PACEMAKER="Requires: pacemaker" RPM_REQ_HEARTBEAT="Requires: heartbeat" RPM_REQ_BASH_COMPLETION="Requires: bash-completion" RPM_REQ_XEN="Requires: xen" fi ;; debian) { $as_echo "$as_me:${as_lineno-$LINENO}: configured for Debian (includes Ubuntu)." >&5 $as_echo "$as_me: configured for Debian (includes Ubuntu)." >&6;} ;; suse) BASH_COMPLETION_SUFFIX=".sh" # RPM_REQ_CHKCONFIG_POST="" chkconfig is part of aaa_base on suse # RPM_REQ_CHKCONFIG_PREUN="" chkconfig is part of aaa_base on suse { $as_echo "$as_me:${as_lineno-$LINENO}: configured for SUSE (includes openSUSE, SLES)." >&5 $as_echo "$as_me: configured for SUSE (includes openSUSE, SLES)." >&6;} RPM_REQ_BASH_COMPLETION="Requires: bash" # The following are disabled for hassle free updates: # RPM_REQ_XEN="Requires: xen" # RPM_REQ_PACEMAKER="Requires: pacemaker" # RPM_REQ_HEARTBEAT="Requires: heartbeat" # Unfortunately gcc on SLES9 is broken with -O2. Works with -O1 if grep -q 'VERSION = 9' /etc/SuSE-release; then CFLAGS="-g -O1" fi ;; "") { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to determine what distribution we are running on. Distribution-specific features will be disabled." >&5 $as_echo "$as_me: WARNING: Unable to determine what distribution we are running on. Distribution-specific features will be disabled." >&2;} ;; esac test -z $INITDIR && INITDIR="$sysconfdir/init.d" if test "$WITH_UDEV" = "yes"; then udev_version=`$UDEVADM version 2>/dev/null` || udev_version=`$UDEVINFO -V | cut -d " " -f 3` if test -z $udev_version || test $udev_version -lt 85; then UDEV_RULE_SUFFIX=".disabled" { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Obsolete or unknown udev version. Installing disabled udev rules." >&5 $as_echo "$as_me: WARNING: Obsolete or unknown udev version. Installing disabled udev rules." >&2;} fi fi if test "$WITH_NOARCH_SUBPACKAGES" = "yes"; then RPM_SUBPACKAGE_NOARCH="BuildArch: noarch" fi DRBD_LIB_DIR=$localstatedir/lib/$PACKAGE_TARNAME DRBD_RUN_DIR=$localstatedir/run/$PACKAGE_TARNAME DRBD_LOCK_DIR=$localstatedir/lock DRBD_CONFIG_DIR=$sysconfdir cat >>confdefs.h <<_ACEOF #define DRBD_LIB_DIR "$DRBD_LIB_DIR" _ACEOF cat >>confdefs.h <<_ACEOF #define DRBD_RUN_DIR "$DRBD_RUN_DIR" _ACEOF cat >>confdefs.h <<_ACEOF #define DRBD_LOCK_DIR "$DRBD_LOCK_DIR" _ACEOF cat >>confdefs.h <<_ACEOF #define DRBD_CONFIG_DIR "$DRBD_CONFIG_DIR" _ACEOF if test "$WITH_83_SUPPORT" = "yes"; then $as_echo "#define DRBD_LEGACY_83 1" >>confdefs.h fi if test "$WITH_84_SUPPORT" = "yes"; then $as_echo "#define DRBD_LEGACY_84 1" >>confdefs.h fi if test -z $SPECMODE; then ac_config_files="$ac_config_files Makefile user/shared/Makefile user/v9/Makefile user/v83/Makefile user/v84/Makefile scripts/Makefile documentation/v9/Makefile documentation/v83/Makefile documentation/v84/Makefile scripts/drbd.rules user/drbdmon/Makefile" ac_config_headers="$ac_config_headers user/shared/config.h" else ac_config_files="$ac_config_files drbd.spec" fi cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by DRBD $as_me 8.9.10, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ DRBD config.status 8.9.10 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "user/shared/Makefile") CONFIG_FILES="$CONFIG_FILES user/shared/Makefile" ;; "user/v9/Makefile") CONFIG_FILES="$CONFIG_FILES user/v9/Makefile" ;; "user/v83/Makefile") CONFIG_FILES="$CONFIG_FILES user/v83/Makefile" ;; "user/v84/Makefile") CONFIG_FILES="$CONFIG_FILES user/v84/Makefile" ;; "scripts/Makefile") CONFIG_FILES="$CONFIG_FILES scripts/Makefile" ;; "documentation/v9/Makefile") CONFIG_FILES="$CONFIG_FILES documentation/v9/Makefile" ;; "documentation/v83/Makefile") CONFIG_FILES="$CONFIG_FILES documentation/v83/Makefile" ;; "documentation/v84/Makefile") CONFIG_FILES="$CONFIG_FILES documentation/v84/Makefile" ;; "scripts/drbd.rules") CONFIG_FILES="$CONFIG_FILES scripts/drbd.rules" ;; "user/drbdmon/Makefile") CONFIG_FILES="$CONFIG_FILES user/drbdmon/Makefile" ;; "user/shared/config.h") CONFIG_HEADERS="$CONFIG_HEADERS user/shared/config.h" ;; "drbd.spec") CONFIG_FILES="$CONFIG_FILES drbd.spec" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi drbd-utils-8.9.10/user/0000755000175000017500000000000013027242657014544 5ustar apoikosapoikosdrbd-utils-8.9.10/user/shared/0000755000175000017500000000000013027242657016012 5ustar apoikosapoikosdrbd-utils-8.9.10/user/shared/Makefile.in0000644000175000017500000000700413011114651020041 0ustar apoikosapoikos# Makefile for drbd.o # # This file is part of DRBD by Philipp Reisner and Lars Ellenberg. # # drbd is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. # # variables set by configure DISTRO = @DISTRO@ prefix = @prefix@ exec_prefix = @exec_prefix@ localstatedir = @localstatedir@ datarootdir = @datarootdir@ datadir = @datadir@ sbindir = @sbindir@ sysconfdir = @sysconfdir@ BASH_COMPLETION_SUFFIX = @BASH_COMPLETION_SUFFIX@ UDEV_RULE_SUFFIX = @UDEV_RULE_SUFFIX@ INITDIR = @INITDIR@ LIBDIR = @prefix@/lib/@PACKAGE_TARNAME@ CC = @CC@ CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ LN_S = @LN_S@ DRBD_LIB_DIR = @DRBD_LIB_DIR@ DRBD_RUN_DIR = @DRBD_RUN_DIR@ DRBD_LOCK_DIR = @DRBD_LOCK_DIR@ DRBD_CONFIG_DIR = @DRBD_CONFIG_DIR@ # features enabled or disabled by configure WITH_83_SUPPORT = @WITH_83_SUPPORT@ WITH_84_SUPPORT = @WITH_84_SUPPORT@ WITH_UDEV = @WITH_UDEV@ WITH_XEN = @WITH_XEN@ WITH_PACEMAKER = @WITH_PACEMAKER@ WITH_RGMANAGER = @WITH_RGMANAGER@ WITH_BASHCOMPLETION = @WITH_BASHCOMPLETION@ # for some reason some of the commands below only work correctly in bash, # and not in e.g. dash. I'm too lazy to fix it to be compatible. SHELL=/bin/bash # variables meant to be overridden from the make command line DESTDIR ?= / CFLAGS += -Wall -I../../drbd-headers -I.. -I. CFLAGS += $(EXTRA_CFLAGS) .PHONY: drbd_buildtag.c all: drbdmeta_scanner.c drbd_buildtag.c drbd_buildtag.c: @set -e; exec > $@.new; \ echo -e "/* automatically generated. DO NOT EDIT. */"; \ echo -e "#include "; \ echo -e "const char *drbd_buildtag(void)\n{"; \ if test -e ../../.git && GITHEAD=$$(git rev-parse HEAD); then \ GITDIFF=$$(cd .. && git diff --name-only HEAD | \ tr -s '\t\n' ' ' | \ sed -e 's/^/ /;s/ *$$//'); \ echo -e "\treturn \"GIT-hash: $$GITHEAD$$GITDIFF\""; \ elif ! test -e $@ ; then \ echo >&2 "$@ not found."; \ test -e ../../.git && \ >&2 printf "%s\n" \ "git did not work, but this looks like a git checkout?" \ "Install git and try again." || \ echo >&2 "Your DRBD source tree is broken. Unpack again."; \ exit 1; \ else \ grep return $@ ; \ fi ; \ echo -e "\t\t\" build by $$USER@$$HOSTNAME, `date "+%F %T"`\";\n}"; \ mv -f $@.new $@ drbdmeta_scanner.c: drbdmeta_scanner.fl drbdmeta_parser.h flex -s -odrbdmeta_scanner.c drbdmeta_scanner.fl # drbdmeta is in this directory; but some header files it depends on are in # v9, so that's where it gets built and installed. clean: rm -f drbdmeta_scanner.c ! test -e ../../.git || rm -f drbd_buildtag.c distclean: clean install: ; @true uninstall: ; @true .PHONY: install uninstall clean distclean ../../configure: @echo "please (re-)run ./autogen.sh with appropriate arguments"; exit 1 ../../config.status: ../../configure @echo "please (re-)run ./configure with appropriate arguments"; exit 1 Makefile.in: ; Makefile: Makefile.in ../../config.status cd ../.. && ./config.status user/shared/Makefile drbd-utils-8.9.10/user/shared/libgenl.c0000644000175000017500000006214313002133653017564 0ustar apoikosapoikos#include "libgenl.h" #include #include #include #include #include int genl_join_mc_group(struct genl_sock *s, const char *name) { int g_id; int i; BUG_ON(!s || !s->s_family); for (i = 0; i < 32; i++) { if (!s->s_family->mc_groups[i].id) continue; if (strcmp(s->s_family->mc_groups[i].name, name)) continue; g_id = s->s_family->mc_groups[i].id; return setsockopt(s->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &g_id, sizeof(g_id)); } return -2; } #define DO_OR_LOG_AND_FAIL(x) \ do { \ int err = x; \ if (err) { \ dbg(1, "%s failed: %d %s\n", \ #x, err, strerror(errno)); \ goto fail; \ } \ } while(0) static struct genl_sock *genl_connect(__u32 nl_groups) { struct genl_sock *s = calloc(1, sizeof(*s)); socklen_t sock_len; int bsz = 1 << 20; if (!s) return NULL; /* autobind; kernel is responsible to give us something unique * in bind() below. */ s->s_local.nl_pid = 0; s->s_local.nl_family = AF_NETLINK; /* * If we want to receive multicast traffic on this socket, kernels * before v2.6.23-rc1 require us to indicate which multicast groups we * are interested in in nl_groups. */ s->s_local.nl_groups = nl_groups; s->s_peer.nl_family = AF_NETLINK; /* start with some sane sequence number */ s->s_seq_expect = s->s_seq_next = time(0); s->s_fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_GENERIC); if (s->s_fd == -1) goto fail; sock_len = sizeof(s->s_local); DO_OR_LOG_AND_FAIL(setsockopt(s->s_fd, SOL_SOCKET, SO_SNDBUF, &bsz, sizeof(bsz))); DO_OR_LOG_AND_FAIL(setsockopt(s->s_fd, SOL_SOCKET, SO_RCVBUF, &bsz, sizeof(bsz))); DO_OR_LOG_AND_FAIL(bind(s->s_fd, (struct sockaddr*) &s->s_local, sizeof(s->s_local))); DO_OR_LOG_AND_FAIL(getsockname(s->s_fd, (struct sockaddr*) &s->s_local, &sock_len)); dbg(3, "bound socket to nl_pid:%u, my pid:%u, len:%u, sizeof:%u\n", s->s_local.nl_pid, getpid(), (unsigned)sock_len, (unsigned)sizeof(s->s_local)); return s; fail: free(s); return NULL; } #undef DO_OR_LOG_AND_FAIL static int do_send(int fd, const void *buf, int len) { int c; while ((c = write(fd, buf, len)) < len) { if (c == -1) { if (errno == EINTR) continue; return -1; } buf += c; len -= c; } return 0; } int genl_send(struct genl_sock *s, struct msg_buff *msg) { struct nlmsghdr *n = (struct nlmsghdr *)msg->data; n->nlmsg_len = msg->tail - msg->data; n->nlmsg_flags |= NLM_F_REQUEST; n->nlmsg_seq = s->s_seq_expect = s->s_seq_next++; n->nlmsg_pid = s->s_local.nl_pid; #define LOCAL_DEBUG_LEVEL 3 #if LOCAL_DEBUG_LEVEL <= DEBUG_LEVEL struct genlmsghdr *g = nlmsg_data(n); dbg(LOCAL_DEBUG_LEVEL, "sending %smessage, pid:%u seq:%u, g.cmd/version:%u/%u", n->nlmsg_type == GENL_ID_CTRL ? "ctrl " : "", n->nlmsg_pid, n->nlmsg_seq, g->cmd, g->version); #endif return do_send(s->s_fd, msg->data, n->nlmsg_len); } /* "inspired" by libnl nl_recv() * You pass in one iovec, which may contain pre-allocated buffer space, * obtained by malloc(). It will be realloc()ed on demand. * Caller is responsible for free()ing it up on return, * regardless of return code. */ int genl_recv_timeout(struct genl_sock *s, struct iovec *iov, int timeout_ms) { struct sockaddr_nl addr; struct pollfd pfd; int flags; struct msghdr msg = { .msg_name = &addr, .msg_namelen = sizeof(struct sockaddr_nl), .msg_iov = iov, .msg_iovlen = 1, .msg_control = NULL, .msg_controllen = 0, .msg_flags = 0, }; int n; if (!iov->iov_len) { iov->iov_len = 8192; iov->iov_base = malloc(iov->iov_len); } flags = MSG_PEEK; retry: pfd.fd = s->s_fd; pfd.events = POLLIN; if ((poll(&pfd, 1, timeout_ms) != 1) || !(pfd.revents & POLLIN)) return 0; /* which is E_RCV_TIMEDOUT */ /* for most cases this method will memcopy twice, as the default buffer * is large enough. But for those few other cases, we now have a * chance to realloc before the rest of the datagram is discarded. */ n = recvmsg(s->s_fd, &msg, flags); if (!n) return 0; else if (n < 0) { if (errno == EINTR) { dbg(3, "recvmsg() returned EINTR, retrying\n"); goto retry; } else if (errno == EAGAIN) { dbg(3, "recvmsg() returned EAGAIN, aborting\n"); return 0; } else if (errno == ENOBUFS) { dbg(3, "recvmsg() returned ENOBUFS\n"); return -E_RCV_ENOBUFS; } else { dbg(3, "recvmsg() returned %d, errno = %d\n", n, errno); return -E_RCV_FAILED; } } if (iov->iov_len < (unsigned)n || msg.msg_flags & MSG_TRUNC) { /* Provided buffer is not long enough, enlarge it * and try again. */ iov->iov_len *= 2; iov->iov_base = realloc(iov->iov_base, iov->iov_len); goto retry; } else if (flags != 0) { /* Buffer is big enough, do the actual reading */ flags = 0; goto retry; } if (msg.msg_namelen != sizeof(struct sockaddr_nl)) return -E_RCV_NO_SOURCE_ADDR; if (addr.nl_pid != 0) { dbg(3, "ignoring message from sender pid %u != 0\n", addr.nl_pid); goto retry; } return n; } /* Note that one datagram may contain multiple netlink messages * (e.g. for a dump response). This only checks the _first_ message, * caller has to iterate over multiple messages with nlmsg_for_each_msg() * when necessary. */ int genl_recv_msgs(struct genl_sock *s, struct iovec *iov, char **err_desc, int timeout_ms) { struct nlmsghdr *nlh; int c = genl_recv_timeout(s, iov, timeout_ms); if (c <= 0) { if (err_desc) *err_desc = (c == -E_RCV_TIMEDOUT) ? "timed out waiting for reply" : (c == -E_RCV_NO_SOURCE_ADDR) ? "no source address!" : ( c == -E_RCV_ENOBUFS) ? "packets droped, socket receive buffer overrun" : "failed to receive netlink reply"; return c; } nlh = (struct nlmsghdr*)iov->iov_base; if (!nlmsg_ok(nlh, c)) { if (err_desc) *err_desc = "truncated message in netlink reply"; return -E_RCV_MSG_TRUNC; } if (s->s_seq_expect && nlh->nlmsg_seq != s->s_seq_expect) { dbg(2, "sequence mismatch: 0x%x != 0x%x, type:%x flags:%x sportid:%x\n", nlh->nlmsg_seq, s->s_seq_expect, nlh->nlmsg_type, nlh->nlmsg_flags, nlh->nlmsg_pid); if (err_desc) *err_desc = "sequence mismatch in netlink reply"; return -E_RCV_SEQ_MISMATCH; } if (nlh->nlmsg_type == NLMSG_NOOP || nlh->nlmsg_type == NLMSG_OVERRUN) { if (err_desc) *err_desc = "unexpected message type in reply"; return -E_RCV_UNEXPECTED_TYPE; } if (nlh->nlmsg_type == NLMSG_DONE) return -E_RCV_NLMSG_DONE; if (nlh->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *e = nlmsg_data(nlh); errno = -e->error; if (!errno) /* happens if you request NLM_F_ACK */ dbg(3, "got a positive ACK message for seq:%u", s->s_seq_expect); else { dbg(3, "got a NACK message for seq:%u, error:%d", s->s_seq_expect, e->error); if (err_desc) *err_desc = strerror(errno); } return -E_RCV_ERROR_REPLY; } /* good reply message(s) */ dbg(3, "received a good message for seq:%u", s->s_seq_expect); return c; } static struct genl_family genl_ctrl = { .id = GENL_ID_CTRL, .name = "nlctrl", .version = 0x2, .maxattr = CTRL_ATTR_MAX, }; struct genl_sock *genl_connect_to_family(struct genl_family *family) { struct genl_sock *s = NULL; struct msg_buff *msg; struct nlmsghdr *nlh; struct nlattr *nla; struct iovec iov = { .iov_len = 0 }; int rem; BUG_ON(!family); BUG_ON(!strlen(family->name)); msg = msg_new(DEFAULT_MSG_SIZE); if (!msg) { dbg(1, "could not allocate genl message"); goto out; } s = genl_connect(family->nl_groups); if (!s) { dbg(1, "error creating netlink socket"); goto out; } genlmsg_put(msg, &genl_ctrl, 0, CTRL_CMD_GETFAMILY); nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family->name); if (genl_send(s, msg)) { dbg(1, "failed to send netlink message"); free(s); s = NULL; goto out; } if (genl_recv_msgs(s, &iov, NULL, 3000) <= 0) { close(s->s_fd); free(s); s = NULL; goto out; } nlh = (struct nlmsghdr*)iov.iov_base; nla_for_each_attr(nla, nlmsg_attrdata(nlh, GENL_HDRLEN), nlmsg_attrlen(nlh, GENL_HDRLEN), rem) { switch (nla_type(nla)) { case CTRL_ATTR_FAMILY_ID: family->id = nla_get_u16(nla); dbg(2, "'%s' genl family id: %d", family->name, family->id); break; case CTRL_ATTR_FAMILY_NAME: break; #ifdef HAVE_CTRL_ATTR_VERSION case CTRL_ATTR_VERSION: family->version = nla_get_u32(nla); dbg(2, "'%s' genl family version: %d", family->name, family->version); break; #endif #ifdef HAVE_CTRL_ATTR_HDRSIZE case CTRL_ATTR_HDRSIZE: family->hdrsize = nla_get_u32(nla); dbg(2, "'%s' genl family hdrsize: %d", family->name, family->hdrsize); break; #endif #ifdef HAVE_CTRL_ATTR_MCAST_GROUPS case CTRL_ATTR_MCAST_GROUPS: { static struct nla_policy policy[] = { [CTRL_ATTR_MCAST_GRP_NAME] = { .type = NLA_NUL_STRING, .len = GENL_NAMSIZ }, [CTRL_ATTR_MCAST_GRP_ID] = { .type = NLA_U32 }, }; struct nlattr *ntb[__CTRL_ATTR_MCAST_GRP_MAX]; struct nlattr *idx; int tmp; int i = 0; nla_for_each_nested(idx, nla, tmp) { BUG_ON(i >= 32); nla_parse_nested(ntb, CTRL_ATTR_MCAST_GRP_MAX, idx, policy); if (ntb[CTRL_ATTR_MCAST_GRP_NAME] && ntb[CTRL_ATTR_MCAST_GRP_ID]) { struct genl_multicast_group *grp = &family->mc_groups[i++]; grp->id = nla_get_u32(ntb[CTRL_ATTR_MCAST_GRP_ID]); nla_strlcpy(grp->name, ntb[CTRL_ATTR_MCAST_GRP_NAME], sizeof(grp->name)); dbg(2, "'%s'-'%s' multicast group found (id: %u)\n", family->name, grp->name, grp->id); } } break; }; #endif default: ; } } if (!family->id) dbg(1, "genl family '%s' not found", family->name); else s->s_family = family; out: free(iov.iov_base); msg_free(msg); return s; } /* * Stripped down copy from linux-2.6.32/lib/nlattr.c * skb -> "msg_buff" * - Lars Ellenberg * * NETLINK Netlink attributes * * Authors: Thomas Graf * Alexey Kuznetsov */ #include #include static __u16 nla_attr_minlen[NLA_TYPE_MAX+1] __read_mostly = { [NLA_U8] = sizeof(__u8), [NLA_U16] = sizeof(__u16), [NLA_U32] = sizeof(__u32), [NLA_U64] = sizeof(__u64), [NLA_NESTED] = NLA_HDRLEN, }; static int validate_nla(struct nlattr *nla, int maxtype, const struct nla_policy *policy) { const struct nla_policy *pt; int minlen = 0, attrlen = nla_len(nla), type = nla_type(nla); if (type <= 0 || type > maxtype) return 0; pt = &policy[type]; BUG_ON(pt->type > NLA_TYPE_MAX); switch (pt->type) { case NLA_FLAG: if (attrlen > 0) return -ERANGE; break; case NLA_NUL_STRING: if (pt->len) minlen = min_t(int, attrlen, pt->len + 1); else minlen = attrlen; if (!minlen || memchr(nla_data(nla), '\0', minlen) == NULL) return -EINVAL; /* fall through */ case NLA_STRING: if (attrlen < 1) return -ERANGE; if (pt->len) { char *buf = nla_data(nla); if (buf[attrlen - 1] == '\0') attrlen--; if (attrlen > pt->len) return -ERANGE; } break; case NLA_BINARY: if (pt->len && attrlen > pt->len) return -ERANGE; break; case NLA_NESTED_COMPAT: if (attrlen < pt->len) return -ERANGE; if (attrlen < NLA_ALIGN(pt->len)) break; if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN) return -ERANGE; nla = nla_data(nla) + NLA_ALIGN(pt->len); if (attrlen < NLA_ALIGN(pt->len) + NLA_HDRLEN + nla_len(nla)) return -ERANGE; break; case NLA_NESTED: /* a nested attributes is allowed to be empty; if its not, * it must have a size of at least NLA_HDRLEN. */ if (attrlen == 0) break; default: if (pt->len) minlen = pt->len; else if (pt->type != NLA_UNSPEC) minlen = nla_attr_minlen[pt->type]; if (attrlen < minlen) return -ERANGE; } return 0; } /** * nla_validate - Validate a stream of attributes * @head: head of attribute stream * @len: length of attribute stream * @maxtype: maximum attribute type to be expected * @policy: validation policy * * Validates all attributes in the specified attribute stream against the * specified policy. Attributes with a type exceeding maxtype will be * ignored. See documenation of struct nla_policy for more details. * * Returns 0 on success or a negative error code. */ int nla_validate(struct nlattr *head, int len, int maxtype, const struct nla_policy *policy) { struct nlattr *nla; int rem, err; nla_for_each_attr(nla, head, len, rem) { err = validate_nla(nla, maxtype, policy); if (err < 0) goto errout; } err = 0; errout: return err; } /** * nla_policy_len - Determin the max. length of a policy * @policy: policy to use * @n: number of policies * * Determines the max. length of the policy. It is currently used * to allocated Netlink buffers roughly the size of the actual * message. * * Returns 0 on success or a negative error code. */ int nla_policy_len(const struct nla_policy *p, int n) { int i, len = 0; for (i = 0; i < n; i++, p++) { if (p->len) len += nla_total_size(p->len); else if (nla_attr_minlen[p->type]) len += nla_total_size(nla_attr_minlen[p->type]); } return len; } /** * nla_parse - Parse a stream of attributes into a tb buffer * @tb: destination array with maxtype+1 elements * @maxtype: maximum attribute type to be expected * @head: head of attribute stream * @len: length of attribute stream * @policy: validation policy * * Parses a stream of attributes and stores a pointer to each attribute in * the tb array accessable via the attribute type. Attributes with a type * exceeding maxtype will be silently ignored for backwards compatibility * reasons. policy may be set to NULL if no validation is required. * * Returns 0 on success or a negative error code. */ int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, const struct nla_policy *policy) { struct nlattr *nla; int rem, err; memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); nla_for_each_attr(nla, head, len, rem) { __u16 type = nla_type(nla); if (type > 0 && type <= maxtype) { if (policy) { err = validate_nla(nla, maxtype, policy); if (err < 0) goto errout; } tb[type] = nla; } } if (unlikely(rem > 0)) dbg(1, "netlink: %d bytes leftover after parsing " "attributes.\n", rem); err = 0; errout: if (err) dbg(1, "netlink: policy violation t:%d[%x] e:%d\n", nla_type(nla), nla->nla_type, err); return err; } /** * nla_find - Find a specific attribute in a stream of attributes * @head: head of attribute stream * @len: length of attribute stream * @attrtype: type of attribute to look for * * Returns the first attribute in the stream matching the specified type. */ struct nlattr *nla_find(struct nlattr *head, int len, int attrtype) { struct nlattr *nla; int rem; nla_for_each_attr(nla, head, len, rem) if (nla_type(nla) == attrtype) return nla; return NULL; } /** * nla_strlcpy - Copy string attribute payload into a sized buffer * @dst: where to copy the string to * @nla: attribute to copy the string from * @dstsize: size of destination buffer * * Copies at most dstsize - 1 bytes into the destination buffer. * The result is always a valid NUL-terminated string. Unlike * strlcpy the destination buffer is always padded out. * * Returns the length of the source buffer. */ size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) { size_t srclen = nla_len(nla); char *src = nla_data(nla); if (srclen > 0 && src[srclen - 1] == '\0') srclen--; if (dstsize > 0) { size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen; memset(dst, 0, dstsize); memcpy(dst, src, len); } return srclen; } /** * nla_memcpy - Copy a netlink attribute into another memory area * @dest: where to copy to memcpy * @src: netlink attribute to copy from * @count: size of the destination area * * Note: The number of bytes copied is limited by the length of * attribute's payload. memcpy * * Returns the number of bytes copied. */ int nla_memcpy(void *dest, const struct nlattr *src, int count) { int minlen = min_t(int, count, nla_len(src)); memcpy(dest, nla_data(src), minlen); return minlen; } /** * nla_memcmp - Compare an attribute with sized memory area * @nla: netlink attribute * @data: memory area * @size: size of memory area */ int nla_memcmp(const struct nlattr *nla, const void *data, size_t size) { int d = nla_len(nla) - size; if (d == 0) d = memcmp(nla_data(nla), data, size); return d; } /** * nla_strcmp - Compare a string attribute against a string * @nla: netlink string attribute * @str: another string */ int nla_strcmp(const struct nlattr *nla, const char *str) { int len = strlen(str) + 1; int d = nla_len(nla) - len; if (d == 0) d = memcmp(nla_data(nla), str, len); return d; } /** * __nla_reserve - reserve room for attribute on the msg * @msg: message buffer to reserve room on * @attrtype: attribute type * @attrlen: length of attribute payload * * Adds a netlink attribute header to a message buffer and reserves * room for the payload but does not copy it. * * The caller is responsible to ensure that the msg provides enough * tailroom for the attribute header and payload. */ struct nlattr *__nla_reserve(struct msg_buff *msg, int attrtype, int attrlen) { struct nlattr *nla; nla = (struct nlattr *) msg_put(msg, nla_total_size(attrlen)); nla->nla_type = attrtype; nla->nla_len = nla_attr_size(attrlen); memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); return nla; } /** * __nla_reserve_nohdr - reserve room for attribute without header * @msg: message buffer to reserve room on * @attrlen: length of attribute payload * * Reserves room for attribute payload without a header. * * The caller is responsible to ensure that the msg provides enough * tailroom for the payload. */ void *__nla_reserve_nohdr(struct msg_buff *msg, int attrlen) { void *start; start = msg_put(msg, NLA_ALIGN(attrlen)); memset(start, 0, NLA_ALIGN(attrlen)); return start; } /** * nla_reserve - reserve room for attribute on the msg * @msg: message buffer to reserve room on * @attrtype: attribute type * @attrlen: length of attribute payload * * Adds a netlink attribute header to a message buffer and reserves * room for the payload but does not copy it. * * Returns NULL if the tailroom of the msg is insufficient to store * the attribute header and payload. */ struct nlattr *nla_reserve(struct msg_buff *msg, int attrtype, int attrlen) { if (unlikely(msg_tailroom(msg) < nla_total_size(attrlen))) return NULL; return __nla_reserve(msg, attrtype, attrlen); } /** * nla_reserve_nohdr - reserve room for attribute without header * @msg: message buffer to reserve room on * @attrlen: length of attribute payload * * Reserves room for attribute payload without a header. * * Returns NULL if the tailroom of the msg is insufficient to store * the attribute payload. */ void *nla_reserve_nohdr(struct msg_buff *msg, int attrlen) { if (unlikely(msg_tailroom(msg) < NLA_ALIGN(attrlen))) return NULL; return __nla_reserve_nohdr(msg, attrlen); } /** * __nla_put - Add a netlink attribute to a message buffer * @msg: message buffer to add attribute to * @attrtype: attribute type * @attrlen: length of attribute payload * @data: head of attribute payload * * The caller is responsible to ensure that the msg provides enough * tailroom for the attribute header and payload. */ void __nla_put(struct msg_buff *msg, int attrtype, int attrlen, const void *data) { struct nlattr *nla; nla = __nla_reserve(msg, attrtype, attrlen); memcpy(nla_data(nla), data, attrlen); } /** * __nla_put_nohdr - Add a netlink attribute without header * @msg: message buffer to add attribute to * @attrlen: length of attribute payload * @data: head of attribute payload * * The caller is responsible to ensure that the msg provides enough * tailroom for the attribute payload. */ void __nla_put_nohdr(struct msg_buff *msg, int attrlen, const void *data) { void *start; start = __nla_reserve_nohdr(msg, attrlen); memcpy(start, data, attrlen); } /** * nla_put - Add a netlink attribute to a message buffer * @msg: message buffer to add attribute to * @attrtype: attribute type * @attrlen: length of attribute payload * @data: head of attribute payload * * Returns -EMSGSIZE if the tailroom of the msg is insufficient to store * the attribute header and payload. */ int nla_put(struct msg_buff *msg, int attrtype, int attrlen, const void *data) { if (unlikely(msg_tailroom(msg) < nla_total_size(attrlen))) return -EMSGSIZE; __nla_put(msg, attrtype, attrlen, data); return 0; } /* TODO add an architecture/platform blacklist */ #define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 1 #define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) /** * nla_need_padding_for_64bit - test 64-bit alignment of the next attribute * @msg: message buffer the message is stored in * * Return true if padding is needed to align the next attribute (nla_data()) to * a 64-bit aligned area. */ static inline bool nla_need_padding_for_64bit(struct msg_buff *msg) { #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS /* The nlattr header is 4 bytes in size, that's why we test * if the msg->data _is_ aligned. A NOP attribute, plus * nlattr header for next attribute, will make nla_data() * 8-byte aligned. */ if (IS_ALIGNED((unsigned long)msg_tail_pointer(msg), 8)) return true; #endif return false; } /** * nla_align_64bit - 64-bit align the nla_data() of next attribute * @msg: message buffer the message is stored in * @padattr: attribute type for the padding * * Conditionally emit a padding netlink attribute in order to make * the next attribute we emit have a 64-bit aligned nla_data() area. * This will only be done in architectures which do not have * CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS defined. * * Returns zero on success or a negative error code. */ static inline int nla_align_64bit(struct msg_buff *msg, int padattr) { if (nla_need_padding_for_64bit(msg) && !nla_reserve(msg, padattr, 0)) return -EMSGSIZE; return 0; } /** * nla_total_size_64bit - total length of attribute including padding * @payload: length of payload */ static inline int nla_total_size_64bit(int payload) { return NLA_ALIGN(nla_attr_size(payload)) #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS + NLA_ALIGN(nla_attr_size(0)) #endif ; } /** * __nla_reserve_64bit - reserve room for attribute on the msg and align it * @msg: message buffer to reserve room on * @attrtype: attribute type * @attrlen: length of attribute payload * @padattr: attribute type for the padding * * Adds a netlink attribute header to a socket buffer and reserves * room for the payload but does not copy it. It also ensure that this * attribute will have a 64-bit aligned nla_data() area. * * The caller is responsible to ensure that the msg provides enough * tailroom for the attribute header and payload. */ struct nlattr *__nla_reserve_64bit(struct msg_buff *msg, int attrtype, int attrlen, int padattr) { if (nla_need_padding_for_64bit(msg)) nla_align_64bit(msg, padattr); return __nla_reserve(msg, attrtype, attrlen); } /** * __nla_put_64bit - Add a netlink attribute to a socket buffer and align it * @msg: message buffer to add attribute to * @attrtype: attribute type * @attrlen: length of attribute payload * @data: head of attribute payload * @padattr: attribute type for the padding * * The caller is responsible to ensure that the msg provides enough * tailroom for the attribute header and payload. */ void __nla_put_64bit(struct msg_buff *msg, int attrtype, int attrlen, const void *data, int padattr) { struct nlattr *nla; nla = __nla_reserve_64bit(msg, attrtype, attrlen, padattr); memcpy(nla_data(nla), data, attrlen); } /** * nla_put_64bit - Add a netlink attribute to a socket buffer and align it * @msg: message buffer to add attribute to * @attrtype: attribute type * @attrlen: length of attribute payload * @data: head of attribute payload * @padattr: attribute type for the padding * * Returns -EMSGSIZE if the tailroom of the msg is insufficient to store * the attribute header and payload. */ int nla_put_64bit(struct msg_buff *msg, int attrtype, int attrlen, const void *data, int padattr) { size_t len; if (nla_need_padding_for_64bit(msg)) len = nla_total_size_64bit(attrlen); else len = nla_total_size(attrlen); if (unlikely(msg_tailroom(msg) < len)) return -EMSGSIZE; __nla_put_64bit(msg, attrtype, attrlen, data, padattr); return 0; } /** * nla_put_nohdr - Add a netlink attribute without header * @msg: message buffer to add attribute to * @attrlen: length of attribute payload * @data: head of attribute payload * * Returns -EMSGSIZE if the tailroom of the msg is insufficient to store * the attribute payload. */ int nla_put_nohdr(struct msg_buff *msg, int attrlen, const void *data) { if (unlikely(msg_tailroom(msg) < NLA_ALIGN(attrlen))) return -EMSGSIZE; __nla_put_nohdr(msg, attrlen, data); return 0; } /** * nla_append - Add a netlink attribute without header or padding * @msg: message buffer to add attribute to * @attrlen: length of attribute payload * @data: head of attribute payload * * Returns -EMSGSIZE if the tailroom of the msg is insufficient to store * the attribute payload. */ int nla_append(struct msg_buff *msg, int attrlen, const void *data) { if (unlikely(msg_tailroom(msg) < NLA_ALIGN(attrlen))) return -EMSGSIZE; memcpy(msg_put(msg, attrlen), data, attrlen); return 0; } drbd-utils-8.9.10/user/shared/shared_main.c0000644000175000017500000002250313021555314020421 0ustar apoikosapoikos/* shared_main.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2014, LINBIT HA Solutions GmbH. drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #define _XOPEN_SOURCE 600 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drbd_endian.h" #include "shared_main.h" #include "shared_tool.h" extern struct ifreq *ifreq_list; struct d_globals global_options = { .cmd_timeout_short = CMD_TIMEOUT_SHORT_DEF, .cmd_timeout_medium = CMD_TIMEOUT_MEDIUM_DEF, .cmd_timeout_medium = CMD_TIMEOUT_LONG_DEF, .dialog_refresh = 1, .usage_count = UC_ASK, }; void chld_sig_hand(int __attribute((unused)) unused) { // do nothing. But interrupt systemcalls :) } unsigned minor_by_id(const char *id) { if (strncmp(id, "minor-", 6)) return -1U; return m_strtoll(id + 6, 1); } /* * I'd really rather parse the output of * ip -o a s * once, and be done. * But anyways.... */ struct ifreq *get_ifreq(void) { int sockfd, num_ifaces; struct ifreq *ifr; struct ifconf ifc; size_t buf_size; if (0 > (sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) { perror("Cannot open socket"); exit(EXIT_FAILURE); } num_ifaces = 0; ifc.ifc_req = NULL; /* realloc buffer size until no overflow occurs */ do { num_ifaces += 16; /* initial guess and increment */ buf_size = ++num_ifaces * sizeof(struct ifreq); ifc.ifc_len = buf_size; if (NULL == (ifc.ifc_req = realloc(ifc.ifc_req, ifc.ifc_len))) { fprintf(stderr, "Out of memory.\n"); return NULL; } if (ioctl(sockfd, SIOCGIFCONF, &ifc)) { perror("ioctl SIOCFIFCONF"); free(ifc.ifc_req); return NULL; } } while (buf_size <= (size_t) ifc.ifc_len); num_ifaces = ifc.ifc_len / sizeof(struct ifreq); /* Since we allocated at least one more than necessary, * this serves as a stop marker for the iteration in * have_ip() */ ifc.ifc_req[num_ifaces].ifr_name[0] = 0; for (ifr = ifc.ifc_req; ifr->ifr_name[0] != 0; ifr++) { /* we only want to look up the presence or absence of a certain address * here. but we want to skip "down" interfaces. if an interface is down, * we store an invalid sa_family, so the lookup will skip it. */ struct ifreq ifr_for_flags = *ifr; /* get a copy to work with */ if (ioctl(sockfd, SIOCGIFFLAGS, &ifr_for_flags) < 0) { perror("ioctl SIOCGIFFLAGS"); ifr->ifr_addr.sa_family = -1; /* what's wrong here? anyways: skip */ continue; } if (!(ifr_for_flags.ifr_flags & IFF_UP)) { ifr->ifr_addr.sa_family = -1; /* is not up: skip */ continue; } } close(sockfd); return ifc.ifc_req; } int have_ip_ipv4(const char *ip) { struct ifreq *ifr; struct in_addr query_addr; query_addr.s_addr = inet_addr(ip); if (!ifreq_list) ifreq_list = get_ifreq(); for (ifr = ifreq_list; ifr && ifr->ifr_name[0] != 0; ifr++) { /* SIOCGIFCONF only supports AF_INET */ struct sockaddr_in *list_addr = (struct sockaddr_in *)&ifr->ifr_addr; if (ifr->ifr_addr.sa_family != AF_INET) continue; if (query_addr.s_addr == list_addr->sin_addr.s_addr) return 1; } return 0; } int have_ip_ipv6(const char *ip) { FILE *if_inet6; struct in6_addr addr6, query_addr; unsigned int b[4]; char tmp_ip[INET6_ADDRSTRLEN+1]; char name[20]; /* IFNAMSIZ aka IF_NAMESIZE is 16 */ int i; /* don't want to do getaddrinfo lookup, but inet_pton get's confused by * %eth0 link local scope specifiers. So we have a temporary copy * without that part. */ for (i=0; ip[i] && ip[i] != '%' && i < INET6_ADDRSTRLEN; i++) tmp_ip[i] = ip[i]; tmp_ip[i] = 0; if (inet_pton(AF_INET6, tmp_ip, &query_addr) <= 0) return 0; #define PROC_IF_INET6 "/proc/net/if_inet6" if_inet6 = fopen(PROC_IF_INET6, "r"); if (!if_inet6) { if (errno != ENOENT) perror("open of " PROC_IF_INET6 " failed:"); #undef PROC_IF_INET6 return 0; } while (fscanf (if_inet6, X32(08) X32(08) X32(08) X32(08) " %*x %*x %*x %*x %s", b, b + 1, b + 2, b + 3, name) > 0) { for (i = 0; i < 4; i++) addr6.s6_addr32[i] = cpu_to_be32(b[i]); if (memcmp(&query_addr, &addr6, sizeof(struct in6_addr)) == 0) { fclose(if_inet6); return 1; } } fclose(if_inet6); return 0; } int have_ip(const char *af, const char *ip) { if (!strcmp(af, "ipv4")) return have_ip_ipv4(ip); else if (!strcmp(af, "ipv6")) return have_ip_ipv6(ip); return 1; /* SCI */ } extern char *progname; void substitute_deprecated_cmd(char **c, char *deprecated, char *substitution) { if (!strcmp(*c, deprecated)) { fprintf(stderr, "'%s %s' is deprecated, use '%s %s' instead.\n", progname, deprecated, progname, substitution); *c = substitution; } } pid_t my_fork(void) { pid_t pid = -1; int try; for (try = 0; try < 10; try++) { errno = 0; pid = fork(); if (pid != -1 || errno != EAGAIN) return pid; err("fork: retry: Resource temporarily unavailable\n"); usleep(100 * 1000); } return pid; } void m__system(char **argv, int flags, const char *res_name, pid_t *kid, int *fd, int *ex) { pid_t pid; int status, rv = -1; int timeout = 0; char **cmdline = argv; int pipe_fds[2]; struct sigaction so; struct sigaction sa; if (flags & (RETURN_STDERR_FD | RETURN_STDOUT_FD)) assert(fd); sa.sa_handler = &alarm_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (dry_run || verbose) { if (sh_varname && *cmdline) printf("%s=%s\n", sh_varname, res_name ? shell_escape(res_name) : ""); while (*cmdline) { printf("%s ", shell_escape(*cmdline++)); } printf("\n"); if (dry_run) { if (kid) *kid = -1; if (fd) *fd = -1; if (ex) *ex = 0; return; } } /* flush stdout and stderr, so output of drbdadm * and helper binaries is reported in order! */ fflush(stdout); fflush(stderr); if (adjust_with_progress && !(flags & RETURN_STDERR_FD)) flags |= SUPRESS_STDERR; /* create the pipe in any case: * it helps the analyzer and later we have: * '*fd = pipe_fds[0];' */ if (pipe(pipe_fds) < 0) { perror("pipe"); fprintf(stderr, "Error in pipe, giving up.\n"); exit(E_EXEC_ERROR); } pid = my_fork(); if (pid == -1) { fprintf(stderr, "Can not fork\n"); exit(E_EXEC_ERROR); } if (pid == 0) { prctl(PR_SET_PDEATHSIG, SIGKILL); /* Child: close reading end. */ close(pipe_fds[0]); if (flags & RETURN_STDOUT_FD) { dup2(pipe_fds[1], STDOUT_FILENO); } if (flags & RETURN_STDERR_FD) { dup2(pipe_fds[1], STDERR_FILENO); } close(pipe_fds[1]); if (flags & SUPRESS_STDERR) { FILE *f = freopen("/dev/null", "w", stderr); if (!f) fprintf(stderr, "freopen(/dev/null) failed\n"); } if (argv[0]) execvp(argv[0], argv); fprintf(stderr, "Can not exec\n"); exit(E_EXEC_ERROR); } /* Parent process: close writing end. */ close(pipe_fds[1]); if (flags & SLEEPS_FINITE) { sigaction(SIGALRM, &sa, &so); alarm_raised = 0; switch (flags & SLEEPS_MASK) { case SLEEPS_SHORT: timeout = global_options.cmd_timeout_short; break; case SLEEPS_LONG: timeout = global_options.cmd_timeout_medium; break; case SLEEPS_VERY_LONG: timeout = global_options.cmd_timeout_long; break; default: fprintf(stderr, "logic bug in %s:%d\n", __FILE__, __LINE__); exit(E_THINKO); } alarm(timeout); } if (kid) *kid = pid; if (flags & (RETURN_STDOUT_FD | RETURN_STDERR_FD) || flags == RETURN_PID) { if (fd) *fd = pipe_fds[0]; return; } while (1) { if (waitpid(pid, &status, 0) == -1) { if (errno != EINTR) break; if (alarm_raised) { alarm(0); sigaction(SIGALRM, &so, NULL); rv = 0x100; break; } else { fprintf(stderr, "logic bug in %s:%d\n", __FILE__, __LINE__); exit(E_EXEC_ERROR); } } else { if (WIFEXITED(status)) { rv = WEXITSTATUS(status); break; } } } /* Do not close earlier, else the child gets EPIPE. */ close(pipe_fds[0]); if (flags & SLEEPS_FINITE) { if (rv >= 10 && !(flags & (DONT_REPORT_FAILED | SUPRESS_STDERR))) { fprintf(stderr, "Command '"); for (cmdline = argv; *cmdline; cmdline++) { fprintf(stderr, "%s", *cmdline); if (cmdline[1]) fputc(' ', stderr); } if (alarm_raised) { fprintf(stderr, "' did not terminate within %u seconds\n", timeout); exit(E_EXEC_ERROR); } else { fprintf(stderr, "' terminated with exit code %d\n", rv); } } } fflush(stdout); fflush(stderr); if (ex) *ex = rv; } drbd-utils-8.9.10/user/shared/drbdmeta.c0000644000175000017500000045220313027211604017732 0ustar apoikosapoikos/* drbdmeta.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2004-2008, LINBIT Information Technologies GmbH Copyright (C) 2004-2008, Philipp Reisner Copyright (C) 2004-2008, Lars Ellenberg drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* have the first, otherwise you get e.g. "redefined" types from * sys/types.h and other weird stuff */ #define INITIALIZE_BITMAP 0 #define _GNU_SOURCE #define _XOPEN_SOURCE 600 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* only use DRBD_MAGIC from here! */ #include /* for BLKFLSBUF */ #include "drbd_endian.h" #include "drbdtool_common.h" #include "drbd_strings.h" #include "drbd_meta_data.h" #include "drbdmeta_parser.h" #include "config.h" extern FILE* yyin; YYSTYPE yylval; int force = 0; int verbose = 0; int ignore_sanity_checks = 0; int dry_run = 0; int option_peer_max_bio_size = 0; int option_node_id = -1; unsigned option_al_stripes = 1; unsigned option_al_stripe_size_4k = 8; unsigned option_al_stripes_used = 0; struct option metaopt[] = { { "ignore-sanity-checks", no_argument, &ignore_sanity_checks, 1000 }, { "dry-run", no_argument, &dry_run, 1000 }, { "force", no_argument, 0, 'f' }, { "verbose", no_argument, 0, 'v' }, { "peer-max-bio-size", required_argument, NULL, 'p' }, { "node-id", required_argument, NULL, 'i' }, { "al-stripes", required_argument, NULL, 's' }, { "al-stripe-size-kB", required_argument, NULL, 'z' }, { NULL, 0, 0, 0 }, }; /* FIXME? should use sector_t and off_t, not long/uint64_t ... */ /* Note RETURN VALUES: * exit code convention: int vXY_something() and meta_blah return some negative * error code, usually -1, when failed, 0 for success. * * FIXME some of the return -1; probably should better be exit(something); * or some of the exit() should be rather some return? * * AND, the exit codes should follow some defined scheme. */ #if 0 #define ASSERT(x) ((void)(0)) #define d_expect(x) (x) #else #define ASSERT(x) do { if (!(x)) { \ fprintf(stderr, "%s:%u:%s: ASSERT(%s) failed.\n", \ __FILE__ , __LINE__ , __func__ , #x ); \ abort(); } \ } while (0) #define d_expect(x) ({ \ int _x = (x); \ if (!_x) \ fprintf(stderr, "%s:%u:%s: ASSERT(%s) failed.\n",\ __FILE__ , __LINE__ , __func__ , #x ); \ _x; }) #endif static int confirmed(const char *text) { const char yes[] = "yes"; const ssize_t N = sizeof(yes); char *answer = NULL; size_t n = 0; int ok; fprintf(stderr, "\n%s\n", text); if (force) { fprintf(stderr, "*** confirmation forced via --force option ***\n"); ok = 1; } else { fprintf(stderr, "[need to type '%s' to confirm] ", yes); ok = getline(&answer,&n,stdin) == N && strncmp(answer,yes,N-1) == 0; free(answer); fprintf(stderr, "\n"); } return ok; } /* * FIXME * * when configuring a drbd device: * * Require valid drbd meta data at the respective location. A meta data * block would only be created by the drbdmeta command. * * (How) do we want to implement this: A meta data block contains some * reference to the physical device it belongs. Refuse to attach not * corresponding meta data. * * THINK: put a checksum within the on-disk meta data block, too? * * When asked to create a new meta data block, the drbdmeta command * warns loudly if either the data device or the meta data device seem * to contain some data, and requires explicit confirmation anyways. * * See current implementation in check_for_existing_data below. * * XXX should also be done for meta-data != internal, i.e. refuse to * create meta data blocks on a device that seems to be in use for * something else. * * Maybe with an external meta data device, we want to require a "meta * data device super block", which could also serve as TOC to the meta * data, once we have variable size meta data. Other option could be a * /var/lib/drbd/md-toc plain file, and some magic block on every device * that serves as md storage. * * For certain content on the lower level device, we should refuse * always. e.g. refuse to be created on top of a LVM2 physical volume, * or on top of swap space. This would require people to do an dd * if=/dev/zero of=device. Protects them from shooting themselves, * and blaming us... */ /* reiserfs sb offset is 64k plus * align it to 4k, in case someone has unusual hard sect size (!= 512), * otherwise direct io will fail with EINVAL */ #define SO_MUCH (68*1024) /* * I think this block of declarations and definitions should be * in some common.h, too. * { */ #ifndef ALIGN # define ALIGN(x,a) ( ((x) + (a)-1) &~ ((a)-1) ) #endif #define MD_AL_OFFSET_07 8 #define MD_AL_MAX_SECT_07 64 #define MD_BM_OFFSET_07 (MD_AL_OFFSET_07 + MD_AL_MAX_SECT_07) #define MD_RESERVED_SECT_07 ( (uint64_t)(128ULL << 11) ) #define MD_BM_MAX_BYTE_07 ( (uint64_t)(MD_RESERVED_SECT_07 - MD_BM_OFFSET_07)*512 ) #if BITS_PER_LONG == 32 #define MD_BM_MAX_BYTE_FLEX ( (uint64_t)(1ULL << (32-3)) ) #else #define MD_BM_MAX_BYTE_FLEX ( (uint64_t)(1ULL << (38-3)) ) #endif #define DEFAULT_BM_BLOCK_SIZE (1<<12) #define DRBD_MD_MAGIC_06 (DRBD_MAGIC+2) #define DRBD_MD_MAGIC_07 (DRBD_MAGIC+3) #define DRBD_MD_MAGIC_08 (DRBD_MAGIC+4) #define DRBD_MD_MAGIC_84_UNCLEAN (DRBD_MAGIC+5) #define DRBD_MD_MAGIC_09 (DRBD_MAGIC+6) /* * } * end of should-be-shared */ /* * global variables and data types */ /* buffer_size has to be a multiple of 4096, and at least 32k. * Pending a "nice" implementation of replay_al_84 for striped activity log, * I chose a big buffer hopefully large enough to hold the whole activity log, * even with "large" number of stripes and stripe sizes. * * If you chose to change buffer_size, double check also fprintf_bm(), * and how it calculates its chunk size. */ const size_t buffer_size = 32 * 1024 * 1024; size_t pagesize; /* = sysconf(_SC_PAGESIZE) */ int opened_odirect = 1; void *on_disk_buffer = NULL; int global_argc; char **global_argv; char *progname = NULL; enum md_format { DRBD_V06, DRBD_V07, DRBD_V08, DRBD_V09, DRBD_UNKNOWN, }; /* let gcc help us get it right. * some explicit endian types */ typedef struct { uint64_t le; } le_u64; typedef struct { uint64_t be; } be_u64; typedef struct { uint32_t le; } le_u32; typedef struct { uint32_t be; } be_u32; typedef struct { int32_t be; } be_s32; typedef struct { uint16_t be; } be_u16; typedef struct { unsigned long le; } le_ulong; typedef struct { unsigned long be; } be_ulong; /* NOTE that this structure does not need to be packed, * aligned, nor does it need to be in the same order as the on_disk variants. */ struct peer_md_cpu { uint64_t bitmap_uuid; uint64_t bitmap_dagtag; uint32_t flags; int32_t bitmap_index; }; struct md_cpu { uint64_t current_uuid; uint64_t history_uuids[HISTORY_UUIDS]; /* present since drbd 0.6 */ uint32_t gc[GEN_CNT_SIZE]; /* generation counter */ uint32_t magic; /* added in drbd 0.7; * 0.7 stores effevtive_size on disk as kb, 0.8 in units of sectors. * we use sectors in our general working structure here */ uint64_t effective_size; /* last agreed size */ uint32_t md_size_sect; int32_t al_offset; /* signed sector offset to this block */ uint32_t al_nr_extents; /* important for restoring the AL */ int32_t bm_offset; /* signed sector offset to the bitmap, from here */ /* Since DRBD 0.8 we have uuid instead of gc */ uint32_t flags; uint64_t device_uuid; uint32_t bm_bytes_per_bit; uint32_t la_peer_max_bio_size; /* Since DRBD 9.0 the following new stuff: */ uint32_t max_peers; int32_t node_id; struct peer_md_cpu peers[DRBD_PEERS_MAX]; uint32_t al_stripes; uint32_t al_stripe_size_4k; }; /* * drbdmeta specific types */ struct format_ops; struct format { const struct format_ops *ops; char *md_device_name; /* well, in 06 it is file name */ char *drbd_dev_name; unsigned minor; /* cache, determined from drbd_dev_name */ int lock_fd; int drbd_fd; /* no longer used! */ int ll_fd; /* not yet used here */ int md_fd; int md_hard_sect_size; /* unused in 06 */ int md_index; unsigned int bm_bytes; unsigned int bits_set; /* 32 bit should be enough. @4k ==> 16TB */ int bits_counted:1; int update_lk_bdev:1; /* need to update the last known bdev info? */ struct md_cpu md; /* _byte_ offsets of our "super block" and other data, within fd */ uint64_t md_offset; uint64_t al_offset; uint64_t bm_offset; /* if create_md actually does convert, * we want to wipe the old meta data block _after_ convertion. */ uint64_t wipe_fixed; uint64_t wipe_flex; uint64_t wipe_resize; /* convenience */ uint64_t bd_size; /* size of block device for internal meta data */ /* size limit due to available on-disk bitmap */ uint64_t max_usable_sect; /* last-known bdev info, * to increase the chance of finding internal meta data in case the * lower level device has been resized without telling DRBD. * Loaded from file for internal metadata */ struct bdev_info lk_bd; }; /* - parse is expected to exit() if it does not work out. * - open is expected to read the respective on_disk members, * and copy the "superblock" meta data into the struct mem_cpu * FIXME describe rest of them, and when they should exit, * return error or success. */ struct format_ops { const char *name; char **args; int (*parse) (struct format *, char **, int, int *); int (*open) (struct format *); int (*close) (struct format *); int (*md_initialize) (struct format *, int do_disk_writes, int max_peers); int (*md_disk_to_cpu) (struct format *); int (*md_cpu_to_disk) (struct format *); void (*get_gi) (struct md_cpu *md, int node_id); void (*show_gi) (struct md_cpu *md, int node_id); void (*set_gi) (struct md_cpu *md, int node_id, char **argv, int argc); int (*outdate_gi) (struct md_cpu *md); int (*invalidate_gi) (struct md_cpu *md); }; struct format_ops f_ops[]; /* * -- DRBD 0.6 -------------------------------------- */ struct __packed md_on_disk_06 { be_u32 gc[GEN_CNT_SIZE]; /* generation counter */ be_u32 magic; }; void md_disk_06_to_cpu(struct md_cpu *cpu, const struct md_on_disk_06 *disk) { int i; memset(cpu, 0, sizeof(*cpu)); for (i = 0; i < GEN_CNT_SIZE; i++) cpu->gc[i] = be32_to_cpu(disk->gc[i].be); cpu->magic = be32_to_cpu(disk->magic.be); cpu->max_peers = 1; } void md_cpu_to_disk_06(struct md_on_disk_06 *disk, struct md_cpu *cpu) { int i; for (i = 0; i < GEN_CNT_SIZE; i++) disk->gc[i].be = cpu_to_be32(cpu->gc[i]); disk->magic.be = cpu_to_be32(cpu->magic); } int v06_validate_md(struct format *cfg) { if (cfg->md.magic != DRBD_MD_MAGIC_06) { fprintf(stderr, "v06 Magic number not found\n"); return -1; } return 0; } /* * -- DRBD 0.7 -------------------------------------- */ unsigned long bm_bytes(const struct md_cpu const *md, uint64_t sectors); struct __packed md_on_disk_07 { be_u64 la_kb; /* last agreed size. */ be_u32 gc[GEN_CNT_SIZE]; /* generation counter */ be_u32 magic; be_u32 md_size_kb; be_s32 al_offset; /* signed sector offset to this block */ be_u32 al_nr_extents; /* important for restoring the AL */ be_s32 bm_offset; /* signed sector offset to the bitmap, from here */ char reserved[8 * 512 - 48]; }; void md_disk_07_to_cpu(struct md_cpu *cpu, const struct md_on_disk_07 *disk) { int i; memset(cpu, 0, sizeof(*cpu)); cpu->effective_size = be64_to_cpu(disk->la_kb.be) << 1; for (i = 0; i < GEN_CNT_SIZE; i++) cpu->gc[i] = be32_to_cpu(disk->gc[i].be); cpu->magic = be32_to_cpu(disk->magic.be); cpu->md_size_sect = be32_to_cpu(disk->md_size_kb.be) << 1; cpu->al_offset = be32_to_cpu(disk->al_offset.be); cpu->al_nr_extents = be32_to_cpu(disk->al_nr_extents.be); cpu->bm_offset = be32_to_cpu(disk->bm_offset.be); cpu->bm_bytes_per_bit = DEFAULT_BM_BLOCK_SIZE; cpu->max_peers = 1; cpu->al_stripes = 1; cpu->al_stripe_size_4k = 8; } void md_cpu_to_disk_07(struct md_on_disk_07 *disk, const struct md_cpu const *cpu) { int i; disk->la_kb.be = cpu_to_be64(cpu->effective_size >> 1); for (i = 0; i < GEN_CNT_SIZE; i++) disk->gc[i].be = cpu_to_be32(cpu->gc[i]); disk->magic.be = cpu_to_be32(cpu->magic); disk->md_size_kb.be = cpu_to_be32(cpu->md_size_sect >> 1); disk->al_offset.be = cpu_to_be32(cpu->al_offset); disk->al_nr_extents.be = cpu_to_be32(cpu->al_nr_extents); disk->bm_offset.be = cpu_to_be32(cpu->bm_offset); memset(disk->reserved, 0, sizeof(disk->reserved)); } int is_valid_md(enum md_format f, const struct md_cpu const *md, const int md_index, const uint64_t ll_size) { uint64_t md_size_sect; const char *v = f_ops[f].name; int al_size_sect; int n; ASSERT(f == DRBD_V07 || f == DRBD_V08 || f == DRBD_V09); if ((f == DRBD_V07 && md->magic != DRBD_MD_MAGIC_07) || (f == DRBD_V08 && md->magic != DRBD_MD_MAGIC_08 && md->magic != DRBD_MD_MAGIC_84_UNCLEAN) || (f == DRBD_V09 && md->magic != DRBD_MD_MAGIC_09)) { if (verbose >= 1) fprintf(stderr, "%s Magic number not found\n", v); return 0; } if (md->max_peers < 1 || md->max_peers > DRBD_PEERS_MAX) { fprintf(stderr, "%s max-peers value %d out of bounds\n", v, md->max_peers); return 0; } if (md->node_id < -1 || md->node_id > DRBD_PEERS_MAX + 1) { fprintf(stderr, "%s device node-id value %d out of bounds\n", v, md->node_id); return 0; } for (n = 0; n < md->max_peers; n++) { if (md->peers[n].bitmap_index < -1 || md->peers[n].bitmap_index > DRBD_PEERS_MAX + 1) { fprintf(stderr, "%s peer device %d node-id value %d out of bounds\n", v, n, md->peers[n].bitmap_index); return 0; } } al_size_sect = md->al_stripes * md->al_stripe_size_4k * 8; switch(md_index) { default: case DRBD_MD_INDEX_INTERNAL: case DRBD_MD_INDEX_FLEX_EXT: if (md->al_offset != MD_AL_OFFSET_07) { fprintf(stderr, "%s Magic number (al_offset) not found\n", v); fprintf(stderr, "\texpected: %d, found %d\n", MD_AL_OFFSET_07, md->al_offset); return 0; } if (md->bm_offset != MD_AL_OFFSET_07 + al_size_sect) { fprintf(stderr, "%s bm_offset: expected %d, found %d\n", v, MD_AL_OFFSET_07 + al_size_sect, md->bm_offset); return 0; } break; case DRBD_MD_INDEX_FLEX_INT: if (md->al_offset != -al_size_sect) { fprintf(stderr, "%s al_offset: expected %d, found %d\n", v, -al_size_sect, md->al_offset); return 0; } md_size_sect = bm_bytes(md, ll_size >> 9) >> 9; md_size_sect = ALIGN(md_size_sect, 8); /* align on 4K blocks */ /* plus the "drbd meta data super block", * and the activity log; unit still sectors */ md_size_sect += MD_AL_OFFSET_07 + al_size_sect; if (md->bm_offset != -(int64_t)md_size_sect + MD_AL_OFFSET_07) { fprintf(stderr, "strange bm_offset %d (expected: "D64")\n", md->bm_offset, -(int64_t)md_size_sect + MD_AL_OFFSET_07); return 0; }; if (md->md_size_sect != md_size_sect) { fprintf(stderr, "strange md_size_sect %u (expected: "U64")\n", md->md_size_sect, md_size_sect); if (f == DRBD_V08) return 0; /* else not an error, * was inconsistently implemented in v07 */ } break; } /* FIXME consistency check, effevtive_size < ll_device_size, * no overlap with internal meta data, * no overlap of flexible meta data offsets/sizes * ... */ return 1; /* VALID */ } /* * these stay the same for 0.8, too: */ struct al_sector_cpu { uint32_t magic; uint32_t tr_number; struct { uint32_t pos; uint32_t extent; } updates[62]; uint32_t xor_sum; }; struct __packed al_sector_on_disk { be_u32 magic; be_u32 tr_number; struct __packed { be_u32 pos; be_u32 extent; } updates[62]; be_u32 xor_sum; be_u32 pad; }; int v07_al_disk_to_cpu(struct al_sector_cpu *al_cpu, struct al_sector_on_disk *al_disk) { uint32_t xor_sum = 0; int i; al_cpu->magic = be32_to_cpu(al_disk->magic.be); al_cpu->tr_number = be32_to_cpu(al_disk->tr_number.be); for (i = 0; i < 62; i++) { al_cpu->updates[i].pos = be32_to_cpu(al_disk->updates[i].pos.be); al_cpu->updates[i].extent = be32_to_cpu(al_disk->updates[i].extent.be); xor_sum ^= al_cpu->updates[i].extent; } al_cpu->xor_sum = be32_to_cpu(al_disk->xor_sum.be); return al_cpu->magic == DRBD_MAGIC && al_cpu->xor_sum == xor_sum; } /* * -- DRBD 8.0, 8.2, 8.3 -------------------------------------- */ struct __packed md_on_disk_08 { be_u64 effective_size; /* last agreed size. */ be_u64 uuid[UI_SIZE]; // UUIDs. be_u64 device_uuid; be_u64 reserved_u64_1; be_u32 flags; be_u32 magic; be_u32 md_size_sect; be_s32 al_offset; /* signed sector offset to this block */ be_u32 al_nr_extents; /* important for restoring the AL */ be_s32 bm_offset; /* signed sector offset to the bitmap, from here */ be_u32 bm_bytes_per_bit; be_u32 la_peer_max_bio_size; /* last peer max_bio_size */ /* see al_tr_number_to_on_disk_sector() */ be_u32 al_stripes; be_u32 al_stripe_size_4k; be_u32 reserved_u32[1]; char reserved[8 * 512 - (8*(UI_SIZE+3)+4*11)]; }; void md_disk_08_to_cpu(struct md_cpu *cpu, const struct md_on_disk_08 *disk) { int i; memset(cpu, 0, sizeof(*cpu)); cpu->effective_size = be64_to_cpu(disk->effective_size.be); cpu->current_uuid = be64_to_cpu(disk->uuid[UI_CURRENT].be); cpu->peers[0].bitmap_uuid = be64_to_cpu(disk->uuid[UI_BITMAP].be); for (i = 0; i < HISTORY_UUIDS_V08; i++) cpu->history_uuids[i] = be64_to_cpu(disk->uuid[UI_HISTORY_START + i].be); cpu->device_uuid = be64_to_cpu(disk->device_uuid.be); cpu->flags = be32_to_cpu(disk->flags.be); cpu->magic = be32_to_cpu(disk->magic.be); cpu->md_size_sect = be32_to_cpu(disk->md_size_sect.be); cpu->al_offset = be32_to_cpu(disk->al_offset.be); cpu->al_nr_extents = be32_to_cpu(disk->al_nr_extents.be); cpu->bm_offset = be32_to_cpu(disk->bm_offset.be); cpu->bm_bytes_per_bit = be32_to_cpu(disk->bm_bytes_per_bit.be); cpu->la_peer_max_bio_size = be32_to_cpu(disk->la_peer_max_bio_size.be); cpu->max_peers = 1; cpu->al_stripes = be32_to_cpu(disk->al_stripes.be); cpu->al_stripe_size_4k = be32_to_cpu(disk->al_stripe_size_4k.be); /* not set? --> default to old fixed size activity log */ if (cpu->al_stripes == 0 && cpu->al_stripe_size_4k == 0) { cpu->al_stripes = 1; cpu->al_stripe_size_4k = 8; } } void md_cpu_to_disk_08(struct md_on_disk_08 *disk, const struct md_cpu *cpu) { int i; memset(disk, 0, sizeof(*disk)); disk->effective_size.be = cpu_to_be64(cpu->effective_size); disk->uuid[UI_CURRENT].be = cpu_to_be64(cpu->current_uuid); disk->uuid[UI_BITMAP].be = cpu_to_be64(cpu->peers[0].bitmap_uuid); for (i = 0; i < HISTORY_UUIDS_V08; i++) disk->uuid[UI_HISTORY_START + i].be = cpu_to_be64(cpu->history_uuids[i]); disk->device_uuid.be = cpu_to_be64(cpu->device_uuid); disk->flags.be = cpu_to_be32(cpu->flags); disk->magic.be = cpu_to_be32(cpu->magic); disk->md_size_sect.be = cpu_to_be32(cpu->md_size_sect); disk->al_offset.be = cpu_to_be32(cpu->al_offset); disk->al_nr_extents.be = cpu_to_be32(cpu->al_nr_extents); disk->bm_offset.be = cpu_to_be32(cpu->bm_offset); disk->bm_bytes_per_bit.be = cpu_to_be32(cpu->bm_bytes_per_bit); disk->la_peer_max_bio_size.be = cpu_to_be32(cpu->la_peer_max_bio_size); disk->al_stripes.be = cpu_to_be32(cpu->al_stripes); disk->al_stripe_size_4k.be = cpu_to_be32(cpu->al_stripe_size_4k); } /* * -- DRBD 8.4 -------------------------------------- */ /* new in 8.4: 4k al transaction blocks */ #define AL_UPDATES_PER_TRANSACTION 64 #define AL_CONTEXT_PER_TRANSACTION 919 /* from DRBD 8.4 linux/drbd/drbd_limits.h, DRBD_AL_EXTENTS_MAX */ #define AL_EXTENTS_MAX 65534 enum al_transaction_types { AL_TR_UPDATE = 0, AL_TR_INITIALIZED = 0xffff }; struct __packed al_4k_transaction_on_disk { /* don't we all like magic */ be_u32 magic; /* to identify the most recent transaction block * in the on disk ring buffer */ be_u32 tr_number; /* checksum on the full 4k block, with this field set to 0. */ be_u32 crc32c; /* type of transaction, special transaction types like: * purge-all, set-all-idle, set-all-active, ... to-be-defined * see also enum al_transaction_types */ be_u16 transaction_type; /* we currently allow only a few thousand extents, * so 16bit will be enough for the slot number. */ /* how many updates in this transaction */ be_u16 n_updates; /* maximum slot number, "al-extents" in drbd.conf speak. * Having this in each transaction should make reconfiguration * of that parameter easier. */ be_u16 context_size; /* slot number the context starts with */ be_u16 context_start_slot_nr; /* Some reserved bytes. Expected usage is a 64bit counter of * sectors-written since device creation, and other data generation tag * supporting usage */ be_u32 __reserved[4]; /* --- 36 byte used --- */ /* Reserve space for up to AL_UPDATES_PER_TRANSACTION changes * in one transaction, then use the remaining byte in the 4k block for * context information. "Flexible" number of updates per transaction * does not help, as we have to account for the case when all update * slots are used anyways, so it would only complicate code without * additional benefit. */ be_u16 update_slot_nr[AL_UPDATES_PER_TRANSACTION]; /* but the extent number is 32bit, which at an extent size of 4 MiB * allows to cover device sizes of up to 2**54 Byte (16 PiB) */ be_u32 update_extent_nr[AL_UPDATES_PER_TRANSACTION]; /* --- 420 bytes used (36 + 64*6) --- */ /* 4096 - 420 = 3676 = 919 * 4 */ be_u32 context[AL_CONTEXT_PER_TRANSACTION]; }; struct al_4k_cpu { uint32_t magic; uint32_t tr_number; uint32_t crc32c; uint16_t transaction_type; uint16_t n_updates; uint16_t context_size; uint16_t context_start_slot_nr; uint32_t __reserved[4]; uint16_t update_slot_nr[AL_UPDATES_PER_TRANSACTION]; uint32_t update_extent_nr[AL_UPDATES_PER_TRANSACTION]; uint32_t context[AL_CONTEXT_PER_TRANSACTION]; uint32_t is_valid; }; /* --- */ int v84_al_disk_to_cpu(struct al_4k_cpu *al_cpu, struct al_4k_transaction_on_disk *al_disk) { unsigned crc = 0; unsigned i; al_cpu->magic = be32_to_cpu(al_disk->magic.be); al_cpu->tr_number = be32_to_cpu(al_disk->tr_number.be); al_cpu->crc32c = be32_to_cpu(al_disk->crc32c.be); al_cpu->transaction_type = be16_to_cpu(al_disk->transaction_type.be); al_cpu->n_updates = be16_to_cpu(al_disk->n_updates.be); al_cpu->context_size = be16_to_cpu(al_disk->context_size.be); al_cpu->context_start_slot_nr = be16_to_cpu(al_disk->context_start_slot_nr.be); /* reserverd al_disk->__reserved[4] */ for (i=0; i < AL_UPDATES_PER_TRANSACTION; i++) al_cpu->update_slot_nr[i] = be16_to_cpu(al_disk->update_slot_nr[i].be); for (i=0; i < AL_UPDATES_PER_TRANSACTION; i++) al_cpu->update_extent_nr[i] = be32_to_cpu(al_disk->update_extent_nr[i].be); for (i=0; i < AL_CONTEXT_PER_TRANSACTION; i++) al_cpu->context[i] = be32_to_cpu(al_disk->context[i].be); al_disk->crc32c.be = 0; crc = crc32c(crc, (void*)al_disk, 4096); al_cpu->is_valid = (al_cpu->magic == DRBD_AL_MAGIC && al_cpu->crc32c == crc); return al_cpu->is_valid; } /* * -- DRBD 9.0 -------------------------------------- */ /* struct meta_data_on_disk_9 is in drbd_meta_data.h */ void md_disk_09_to_cpu(struct md_cpu *cpu, const struct meta_data_on_disk_9 *disk) { int p, i; memset(cpu, 0, sizeof(*cpu)); cpu->effective_size = be64_to_cpu(disk->effective_size.be); cpu->device_uuid = be64_to_cpu(disk->device_uuid.be); cpu->flags = be32_to_cpu(disk->flags.be); cpu->magic = be32_to_cpu(disk->magic.be); cpu->md_size_sect = be32_to_cpu(disk->md_size_sect.be); cpu->al_offset = be32_to_cpu(disk->al_offset.be); cpu->al_nr_extents = be32_to_cpu(disk->al_nr_extents.be); cpu->bm_offset = be32_to_cpu(disk->bm_offset.be); cpu->bm_bytes_per_bit = be32_to_cpu(disk->bm_bytes_per_bit.be); cpu->la_peer_max_bio_size = be32_to_cpu(disk->la_peer_max_bio_size.be); cpu->max_peers = be32_to_cpu(disk->bm_max_peers.be); cpu->node_id = be32_to_cpu(disk->node_id.be); cpu->al_stripes = be32_to_cpu(disk->al_stripes.be); cpu->al_stripe_size_4k = be32_to_cpu(disk->al_stripe_size_4k.be); if (cpu->max_peers > DRBD_PEERS_MAX) cpu->max_peers = DRBD_PEERS_MAX; cpu->current_uuid = be64_to_cpu(disk->current_uuid.be); for (p = 0; p < DRBD_NODE_ID_MAX; p++) { cpu->peers[p].flags = be32_to_cpu(disk->peers[p].flags.be); cpu->peers[p].bitmap_index = be32_to_cpu(disk->peers[p].bitmap_index.be); cpu->peers[p].bitmap_uuid = be64_to_cpu(disk->peers[p].bitmap_uuid.be); cpu->peers[p].bitmap_dagtag = be64_to_cpu(disk->peers[p].bitmap_dagtag.be); } BUILD_BUG_ON(ARRAY_SIZE(cpu->history_uuids) != ARRAY_SIZE(disk->history_uuids)); for (i = 0; i < ARRAY_SIZE(cpu->history_uuids); i++) cpu->history_uuids[i] = be64_to_cpu(disk->history_uuids[i].be); } void md_cpu_to_disk_09(struct meta_data_on_disk_9 *disk, const struct md_cpu *cpu) { int p, i; memset(disk, 0, sizeof(*disk)); disk->effective_size.be = cpu_to_be64(cpu->effective_size); disk->device_uuid.be = cpu_to_be64(cpu->device_uuid); disk->flags.be = cpu_to_be32(cpu->flags); disk->magic.be = cpu_to_be32(cpu->magic); disk->md_size_sect.be = cpu_to_be32(cpu->md_size_sect); disk->al_offset.be = cpu_to_be32(cpu->al_offset); disk->al_nr_extents.be = cpu_to_be32(cpu->al_nr_extents); disk->bm_offset.be = cpu_to_be32(cpu->bm_offset); disk->bm_bytes_per_bit.be = cpu_to_be32(cpu->bm_bytes_per_bit); disk->la_peer_max_bio_size.be = cpu_to_be32(cpu->la_peer_max_bio_size); disk->bm_max_peers.be = cpu_to_be32(cpu->max_peers); disk->node_id.be = cpu_to_be32(cpu->node_id); disk->al_stripes.be = cpu_to_be32(cpu->al_stripes); disk->al_stripe_size_4k.be = cpu_to_be32(cpu->al_stripe_size_4k); disk->current_uuid.be = cpu_to_be64(cpu->current_uuid); for (p = 0; p < DRBD_NODE_ID_MAX; p++) { disk->peers[p].flags.be = cpu_to_be32(cpu->peers[p].flags); disk->peers[p].bitmap_index.be = cpu_to_be32(cpu->peers[p].bitmap_index); disk->peers[p].bitmap_uuid.be = cpu_to_be64(cpu->peers[p].bitmap_uuid); disk->peers[p].bitmap_dagtag.be = cpu_to_be64(cpu->peers[p].bitmap_dagtag); } BUILD_BUG_ON(ARRAY_SIZE(disk->history_uuids) != ARRAY_SIZE(cpu->history_uuids)); for (i = 0; i < ARRAY_SIZE(disk->history_uuids); i++) disk->history_uuids[i].be = cpu_to_be64(cpu->history_uuids[i]); } /* * -------------------------------------------------- */ /* pre declarations */ void m_get_gc(struct md_cpu *md, int node_id); void m_show_gc(struct md_cpu *md, int node_id); void m_set_gc(struct md_cpu *md, int node_id, char **argv, int argc); int m_outdate_gc(struct md_cpu *md); int m_invalidate_gc(struct md_cpu *md); void m_get_uuid(struct md_cpu *md, int node_id); void m_show_uuid(struct md_cpu *md, int node_id); void m_set_uuid(struct md_cpu *md, int node_id, char **argv, int argc); void m_get_v9_uuid(struct md_cpu *md, int node_id); void m_show_v9_uuid(struct md_cpu *md, int node_id); void m_set_v9_uuid(struct md_cpu *md, int node_id, char **argv, int argc); int m_outdate_uuid(struct md_cpu *md); int m_invalidate_uuid(struct md_cpu *md); int m_invalidate_v9_uuid(struct md_cpu *md); int generic_md_close(struct format *cfg); int v06_md_cpu_to_disk(struct format *cfg); int v06_md_disk_to_cpu(struct format *cfg); int v06_parse(struct format *cfg, char **argv, int argc, int *ai); int v06_md_open(struct format *cfg); int v06_md_initialize(struct format *cfg, int do_disk_writes, int max_peers); int v07_md_cpu_to_disk(struct format *cfg); int v07_md_disk_to_cpu(struct format *cfg); int v07_parse(struct format *cfg, char **argv, int argc, int *ai); int v07_md_initialize(struct format *cfg, int do_disk_writes, int max_peers); int v07_style_md_open(struct format *cfg); int v08_md_open(struct format *cfg); int v08_md_cpu_to_disk(struct format *cfg); int v08_md_disk_to_cpu(struct format *cfg); int v08_md_initialize(struct format *cfg, int do_disk_writes, int max_peers); int v08_md_close(struct format *cfg); int v09_md_disk_to_cpu(struct format *cfg); int v09_md_cpu_to_disk(struct format *cfg); int v09_md_initialize(struct format *cfg, int do_disk_writes, int max_peers); /* return codes for md_open */ enum { VALID_MD_FOUND = 0, NO_VALID_MD_FOUND = -1, VALID_MD_FOUND_AT_LAST_KNOWN_LOCATION = -2, }; struct format_ops f_ops[] = { [DRBD_V06] = { .name = "v06", .args = (char *[]){"minor", NULL}, .parse = v06_parse, .open = v06_md_open, .close = generic_md_close, .md_initialize = v06_md_initialize, .md_disk_to_cpu = v06_md_disk_to_cpu, .md_cpu_to_disk = v06_md_cpu_to_disk, .get_gi = m_get_gc, .show_gi = m_show_gc, .set_gi = m_set_gc, .outdate_gi = m_outdate_gc, .invalidate_gi = m_invalidate_gc, }, [DRBD_V07] = { .name = "v07", .args = (char *[]){"device", "index", NULL}, .parse = v07_parse, .open = v07_style_md_open, .close = generic_md_close, .md_initialize = v07_md_initialize, .md_disk_to_cpu = v07_md_disk_to_cpu, .md_cpu_to_disk = v07_md_cpu_to_disk, .get_gi = m_get_gc, .show_gi = m_show_gc, .set_gi = m_set_gc, .outdate_gi = m_outdate_gc, .invalidate_gi = m_invalidate_gc, }, [DRBD_V08] = { .name = "v08", .args = (char *[]){"device", "index", NULL}, .parse = v07_parse, .open = v08_md_open, .close = v08_md_close, .md_initialize = v08_md_initialize, .md_disk_to_cpu = v08_md_disk_to_cpu, .md_cpu_to_disk = v08_md_cpu_to_disk, .get_gi = m_get_uuid, .show_gi = m_show_uuid, .set_gi = m_set_uuid, .outdate_gi = m_outdate_uuid, .invalidate_gi = m_invalidate_uuid, }, [DRBD_V09] = { .name = "v09", .args = (char *[]){"device", "index", NULL}, .parse = v07_parse, .open = v08_md_open, .close = v08_md_close, .md_initialize = v09_md_initialize, .md_disk_to_cpu = v09_md_disk_to_cpu, .md_cpu_to_disk = v09_md_cpu_to_disk, .get_gi = m_get_v9_uuid, .show_gi = m_show_v9_uuid, .set_gi = m_set_v9_uuid, .outdate_gi = m_outdate_uuid, .invalidate_gi = m_invalidate_v9_uuid, }, }; static inline enum md_format format_version(struct format *cfg) { return (cfg->ops - f_ops); } static inline int is_v06(struct format *cfg) { return format_version(cfg) == DRBD_V06; } static inline int is_v07(struct format *cfg) { return format_version(cfg) == DRBD_V07; } static inline int is_v08(struct format *cfg) { return format_version(cfg) == DRBD_V08; } static inline int is_v09(struct format *cfg) { return format_version(cfg) == DRBD_V09; } /****************************************** Commands we know about: ******************************************/ struct meta_cmd { const char *name; const char *args; int (*function) (struct format *, char **argv, int argc); int show_in_usage:1; int node_id_required:1; int modifies_md:1; }; /* Global command pointer, to be able to change behavior in helper functions * based on which top-level command is being processed. */ static struct meta_cmd *command; /* pre declarations */ int meta_get_gi(struct format *cfg, char **argv, int argc); int meta_show_gi(struct format *cfg, char **argv, int argc); int meta_dump_md(struct format *cfg, char **argv, int argc); int meta_apply_al(struct format *cfg, char **argv, int argc); int meta_restore_md(struct format *cfg, char **argv, int argc); int meta_verify_dump_file(struct format *cfg, char **argv, int argc); int meta_create_md(struct format *cfg, char **argv, int argc); int meta_wipe_md(struct format *cfg, char **argv, int argc); int meta_outdate(struct format *cfg, char **argv, int argc); int meta_invalidate(struct format *cfg, char **argv, int argc); int meta_set_gi(struct format *cfg, char **argv, int argc); int meta_read_dev_uuid(struct format *cfg, char **argv, int argc); int meta_write_dev_uuid(struct format *cfg, char **argv, int argc); int meta_dstate(struct format *cfg, char **argv, int argc); int meta_chk_offline_resize(struct format *cfg, char **argv, int argc); int meta_forget_peer(struct format *cfg, char **argv, int argc); struct meta_cmd cmds[] = { {"get-gi", 0, meta_get_gi, 1, 1, 0}, {"show-gi", 0, meta_show_gi, 1, 1, 0}, {"dump-md", 0, meta_dump_md, 1, 0, 0}, {"restore-md", "file", meta_restore_md, 1, 0, 1}, {"verify-dump", "file", meta_verify_dump_file, 1, 0, 0}, {"apply-al", 0, meta_apply_al, 1, 0, 1}, {"wipe-md", 0, meta_wipe_md, 1, 0, 1}, {"outdate", 0, meta_outdate, 1, 0, 1}, {"invalidate", 0, meta_invalidate, 1, 0, 1}, {"dstate", 0, meta_dstate, 1, 0, 0}, {"read-dev-uuid", 0, meta_read_dev_uuid, 0, 0, 0}, {"write-dev-uuid", "VAL", meta_write_dev_uuid, 0, 0, 1}, /* FIXME: Get and set node and peer ids */ {"set-gi", ":::VAL:VAL:...", meta_set_gi, 0, 1, 1}, {"check-resize", 0, meta_chk_offline_resize, 1, 0, 1}, {"create-md", "[--peer-max-bio-size {val}] " "[--al-stripes {val}] " "[--al-stripe-size-kB {val}] " "{max_peers}", meta_create_md, 1, 0, 1}, {"forget-peer", 0, meta_forget_peer, 1, 1, 1}, }; /* * generic helpers */ #define PREAD(cfg,b,c,d) pread_or_die((cfg),(b),(c),(d), __func__ ) #define PWRITE(cfg,b,c,d) pwrite_or_die((cfg),(b),(c),(d), __func__ ) /* Do we want to exit() right here, * or do we want to duplicate the error handling everywhere? */ void pread_or_die(struct format *cfg, void *buf, size_t count, off_t offset, const char* tag) { int fd = cfg->md_fd; ssize_t c = pread(fd, buf, count, offset); if (verbose >= 2) { fflush(stdout); fprintf(stderr, " %-26s: pread(%u, ...,%6lu,%12llu)\n", tag, fd, (unsigned long)count, (unsigned long long)offset); if (count & ((1<<12)-1)) fprintf(stderr, "\tcount will cause EINVAL on hard sect size != 512\n"); if (offset & ((1<<12)-1)) fprintf(stderr, "\toffset will cause EINVAL on hard sect size != 512\n"); } if (c < 0) { fprintf(stderr,"pread(%u,...,%lu,%llu) in %s failed: %s\n", fd, (unsigned long)count, (unsigned long long)offset, tag, strerror(errno)); exit(10); } else if ((size_t)c != count) { fprintf(stderr,"confused in %s: expected to read %d bytes," " actually read %d\n", tag, (int)count, (int)c); exit(10); } if (verbose > 10) fprintf_hex(stderr, offset, buf, count); } #define min(x,y) ((x) < (y) ? (x) : (y)) #define min3(x,y,z) (min(min(x,y),z)) void validate_offsets_or_die(struct format *cfg, size_t count, off_t offset, const char* tag) { off_t al_offset = cfg->md_offset + cfg->md.al_offset * 512LL; off_t bm_offset = cfg->md_offset + cfg->md.bm_offset * 512LL; off_t min_offset; off_t max_offset; if (al_offset != cfg->al_offset) fprintf(stderr, "%s: ambiguous al_offset: "U64" vs %llu\n", tag, cfg->al_offset, (unsigned long long)al_offset); if (bm_offset != cfg->bm_offset) fprintf(stderr, "%s: ambiguous bm_offset: "U64" vs %llu\n", tag, cfg->bm_offset, (unsigned long long)bm_offset); min_offset = min3(cfg->md_offset, al_offset, bm_offset); max_offset = min_offset + cfg->md.md_size_sect * 512LL; if (min_offset < 0) fprintf(stderr, "%s: negative minimum offset: %lld\n", tag, (long long)min_offset); /* If we wipe some old meta data block, * that hopefully falls outside the range of the current meta data. * Skip the range check below. */ if (offset != 0 && (offset == cfg->wipe_fixed ||offset == cfg->wipe_flex ||offset == cfg->wipe_resize)) return; if (offset < min_offset || (offset + count) > max_offset) { fprintf(stderr, "%s: offset+count ("U64"+%zu) not in meta data area range ["U64"; "U64"], aborted\n", tag, offset, count, min_offset, max_offset); if (ignore_sanity_checks) { fprintf(stderr, "Ignored due to --ignore-sanity-checks\n"); } else { fprintf(stderr, "If you want to force this, tell me to --ignore-sanity-checks\n"); exit(10); } } } static unsigned n_writes = 0; void pwrite_or_die(struct format *cfg, const void *buf, size_t count, off_t offset, const char* tag) { int fd = cfg->md_fd; ssize_t c; validate_offsets_or_die(cfg, count, offset, tag); ++n_writes; if (dry_run) { fprintf(stderr, " %-26s: pwrite(%u, ...,%6lu,%12llu) SKIPPED DUE TO DRY-RUN\n", tag, fd, (unsigned long)count, (unsigned long long)offset); if (verbose > 10) fprintf_hex(stderr, offset, buf, count); return; } c = pwrite(fd, buf, count, offset); if (verbose >= 2) { fflush(stdout); fprintf(stderr, " %-26s: pwrite(%u, ...,%6lu,%12llu)\n", tag, fd, (unsigned long)count, (unsigned long long)offset); if (count & ((1<<12)-1)) fprintf(stderr, "\tcount will cause EINVAL on hard sect size != 512\n"); if (offset & ((1<<12)-1)) fprintf(stderr, "\toffset will cause EINVAL on hard sect size != 512\n"); } if (c < 0) { fprintf(stderr,"pwrite(%u,...,%lu,%llu) in %s failed: %s\n", fd, (unsigned long)count, (unsigned long long)offset, tag, strerror(errno)); exit(10); } else if ((size_t)c != count) { /* FIXME we might just now have corrupted the on-disk data */ fprintf(stderr,"confused in %s: expected to write %d bytes," " actually wrote %d\n", tag, (int)count, (int)c); exit(10); } } size_t pwrite_with_limit_or_die(struct format *cfg, const void *buf, size_t count, off_t offset, off_t limit, const char* tag) { if (offset >= limit) { fprintf(stderr,"confused in %s: offset (%llu) > limit (%llu)\n", tag, (unsigned long long)offset, (unsigned long long)limit); exit(10); } if (count > limit - offset) { fprintf(stderr,"in %s: truncating byte count from %lu to %lu\n", tag, (unsigned long)count, (unsigned long)(limit -offset)); count = limit - offset; } pwrite_or_die(cfg, buf, count, offset, tag); return count; } void m_get_gc(struct md_cpu *md, int node_id __attribute((unused))) { dt_print_gc(md->gc); } void m_show_gc(struct md_cpu *md, int node_id __attribute((unused))) { dt_pretty_print_gc(md->gc); } void m_get_uuid(struct md_cpu *md, int node_id) { uint64_t uuids[] = { [UI_CURRENT] = md->current_uuid, [UI_BITMAP] = md->peers[node_id].bitmap_uuid, [UI_HISTORY_START] = md->history_uuids[0], [UI_HISTORY_END] = md->history_uuids[1], }; dt_print_uuids(uuids, md->flags); } void m_show_uuid(struct md_cpu *md, int node_id) { uint64_t uuids[] = { [UI_CURRENT] = md->current_uuid, [UI_BITMAP] = md->peers[node_id].bitmap_uuid, [UI_HISTORY_START] = md->history_uuids[0], [UI_HISTORY_END] = md->history_uuids[1], }; dt_pretty_print_uuids(uuids, md->flags); } void m_get_v9_uuid(struct md_cpu *md, int node_id) { uint64_t uuids[] = { [UI_CURRENT] = md->current_uuid, [UI_BITMAP] = md->peers[node_id].bitmap_uuid, [UI_HISTORY_START] = md->history_uuids[0], [UI_HISTORY_END] = md->history_uuids[1], }; dt_print_v9_uuids(uuids, md->flags, md->peers[node_id].flags); } void m_show_v9_uuid(struct md_cpu *md, int node_id) { uint64_t uuids[] = { [UI_CURRENT] = md->current_uuid, [UI_BITMAP] = md->peers[node_id].bitmap_uuid, [UI_HISTORY_START] = md->history_uuids[0], [UI_HISTORY_END] = md->history_uuids[1], }; dt_pretty_print_v9_uuids(uuids, md->flags, md->peers[node_id].flags); } int m_strsep_u32(char **s, uint32_t *val) { char *t, *e; unsigned long v; if ((t = strsep(s, ":"))) { if (strlen(t)) { e = t; errno = 0; v = strtoul(t, &e, 0); if (*e != 0) { fprintf(stderr, "'%s' is not a number.\n", *s); exit(10); } if (errno) { fprintf(stderr, "'%s': ", *s); perror(0); exit(10); } if (v > 0xFFffFFffUL) { fprintf(stderr, "'%s' is out of range (max 0xFFffFFff).\n", *s); exit(10); } *val = (uint32_t)v; } return 1; } return 0; } int m_strsep_u64(char **s, uint64_t *val) { char *t, *e; uint64_t v; if ((t = strsep(s, ":"))) { if (strlen(t)) { e = t; errno = 0; v = strto_u64(t, &e, 16); if (*e != 0) { fprintf(stderr, "'%s' is not a number.\n", *s); exit(10); } if (errno) { fprintf(stderr, "'%s': ", *s); perror(0); exit(10); } *val = v; } return 1; } return 0; } int m_strsep_bit(char **s, uint32_t *val, int mask) { uint32_t d; int rv; d = *val & mask ? 1 : 0; rv = m_strsep_u32(s, &d); if (d > 1) { fprintf(stderr, "'%d' is not 0 or 1.\n", d); exit(10); } if (d) *val |= mask; else *val &= ~mask; return rv; } void m_set_gc(struct md_cpu *md, int node_id __attribute((unused)), char **argv, int argc __attribute((unused))) { char **str; str = &argv[0]; do { if (!m_strsep_bit(str, &md->gc[Flags], MDF_CONSISTENT)) break; if (!m_strsep_u32(str, &md->gc[HumanCnt])) break; if (!m_strsep_u32(str, &md->gc[TimeoutCnt])) break; if (!m_strsep_u32(str, &md->gc[ConnectedCnt])) break; if (!m_strsep_u32(str, &md->gc[ArbitraryCnt])) break; if (!m_strsep_bit(str, &md->gc[Flags], MDF_PRIMARY_IND)) break; if (!m_strsep_bit(str, &md->gc[Flags], MDF_CONNECTED_IND)) break; if (!m_strsep_bit(str, &md->gc[Flags], MDF_FULL_SYNC)) break; } while (0); } void m_set_uuid(struct md_cpu *md, int node_id, char **argv, int argc __attribute((unused))) { char **str; int i; str = &argv[0]; do { if (!m_strsep_u64(str, &md->current_uuid)) break; if (!m_strsep_u64(str, &md->peers[node_id].bitmap_uuid)) break; for (i = 0; i < HISTORY_UUIDS_V08; i++) if (!m_strsep_u64(str, &md->history_uuids[i])) return; if (!m_strsep_bit(str, &md->flags, MDF_CONSISTENT)) break; if (!m_strsep_bit(str, &md->flags, MDF_WAS_UP_TO_DATE)) break; if (!m_strsep_bit(str, &md->flags, MDF_PRIMARY_IND)) break; if (!m_strsep_bit(str, &md->flags, MDF_CONNECTED_IND)) break; if (!m_strsep_bit(str, &md->flags, MDF_FULL_SYNC)) break; if (!m_strsep_bit(str, &md->flags, MDF_PEER_OUT_DATED)) break; if (!m_strsep_bit(str, &md->flags, MDF_CRASHED_PRIMARY)) break; } while (0); } void m_set_v9_uuid(struct md_cpu *md, int node_id, char **argv, int argc __attribute((unused))) { char **str; int i; str = &argv[0]; do { if (!m_strsep_u64(str, &md->current_uuid)) break; if (!m_strsep_u64(str, &md->peers[node_id].bitmap_uuid)) break; for (i = 0; i < HISTORY_UUIDS_V08; i++) if (!m_strsep_u64(str, &md->history_uuids[i])) return; if (!m_strsep_bit(str, &md->flags, MDF_CONSISTENT)) break; if (!m_strsep_bit(str, &md->flags, MDF_WAS_UP_TO_DATE)) break; if (!m_strsep_bit(str, &md->flags, MDF_PRIMARY_IND)) break; if (!m_strsep_bit(str, &md->flags, MDF_CRASHED_PRIMARY)) break; if (!m_strsep_bit(str, &md->flags, MDF_AL_CLEAN)) break; if (!m_strsep_bit(str, &md->flags, MDF_AL_DISABLED)) break; if (!m_strsep_bit(str, &md->peers[node_id].flags, MDF_PEER_CONNECTED)) break; if (!m_strsep_bit(str, &md->peers[node_id].flags, MDF_PEER_OUTDATED)) break; if (!m_strsep_bit(str, &md->peers[node_id].flags, MDF_PEER_FENCING)) break; if (!m_strsep_bit(str, &md->peers[node_id].flags, MDF_PEER_FULL_SYNC)) break; if (!m_strsep_bit(str, &md->peers[node_id].flags, MDF_PEER_DEVICE_SEEN)) break; } while (0); } int m_outdate_gc(struct md_cpu *md __attribute((unused))) { fprintf(stderr, "Can not outdate GC based meta data!\n"); return 5; } int m_outdate_uuid(struct md_cpu *md) { if ( !(md->flags & MDF_CONSISTENT) ) { return 5; } md->flags &= ~MDF_WAS_UP_TO_DATE; return 0; } int m_invalidate_gc(struct md_cpu *md) { md->gc[Flags] &= ~MDF_CONSISTENT; md->gc[Flags] |= MDF_FULL_SYNC; return 5; } int m_invalidate_uuid(struct md_cpu *md) { md->flags &= ~MDF_CONSISTENT; md->flags &= ~MDF_WAS_UP_TO_DATE; md->flags |= MDF_FULL_SYNC; return 0; } int m_invalidate_v9_uuid(struct md_cpu *md) { int node_id; md->flags &= ~MDF_CONSISTENT; md->flags &= ~MDF_WAS_UP_TO_DATE; for (node_id = 0; node_id < DRBD_NODE_ID_MAX; node_id++) { md->peers[node_id].flags |= MDF_PEER_FULL_SYNC; } return 0; } /****************************************** begin of v06 {{{ ******************************************/ int v06_md_disk_to_cpu(struct format *cfg) { PREAD(cfg, on_disk_buffer, sizeof(struct md_on_disk_06), cfg->md_offset); md_disk_06_to_cpu(&cfg->md, (struct md_on_disk_06*)on_disk_buffer); return v06_validate_md(cfg); } int v06_md_cpu_to_disk(struct format *cfg) { if (v06_validate_md(cfg)) return -1; md_cpu_to_disk_06(on_disk_buffer, &cfg->md); PWRITE(cfg, on_disk_buffer, sizeof(struct md_on_disk_06), cfg->md_offset); return 0; } int v06_parse(struct format *cfg, char **argv, int argc, int *ai) { unsigned long minor; char *e; if (argc < 1) { fprintf(stderr, "Too few arguments for format\n"); exit(20); } e = argv[0]; minor = strtol(argv[0], &e, 0); if (*e != 0 || minor > 255UL) { fprintf(stderr, "'%s' is not a valid minor number.\n", argv[0]); exit(20); } if (asprintf(&e, "%s/drbd%lu", DRBD_LIB_DIR, minor) <= 18) { fprintf(stderr, "asprintf() failed.\n"); exit(20); }; cfg->md_device_name = e; *ai += 1; return 0; } int v06_md_open(struct format *cfg) { struct stat sb; cfg->md_fd = open(cfg->md_device_name, O_RDWR); if (cfg->md_fd == -1) { PERROR("open(%s) failed", cfg->md_device_name); return NO_VALID_MD_FOUND; } if (fstat(cfg->md_fd, &sb)) { PERROR("fstat() failed"); return NO_VALID_MD_FOUND; } if (!S_ISREG(sb.st_mode)) { fprintf(stderr, "'%s' is not a plain file!\n", cfg->md_device_name); return NO_VALID_MD_FOUND; } if (cfg->ops->md_disk_to_cpu(cfg)) { return NO_VALID_MD_FOUND; } return VALID_MD_FOUND; } int generic_md_close(struct format *cfg) { /* On /dev/ram0 we may not use O_SYNC for some kernels (eg. RHEL6 2.6.32), * and fsync() returns EIO, too. So we don't do error checking here. */ fsync(cfg->md_fd); if (close(cfg->md_fd)) { PERROR("close() failed"); return -1; } return 0; } int v06_md_initialize(struct format *cfg, int do_disk_writes __attribute((unused)), int max_peers __attribute((unused))) { cfg->md.gc[Flags] = 0; cfg->md.gc[HumanCnt] = 1; /* THINK 0? 1? */ cfg->md.gc[TimeoutCnt] = 1; cfg->md.gc[ConnectedCnt] = 1; cfg->md.gc[ArbitraryCnt] = 1; cfg->md.max_peers = 1; cfg->md.magic = DRBD_MD_MAGIC_06; return 0; } /****************************************** }}} end of v06 ******************************************/ static uint64_t max_usable_sectors(struct format *cfg) { /* We currently have two possible layouts: * external: * |----------- md_size_sect ------------------| * [ 4k superblock ][ activity log ][ Bitmap ] * | al_offset == 8 | * | bm_offset = al_offset + X | * ==> bitmap sectors = md_size_sect - bm_offset * * internal: * |----------- md_size_sect ------------------| * [data.....][ Bitmap ][ activity log ][ 4k superblock ] * | al_offset < 0 | * | bm_offset = al_offset - Y | * ==> bitmap sectors = Y = al_offset - bm_offset * * There also used to be the fixed size internal meta data, * which covers the last 128 MB of the device, * and has the same layout as the "external:" above. */ if(cfg->md_index == DRBD_MD_INDEX_INTERNAL || cfg->md_index == DRBD_MD_INDEX_FLEX_INT) { /* for internal meta data, the available storage is limitted by * the first meta data sector, even if the available bitmap * space would support more. */ return min3(cfg->md_offset, cfg->al_offset, cfg->bm_offset ) >> 9; } else { /* For external meta data, * we are limited by available on-disk bitmap space. * Ok, and by the lower level storage device; * which we don't know about here :-( */ ASSERT(cfg->md.bm_bytes_per_bit == 4096); return /* bitmap sectors */ (uint64_t)(cfg->md.md_size_sect - cfg->md.bm_offset) * 512 /* sector size */ * 8 /* bits per byte */ / 64 /* 64 bit words, for interoperability */ / cfg->md.max_peers * 64 /* back to bits, per bitmap slot */ /* storage bytes per bitmap bit; * currently always 4096 */ * cfg->md.bm_bytes_per_bit / 512; /* and back to sectors */; } #undef min } void re_initialize_md_offsets(struct format *cfg) { uint64_t md_size_sect; int al_size_sect; /* These two are needed for bm_bytes()... Ensure sane defaults... */ if (cfg->md.bm_bytes_per_bit == 0) cfg->md.bm_bytes_per_bit = DEFAULT_BM_BLOCK_SIZE; if (cfg->md.max_peers == 0) cfg->md.max_peers = 1; al_size_sect = cfg->md.al_stripes * cfg->md.al_stripe_size_4k * 8; switch(cfg->md_index) { default: cfg->md.md_size_sect = MD_RESERVED_SECT_07; cfg->md.al_offset = MD_AL_OFFSET_07; cfg->md.bm_offset = cfg->md.al_offset + al_size_sect; break; case DRBD_MD_INDEX_FLEX_EXT: /* just occupy the full device; unit: sectors */ cfg->md.md_size_sect = cfg->bd_size >> 9; cfg->md.al_offset = MD_AL_OFFSET_07; cfg->md.bm_offset = cfg->md.al_offset + al_size_sect; break; case DRBD_MD_INDEX_INTERNAL: /* only v07 */ cfg->md.md_size_sect = MD_RESERVED_SECT_07; cfg->md.al_offset = MD_AL_OFFSET_07; cfg->md.bm_offset = MD_BM_OFFSET_07; break; case DRBD_MD_INDEX_FLEX_INT: /* al size is still fixed */ cfg->md.al_offset = -al_size_sect; /* we need (slightly less than) ~ this much bitmap sectors: */ md_size_sect = bm_bytes(&cfg->md, cfg->bd_size >> 9) >> 9; md_size_sect = ALIGN(md_size_sect, 8); /* align on 4K blocks */ if (md_size_sect > (MD_BM_MAX_BYTE_FLEX>>9)) { fprintf(stderr, "Bitmap for that device got too large.\n"); if (BITS_PER_LONG == 32) fprintf(stderr, "Maybe try a 64bit arch?\n"); exit(10); } /* plus the "drbd meta data super block", * and the activity log; unit still sectors */ md_size_sect += MD_AL_OFFSET_07 + al_size_sect; cfg->md.md_size_sect = md_size_sect; cfg->md.bm_offset = -md_size_sect + MD_AL_OFFSET_07; break; } cfg->al_offset = cfg->md_offset + cfg->md.al_offset * 512LL; cfg->bm_offset = cfg->md_offset + cfg->md.bm_offset * 512LL; cfg->max_usable_sect = max_usable_sectors(cfg); if (verbose >= 2) { fprintf(stderr,"md_offset: "U64"\n", cfg->md_offset); fprintf(stderr,"al_offset: "U64" (%d)\n", cfg->al_offset, cfg->md.al_offset); fprintf(stderr,"bm_offset: "U64" (%d)\n", cfg->bm_offset, cfg->md.bm_offset); fprintf(stderr,"md_size_sect: "U32"\n", cfg->md.md_size_sect); fprintf(stderr,"max_usable_sect: "U64"\n", cfg->max_usable_sect); } } void initialize_al(struct format *cfg) { unsigned int mx = cfg->md.al_stripes * cfg->md.al_stripe_size_4k; size_t al_size = mx * 4096; memset(on_disk_buffer, 0x00, al_size); if (format_version(cfg) >= DRBD_V08) { /* DRBD <= 8.3 does not care if it is all zero, * or otherwise wrong magic. * * For 8.4 and 9.0, we initialize to something that is * valid magic, valid crc, and transaction_type = 0xffff. */ struct al_4k_transaction_on_disk *al = on_disk_buffer; unsigned crc_be = 0; int i; for (i = 0; i < mx; i++, al++) { al->magic.be = cpu_to_be32(DRBD_AL_MAGIC); al->transaction_type.be = cpu_to_be16(AL_TR_INITIALIZED); /* crc calculated once */ if (i == 0) crc_be = cpu_to_be32(crc32c(0, (void*)al, 4096)); al->crc32c.be = crc_be; } } pwrite_or_die(cfg, on_disk_buffer, al_size, cfg->al_offset, "md_initialize_common:AL"); } void check_for_existing_data(struct format *cfg); /* MAYBE DOES DISK WRITES!! */ int md_initialize_common(struct format *cfg, int do_disk_writes) { cfg->md.al_nr_extents = 257; /* arbitrary. */ cfg->md.bm_bytes_per_bit = DEFAULT_BM_BLOCK_SIZE; re_initialize_md_offsets(cfg); if (!do_disk_writes) return 0; check_for_existing_data(cfg); /* do you want to initialize al to something more useful? */ printf("initializing activity log\n"); if (MD_AL_MAX_SECT_07*512 > buffer_size) { fprintf(stderr, "%s:%u: LOGIC BUG\n" , __FILE__ , __LINE__ ); exit(111); } initialize_al(cfg); /* THINK * do we really need to initialize the bitmap? */ if (INITIALIZE_BITMAP) { /* need to sector-align this for O_DIRECT. * "sector" here means hard-sect size, which may be != 512. * Note that even though ALIGN does round up, for sector sizes * of 512, 1024, 2048, 4096 Bytes, this will be fully within * the claimed meta data area, since we already align all * "interesting" parts of that to 4kB */ const size_t bm_bytes = ALIGN(cfg->bm_bytes, cfg->md_hard_sect_size); size_t i = bm_bytes; off_t bm_on_disk_off = cfg->bm_offset; unsigned int percent_done = 0; unsigned int percent_last_report = 0; size_t chunk; fprintf(stderr,"initializing bitmap (%u KB)\n", (unsigned int)(bm_bytes>>10)); memset(on_disk_buffer, 0xff, buffer_size); while (i) { chunk = buffer_size < i ? buffer_size : i; pwrite_or_die(cfg, on_disk_buffer, chunk, bm_on_disk_off, "md_initialize_common:BM"); bm_on_disk_off += chunk; i -= chunk; percent_done = 100*(bm_bytes-i)/bm_bytes; if (percent_done != percent_last_report) { fprintf(stderr,"\r%u%%", percent_done); percent_last_report = percent_done; } } fprintf(stderr,"\r100%%\n"); } else { fprintf(stderr,"NOT initializing bitmap\n"); } return 0; } /****************************************** begin of v07 {{{ ******************************************/ uint64_t v07_style_md_get_byte_offset(const int idx, const uint64_t bd_size) { uint64_t offset; switch(idx) { default: /* external, some index */ offset = MD_RESERVED_SECT_07 * idx * 512; break; case DRBD_MD_INDEX_INTERNAL: offset = (bd_size & ~4095LLU) - MD_RESERVED_SECT_07 * 512; break; case DRBD_MD_INDEX_FLEX_INT: /* sizeof(struct md_on_disk_07) == 4k * position: last 4k aligned block of 4k size */ offset = bd_size - 4096LLU; offset &= ~4095LLU; break; case DRBD_MD_INDEX_FLEX_EXT: offset = 0; break; } return offset; } void printf_al_07(struct format *cfg, struct al_sector_on_disk *al_disk) { struct al_sector_cpu al_cpu; unsigned s, i; unsigned max_slot_nr = 0; for (s = 0; s < MD_AL_MAX_SECT_07; s++) { int ok = v07_al_disk_to_cpu(&al_cpu, al_disk + s); printf("# sector %2u { %s\n", s, ok ? "valid" : "invalid"); printf("# \tmagic: 0x%08x\n", al_cpu.magic); printf("# \ttr: %10u\n", al_cpu.tr_number); for (i = 0; i < 62; i++) { printf("# \t%2u: %10u %10u\n", i, al_cpu.updates[i].pos, al_cpu.updates[i].extent); if (al_cpu.updates[i].pos > max_slot_nr && al_cpu.updates[i].pos != -1U) max_slot_nr = al_cpu.updates[i].pos; } printf("# \txor: 0x%08x\n", al_cpu.xor_sum); printf("# }\n"); } if (max_slot_nr >= cfg->md.al_nr_extents) printf( "### CAUTION: maximum slot number found in AL: %u\n" "### CAUTION: but 'super-block' al-extents is: %u\n", max_slot_nr, cfg->md.al_nr_extents); } void printf_al_84(struct format *cfg, struct al_4k_transaction_on_disk *al_disk, unsigned block_nr_offset, unsigned N) { struct al_4k_cpu al_cpu; unsigned b, i; unsigned max_slot_nr = 0; for (b = 0; b < N; b++) { int ok = v84_al_disk_to_cpu(&al_cpu, al_disk + b); if (!ok) { printf("# block %2u { INVALID }\n", b + block_nr_offset); continue; } if (al_cpu.transaction_type == 0xffff) { printf("# block %2u { INITIALIZED }\n", b + block_nr_offset); continue; } printf("# block %2u {\n", b + block_nr_offset); printf("# \tmagic: 0x%08x\n", al_cpu.magic); printf("# \ttype: 0x%04x\n", al_cpu.transaction_type); printf("# \ttr: %10u\n", al_cpu.tr_number); printf("# \tactive set size: %u\n", al_cpu.context_size); if (al_cpu.context_size -1 > max_slot_nr) max_slot_nr = al_cpu.context_size -1; for (i = 0; i < AL_CONTEXT_PER_TRANSACTION; i++) { unsigned slot = al_cpu.context_start_slot_nr + i; if (al_cpu.context[i] == ~0U && slot >= al_cpu.context_size) continue; if (slot > max_slot_nr) max_slot_nr = slot; printf("# \t%2u: %10u %10u\n", i, slot, al_cpu.context[i]); } printf("# \tupdates: %u\n", al_cpu.n_updates); for (i = 0; i < AL_UPDATES_PER_TRANSACTION; i++) { if (i >= al_cpu.n_updates && al_cpu.update_slot_nr[i] == (uint16_t)(~0U)) continue; printf("# \t%2u: %10u %10u\n", i, al_cpu.update_slot_nr[i], al_cpu.update_extent_nr[i]); if (al_cpu.update_slot_nr[i] > max_slot_nr) max_slot_nr = al_cpu.update_slot_nr[i]; } printf("# \tcrc32c: 0x%08x\n", al_cpu.crc32c); printf("# }\n"); } if (max_slot_nr >= cfg->md.al_nr_extents) printf( "### CAUTION: maximum slot number found in AL: %u\n" "### CAUTION: but 'super-block' al-extents is: %u\n", max_slot_nr, cfg->md.al_nr_extents); } void printf_al(struct format *cfg) { off_t al_on_disk_off = cfg->al_offset; off_t al_size = cfg->md.al_stripes * cfg->md.al_stripe_size_4k * 4096; struct al_sector_on_disk *al_512_disk = on_disk_buffer; struct al_4k_transaction_on_disk *al_4k_disk = on_disk_buffer; unsigned block_nr_offset = 0; unsigned N; int is_al_84 = is_v09(cfg) || (is_v08(cfg) && (cfg->md.al_stripes != 1 || cfg->md.al_stripe_size_4k != 8)); printf("# al {\n"); while (al_size) { off_t chunk = al_size; if (chunk > buffer_size) chunk = buffer_size; ASSERT(chunk); pread_or_die(cfg, on_disk_buffer, chunk, al_on_disk_off, "printf_al"); al_on_disk_off += chunk; al_size -= chunk; N = chunk/4096; /* FIXME * we should introduce a new meta data "super block" magic, so we won't * have the same super block with two different activity log * transaction layouts */ if (format_version(cfg) < DRBD_V08) printf_al_07(cfg, al_512_disk); /* looks like we have the new al format */ else if (is_al_84 || DRBD_AL_MAGIC == be32_to_cpu(al_4k_disk[0].magic.be) || DRBD_AL_MAGIC == be32_to_cpu(al_4k_disk[1].magic.be)) { is_al_84 = 1; printf_al_84(cfg, al_4k_disk, block_nr_offset, N); } /* try the old al format anyways */ else printf_al_07(cfg, al_512_disk); block_nr_offset += N; if (al_size && !is_al_84) { printf("### UNEXPECTED ACTIVITY LOG SIZE!\n"); } } printf("# }\n"); } /* One activity log extent represents 4M of storage, * one bit corresponds to 4k. * 4M / 4k / 8bit per byte */ #define BM_BYTES_PER_AL_EXT (1UL << (22 - 12 - 3)) struct al_cursor { unsigned i; uint32_t tr_number; }; static int replay_al_07(struct format *cfg, uint32_t *hot_extent) { unsigned int mx; struct al_sector_cpu al_cpu[MD_AL_MAX_SECT_07]; unsigned char valid[MD_AL_MAX_SECT_07]; struct al_sector_on_disk *al_disk = on_disk_buffer; unsigned b, i; int found_valid = 0; struct al_cursor oldest = { 0, }; struct al_cursor newest = { 0, }; /* Endian convert, validate, and find oldest to newest log range. * In contrast to the 8.4 log format, this log format does NOT * use all log space, but only as many sectors as absolutely necessary. * * We need to trust the "al_nr_extents" setting in the "super block". */ #define AL_EXTENTS_PT 61 /* mx = 1 + div_ceil(al_nr_extents, AL_EXTENTS_PT); */ mx = 1 + (cfg->md.al_nr_extents + AL_EXTENTS_PT -1) / AL_EXTENTS_PT; for (b = 0; b < mx; b++) { valid[b] = v07_al_disk_to_cpu(al_cpu + b, al_disk + b); if (!valid[b]) continue; if (++found_valid == 1) { oldest.i = b; oldest.tr_number = al_cpu[b].tr_number; newest = oldest; continue; } d_expect(al_cpu[b].tr_number != oldest.tr_number); d_expect(al_cpu[b].tr_number != newest.tr_number); if ((int)al_cpu[b].tr_number - (int)oldest.tr_number < 0) { d_expect(oldest.tr_number - al_cpu[b].tr_number + b - oldest.i == mx); oldest.i = b; oldest.tr_number = al_cpu[b].tr_number; } if ((int)al_cpu[b].tr_number - (int)newest.tr_number > 0) { d_expect(al_cpu[b].tr_number - newest.tr_number == b - newest.i); newest.i = b; newest.tr_number = al_cpu[b].tr_number; } } if (!found_valid) { /* not even one transaction was valid. * Has this ever been initialized correctly? */ fprintf(stderr, "No usable activity log found.\n"); /* with up to 8.3 style activity log, this is NOT an error. */ return 0; } /* we do expect at most one corrupt transaction, and only in case * things went wrong during transaction write. */ if (found_valid != mx) { fprintf(stderr, "%u corrupt or uninitialized AL transactions found\n", mx - found_valid); fprintf(stderr, "You can safely ignore this if this node was cleanly stopped (no crash).\n"); } /* Any other paranoia checks possible with this log format? */ /* Ok, so we found valid update transactions. Reconstruct the "active * set" at the time of the newest transaction. */ /* wrap around */ if (newest.i < oldest.i) newest.i += mx; for (b = oldest.i; b <= newest.i; b++) { unsigned idx = b % mx; if (!valid[idx]) continue; /* This loop processes both "context" and "update" information. * There is only one update, on index 0, * which is why this loop counts down. */ for (i = AL_EXTENTS_PT; (int)i >= 0; i--) { unsigned slot = al_cpu[idx].updates[i].pos; if (al_cpu[idx].updates[i].extent == ~0U) continue; if (slot >= AL_EXTENTS_MAX) { fprintf(stderr, "slot number out of range: tr:%u slot:%u\n", idx, slot); continue; } hot_extent[slot] = al_cpu[idx].updates[i].extent; } } return found_valid; } static unsigned int al_tr_number_to_on_disk_slot(struct format *cfg, unsigned int b, unsigned int mx) { const unsigned int stripes = cfg->md.al_stripes; const unsigned int stripe_size_4kB = cfg->md.al_stripe_size_4k; /* transaction number, modulo on-disk ring buffer wrap around */ unsigned int t = b % mx; /* ... to aligned 4k on disk block */ t = ((t % stripes) * stripe_size_4kB) + t/stripes; return t; } /* Expects the AL to be read into on_disk_buffer already. * Returns negative error code for non-interpretable data, * 0 for "just mark me clean, nothing more to do", * and positive if we have to apply something. */ int replay_al_84(struct format *cfg, uint32_t *hot_extent) { const unsigned int mx = cfg->md.al_stripes * cfg->md.al_stripe_size_4k; struct al_4k_transaction_on_disk *al_disk = on_disk_buffer; struct al_4k_cpu *al_cpu = NULL; unsigned b, o, i; int found_valid = 0; int found_valid_updates = 0; struct al_cursor oldest = { 0, }; struct al_cursor newest = { 0, }; al_cpu = calloc(mx, sizeof(*al_cpu)); if (!al_cpu) { fprintf(stderr, "Could not calloc(%u, sizeof(*al_cpu))\n", mx); exit(30); /* FIXME sane exit codes */ } /* endian convert, validate, and find oldest to newest log range */ for (b = 0; b < mx; b++) { o = al_tr_number_to_on_disk_slot(cfg, b, mx); if (!v84_al_disk_to_cpu(al_cpu + b, al_disk + o)) continue; ++found_valid; if (al_cpu[b].transaction_type == AL_TR_INITIALIZED) continue; d_expect(al_cpu[b].transaction_type == AL_TR_UPDATE); if (++found_valid_updates == 1) { oldest.i = b; oldest.tr_number = al_cpu[b].tr_number; newest = oldest; continue; } d_expect(al_cpu[b].tr_number != oldest.tr_number); d_expect(al_cpu[b].tr_number != newest.tr_number); if ((int)al_cpu[b].tr_number - (int)oldest.tr_number < 0) { d_expect(oldest.tr_number - al_cpu[b].tr_number + b - oldest.i == mx); oldest.i = b; oldest.tr_number = al_cpu[b].tr_number; } if ((int)al_cpu[b].tr_number - (int)newest.tr_number > 0) { d_expect(al_cpu[b].tr_number - newest.tr_number == b - newest.i); newest.i = b; newest.tr_number = al_cpu[b].tr_number; } } if (!found_valid) { /* not even one transaction was valid. * Has this ever been initialized correctly? */ fprintf(stderr, "No usable activity log found. Do you need to create-md?\n"); free(al_cpu); return -ENODATA; } /* we do expect at most one corrupt transaction, and only in case * things went wrong during transaction write. */ if (found_valid != mx) fprintf(stderr, "%u corrupt AL transactions found\n", mx - found_valid); if (!found_valid_updates) { if (found_valid == mx) /* nothing to do, all slots are valid AL_TR_INITIALIZED */ return 0; /* this is only expected, in case the _first_ transaction * somehow failed. */ if (!al_cpu[0].is_valid && found_valid == mx - 1) return 0; /* Hmm. Some transactions are valid. * Some are not. * This is not expected. */ /* FIXME how do we want to handle this? */ fprintf(stderr, "No valid AL update transaction found.\n"); return -EINVAL; } /* FIXME what do we do * about more than one corrupt transaction? * about corrupt transaction in the middle of the oldest -> newest range? */ /* Ok, so we found valid update transactions. Reconstruct the "active * set" at the time of the newest transaction. */ /* wrap around */ if (newest.i < oldest.i) newest.i += mx; for (b = oldest.i; b <= newest.i; b++) { unsigned idx = b % mx; if (!al_cpu[idx].is_valid || al_cpu[idx].transaction_type == AL_TR_INITIALIZED) continue; for (i = 0; i < AL_CONTEXT_PER_TRANSACTION; i++) { unsigned slot = al_cpu[idx].context_start_slot_nr + i; if (al_cpu[idx].context[i] == ~0U && slot >= al_cpu[idx].context_size) continue; if (slot >= AL_EXTENTS_MAX) { fprintf(stderr, "slot number out of range: tr:%u slot:%u\n", idx, slot); continue; } hot_extent[slot] = al_cpu[idx].context[i]; } for (i = 0; i < AL_UPDATES_PER_TRANSACTION; i++) { unsigned slot = al_cpu[idx].update_slot_nr[i]; if (i >= al_cpu[idx].n_updates && slot == (uint16_t)(~0U)) continue; if (slot >= AL_EXTENTS_MAX) { fprintf(stderr, "update slot number out of range: tr:%u slot:%u\n", idx, slot); continue; } hot_extent[slot] = al_cpu[idx].update_extent_nr[i]; } } return found_valid_updates; } int cmp_u32(const void *p1, const void *p2) { const unsigned a = *(unsigned *)p1; const unsigned b = *(unsigned *)p2; /* how best to deal with 32bit wrap? */ return a < b ? -1 : a == b ? 0 : 1; } void apply_al(struct format *cfg, uint32_t *hot_extent) { const unsigned int extents_size = BM_BYTES_PER_AL_EXT * cfg->md.max_peers; const size_t bm_bytes = ALIGN(cfg->bm_bytes, cfg->md_hard_sect_size); off_t bm_on_disk_off = cfg->bm_offset; size_t bm_on_disk_pos = 0; size_t chunk = 0; int i, j; /* can only be AL_EXTENTS_MAX * BM_BYTES_PER_AL_EXT * 8, * which currently is 65534 * 128 * 8 == 67106816 * fits easily into 32bit. */ unsigned additional_bits_set = 0; uint64_t *w; char ppb[10]; /* Now, actually apply this stuff to the on-disk bitmap. * Since one AL extent corresponds to 128 bytes of bitmap, * we need to do some read/modify/write cycles here. * * Note that this can be slow due to the use of O_DIRECT, * worst case it does 65534 (AL_EXTENTS_MAX) cycles of * - read 128 kByte (buffer_size) * - memset 128 Bytes (BM_BYTES_PER_AL_EXT) to 0xff * - write 128 kByte * This implementation could optimized in various ways: * - don't use direct IO; has other drawbacks * - first scan hot_extents for extent ranges, * and optimize the IO size. * - use aio with multiple buffers * - ... */ for (i = 0; i < AL_EXTENTS_MAX; i++) { size_t bm_pos; unsigned bits_set = 0; if (hot_extent[i] == ~0U) break; ASSERT(cfg->md.bm_bytes_per_bit == 4096); ASSERT(BM_BYTES_PER_AL_EXT % 4 == 0); bm_pos = hot_extent[i] * extents_size; if (bm_pos >= bm_bytes) { fprintf(stderr, "extent %u beyond end of bitmap!\n", hot_extent[i]); /* could break or return error here, * but I'll just print a warning, and skip, each of them. */ continue; } /* On first iteration, or when the current position in the bitmap * exceeds the current buffer, write out the current buffer, if any, * and read in the next (at most buffer_size) chunk of bitmap, * containing the currently processed bitmap region. */ if (i == 0 || bm_pos + extents_size >= bm_on_disk_pos + chunk) { if (i != 0) pwrite_or_die(cfg, on_disk_buffer, chunk, bm_on_disk_off + bm_on_disk_pos, "apply_al"); /* don't special case logical sector size != 512, * operate in 4k always. */ bm_on_disk_pos = bm_pos & ~(off_t)(4095); chunk = bm_bytes - bm_on_disk_pos; if (chunk > buffer_size) chunk = buffer_size; pread_or_die(cfg, on_disk_buffer, chunk, bm_on_disk_off + bm_on_disk_pos, "apply_al"); } ASSERT(bm_pos - bm_on_disk_pos <= chunk - extents_size); ASSERT((bm_pos - bm_on_disk_pos) % sizeof(uint64_t) == 0); w = (uint64_t *)on_disk_buffer + (bm_pos - bm_on_disk_pos)/sizeof(uint64_t); for (j = 0; j < extents_size/sizeof(uint64_t); j++) bits_set += generic_hweight64(w[j]); additional_bits_set += extents_size * 8 - bits_set; memset((char*)on_disk_buffer + (bm_pos - bm_on_disk_pos), 0xff, extents_size); } /* we still need to write out the buffer of the last iteration */ if (i != 0) { pwrite_or_die(cfg, on_disk_buffer, chunk, bm_on_disk_off + bm_on_disk_pos, "apply_al"); fprintf(stderr, "Marked additional %s as out-of-sync based on AL.\n", ppsize(ppb, additional_bits_set * 4)); } else fprintf(stderr, "Nothing to do.\n"); } int need_to_apply_al(struct format *cfg) { switch (format_version(cfg)) { case DRBD_V06: return 0; /* there was no activity log in 0.6 */ case DRBD_V07: return cfg->md.gc[Flags] & MDF_PRIMARY_IND; case DRBD_V08: case DRBD_V09: return cfg->md.flags & MDF_PRIMARY_IND; case DRBD_UNKNOWN: fprintf(stderr, "BUG in %s().\n", __FUNCTION__); } return 0; } int v08_move_internal_md_after_resize(struct format *cfg); int meta_apply_al(struct format *cfg, char **argv __attribute((unused)), int argc) { off_t al_size; struct al_4k_transaction_on_disk *al_4k_disk = on_disk_buffer; uint32_t hot_extent[AL_EXTENTS_MAX]; int need_to_update_md_flags = 0; int re_initialize_anyways = 0; int err; if (argc > 0) fprintf(stderr, "Ignoring additional arguments\n"); if (format_version(cfg) < DRBD_V07) { fprintf(stderr, "apply-al only implemented for DRBD >= 0.7\n"); return -1; } err = cfg->ops->open(cfg); if (err == VALID_MD_FOUND_AT_LAST_KNOWN_LOCATION) { if (v08_move_internal_md_after_resize(cfg) == 0) err = cfg->ops->open(cfg); } if (err != VALID_MD_FOUND) { fprintf(stderr, "No valid meta data found\n"); return -1; } al_size = cfg->md.al_stripes * cfg->md.al_stripe_size_4k * 4096; /* read in first chunk (which is actually the whole AL * for old fixed size 32k activity log */ pread_or_die(cfg, on_disk_buffer, al_size < buffer_size ? al_size : buffer_size, cfg->al_offset, "apply_al"); /* init all extent numbers to -1U aka "unused" */ memset(hot_extent, 0xff, sizeof(hot_extent)); /* replay al */ if (is_v07(cfg)) err = replay_al_07(cfg, hot_extent); /* FIXME * we should introduce a new meta data "super block" magic, so we won't * have the same super block with two different activity log * transaction layouts */ else if (DRBD_MD_MAGIC_84_UNCLEAN == cfg->md.magic || DRBD_MD_MAGIC_09 == cfg->md.magic || DRBD_AL_MAGIC == be32_to_cpu(al_4k_disk[0].magic.be) || DRBD_AL_MAGIC == be32_to_cpu(al_4k_disk[1].magic.be) || cfg->md.al_stripes != 1 || cfg->md.al_stripe_size_4k != 8) { err = replay_al_84(cfg, hot_extent); } else { /* try the old al format anyways, this may be the first time we * run after upgrading from < 8.4 to 8.4, and we need to * transparently "convert" the activity log format. */ err = replay_al_07(cfg, hot_extent); re_initialize_anyways = 1; } if (err < 0) { /* ENODATA: * most likely this is an uninitialized, * or at least non-8.4-style activity log. * Cannot do anything about that. * * EINVAL: * Some valid 8.4 style INITIALIZED transactions found, * but others have been corrupt, and no single "usable" * update transaction was found. * FIXME: what to do about that? * We probably need some "FORCE" mode as well. */ if (need_to_apply_al(cfg)) { /* 1, 2, 10, 20? FIXME sane exit codes! */ if (err == -ENODATA) return 1; return 2; } else if (is_v08(cfg) || is_v09(cfg)) { fprintf(stderr, "Error ignored, no need to apply the AL\n"); re_initialize_anyways = 1; } } /* do we need to actually apply it? */ if (err > 0 && need_to_apply_al(cfg)) { /* process hot extents in order, to reduce disk seeks. */ qsort(hot_extent, ARRAY_SIZE(hot_extent), sizeof(hot_extent[0]), cmp_u32); apply_al(cfg, hot_extent); need_to_update_md_flags = 1; } /* (Re-)initialize the activity log. * This is needed on 8.4, and does not hurt on < 8.4. * It may cause a "No usable activity log found" kernel message * if it is attached to < 8.4, but that is cosmetic. * We can skip this, if it was clean anyways (err == 0), * or if we know that this is for 0.7. */ if (re_initialize_anyways || (err > 0 && !is_v07(cfg))) initialize_al(cfg); if (format_version(cfg) >= DRBD_V08 && ((cfg->md.flags & MDF_AL_CLEAN) == 0 || cfg->md.magic != DRBD_MD_MAGIC_08)) need_to_update_md_flags = 1; err = 0; if (need_to_update_md_flags) { /* Must not touch MDF_PRIMARY_IND. * This flag is used in-kernel to determine which * "wait-for-connection-timeout" is to be used. * Maybe it is time to reconsider the concept or * current implementation of "degr-wfc-timeout". * RFC: * If we set MDF_CRASHED_PRIMARY, in case MDF_PRIMARY_IND * was set, and clear MDF_PRIMARY_IND here, we can then * USE_DEGR_WFC_T as long as MDF_CRASHED_PRIMARY is set. * Maybe that even results in better semantics. */ if (format_version(cfg) >= DRBD_V08) cfg->md.flags |= MDF_AL_CLEAN; if (is_v08(cfg)) cfg->md.magic = DRBD_MD_MAGIC_08; err = cfg->ops->md_cpu_to_disk(cfg); err = cfg->ops->close(cfg) || err; if (err) fprintf(stderr, "update of super block flags failed\n"); } return err; } unsigned long bm_bytes(const struct md_cpu const *md, uint64_t sectors) { unsigned long long bm_bits; unsigned long sectors_per_bit = md->bm_bytes_per_bit >> 9; /* we announced 1 PiB as "supported" iirc. */ ASSERT(sectors <= (1ULL << (50-9))); /* sectors_per_bit == 0 would trigger a division by zero. * At some point we will want to store sectors_per_bit directly, * and not bytes_per_bit. * To keep sanity, we limit ourselves to tracking only power-of-two * multiples of 4k */ ASSERT(md->bm_bytes_per_bit >= 4096); ASSERT((md->bm_bytes_per_bit & (md->bm_bytes_per_bit - 1)) == 0); /* round up storage sectors to full "bitmap sectors per bit", then * convert to number of bits needed, and round that up to 64bit words * to ease interoperability between 32bit and 64bit architectures. */ bm_bits = (sectors + sectors_per_bit -1)/sectors_per_bit; bm_bits = ALIGN(bm_bits, 64); /* convert to bytes, multiply by number of peers, * and, because we do all our meta data IO in 4k blocks, * round up to full 4k */ return ALIGN(bm_bits / 8 * md->max_peers, 4096); } static void fprintf_bm_eol(FILE *f, unsigned int i, int peer_nr, const char* indent) { if ((i & 63) == peer_nr) fprintf(f, "\n%s # at %llukB\n%s ", indent, (128LLU * (i - peer_nr)), indent); else fprintf(f, "\n%s ", indent); } static unsigned int round_down(unsigned int i, unsigned int g) { return i / g * g; /* return i - i % g; */ } /* le_u64, because we want to be able to hexdump it reliably * regardless of sizeof(long) */ static void fprintf_bm(FILE *f, struct format *cfg, int peer_nr, const char* indent) { const int WPL = 8; off_t bm_on_disk_off = cfg->bm_offset; le_u32 const *bm = on_disk_buffer; le_u32 cw; /* current word for rl encoding */ le_u32 lw = {0}; /* low word for 64 bit output */ const unsigned int n = cfg->bm_bytes/sizeof(*bm); unsigned int max_peers = cfg->md.max_peers; unsigned int count = 0; unsigned int bits_set = 0; unsigned int n_buffer = 0; unsigned int r; /* real offset */ unsigned int i; /* in-buffer offset */ unsigned int j; /* * The code below is a bit "funky" (ugly, unreadable, not only) with * the modulos, and implicit offset modulo continuation on buffer wrap. * To work, it requires the "chunk size" that is read-in per iteration * to be a multiple of max_peer_size * 8 bytes, or it will be seriously * confused on buffer wrap. * IO-size should be a multiple of 4k anyways (because of O_DIRECT), * align on 4k * max_peers seems to be an easy enough fix for said confusion. * If you change buffer_size, double check this hackish reasoning as well. */ const size_t max_chunk_size = round_down(buffer_size, 4096 * max_peers); ASSERT(buffer_size >= DRBD_PEERS_MAX * 4096); ASSERT(max_chunk_size); i = peer_nr; r = peer_nr; cw.le = 0; /* silence compiler warning */ fprintf(f, "{"); if (r < n) goto start; while (r < n) { /* need to read on first iteration, * and on buffer wrap */ if (i * sizeof(*bm) >= max_chunk_size) { size_t chunk; i -= max_chunk_size / sizeof(*bm); start: chunk = ALIGN((n - round_down(r, max_peers)) * sizeof(*bm), cfg->md_hard_sect_size); if (chunk > max_chunk_size) chunk = max_chunk_size; ASSERT(chunk); pread_or_die(cfg, on_disk_buffer, chunk, bm_on_disk_off, "fprintf_bm"); bm_on_disk_off += chunk; n_buffer = chunk / sizeof(*bm); } next: ASSERT(i < n_buffer); if (count == 0) cw = bm[i]; if (i % (WPL * max_peers) == peer_nr) { if (!count) fprintf_bm_eol(f, r, peer_nr, indent); /* j = i, because it may be continuation after buffer wrap */ for (j = i; j < n_buffer && cw.le == bm[j].le; j += max_peers) ; unsigned int tmp = round_down(j / max_peers - i / max_peers, WPL); if (tmp > WPL) { count += tmp; r += tmp * max_peers; i += tmp * max_peers; if (j >= n_buffer && r < n) continue; } if (count) { fprintf(f, " %u times 0x%08X%08X;", count / 2, le32_to_cpu(cw.le), le32_to_cpu(cw.le)); bits_set += count * generic_hweight32(cw.le); count = 0; if (r >= n) break; /* don't "continue;", we may have not advanced i after buffer wrap, * so that would be treated as an other buffer wrap */ goto next; } } ASSERT(i < n_buffer); if (((i / max_peers) & 1) == 0) lw = bm[i]; else fprintf(f, " 0x%08X%08X;", le32_to_cpu(bm[i].le), le32_to_cpu(lw.le)); bits_set += generic_hweight32(bm[i].le); r += max_peers; i += max_peers; } fprintf(f, "\n%s}\n", indent); cfg->bits_set = bits_set; } void printf_bm(struct format *cfg) { int i; switch (format_version(cfg)) { case DRBD_V06: return; case DRBD_V07: case DRBD_V08: printf("bm "); fprintf_bm(stdout, cfg, 0, ""); break; case DRBD_V09: for (i = 0; i < cfg->md.max_peers; i++) { printf("bitmap[%d] ", i); fprintf_bm(stdout, cfg, i, ""); } break; case DRBD_UNKNOWN: fprintf(stderr, "BUG in %s().\n", __FUNCTION__); } } static void clip_effective_size_and_bm_bytes(struct format *cfg) { if (cfg->md.effective_size > cfg->max_usable_sect) { printf("# la-size-sect was too big (%llu), truncated (%llu)!\n", (unsigned long long)cfg->md.effective_size, (unsigned long long)cfg->max_usable_sect); cfg->md.effective_size = cfg->max_usable_sect; } cfg->bm_bytes = bm_bytes(&cfg->md, cfg->md.effective_size); } int v07_style_md_open(struct format *cfg) { struct stat sb; unsigned int hard_sect_size = 0; int ioctl_err; int open_flags = O_RDWR | O_DIRECT; /* For old-style fixed size indexed external meta data, * we cannot really use O_EXCL, we have to trust the given minor. * * For internal, or "flexible" external meta data, we open O_EXCL to * avoid accidentally damaging otherwise in-use data, just because * someone had a typo in the command line. */ if (cfg->md_index < 0) open_flags |= O_EXCL; retry: cfg->md_fd = open(cfg->md_device_name, open_flags ); if (cfg->md_fd == -1) { int save_errno = errno; PERROR("open(%s) failed", cfg->md_device_name); if (save_errno == EBUSY && (open_flags & O_EXCL)) { if ((!force && command->function == &meta_apply_al) || !confirmed("Exclusive open failed. Do it anyways?")) { printf("Operation canceled.\n"); exit(20); } open_flags &= ~O_EXCL; goto retry; } if (save_errno == EINVAL && (open_flags & O_DIRECT)) { /* shoo. O_DIRECT is not supported? * retry, but remember this, so we can * BLKFLSBUF appropriately */ fprintf(stderr, "could not open with O_DIRECT, retrying without\n"); open_flags &= ~O_DIRECT; opened_odirect = 0; goto retry; } exit(20); } if (fstat(cfg->md_fd, &sb)) { PERROR("fstat(%s) failed", cfg->md_device_name); exit(20); } if (!S_ISBLK(sb.st_mode)) { if (!force) { fprintf(stderr, "'%s' is not a block device!\n", cfg->md_device_name); exit(20); } cfg->bd_size = sb.st_size; } if (format_version(cfg) >= DRBD_V08) { ASSERT(cfg->md_index != DRBD_MD_INDEX_INTERNAL); } ioctl_err = ioctl(cfg->md_fd, BLKSSZGET, &hard_sect_size); if (ioctl_err) { fprintf(stderr, "ioctl(md_fd, BLKSSZGET) returned %d, " "assuming hard_sect_size is 512 Byte\n", ioctl_err); cfg->md_hard_sect_size = 512; } else { cfg->md_hard_sect_size = hard_sect_size; if (verbose >= 2) fprintf(stderr, "hard_sect_size is %d Byte\n", cfg->md_hard_sect_size); } if (!cfg->bd_size) cfg->bd_size = bdev_size(cfg->md_fd); /* check_for_existing_data() wants to read that much, * so having less than that doesn't make sense. * It's only 68kB anyway! */ if (cfg->bd_size < SO_MUCH) { fprintf(stderr, "%s is only %llu bytes. That's not enough.\n", cfg->md_device_name, (long long unsigned)cfg->bd_size); exit(10); } cfg->md_offset = v07_style_md_get_byte_offset(cfg->md_index, cfg->bd_size); if (cfg->md_offset > cfg->bd_size - 4096) { fprintf(stderr, "Device too small: expecting meta data block at\n" "byte offset %lld, but %s is only %llu bytes.\n", (signed long long)cfg->md_offset, cfg->md_device_name, (long long unsigned)cfg->bd_size); exit(10); } if (!opened_odirect && (MAJOR(sb.st_rdev) != RAMDISK_MAJOR)) { ioctl_err = ioctl(cfg->md_fd, BLKFLSBUF); /* report error, but otherwise ignore. we could not open * O_DIRECT, it is a "strange" device anyways. */ if (ioctl_err) fprintf(stderr, "ioctl(md_fd, BLKFLSBUF) returned %d, " "we may read stale data\n", ioctl_err); } if (cfg->ops->md_disk_to_cpu(cfg)) { /* no valid meta data found. but we want to initialize * al_offset and bm_offset anyways, so check_for_existing_data * has something to work with. */ return NO_VALID_MD_FOUND; } cfg->al_offset = cfg->md_offset + cfg->md.al_offset * 512LL; cfg->bm_offset = cfg->md_offset + cfg->md.bm_offset * 512LL; cfg->max_usable_sect = max_usable_sectors(cfg); clip_effective_size_and_bm_bytes(cfg); cfg->bits_set = -1U; /* FIXME paranoia verify that unused bits and words are unset... */ /* FIXME paranoia verify that unused bits and words are unset... */ return VALID_MD_FOUND; } int v07_md_disk_to_cpu(struct format *cfg) { struct md_cpu md; int ok; PREAD(cfg, on_disk_buffer, sizeof(struct md_on_disk_07), cfg->md_offset); md_disk_07_to_cpu(&md, (struct md_on_disk_07*)on_disk_buffer); ok = is_valid_md(DRBD_V07, &md, cfg->md_index, cfg->bd_size); if (ok) cfg->md = md; return ok ? 0 : -1; } int v07_md_cpu_to_disk(struct format *cfg) { if (!is_valid_md(DRBD_V07, &cfg->md, cfg->md_index, cfg->bd_size)) return -1; md_cpu_to_disk_07(on_disk_buffer, &cfg->md); PWRITE(cfg, on_disk_buffer, sizeof(struct md_on_disk_07), cfg->md_offset); return 0; } int v07_parse(struct format *cfg, char **argv, int argc, int *ai) { long index; char *e; if (argc < 2) { fprintf(stderr, "Too few arguments for format\n"); return -1; } cfg->md_device_name = strdup(argv[0]); if (!strcmp(argv[1],"internal")) { index = is_v07(cfg) ? DRBD_MD_INDEX_INTERNAL : DRBD_MD_INDEX_FLEX_INT; } else if (!strcmp(argv[1],"flex-external")) { index = DRBD_MD_INDEX_FLEX_EXT; } else if (!strcmp(argv[1],"flex-internal")) { index = DRBD_MD_INDEX_FLEX_INT; } else { e = argv[1]; errno = 0; index = strtol(argv[1], &e, 0); if (*e != 0 || 0 > index || index > 255 || errno != 0) { fprintf(stderr, "'%s' is not a valid index number.\n", argv[1]); return -1; } } cfg->md_index = index; *ai += 2; return 0; } int v07_md_initialize(struct format *cfg, int do_disk_writes, int max_peers __attribute((unused))) { memset(&cfg->md, 0, sizeof(cfg->md)); cfg->md.effective_size = 0; cfg->md.gc[Flags] = 0; cfg->md.gc[HumanCnt] = 1; /* THINK 0? 1? */ cfg->md.gc[TimeoutCnt] = 1; cfg->md.gc[ConnectedCnt] = 1; cfg->md.gc[ArbitraryCnt] = 1; cfg->md.max_peers = 1; cfg->md.magic = DRBD_MD_MAGIC_07; /* No striping in v07! * But some parts of the common code expect these members to be properly initialized. */ cfg->md.al_stripes = 1; cfg->md.al_stripe_size_4k = 8; return md_initialize_common(cfg, do_disk_writes); } /****************************************** }}} end of v07 ******************************************/ /****************************************** begin of v08 {{{ ******************************************/ /* if this returns with something != 0 in cfg->lk_bd.bd_size, * caller knows he must move the meta data to actually find it. */ void v08_check_for_resize(struct format *cfg) { struct md_cpu md_test; off_t flex_offset; int found = 0; /* you should not call me if you already found something. */ ASSERT(cfg->md.magic == 0); /* check for resized lower level device ... only check for drbd 8 */ if (format_version(cfg) < DRBD_V08) return; if (cfg->md_index != DRBD_MD_INDEX_FLEX_INT) return; /* Do we know anything? Maybe it never was stored. */ if (lk_bdev_load(cfg->minor, &cfg->lk_bd)) { if (verbose) fprintf(stderr, "no last-known offset information available.\n"); return; } if (verbose) { fprintf(stderr, " last known info: %llu %s\n", (unsigned long long)cfg->lk_bd.bd_size, cfg->lk_bd.bd_name ?: "-unknown device name-"); if (cfg->lk_bd.bd_uuid) fprintf(stderr, " last known uuid: "X64(016)"\n", cfg->lk_bd.bd_uuid); } /* I just checked that offset, nothing to see there. */ if (cfg->lk_bd.bd_size == cfg->bd_size) return; flex_offset = v07_style_md_get_byte_offset( DRBD_MD_INDEX_FLEX_INT, cfg->lk_bd.bd_size); /* actually check that offset, if it is accessible. */ /* If someone shrunk that device, I won't be able to read it! */ if (flex_offset < cfg->bd_size) { PREAD(cfg, on_disk_buffer, 4096, flex_offset); if (is_v08(cfg)) { md_disk_08_to_cpu(&md_test, (struct md_on_disk_08*)on_disk_buffer); found = is_valid_md(DRBD_V08, &md_test, DRBD_MD_INDEX_FLEX_INT, cfg->lk_bd.bd_size); } else if (is_v09(cfg)) { md_disk_09_to_cpu(&md_test, (struct meta_data_on_disk_9*)on_disk_buffer); found = is_valid_md(DRBD_V09, &md_test, DRBD_MD_INDEX_FLEX_INT, cfg->lk_bd.bd_size); } } if (verbose) { fprintf(stderr, "While checking for internal meta data for drbd%u on %s,\n" "it appears that it may have been relocated.\n" "It used to be ", cfg->minor, cfg->md_device_name); if (cfg->lk_bd.bd_name && strcmp(cfg->lk_bd.bd_name, cfg->md_device_name)) { fprintf(stderr, "on %s ", cfg->lk_bd.bd_name); } fprintf(stderr, "at byte offset %llu", (unsigned long long)flex_offset); if (!found) { fprintf(stderr, ", but I cannot find it now.\n"); if (flex_offset >= cfg->bd_size) fprintf(stderr, "Device is too small now!\n"); } else fprintf(stderr, ", and seems to still be valid.\n"); } if (found) { if (cfg->lk_bd.bd_uuid && md_test.device_uuid != cfg->lk_bd.bd_uuid) { fprintf(stderr, "Last known and found uuid differ!?\n" X64(016)" != "X64(016)"\n", cfg->lk_bd.bd_uuid, cfg->md.device_uuid); if (!force) { found = 0; fprintf(stderr, "You may --force me to ignore that.\n"); } else fprintf(stderr, "You --force'ed me to ignore that.\n"); } } if (found) cfg->md = md_test; return; } int v08_md_open(struct format *cfg) { int r = v07_style_md_open(cfg); if (r == VALID_MD_FOUND) return r; v08_check_for_resize(cfg); if (!cfg->lk_bd.bd_size || !cfg->md.magic) return NO_VALID_MD_FOUND; else return VALID_MD_FOUND_AT_LAST_KNOWN_LOCATION; } int v08_md_disk_to_cpu(struct format *cfg) { struct md_cpu md; int ok; PREAD(cfg, on_disk_buffer, sizeof(struct md_on_disk_08), cfg->md_offset); md_disk_08_to_cpu(&md, (struct md_on_disk_08*)on_disk_buffer); ok = is_valid_md(DRBD_V08, &md, cfg->md_index, cfg->bd_size); if (ok) cfg->md = md; if (verbose >= 3 + !!ok && verbose <= 10) fprintf_hex(stderr, cfg->md_offset, on_disk_buffer, 4096); return ok ? 0 : -1; } int v08_md_cpu_to_disk(struct format *cfg) { if (!is_valid_md(DRBD_V08, &cfg->md, cfg->md_index, cfg->bd_size)) return -1; md_cpu_to_disk_08((struct md_on_disk_08 *)on_disk_buffer, &cfg->md); PWRITE(cfg, on_disk_buffer, sizeof(struct md_on_disk_08), cfg->md_offset); cfg->update_lk_bdev = 1; return 0; } int v08_md_initialize(struct format *cfg, int do_disk_writes, int max_peers __attribute((unused))) { size_t i; memset(&cfg->md, 0, sizeof(cfg->md)); cfg->md.effective_size = 0; cfg->md.current_uuid = UUID_JUST_CREATED; cfg->md.peers[0].bitmap_uuid = 0; for (i = 0; i < ARRAY_SIZE(cfg->md.history_uuids); i++) cfg->md.history_uuids[i] = 0; cfg->md.flags = MDF_AL_CLEAN; cfg->md.max_peers = 1; cfg->md.magic = DRBD_MD_MAGIC_08; cfg->md.al_stripes = option_al_stripes; cfg->md.al_stripe_size_4k = option_al_stripe_size_4k; return md_initialize_common(cfg, do_disk_writes); } int v08_md_close(struct format *cfg) { /* update last known info, if we changed anything, * or if explicitly requested. */ if (cfg->update_lk_bdev && !dry_run) { if (cfg->md_index != DRBD_MD_INDEX_FLEX_INT) lk_bdev_delete(cfg->minor); else { cfg->lk_bd.bd_size = cfg->bd_size; cfg->lk_bd.bd_uuid = cfg->md.device_uuid; cfg->lk_bd.bd_name = cfg->md_device_name; lk_bdev_save(cfg->minor, &cfg->lk_bd); } } return generic_md_close(cfg); } /****************************************** begin of v09 {{{ ******************************************/ int v09_md_disk_to_cpu(struct format *cfg) { struct md_cpu md; int ok; PREAD(cfg, on_disk_buffer, sizeof(struct meta_data_on_disk_9), cfg->md_offset); md_disk_09_to_cpu(&md, (struct meta_data_on_disk_9*)on_disk_buffer); ok = is_valid_md(DRBD_V09, &md, cfg->md_index, cfg->bd_size); if (ok) cfg->md = md; if (verbose >= 3 + !!ok && verbose <= 10) fprintf_hex(stderr, cfg->md_offset, on_disk_buffer, 4096); return ok ? 0 : -1; } int v09_md_cpu_to_disk(struct format *cfg) { if (!is_valid_md(DRBD_V09, &cfg->md, cfg->md_index, cfg->bd_size)) return -1; md_cpu_to_disk_09((struct meta_data_on_disk_9 *)on_disk_buffer, &cfg->md); PWRITE(cfg, on_disk_buffer, sizeof(struct meta_data_on_disk_9), cfg->md_offset); cfg->update_lk_bdev = 1; return 0; } int v09_md_initialize(struct format *cfg, int do_disk_writes, int max_peers) { int p, i; memset(&cfg->md, 0, sizeof(cfg->md)); cfg->md.effective_size = 0; cfg->md.max_peers = max_peers; cfg->md.flags = MDF_AL_CLEAN; cfg->md.node_id = -1; cfg->md.magic = DRBD_MD_MAGIC_09; cfg->md.al_stripes = option_al_stripes; cfg->md.al_stripe_size_4k = option_al_stripe_size_4k; cfg->md.current_uuid = UUID_JUST_CREATED; for (i = 0; i < ARRAY_SIZE(cfg->md.history_uuids); i++) cfg->md.history_uuids[i] = 0; for (p = 0; p < DRBD_NODE_ID_MAX; p++) { cfg->md.peers[p].bitmap_uuid = 0; cfg->md.peers[p].flags = 0; cfg->md.peers[p].bitmap_index = -1; } return md_initialize_common(cfg, do_disk_writes); } /****************************************** }}} end of v09 ******************************************/ int meta_get_gi(struct format *cfg, char **argv __attribute((unused)), int argc) { if (argc > 0) { fprintf(stderr, "Ignoring additional arguments\n"); } if (cfg->ops->open(cfg)) return -1; cfg->ops->get_gi(&cfg->md, option_node_id); return cfg->ops->close(cfg); } int meta_show_gi(struct format *cfg, char **argv __attribute((unused)), int argc) { char ppb[10]; if (argc > 0) { fprintf(stderr, "Ignoring additional arguments\n"); } if (cfg->ops->open(cfg)) return -1; // find the correct slot from node-id. cfg->ops->show_gi(&cfg->md, option_node_id); if (cfg->md.effective_size) { printf("last agreed size: %s (%llu sectors)\n", ppsize(ppb, cfg->md.effective_size >> 1), (unsigned long long)cfg->md.effective_size); printf("last agreed max bio size: %u Byte\n", cfg->md.la_peer_max_bio_size); #if 0 /* FIXME implement count_bits() */ printf("%u bits set in the bitmap [ %s out of sync ]\n", cfg->bits_set, ppsize(ppb, cfg->bits_set * 4)); #endif } else { printf("zero size device -- never seen peer yet?\n"); } return cfg->ops->close(cfg); } int meta_dstate(struct format *cfg, char **argv __attribute((unused)), int argc) { if (argc > 0) { fprintf(stderr, "Ignoring additional arguments\n"); } if (cfg->ops->open(cfg)) { fprintf(stderr, "No valid meta data found\n"); return -1; } if(cfg->md.flags & MDF_CONSISTENT) { if(cfg->md.flags & MDF_WAS_UP_TO_DATE) { if (cfg->md.flags & MDF_PEER_OUT_DATED) printf("UpToDate\n"); else printf("Consistent\n"); } else { printf("Outdated\n"); } } else { printf("Inconsistent\n"); } return cfg->ops->close(cfg); } int meta_set_gi(struct format *cfg, char **argv, int argc) { struct md_cpu tmp; int err; if (argc > 1) { fprintf(stderr, "Ignoring additional arguments\n"); } if (argc < 1) { fprintf(stderr, "Required Argument missing\n"); exit(10); } if (cfg->ops->open(cfg)) return -1; tmp = cfg->md; cfg->ops->set_gi(&tmp, option_node_id, argv, argc); printf("previously "); cfg->ops->get_gi(&cfg->md, option_node_id); printf("set GI to "); cfg->ops->get_gi(&tmp, option_node_id); if (!confirmed("Write new GI to disk?")) { printf("Operation canceled.\n"); exit(0); } cfg->md = tmp; err = cfg->ops->md_cpu_to_disk(cfg); err = cfg->ops->close(cfg) || err; if (err) fprintf(stderr, "update failed\n"); return err; } void print_dump_header() { char time_str[60]; time_t t = time(NULL); int i; strftime(time_str, sizeof(time_str), "%F %T %z [%s]", localtime(&t)); printf("# DRBD meta data dump\n# %s\n# %s>", time_str, get_hostname()); for (i=0; i < global_argc; i++) printf(" %s",global_argv[i]); printf("\n#\n\n"); } int meta_dump_md(struct format *cfg, char **argv __attribute((unused)), int argc) { int al_is_clean; int i; if (argc > 0) { fprintf(stderr, "Ignoring additional arguments\n"); } i = cfg->ops->open(cfg); if (i == NO_VALID_MD_FOUND) { fprintf(stderr, "No valid meta data found\n"); return -1; } al_is_clean = DRBD_MD_MAGIC_84_UNCLEAN != cfg->md.magic && (cfg->md.flags & MDF_AL_CLEAN) != 0; if (!al_is_clean) { fprintf(stderr, "Found meta data is \"unclean\", please apply-al first\n"); if (!force) return -1; } print_dump_header(); printf("version \"%s\";\n\n", cfg->ops->name); if (!al_is_clean) /* So we have been forced. Still cause a parse error for restore-md. */ printf("This_is_an_unclean_meta_data_dump._Don't_trust_the_bitmap.\n" "# You should \"apply-al\" first, if you plan to restore this.\n\n"); if (format_version(cfg) >= DRBD_V09) printf("max-peers %d;\n", cfg->md.max_peers); printf("# md_size_sect %llu\n", (long long unsigned)cfg->md.md_size_sect); if (i == VALID_MD_FOUND_AT_LAST_KNOWN_LOCATION) { printf("#\n" "### Device seems to have been resized!\n" "### dumping meta data from the last known position\n" "### current size of %s: %llu byte\n" "### expected position of meta data:\n", cfg->md_device_name, (unsigned long long)cfg->bd_size); printf("## md_offset %llu\n", (long long unsigned)cfg->md_offset); printf("## al_offset %llu\n", (long long unsigned)cfg->al_offset); printf("## bm_offset %llu\n", (long long unsigned)cfg->bm_offset); printf( "### last known size of %s: %llu byte\n" "### adjusted position of meta data:\n", cfg->lk_bd.bd_name ?: "-?-", (unsigned long long)cfg->lk_bd.bd_size); cfg->md_offset = v07_style_md_get_byte_offset( DRBD_MD_INDEX_FLEX_INT, cfg->lk_bd.bd_size); cfg->al_offset = cfg->md_offset + cfg->md.al_offset * 512LL; cfg->bm_offset = cfg->md_offset + cfg->md.bm_offset * 512LL; cfg->bm_bytes = bm_bytes(&cfg->md, cfg->md.effective_size); } printf("# md_offset %llu\n", (long long unsigned)cfg->md_offset); printf("# al_offset %llu\n", (long long unsigned)cfg->al_offset); printf("# bm_offset %llu\n", (long long unsigned)cfg->bm_offset); printf("\n"); switch (format_version(cfg)) { case DRBD_V06: case DRBD_V07: printf("gc {\n "); for (i = 0; i < GEN_CNT_SIZE; i++) { printf(" %d;", cfg->md.gc[i]); } printf("\n}\n"); break; case DRBD_V08: printf("uuid {\n"); printf(" 0x"X64(016)"; 0x"X64(016)"; 0x"X64(016)"; 0x"X64(016)";\n", cfg->md.current_uuid, cfg->md.peers[0].bitmap_uuid, cfg->md.history_uuids[0], cfg->md.history_uuids[1]); printf(" flags 0x"X32(08)";\n", cfg->md.peers[0].flags); printf("}\n"); break; case DRBD_V09: printf("node-id %d;\n" "current-uuid 0x"X64(016)";\n" "flags 0x"X32(08)";\n", cfg->md.node_id, cfg->md.current_uuid, cfg->md.flags); for (i = 0; i < DRBD_NODE_ID_MAX; i++) { struct peer_md_cpu *peer = &cfg->md.peers[i]; printf("peer[%d] {\n", i); if (format_version(cfg) >= DRBD_V09) { printf(" bitmap-index %d;\n", peer->bitmap_index); } printf(" bitmap-uuid 0x"X64(016)";\n" " bitmap-dagtag 0x"X64(016)";\n" " flags 0x"X32(08)";\n", peer->bitmap_uuid, peer->bitmap_dagtag, peer->flags); printf("}\n"); } printf("history-uuids {"); for (i = 0; i < ARRAY_SIZE(cfg->md.history_uuids); i++) printf("%s0x"X64(016)";", i % 4 ? " " : "\n ", cfg->md.history_uuids[i]); printf("\n}\n"); break; case DRBD_UNKNOWN: fprintf(stderr, "BUG in %s().\n", __FUNCTION__); } if (format_version(cfg) >= DRBD_V07) { printf("# al-extents %u;\n", cfg->md.al_nr_extents); printf("la-size-sect "U64";\n", cfg->md.effective_size); if (format_version(cfg) >= DRBD_V08) { printf("bm-byte-per-bit "U32";\n", cfg->md.bm_bytes_per_bit); printf("device-uuid 0x"X64(016)";\n", cfg->md.device_uuid); printf("la-peer-max-bio-size %d;\n", cfg->md.la_peer_max_bio_size); printf("al-stripes "U32";\n", cfg->md.al_stripes); printf("al-stripe-size-4k "U32";\n", cfg->md.al_stripe_size_4k); } printf("# bm-bytes %u;\n", cfg->bm_bytes); printf_bm(cfg); /* pretty prints the whole bitmap */ printf("# bits-set %u;\n", cfg->bits_set); /* This is half assed, still. Hide it. */ if (verbose >= 10) printf_al(cfg); } return cfg->ops->close(cfg); } void md_parse_error(int expected_token, int seen_token,const char *etext) { if (!etext) { switch(expected_token) { /* leading space indicates to strip off "expected" below */ default : etext = " invalid/unexpected token!"; break; case 0 : etext = "end of file"; break; case ';': etext = "semicolon (;)"; break; case '{': etext = "opening brace ({)"; break; case '}': etext = "closing brace (})"; break; case '[': etext = "opening bracket ([)"; break; case ']': etext = "closing bracket (])"; break; case TK_BM: etext = "keyword 'bm'"; break; case TK_BITMAP: etext = "keyword 'bitmap'"; break; case TK_BM_BYTE_PER_BIT: etext = "keyword 'bm-byte-per-bit'"; break; case TK_DEVICE_UUID: etext = "keyword 'device-uuid'"; break; case TK_FLAGS: etext = "keyword 'flags'"; break; case TK_GC: etext = "keyword 'gc'"; break; case TK_LA_SIZE: etext = "keyword 'la-size-sect'"; break; case TK_TIMES: etext = "keyword 'times'"; break; case TK_UUID: etext = "keyword 'uuid'"; break; case TK_VERSION: etext = "keyword 'version'"; break; case TK_NODE_ID: etext = "keyword 'node-id'"; break; case TK_CURRENT_UUID: etext = "keyword 'current-uuid'"; break; case TK_BITMAP_UUID: etext = "keyword 'bitmap-uuid'"; break; case TK_BITMAP_DAGTAG: etext = "keyword 'bitmap-dagtag'"; break; case TK_PEER: etext = "keyword 'peer'"; break; case TK_HASH: etext = "keyword 'hash'"; break; case TK_MAX_PEERS: etext = "keyword 'max-peers'"; break; case TK_NUM: etext = "number ([0-9], up to 20 digits)"; break; case TK_STRING: etext = "short quoted string " "(\"..up to 20 characters, no newline..\")"; break; case TK_U32: etext = "an 8-digit hex number"; break; case TK_U64: etext = "a 16-digit hex number"; break; } } fflush(stdout); fprintf(stderr,"Parse error in line %u: %s%s", yylineno, etext, (etext[0] == ' ' ? ":" : " expected") ); switch(seen_token) { case 0: fprintf(stderr, ", but end of file encountered\n"); break; case 1 ... 58: /* ord(';') == 58 */ case 60 ... 122: /* ord('{') == 123 */ case 124: /* ord('}') == 125 */ case 126 ... 257: /* oopsie. these should never be returned! */ fprintf(stderr, "; got token value %u (this should never happen!)\n", seen_token); break; break; case TK_INVALID_CHAR: fprintf(stderr,"; got invalid input character '\\x%02x' [%c]\n", (unsigned char)yylval.txt[0], yylval.txt[0]); break; case ';': case '{': case '}': fprintf(stderr, ", not '%c'\n", seen_token); break; case TK_NUM: case TK_U32: case TK_U64: fprintf(stderr, ", not some number\n"); break; case TK_INVALID: /* already reported by scanner */ fprintf(stderr,"\n"); break; default: fprintf(stderr, ", not '%s'\n", yylval.txt); } exit(10); } static void EXP(int expected_token) { int tok = yylex(); if (tok != expected_token) md_parse_error(expected_token, tok, NULL); } static int assign_32_of_64bit(int i, uint64_t value, int max_peers) { le_u32 *bm = on_disk_buffer; if (i >= buffer_size / sizeof(*bm)) return i; // Do no advance i after leaving the window if (i >= 0) { // only assign data, while within the window if (((i / max_peers) & 1) == 0) bm[i].le = cpu_to_le32((uint32_t) value); // little endian low word => lower address else bm[i].le = cpu_to_le32((uint32_t) (value >> 32)); } return i + max_peers; } int parse_bitmap_window_one_peer(struct format *cfg, int window, int peer_nr, int parse_only) { unsigned int max_peers = cfg->md.max_peers; le_u32 *bm = on_disk_buffer; uint64_t value; int i, times; i = peer_nr - window * (buffer_size / sizeof(*bm)); if (format_version(cfg) < DRBD_V09) EXP(TK_BM); else { EXP(TK_BITMAP); EXP('['); EXP(TK_NUM); EXP(']'); if (yylval.u64 != peer_nr) { fprintf(stderr, "Parse error in line %u: " "Expected peer slot %d but found %d\n", yylineno, i, (int)yylval.u64); exit(10); } } EXP('{'); while(1) { int tok = yylex(); switch(tok) { case TK_U64: EXP(';'); /* NOTE: * even though this EXP(';'); already advanced * to the next token, yylval will *not* be updated * for * ';', so it is still valid. * * This seemed to be the least ugly way to implement a * "parse_only" functionality without ugly if-branches * or the maintenance nightmare of code duplication */ if (parse_only) { i += max_peers * (sizeof(value) / sizeof(*bm)); break; } value = yylval.u64; i = assign_32_of_64bit(i, value, max_peers); i = assign_32_of_64bit(i, value, max_peers); break; case TK_NUM: times = yylval.u64; EXP(TK_TIMES); EXP(TK_U64); EXP(';'); if (parse_only) { i += times * max_peers * (sizeof(value) / sizeof(*bm)); break; } value = yylval.u64; while(times--) { i = assign_32_of_64bit(i, value, max_peers); i = assign_32_of_64bit(i, value, max_peers); } break; case '}': goto break_loop; default: md_parse_error(0 /* ignored, since etext is set */, tok, "repeat count, 16-digit hex number, or closing brace (})"); goto break_loop; } } break_loop: return i - peer_nr; } int parse_bitmap_window(struct format *cfg, int window, int parse_only) { int words = 0, i; if (format_version(cfg) < DRBD_V09) { return parse_bitmap_window_one_peer(cfg, window, 0, parse_only); } else /* >= DRBD_V09 */ { for (i = 0; i < cfg->md.max_peers; i++) { words = parse_bitmap_window_one_peer(cfg, window, i, parse_only); } } return words; } void parse_bitmap(struct format *cfg, int parse_only) { le_u32 *bm = on_disk_buffer; off_t bm_max_on_disk_off; long start_pos; int window = 0; int words; int truncated = 0; start_pos = ftell(yyin) - my_yy_unscaned_characters(); bm_max_on_disk_off = cfg->bm_offset + ALIGN(cfg->bm_bytes, 4096); do { fseek(yyin, start_pos, SEEK_SET); yyrestart(yyin); words = parse_bitmap_window(cfg, window, parse_only); if (words > 0 && !truncated) { size_t s = words * sizeof(*bm); size_t c; memset(bm + words, 0x00, buffer_size - s); /* need to sector-align this for O_DIRECT. to be * generic, maybe we even need to PAGE align it? */ s = ALIGN(s, cfg->md_hard_sect_size); if (parse_only) { c = bm_max_on_disk_off - (cfg->bm_offset + window * buffer_size); if (c > s) c = s; } else c = pwrite_with_limit_or_die(cfg, on_disk_buffer, s, cfg->bm_offset + window * buffer_size, bm_max_on_disk_off, "meta_restore_md"); if (s != c) { fprintf(stderr, "Bitmap info too large, truncated!\n"); /* If the bitmap info was truncated, there will * be garbage, still, and the EXP(0) below would * crap out. "Drain" that garbage here, * while still checking for parse errors. */ truncated = 1; } } window++; } while (words == buffer_size / sizeof(*bm)); } int verify_dumpfile_or_restore(struct format *cfg, char **argv, int argc, int parse_only) { int old_max_peers = -1; int new_max_peers = 1; int i; int err; char slots_seen[DRBD_NODE_ID_MAX] = { 0, }; int cur_slot; if (argc > 0) { yyin = fopen(argv[0],"r"); if(yyin == NULL) { fprintf(stderr, "open of '%s' failed.\n",argv[0]); exit(20); } } if (!parse_only) { if (cfg->ops->open(cfg) != NO_VALID_MD_FOUND) { old_max_peers = cfg->md.max_peers; if (!confirmed("Valid meta-data in place, overwrite?")) return -1; } else { ASSERT(!is_v06(cfg)); } } EXP(TK_VERSION); EXP(TK_STRING); if(strcmp(yylval.txt,cfg->ops->name)) { fprintf(stderr,"dump is '%s' you requested '%s'.\n", yylval.txt,cfg->ops->name); exit(10); } EXP(';'); if (is_v09(cfg)) { EXP(TK_MAX_PEERS); EXP(TK_NUM); EXP(';'); new_max_peers = yylval.u64; } cfg->ops->md_initialize(cfg, 0, new_max_peers); if (!parse_only) { fprintf(stderr, "reinitializing\n"); if (old_max_peers < new_max_peers && cfg->md_index != DRBD_MD_INDEX_FLEX_INT) { printf("Meta data needs more space now, since max_peers\n" "is bigger than in existing meta_data. (%d -> %d)\n", old_max_peers, new_max_peers); } check_for_existing_data(cfg); } if (format_version(cfg) < DRBD_V08) { EXP(TK_GC); EXP('{'); for (i = 0; i < GEN_CNT_SIZE; i++) { EXP(TK_NUM); EXP(';'); cfg->md.gc[i] = yylval.u64; } EXP('}'); } else { // >= 08 if (is_v08(cfg)) { EXP(TK_UUID); EXP('{'); EXP(TK_U64); EXP(';'); cfg->md.current_uuid = yylval.u64; EXP(TK_U64); EXP(';'); cfg->md.peers[0].bitmap_uuid = yylval.u64; for (i = 0; i < HISTORY_UUIDS_V08; i++) { EXP(TK_U64); EXP(';'); cfg->md.history_uuids[i] = yylval.u64; } EXP(TK_FLAGS); EXP(TK_U32); EXP(';'); cfg->md.flags = (uint32_t)yylval.u64; EXP('}'); } else /* >= 09 */ { EXP(TK_NODE_ID); EXP(TK_NUM); EXP(';'); cfg->md.node_id = yylval.u64; EXP(TK_CURRENT_UUID); EXP(TK_U64); EXP(';'); cfg->md.current_uuid = yylval.u64; EXP(TK_FLAGS); EXP(TK_U32); EXP(';'); cfg->md.flags = (uint32_t)yylval.u64; for (i = 0; i < DRBD_NODE_ID_MAX; i++) { EXP(TK_PEER); EXP('['); EXP(TK_NUM); EXP(']'); cur_slot = yylval.u64; if (cur_slot < 0 || cur_slot >= DRBD_NODE_ID_MAX) { fprintf(stderr, "Parse error in line %u: " "Slot %d out of range\n", yylineno, cur_slot); exit(10); } if (slots_seen[cur_slot]) { fprintf(stderr, "Parse error in line %u: " "Peer slot %d defined multiple times\n", yylineno, cur_slot); exit(10); } slots_seen[cur_slot] = 1; EXP('{'); EXP(TK_BITMAP_INDEX); EXP(TK_NUM); EXP(';'); cfg->md.peers[cur_slot].bitmap_index = yylval.u64; EXP(TK_BITMAP_UUID); EXP(TK_U64); EXP(';'); cfg->md.peers[cur_slot].bitmap_uuid = yylval.u64; EXP(TK_BITMAP_DAGTAG); EXP(TK_U64); EXP(';'); cfg->md.peers[cur_slot].bitmap_dagtag = yylval.u64; EXP(TK_FLAGS); EXP(TK_U32); EXP(';'); cfg->md.peers[cur_slot].flags = (uint32_t)yylval.u64; EXP('}'); } EXP(TK_HISTORY_UUIDS); EXP('{'); for (i = 0; i < ARRAY_SIZE(cfg->md.history_uuids); i++) { EXP(TK_U64); EXP(';'); cfg->md.history_uuids[i] = yylval.u64; } EXP('}'); } } EXP(TK_LA_SIZE); EXP(TK_NUM); EXP(';'); cfg->md.effective_size = yylval.u64; if (format_version(cfg) >= DRBD_V08) { EXP(TK_BM_BYTE_PER_BIT); EXP(TK_NUM); EXP(';'); cfg->md.bm_bytes_per_bit = yylval.u64; /* Check whether the value of bm_bytes_per_bit is * a power-of-two multiple of 4k. */ if (yylval.u64 < 4096 || (yylval.u64 & (yylval.u64 -1)) != 0) { fprintf(stderr, "Invalid value for bm-byte-per-bit: " "value must be a power-of-two multiple of 4096\n"); exit(10); } EXP(TK_DEVICE_UUID); EXP(TK_U64); EXP(';'); cfg->md.device_uuid = yylval.u64; EXP(TK_LA_BIO_SIZE); EXP(TK_NUM); EXP(';'); cfg->md.la_peer_max_bio_size = yylval.u64; EXP(TK_AL_STRIPES); EXP(TK_NUM); EXP(';'); cfg->md.al_stripes = yylval.u64; EXP(TK_AL_STRIPE_SIZE_4K); EXP(TK_NUM); EXP(';'); cfg->md.al_stripe_size_4k = yylval.u64; } else { cfg->md.bm_bytes_per_bit = DEFAULT_BM_BLOCK_SIZE; } if (option_al_stripes != cfg->md.al_stripes || option_al_stripe_size_4k != cfg->md.al_stripe_size_4k) { if (option_al_stripes_used) { fprintf(stderr, "override activity log striping from commandline\n"); cfg->md.al_stripes = option_al_stripes; cfg->md.al_stripe_size_4k = option_al_stripe_size_4k; } if (verbose >= 2) fprintf(stderr, "adjusting activity-log and bitmap offsets\n"); re_initialize_md_offsets(cfg); } clip_effective_size_and_bm_bytes(cfg); parse_bitmap(cfg, parse_only); /* there should be no trailing garbage in the input file */ EXP(0); if (parse_only) { printf("input file parsed ok\n"); return 0; } err = cfg->ops->md_cpu_to_disk(cfg); err = cfg->ops->close(cfg) || err; if (err) { fprintf(stderr, "Writing failed\n"); return -1; } printf("Successfully restored meta data\n"); return 0; } int meta_restore_md(struct format *cfg, char **argv, int argc) { return verify_dumpfile_or_restore(cfg,argv,argc,0); } int meta_verify_dump_file(struct format *cfg, char **argv, int argc) { return verify_dumpfile_or_restore(cfg,argv,argc,1); } void md_convert_07_to_08(struct format *cfg) { int i,j; /* * FIXME * what about the UI_BITMAP, and the Activity Log? * how to bring them over for internal meta data? * * maybe just refuse to convert anything that is not * "clean"? how to detect that? * * FIXME: if I am a crashed R_PRIMARY, or D_INCONSISTENT, * or Want-Full-Sync or the like, * refuse, and indicate how to solve this */ printf("Converting meta data...\n"); //if (!cfg->bits_counted) count_bits(cfg); /* FIXME: * if this is "internal" meta data, and I have bits set, * either move the bitmap into the newly expected place, * or refuse, and indicate how to solve this */ /* KB <-> sectors is done in the md disk<->cpu functions. * We only need to adjust the magic here. */ cfg->md.magic = DRBD_MD_MAGIC_08; // The MDF Flags are (nearly) the same in 07 and 08 cfg->md.flags = cfg->md.gc[Flags]; cfg->md.current_uuid = (uint64_t)(cfg->md.gc[HumanCnt] & 0xffff) << 48 | (uint64_t)(cfg->md.gc[TimeoutCnt] & 0xffff) << 32 | (uint64_t)((cfg->md.gc[ConnectedCnt]+cfg->md.gc[ArbitraryCnt]) & 0xffff) << 16 | (uint64_t)0xbabe; cfg->md.peers[0].bitmap_uuid = (uint64_t)0; for (i = cfg->bits_set ? UI_BITMAP : UI_HISTORY_START, j = 1; i <= UI_HISTORY_END ; i++, j++) { if (i == UI_BITMAP) cfg->md.peers[0].bitmap_uuid = cfg->md.current_uuid - j*0x10000; else cfg->md.history_uuids[i - UI_HISTORY_START] = cfg->md.current_uuid - j*0x10000; } /* unconditionally re-initialize offsets, * not necessary if fixed size external, * necessary if flex external or internal */ re_initialize_md_offsets(cfg); if (!is_valid_md(DRBD_V08, &cfg->md, cfg->md_index, cfg->bd_size)) { fprintf(stderr, "Conversion failed.\nThis is a bug :(\n"); exit(111); } } void md_convert_08_to_07(struct format *cfg) { /* * FIXME * what about the UI_BITMAP, and the Activity Log? * how to bring them over for internal meta data? * * maybe just refuse to convert anything that is not * "clean"? how to detect that? * * FIXME: if I am a crashed R_PRIMARY, or D_INCONSISTENT, * or Want-Full-Sync or the like, * refuse, and indicate how to solve this */ printf("Converting meta data...\n"); //if (!cfg->bits_counted) count_bits(cfg); /* FIXME: * if this is "internal" meta data, and I have bits set, * either move the bitmap into the newly expected place, * or refuse, and indicate how to solve this */ /* KB <-> sectors is done in the md disk<->cpu functions. * We only need to adjust the magic here. */ cfg->md.magic = DRBD_MD_MAGIC_07; /* FIXME somehow generate GCs in a sane way */ /* FIXME convert the flags? */ printf("Conversion v08 -> v07 is BROKEN!\n" "Be prepared to manually intervene!\n"); /* FIXME put some more helpful text here, indicating what exactly is to * be done to make this work as expected. */ /* unconditionally re-initialize offsets, * not necessary if fixed size external, * necessary if flex external or internal */ re_initialize_md_offsets(cfg); if (!is_valid_md(DRBD_V07, &cfg->md, cfg->md_index, cfg->bd_size)) { fprintf(stderr, "Conversion failed.\nThis is a bug :(\n"); exit(111); } } void md_convert_08_to_09(struct format *cfg) { int p; for (p = 0; p < DRBD_NODE_ID_MAX; p++) { cfg->md.peers[p].bitmap_uuid = 0; cfg->md.peers[p].flags = 0; cfg->md.peers[p].bitmap_index = -1; } if (cfg->md.flags & MDF_CONNECTED_IND) cfg->md.peers[0].flags |= MDF_PEER_CONNECTED; if (cfg->md.flags & MDF_FULL_SYNC) cfg->md.peers[0].flags |= MDF_PEER_FULL_SYNC; if (cfg->md.flags & MDF_PEER_OUT_DATED) cfg->md.peers[0].flags |= MDF_PEER_OUTDATED; cfg->md.flags &= ~(MDF_CONNECTED_IND | MDF_FULL_SYNC | MDF_PEER_OUT_DATED); cfg->md.magic = DRBD_MD_MAGIC_09; re_initialize_md_offsets(cfg); if (!is_valid_md(DRBD_V09, &cfg->md, cfg->md_index, cfg->bd_size)) { fprintf(stderr, "Conversion failed.\nThis is a bug :(\n"); exit(111); } } void md_convert_09_to_08(struct format *cfg) { if (cfg->md.peers[0].flags & MDF_PEER_CONNECTED) cfg->md.flags |= MDF_CONNECTED_IND; if (cfg->md.peers[0].flags & MDF_PEER_FULL_SYNC) cfg->md.flags |= MDF_FULL_SYNC; if (cfg->md.peers[0].flags & MDF_PEER_OUTDATED) cfg->md.flags |= MDF_PEER_OUT_DATED; cfg->md.magic = DRBD_MD_MAGIC_08; cfg->md.max_peers = 1; re_initialize_md_offsets(cfg); if (!is_valid_md(DRBD_V08, &cfg->md, cfg->md_index, cfg->bd_size)) { fprintf(stderr, "Conversion failed.\nThis is a bug :(\n"); exit(111); } } void convert_md(struct format *cfg, enum md_format from) { enum md_format to = format_version(cfg); switch(to) { default: case DRBD_UNKNOWN: case DRBD_V06: fprintf(stderr, "BUG in %s() %d.\n", __FUNCTION__, __LINE__); exit(10); case DRBD_V07: switch(from) { case DRBD_V09: md_convert_09_to_08(cfg); case DRBD_V08: md_convert_08_to_07(cfg); case DRBD_V07: break; case DRBD_V06: case DRBD_UNKNOWN: default: fprintf(stderr, "BUG in %s() %d.\n", __FUNCTION__, __LINE__); exit(10); } break; case DRBD_V08: switch(from) { default: case DRBD_UNKNOWN: case DRBD_V06: fprintf(stderr, "BUG in %s() %d.\n", __FUNCTION__, __LINE__); exit(10); case DRBD_V07: md_convert_07_to_08(cfg); case DRBD_V08: break; case DRBD_V09: md_convert_09_to_08(cfg); } break; case DRBD_V09: switch(from) { default: case DRBD_UNKNOWN: case DRBD_V06: fprintf(stderr, "BUG in %s() %d.\n", __FUNCTION__, __LINE__); exit(10); case DRBD_V07: md_convert_07_to_08(cfg); case DRBD_V08: md_convert_08_to_09(cfg); case DRBD_V09: ; } } } /* if on the physical device we find some data we can interpret, * print some informational message about what we found, * and what we think how much room it needs. * * look into /usr/share/misc/magic for inspiration * also consider e.g. xfsprogs/libdisk/fstype.c, * and of course the linux kernel headers... */ struct fstype_s { const char * type; unsigned long long bnum, bsize; }; int may_be_extX(const char *data, struct fstype_s *f) { unsigned int size; if (le16_to_cpu(*(uint16_t*)(data+0x438)) == 0xEF53) { if ( (le32_to_cpu(*(data+0x45c)) & 4) == 4 ) f->type = "ext3 filesystem"; else f->type = "ext2 filesystem"; f->bnum = le32_to_cpu(*(uint32_t*)(data+0x404)); size = le32_to_cpu(*(uint32_t*)(data+0x418)); f->bsize = size == 0 ? 1024 : size == 1 ? 2048 : size == 2 ? 4096 : 4096; /* DEFAULT */ return 1; } return 0; } int may_be_xfs(const char *data, struct fstype_s *f) { if (be32_to_cpu(*(uint32_t*)(data+0)) == 0x58465342) { f->type = "xfs filesystem"; f->bsize = be32_to_cpu(*(uint32_t*)(data+4)); f->bnum = be64_to_cpu(*(uint64_t*)(data+8)); return 1; } return 0; } int may_be_reiserfs(const char *data, struct fstype_s *f) { if (strncmp("ReIsErFs",data+0x10034,8) == 0 || strncmp("ReIsEr2Fs",data+0x10034,9) == 0) { f->type = "reiser filesystem"; f->bnum = le32_to_cpu(*(uint32_t*)(data+0x10000)); f->bsize = le16_to_cpu(*(uint16_t*)(data+0x1002c)); return 1; } return 0; } int may_be_jfs(const char *data, struct fstype_s *f) { if (strncmp("JFS1",data+0x8000,4) == 0) { f->type = "JFS filesystem"; f->bnum = le64_to_cpu(*(uint64_t*)(data+0x8008)); f->bsize = le32_to_cpu(*(uint32_t*)(data+0x8018)); return 1; } return 0; } /* really large block size, * will always refuse */ #define REFUSE_BSIZE 0xFFFFffffFFFF0000LLU #define ERR_BSIZE 0xFFFFffffFFFF0001LLU #define REFUSE_IT() do { f->bnum = 1; f->bsize = REFUSE_BSIZE; } while(0) #define REFUSE_IT_ERR() do { f->bnum = 1; f->bsize = ERR_BSIZE; } while(0) int may_be_swap(const char *data, struct fstype_s *f) { int looks_like_swap = strncmp(data+(1<<12)-10, "SWAP-SPACE", 10) == 0 || strncmp(data+(1<<12)-10, "SWAPSPACE2", 10) == 0 || strncmp(data+(1<<13)-10, "SWAP-SPACE", 10) == 0 || strncmp(data+(1<<13)-10, "SWAPSPACE2", 10) == 0; if (looks_like_swap) { f->type = "swap space signature"; REFUSE_IT(); return 1; } return 0; } #define N_ERR_LINES 4 #define MAX_ERR_LINE_LEN 1024 int guessed_size_from_pvs(struct fstype_s *f, char *dev_name) { char buf_in[200]; char *buf_err[N_ERR_LINES]; size_t c; unsigned long long bnum; int pipes[3][2]; int err_lines = 0; FILE *child_err = NULL; int i; int ret = 0; pid_t pid; buf_err[0] = calloc(N_ERR_LINES, MAX_ERR_LINE_LEN); if (!buf_err[0]) return 0; for (i = 1; i < N_ERR_LINES; i++) buf_err[i] = buf_err[i-1] + MAX_ERR_LINE_LEN; for (i = 0; i < 3; i++) { if (pipe(pipes[i])) goto out; } pid = fork(); if (pid < 0) goto out; setenv("dev_name", dev_name, 1); if (pid == 0) { /* child */ char *argv[] = { "sh", "-vxc", "pvs -vvv --noheadings --nosuffix --units s -o pv_size" " --config \"devices { write_cache_state=0 filter = [ 'a|$dev_name|', 'r|.|' ] }\"", NULL, }; close(pipes[0][1]); /* close unused pipe ends */ close(pipes[1][0]); close(pipes[2][0]); dup2(pipes[0][0],0); /* map to expected stdin/out/err */ dup2(pipes[1][1],1); dup2(pipes[2][1],2); close(0); /* we do not use stdin */ execvp(argv[0], argv); _exit(0); } /* parent */ close(pipes[0][0]); /* close unused pipe ends */ close(pipes[1][1]); close(pipes[2][1]); close(pipes[0][1]); /* we do not use stdin in child */ /* We use blocking IO on pipes. This could deadlock, * If the child process would do something unexpected. * We do know the behaviour of pvs, though, * and expect only a few bytes on stdout, * and quite a few debug messages on stderr. * * First drain stderr, keeping the last N_ERR_LINES, * then read stdout. */ child_err = fdopen(pipes[2][0], "r"); if (child_err) { char *b; do { err_lines = (err_lines + 1) % N_ERR_LINES; b = fgets(buf_err[err_lines], MAX_ERR_LINE_LEN, child_err); } while (b); } c = read(pipes[1][0], buf_in, sizeof(buf_in)-1); if (c > 0) { buf_in[c] = 0; if (1 == sscanf(buf_in, " %llu\n", &bnum)) { f->bnum = bnum; f->bsize = 512; ret = 1; } } if (!ret) { for (i = 0; i < N_ERR_LINES; i++) { char *b = buf_err[(err_lines + i) % N_ERR_LINES]; if (b[0] == 0) continue; fprintf(stderr, "pvs stderr:%s", b); } fprintf(stderr, "\n"); } i = 2; out: for ( ; i >= 0; i--) { close(pipes[i][0]); close(pipes[i][1]); } if (child_err) fclose(child_err); free(buf_err[0]); return ret; } int may_be_LVM(const char *data, struct fstype_s *f, char *dev_name) { if (strncmp("LVM2",data+0x218,4) == 0) { f->type = "LVM2 physical volume signature"; if (!guessed_size_from_pvs(f, dev_name)) REFUSE_IT_ERR(); return 1; } return 0; } /* XXX should all this output go to stderr? */ void check_for_existing_data(struct format *cfg) { struct fstype_s f; size_t i; uint64_t fs_kB; uint64_t max_usable_kB; PREAD(cfg, on_disk_buffer, SO_MUCH, 0); for (i = 0; i < SO_MUCH/sizeof(long); i++) { if (((long*)(on_disk_buffer))[i] != 0LU) break; } /* all zeros? no message */ if (i == SO_MUCH/sizeof(long)) return; f.type = "some data"; f.bnum = 0; f.bsize = 0; /* FIXME add more detection magic. * Or, rather, use some lib. */ (void)( may_be_swap (on_disk_buffer,&f) || may_be_LVM (on_disk_buffer,&f, cfg->md_device_name) || may_be_extX (on_disk_buffer,&f) || may_be_xfs (on_disk_buffer,&f) || may_be_jfs (on_disk_buffer,&f) || may_be_reiserfs (on_disk_buffer,&f) ); /* FIXME * some of the messages below only make sense for internal meta data. * for external meta data, we now only checked the meta-disk. * we should still check the actual lower level storage area for * existing data, too, and give appropriate warnings when it would * appear to be truncated by too small external meta data */ printf("md_offset %llu\n", (long long unsigned)cfg->md_offset); printf("al_offset %llu\n", (long long unsigned)cfg->al_offset); printf("bm_offset %llu\n", (long long unsigned)cfg->bm_offset); printf("\nFound %s\n", f.type); /* FIXME overflow check missing! * relevant for ln2(bsize) + ln2(bnum) >= 64, thus only for * device sizes of more than several exa byte. * seems irrelevant to me for now. */ fs_kB = ((f.bsize * f.bnum) + (1<<10)-1) >> 10; max_usable_kB = max_usable_sectors(cfg) >> 1; if (f.bnum) { if (cfg->md_index >= 0 || cfg->md_index == DRBD_MD_INDEX_FLEX_EXT) { printf("\nThis would corrupt existing data.\n"); if (ignore_sanity_checks) { printf("\nIgnoring sanity check on user request.\n\n"); return; } printf( "If you want me to do this, you need to zero out the first part\n" "of the device (destroy the content).\n" "You should be very sure that you mean it.\n" "Operation refused.\n\n"); exit(40); /* FIXME sane exit code! */ } if (f.bsize < REFUSE_BSIZE) printf("%12llu kB data area apparently used\n", (unsigned long long)fs_kB); printf("%12llu kB left usable by current configuration\n", (unsigned long long)max_usable_kB); if (f.bsize == ERR_BSIZE) printf( "Could not determine the size of the actually used data area.\n\n"); if (f.bsize >= REFUSE_BSIZE) { printf( "Device size would be truncated, which\n" "would corrupt data and result in\n" "'access beyond end of device' errors.\n"); if (ignore_sanity_checks) { printf("\nIgnoring sanity check on user request.\n\n"); return; } printf( "If you want me to do this, you need to zero out the first part\n" "of the device (destroy the content).\n" "You should be very sure that you mean it.\n" "Operation refused.\n\n"); exit(40); /* FIXME sane exit code! */ } /* looks like file system data */ if (fs_kB > max_usable_kB) { printf( "\nDevice size would be truncated, which\n" "would corrupt data and result in\n" "'access beyond end of device' errors.\n" "You need to either\n" " * use external meta data (recommended)\n" " * shrink that filesystem first\n" " * zero out the device (destroy the filesystem)\n" "Operation refused.\n\n"); exit(40); /* FIXME sane exit code! */ } else { printf( "\nEven though it looks like this would place the new meta data into\n" "unused space, you still need to confirm, as this is only a guess.\n"); } } else printf("\n ==> This might destroy existing data! <==\n"); if (!confirmed("Do you want to proceed?")) { printf("Operation canceled.\n"); exit(1); // 1 to avoid online resource counting } } /* tries to guess what is in the on_disk_buffer */ enum md_format detect_md(struct md_cpu *md, const uint64_t ll_size) { struct md_cpu md_test; enum md_format have = DRBD_UNKNOWN; md_disk_07_to_cpu(&md_test, (struct md_on_disk_07*)on_disk_buffer); if (is_valid_md(DRBD_V07, &md_test, DRBD_MD_INDEX_FLEX_INT, ll_size)) { have = DRBD_V07; *md = md_test; } md_disk_08_to_cpu(&md_test, (struct md_on_disk_08*)on_disk_buffer); if (is_valid_md(DRBD_V08, &md_test, DRBD_MD_INDEX_FLEX_INT, ll_size)) { have = DRBD_V08; *md = md_test; } md_disk_09_to_cpu(&md_test, (struct meta_data_on_disk_9*)on_disk_buffer); if (is_valid_md(DRBD_V09, &md_test, DRBD_MD_INDEX_FLEX_INT, ll_size)) { have = DRBD_V09; *md = md_test; } return have; } void check_internal_md_flavours(struct format * cfg) { struct md_cpu md_now; off_t fixed_offset, flex_offset; enum md_format have = DRBD_UNKNOWN; int fixed = 0; /* as opposed to flex */ ASSERT( cfg->md_index == DRBD_MD_INDEX_INTERNAL || cfg->md_index == DRBD_MD_INDEX_FLEX_INT ); fixed_offset = v07_style_md_get_byte_offset( DRBD_MD_INDEX_INTERNAL, cfg->bd_size); flex_offset = v07_style_md_get_byte_offset( DRBD_MD_INDEX_FLEX_INT, cfg->bd_size); /* printf("%lld\n%lld\n%lld\n", (long long unsigned)cfg->bd_size, (long long unsigned)fixed_offset, (long long unsigned)flex_offset); */ if (0 <= fixed_offset && fixed_offset < (off_t)cfg->bd_size - 4096) { struct md_cpu md_test; /* ... v07 fixed-size internal meta data? */ PREAD(cfg, on_disk_buffer, 4096, fixed_offset); md_disk_07_to_cpu(&md_test, (struct md_on_disk_07*)on_disk_buffer); if (is_valid_md(DRBD_V07, &md_test, DRBD_MD_INDEX_INTERNAL, cfg->bd_size)) { have = DRBD_V07; fixed = 1; md_now = md_test; } } if (have == DRBD_UNKNOWN) { PREAD(cfg, on_disk_buffer, 4096, flex_offset); have = detect_md(&md_now, cfg->bd_size); } if (have == DRBD_UNKNOWN) return; fprintf(stderr, "You want me to create a %s%s style %s internal meta data block.\n", cfg->ops->name, (is_v07(cfg) && cfg->md_index == DRBD_MD_INDEX_FLEX_INT) ? "(plus)" : "", cfg->md_index == DRBD_MD_INDEX_FLEX_INT ? "flexible-size" : "fixed-size"); fprintf(stderr, "There appears to be a %s %s internal meta data block\n" "already in place on %s at byte offset %llu\n", f_ops[have].name, fixed ? "fixed-size" : "flexible-size", cfg->md_device_name, fixed ? (long long unsigned)fixed_offset : (long long unsigned)flex_offset); if (format_version(cfg) == have) { if (have != DRBD_V07 && (cfg->md.al_stripes != option_al_stripes || cfg->md.al_stripe_size_4k != option_al_stripe_size_4k)) { if (confirmed("Do you want to change the activity log stripe settings *only*?")) { fprintf(stderr, "Sorry, not yet fully implemented\n" "Try dump-md > dump.txt; restore-md -s x -z y dump.txt\n"); exit(30); /* * ??? * cfg->md.al_stripes = option_al_stripes; * cfg->md.al_stripe_size_4k = option_al_stripe_size_4k; * re_initialize_md_offsets(cfg); * return; * ??? */ } } if (!confirmed("Do you really want to overwrite the existing meta-data?")) { printf("Operation cancelled.\n"); exit(1); // 1 to avoid online resource counting } cfg->md.magic = 0; } else { char msg[160]; snprintf(msg, 160, "Valid %s meta-data found, convert to %s?", f_ops[have].name, cfg->ops->name); if (confirmed(msg)) { cfg->md = md_now; convert_md(cfg, have); } else { snprintf(msg, 160, "So you want me to replace the %s meta-data\n" "with newly initialized %s meta-data?", f_ops[have].name, cfg->ops->name); if (!confirmed(msg)) { printf("Operation cancelled.\n"); exit(1); // 1 to avoid online resource counting } cfg->md.magic = 0; } } /* we have two "internal" layouts: * v07 "fixed" internal: * | data .... |MD super block |AL | bitmap | * v07 "plus", v08, v09 "flexible" internal: * | data .... | bitmap |AL |MD super block | * If we change from one layout to the other, * we want to wipe the former MD super block * after successful conversion. */ /* we convert from v07 "fixed" to flexible internal, we wipe the "fixed" offset */ if (have == DRBD_V07 && fixed && cfg->md_index == DRBD_MD_INDEX_FLEX_INT) cfg->wipe_fixed = fixed_offset; /* we convert from "flexible" to v07 fixed, we wipe the "flexible" offset */ else if ((have != DRBD_V07 || fixed == 0) && (is_v07(cfg) && cfg->md_index == DRBD_MD_INDEX_INTERNAL)) cfg->wipe_flex = flex_offset; } void wipe_after_convert(struct format *cfg) { memset(on_disk_buffer, 0x00, 4096); if (cfg->wipe_fixed) pwrite_or_die(cfg, on_disk_buffer, 4096, cfg->wipe_fixed, "wipe fixed-size v07 internal md"); if (cfg->wipe_flex) pwrite_or_die(cfg, on_disk_buffer, 4096, cfg->wipe_flex, "wipe flexible-size internal md"); } void check_external_md_flavours(struct format * cfg) { struct md_cpu md_now; enum md_format have = DRBD_UNKNOWN; char msg[160]; ASSERT( cfg->md_index >= 0 || cfg->md_index == DRBD_MD_INDEX_FLEX_EXT ); if (cfg->md.magic) { if (!confirmed("Valid meta data seems to be in place.\n" "Do you really want to overwrite?")) { printf("Operation cancelled.\n"); exit(1); } cfg->md.magic = 0; return; } PREAD(cfg, on_disk_buffer, 4096, cfg->md_offset); have = detect_md(&md_now, cfg->bd_size); if (have == DRBD_UNKNOWN) return; snprintf(msg, 160, "Valid %s meta-data found, convert to %s?", f_ops[have].name, cfg->ops->name); if (confirmed(msg)) { cfg->md = md_now; convert_md(cfg, have); } else { snprintf(msg, 160, "So you want me to replace the %s meta-data\n" "with newly initialized %s meta-data?", f_ops[have].name, cfg->ops->name); if (confirmed(msg)) { cfg->md.magic = 0; return; } printf("Operation cancelled.\n"); exit(1); } } /* ok, so there is no valid meta data at the end of the device, * but there is valid internal meta data at the "last known" * position. Move the stuff. * Areas may overlap: * |--...~//~[BITMAP][AL][SB]| <<- last known * |--.......~//~[BITMAP][AL][SB]| <<- what it should look like now * So we move it in chunks. */ int v08_move_internal_md_after_resize(struct format *cfg) { struct md_cpu md_old; off_t old_offset; off_t old_bm_offset; off_t cur_offset; off_t last_chunk_size; int err; ASSERT(format_version(cfg) >= DRBD_V08); ASSERT(cfg->md_index == DRBD_MD_INDEX_FLEX_INT); ASSERT(cfg->lk_bd.bd_size <= cfg->bd_size); /* we just read it in v08_check_for_resize(). * no need to do it again, but ASSERT this. */ md_old = cfg->md; ASSERT(is_valid_md(format_version(cfg), &md_old, DRBD_MD_INDEX_FLEX_INT, cfg->lk_bd.bd_size)); old_offset = v07_style_md_get_byte_offset(DRBD_MD_INDEX_FLEX_INT, cfg->lk_bd.bd_size); /* fix AL and bitmap offsets, populate byte offsets for the new location */ re_initialize_md_offsets(cfg); fprintf(stderr, "Moving the internal meta data to its proper location\n"); if (verbose >= 2) { fprintf(stderr,"old md_offset: "U64"\n", old_offset); fprintf(stderr,"old al_offset: %llu (%d)\n", old_offset + md_old.al_offset * 512LL, md_old.al_offset); fprintf(stderr,"old bm_offset: %llu (%d)\n", old_offset + md_old.bm_offset * 512LL, md_old.bm_offset); fprintf(stderr,"new md_offset: "U64"\n", cfg->md_offset); fprintf(stderr,"new al_offset: "U64" (%d)\n", cfg->al_offset, cfg->md.al_offset); fprintf(stderr,"new bm_offset: "U64" (%d)\n", cfg->bm_offset, cfg->md.bm_offset); fprintf(stderr,"md_size_sect: "U32"\n", cfg->md.md_size_sect); fprintf(stderr,"max_usable_sect: "U64"\n", cfg->max_usable_sect); } /* FIXME * If the new meta data area overlaps the old "super block", * and we crash before we successfully wrote the new super block, * but after we overwrote the old, we are out of luck! * But I don't want to write the new superblock early, either. */ /* move activity log, fixed size immediately preceeding the "super block". */ cur_offset = old_offset + md_old.al_offset * 512LL; PREAD(cfg, on_disk_buffer, old_offset - cur_offset, cur_offset); PWRITE(cfg, on_disk_buffer, old_offset - cur_offset, cfg->al_offset); /* The AL was of fixed size. * Bitmap is of flexible size, new bitmap is likely larger. * We do not initialize that part, we just leave "garbage" in there. * Once DRBD "agrees" on the new lower level device size, that part of * the bitmap will be handled by the module, anyways. */ old_bm_offset = old_offset + cfg->md.bm_offset * 512LL; /* move bitmap, in chunks, peel off from the end. */ cur_offset = old_offset + cfg->md.al_offset * 512LL - buffer_size; while (cur_offset > old_bm_offset) { PREAD(cfg, on_disk_buffer, buffer_size, cur_offset); PWRITE(cfg, on_disk_buffer, buffer_size, cfg->bm_offset + (cur_offset - old_bm_offset)); cur_offset -= buffer_size; } /* Adjust for last, possibly partial buffer. */ last_chunk_size = buffer_size - (old_bm_offset - cur_offset); PREAD(cfg, on_disk_buffer, last_chunk_size, old_bm_offset); PWRITE(cfg, on_disk_buffer, last_chunk_size, cfg->bm_offset); /* fix bitmap offset in meta data, * and rewrite the "super block" */ re_initialize_md_offsets(cfg); err = cfg->ops->md_cpu_to_disk(cfg); if (!err) printf("Internal drbd meta data successfully moved.\n"); if (!err && old_offset < cfg->bm_offset) { /* wipe out previous meta data block, it has been superseded. */ cfg->wipe_resize = old_offset; memset(on_disk_buffer, 0, 4096); PWRITE(cfg, on_disk_buffer, 4096, old_offset); } err = cfg->ops->close(cfg) || err; if (err) fprintf(stderr, "operation failed\n"); return err; } int meta_create_md(struct format *cfg, char **argv __attribute((unused)), int argc) { int err = 0; int max_peers = 1; if (is_v09(cfg)) { if (argc < 1) { fprintf(stderr, "USAGE: %s MINOR v09 ... create-md MAX_PEERS\n" "\n" " MAX_PEERS argument missing\n", progname); exit(20); } else if (argc > 1) fprintf(stderr, "Ignoring additional arguments\n"); max_peers = m_strtoll(argv[0], 1); } else if (argc > 0) fprintf(stderr, "Ignoring additional arguments\n"); if (max_peers < 1 || max_peers > DRBD_PEERS_MAX) { fprintf(stderr, "MAX_PEERS argument not in allowed range 1 .. %d.\n", DRBD_PEERS_MAX); exit(20); } err = cfg->ops->open(cfg); /* Suggest to move existing meta data after offline resize. Though, if * you --force create-md, you probably mean it, so we don't even ask. * If you want to automatically move it, use check-resize. */ if (err == VALID_MD_FOUND_AT_LAST_KNOWN_LOCATION) { if (option_al_stripes_used) { if (option_al_stripes != cfg->md.al_stripes || option_al_stripe_size_4k != cfg->md.al_stripe_size_4k) { fprintf(stderr, "Cannot move after offline resize and change AL-striping at the same time, yet.\n"); exit(20); } } if (!force && confirmed("Move internal meta data from last-known position?\n")) { /* Maybe we want to use some library that provides detection of * fs/partition/usage types? */ check_for_existing_data(cfg); return v08_move_internal_md_after_resize(cfg); } /* else: reset cfg->md, it needs to be re-initialized below */ memset(&cfg->md, 0, sizeof(cfg->md)); } /* the offset of v07 fixed-size internal meta data is different from * the offset of the flexible-size v07 ("plus") and v08 (default) * internal meta data. * to avoid the situation where we would have "valid" meta data blocks * of different versions at different offsets, we also need to check * the other format, and the other offset. * * on a request to create v07 fixed-size internal meta data, we also * check flex-internal v08 [and v07 (plus)] at the other offset. * * on a request to create v08 flex-internal meta data (or v07 plus, for * that matter), we also check the same offset for the respective other * flex-internal format version, as well as the v07 fixed-size internal * meta data offset for its flavor of meta data. */ if (cfg->md_index == DRBD_MD_INDEX_INTERNAL || cfg->md_index == DRBD_MD_INDEX_FLEX_INT) check_internal_md_flavours(cfg); else check_external_md_flavours(cfg); if (!cfg->md.magic) /* not converted: initialize */ /* calls check_for_existing_data() internally */ err = cfg->ops->md_initialize(cfg, 1, max_peers); /* Clears on disk AL implicitly */ else { if (format_version(cfg) >= DRBD_V09 && max_peers != 1) printf("Warning: setting max_peers to 1 instead of %d\n\n", max_peers); err = 0; /* we have sucessfully converted somthing */ check_for_existing_data(cfg); } cfg->md.la_peer_max_bio_size = option_peer_max_bio_size; /* FIXME * if this converted fixed-size 128MB internal meta data * to flexible size, we'd need to move the AL and bitmap * over to the new location! * But the upgrade procedure in such case is documented to first get * the previous DRBD into "clean" L_ESTABLISHED R_SECONDARY/R_SECONDARY, so AL * and bitmap should be empty anyways. */ printf("Writing meta data...\n"); err = err || cfg->ops->md_cpu_to_disk(cfg); // <- short circuit if (!err) wipe_after_convert(cfg); err = cfg->ops->close(cfg) || err; // <- close always if (err) fprintf(stderr, "operation failed\n"); else printf("New drbd meta data block successfully created.\n"); return err; } int meta_wipe_md(struct format *cfg, char **argv __attribute((unused)), int argc) { int virgin, err; if (argc > 0) { fprintf(stderr, "Ignoring additional arguments\n"); } virgin = cfg->ops->open(cfg); if (virgin) { fprintf(stderr,"There appears to be no drbd meta data to wipe out?\n"); return 0; } if (!confirmed("Do you really want to wipe out the DRBD meta data?")) { printf("Operation cancelled.\n"); exit(1); } printf("Wiping meta data...\n"); memset(on_disk_buffer, 0, 4096); PWRITE(cfg, on_disk_buffer, 4096, cfg->md_offset); err = cfg->ops->close(cfg); if (err) fprintf(stderr, "operation failed\n"); else printf("DRBD meta data block successfully wiped out.\n"); /* delete last-known bdev info, it is of no use now. */ lk_bdev_delete(cfg->minor); return err; } int meta_outdate(struct format *cfg, char **argv __attribute((unused)), int argc) { int err; if (argc > 0) { fprintf(stderr, "Ignoring additional arguments\n"); } if (cfg->ops->open(cfg)) return -1; if (cfg->ops->outdate_gi(&cfg->md)) { fprintf(stderr, "Device is inconsistent.\n"); exit(5); } err = cfg->ops->md_cpu_to_disk(cfg); err = cfg->ops->close(cfg) || err; // <- close always if (err) fprintf(stderr, "update failed\n"); return err; } int meta_invalidate(struct format *cfg, char **argv __attribute((unused)), int argc) { int err; if (argc > 0) { fprintf(stderr, "Ignoring additional arguments\n"); } if (cfg->ops->open(cfg)) return -1; cfg->ops->invalidate_gi(&cfg->md); err = cfg->ops->md_cpu_to_disk(cfg); err = cfg->ops->close(cfg) || err; // <- close always if (err) fprintf(stderr, "update failed\n"); return err; } int meta_read_dev_uuid(struct format *cfg, char **argv __attribute((unused)), int argc) { if (argc > 0) { fprintf(stderr, "Ignoring additional arguments\n"); } if (cfg->ops->open(cfg)) return -1; printf(X64(016)"\n",cfg->md.device_uuid); return cfg->ops->close(cfg); } int meta_write_dev_uuid(struct format *cfg, char **argv, int argc) { int err; if (argc > 1) { fprintf(stderr, "Ignoring additional arguments\n"); } if (argc < 1) { fprintf(stderr, "Required Argument missing\n"); exit(10); } if (cfg->ops->open(cfg)) return -1; cfg->md.device_uuid = strto_u64(argv[0],NULL,16); err = cfg->ops->md_cpu_to_disk(cfg); err = cfg->ops->close(cfg) || err; if (err) fprintf(stderr, "update failed\n"); return err; } void print_usage_and_exit() { char **args; size_t i; printf ("\nUSAGE: %s [--force] DEVICE FORMAT [FORMAT ARGS...] COMMAND [CMD ARGS...]\n", progname); printf("\nFORMATS:\n"); for (i = DRBD_V06; i < DRBD_UNKNOWN; i++) { printf(" %s", f_ops[i].name); if ((args = f_ops[i].args)) { while (*args) { printf(" %s", *args++); } } printf("\n"); } printf("\nCOMMANDS:\n"); for (i = 0; i < ARRAY_SIZE(cmds); i++) { if (!cmds[i].show_in_usage) continue; printf(" %s%s %s\n", cmds[i].name, cmds[i].node_id_required ? " --node-id {val}" : "", cmds[i].args ? cmds[i].args : ""); } exit(20); } int parse_format(struct format *cfg, char **argv, int argc, int *ai) { enum md_format f; if (argc < 1) { fprintf(stderr, "Format identifier missing\n"); return -1; } for (f = DRBD_V06; f < DRBD_UNKNOWN; f++) { if (!strcmp(f_ops[f].name, argv[0])) break; } if (f == DRBD_UNKNOWN) { fprintf(stderr, "Unknown format '%s'.\n", argv[0]); return -1; } (*ai)++; cfg->ops = f_ops + f; return cfg->ops->parse(cfg, argv + 1, argc - 1, ai); } static enum drbd_disk_state drbd_str_disk(const char *str) { /* drbd 8.4 and earlier provide "Local/Remote" * drbd 9. only "Local". */ const char *slash = strchr(str, '/'); size_t len; int n; if (slash) len = slash - str; else len = strlen(str); for (n = 0; n < drbd_disk_state_names.size; n++) { if (drbd_disk_state_names.names[n] && !strncmp(str, drbd_disk_state_names.names[n], len)) return (enum drbd_disk_state)n; } if (!strcmp(str, "Unconfigured")) return D_DISKLESS; fprintf(stderr, "Unexpected output from drbdsetup >%s<\n", str); exit(20); } int is_attached(int minor) { char minor_string[7], result[40]; char *argv[] = { "drbdsetup", minor_string, "dstate", NULL }; int pipes[2]; pid_t pid; int rr, exitcode; if (pipe(pipes)) { perror("drbdsetup pipe"); exit(20); } snprintf(minor_string, ARRAY_SIZE(minor_string), "%d", minor); pid = fork(); if (pid == -1) { perror("fork for drbdsetup"); exit(20); } if (pid == 0) { FILE *f = freopen("/dev/null", "w", stderr); if (!f) fprintf(stderr, "freopen(/dev/null) failed\n"); close(pipes[0]); dup2(pipes[1], 1); execvp(argv[0], argv); fprintf(stderr, "Can not exec drbdsetup\n"); exit(20); } close(pipes[1]); rr = read(pipes[0], result, ARRAY_SIZE(result)); close(pipes[0]); waitpid(pid, &exitcode, 0); if (WEXITSTATUS(exitcode) == 20 || WEXITSTATUS(exitcode) == 10) return 0; /* 20 == no module; 10 == no minor */ if (rr < 1) { perror("read from drbdsetup\n"); exit(20); } result[rr-1] = 0; return drbd_str_disk(result) > D_DISKLESS ? 1 : 0; } int meta_chk_offline_resize(struct format *cfg, char **argv, int argc) { int err; err = cfg->ops->open(cfg); /* this is first, so that lk-bdev-info files are removed/updated * if we find valid meta data in the expected place. */ if (err == VALID_MD_FOUND) { /* Do not clutter the output of the init script printf("Found valid meta data in the expected location, %llu bytes into %s.\n", (unsigned long long)cfg->md_offset, cfg->md_device_name); */ /* create, delete or update the last known info */ if (lk_bdev_load(cfg->minor, &cfg->lk_bd) < 0) return -1; if (cfg->md_index != DRBD_MD_INDEX_FLEX_INT) lk_bdev_delete(cfg->minor); else if (cfg->lk_bd.bd_size != cfg->bd_size || cfg->lk_bd.bd_uuid != cfg->md.device_uuid) cfg->update_lk_bdev = 1; return cfg->ops->close(cfg); } else if (err == NO_VALID_MD_FOUND) { if (format_version(cfg) < DRBD_V08 || cfg->md_index != DRBD_MD_INDEX_FLEX_INT) { fprintf(stderr, "Operation only supported for >= v8 internal meta data\n"); return -1; } fprintf(stderr, "no suitable meta data found :(\n"); return -1; /* sorry :( */ } /* VALID_MD_FOUND_AT_LAST_KNOWN_LOCATION */ ASSERT(format_version(cfg) >= DRBD_V08); ASSERT(cfg->md_index == DRBD_MD_INDEX_FLEX_INT); ASSERT(cfg->lk_bd.bd_size); ASSERT(cfg->md.magic); return v08_move_internal_md_after_resize(cfg); } int meta_forget_peer(struct format *cfg, char **argv, int argc) { int err; err = cfg->ops->open(cfg); if (err) return -1; cfg->md.peers[option_node_id].bitmap_index = -1; cfg->md.peers[option_node_id].bitmap_uuid = 0; cfg->md.peers[option_node_id].flags = 0; cfg->ops->md_cpu_to_disk(cfg); err = cfg->ops->close(cfg) || err; if (err) fprintf(stderr, "update failed\n"); return err; } /* CALL ONLY ONCE as long as on_disk_buffer is global! */ struct format *new_cfg() { int err; struct format *cfg; errno = 0; pagesize = sysconf(_SC_PAGESIZE); if (errno) { perror("could not determine pagesize"); exit(20); } cfg = calloc(1, sizeof(struct format)); if (!cfg) { fprintf(stderr, "could not calloc() cfg\n"); exit(20); } err = posix_memalign(&on_disk_buffer, pagesize, ALIGN(buffer_size, pagesize)); if (err) { fprintf(stderr, "could not posix_memalign() on_disk_buffer\n"); exit(20); } return cfg; } int main(int argc, char **argv) { struct format *cfg; size_t i; int ai, rv; bool minor_attached = false; #if 1 if (sizeof(struct md_on_disk_07) != 4096) { fprintf(stderr, "Where did you get this broken build!?\n" "sizeof(md_on_disk_07) == %lu, should be 4096\n", (unsigned long)sizeof(struct md_on_disk_07)); exit(111); } if (sizeof(struct md_on_disk_08) != 4096) { fprintf(stderr, "Where did you get this broken build!?\n" "sizeof(md_on_disk_08) == %lu, should be 4096\n", (unsigned long)sizeof(struct md_on_disk_08)); exit(111); } if (sizeof(struct meta_data_on_disk_9) != 4096) { fprintf(stderr, "Where did you get this broken build!?\n" "sizeof(meta_data_on_disk_9) == %lu, should be 4096\n", (unsigned long)sizeof(struct meta_data_on_disk_9)); exit(111); } #if 0 printf("v07: al_offset: %u\n", (int)&(((struct md_on_disk_07*)0)->al_offset)); printf("v07: bm_offset: %u\n", (int)&(((struct md_on_disk_07*)0)->bm_offset)); printf("v08: al_offset: %u\n", (int)&(((struct md_on_disk_08*)0)->al_offset)); printf("v08: bm_offset: %u\n", (int)&(((struct md_on_disk_08*)0)->bm_offset)); exit(0); #endif #endif if ((progname = strrchr(argv[0], '/'))) { argv[0] = ++progname; } else { progname = argv[0]; } if (argc < 4) print_usage_and_exit(); /* so dump_md can write a nice header */ global_argc = argc; global_argv = argv; /* Check for options (e.g. --force) */ while (1) { int c = getopt_long(argc, argv, make_optstring(metaopt), metaopt, 0); if (c == -1) break; switch (c) { case 0: break; case 'f': force = 1; break; case 'v': verbose++; break; case 'p': option_peer_max_bio_size = m_strtoll(optarg, 1); if (option_peer_max_bio_size < 0 || option_peer_max_bio_size > 1024 * 1024) { fprintf(stderr, "peer-max-bio-size out of range (0...1M)\n"); exit(10); } break; case 'i': option_node_id = m_strtoll(optarg, 1); if (option_node_id < 0 || option_node_id > (DRBD_PEERS_MAX - 1)) { fprintf(stderr, "node-id out of range (0...%d)\n", DRBD_PEERS_MAX - 1); exit(10); } break; case 's': option_al_stripes = m_strtoll(optarg, 1); option_al_stripes_used = 1; break; case 'z': option_al_stripe_size_4k = m_strtoll(optarg, 'k')/4; option_al_stripes_used = 1; break; default: print_usage_and_exit(); break; } } // Next argument to process is specified by optind... ai = optind; cfg = new_cfg(); cfg->drbd_dev_name = argv[ai++]; if (parse_format(cfg, argv + ai, argc - ai, &ai)) { /* parse has already printed some error message */ exit(20); } if (ai >= argc) { fprintf(stderr, "command missing\n"); exit(20); } for (i = 0; i < ARRAY_SIZE(cmds); i++) { if (!strcmp(cmds[i].name, argv[ai])) { command = cmds + i; break; } } if (command == NULL) { fprintf(stderr, "Unknown command '%s'.\n", argv[ai]); exit(20); } ai++; /* does exit() unless we acquired the lock. * unlock happens implicitly when the process dies, * but may be requested implicitly */ if (strcmp(cfg->drbd_dev_name, "-")) { cfg->minor = dt_minor_of_dev(cfg->drbd_dev_name); if (cfg->minor < 0) { fprintf(stderr, "Cannot determine minor device number of " "drbd device '%s'", cfg->drbd_dev_name); exit(20); } cfg->lock_fd = dt_lock_drbd(cfg->minor); /* check whether this is in use */ minor_attached = is_attached(cfg->minor); if (minor_attached && command->modifies_md) { fprintf(stderr, "Device '%s' is configured!\n", cfg->drbd_dev_name); exit(20); } } else { cfg->minor = -1; cfg->lock_fd = -1; } if (option_peer_max_bio_size && command->function != &meta_create_md) { fprintf(stderr, "The --peer-max-bio-size option is only allowed with create-md\n"); exit(10); } if (option_al_stripes_used && command->function != &meta_create_md && command->function != &meta_restore_md) { fprintf(stderr, "The --al-stripe* options are only allowed with create-md and restore-md\n"); exit(10); } /* at some point I'd like to go for this: (16*1024*1024/4) */ if ((uint64_t)option_al_stripes * option_al_stripe_size_4k > (buffer_size/4096)) { fprintf(stderr, "invalid (too large) al-stripe* settings\n"); exit(10); } if (option_al_stripes * option_al_stripe_size_4k < 32/4) { fprintf(stderr, "invalid (too small) al-stripe* settings\n"); exit(10); } if (option_node_id != -1 && !command->node_id_required) { fprintf(stderr, "The %s command does not accept the --node-id option\n", command->name); exit(10); } /* Hope this is sufficcient for backward compat */ if (!is_v09(cfg) && command->node_id_required) { if (option_node_id == -1) option_node_id = 0; else if (option_node_id != 0) fprintf(stderr, "Not v09, implicitly set --node-id = 0\n"); } if (option_node_id == -1 && command->node_id_required) { fprintf(stderr, "The %s command requires the --node-id option\n", command->name); exit(10); } rv = command->function(cfg, argv + ai, argc - ai); if (minor_attached) fprintf(stderr, "# Output might be stale, since minor %d is attached\n", cfg->minor); return rv; /* and if we want an explicit free, * this would be the place for it. * free(cfg->md_device_name), free(cfg) ... */ } drbd-utils-8.9.10/user/shared/libgenl.h0000644000175000017500000007433712747646700017622 0ustar apoikosapoikos#ifndef LIBGENL_H #define LIBGENL_H /* * stripped down copy of * linux-2.6.32/include/net/netlink.h and * linux-2.6.32/include/net/genetlink.h * * sk_buff -> "msg_buff" */ #include #include #include #include #include #include #include #include #include #ifndef SOL_NETLINK #define SOL_NETLINK 270 #endif #define DEBUG_LEVEL 1 #define dbg(lvl, fmt, arg...) \ do { \ if (lvl <= DEBUG_LEVEL) \ fprintf(stderr, "<%d>" fmt "\n", \ lvl , ##arg); \ } while (0) #define BUG_ON(cond) \ do { \ int __cond = (cond); \ if (!__cond) \ break; \ fprintf(stderr, "BUG: %s:%d: %s == %u\n", \ __FILE__, __LINE__, \ #cond, __cond); \ abort(); \ } while (0) #define min_t(type, x, y) ({ \ type __min1 = (x); \ type __min2 = (y); \ __min1 < __min2 ? __min1: __min2; }) #ifndef __read_mostly #define __read_mostly #endif #ifndef unlikely #define unlikely(arg) (arg) #endif #ifndef ARRAY_SIZE #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #endif struct msg_buff { /* housekeeping */ unsigned char *tail; unsigned char *end; /* start of data to be send(), * or received into */ unsigned char data[0]; }; #define DEFAULT_MSG_SIZE 8192 static inline unsigned char *msg_tail_pointer(struct msg_buff *msg) { return msg->tail; } static inline int msg_tailroom(struct msg_buff *msg) { return msg->end - msg->tail; } static inline struct msg_buff *msg_new(size_t size) { struct msg_buff *m = calloc(1, sizeof(*m) + size); if (!m) return NULL; m->tail = m->data; m->end = m->tail + size; return m; } static inline void msg_free(struct msg_buff *m) { free(m); } static inline void *msg_put(struct msg_buff *msg, unsigned int len) { void *tmp = msg->tail; msg->tail += len; BUG_ON(msg->tail > msg->end); return (void*)tmp; } /* ======================================================================== * Netlink Messages and Attributes Interface (As Seen On TV) * ------------------------------------------------------------------------ * Messages Interface * ------------------------------------------------------------------------ * * Message Format: * <--- nlmsg_total_size(payload) ---> * <-- nlmsg_msg_size(payload) -> * +----------+- - -+-------------+- - -+-------- - - * | nlmsghdr | Pad | Payload | Pad | nlmsghdr * +----------+- - -+-------------+- - -+-------- - - * nlmsg_data(nlh)---^ ^ * nlmsg_next(nlh)-----------------------+ * * Payload Format: * <---------------------- nlmsg_len(nlh) ---------------------> * <------ hdrlen ------> <- nlmsg_attrlen(nlh, hdrlen) -> * +----------------------+- - -+--------------------------------+ * | Family Header | Pad | Attributes | * +----------------------+- - -+--------------------------------+ * nlmsg_attrdata(nlh, hdrlen)---^ * * Data Structures: * struct nlmsghdr netlink message header * * Message Construction: * nlmsg_new() create a new netlink message * nlmsg_put() add a netlink message to an msg * nlmsg_put_answer() callback based nlmsg_put() * nlmsg_end() finanlize netlink message * nlmsg_get_pos() return current position in message * nlmsg_trim() trim part of message * nlmsg_cancel() cancel message construction * nlmsg_free() free a netlink message * * Message Sending: * nlmsg_multicast() multicast message to several groups * nlmsg_unicast() unicast a message to a single socket * nlmsg_notify() send notification message * * Message Length Calculations: * nlmsg_msg_size(payload) length of message w/o padding * nlmsg_total_size(payload) length of message w/ padding * nlmsg_padlen(payload) length of padding at tail * * Message Payload Access: * nlmsg_data(nlh) head of message payload * nlmsg_len(nlh) length of message payload * nlmsg_attrdata(nlh, hdrlen) head of attributes data * nlmsg_attrlen(nlh, hdrlen) length of attributes data * * Message Parsing: * nlmsg_ok(nlh, remaining) does nlh fit into remaining bytes? * nlmsg_next(nlh, remaining) get next netlink message * nlmsg_parse() parse attributes of a message * nlmsg_find_attr() find an attribute in a message * nlmsg_for_each_msg() loop over all messages * nlmsg_validate() validate netlink message incl. attrs * nlmsg_for_each_attr() loop over all attributes * * Misc: * nlmsg_report() report back to application? * * ------------------------------------------------------------------------ * Attributes Interface * ------------------------------------------------------------------------ * * Attribute Format: * <------- nla_total_size(payload) -------> * <---- nla_attr_size(payload) -----> * +----------+- - -+- - - - - - - - - +- - -+-------- - - * | Header | Pad | Payload | Pad | Header * +----------+- - -+- - - - - - - - - +- - -+-------- - - * <- nla_len(nla) -> ^ * nla_data(nla)----^ | * nla_next(nla)-----------------------------' * * Data Structures: * struct nlattr netlink attribute header * * Attribute Construction: * nla_reserve(msg, type, len) reserve room for an attribute * nla_reserve_nohdr(msg, len) reserve room for an attribute w/o hdr * nla_put(msg, type, len, data) add attribute to msg * nla_put_nohdr(msg, len, data) add attribute w/o hdr * nla_append(msg, len, data) append data to msg * * Attribute Construction for Basic Types: * nla_put_u8(msg, type, value) add u8 attribute to msg * nla_put_u16(msg, type, value) add u16 attribute to msg * nla_put_u32(msg, type, value) add u32 attribute to msg * nla_put_u64(msg, type, value) add u64 attribute to msg * nla_put_string(msg, type, str) add string attribute to msg * nla_put_flag(msg, type) add flag attribute to msg * nla_put_msecs(msg, type, jiffies) add msecs attribute to msg * * Exceptions Based Attribute Construction: * NLA_PUT(msg, type, len, data) add attribute to msg * NLA_PUT_U8(msg, type, value) add u8 attribute to msg * NLA_PUT_U16(msg, type, value) add u16 attribute to msg * NLA_PUT_U32(msg, type, value) add u32 attribute to msg * NLA_PUT_U64(msg, type, value) add u64 attribute to msg * NLA_PUT_STRING(msg, type, str) add string attribute to msg * NLA_PUT_FLAG(msg, type) add flag attribute to msg * NLA_PUT_MSECS(msg, type, jiffies) add msecs attribute to msg * * The meaning of these functions is equal to their lower case * variants but they jump to the label nla_put_failure in case * of a failure. * * Nested Attributes Construction: * nla_nest_start(msg, type) start a nested attribute * nla_nest_end(msg, nla) finalize a nested attribute * nla_nest_cancel(msg, nla) cancel nested attribute construction * * Attribute Length Calculations: * nla_attr_size(payload) length of attribute w/o padding * nla_total_size(payload) length of attribute w/ padding * nla_padlen(payload) length of padding * * Attribute Payload Access: * nla_data(nla) head of attribute payload * nla_len(nla) length of attribute payload * * Attribute Payload Access for Basic Types: * nla_get_u8(nla) get payload for a u8 attribute * nla_get_u16(nla) get payload for a u16 attribute * nla_get_u32(nla) get payload for a u32 attribute * nla_get_u64(nla) get payload for a u64 attribute * nla_get_flag(nla) return 1 if flag is true * nla_get_msecs(nla) get payload for a msecs attribute * * Attribute Misc: * nla_memcpy(dest, nla, count) copy attribute into memory * nla_memcmp(nla, data, size) compare attribute with memory area * nla_strlcpy(dst, nla, size) copy attribute to a sized string * nla_strcmp(nla, str) compare attribute with string * * Attribute Parsing: * nla_ok(nla, remaining) does nla fit into remaining bytes? * nla_next(nla, remaining) get next netlink attribute * nla_validate() validate a stream of attributes * nla_validate_nested() validate a stream of nested attributes * nla_find() find attribute in stream of attributes * nla_find_nested() find attribute in nested attributes * nla_parse() parse and validate stream of attrs * nla_parse_nested() parse nested attribuets * nla_for_each_attr() loop over all attributes * nla_for_each_nested() loop over the nested attributes *========================================================================= */ /** * Standard attribute types to specify validation policy */ enum { NLA_UNSPEC, NLA_U8, NLA_U16, NLA_U32, NLA_U64, NLA_STRING, NLA_FLAG, NLA_MSECS, NLA_NESTED, NLA_NESTED_COMPAT, NLA_NUL_STRING, NLA_BINARY, __NLA_TYPE_MAX, }; #define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) /** * struct nla_policy - attribute validation policy * @type: Type of attribute or NLA_UNSPEC * @len: Type specific length of payload * * Policies are defined as arrays of this struct, the array must be * accessible by attribute type up to the highest identifier to be expected. * * Meaning of `len' field: * NLA_STRING Maximum length of string * NLA_NUL_STRING Maximum length of string (excluding NUL) * NLA_FLAG Unused * NLA_BINARY Maximum length of attribute payload * NLA_NESTED_COMPAT Exact length of structure payload * All other Exact length of attribute payload * * Example: * static struct nla_policy my_policy[ATTR_MAX+1] __read_mostly = { * [ATTR_FOO] = { .type = NLA_U16 }, * [ATTR_BAR] = { .type = NLA_STRING, .len = BARSIZ }, * [ATTR_BAZ] = { .len = sizeof(struct mystruct) }, * }; */ struct nla_policy { __u16 type; __u16 len; }; extern int nla_validate(struct nlattr *head, int len, int maxtype, const struct nla_policy *policy); extern int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, const struct nla_policy *policy); extern int nla_policy_len(const struct nla_policy *, int); extern struct nlattr * nla_find(struct nlattr *head, int len, int attrtype); extern size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize); extern int nla_memcpy(void *dest, const struct nlattr *src, int count); extern int nla_memcmp(const struct nlattr *nla, const void *data, size_t size); extern int nla_strcmp(const struct nlattr *nla, const char *str); extern struct nlattr * __nla_reserve(struct msg_buff *msg, int attrtype, int attrlen); extern void * __nla_reserve_nohdr(struct msg_buff *msg, int attrlen); extern struct nlattr * nla_reserve(struct msg_buff *msg, int attrtype, int attrlen); extern void * nla_reserve_nohdr(struct msg_buff *msg, int attrlen); extern void __nla_put(struct msg_buff *msg, int attrtype, int attrlen, const void *data); extern void __nla_put_nohdr(struct msg_buff *msg, int attrlen, const void *data); extern int nla_put(struct msg_buff *msg, int attrtype, int attrlen, const void *data); extern int nla_put_nohdr(struct msg_buff *msg, int attrlen, const void *data); extern int nla_append(struct msg_buff *msg, int attrlen, const void *data); extern int nla_put_64bit(struct msg_buff *msg, int attrtype, int attrlen, const void *data, int padattr); #define COMPAT_HAVE_NLA_PUT_64BIT 1 /************************************************************************** * Netlink Messages **************************************************************************/ /** * nlmsg_msg_size - length of netlink message not including padding * @payload: length of message payload */ static inline int nlmsg_msg_size(int payload) { return NLMSG_HDRLEN + payload; } /** * nlmsg_total_size - length of netlink message including padding * @payload: length of message payload */ static inline int nlmsg_total_size(int payload) { return NLMSG_ALIGN(nlmsg_msg_size(payload)); } /** * nlmsg_padlen - length of padding at the message's tail * @payload: length of message payload */ static inline int nlmsg_padlen(int payload) { return nlmsg_total_size(payload) - nlmsg_msg_size(payload); } /** * nlmsg_data - head of message payload * @nlh: netlink messsage header */ static inline void *nlmsg_data(const struct nlmsghdr *nlh) { return (unsigned char *) nlh + NLMSG_HDRLEN; } /** * nlmsg_len - length of message payload * @nlh: netlink message header */ static inline int nlmsg_len(const struct nlmsghdr *nlh) { return nlh->nlmsg_len - NLMSG_HDRLEN; } /** * nlmsg_attrdata - head of attributes data * @nlh: netlink message header * @hdrlen: length of family specific header */ static inline struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) { unsigned char *data = nlmsg_data(nlh); return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); } /** * nlmsg_attrlen - length of attributes data * @nlh: netlink message header * @hdrlen: length of family specific header */ static inline int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) { return nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen); } /** * nlmsg_ok - check if the netlink message fits into the remaining bytes * @nlh: netlink message header * @remaining: number of bytes remaining in message stream */ static inline int nlmsg_ok(const struct nlmsghdr *nlh, int remaining) { return (remaining >= (int) sizeof(struct nlmsghdr) && nlh->nlmsg_len >= sizeof(struct nlmsghdr) && nlh->nlmsg_len <= (__u32)remaining); } /** * nlmsg_next - next netlink message in message stream * @nlh: netlink message header * @remaining: number of bytes remaining in message stream * * Returns the next netlink message in the message stream and * decrements remaining by the size of the current message. */ static inline struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining) { int totlen = NLMSG_ALIGN(nlh->nlmsg_len); *remaining -= totlen; return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); } /** * nlmsg_parse - parse attributes of a netlink message * @nlh: netlink message header * @hdrlen: length of family specific header * @tb: destination array with maxtype+1 elements * @maxtype: maximum attribute type to be expected * @policy: validation policy * * See nla_parse() */ static inline int nlmsg_parse(const struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], int maxtype, const struct nla_policy *policy) { if (nlh->nlmsg_len < (__u32)nlmsg_msg_size(hdrlen)) return -EINVAL; return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), policy); } /** * nlmsg_find_attr - find a specific attribute in a netlink message * @nlh: netlink message header * @hdrlen: length of familiy specific header * @attrtype: type of attribute to look for * * Returns the first attribute which matches the specified type. */ static inline struct nlattr *nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype) { return nla_find(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), attrtype); } /** * nlmsg_validate - validate a netlink message including attributes * @nlh: netlinket message header * @hdrlen: length of familiy specific header * @maxtype: maximum attribute type to be expected * @policy: validation policy */ static inline int nlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, const struct nla_policy *policy) { if (nlh->nlmsg_len < (__u32)nlmsg_msg_size(hdrlen)) return -EINVAL; return nla_validate(nlmsg_attrdata(nlh, hdrlen), nlmsg_attrlen(nlh, hdrlen), maxtype, policy); } /** * nlmsg_report - need to report back to application? * @nlh: netlink message header * * Returns 1 if a report back to the application is requested. */ static inline int nlmsg_report(const struct nlmsghdr *nlh) { return !!(nlh->nlmsg_flags & NLM_F_ECHO); } /** * nlmsg_for_each_attr - iterate over a stream of attributes * @pos: loop counter, set to current attribute * @nlh: netlink message header * @hdrlen: length of familiy specific header * @rem: initialized to len, holds bytes currently remaining in stream */ #define nlmsg_for_each_attr(pos, nlh, hdrlen, rem) \ nla_for_each_attr(pos, nlmsg_attrdata(nlh, hdrlen), \ nlmsg_attrlen(nlh, hdrlen), rem) /** * nlmsg_for_each_msg - iterate over a stream of messages * @pos: loop counter, set to current message * @head: head of message stream * @len: length of message stream * @rem: initialized to len, holds bytes currently remaining in stream */ #define nlmsg_for_each_msg(pos, head, len, rem) \ for (pos = head, rem = len; \ nlmsg_ok(pos, rem); \ pos = nlmsg_next(pos, &(rem))) /************************************************************************** * Netlink Attributes **************************************************************************/ /** * nla_attr_size - length of attribute not including padding * @payload: length of payload */ static inline int nla_attr_size(int payload) { return NLA_HDRLEN + payload; } /** * nla_total_size - total length of attribute including padding * @payload: length of payload */ static inline int nla_total_size(int payload) { return NLA_ALIGN(nla_attr_size(payload)); } /** * nla_padlen - length of padding at the tail of attribute * @payload: length of payload */ static inline int nla_padlen(int payload) { return nla_total_size(payload) - nla_attr_size(payload); } #ifndef NLA_TYPE_MASK #define NLA_TYPE_MASK ~0 #endif /** * nla_type - attribute type * @nla: netlink attribute */ static inline int nla_type(const struct nlattr *nla) { return nla->nla_type & NLA_TYPE_MASK; } /** * nla_data - head of payload * @nla: netlink attribute */ static inline void *nla_data(const struct nlattr *nla) { return (char *) nla + NLA_HDRLEN; } /** * nla_len - length of payload * @nla: netlink attribute */ static inline int nla_len(const struct nlattr *nla) { return nla->nla_len - NLA_HDRLEN; } /** * nla_ok - check if the netlink attribute fits into the remaining bytes * @nla: netlink attribute * @remaining: number of bytes remaining in attribute stream */ static inline int nla_ok(const struct nlattr *nla, int remaining) { return remaining >= (int) sizeof(*nla) && nla->nla_len >= sizeof(*nla) && nla->nla_len <= remaining; } /** * nla_next - next netlink attribute in attribute stream * @nla: netlink attribute * @remaining: number of bytes remaining in attribute stream * * Returns the next netlink attribute in the attribute stream and * decrements remaining by the size of the current attribute. */ static inline struct nlattr *nla_next(const struct nlattr *nla, int *remaining) { int totlen = NLA_ALIGN(nla->nla_len); *remaining -= totlen; return (struct nlattr *) ((char *) nla + totlen); } /** * nla_find_nested - find attribute in a set of nested attributes * @nla: attribute containing the nested attributes * @attrtype: type of attribute to look for * * Returns the first attribute which matches the specified type. */ static inline struct nlattr *nla_find_nested(struct nlattr *nla, int attrtype) { return nla_find(nla_data(nla), nla_len(nla), attrtype); } /** * nla_parse_nested - parse nested attributes * @tb: destination array with maxtype+1 elements * @maxtype: maximum attribute type to be expected * @nla: attribute containing the nested attributes * @policy: validation policy * * See nla_parse() */ static inline int nla_parse_nested(struct nlattr *tb[], int maxtype, const struct nlattr *nla, const struct nla_policy *policy) { return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); } /** * nla_put_u8 - Add a u8 netlink attribute to a message buffer * @msg: message buffer to add attribute to * @attrtype: attribute type * @value: numeric value */ static inline int nla_put_u8(struct msg_buff *msg, int attrtype, __u8 value) { return nla_put(msg, attrtype, sizeof(__u8), &value); } /** * nla_put_u16 - Add a u16 netlink attribute to a message buffer * @msg: message buffer to add attribute to * @attrtype: attribute type * @value: numeric value */ static inline int nla_put_u16(struct msg_buff *msg, int attrtype, __u16 value) { return nla_put(msg, attrtype, sizeof(__u16), &value); } /** * nla_put_u32 - Add a u32 netlink attribute to a message buffer * @msg: message buffer to add attribute to * @attrtype: attribute type * @value: numeric value */ static inline int nla_put_u32(struct msg_buff *msg, int attrtype, __u32 value) { return nla_put(msg, attrtype, sizeof(__u32), &value); } /** * nla_put_64 - Add a u64 netlink attribute to a message buffer * @msg: message buffer to add attribute to * @attrtype: attribute type * @value: numeric value */ static inline int nla_put_u64(struct msg_buff *msg, int attrtype, __u64 value) { return nla_put(msg, attrtype, sizeof(__u64), &value); } /** * nla_put_string - Add a string netlink attribute to a message buffer * @msg: message buffer to add attribute to * @attrtype: attribute type * @str: NUL terminated string */ static inline int nla_put_string(struct msg_buff *msg, int attrtype, const char *str) { return nla_put(msg, attrtype, strlen(str) + 1, str); } /** * nla_put_flag - Add a flag netlink attribute to a message buffer * @msg: message buffer to add attribute to * @attrtype: attribute type */ static inline int nla_put_flag(struct msg_buff *msg, int attrtype) { return nla_put(msg, attrtype, 0, NULL); } #define NLA_PUT(msg, attrtype, attrlen, data) \ do { \ if (unlikely(nla_put(msg, attrtype, attrlen, data) < 0)) \ goto nla_put_failure; \ } while(0) #define NLA_PUT_TYPE(msg, type, attrtype, value) \ do { \ type __tmp = value; \ NLA_PUT(msg, attrtype, sizeof(type), &__tmp); \ } while(0) #define NLA_PUT_U8(msg, attrtype, value) \ NLA_PUT_TYPE(msg, __u8, attrtype, value) #define NLA_PUT_U16(msg, attrtype, value) \ NLA_PUT_TYPE(msg, __u16, attrtype, value) #define NLA_PUT_LE16(msg, attrtype, value) \ NLA_PUT_TYPE(msg, __le16, attrtype, value) #define NLA_PUT_BE16(msg, attrtype, value) \ NLA_PUT_TYPE(msg, __be16, attrtype, value) #define NLA_PUT_U32(msg, attrtype, value) \ NLA_PUT_TYPE(msg, __u32, attrtype, value) #define NLA_PUT_BE32(msg, attrtype, value) \ NLA_PUT_TYPE(msg, __be32, attrtype, value) #define NLA_PUT_U64(msg, attrtype, value) \ NLA_PUT_TYPE(msg, __u64, attrtype, value) #define NLA_PUT_BE64(msg, attrtype, value) \ NLA_PUT_TYPE(msg, __be64, attrtype, value) #define NLA_PUT_STRING(msg, attrtype, value) \ NLA_PUT(msg, attrtype, strlen(value) + 1, value) #define NLA_PUT_FLAG(msg, attrtype) \ NLA_PUT(msg, attrtype, 0, NULL) /** * nla_get_u32 - return payload of u32 attribute * @nla: u32 netlink attribute */ static inline __u32 nla_get_u32(const struct nlattr *nla) { return *(__u32 *) nla_data(nla); } /** * nla_get_be32 - return payload of __be32 attribute * @nla: __be32 netlink attribute */ static inline __be32 nla_get_be32(const struct nlattr *nla) { return *(__be32 *) nla_data(nla); } /** * nla_get_u16 - return payload of u16 attribute * @nla: u16 netlink attribute */ static inline __u16 nla_get_u16(const struct nlattr *nla) { return *(__u16 *) nla_data(nla); } /** * nla_get_be16 - return payload of __be16 attribute * @nla: __be16 netlink attribute */ static inline __be16 nla_get_be16(const struct nlattr *nla) { return *(__be16 *) nla_data(nla); } /** * nla_get_le16 - return payload of __le16 attribute * @nla: __le16 netlink attribute */ static inline __le16 nla_get_le16(const struct nlattr *nla) { return *(__le16 *) nla_data(nla); } /** * nla_get_u8 - return payload of u8 attribute * @nla: u8 netlink attribute */ static inline __u8 nla_get_u8(const struct nlattr *nla) { return *(__u8 *) nla_data(nla); } /** * nla_get_u64 - return payload of u64 attribute * @nla: u64 netlink attribute */ static inline __u64 nla_get_u64(const struct nlattr *nla) { __u64 tmp; nla_memcpy(&tmp, nla, sizeof(tmp)); return tmp; } /** * nla_get_be64 - return payload of __be64 attribute * @nla: __be64 netlink attribute */ static inline __be64 nla_get_be64(const struct nlattr *nla) { return *(__be64 *) nla_data(nla); } /** * nla_get_flag - return payload of flag attribute * @nla: flag netlink attribute */ static inline int nla_get_flag(const struct nlattr *nla) { return !!nla; } /** * nla_nest_start - Start a new level of nested attributes * @msg: message buffer to add attributes to * @attrtype: attribute type of container * * Returns the container attribute */ static inline struct nlattr *nla_nest_start(struct msg_buff *msg, int attrtype) { struct nlattr *start = (struct nlattr *)msg->tail; if (nla_put(msg, attrtype, 0, NULL) < 0) return NULL; return start; } /** * nla_nest_end - Finalize nesting of attributes * @msg: message buffer the attributes are stored in * @start: container attribute * * Corrects the container attribute header to include the all * appeneded attributes. * * Returns the total data length of the msg. */ static inline int nla_nest_end(struct msg_buff *msg, struct nlattr *start) { start->nla_len = msg->tail - (unsigned char *)start; return msg->tail - msg->data; } /** * nla_validate_nested - Validate a stream of nested attributes * @start: container attribute * @maxtype: maximum attribute type to be expected * @policy: validation policy * * Validates all attributes in the nested attribute stream against the * specified policy. Attributes with a type exceeding maxtype will be * ignored. See documenation of struct nla_policy for more details. * * Returns 0 on success or a negative error code. */ static inline int nla_validate_nested(struct nlattr *start, int maxtype, const struct nla_policy *policy) { return nla_validate(nla_data(start), nla_len(start), maxtype, policy); } /** * nla_for_each_attr - iterate over a stream of attributes * @pos: loop counter, set to current attribute * @head: head of attribute stream * @len: length of attribute stream * @rem: initialized to len, holds bytes currently remaining in stream */ #define nla_for_each_attr(pos, head, len, rem) \ for (pos = head, rem = len; \ nla_ok(pos, rem); \ pos = nla_next(pos, &(rem))) /** * nla_for_each_nested - iterate over nested attributes * @pos: loop counter, set to current attribute * @nla: attribute containing the nested attributes * @rem: initialized to len, holds bytes currently remaining in stream */ #define nla_for_each_nested(pos, nla, rem) \ nla_for_each_attr(pos, nla_data(nla), nla_len(nla), rem) /** * struct genl_multicast_group - generic netlink multicast group * @name: name of the multicast group, names are per-family * @id: multicast group ID, assigned by the core, to use with * genlmsg_multicast(). */ struct genl_multicast_group { char name[GENL_NAMSIZ]; __u32 id; }; /** * struct genl_family - generic netlink family * @id: protocol family idenfitier * @hdrsize: length of user specific header in bytes * @name: name of family * @version: protocol version * @maxattr: maximum number of attributes supported * @attrbuf: buffer to store parsed attributes * @ops_list: list of all assigned operations * @mcast_groups: multicast groups list */ struct genl_family { unsigned int id; unsigned int hdrsize; char name[GENL_NAMSIZ]; unsigned int version; unsigned int maxattr; /* 32 should be enough for most genl families */ struct genl_multicast_group mc_groups[32]; __u32 nl_groups; }; /** * struct genl_info - receiving information * @snd_seq: sending sequence number * @nlhdr: netlink message header * @genlhdr: generic netlink message header * @userhdr: user specific header * @attrs: netlink attributes */ struct genl_info { __u32 seq; struct nlmsghdr * nlhdr; struct genlmsghdr * genlhdr; void * userhdr; struct nlattr ** attrs; }; /** * genlmsg_put - Add generic netlink header to netlink message * @msg: message buffer holding the message * @family: generic netlink family * @flags netlink message flags * @cmd: generic netlink command * * Returns pointer to user specific header */ static inline void *genlmsg_put(struct msg_buff *msg, struct genl_family *family, int flags, __u8 cmd) { const unsigned hdrsize = NLMSG_HDRLEN + GENL_HDRLEN + family->hdrsize; struct nlmsghdr *nlh; struct genlmsghdr *hdr; if (unlikely(msg_tailroom(msg) < nlmsg_total_size(hdrsize))) return NULL; nlh = msg_put(msg, hdrsize); nlh->nlmsg_type = family->id; nlh->nlmsg_flags = flags; /* pid and seq will be reassigned in genl_send() */ nlh->nlmsg_pid = 0; nlh->nlmsg_seq = 0; hdr = nlmsg_data(nlh); hdr->cmd = cmd; hdr->version = family->version; /* truncated to u8! */ hdr->reserved = 0; return (char *) hdr + GENL_HDRLEN; } /** * gennlmsg_data - head of message payload * @gnlh: genetlink messsage header */ static inline void *genlmsg_data(const struct genlmsghdr *gnlh) { return ((unsigned char *) gnlh + GENL_HDRLEN); } /** * genlmsg_len - length of message payload * @gnlh: genetlink message header */ static inline int genlmsg_len(const struct genlmsghdr *gnlh) { struct nlmsghdr *nlh = (struct nlmsghdr *)((unsigned char *)gnlh - NLMSG_HDRLEN); return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); } /** * genlmsg_msg_size - length of genetlink message not including padding * @payload: length of message payload */ static inline int genlmsg_msg_size(int payload) { return GENL_HDRLEN + payload; } /** * genlmsg_total_size - length of genetlink message including padding * @payload: length of message payload */ static inline int genlmsg_total_size(int payload) { return NLMSG_ALIGN(genlmsg_msg_size(payload)); } /* * Some helpers to simplify communicating with a particular family */ struct genl_sock { struct sockaddr_nl s_local; struct sockaddr_nl s_peer; int s_fd; unsigned int s_seq_next; unsigned int s_seq_expect; unsigned int s_flags; struct genl_family *s_family; }; extern struct genl_sock *genl_connect_to_family(struct genl_family *family); extern int genl_join_mc_group(struct genl_sock *s, const char *name); extern int genl_send(struct genl_sock *s, struct msg_buff *msg); enum { E_RCV_TIMEDOUT = 0, E_RCV_FAILED, E_RCV_NO_SOURCE_ADDR, E_RCV_SEQ_MISMATCH, E_RCV_MSG_TRUNC, E_RCV_UNEXPECTED_TYPE, E_RCV_NLMSG_DONE, E_RCV_ERROR_REPLY, E_RCV_ENOBUFS, }; /* returns negative E_RCV_*, or length of message */ extern int genl_recv_msgs(struct genl_sock *s, struct iovec *iov, char **err_desc, int timeout_ms); #endif /* LIBGENL_H */ drbd-utils-8.9.10/user/shared/config.h.in0000644000175000017500000000234313012630503020020 0ustar apoikosapoikos/* user/shared/config.h.in. Generated from configure.ac by autoheader. */ /* Local configuration directory. Commonly /etc or /usr/local/etc */ #undef DRBD_CONFIG_DIR /* Include support for drbd-8.3 kernel code */ #undef DRBD_LEGACY_83 /* Include support for drbd-8.4 kernel code */ #undef DRBD_LEGACY_84 /* Local state directory. Commonly /var/lib/drbd or /usr/local/var/lib/drbd */ #undef DRBD_LIB_DIR /* Local lock directory. Commonly /var/lock or /usr/local/var/lock */ #undef DRBD_LOCK_DIR /* Runtime state directory. Commonly /var/run/drbd or /usr/local/var/run/drbd */ #undef DRBD_RUN_DIR /* Does genetlink provide CTRL_CMD_DELMCAST_GRP already */ #undef HAVE_CTRL_CMD_DELMCAST_GRP /* define if the compiler supports basic C++11 syntax */ #undef HAVE_CXX11 /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION drbd-utils-8.9.10/user/shared/shared_tool.h0000644000175000017500000000722412477305373020476 0ustar apoikosapoikos#ifndef TOOL_SHARED_H #define TOOL_SHARED_H #include /* for BLKGETSIZE64 */ #include #include #include #include #ifndef IN_IS_ADDR_LOOPBACK #define IN_IS_ADDR_LOOPBACK(a) ((htonl((a)->s_addr) & 0xff000000) == 0x7f000000) #endif #define COMM_TIMEOUT 120 /* MetaDataIndex for v06 / v07 style meta data blocks */ enum MetaDataIndex { Flags, /* Consistency flag,connected-ind,primary-ind */ HumanCnt, /* human-intervention-count */ TimeoutCnt, /* timout-count */ ConnectedCnt, /* connected-count */ ArbitraryCnt, /* arbitrary-count */ GEN_CNT_SIZE /* MUST BE LAST! (and Flags must stay first...) */ }; /* #define PERROR(fmt, args...) \ do { fprintf(stderr,fmt ": " , ##args); perror(0); } while (0) */ #define PERROR(fmt, args...) fprintf(stderr, fmt ": %m\n" , ##args); enum new_strtoll_errs { MSE_OK, MSE_DEFAULT_UNIT, MSE_MISSING_NUMBER, MSE_INVALID_NUMBER, MSE_INVALID_UNIT, MSE_OUT_OF_RANGE, }; enum new_strtoll_errs new_strtoll(const char *s, const char def_unit, unsigned long long *rv); struct option; struct d_address; extern const char* shell_escape(const char* s); extern char* ppsize(char* buf, unsigned long long size); extern const char* make_optstring(struct option *options); extern int fget_token(char *s, int size, FILE* stream); extern int sget_token(char *s, int size, const char** text); extern uint64_t bdev_size(int fd); /* In-place unescape double quotes and backslash escape sequences from a * double quoted string. Note: backslash is only useful to quote itself, or * double quote, no special treatment to any c-style escape sequences. */ extern void unescape(char *txt); extern volatile int alarm_raised; /* If the lower level device is resized, * and DRBD did not move its "internal" meta data in time, * the next time we try to attach, we won't find our meta data. * * Some helpers for storing and retrieving "last known" * information, to be able to find it regardless, * without scanning the full device for magic numbers. */ /* We may want to store more things later... if so, we can easily change to * some NULL terminated tag-value list format then. * For now: store the last known lower level block device size, * and its /dev/ */ struct bdev_info { uint64_t bd_size; uint64_t bd_uuid; char *bd_name; }; /* these return 0 on sucess, error code if something goes wrong. */ /* create (update) the last-known-bdev-info file */ extern int lk_bdev_save(const unsigned minor, const struct bdev_info *bd); /* we may want to remove all stored information */ extern int lk_bdev_delete(const unsigned minor); /* load info from that file. * caller should free(bd->bd_name) once it is no longer needed. */ extern int lk_bdev_load(const unsigned minor, struct bdev_info *bd); extern void get_random_bytes(void* buffer, int len); /* Since glibc 2.8~20080505-0ubuntu7 asprintf() is declared with the warn_unused_result attribute.... */ extern int m_asprintf(char **strp, const char *fmt, ...); extern void fprintf_hex(FILE *fp, off_t file_offset, const void *buf, unsigned len); extern void ensure_sanity_of_res_name(char *stg); extern bool addr_scope_local(const char *input); extern unsigned long long m_strtoll(const char* s,const char def_unit); extern int only_digits(const char *s); extern int dt_lock_drbd(int minor); extern void dt_unlock_drbd(int lock_fd); extern int dt_minor_of_dev(const char *device); extern void dt_print_gc(const uint32_t* gen_cnt); extern void dt_pretty_print_gc(const uint32_t* gen_cnt); extern void initialize_err(void); extern int err(const char *format, ...); extern const char *esc_xml(char *str); extern const char *esc(char *str); #endif drbd-utils-8.9.10/user/shared/drbd_endian.h0000644000175000017500000001164712615625471020426 0ustar apoikosapoikos#ifndef DRBD_ENDIAN_H #define DRBD_ENDIAN_H 1 /* * we don't want additional dependencies on other packages, * and we want to avoid to introduce incompatibilities by including kernel * headers from user space. * * we need the uint32_t and uint64_t types, * the hamming weight functions, * and the cpu_to_le etc. endianness convert functions. */ #include #include #ifndef BITS_PER_LONG # if defined(__SIZEOF_LONG__) # define BITS_PER_LONG (__SIZEOF_LONG__ * 8) # elif defined(__WORDSIZE) # define BITS_PER_LONG __WORDSIZE # else /* wtf is wrong with your libc headers? */ # error "neither BITS_PER_LONG, __SIZEOF_LONG__, nor __WORDSIZE defined" # endif #endif /* linux/byteorder/swab.h */ /* casts are necessary for constants, because we never know for sure * how U/UL/ULL map to __u16, uint32_t, uint64_t. At least not in a portable way. */ /* * __asm__("bswap %0" : "=r" (x) : "0" (x)); * oh, well... */ #define __swab16(x) \ ({ \ __u16 __x = (x); \ ((__u16)( \ (((__u16)(__x) & (__u16)0x00ffUL) << 8) | \ (((__u16)(__x) & (__u16)0xff00UL) >> 8) )); \ }) #define __swab32(x) \ ({ \ uint32_t __x = (x); \ ((uint32_t)( \ (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ }) #define __swab64(x) \ ({ \ uint64_t __x = (x); \ ((uint64_t)( \ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \ }) /* * linux/byteorder/little_endian.h * linux/byteorder/big_endian.h */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define cpu_to_le64(x) ((uint64_t)(x)) #define le64_to_cpu(x) ((uint64_t)(x)) #define cpu_to_le32(x) ((uint32_t)(x)) #define le32_to_cpu(x) ((uint32_t)(x)) #define cpu_to_le16(x) ((__u16)(x)) #define le16_to_cpu(x) ((__u16)(x)) #define cpu_to_be64(x) __swab64((x)) #define be64_to_cpu(x) __swab64((x)) #define cpu_to_be32(x) __swab32((x)) #define be32_to_cpu(x) __swab32((x)) #define cpu_to_be16(x) __swab16((x)) #define be16_to_cpu(x) __swab16((x)) #elif __BYTE_ORDER == __BIG_ENDIAN # define cpu_to_le64(x) __swab64((x)) # define le64_to_cpu(x) __swab64((x)) # define cpu_to_le32(x) __swab32((x)) # define le32_to_cpu(x) __swab32((x)) # define cpu_to_le16(x) __swab16((x)) # define le16_to_cpu(x) __swab16((x)) # define cpu_to_be64(x) ((uint64_t)(x)) # define be64_to_cpu(x) ((uint64_t)(x)) # define cpu_to_be32(x) ((uint32_t)(x)) # define be32_to_cpu(x) ((uint32_t)(x)) # define cpu_to_be16(x) ((__u16)(x)) # define be16_to_cpu(x) ((__u16)(x)) #else # error "sorry, weird endianness on this box" #endif /* linux/bitops.h */ /* * hweightN: returns the hamming weight (i.e. the number * of bits set) of a N-bit word */ static inline unsigned int generic_hweight32(unsigned int w) { unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); res = (res & 0x33333333) + ((res >> 2) & 0x33333333); res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); } static inline unsigned long generic_hweight64(uint64_t w) { #if BITS_PER_LONG < 64 return generic_hweight32((unsigned int)(w >> 32)) + generic_hweight32((unsigned int)w); #else uint64_t res; res = (w & 0x5555555555555555) + ((w >> 1) & 0x5555555555555555); res = (res & 0x3333333333333333) + ((res >> 2) & 0x3333333333333333); res = (res & 0x0F0F0F0F0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F0F0F0F0F); res = (res & 0x00FF00FF00FF00FF) + ((res >> 8) & 0x00FF00FF00FF00FF); res = (res & 0x0000FFFF0000FFFF) + ((res >> 16) & 0x0000FFFF0000FFFF); return (res & 0x00000000FFFFFFFF) + ((res >> 32) & 0x00000000FFFFFFFF); #endif } static inline unsigned long hweight_long(unsigned long w) { return sizeof(w) == 4 ? generic_hweight32(w) : generic_hweight64(w); } /* * Format macros for printf() */ #if BITS_PER_LONG == 32 # define X32(a) "%"#a"X" # define X64(a) "%"#a"llX" # define D32 "%d" # define D64 "%lld" # define U32 "%u" # define U64 "%llu" #elif BITS_PER_LONG == 64 # define X32(a) "%"#a"X" # define X64(a) "%"#a"lX" # define D32 "%d" # define D64 "%ld" # define U32 "%u" # define U64 "%lu" #else # error "sorry, unsupported word length on this box" #endif #if BITS_PER_LONG == 32 # define strto_u64 strtoull #elif BITS_PER_LONG == 64 # define strto_u64 strtoul #else # error "sorry, unsupported word length on this box" #endif #endif drbd-utils-8.9.10/user/shared/shared_tool.c0000644000175000017500000005022513011114651020446 0ustar apoikosapoikos/* These are common functions that are used across _all_ userspace, * ie. even across 9, 84, and 83. * * Deduplicated here for easier maintenance. */ #define _GNU_SOURCE #define _XOPEN_SOURCE 600 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "drbdadm.h" #include "drbd_endian.h" #include "linux/drbd.h" #include "drbdtool_common.h" #include "shared_tool.h" #include "shared_main.h" const char* shell_escape(const char* s) { /* ugly static buffer. so what. */ static char buffer[1024]; char *c = buffer; *c = '\0'; if (s == NULL) return buffer; while (*s) { if (buffer + sizeof(buffer) < c+2) break; switch(*s) { /* set of 'clean' characters */ case '%': case '+': case '-': case '.': case '/': case '0' ... '9': case ':': case '=': case '@': case 'A' ... 'Z': case '_': case 'a' ... 'z': break; /* escape everything else */ default: *c++ = '\\'; } *c++ = *s++; } *c = '\0'; return buffer; } /* In-place unescape double quotes and backslash escape sequences from a * double quoted string. Note: backslash is only useful to quote itself, or * double quote, no special treatment to any c-style escape sequences. */ void unescape(char *txt) { char *ue, *e; e = ue = txt; for (;;) { if (*ue == '"') { ue++; continue; } if (*ue == '\\') ue++; if (!*ue) break; *e++ = *ue++; } *e = '\0'; } /* input size is expected to be in KB */ char *ppsize(char *buf, unsigned long long size) { /* Needs 9 bytes at max including trailing NUL: * -1ULL ==> "16384 EB" */ static char units[] = { 'K', 'M', 'G', 'T', 'P', 'E' }; int base = 0; while (size >= 10000 && base < sizeof(units)-1) { /* shift + round */ size = (size >> 10) + !!(size & (1<<9)); base++; } sprintf(buf, "%u %cB", (unsigned)size, units[base]); return buf; } const char *make_optstring(struct option *options) { static char buffer[200]; char seen[256]; struct option *opt; char *c; memset(seen, 0, sizeof(seen)); opt = options; c = buffer; while (opt->name) { if (0 < opt->val && opt->val < 256) { if (seen[opt->val]++) { fprintf(stderr, "internal error: --%s has duplicate opt->val '%c'\n", opt->name, opt->val); abort(); } *c++ = opt->val; if (opt->has_arg != no_argument) { *c++ = ':'; if (opt->has_arg == optional_argument) *c++ = ':'; } } opt++; } *c = 0; return buffer; } /* s: token buffer * size: size of s, _including_ the terminating NUL * stream: to read from. * s is guaranteed to be NUL terminated * if a token (including the NUL) needs more size bytes, * s will contain only a truncated token, and the next call will * return the next size-1 non-white-space bytes of stream. */ int fget_token(char *s, int size, FILE* stream) { int c; char* sp = s; *sp = 0; /* terminate even if nothing is found */ --size; /* account for the terminating NUL */ do { // eat white spaces in front. c = getc(stream); if( c == EOF) return EOF; } while (!isgraph(c)); do { // read the first word into s *sp++ = c; c = getc(stream); if ( c == EOF) break; } while (isgraph(c) && --size); *sp=0; return 1; } int sget_token(char *s, int size, const char** text) { int c; char* sp = s; *sp = 0; /* terminate even if nothing is found */ --size; /* account for the terminating NUL */ do { // eat white spaces in front. c = *(*text)++; if( c == 0) return EOF; } while (!isgraph(c)); do { // read the first word into s *sp++ = c; c = *(*text)++; if ( c == 0) break; } while (isgraph(c) && --size); *sp=0; return 1; } uint64_t bdev_size(int fd) { uint64_t size64; /* size in byte. */ long size; /* size in sectors. */ int err; err = ioctl(fd, BLKGETSIZE64, &size64); if (err) { if (errno == EINVAL) { printf("INFO: falling back to BLKGETSIZE\n"); err = ioctl(fd, BLKGETSIZE, &size); if (err) { perror("ioctl(,BLKGETSIZE,) failed"); exit(20); } size64 = (uint64_t)512 *size; } else { perror("ioctl(,BLKGETSIZE64,) failed"); exit(20); } } return size64; } char *lk_bdev_path(unsigned minor) { char *path; m_asprintf(&path, "%s/drbd-minor-%d.lkbd", DRBD_LIB_DIR, minor); return path; } /* If the lower level device is resized, * and DRBD did not move its "internal" meta data in time, * the next time we try to attach, we won't find our meta data. * * Some helpers for storing and retrieving "last known" * information, to be able to find it regardless, * without scanning the full device for magic numbers. */ /* these return 0 on sucess, error code if something goes wrong. */ /* NOTE: file format for now: * one line, starting with size in byte, followed by tab, * followed by device name, followed by newline. */ int lk_bdev_save(const unsigned minor, const struct bdev_info *bd) { FILE *fp; char *path = lk_bdev_path(minor); int ok = 0; fp = fopen(path, "w"); /* Do not use stderr (or stdout) while having any FD open for write. We might get called with stdin, stdout and stderr closed. then fp might be on FD 2. Using stderr would send the text to the file. Work around: Error messages after closing the writebale FD. */ if (!fp) goto fail_no_fp; ok = fprintf(fp, "%llu\t%s\n", (unsigned long long) bd->bd_size, bd->bd_name); if (ok <= 0) goto fail; if (bd->bd_uuid) fprintf(fp, "uuid:\t"X64(016)"\n", bd->bd_uuid); fail: fflush(fp); fsync(fileno(fp)); fclose(fp); fail_no_fp: if (ok <= 0) /* MAYBE: unlink. But maybe partial info is better than no info? */ fprintf(stderr, "lk_bdev_save(%s) failed: %m\n", path); free(path); return ok <= 0 ? -1 : 0; } /* we may want to remove all stored information */ int lk_bdev_delete(const unsigned minor) { char *path = lk_bdev_path(minor); int rc = unlink(path); if (rc && errno != ENOENT) fprintf(stderr, "lk_bdev_delete(%s) failed: %m\n", path); free(path); return rc; } /* load info from that file. * caller should free(bd->bd_name) once it is no longer needed. */ int lk_bdev_load(const unsigned minor, struct bdev_info *bd) { FILE *fp; char *path; char *bd_name; unsigned long long bd_size; unsigned long long bd_uuid; char nl[2]; int rc = -1; if (!bd) return -1; path = lk_bdev_path(minor); fp = fopen(path, "r"); if (!fp) { if (errno != ENOENT) fprintf(stderr, "lk_bdev_load(%s) failed: %m\n", path); goto out; } rc = fscanf(fp, "%llu %ms%[\n]uuid: %llx%[\n]", &bd_size, &bd_name, nl, &bd_uuid, nl); /* rc == 5: successfully converted two lines. * == 4: newline not found, possibly truncated uuid * == 3: first line complete, uuid missing. * == 2: new line not found, possibly truncated pathname, * or early whitespace * == 1: found some number, but no more. * incomplete file? try anyways. */ bd->bd_uuid = (rc >= 4) ? bd_uuid : 0; bd->bd_name = (rc >= 2) ? bd_name : NULL; bd->bd_size = (rc >= 1) ? bd_size : 0; if (rc < 1) { fprintf(stderr, "lk_bdev_load(%s): parse error\n", path); rc = -1; } else rc = 0; fclose(fp); out: free(path); return rc; } void get_random_bytes(void* buffer, int len) { int fd; fd = open("/dev/urandom",O_RDONLY); if( fd == -1) { perror("Open of /dev/urandom failed"); exit(20); } if(read(fd,buffer,len) != len) { fprintf(stderr,"Reading from /dev/urandom failed\n"); exit(20); } close(fd); } int m_asprintf(char **strp, const char *fmt, ...) { int r; va_list ap; va_start(ap, fmt); r = vasprintf(strp, fmt, ap); va_end(ap); if (r == -1) { fprintf(stderr, "vasprintf() failed. Out of memory?\n"); exit(10); } return r; } /* print len bytes from buf in the format of well known "hd", * adjust displayed offset by file_offset */ void fprintf_hex(FILE *fp, off_t file_offset, const void *buf, unsigned len) { const unsigned char *c = buf; unsigned o; int skipped = 0; for (o = 0; o + 16 < len; o += 16, c += 16) { if (o && !memcmp(c - 16, c, 16)) { skipped = 1; continue; } if (skipped) { skipped = 0; fprintf(fp, "*\n"); } /* no error check here, don't know what to do about errors */ fprintf(fp, /* offset */ "%08llx" /* two times 8 byte as byte stream, on disk order */ " %02x %02x %02x %02x %02x %02x %02x %02x" " %02x %02x %02x %02x %02x %02x %02x %02x" /* the same as printable char or '.' */ " |%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c|\n", (unsigned long long)o + file_offset, c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7], c[8], c[9], c[10], c[11], c[12], c[13], c[14], c[15], #define p_(x) (isprint(x) ? x : '.') #define p(a,b,c,d,e,f,g,h) \ p_(a), p_(b), p_(c), p_(d), p_(e), p_(f), p_(g), p_(h) p(c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]), p(c[8], c[9], c[10], c[11], c[12], c[13], c[14], c[15]) ); } if (skipped) { fprintf(fp, "*\n"); } if (o < len) { unsigned remaining = len - o; unsigned i; fprintf(fp, "%08llx ", (unsigned long long)o + file_offset); for (i = 0; i < remaining; i++) { if (i == 8) fprintf(fp, " "); fprintf(fp, " %02x", c[i]); } fprintf(fp, "%*s |", (16 - i)*3 + (i < 8), ""); for (i = 0; i < remaining; i++) fprintf(fp, "%c", p_(c[i])); #undef p #undef p_ fprintf(fp, "|\n"); } fprintf(fp, "%08llx\n", (unsigned long long)len + file_offset); } void ensure_sanity_of_res_name(char *stg) { unsigned code; if (!*stg) { fprintf(stderr, "Resource name is empty.\n"); exit(1); } while (*stg) { /* No, we won't verify valid UTF-8, and neither check for unicode * control sequences. */ /* Only works for ASCII derived code sets. */ code = * (unsigned char*) stg; if (code < ' ' || code == '\x7f') { fprintf(stderr, "Resource name is invalid - please don't use control characters.\n"); exit(1); } stg++; } return; } bool addr_scope_local(const char *input) { struct in_addr addr4; struct in6_addr addr6; if (inet_pton(AF_INET6, input, &addr6) == 1) return IN6_IS_ADDR_LOOPBACK(&addr6); else if (inet_pton(AF_INET, input, &addr4) == 1) return IN_IS_ADDR_LOOPBACK(&addr4); return false; } unsigned long long m_strtoll(const char *s, const char def_unit) { unsigned long long r; switch(new_strtoll(s, def_unit, &r)) { case MSE_OK: return r; case MSE_DEFAULT_UNIT: fprintf(stderr, "unexpected default unit: %d\n",def_unit); exit(100); case MSE_MISSING_NUMBER: fprintf(stderr, "missing number argument\n"); exit(100); case MSE_INVALID_NUMBER: fprintf(stderr, "%s is not a valid number\n", s); exit(20); case MSE_INVALID_UNIT: fprintf(stderr, "%s is not a valid number\n", s); exit(20); case MSE_OUT_OF_RANGE: fprintf(stderr, "%s: out of range\n", s); exit(20); default: fprintf(stderr, "m_stroll() is confused\n"); exit(20); } } volatile int alarm_raised; /* nothing. just interrupt F_SETLKW */ void alarm_handler(int __attribute((unused)) signo) { alarm_raised = 1; } /* it is implicitly unlocked when the process dies. * but if you want to explicitly unlock it, just close it. */ int unlock_fd(int fd) { return close(fd); } int get_fd_lockfile_timeout(const char *path, int seconds) { int fd, err; struct sigaction sa,so; struct flock fl = { .l_type = F_WRLCK, .l_whence = 0, .l_start = 0, .l_len = 0 }; if ((fd = open(path, O_RDWR | O_CREAT, 0600)) < 0) { fprintf(stderr,"open(%s): %m\n",path); return -1; } if (seconds) { sa.sa_handler=alarm_handler; sigemptyset(&sa.sa_mask); sa.sa_flags=0; sigaction(SIGALRM,&sa,&so); alarm(seconds); err = fcntl(fd,F_SETLKW,&fl); if (err) err = errno; alarm(0); sigaction(SIGALRM,&so,NULL); } else { err = fcntl(fd,F_SETLK,&fl); if (err) err = errno; } if (!err) return fd; if (err != EINTR && err != EAGAIN) { close(fd); errno = err; fprintf(stderr,"fcntl(%s,...): %m\n", path); return -1; } /* do we want to know this? */ if (!fcntl(fd,F_GETLK,&fl)) { fprintf(stderr,"lock on %s currently held by pid:%u\n", path, fl.l_pid); } close(fd); return -1; } int dt_minor_of_dev(const char *device) { struct stat sb; long m; int digits_only = only_digits(device); const char *c = device; /* On udev/devfs based system the device nodes does not * exist before the drbd is created. * * If the device name starts with /dev/drbd followed by * only digits, or if only digits are given, * those digits are the minor number. * * Otherwise, we cannot reliably determine the minor number! * * We allow "arbitrary" device names in drbd.conf, * and those may well contain digits. * Interpreting any digits as minor number is dangerous! */ if (!digits_only) { if (!strncmp("/dev/drbd", device, 9) && only_digits(device + 9)) c = device + 9; /* if the device node exists, * and is a block device with the correct major, * do not enforce further naming conventions. * people without udev, and not using drbdadm * may do whatever they like. */ else if (!stat(device,&sb) && S_ISBLK(sb.st_mode) && major(sb.st_rdev) == LANANA_DRBD_MAJOR) return minor(sb.st_rdev); else return -1; } /* ^[0-9]+$ or ^/dev/drbd[0-9]+$ */ errno = 0; m = strtol(c, NULL, 10); if (!errno) return m; return -1; } int only_digits(const char *s) { const char *c; for (c = s; isdigit(*c); c++) ; return c != s && *c == 0; } int dt_lock_drbd(int minor) { int sz, lfd; char *lfname; /* THINK. * maybe we should also place a fcntl lock on the * _physical_device_ we open later... * * This lock is to prevent a drbd minor from being configured * by drbdsetup while drbdmeta is about to mess with its meta data. * * If you happen to mess with the meta data of one device, * pretending it belongs to an other, you'll screw up completely. * * We should store something in the meta data to detect such abuses. */ /* NOTE that /var/lock/drbd-*-* may not be "secure", * maybe we should rather use /var/lock/drbd/drbd-*-*, * and make sure that /var/lock/drbd is drwx.-..-. root:root ... */ sz = asprintf(&lfname, DRBD_LOCK_DIR "/drbd-%d-%d", LANANA_DRBD_MAJOR, minor); if (sz < 0) { perror(""); exit(20); } lfd = get_fd_lockfile_timeout(lfname, 1); free (lfname); if (lfd < 0) exit(20); return lfd; } /* ignore errors */ void dt_unlock_drbd(int lock_fd) { if (lock_fd >= 0) unlock_fd(lock_fd); } void dt_print_gc(const uint32_t* gen_cnt) { printf("%d:%d:%d:%d:%d:%d:%d:%d\n", gen_cnt[Flags] & MDF_CONSISTENT ? 1 : 0, gen_cnt[HumanCnt], gen_cnt[TimeoutCnt], gen_cnt[ConnectedCnt], gen_cnt[ArbitraryCnt], gen_cnt[Flags] & MDF_PRIMARY_IND ? 1 : 0, gen_cnt[Flags] & MDF_CONNECTED_IND ? 1 : 0, gen_cnt[Flags] & MDF_FULL_SYNC ? 1 : 0); } void dt_pretty_print_gc(const uint32_t* gen_cnt) { printf("\n" " WantFullSync |\n" " ConnectedInd | |\n" " lastState | | |\n" " ArbitraryCnt | | | |\n" " ConnectedCnt | | | | |\n" " TimeoutCnt | | | | | |\n" " HumanCnt | | | | | | |\n" "Consistent | | | | | | | |\n" " --------+-----+-----+-----+-----+-----+-----+-----+\n" " %3s | %3d | %3d | %3d | %3d | %3s | %3s | %3s \n" "\n", gen_cnt[Flags] & MDF_CONSISTENT ? "1/c" : "0/i", gen_cnt[HumanCnt], gen_cnt[TimeoutCnt], gen_cnt[ConnectedCnt], gen_cnt[ArbitraryCnt], gen_cnt[Flags] & MDF_PRIMARY_IND ? "1/p" : "0/s", gen_cnt[Flags] & MDF_CONNECTED_IND ? "1/c" : "0/n", gen_cnt[Flags] & MDF_FULL_SYNC ? "1/y" : "0/n"); } void dt_print_uuids(const uint64_t* uuid, unsigned int flags) { int i; printf(X64(016)":"X64(016)":", uuid[UI_CURRENT], uuid[UI_BITMAP]); for ( i=UI_HISTORY_START ; i<=UI_HISTORY_END ; i++ ) { printf(X64(016)":", uuid[i]); } printf("%d:%d:%d:%d:%d:%d:%d\n", flags & MDF_CONSISTENT ? 1 : 0, flags & MDF_WAS_UP_TO_DATE ? 1 : 0, flags & MDF_PRIMARY_IND ? 1 : 0, flags & MDF_CONNECTED_IND ? 1 : 0, flags & MDF_FULL_SYNC ? 1 : 0, flags & MDF_PEER_OUT_DATED ? 1 : 0, flags & MDF_CRASHED_PRIMARY ? 1 : 0); } enum new_strtoll_errs new_strtoll(const char *s, const char def_unit, unsigned long long *rv) { char unit = 0; char dummy = 0; int shift, c; switch (def_unit) { default: return MSE_DEFAULT_UNIT; case 0: case 1: case '1': shift = 0; break; case 'K': case 'k': shift = -10; break; case 's': shift = -9; // sectors break; /* case 'M': case 'm': case 'G': case 'g': */ } if (!s || !*s) return MSE_MISSING_NUMBER; c = sscanf(s, "%llu%c%c", rv, &unit, &dummy); if (c != 1 && c != 2) return MSE_INVALID_NUMBER; switch (unit) { case 0: return MSE_OK; case 'K': case 'k': shift += 10; break; case 'M': case 'm': shift += 20; break; case 'G': case 'g': shift += 30; break; case 's': shift += 9; break; default: return MSE_INVALID_UNIT; } /* if shift is negative (e.g. default unit 'K', actual unit 's'), * convert to positive, and shift right, rounding up. */ if (shift < 0) { shift = -shift; *rv = (*rv + (1ULL << shift) - 1) >> shift; return MSE_OK; } /* if shift is positive, first check for overflow */ if (*rv > (~0ULL >> shift)) return MSE_OUT_OF_RANGE; /* then convert */ *rv = *rv << shift; return MSE_OK; } struct err_state { unsigned int stderr_available:1; }; static struct err_state err_state = { /* all zero */ }; /* Call initialize_err() before creating any FD */ void initialize_err(void) { int err; err = fcntl(STDERR_FILENO, F_GETFL); if (err < 0 && errno == EBADF) { err_state.stderr_available = 0; openlog(NULL, LOG_PID, LOG_SYSLOG); } else { err_state.stderr_available = 1; } } int err(const char *format, ...) { va_list ap; int n; va_start(ap, format); if (err_state.stderr_available) { n = vfprintf(stderr, format, ap); } else { vsyslog(LOG_ERR, format, ap); n = 1; } va_end(ap); return n; } /* if @str is NULL or the empty string, return ""; * if @str contains ' ', '\t' or '\\', * surround it with ", and escape space, tab, backslash and double-quote with backslash. * This escape is for escaping tokens for the drbdadm config parser, * so any legal input do drbdadm "drbdadm dump" should result in output, which, * if fed into an additional "drbdadm dump" should give the same output again. */ const char *esc(char *str) { static char buffer[1024]; char *ue = str, *e = buffer; if (!str || !str[0]) { return "\"\""; } if (0 == fnmatch("*[!a-zA-Z0-9/._-]*", str, 0) || strlen(str) > 80) { *e++ = '"'; while (*ue) { if (*ue == '"' || *ue == '\\') { *e++ = '\\'; } if (e - buffer >= 1022) { err("string too long.\n"); exit(E_SYNTAX); } *e++ = *ue++; if (e - buffer >= 1022) { err("string too long.\n"); exit(E_SYNTAX); } } *e++ = '"'; *e++ = '\0'; return buffer; } return str; } /* escape a few things that are not legal in xml content; good enough for our * purposes, but likely not "academically correct" resp. "complete". */ const char *esc_xml(char *str) { static char buffer[1024]; char *ue = str, *e = buffer; if (!str || !str[0]) { return ""; } if (strchr(str, '"') || strchr(str, '\'') || strchr(str, '<') || strchr(str, '>') || strchr(str, '&') || strchr(str, '\\')) { while (*ue) { #define XML_ENCODE_SPECIAL(_ch, _repl) \ case _ch: \ if (e - buffer >= sizeof(buffer)-1-strlen(_repl)) goto too_long; \ strcpy(e, _repl); e += strlen(_repl); break; switch (*ue) { XML_ENCODE_SPECIAL('\'', "'"); XML_ENCODE_SPECIAL('"', """); XML_ENCODE_SPECIAL('<', "<"); XML_ENCODE_SPECIAL('>', ">"); XML_ENCODE_SPECIAL('&', "&"); default: *e++ = *ue; if (e - buffer >= 1022) goto too_long; } ue++; } *e++ = '\0'; return buffer; } return str; too_long: err("string too long.\n"); exit(E_SYNTAX); } drbd-utils-8.9.10/user/shared/drbdmeta_parser.h0000644000175000017500000000136612466702074021327 0ustar apoikosapoikostypedef union YYSTYPE { char* txt; uint64_t u64; } YYSTYPE; #define YYSTYPE_IS_DECLARED 1 #define YYSTYPE_IS_TRIVIAL 1 #define YY_NO_UNPUT 1 extern YYSTYPE yylval; extern int yylineno; enum yytokentype { TK_STRING = 258, TK_U64, TK_U32, TK_NUM, TK_GC, TK_BM, TK_UUID, TK_VERSION, TK_LA_SIZE, TK_BM_BYTE_PER_BIT, TK_DEVICE_UUID, TK_TIMES, TK_FLAGS, TK_INVALID, TK_INVALID_CHAR, TK_LA_BIO_SIZE, TK_NODE_ID, TK_BITMAP_INDEX, TK_CURRENT_UUID, TK_BITMAP, TK_BITMAP_UUID, TK_BITMAP_DAGTAG, TK_HISTORY_UUIDS, TK_PEER, TK_HASH, TK_MAX_PEERS, TK_AL_STRIPES, TK_AL_STRIPE_SIZE_4K, }; /* avoid compiler warnings about implicit declaration */ int yylex(void); void yyrestart (FILE *input_file); int my_yy_unscaned_characters(void); drbd-utils-8.9.10/user/shared/shared_parser.h0000644000175000017500000000236312535312223020777 0ustar apoikosapoikos/* shared_parser.h This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2014, LINBIT HA Solutions GmbH. drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __SHARED_PARSER_H__ #define __SHARED_PARSER_H__ struct include_file_buffer { int line; char *config_file; char *config_save; }; void include_file(FILE *f, char *name); void save_parse_context(struct include_file_buffer *buffer, FILE *f, char *name); void restore_parse_context(struct include_file_buffer *buffer); int check_uniq(const char *what, const char *fmt, ...); int check_upr(const char *what, const char *fmt, ...); #endif drbd-utils-8.9.10/user/shared/shared_parser.c0000644000175000017500000000331413016771234020775 0ustar apoikosapoikos/* * drbdadm_parser.c a hand crafted parser This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2014, LINBIT HA Solutions GmbH drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "shared_parser.h" #include "drbdadm.h" #include "drbdadm_parser.h" extern void my_parse(void); void save_parse_context(struct include_file_buffer *buffer, FILE *f, char *name) { buffer->line = line; buffer->config_file = config_file; buffer->config_save = config_save; line = 1; config_file = name; config_save = canonify_path(name); my_yypush_buffer_state(f); } void restore_parse_context(struct include_file_buffer *buffer) { yypop_buffer_state(); line = buffer->line; config_file = buffer->config_file; config_save = buffer->config_save; } void include_file(FILE *f, char *name) { struct include_file_buffer buffer; save_parse_context(&buffer, f, name); my_parse(); restore_parse_context(&buffer); } drbd-utils-8.9.10/user/shared/shared_main.h0000644000175000017500000000641612537770671020453 0ustar apoikosapoikos/* shared_main.h This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2014, LINBIT HA Solutions GmbH. drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __SHARED_MAIN_H__ #define __SHARED_MAIN_H__ #define CMD_TIMEOUT_SHORT_DEF 5 #define CMD_TIMEOUT_MEDIUM_DEF 121 #define CMD_TIMEOUT_LONG_DEF 600 extern struct d_globals global_options; void alarm_handler(int __attribute((unused)) signo); void chld_sig_hand(int __attribute((unused)) unused); unsigned minor_by_id(const char *id); void substitute_deprecated_cmd(char **c, char *deprecated, char *substitution); struct ifreq *get_ifreq(void); int have_ip(const char *af, const char *ip); int have_ip_ipv4(const char *ip); int have_ip_ipv6(const char *ip); const char *drbd_buildtag(void); #define E_USAGE 1 #define E_SYNTAX 2 #define E_CONFIG_INVALID 10 #define E_EXEC_ERROR 20 #define E_THINKO 42 /* :) */ enum { SLEEPS_FINITE = 1, SLEEPS_SHORT = 2+1, SLEEPS_LONG = 4+1, SLEEPS_VERY_LONG = 8+1, SLEEPS_MASK = 15, RETURN_PID = 2, SLEEPS_FOREVER = 4, SUPRESS_STDERR = 0x10, RETURN_STDOUT_FD = 0x20, RETURN_STDERR_FD = 0x40, DONT_REPORT_FAILED = 0x80, }; /* for check_uniq(): Check for uniqueness of certain values... * comment out if you want to NOT choke on the first conflict */ #define EXIT_ON_CONFLICT 1 /* for verify_ips(): are not verifyable ips fatal? */ #define INVALID_IP_IS_INVALID_CONF 1 enum usage_count_type { UC_YES, UC_NO, UC_ASK, }; enum pp_flags { MATCH_ON_PROXY = 1, DRBDSETUP_SHOW = 2, }; struct d_globals { unsigned int cmd_timeout_short; unsigned int cmd_timeout_medium; unsigned int cmd_timeout_long; int disable_ip_verification; int minor_count; int dialog_refresh; enum usage_count_type usage_count; }; #define IFI_HADDR 8 #define IFI_ALIAS 1 struct ifi_info { char ifi_name[IFNAMSIZ]; /* interface name, nul terminated */ uint8_t ifi_haddr[IFI_HADDR]; /* hardware address */ uint16_t ifi_hlen; /* bytes in hardware address, 0, 6, 8 */ short ifi_flags; /* IFF_xxx constants from */ short ifi_myflags; /* our own IFI_xxx flags */ struct sockaddr *ifi_addr; /* primary address */ struct ifi_info *ifi_next; /* next ifi_info structure */ }; extern int dry_run; extern int verbose; extern int adjust_with_progress; extern char *sh_varname; extern void m__system(char **argv, int flags, const char *res_name, pid_t *kid, int *fd, int *ex); static inline int m_system_ex(char **argv, int flags, const char *res_name) { int ex = -1; m__system(argv, flags, res_name, NULL, NULL, &ex); return ex; } #endif drbd-utils-8.9.10/user/shared/drbdmeta_scanner.fl0000644000175000017500000000516013002347271021621 0ustar apoikosapoikos%{ #include "drbd_endian.h" #include "drbdmeta_parser.h" #include "drbdtool_common.h" static void bad_token(char*); //#define DP printf("%s ",yytext); #define DP #define CP yylval.txt=yytext #define YY_NO_INPUT 1 #define YY_NO_UNPUT 1 %} %option noyywrap %option yylineno %option nounput /* remember to avoid backing up. * tell user about bad/unexpected tokens. */ OP [{};\[\]] WS [ \r\t\n] COMMENT \#[^\n]* /* 1<<63 is 19 digits. has to be enough. * 20 digits would risk overflow of 64bit unsigned int */ NUM -?[0-9]{1,19} NUM_TOO_LONG {NUM}[0-9] U64 0x[0-9A-Fa-f]{16} U32 0x[0-9A-Fa-f]{8} INVALID_HEX 0x[0-9A-Fa-f]{0,17} STRING \"[^\"\r\n]{1,20}\" EMPTY_STRING \"\"? INVALID_STRING \"[^\"\r\n]{1,20} INVALID_TOKEN [-_a-zA-Z0-9]{1,100} INVALID_CHAR [^-_a-zA-Z0-9 \t\r\n\";{}] %% {WS} /* skip silently */ {COMMENT} /* skip silently */ {OP} DP; return yytext[0]; {STRING} unescape(yytext); DP; CP; return TK_STRING; {U64} yylval.u64 = strto_u64(yytext, NULL, 16); DP; return TK_U64; {U32} yylval.u64 = strto_u64(yytext, NULL, 16); DP; return TK_U32; {NUM} yylval.u64 = strto_u64(yytext, NULL, 10); DP; return TK_NUM; gc DP; CP; return TK_GC; bm DP; CP; return TK_BM; uuid DP; CP; return TK_UUID; version DP; CP; return TK_VERSION; la-size-sect DP; CP; return TK_LA_SIZE; bm-byte-per-bit DP; CP; return TK_BM_BYTE_PER_BIT; device-uuid DP; CP; return TK_DEVICE_UUID; times DP; CP; return TK_TIMES; flags DP; CP; return TK_FLAGS; al-stripes DP; CP; return TK_AL_STRIPES; al-stripe-size-4k DP; CP; return TK_AL_STRIPE_SIZE_4K; la-peer-max-bio-size DP; CP; return TK_LA_BIO_SIZE; node-id DP; CP; return TK_NODE_ID; current-uuid DP; CP; return TK_CURRENT_UUID; bitmap DP; CP; return TK_BITMAP; bitmap-uuid DP; CP; return TK_BITMAP_UUID; bitmap-dagtag DP; CP; return TK_BITMAP_DAGTAG; history-uuids DP; CP; return TK_HISTORY_UUIDS; peer DP; CP; return TK_PEER; hash DP; CP; return TK_HASH; max-peers DP; CP; return TK_MAX_PEERS; bitmap-index DP; CP; return TK_BITMAP_INDEX; {INVALID_STRING} CP; bad_token("invalid string"); return TK_INVALID; {EMPTY_STRING} CP; bad_token("invalid string"); return TK_INVALID; {INVALID_HEX} CP; bad_token("invalid hex number (only 8 or 16 hex digits accepted)"); return TK_INVALID; {NUM_TOO_LONG} CP; bad_token("number too big"); return TK_INVALID; {INVALID_TOKEN} CP; bad_token("unknown token"); return TK_INVALID; {INVALID_CHAR} CP; return TK_INVALID_CHAR; %% static void bad_token(char *msg) { fflush(stdout); fprintf(stderr,"line %u: %s: %s ...\n", yylineno, msg, yytext); } int my_yy_unscaned_characters(void) { return (yy_n_chars) - ((yy_c_buf_p) - YY_CURRENT_BUFFER->yy_ch_buf); } drbd-utils-8.9.10/user/shared/drbd_buildtag.c0000644000175000017500000000032713027211651020734 0ustar apoikosapoikos/* automatically generated. DO NOT EDIT. */ #include const char *drbd_buildtag(void) { return "GIT-hash: 54df7406bd33851eb5fdde48362754bfd71e0500" " build by phil@fat-tyre, 2016-12-23 13:08:09"; } drbd-utils-8.9.10/user/shared/wrap_printf.c0000644000175000017500000000224612466702074020515 0ustar apoikosapoikos#include #include #include #include #include int wrap_printf(int indent, const char *format, ...) { static int columns, col; va_list ap1, ap2; int n; const char *nl; if (columns == 0) { struct winsize ws = { }; ioctl(1, TIOCGWINSZ, &ws); columns = ws.ws_col; if (columns <= 0) columns = 80; } va_start(ap1, format); va_copy(ap2, ap1); n = vsnprintf(NULL, 0, format, ap1); va_end(ap1); if (col + n > columns) { putchar('\n'); if (*format == '\n') format++; col = 0; } if (col == 0) { while (*format == ' ') format++; col += indent; while (indent--) putchar(' '); } n = vprintf(format, ap2); va_end(ap2); if (n > 0) col += n; nl = strrchr(format, '\n'); if (nl && nl[1] == 0) col = 0; return n; } int wrap_printf_wordwise(int indent, const char *str) { int n = 0; do { const char *fmt = "%.*s", *s; int m; if (*str == ' ') { fmt = " %.*s"; while (*str == ' ') str++; } for (s = str; *s && *s != ' '; s++) /* nothing */ ; m = wrap_printf(indent, fmt, s - str, str); if (m < 0) return m; n += m; str = s; } while (*str); return n; } drbd-utils-8.9.10/user/shared/wrap_printf.h0000644000175000017500000000035412466702074020520 0ustar apoikosapoikos#ifndef __WRAP_PRINTF #define __WRAP_PRINTF extern int wrap_printf(int indent, const char *format, ...) __attribute__((format(printf, 2, 3))); extern int wrap_printf_wordwise(int indent, const char *str); #endif /* __WRAP_PRINTF */ drbd-utils-8.9.10/user/v83/0000755000175000017500000000000013027242657015164 5ustar apoikosapoikosdrbd-utils-8.9.10/user/v83/Makefile.in0000644000175000017500000000655412634271674017247 0ustar apoikosapoikos# Makefile for drbd.o # # This file is part of DRBD by Philipp Reisner and Lars Ellenberg. # # drbd is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. # VPATH = ../shared # variables set by configure DISTRO = @DISTRO@ prefix = @prefix@ exec_prefix = @exec_prefix@ localstatedir = @localstatedir@ datarootdir = @datarootdir@ datadir = @datadir@ sbindir = @sbindir@ sysconfdir = @sysconfdir@ BASH_COMPLETION_SUFFIX = @BASH_COMPLETION_SUFFIX@ UDEV_RULE_SUFFIX = @UDEV_RULE_SUFFIX@ INITDIR = @INITDIR@ LIBDIR = @prefix@/lib/@PACKAGE_TARNAME@ CC = @CC@ CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ LN_S = @LN_S@ # features enabled or disabled by configure WITH_83_SUPPORT = @WITH_83_SUPPORT@ WITH_UDEV = @WITH_UDEV@ WITH_XEN = @WITH_XEN@ WITH_PACEMAKER = @WITH_PACEMAKER@ WITH_RGMANAGER = @WITH_RGMANAGER@ WITH_BASHCOMPLETION = @WITH_BASHCOMPLETION@ # variables meant to be overridden from the make command line DESTDIR ?= / CFLAGS += -Wall -I. -I../shared drbdadm-obj = drbdadm_scanner.o drbdadm_parser.o drbdadm_main.o \ drbdadm_adjust.o drbdtool_common.o drbdadm_usage_cnt.o \ drbd_buildtag.o drbdadm_minor_table.o shared_tool.o \ shared_main.o shared_parser.o drbdsetup-obj = drbdsetup.o drbdtool_common.o drbd_buildtag.o \ drbd_strings.o shared_tool.o all-obj := $(drbdadm-obj) $(drbdsetup-obj) all: tools ../shared_prereqs.mk: ; include ../shared_prereqs.mk ifeq ($(WITH_83_SUPPORT),yes) tools: drbdadm-83 drbdsetup-83 else tools: endif .PHONY: drbdadm drbdsetup drbdadm drbdsetup: echo >&2 "You meant to ask for $@-83" ; exit 1 drbdadm-83: $(drbdadm-obj) $(LINK.c) $(LDFLAGS) -o $@ $^ drbdadm_scanner.c: drbdadm_scanner.fl drbdadm_parser.h flex -s -odrbdadm_scanner.c drbdadm_scanner.fl drbdsetup-83: $(drbdsetup-obj) $(LINK.c) $(LDFLAGS) -o $@ $^ clean: rm -f drbdadm_scanner.c rm -f drbdsetup-83 drbdadm-83 $(all-obj) rm -f *~ distclean: clean rm -f $(all-dep) install: ifeq ($(WITH_83_SUPPORT),yes) install -d $(DESTDIR)$(localstatedir)/lib/drbd install -d $(DESTDIR)$(localstatedir)/lock install -d $(DESTDIR)/lib/drbd/ if getent group haclient > /dev/null 2> /dev/null ; then \ install -g haclient -m 4750 drbdsetup-83 $(DESTDIR)/lib/drbd/ ; \ install -m 755 drbdadm-83 $(DESTDIR)/lib/drbd/ ; \ else \ install -m 755 drbdsetup-83 $(DESTDIR)/lib/drbd/ ; \ install -m 755 drbdadm-83 $(DESTDIR)/lib/drbd/ ; \ fi endif uninstall: rm -f $(DESTDIR)/lib/drbd/drbdsetup-83 rm -f $(DESTDIR)/lib/drbd/drbdadm-83 .PHONY: install uninstall clean distclean ../../configure: @echo "please (re-)run ./autogen.sh with appropriate arguments"; exit 1 ../../config.status: ../../configure @echo "please (re-)run ./configure with appropriate arguments"; exit 1 Makefile.in: ; Makefile: Makefile.in ../../config.status cd ../.. && ./config.status user/v83/Makefile drbd-utils-8.9.10/user/v83/drbdadm_main.c0000644000175000017500000023201013011114651017710 0ustar apoikosapoikos/* drbdadm_main.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2002-2008, LINBIT Information Technologies GmbH. Copyright (C) 2002-2008, Philipp Reisner . Copyright (C) 2002-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linux/drbd_limits.h" #include "drbdtool_common.h" #include "drbdadm.h" #include "shared_main.h" #define MAX_ARGS 40 static int indent = 0; #define INDENT_WIDTH 4 #define BFMT "%s;\n" #define IPV4FMT "%-16s %s %s:%s;\n" #define IPV6FMT "%-16s %s [%s]:%s;\n" #define MDISK "%-16s %s [%s];\n" #define FMDISK "%-16s %s;\n" #define printI(fmt, args... ) printf("%*s" fmt,INDENT_WIDTH * indent,"" , ## args ) #define printA(name, val ) \ printf("%*s%*s %3s;\n", \ INDENT_WIDTH * indent,"" , \ -24+INDENT_WIDTH * indent, \ name, val ) char *progname; struct adm_cmd { const char *name; int (*function) (struct d_resource *, const char *); /* which level this command is for. * 0: don't show this command, ever * 1: normal administrative commands, shown in normal help * 2-4: shown on "drbdadm hidden-commands" * 2: useful for shell scripts * 3: callbacks potentially called from kernel module on certain events * 4: advanced, experts and developers only */ unsigned int show_in_usage:3; /* if set, command requires an explicit resource name */ unsigned int res_name_required:1; /* error out if the ip specified is not available/active now */ unsigned int verify_ips:1; /* if set, use the "cache" in /var/lib/drbd to figure out * which config file to use. * This is necessary for handlers (callbacks from kernel) to work * when using "drbdadm -c /some/other/config/file" */ unsigned int use_cached_config_file:1; unsigned int need_peer:1; unsigned int is_proxy_cmd:1; unsigned int uc_dialog:1; /* May show usage count dialog */ unsigned int test_config:1; /* Allow -t option */ }; struct deferred_cmd { int (*function) (struct d_resource *, const char *); const char *arg; struct d_resource *res; struct deferred_cmd *next; }; struct option admopt[] = { {"stacked", no_argument, 0, 'S'}, {"dry-run", no_argument, 0, 'd'}, {"verbose", no_argument, 0, 'v'}, {"config-file", required_argument, 0, 'c'}, {"config-to-test", required_argument, 0, 't'}, {"drbdsetup", required_argument, 0, 's'}, {"drbdmeta", required_argument, 0, 'm'}, {"drbd-proxy-ctl", required_argument, 0, 'p'}, {"sh-varname", required_argument, 0, 'n'}, {"force", no_argument, 0, 'f'}, {"peer", required_argument, 0, 'P'}, {"version", no_argument, 0, 'V'}, {0, 0, 0, 0} }; extern void my_parse(); extern int yydebug; extern FILE *yyin; int adm_attach(struct d_resource *, const char *); int adm_connect(struct d_resource *, const char *); int adm_generic_s(struct d_resource *, const char *); int adm_status_xml(struct d_resource *, const char *); int adm_generic_l(struct d_resource *, const char *); int adm_resize(struct d_resource *, const char *); int adm_syncer(struct d_resource *, const char *); static int adm_up(struct d_resource *, const char *); extern int adm_adjust(struct d_resource *, const char *); static int adm_dump(struct d_resource *, const char *); static int adm_dump_xml(struct d_resource *, const char *); static int adm_wait_c(struct d_resource *, const char *); static int adm_wait_ci(struct d_resource *, const char *); static int adm_proxy_up(struct d_resource *, const char *); static int adm_proxy_down(struct d_resource *, const char *); static int sh_nop(struct d_resource *, const char *); static int sh_resources(struct d_resource *, const char *); static int sh_resource(struct d_resource *, const char *); static int sh_mod_parms(struct d_resource *, const char *); static int sh_dev(struct d_resource *, const char *); static int sh_udev(struct d_resource *, const char *); static int sh_minor(struct d_resource *, const char *); static int sh_ip(struct d_resource *, const char *); static int sh_lres(struct d_resource *, const char *); static int sh_ll_dev(struct d_resource *, const char *); static int sh_md_dev(struct d_resource *, const char *); static int sh_md_idx(struct d_resource *, const char *); static int sh_b_pri(struct d_resource *, const char *); static int sh_status(struct d_resource *, const char *); static int admm_generic(struct d_resource *, const char *); static int adm_khelper(struct d_resource *, const char *); static int adm_generic_b(struct d_resource *, const char *); static int hidden_cmds(struct d_resource *, const char *); static int adm_outdate(struct d_resource *, const char *); static int adm_chk_resize(struct d_resource *res, const char *cmd); static void dump_options(char *name, struct d_option *opts); static char *get_opt_val(struct d_option *, const char *, char *); static void register_config_file(struct d_resource *res, const char *cfname); char ss_buffer[1024]; struct utsname nodeinfo; int line = 1; int fline; char *config_file = NULL; char *config_save = NULL; char *config_test = NULL; struct d_resource *config = NULL; struct d_resource *common = NULL; struct ifreq *ifreq_list = NULL; int is_drbd_top; int nr_resources; int nr_stacked; int nr_normal; int nr_ignore; int highest_minor; int config_from_stdin = 0; int config_valid = 1; int no_tty; int dry_run = 0; int verbose = 0; int adjust_with_progress = 0; int do_verify_ips = 0; int do_register_minor = 1; /* whether drbdadm was called with "all" instead of resource name(s) */ int all_resources = 0; char *drbdsetup = NULL; char *drbdmeta = NULL; char *drbd_proxy_ctl; char *sh_varname = NULL; char *setup_opts[10]; char *connect_to_host = NULL; int soi = 0; struct deferred_cmd *deferred_cmds[3] = { NULL, NULL, NULL }; struct deferred_cmd *deferred_cmds_tail[3] = { NULL, NULL, NULL }; /* DRBD adm_cmd flags shortcuts, * to avoid merge conflicts and unreadable diffs * when we add the next flag */ #define DRBD_acf1_default \ .show_in_usage = 1, \ .res_name_required = 1, \ .verify_ips = 0, \ .uc_dialog = 1, \ #define DRBD_acf1_connect \ .show_in_usage = 1, \ .res_name_required = 1, \ .verify_ips = 1, \ .need_peer = 1, \ .uc_dialog = 1, \ #define DRBD_acf1_defnet \ .show_in_usage = 1, \ .res_name_required = 1, \ .verify_ips = 1, \ .uc_dialog = 1, \ #define DRBD_acf3_handler \ .show_in_usage = 3, \ .res_name_required = 1, \ .verify_ips = 0, \ .use_cached_config_file = 1, \ #define DRBD_acf4_advanced \ .show_in_usage = 4, \ .res_name_required = 1, \ .verify_ips = 0, \ .uc_dialog = 1, \ #define DRBD_acf1_dump \ .show_in_usage = 1, \ .res_name_required = 1, \ .verify_ips = 1, \ .uc_dialog = 1, \ .test_config = 1, \ #define DRBD_acf2_shell \ .show_in_usage = 2, \ .res_name_required = 1, \ .verify_ips = 0, \ #define DRBD_acf2_proxy \ .show_in_usage = 2, \ .res_name_required = 1, \ .verify_ips = 0, \ .need_peer = 1, \ .is_proxy_cmd = 1, \ #define DRBD_acf2_hook \ .show_in_usage = 2, \ .res_name_required = 1, \ .verify_ips = 0, \ .use_cached_config_file = 1, \ #define DRBD_acf2_gen_shell \ .show_in_usage = 2, \ .res_name_required = 0, \ .verify_ips = 0, \ struct adm_cmd cmds[] = { /* name, function, flags * sort order: * - normal config commands, * - normal meta data manipulation * - sh-* * - handler * - advanced ***/ {"attach", adm_attach, DRBD_acf1_default}, {"detach", adm_generic_l, DRBD_acf1_default}, {"connect", adm_connect, DRBD_acf1_connect}, {"disconnect", adm_generic_s, DRBD_acf1_default}, {"up", adm_up, DRBD_acf1_connect}, {"down", adm_generic_l, DRBD_acf1_default}, {"primary", adm_generic_l, DRBD_acf1_default}, {"secondary", adm_generic_l, DRBD_acf1_default}, {"invalidate", adm_generic_b, DRBD_acf1_default}, {"invalidate-remote", adm_generic_l, DRBD_acf1_defnet}, {"outdate", adm_outdate, DRBD_acf1_default}, {"resize", adm_resize, DRBD_acf1_defnet}, {"syncer", adm_syncer, DRBD_acf1_defnet}, {"verify", adm_generic_s, DRBD_acf1_defnet}, {"pause-sync", adm_generic_s, DRBD_acf1_defnet}, {"resume-sync", adm_generic_s, DRBD_acf1_defnet}, {"adjust", adm_adjust, DRBD_acf1_connect}, {"wait-connect", adm_wait_c, DRBD_acf1_defnet}, {"wait-con-int", adm_wait_ci, .show_in_usage = 1,.verify_ips = 1,}, {"status", adm_status_xml, DRBD_acf2_gen_shell}, {"role", adm_generic_s, DRBD_acf1_default}, {"cstate", adm_generic_s, DRBD_acf1_default}, {"dstate", adm_generic_b, DRBD_acf1_default}, {"dump", adm_dump, DRBD_acf1_dump}, {"dump-xml", adm_dump_xml, DRBD_acf1_dump}, {"create-md", adm_create_md, DRBD_acf1_default}, {"show-gi", adm_generic_b, DRBD_acf1_default}, {"get-gi", adm_generic_b, DRBD_acf1_default}, {"dump-md", admm_generic, DRBD_acf1_default}, {"wipe-md", admm_generic, DRBD_acf1_default}, {"hidden-commands", hidden_cmds,.show_in_usage = 1,}, {"sh-nop", sh_nop, DRBD_acf2_gen_shell .uc_dialog = 1, .test_config = 1}, {"sh-resources", sh_resources, DRBD_acf2_gen_shell}, {"sh-resource", sh_resource, DRBD_acf2_shell}, {"sh-mod-parms", sh_mod_parms, DRBD_acf2_gen_shell}, {"sh-dev", sh_dev, DRBD_acf2_shell}, {"sh-udev", sh_udev, DRBD_acf2_hook}, {"sh-minor", sh_minor, DRBD_acf2_shell}, {"sh-ll-dev", sh_ll_dev, DRBD_acf2_shell}, {"sh-md-dev", sh_md_dev, DRBD_acf2_shell}, {"sh-md-idx", sh_md_idx, DRBD_acf2_shell}, {"sh-ip", sh_ip, DRBD_acf2_shell}, {"sh-lr-of", sh_lres, DRBD_acf2_shell}, {"sh-b-pri", sh_b_pri, DRBD_acf2_shell}, {"sh-status", sh_status, DRBD_acf2_gen_shell}, {"proxy-up", adm_proxy_up, DRBD_acf2_proxy}, {"proxy-down", adm_proxy_down, DRBD_acf2_proxy}, {"before-resync-target", adm_khelper, DRBD_acf3_handler}, {"after-resync-target", adm_khelper, DRBD_acf3_handler}, {"before-resync-source", adm_khelper, DRBD_acf3_handler}, {"pri-on-incon-degr", adm_khelper, DRBD_acf3_handler}, {"pri-lost-after-sb", adm_khelper, DRBD_acf3_handler}, {"fence-peer", adm_khelper, DRBD_acf3_handler}, {"local-io-error", adm_khelper, DRBD_acf3_handler}, {"pri-lost", adm_khelper, DRBD_acf3_handler}, {"initial-split-brain", adm_khelper, DRBD_acf3_handler}, {"split-brain", adm_khelper, DRBD_acf3_handler}, {"out-of-sync", adm_khelper, DRBD_acf3_handler}, {"suspend-io", adm_generic_s, DRBD_acf4_advanced}, {"resume-io", adm_generic_s, DRBD_acf4_advanced}, {"set-gi", admm_generic, DRBD_acf4_advanced}, {"new-current-uuid", adm_generic_s, DRBD_acf4_advanced}, {"check-resize", adm_chk_resize, DRBD_acf4_advanced}, }; void schedule_dcmd(int (*function) (struct d_resource *, const char *), struct d_resource *res, char *arg, int order) { struct deferred_cmd *d, *t; d = calloc(1, sizeof(struct deferred_cmd)); if (d == NULL) { perror("calloc"); exit(E_EXEC_ERROR); } d->function = function; d->res = res; d->arg = arg; /* first to come is head */ if (!deferred_cmds[order]) deferred_cmds[order] = d; /* link it in at tail */ t = deferred_cmds_tail[order]; if (t) t->next = d; /* advance tail */ deferred_cmds_tail[order] = d; } static void _adm_generic(struct d_resource *res, const char *cmd, int flags, pid_t *pid, int *fd, int *ex); /* Returns non-zero if the resource is down. */ static int test_if_resource_is_down(struct d_resource *res) { char buf[1024]; int rr, s = 0; int fd; pid_t pid; int old_verbose = verbose; if (dry_run) { fprintf(stderr, "Logic bug: should not be dry-running here.\n"); exit(E_THINKO); } if (verbose == 1) verbose = 0; _adm_generic(res, "role", SLEEPS_SHORT | RETURN_STDOUT_FD | SUPRESS_STDERR, &pid, &fd, NULL); verbose = old_verbose; if (fd < 0) { fprintf(stderr, "Strange: got negative fd.\n"); exit(E_THINKO); } while (1) { rr = read(fd, buf + s, sizeof(buf) - s); if (rr <= 0) break; s += rr; } close(fd); waitpid(pid, NULL, 0); /* Reap the child process, do not leave a zombie around. */ alarm(0); if (s == 0 || strncmp(buf, "Unconfigured", strlen("Unconfigured")) == 0) return 1; return 0; } enum do_register { SAME_ANYWAYS, DO_REGISTER }; enum do_register if_conf_differs_confirm_or_abort(struct d_resource *res) { int minor = res->me->device_minor; char *f; /* if the resource was down, * just register the new config file */ if (test_if_resource_is_down(res)) { unregister_minor(minor); return DO_REGISTER; } f = lookup_minor(minor); /* if there was nothing registered before, * there is nothing to compare to */ if (!f) return DO_REGISTER; /* no need to register the same thing again */ if (strcmp(f, config_save) == 0) return SAME_ANYWAYS; fprintf(stderr, "Warning: resource %s\n" "last used config file: %s\n" " current config file: %s\n", res->name, f, config_save); /* implicitly force if we don't have a tty */ if (no_tty) force = 1; if (!confirmed("Do you want to proceed " "and register the current config file?")) { printf("Operation canceled.\n"); exit(E_USAGE); } return DO_REGISTER; } static void register_config_file(struct d_resource *res, const char *cfname) { int minor = res->me->device_minor; if (test_if_resource_is_down(res)) unregister_minor(minor); else register_minor(minor, cfname); } enum on_error { KEEP_RUNNING, EXIT_ON_FAIL }; int call_cmd_fn(int (*function) (struct d_resource *, const char *), const char *fn_name, struct d_resource *res, enum on_error on_error) { int rv; int really_register = do_register_minor && DO_REGISTER == if_conf_differs_confirm_or_abort(res) && /* adm_up and adm_adjust only * "schedule" the commands, don't register yet! */ function != adm_up && function != adm_adjust; rv = function(res, fn_name); if (rv >= 20) { fprintf(stderr, "%s %s %s: exited with code %d\n", progname, fn_name, res->name, rv); if (on_error == EXIT_ON_FAIL) exit(rv); } if (rv == 0 && really_register) register_config_file(res, config_save); return rv; } int call_cmd(struct adm_cmd *cmd, struct d_resource *res, enum on_error on_error) { if (!res->peer) set_peer_in_resource(res, cmd->need_peer); return call_cmd_fn(cmd->function, cmd->name, res, on_error); } int _run_dcmds(int order) { struct deferred_cmd *d = deferred_cmds[order]; struct deferred_cmd *t; int r = 0; int rv = 0; while (d) { r = call_cmd_fn(d->function, d->arg, d->res, KEEP_RUNNING); t = d->next; free(d); d = t; if (r > rv) rv = r; } return rv; } int run_dcmds(void) { return _run_dcmds(0) || _run_dcmds(1) || _run_dcmds(2); } /*** These functions are used to the print the config ***/ static void dump_options2(char *name, struct d_option *opts, void(*within)(void*), void *ctx) { if (!opts && !(within && ctx)) return; printI("%s {\n", name); ++indent; while (opts) { if (opts->value) printA(opts->name, opts->is_escaped ? opts->value : esc(opts-> value)); else printI(BFMT, opts->name); opts = opts->next; } if (within) within(ctx); --indent; printI("}\n"); } static void dump_options(char *name, struct d_option *opts) { dump_options2(name, opts, NULL, NULL); } void dump_proxy_plugins(void *ctx) { struct d_option *opt = ctx; dump_options("plugin", opt); } static void dump_global_info() { if (!global_options.minor_count && !global_options.disable_ip_verification && global_options.dialog_refresh == 1) return; printI("global {\n"); ++indent; if (global_options.disable_ip_verification) printI("disable-ip-verification;\n"); if (global_options.minor_count) printI("minor-count %i;\n", global_options.minor_count); if (global_options.dialog_refresh != 1) printI("dialog-refresh %i;\n", global_options.dialog_refresh); --indent; printI("}\n\n"); } static void fake_startup_options(struct d_resource *res); static void dump_common_info() { if (!common) return; printI("common {\n"); ++indent; if (common->protocol) printA("protocol", common->protocol); fake_startup_options(common); dump_options("net", common->net_options); dump_options("disk", common->disk_options); dump_options("syncer", common->sync_options); dump_options("startup", common->startup_options); dump_options2("proxy", common->proxy_options, dump_proxy_plugins, common->proxy_plugins); dump_options("handlers", common->handlers); --indent; printf("}\n\n"); } static void dump_address(char *name, char *addr, char *port, char *af) { if (!strcmp(af, "ipv6")) printI(IPV6FMT, name, af, addr, port); else printI(IPV4FMT, name, af, addr, port); } static void dump_proxy_info(struct d_proxy_info *pi) { printI("proxy on %s {\n", names_to_str(pi->on_hosts)); ++indent; dump_address("inside", pi->inside_addr, pi->inside_port, pi->inside_af); dump_address("outside", pi->outside_addr, pi->outside_port, pi->outside_af); --indent; printI("}\n"); } static void dump_host_info(struct d_host_info *hi) { if (!hi) { printI(" # No host section data available.\n"); return; } if (hi->lower) { printI("stacked-on-top-of %s {\n", esc(hi->lower->name)); ++indent; printI("# on %s \n", names_to_str(hi->on_hosts)); } else if (hi->by_address) { if (!strcmp(hi->address_family, "ipv6")) printI("floating ipv6 [%s]:%s {\n", hi->address, hi->port); else printI("floating %s %s:%s {\n", hi->address_family, hi->address, hi->port); ++indent; } else { printI("on %s {\n", names_to_str(hi->on_hosts)); ++indent; } printI("device%*s", -19 + INDENT_WIDTH * indent, ""); if (hi->device) printf("%s ", esc(hi->device)); printf("minor %d;\n", hi->device_minor); if (!hi->lower) printA("disk", esc(hi->disk)); if (!hi->by_address) dump_address("address", hi->address, hi->port, hi->address_family); if (!hi->lower) { if (!strncmp(hi->meta_index, "flex", 4)) printI(FMDISK, "flexible-meta-disk", esc(hi->meta_disk)); else if (!strcmp(hi->meta_index, "internal")) printA("meta-disk", "internal"); else printI(MDISK, "meta-disk", esc(hi->meta_disk), hi->meta_index); } if (hi->proxy) dump_proxy_info(hi->proxy); --indent; printI("}\n"); } static void dump_options_xml2(char *name, struct d_option *opts, void(*within)(void*), void *ctx) { if (!opts && !(within && ctx)) return; printI("
\n", name); ++indent; while (opts) { if (opts->value) printI("
\n"); } static void dump_options_xml(char *name, struct d_option *opts) { dump_options_xml2(name, opts, NULL, NULL); } void dump_proxy_plugins_xml(void *ctx) { struct d_option *opt = ctx; dump_options_xml("plugin", opt); } static void dump_global_info_xml() { if (!global_options.minor_count && !global_options.disable_ip_verification && global_options.dialog_refresh == 1) return; printI("\n"); ++indent; if (global_options.disable_ip_verification) printI("\n"); if (global_options.minor_count) printI("\n", global_options.minor_count); if (global_options.dialog_refresh != 1) printI("\n", global_options.dialog_refresh); --indent; printI("\n"); } static void dump_common_info_xml() { if (!common) return; printI("protocol) printf(" protocol=\"%s\"", common->protocol); printf(">\n"); ++indent; fake_startup_options(common); dump_options_xml("net", common->net_options); dump_options_xml("disk", common->disk_options); dump_options_xml("syncer", common->sync_options); dump_options_xml("startup", common->startup_options); dump_options2("proxy", common->proxy_options, dump_proxy_plugins, common->proxy_plugins); dump_options_xml("handlers", common->handlers); --indent; printI("\n"); } static void dump_proxy_info_xml(struct d_proxy_info *pi) { printI("\n", names_to_str(pi->on_hosts)); ++indent; printI("%s\n", pi->inside_af, pi->inside_port, pi->inside_addr); printI("%s\n", pi->outside_af, pi->outside_port, pi->outside_addr); --indent; printI("\n"); } static void dump_host_info_xml(struct d_host_info *hi) { if (!hi) { printI("\n"); return; } if (hi->by_address) printI("\n"); else printI("\n", names_to_str(hi->on_hosts)); ++indent; printI("%s\n", hi->device_minor, esc_xml(hi->device)); printI("%s\n", esc_xml(hi->disk)); printI("
%s
\n", hi->address_family, hi->port, hi->address); if (!strncmp(hi->meta_index, "flex", 4)) printI("%s\n", esc_xml(hi->meta_disk)); else if (!strcmp(hi->meta_index, "internal")) printI("internal\n"); else { printI("%s\n", hi->meta_index, esc_xml(hi->meta_disk)); } if (hi->proxy) dump_proxy_info_xml(hi->proxy); --indent; printI("
\n"); } static void fake_startup_options(struct d_resource *res) { struct d_option *opt; char *val; if (res->stacked_timeouts) { opt = new_opt(strdup("stacked-timeouts"), NULL); res->startup_options = APPEND(res->startup_options, opt); } if (res->become_primary_on) { val = strdup(names_to_str(res->become_primary_on)); opt = new_opt(strdup("become-primary-on"), val); opt->is_escaped = 1; res->startup_options = APPEND(res->startup_options, opt); } } static int adm_dump(struct d_resource *res, const char *unused __attribute((unused))) { struct d_host_info *host; printI("# resource %s on %s: %s, %s\n", esc(res->name), nodeinfo.nodename, res->ignore ? "ignored" : "not ignored", res->stacked ? "stacked" : "not stacked"); printI("resource %s {\n", esc(res->name)); ++indent; if (res->protocol) printA("protocol", res->protocol); for (host = res->all_hosts; host; host = host->next) dump_host_info(host); fake_startup_options(res); dump_options("net", res->net_options); dump_options("disk", res->disk_options); dump_options("syncer", res->sync_options); dump_options("startup", res->startup_options); dump_options2("proxy", res->proxy_options, dump_proxy_plugins, res->proxy_plugins); dump_options("handlers", res->handlers); --indent; printf("}\n\n"); return 0; } static int adm_dump_xml(struct d_resource *res, const char *unused __attribute((unused))) { struct d_host_info *host; printI("name)); if (res->protocol) printf(" protocol=\"%s\"", res->protocol); printf(">\n"); ++indent; // else if (common && common->protocol) printA("# common protocol", common->protocol); for (host = res->all_hosts; host; host = host->next) dump_host_info_xml(host); fake_startup_options(res); dump_options_xml("net", res->net_options); dump_options_xml("disk", res->disk_options); dump_options_xml("syncer", res->sync_options); dump_options_xml("startup", res->startup_options); dump_options_xml2("proxy", res->proxy_options, dump_proxy_plugins_xml, res->proxy_plugins); dump_options_xml("handlers", res->handlers); --indent; printI("\n"); return 0; } static int sh_nop(struct d_resource *ignored __attribute((unused)), const char *unused __attribute((unused))) { return 0; } static int sh_resources(struct d_resource *ignored __attribute((unused)), const char *unused __attribute((unused))) { struct d_resource *res, *t; int first = 1; for_each_resource(res, t, config) { if (res->ignore) continue; if (is_drbd_top != res->stacked) continue; printf(first ? "%s" : " %s", esc(res->name)); first = 0; } if (!first) printf("\n"); return 0; } static int sh_resource(struct d_resource *res, const char *unused __attribute((unused))) { printf("%s\n", res->name); return 0; } static int sh_dev(struct d_resource *res, const char *unused __attribute((unused))) { printf("%s\n", res->me->device); return 0; } static int sh_udev(struct d_resource *res, const char *unused __attribute((unused))) { /* No shell escape necessary. Udev does not handle it anyways... */ printf("RESOURCE=%s\n", res->name); if (!strncmp(res->me->device, "/dev/drbd", 9)) printf("DEVICE=%s\n", res->me->device + 5); else printf("DEVICE=drbd%u\n", res->me->device_minor); if (!strncmp(res->me->disk, "/dev/", 5)) printf("DISK=%s\n", res->me->disk + 5); else printf("DISK=%s\n", res->me->disk); return 0; } static int sh_minor(struct d_resource *res, const char *unused __attribute((unused))) { printf("%d\n", res->me->device_minor); return 0; } static int sh_ip(struct d_resource *res, const char *unused __attribute((unused))) { printf("%s\n", res->me->address); return 0; } static int sh_lres(struct d_resource *res, const char *unused __attribute((unused))) { if (!is_drbd_top) { fprintf(stderr, "sh-lower-resource only available in stacked mode\n"); exit(E_USAGE); } if (!res->stacked) { fprintf(stderr, "'%s' is not stacked on this host (%s)\n", res->name, nodeinfo.nodename); exit(E_USAGE); } printf("%s\n", res->me->lower->name); return 0; } static int sh_ll_dev(struct d_resource *res, const char *unused __attribute((unused))) { printf("%s\n", res->me->disk); return 0; } static int sh_md_dev(struct d_resource *res, const char *unused __attribute((unused))) { char *r; if (strcmp("internal", res->me->meta_disk) == 0) r = res->me->disk; else r = res->me->meta_disk; printf("%s\n", r); return 0; } static int sh_md_idx(struct d_resource *res, const char *unused __attribute((unused))) { printf("%s\n", res->me->meta_index); return 0; } static int sh_b_pri(struct d_resource *res, const char *unused __attribute((unused))) { int i, rv; if (name_in_names(nodeinfo.nodename, res->become_primary_on) || name_in_names("both", res->become_primary_on)) { /* Opon connect resync starts, and both sides become primary at the same time. One's try might be declined since an other state transition happens. Retry. */ for (i = 0; i < 5; i++) { rv = adm_generic_s(res, "primary"); if (rv == 0) return rv; sleep(1); } return rv; } return 0; } static int sh_mod_parms(struct d_resource *res __attribute((unused)), const char *unused __attribute((unused))) { int mc = global_options.minor_count; if (mc == 0) { mc = highest_minor + 11; if (mc > DRBD_MINOR_COUNT_MAX) mc = DRBD_MINOR_COUNT_MAX; if (mc < DRBD_MINOR_COUNT_DEF) mc = DRBD_MINOR_COUNT_DEF; } printf("minor_count=%d\n", mc); return 0; } static void free_host_info(struct d_host_info *hi) { if (!hi) return; free_names(hi->on_hosts); free(hi->device); free(hi->disk); free(hi->address); free(hi->address_family); free(hi->port); free(hi->meta_disk); free(hi->meta_index); } static void free_options(struct d_option *opts) { struct d_option *f; while (opts) { free(opts->name); free(opts->value); f = opts; opts = opts->next; free(f); } } static void free_config(struct d_resource *res) { struct d_resource *f, *t; struct d_host_info *host; for_each_resource(f, t, res) { free(f->name); free(f->protocol); free(f->device); free(f->disk); free(f->meta_disk); free(f->meta_index); for (host = f->all_hosts; host; host = host->next) free_host_info(host); free_options(f->net_options); free_options(f->disk_options); free_options(f->sync_options); free_options(f->startup_options); free_options(f->proxy_options); free_options(f->handlers); free(f); } if (common) { free_options(common->net_options); free_options(common->disk_options); free_options(common->sync_options); free_options(common->startup_options); free_options(common->proxy_options); free_options(common->handlers); free(common); } if (ifreq_list) free(ifreq_list); } static void expand_opts(struct d_option *co, struct d_option **opts) { struct d_option *no; while (co) { if (!find_opt(*opts, co->name)) { // prepend new item to opts no = new_opt(strdup(co->name), co->value ? strdup(co->value) : NULL); no->next = *opts; *opts = no; } co = co->next; } } static void expand_common(void) { struct d_resource *res, *tmp; struct d_host_info *h; for_each_resource(res, tmp, config) { for (h = res->all_hosts; h; h = h->next) { if (!h->device) m_asprintf(&h->device, "/dev/drbd%u", h->device_minor); } } if (!common) return; for_each_resource(res, tmp, config) { expand_opts(common->net_options, &res->net_options); expand_opts(common->disk_options, &res->disk_options); expand_opts(common->sync_options, &res->sync_options); expand_opts(common->startup_options, &res->startup_options); expand_opts(common->proxy_options, &res->proxy_options); expand_opts(common->handlers, &res->handlers); if (common->protocol && !res->protocol) res->protocol = strdup(common->protocol); if (common->stacked_timeouts) res->stacked_timeouts = 1; if (!res->become_primary_on) res->become_primary_on = common->become_primary_on; if (common->proxy_plugins && !res->proxy_plugins) expand_opts(common->proxy_plugins, &res->proxy_plugins); } } static void find_drbdcmd(char **cmd, char **pathes) { char **path; path = pathes; while (*path) { if (access(*path, X_OK) == 0) { *cmd = *path; return; } path++; } fprintf(stderr, "Can not find command (drbdsetup/drbdmeta)\n"); exit(E_EXEC_ERROR); } #define NA(ARGC) \ ({ if((ARGC) >= MAX_ARGS) { fprintf(stderr,"MAX_ARGS too small\n"); \ exit(E_THINKO); \ } \ (ARGC)++; \ }) #define make_options(OPT) \ while(OPT) { \ if(OPT->value) { \ ssprintf(argv[NA(argc)],"--%s=%s",OPT->name,OPT->value); \ } else { \ ssprintf(argv[NA(argc)],"--%s",OPT->name); \ } \ OPT=OPT->next; \ } #define make_address(ADDR, PORT, AF) \ if (!strcmp(AF, "ipv6")) { \ ssprintf(argv[NA(argc)],"%s:[%s]:%s", AF, ADDR, PORT); \ } else { \ ssprintf(argv[NA(argc)],"%s:%s:%s", AF, ADDR, PORT); \ } int adm_attach(struct d_resource *res, const char *unused __attribute((unused))) { char *argv[MAX_ARGS]; struct d_option *opt; int argc = 0; argv[NA(argc)] = drbdsetup; ssprintf(argv[NA(argc)], "%d", res->me->device_minor); argv[NA(argc)] = "disk"; argv[NA(argc)] = res->me->disk; if (!strcmp(res->me->meta_disk, "internal")) { argv[NA(argc)] = res->me->disk; } else { argv[NA(argc)] = res->me->meta_disk; } argv[NA(argc)] = res->me->meta_index; argv[NA(argc)] = "--set-defaults"; argv[NA(argc)] = "--create-device"; opt = res->disk_options; make_options(opt); argv[NA(argc)] = 0; return m_system_ex(argv, SLEEPS_LONG, res->name); } struct d_option *find_opt(struct d_option *base, char *name) { while (base) { if (!strcmp(base->name, name)) { return base; } base = base->next; } return 0; } int adm_resize(struct d_resource *res, const char *cmd) { char *argv[MAX_ARGS]; struct d_option *opt; int i, argc = 0; int silent; int ex; argv[NA(argc)] = drbdsetup; ssprintf(argv[NA(argc)], "%d", res->me->device_minor); argv[NA(argc)] = "resize"; opt = find_opt(res->disk_options, "size"); if (opt) ssprintf(argv[NA(argc)], "--%s=%s", opt->name, opt->value); for (i = 0; i < soi; i++) argv[NA(argc)] = setup_opts[i]; argv[NA(argc)] = 0; /* if this is not "resize", but "check-resize", be silent! */ silent = strcmp(cmd, "resize") ? SUPRESS_STDERR : 0; ex = m_system_ex(argv, SLEEPS_SHORT | silent, res->name); if (ex) return ex; /* Record last-known bdev info. * Unfortunately drbdsetup did not have enough information * when doing the "resize", and in theory, _our_ information * about the backing device may even be wrong. * Call drbdsetup again, tell it to ask the kernel for * current config, and update the last known bdev info * according to that. */ /* argv[0] = drbdsetup; * argv[1] = minor; */ argv[2] = "check-resize"; argv[3] = NULL; /* ignore exit code */ m_system_ex(argv, SLEEPS_SHORT | silent, res->name); return 0; } int _admm_generic(struct d_resource *res, const char *cmd, int flags) { char *argv[MAX_ARGS]; int argc = 0, i; argv[NA(argc)] = drbdmeta; ssprintf(argv[NA(argc)], "%d", res->me->device_minor); argv[NA(argc)] = "v08"; if (!strcmp(res->me->meta_disk, "internal")) { argv[NA(argc)] = res->me->disk; } else { argv[NA(argc)] = res->me->meta_disk; } if (!strcmp(res->me->meta_index, "flexible")) { if (!strcmp(res->me->meta_disk, "internal")) { argv[NA(argc)] = "flex-internal"; } else { argv[NA(argc)] = "flex-external"; } } else { argv[NA(argc)] = res->me->meta_index; } argv[NA(argc)] = (char *)cmd; for (i = 0; i < soi; i++) { argv[NA(argc)] = setup_opts[i]; } argv[NA(argc)] = 0; return m_system_ex(argv, flags, res->name); } static int admm_generic(struct d_resource *res, const char *cmd) { return _admm_generic(res, cmd, SLEEPS_VERY_LONG); } static void _adm_generic(struct d_resource *res, const char *cmd, int flags, pid_t *pid, int *fd, int *ex) { char *argv[MAX_ARGS]; int argc = 0, i; argv[NA(argc)] = drbdsetup; ssprintf(argv[NA(argc)], "%d", res->me->device_minor); argv[NA(argc)] = (char *)cmd; for (i = 0; i < soi; i++) { argv[NA(argc)] = setup_opts[i]; } argv[NA(argc)] = 0; setenv("DRBD_RESOURCE", res->name, 1); m__system(argv, flags, res->name, pid, fd, ex); } static int adm_generic(struct d_resource *res, const char *cmd, int flags) { int ex; _adm_generic(res, cmd, flags, NULL, NULL, &ex); return ex; } int adm_generic_s(struct d_resource *res, const char *cmd) { return adm_generic(res, cmd, SLEEPS_SHORT); } int adm_status_xml(struct d_resource *res, const char *cmd) { struct d_resource *r, *t; int rv = 0; if (!dry_run) { printf("\n", PACKAGE_VERSION, API_VERSION); printf("\n", config_save); } for_each_resource(r, t, res) { if (r->ignore) continue; rv = adm_generic(r, cmd, SLEEPS_SHORT); if (rv) break; } if (!dry_run) printf("\n\n"); return rv; } int sh_status(struct d_resource *res, const char *cmd) { struct d_resource *r, *t; int rv = 0; if (!dry_run) { printf("_drbd_version=%s\n_drbd_api=%u\n", shell_escape(PACKAGE_VERSION), API_VERSION); printf("_config_file=%s\n\n", shell_escape(config_save)); } for_each_resource(r, t, res) { if (r->ignore) continue; printf("_stacked_on=%s\n", r->stacked && r->me->lower ? shell_escape(r->me->lower->name) : ""); printf("_stacked_on_device=%s\n", r->stacked && r->me->lower ? shell_escape(r->me->lower->me->device) : ""); if (r->stacked && r->me->lower) printf("_stacked_on_minor=%d\n", r->me->lower->me->device_minor); else printf("_stacked_on_minor=\n"); rv = adm_generic(r, cmd, SLEEPS_SHORT); if (rv) break; } return rv; } int adm_generic_l(struct d_resource *res, const char *cmd) { return adm_generic(res, cmd, SLEEPS_LONG); } static int adm_outdate(struct d_resource *res, const char *cmd) { int rv; rv = adm_generic(res, cmd, SLEEPS_SHORT | SUPRESS_STDERR); /* special cases for outdate: * 17: drbdsetup outdate, but is primary and thus cannot be outdated. * 5: drbdsetup outdate, and is inconsistent or worse anyways. */ if (rv == 17) return rv; if (rv == 5) { /* That might mean it is diskless. */ rv = admm_generic(res, cmd); if (rv) rv = 5; return rv; } if (rv || dry_run) { rv = admm_generic(res, cmd); } return rv; } /* shell equivalent: * ( drbdsetup resize && drbdsetup check-resize ) || drbdmeta check-resize */ static int adm_chk_resize(struct d_resource *res, const char *cmd) { /* drbdsetup resize && drbdsetup check-resize */ int ex = adm_resize(res, cmd); if (ex == 0) return 0; /* try drbdmeta check-resize */ return admm_generic(res, cmd); } static int adm_generic_b(struct d_resource *res, const char *cmd) { char buffer[4096]; int fd, status, rv = 0, rr, s = 0; pid_t pid; _adm_generic(res, cmd, SLEEPS_SHORT | RETURN_STDERR_FD, &pid, &fd, NULL); if (!dry_run) { if (fd < 0) { fprintf(stderr, "Strange: got negative fd.\n"); exit(E_THINKO); } while (1) { rr = read(fd, buffer + s, 4096 - s); if (rr <= 0) break; s += rr; } close(fd); rr = waitpid(pid, &status, 0); alarm(0); if (WIFEXITED(status)) rv = WEXITSTATUS(status); if (alarm_raised) { rv = 0x100; } } /* see drbdsetup.c, print_config_error(): * 11: some unspecific state change error * 17: SS_NO_UP_TO_DATE_DISK * In both cases, we don't need to retry with drbdmeta, * it would fail anyways with "Device is configured!" */ if (rv == 11 || rv == 17) { /* Some state transition error, report it ... */ rr = write(fileno(stderr), buffer, s); return rv; } if (rv || dry_run) { /* On other errors rv = 10 .. no minor allocated rv = 20 .. module not loaded rv = 16 .. we are diskless here retry with drbdmeta. */ rv = admm_generic(res, cmd); } return rv; } static int adm_khelper(struct d_resource *res, const char *cmd) { int rv = 0; char *sh_cmd; char minor_string[8]; char *argv[] = { "/bin/sh", "-c", NULL, NULL }; if (!res->peer) { /* Since 8.3.2 we get DRBD_PEER_AF and DRBD_PEER_ADDRESS from the kernel. If we do not know the peer by now, use these to find the peer. */ struct d_host_info *host; char *peer_address = getenv("DRBD_PEER_ADDRESS"); char *peer_af = getenv("DRBD_PEER_AF"); if (peer_address && peer_af) { for (host = res->all_hosts; host; host = host->next) { if (!strcmp(host->address_family, peer_af) && !strcmp(host->address, peer_address)) { res->peer = host; break; } } } } if (res->peer) { setenv("DRBD_PEER_AF", res->peer->address_family, 1); /* since 8.3.0 */ setenv("DRBD_PEER_ADDRESS", res->peer->address, 1); /* since 8.3.0 */ setenv("DRBD_PEER", res->peer->on_hosts->name, 1); /* deprecated */ setenv("DRBD_PEERS", names_to_str(res->peer->on_hosts), 1); /* since 8.3.0, but not usable when using a config with "floating" statements. */ } snprintf(minor_string, sizeof(minor_string), "%u", res->me->device_minor); setenv("DRBD_RESOURCE", res->name, 1); setenv("DRBD_MINOR", minor_string, 1); setenv("DRBD_CONF", config_save, 1); if ((sh_cmd = get_opt_val(res->handlers, cmd, NULL))) { argv[2] = sh_cmd; rv = m_system_ex(argv, SLEEPS_VERY_LONG, res->name); } return rv; } // need to convert discard-node-nodename to discard-local or discard-remote. void convert_discard_opt(struct d_resource *res) { struct d_option *opt; if (res == NULL) return; if ((opt = find_opt(res->net_options, "after-sb-0pri"))) { if (!strncmp(opt->value, "discard-node-", 13)) { if (!strcmp(nodeinfo.nodename, opt->value + 13)) { free(opt->value); opt->value = strdup("discard-local"); } else { free(opt->value); opt->value = strdup("discard-remote"); } } } } int adm_connect(struct d_resource *res, const char *unused __attribute((unused))) { char *argv[MAX_ARGS]; struct d_option *opt; int i; int argc = 0; argv[NA(argc)] = drbdsetup; ssprintf(argv[NA(argc)], "%d", res->me->device_minor); argv[NA(argc)] = "net"; make_address(res->me->address, res->me->port, res->me->address_family); if (res->me->proxy) { make_address(res->me->proxy->inside_addr, res->me->proxy->inside_port, res->me->proxy->inside_af); } else if (res->peer) { make_address(res->peer->address, res->peer->port, res->peer->address_family); } else if (dry_run > 1) { argv[NA(argc)] = "N/A"; } else { fprintf(stderr, "resource %s: cannot change network config without knowing my peer.\n", res->name); return dry_run ? 0 : 20; } argv[NA(argc)] = res->protocol; argv[NA(argc)] = "--set-defaults"; argv[NA(argc)] = "--create-device"; opt = res->net_options; make_options(opt); for (i = 0; i < soi; i++) { argv[NA(argc)] = setup_opts[i]; } argv[NA(argc)] = 0; return m_system_ex(argv, SLEEPS_SHORT, res->name); } struct d_resource *res_by_name(const char *name); struct d_option *del_opt(struct d_option *base, struct d_option *item) { struct d_option *i; if (base == item) { base = item->next; free(item->name); free(item->value); free(item); return base; } for (i = base; i; i = i->next) { if (i->next == item) { i->next = item->next; free(item->name); free(item->value); free(item); return base; } } return base; } // Need to convert after from resourcename to minor_number. void convert_after_option(struct d_resource *res) { struct d_option *opt, *next; struct d_resource *depends_on_res; if (res == NULL) return; opt = res->sync_options; while ((opt = find_opt(opt, "after"))) { next = opt->next; depends_on_res = res_by_name(opt->value); if (!depends_on_res || depends_on_res->ignore) { res->sync_options = del_opt(res->sync_options, opt); } else { free(opt->value); m_asprintf(&opt->value, "%d", depends_on_res->me->device_minor); } opt = next; } } char *proxy_connection_name(struct d_resource *res) { static char conn_name[128]; int counter; counter = snprintf(conn_name, sizeof(conn_name), "%s-%s-%s", names_to_str_c(res->me->proxy->on_hosts, '_'), res->name, names_to_str_c(res->peer->proxy->on_hosts, '_')); if (counter >= sizeof(conn_name)-3) { fprintf(stderr, "The connection name in resource %s got too long.\n", res->name); exit(E_CONFIG_INVALID); } return conn_name; } int do_proxy_conn_up(struct d_resource *res, const char *conn_name) { char *argv[4] = { drbd_proxy_ctl, "-c", NULL, NULL }; int rv; if (!conn_name) conn_name = proxy_connection_name(res); ssprintf(argv[2], "add connection %s %s:%s %s:%s %s:%s %s:%s", conn_name, res->me->proxy->inside_addr, res->me->proxy->inside_port, res->peer->proxy->outside_addr, res->peer->proxy->outside_port, res->me->proxy->outside_addr, res->me->proxy->outside_port, res->me->address, res->me->port); rv = m_system_ex(argv, SLEEPS_SHORT, res->name); return rv; } int do_proxy_conn_plugins(struct d_resource *res, const char *conn_name) { char *argv[MAX_ARGS]; int argc = 0; struct d_option *opt; int counter; if (!conn_name) conn_name = proxy_connection_name(res); argc = 0; argv[NA(argc)] = drbd_proxy_ctl; opt = res->proxy_options; while (opt) { argv[NA(argc)] = "-c"; ssprintf(argv[NA(argc)], "set %s %s %s", opt->name, conn_name, opt->value); opt = opt->next; } counter = 0; opt = res->proxy_plugins; /* Don't send the "set plugin ... END" line if no plugins are defined * - that's incompatible with the drbd proxy version 1. */ if (opt) { while (1) { argv[NA(argc)] = "-c"; ssprintf(argv[NA(argc)], "set plugin %s %d %s", conn_name, counter, opt ? opt->name : "END"); if (!opt) break; opt = opt->next; counter ++; } } argv[NA(argc)] = 0; if (argc > 2) return m_system_ex(argv, SLEEPS_SHORT, res->name); return 0; } int do_proxy_conn_down(struct d_resource *res, const char *conn_name) { char *argv[4] = { drbd_proxy_ctl, "-c", NULL, NULL}; int rv; if (!conn_name) conn_name = proxy_connection_name(res); ssprintf(argv[2], "del connection %s", conn_name); rv = m_system_ex(argv, SLEEPS_SHORT, res->name); return rv; } static int check_proxy(struct d_resource *res, int do_up) { int rv; if (!res->me->proxy) { if (all_resources) return 0; fprintf(stderr, "There is no proxy config for host %s in resource %s.\n", nodeinfo.nodename, res->name); exit(E_CONFIG_INVALID); } if (!name_in_names(nodeinfo.nodename, res->me->proxy->on_hosts)) { if (all_resources) return 0; fprintf(stderr, "The proxy config in resource %s is not for %s.\n", res->name, nodeinfo.nodename); exit(E_CONFIG_INVALID); } if (!res->peer) { fprintf(stderr, "Cannot determine the peer in resource %s.\n", res->name); exit(E_CONFIG_INVALID); } if (!res->peer->proxy) { fprintf(stderr, "There is no proxy config for the peer in resource %s.\n", res->name); if (all_resources) return 0; exit(E_CONFIG_INVALID); } if (do_up) { rv = do_proxy_conn_up(res, NULL); if (!rv) rv = do_proxy_conn_plugins(res, NULL); } else rv = do_proxy_conn_down(res, NULL); return rv; } static int adm_proxy_up(struct d_resource *res, const char *unused __attribute((unused))) { return check_proxy(res, 1); } static int adm_proxy_down(struct d_resource *res, const char *unused __attribute((unused))) { return check_proxy(res, 0); } int adm_syncer(struct d_resource *res, const char *unused __attribute((unused))) { char *argv[MAX_ARGS]; struct d_option *opt; int i, argc = 0; argv[NA(argc)] = drbdsetup; ssprintf(argv[NA(argc)], "%d", res->me->device_minor); argv[NA(argc)] = "syncer"; argv[NA(argc)] = "--set-defaults"; argv[NA(argc)] = "--create-device"; opt = res->sync_options; make_options(opt); for (i = 0; i < soi; i++) { argv[NA(argc)] = setup_opts[i]; } argv[NA(argc)] = 0; return m_system_ex(argv, SLEEPS_SHORT, res->name); } static int adm_up(struct d_resource *res, const char *unused __attribute((unused))) { schedule_dcmd(adm_attach, res, "attach", 0); schedule_dcmd(adm_syncer, res, "syncer", 1); schedule_dcmd(adm_connect, res, "connect", 2); return 0; } /* The stacked-timeouts switch in the startup sections allows us to enforce the use of the specified timeouts instead the use of a sane value. Should only be used if the third node should never become primary. */ static int adm_wait_c(struct d_resource *res, const char *unused __attribute((unused))) { char *argv[MAX_ARGS]; struct d_option *opt; int argc = 0, rv; argv[NA(argc)] = drbdsetup; ssprintf(argv[NA(argc)], "%d", res->me->device_minor); argv[NA(argc)] = "wait-connect"; if (is_drbd_top && !res->stacked_timeouts) { unsigned long timeout = 20; if ((opt = find_opt(res->net_options, "connect-int"))) { timeout = strtoul(opt->value, NULL, 10); // one connect-interval? two? timeout *= 2; } argv[argc++] = "-t"; ssprintf(argv[argc], "%lu", timeout); argc++; } else { opt = res->startup_options; make_options(opt); } argv[NA(argc)] = 0; rv = m_system_ex(argv, SLEEPS_FOREVER, res->name); return rv; } struct d_resource *res_by_minor(const char *id) { struct d_resource *res, *t; unsigned int mm; mm = minor_by_id(id); if (mm == -1U) return NULL; for_each_resource(res, t, config) { if (res->ignore) continue; if (mm == res->me->device_minor) { is_drbd_top = res->stacked; return res; } } return NULL; } struct d_resource *res_by_name(const char *name) { struct d_resource *res, *t; for_each_resource(res, t, config) { if (strcmp(name, res->name) == 0) return res; } return NULL; } /* In case a child exited, or exits, its return code is stored as negative number in the pids[i] array */ static int childs_running(pid_t * pids, int opts) { int i = 0, wr, rv = 0, status; for (i = 0; i < nr_resources; i++) { if (pids[i] <= 0) continue; wr = waitpid(pids[i], &status, opts); if (wr == -1) { // Wait error. if (errno == ECHILD) { printf("No exit code for %d\n", pids[i]); pids[i] = 0; // Child exited before ? continue; } perror("waitpid"); exit(E_EXEC_ERROR); } if (wr == 0) rv = 1; // Child still running. if (wr > 0) { pids[i] = 0; if (WIFEXITED(status)) pids[i] = -WEXITSTATUS(status); if (WIFSIGNALED(status)) pids[i] = -1000; } } return rv; } static void kill_childs(pid_t * pids) { int i; for (i = 0; i < nr_resources; i++) { if (pids[i] <= 0) continue; kill(pids[i], SIGINT); } } /* returns: -1 ... all childs terminated 0 ... timeout expired 1 ... a string was read */ int gets_timeout(pid_t * pids, char *s, int size, int timeout) { int pr, rr, n = 0; struct pollfd pfd; if (s) { pfd.fd = fileno(stdin); pfd.events = POLLIN | POLLHUP | POLLERR | POLLNVAL; n = 1; } if (!childs_running(pids, WNOHANG)) { pr = -1; goto out; } do { pr = poll(&pfd, n, timeout); if (pr == -1) { // Poll error. if (errno == EINTR) { if (childs_running(pids, WNOHANG)) continue; goto out; // pr = -1 here. } perror("poll"); exit(E_EXEC_ERROR); } } while (pr == -1); if (pr == 1) { // Input available. rr = read(fileno(stdin), s, size - 1); if (rr == -1) { perror("read"); exit(E_EXEC_ERROR); } s[rr] = 0; } out: return pr; } static char *get_opt_val(struct d_option *base, const char *name, char *def) { while (base) { if (!strcmp(base->name, name)) { return base->value; } base = base->next; } return def; } static int check_exit_codes(pid_t * pids) { struct d_resource *res, *t; int i = 0, rv = 0; for_each_resource(res, t, config) { if (res->ignore) continue; if (is_drbd_top != res->stacked) continue; if (pids[i] == -5 || pids[i] == -1000) { pids[i] = 0; } if (pids[i] == -20) rv = 20; i++; } return rv; } static int adm_wait_ci(struct d_resource *ignored __attribute((unused)), const char *unused __attribute((unused))) { struct d_resource *res, *t; char *argv[20], answer[40]; pid_t *pids; struct d_option *opt; int rr, wtime, argc, i = 0; time_t start; int saved_stdin, saved_stdout, fd; struct sigaction so, sa; saved_stdin = -1; saved_stdout = -1; if (no_tty) { fprintf(stderr, "WARN: stdin/stdout is not a TTY; using /dev/console"); fprintf(stdout, "WARN: stdin/stdout is not a TTY; using /dev/console"); saved_stdin = dup(fileno(stdin)); if (saved_stdin == -1) perror("dup(stdin)"); saved_stdout = dup(fileno(stdout)); if (saved_stdin == -1) perror("dup(stdout)"); fd = open("/dev/console", O_RDONLY); if (fd == -1) perror("open('/dev/console, O_RDONLY)"); dup2(fd, fileno(stdin)); fd = open("/dev/console", O_WRONLY); if (fd == -1) perror("open('/dev/console, O_WRONLY)"); dup2(fd, fileno(stdout)); } sa.sa_handler = chld_sig_hand; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &sa, &so); pids = alloca(nr_resources * sizeof(pid_t)); /* alloca can not fail, it can "only" overflow the stack :) * but it needs to be initialized anyways! */ memset(pids, 0, nr_resources * sizeof(pid_t)); for_each_resource(res, t, config) { if (res->ignore) continue; if (is_drbd_top != res->stacked) continue; argc = 0; argv[NA(argc)] = drbdsetup; ssprintf(argv[NA(argc)], "%d", res->me->device_minor); argv[NA(argc)] = "wait-connect"; opt = res->startup_options; make_options(opt); argv[NA(argc)] = 0; m__system(argv, RETURN_PID, res->name, &pids[i++], NULL, NULL); } wtime = global_options.dialog_refresh ? : -1; start = time(0); for (i = 0; i < 10; i++) { // no string, but timeout rr = gets_timeout(pids, 0, 0, 1 * 1000); if (rr < 0) break; putchar('.'); fflush(stdout); check_exit_codes(pids); } if (rr == 0) { /* track a "yes", as well as ctrl-d and ctrl-c, * in case our tty is stuck in "raw" mode, and * we get it one character a time (-icanon) */ char yes_string[] = "yes\n"; char *yes_expect = yes_string; int ctrl_c_count = 0; int ctrl_d_count = 0; /* Just in case, if plymouth or usplash is running, * tell them to step aside. * Also try to force canonical tty mode. */ if (system("exec > /dev/null 2>&1; plymouth quit ; usplash_write QUIT ; " "stty echo icanon icrnl")) /* Ignore return value. Cannot do anything about it anyways. */; printf ("\n***************************************************************\n" " DRBD's startup script waits for the peer node(s) to appear.\n" " - In case this node was already a degraded cluster before the\n" " reboot the timeout is %s seconds. [degr-wfc-timeout]\n" " - If the peer was available before the reboot the timeout will\n" " expire after %s seconds. [wfc-timeout]\n" " (These values are for resource '%s'; 0 sec -> wait forever)\n", get_opt_val(config->startup_options, "degr-wfc-timeout", "0"), get_opt_val(config->startup_options, "wfc-timeout", "0"), config->name); printf(" To abort waiting enter 'yes' [ -- ]:"); do { printf("\e[s\e[31G[%4d]:\e[u", (int)(time(0) - start)); // Redraw sec. fflush(stdout); rr = gets_timeout(pids, answer, 40, wtime * 1000); check_exit_codes(pids); if (rr != 1) continue; /* If our tty is in "sane" or "canonical" mode, * we get whole lines. * If it still is in "raw" mode, even though we * tried to set ICANON above, possibly some other * "boot splash thingy" is in operation. * We may be lucky to get single characters. * If a sysadmin sees things stuck during boot, * I expect that ctrl-c or ctrl-d will be one * of the first things that are tried. * In raw mode, we get these characters directly. * But I want them to try that three times ;) */ if (answer[0] && answer[1] == 0) { if (answer[0] == '\3') ++ctrl_c_count; if (answer[0] == '\4') ++ctrl_d_count; if (yes_expect && answer[0] == *yes_expect) ++yes_expect; else if (answer[0] == '\n') yes_expect = yes_string; else yes_expect = NULL; } if (!strcmp(answer, "yes\n") || (yes_expect && *yes_expect == '\0') || ctrl_c_count >= 3 || ctrl_d_count >= 3) { kill_childs(pids); childs_running(pids, 0); check_exit_codes(pids); break; } printf(" To abort waiting enter 'yes' [ -- ]:"); } while (rr != -1); printf("\n"); } if (saved_stdin != -1) { dup2(saved_stdin, fileno(stdin)); dup2(saved_stdout, fileno(stdout)); } return 0; } static void print_cmds(int level) { size_t i; int j = 0; for (i = 0; i < ARRAY_SIZE(cmds); i++) { if (cmds[i].show_in_usage != level) continue; if (j++ % 2) { printf("%-35s\n", cmds[i].name); } else { printf(" %-35s", cmds[i].name); } } if (j % 2) printf("\n"); } static int hidden_cmds(struct d_resource *ignored __attribute((unused)), const char *ignored2 __attribute((unused))) { printf("\nThese additional commands might be useful for writing\n" "nifty shell scripts around drbdadm:\n\n"); print_cmds(2); printf("\nThese commands are used by the kernel part of DRBD to\n" "invoke user mode helper programs:\n\n"); print_cmds(3); printf ("\nThese commands ought to be used by experts and developers:\n\n"); print_cmds(4); printf("\n"); exit(0); } void print_usage_and_exit(const char *addinfo) { struct option *opt; printf("\nUSAGE: %s [OPTION...] [-- DRBDSETUP-OPTION...] COMMAND " "{all|RESOURCE...}\n\n" "OPTIONS:\n", progname); opt = admopt; while (opt->name) { if (opt->has_arg == required_argument) printf(" {--%s|-%c} val\n", opt->name, opt->val); else printf(" {--%s|-%c}\n", opt->name, opt->val); opt++; } printf("\nCOMMANDS:\n"); print_cmds(1); printf("\nVersion: " PACKAGE_VERSION " (api:%d)\n%s\n", API_VERSION, drbd_buildtag()); if (addinfo) printf("\n%s\n", addinfo); exit(E_USAGE); } void verify_ips(struct d_resource *res) { if (global_options.disable_ip_verification) return; if (dry_run == 1 || do_verify_ips == 0) return; if (res->ignore) return; if (res->stacked && !is_drbd_top) return; if (!have_ip(res->me->address_family, res->me->address)) { ENTRY e, *ep; e.key = e.data = ep = NULL; m_asprintf(&e.key, "%s:%s", res->me->address, res->me->port); hsearch_r(e, FIND, &ep, &global_htable); fprintf(stderr, "%s: in resource %s, on %s:\n\t" "IP %s not found on this host.\n", ep ? (char *)ep->data : res->config_file, res->name, names_to_str(res->me->on_hosts), res->me->address); if (INVALID_IP_IS_INVALID_CONF) config_valid = 0; } } static char *conf_file[] = { DRBD_CONFIG_DIR "/drbd-83.conf", DRBD_CONFIG_DIR "/drbd-82.conf", DRBD_CONFIG_DIR "/drbd-08.conf", DRBD_CONFIG_DIR "/drbd.conf", 0 }; int sanity_check_abs_cmd(char *cmd_name) { struct stat sb; if (stat(cmd_name, &sb)) { /* If stat fails, just ignore this sanity check, * we are still iterating over $PATH probably. */ return 0; } if (!(sb.st_mode & S_ISUID) || sb.st_mode & S_IXOTH || sb.st_gid == 0) { static int did_header = 0; if (!did_header) fprintf(stderr, "WARN:\n" " You are using the 'drbd-peer-outdater' as fence-peer program.\n" " If you use that mechanism the dopd heartbeat plugin program needs\n" " to be able to call drbdsetup and drbdmeta with root privileges.\n\n" " You need to fix this with these commands:\n"); did_header = 1; fprintf(stderr, " chgrp haclient %s\n" " chmod o-x %s\n" " chmod u+s %s\n\n", cmd_name, cmd_name, cmd_name); } return 1; } void sanity_check_cmd(char *cmd_name) { char *path, *pp, *c; char abs_path[100]; if (strchr(cmd_name, '/')) { sanity_check_abs_cmd(cmd_name); } else { path = pp = c = strdup(getenv("PATH")); while (1) { c = strchr(pp, ':'); if (c) *c = 0; snprintf(abs_path, 100, "%s/%s", pp, cmd_name); if (sanity_check_abs_cmd(abs_path)) break; if (!c) break; c++; if (!*c) break; pp = c; } free(path); } } /* if the config file is not readable by haclient, * dopd cannot work. * NOTE: we assume that any gid != 0 will be the group dopd will run as, * typically haclient. */ void sanity_check_conf(char *c) { struct stat sb; /* if we cannot stat the config file, * we have other things to worry about. */ if (stat(c, &sb)) return; /* permissions are funny: if it is world readable, * but not group readable, and it belongs to my group, * I am denied access. * For the file to be readable by dopd (hacluster:haclient), * it is not enough to be world readable. */ /* ok if world readable, and NOT group haclient (see NOTE above) */ if (sb.st_mode & S_IROTH && sb.st_gid == 0) return; /* ok if group readable, and group haclient (see NOTE above) */ if (sb.st_mode & S_IRGRP && sb.st_gid != 0) return; fprintf(stderr, "WARN:\n" " You are using the 'drbd-peer-outdater' as fence-peer program.\n" " If you use that mechanism the dopd heartbeat plugin program needs\n" " to be able to read the drbd.config file.\n\n" " You need to fix this with these commands:\n" " chgrp haclient %s\n" " chmod g+r %s\n\n", c, c); } void sanity_check_perm() { static int checked = 0; if (checked) return; sanity_check_cmd(drbdsetup); sanity_check_cmd(drbdmeta); sanity_check_conf(config_file); checked = 1; } void validate_resource(struct d_resource *res) { struct d_option *opt, *next; struct d_name *bpo; if (!res->protocol) { if (!common || !common->protocol) { fprintf(stderr, "%s:%d: in resource %s:\n\tprotocol definition missing.\n", res->config_file, res->start_line, res->name); config_valid = 0; } /* else: * may not have been expanded yet for "dump" subcommand */ } else { res->protocol[0] = toupper(res->protocol[0]); } /* there may be more than one "after" statement, * see commit 89cd0585 */ opt = res->sync_options; while ((opt = find_opt(opt, "after"))) { next = opt->next; if (res_by_name(opt->value) == NULL) { fprintf(stderr, "%s:%d: in resource %s:\n\tresource '%s' mentioned in " "'after' option is not known.\n", res->config_file, res->start_line, res->name, opt->value); /* Non-fatal if run from some script. * When deleting resources, it is an easily made * oversight to leave references to the deleted * resources in sync-after statements. Don't fail on * every pacemaker-induced action, as it would * ultimately lead to all nodes committing suicide. */ if (no_tty) res->sync_options = del_opt(res->sync_options, opt); else config_valid = 0; } opt = next; } if (res->ignore) return; if (!res->me) { fprintf(stderr, "%s:%d: in resource %s:\n\tmissing section 'on %s { ... }'.\n", res->config_file, res->start_line, res->name, nodeinfo.nodename); config_valid = 0; } // need to verify that in the discard-node-nodename options only known // nodenames are mentioned. if ((opt = find_opt(res->net_options, "after-sb-0pri"))) { if (!strncmp(opt->value, "discard-node-", 13)) { if (res->peer && !name_in_names(opt->value + 13, res->peer->on_hosts) && !name_in_names(opt->value + 13, res->me->on_hosts)) { fprintf(stderr, "%s:%d: in resource %s:\n\t" "the nodename in the '%s' option is " "not known.\n\t" "valid nodenames are: '%s %s'.\n", res->config_file, res->start_line, res->name, opt->value, names_to_str(res->me->on_hosts), names_to_str(res->peer->on_hosts)); config_valid = 0; } } } if ((opt = find_opt(res->handlers, "fence-peer"))) { if (strstr(opt->value, "drbd-peer-outdater")) sanity_check_perm(); } opt = find_opt(res->net_options, "allow-two-primaries"); if (name_in_names("both", res->become_primary_on) && opt == NULL) { fprintf(stderr, "%s:%d: in resource %s:\n" "become-primary-on is set to both, but allow-two-primaries " "is not set.\n", res->config_file, res->start_line, res->name); config_valid = 0; } if (!res->peer) set_peer_in_resource(res, 0); if (res->peer && ((res->me->proxy == NULL) != (res->peer->proxy == NULL))) { fprintf(stderr, "%s:%d: in resource %s:\n\t" "Either both 'on' sections must contain a proxy subsection, or none.\n", res->config_file, res->start_line, res->name); config_valid = 0; } for (bpo = res->become_primary_on; bpo; bpo = bpo->next) { if (res->peer && !name_in_names(bpo->name, res->me->on_hosts) && !name_in_names(bpo->name, res->peer->on_hosts) && strcmp(bpo->name, "both")) { fprintf(stderr, "%s:%d: in resource %s:\n\t" "become-primary-on contains '%s', which is not named with the 'on' sections.\n", res->config_file, res->start_line, res->name, bpo->name); config_valid = 0; } } } static void global_validate_maybe_expand_die_if_invalid(int expand) { struct d_resource *res, *tmp; for_each_resource(res, tmp, config) { validate_resource(res); if (!config_valid) exit(E_CONFIG_INVALID); if (expand) { convert_after_option(res); convert_discard_opt(res); } } } /* * returns a pointer to an malloced area that contains * an absolute, canonical, version of path. * aborts if any allocation or syscall fails. * return value should be free()d, once no longer needed. */ char *canonify_path(char *path) { int cwd_fd = -1; char *last_slash; char *tmp; char *that_wd; char *abs_path; if (!path || !path[0]) { fprintf(stderr, "cannot canonify an empty path\n"); exit(E_USAGE); } tmp = strdupa(path); last_slash = strrchr(tmp, '/'); if (last_slash) { *last_slash++ = '\0'; cwd_fd = open(".", O_RDONLY | O_CLOEXEC); if (cwd_fd < 0) { fprintf(stderr, "open(\".\") failed: %m\n"); exit(E_USAGE); } if (chdir(tmp)) { fprintf(stderr, "chdir(\"%s\") failed: %m\n", tmp); exit(E_USAGE); } } else { last_slash = tmp; } that_wd = getcwd(NULL, 0); if (!that_wd) { fprintf(stderr, "getcwd() failed: %m\n"); exit(E_USAGE); } if (!strcmp("/", that_wd)) m_asprintf(&abs_path, "/%s", last_slash); else m_asprintf(&abs_path, "%s/%s", that_wd, last_slash); free(that_wd); if (cwd_fd >= 0) { if (fchdir(cwd_fd) < 0) { fprintf(stderr, "fchdir() failed: %m\n"); exit(E_USAGE); } close(cwd_fd); } return abs_path; } void assign_command_names_from_argv0(char **argv) { /* in case drbdadm is called with an absolute or relative pathname * look for the drbdsetup binary in the same location, * otherwise, just let execvp sort it out... */ if ((progname = strrchr(argv[0], '/')) == 0) { progname = argv[0]; drbdsetup = strdup("drbdsetup-83"); drbdmeta = strdup("drbdmeta"); drbd_proxy_ctl = strdup("drbd-proxy-ctl"); } else { struct cmd_helper { char *name; char **var; }; struct cmd_helper helpers[] = { {"drbdsetup-83", &drbdsetup}, {"drbdmeta", &drbdmeta}, {"drbd-proxy-ctl", &drbd_proxy_ctl}, {NULL, NULL} }; size_t len_dir, l; struct cmd_helper *c; ++progname; len_dir = progname - argv[0]; for (c = helpers; c->name; ++c) { l = len_dir + strlen(c->name) + 1; *(c->var) = malloc(l); if (*(c->var)) { strncpy(*(c->var), argv[0], len_dir); strcpy(*(c->var) + len_dir, c->name); } } /* for pretty printing, truncate to basename */ argv[0] = progname; } } int parse_options(int argc, char **argv) { opterr = 1; optind = 0; while (1) { int c; c = getopt_long(argc, argv, make_optstring(admopt), admopt, 0); if (c == -1) break; switch (c) { case 'S': is_drbd_top = 1; break; case 'v': verbose++; break; case 'd': dry_run++; break; case 'c': if (!strcmp(optarg, "-")) { yyin = stdin; if (asprintf(&config_file, "STDIN") < 0) { fprintf(stderr, "asprintf(config_file): %m\n"); return 20; } config_from_stdin = 1; } else { yyin = fopen(optarg, "r"); if (!yyin) { fprintf(stderr, "Can not open '%s'.\n.", optarg); exit(E_EXEC_ERROR); } if (asprintf(&config_file, "%s", optarg) < 0) { fprintf(stderr, "asprintf(config_file): %m\n"); return 20; } } break; case 't': config_test = optarg; break; case 's': { char *pathes[2]; pathes[0] = optarg; pathes[1] = 0; find_drbdcmd(&drbdsetup, pathes); } break; case 'm': { char *pathes[2]; pathes[0] = optarg; pathes[1] = 0; find_drbdcmd(&drbdmeta, pathes); } break; case 'p': { char *pathes[2]; pathes[0] = optarg; pathes[1] = 0; find_drbdcmd(&drbd_proxy_ctl, pathes); } break; case 'n': { char *c; int shell_var_name_ok = 1; for (c = optarg; *c && shell_var_name_ok; c++) { switch (*c) { case 'a'...'z': case 'A'...'Z': case '0'...'9': case '_': break; default: shell_var_name_ok = 0; } } if (shell_var_name_ok) sh_varname = optarg; else fprintf(stderr, "ignored --sh-varname=%s: " "contains suspect characters, allowed set is [a-zA-Z0-9_]\n", optarg); } break; case 'f': force = 1; break; case 'V': printf("DRBDADM_BUILDTAG=%s\n", shell_escape(drbd_buildtag())); printf("DRBDADM_API_VERSION=%u\n", API_VERSION); printf("DRBD_KERNEL_VERSION_CODE=0x%06x\n", version_code_kernel()); printf("DRBDADM_VERSION_CODE=0x%06x\n", version_code_userland()); printf("DRBDADM_VERSION=%s\n", shell_escape(PACKAGE_VERSION)); exit(0); break; case 'P': connect_to_host = optarg; break; case '?': /* commented out, since opterr=1 * fprintf(stderr,"Unknown option %s\n",argv[optind-1]); */ fprintf(stderr, "try '%s help'\n", progname); return 20; break; } } return 0; } struct adm_cmd *find_cmd(char *cmdname) { struct adm_cmd *cmd = NULL; unsigned int i; if (!strcmp("hidden-commands", cmdname)) { // before parsing the configuration file... hidden_cmds(NULL, NULL); exit(0); } if (!strncmp("help", cmdname, 5)) print_usage_and_exit(NULL); /* R_PRIMARY / R_SECONDARY is not a state, but a role. Whatever that * means, actually. But anyways, we decided to start using _role_ as * the terminus of choice, and deprecate "state". */ substitute_deprecated_cmd(&cmdname, "state", "role"); /* "outdate-peer" got renamed to fence-peer, * it is not required to actually outdate the peer, * depending on situation it may be sufficient to power-reset it * or do some other fencing action, or even call out to "meatware". * The name of the handler should not imply something that is not done. */ substitute_deprecated_cmd(&cmdname, "outdate-peer", "fence-peer"); for (i = 0; i < ARRAY_SIZE(cmds); i++) { if (!strcmp(cmds[i].name, cmdname)) { cmd = cmds + i; break; } } return cmd; } char *config_file_from_arg(char *arg) { char *f; int minor = minor_by_id(arg); if (minor < 0) { /* this is expected, if someone wants to test the configured * handlers from the command line, using resource names */ fprintf(stderr, "Couldn't find minor from id %s, " "expecting minor- as id. " "Trying default config files.\n", arg); return NULL; } f = lookup_minor(minor); if (!f) { fprintf(stderr, "Don't know which config file belongs to minor %d, " "trying default ones...\n", minor); } else { yyin = fopen(f, "r"); if (yyin == NULL) { fprintf(stderr, "Couldn't open file %s for reading, reason: %m\n" "trying default config file...\n", config_file); } } return f; } void assign_default_config_file(void) { int i; for (i = 0; conf_file[i]; i++) { yyin = fopen(conf_file[i], "r"); if (yyin) { config_file = conf_file[i]; break; } } if (!config_file) { fprintf(stderr, "Can not open '%s': %m\n", conf_file[i - 1]); exit(E_CONFIG_INVALID); } } void count_resources_or_die(void) { int m, mc = global_options.minor_count; struct d_resource *res, *tmp; highest_minor = 0; for_each_resource(res, tmp, config) { if (res->ignore) continue; m = res->me->device_minor; if (m > highest_minor) highest_minor = m; nr_resources++; if (res->stacked) nr_stacked++; else if (res->ignore) nr_ignore++; else nr_normal++; } // Just for the case that minor_of_res() returned 0 for all devices. if (nr_resources > (highest_minor + 1)) highest_minor = nr_resources - 1; if (mc && mc < (highest_minor + 1)) { fprintf(stderr, "The highest minor you have in your config is %d" "but a minor_count of %d in your config!\n", highest_minor, mc); exit(E_USAGE); } } void die_if_no_resources(void) { if (!is_drbd_top && nr_ignore > 0 && nr_normal == 0) { fprintf(stderr, "WARN: no normal resources defined for this host (%s)!?\n" "Misspelled name of the local machine with the 'on' keyword ?\n", nodeinfo.nodename); exit(E_USAGE); } if (!is_drbd_top && nr_normal == 0) { fprintf(stderr, "WARN: no normal resources defined for this host (%s)!?\n", nodeinfo.nodename); exit(E_USAGE); } if (is_drbd_top && nr_stacked == 0) { fprintf(stderr, "WARN: nothing stacked for this host (%s), " "nothing to do in stacked mode!\n", nodeinfo.nodename); exit(E_USAGE); } } void print_dump_xml_header(void) { printf("\n", config_save); ++indent; dump_global_info_xml(); dump_common_info_xml(); } void print_dump_header(void) { printf("# %s\n", config_save); dump_global_info(); dump_common_info(); } int main(int argc, char **argv) { size_t i; int rv = 0, r; struct adm_cmd *cmd = NULL; struct d_resource *res, *tmp; char *env_drbd_nodename = NULL; int is_dump_xml; int is_dump; initialize_err(); yyin = NULL; uname(&nodeinfo); /* FIXME maybe fold to lower case ? */ no_tty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout))); env_drbd_nodename = getenv("__DRBD_NODE__"); if (env_drbd_nodename && *env_drbd_nodename) { strncpy(nodeinfo.nodename, env_drbd_nodename, sizeof(nodeinfo.nodename) - 1); nodeinfo.nodename[sizeof(nodeinfo.nodename) - 1] = 0; fprintf(stderr, "\n" " found __DRBD_NODE__ in environment\n" " PRETENDING that I am >>%s<<\n\n", nodeinfo.nodename); } assign_command_names_from_argv0(argv); if (argc == 1) print_usage_and_exit("missing arguments"); // arguments missing. if (drbdsetup == NULL || drbdmeta == NULL || drbd_proxy_ctl == NULL) { fprintf(stderr, "could not strdup argv[0].\n"); exit(E_EXEC_ERROR); } if (!getenv("DRBD_DONT_WARN_ON_VERSION_MISMATCH")) warn_on_version_mismatch(); rv = parse_options(argc, argv); if (rv) return rv; /* store everything before the command name as pass through option/argument */ while (optind < argc) { cmd = find_cmd(argv[optind]); if (cmd) break; setup_opts[soi++] = argv[optind++]; } if (optind == argc) print_usage_and_exit(NULL); if (cmd == NULL) { fprintf(stderr, "Unknown command '%s'.\n", argv[optind]); exit(E_USAGE); } if (config_test && !cmd->test_config) { fprintf(stderr, "The --config-to-test (-t) option is only allowed " "with the dump and sh-nop commands\n"); exit(E_USAGE); } do_verify_ips = cmd->verify_ips; optind++; is_dump_xml = (cmd->function == adm_dump_xml); is_dump = (is_dump_xml || cmd->function == adm_dump); /* remaining argv are expected to be resource names * optind == argc: no resourcenames given. * optind + 1 == argc: exactly one resource name (or "all") given * optind + 1 < argc: multiple resource names given. */ if (optind == argc) { if (is_dump) all_resources = 1; else if (cmd->res_name_required) print_usage_and_exit("missing resourcename arguments"); } else if (optind + 1 < argc) { if (!cmd->res_name_required) fprintf(stderr, "this command will ignore resource names!\n"); else if (cmd->use_cached_config_file) fprintf(stderr, "You should not use this command with multiple resources!\n"); } if (!config_file && cmd->use_cached_config_file) config_file = config_file_from_arg(argv[optind]); if (!config_file) /* may exit if no config file can be used! */ assign_default_config_file(); /* for error-reporting reasons config_file may be re-assigned by adm_adjust, * we need the current value for register_minor, though. * save that. */ if (config_from_stdin) config_save = config_file; else config_save = canonify_path(config_file); my_parse(); if (config_test) { char *saved_config_file = config_file; char *saved_config_save = config_save; config_file = config_test; config_save = canonify_path(config_test); fclose(yyin); yyin = fopen(config_test, "r"); if (!yyin) { fprintf(stderr, "Can not open '%s'.\n.", config_test); exit(E_EXEC_ERROR); } my_parse(); config_file = saved_config_file; config_save = saved_config_save; } if (!config_valid) exit(E_CONFIG_INVALID); post_parse(config, cmd->is_proxy_cmd ? MATCH_ON_PROXY : 0); if (!is_dump || dry_run || verbose) expand_common(); if (is_dump || dry_run || config_from_stdin) do_register_minor = 0; count_resources_or_die(); if (cmd->uc_dialog) uc_node(global_options.usage_count); if (cmd->res_name_required) { if (config == NULL) { fprintf(stderr, "no resources defined!\n"); exit(E_USAGE); } global_validate_maybe_expand_die_if_invalid(!is_dump); if (optind == argc || !strcmp(argv[optind], "all")) { /* either no resource arguments at all, * but command is dump / dump-xml, so implicit "all", * or an explicit "all" argument is given */ all_resources = 1; if (!is_dump || !force) die_if_no_resources(); /* verify ips first, for all of them */ for_each_resource(res, tmp, config) { verify_ips(res); } if (!config_valid) exit(E_CONFIG_INVALID); if (is_dump_xml) print_dump_xml_header(); else if (is_dump) print_dump_header(); for_each_resource(res, tmp, config) { if (!is_dump && res->ignore) continue; if (!is_dump && is_drbd_top != res->stacked) continue; int r = call_cmd(cmd, res, EXIT_ON_FAIL); /* does exit for r >= 20! */ /* this super positioning of return values is soo ugly * anyone any better idea? */ if (r > rv) rv = r; } if (is_dump_xml) { --indent; printf("\n"); } } else { /* explicit list of resources to work on */ for (i = optind; (int)i < argc; i++) { res = res_by_name(argv[i]); if (!res) res = res_by_minor(argv[i]); if (!res) { fprintf(stderr, "'%s' not defined in your config.\n", argv[i]); exit(E_USAGE); } if (res->ignore && !is_dump) { fprintf(stderr, "'%s' ignored, since this host (%s) is not mentioned with an 'on' keyword.\n", res->name, nodeinfo.nodename); rv = E_USAGE; continue; } if (is_drbd_top != res->stacked && !is_dump) { fprintf(stderr, "'%s' is a %s resource, and not available in %s mode.\n", res->name, res-> stacked ? "stacked" : "normal", is_drbd_top ? "stacked" : "normal"); rv = E_USAGE; continue; } verify_ips(res); if (!is_dump && !config_valid) exit(E_CONFIG_INVALID); r = call_cmd(cmd, res, EXIT_ON_FAIL); /* does exit for rv >= 20! */ if (r > rv) rv = r; } } } else { // Commands which do not need a resource name /* no call_cmd, as that implies register_minor, * which does not make sense for resource independent commands */ rv = cmd->function(config, cmd->name); if (rv >= 10) { /* why do we special case the "generic sh-*" commands? */ fprintf(stderr, "command %s exited with code %d\n", cmd->name, rv); exit(rv); } } /* do we really have to bitor the exit code? * it is even only a Boolean value in this case! */ r = run_dcmds(); if (r > rv) rv = r; free_config(config); return rv; } void yyerror(char *text) { fprintf(stderr, "%s:%d: %s\n", config_file, line, text); exit(E_SYNTAX); } drbd-utils-8.9.10/user/v83/drbdsetup.c0000644000175000017500000023157213002133653017322 0ustar apoikosapoikos/* drbdsetup.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. Copyright (C) 1999-2008, Philipp Reisner . Copyright (C) 2002-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define __bitwise /* Build-workaround for broken RHEL4 kernels (2.6.9_78.0.1) */ #include #include #include #include #include #include #include "unaligned.h" #include "drbdtool_common.h" #ifndef __CONNECTOR_H #error "You need to set KDIR while building drbdsetup." #endif #ifndef AF_INET_SDP #define AF_INET_SDP 27 #define PF_INET_SDP AF_INET_SDP #endif enum usage_type { BRIEF, FULL, XML, }; struct drbd_tag_list { struct nlmsghdr *nl_header; struct cn_msg *cn_header; struct drbd_nl_cfg_req* drbd_p_header; unsigned short *tag_list_start; unsigned short *tag_list_cpos; int tag_size; }; struct drbd_argument { const char* name; const enum drbd_tags tag; int (*convert_function)(struct drbd_argument *, struct drbd_tag_list *, char *); }; struct drbd_option { const char* name; const char short_name; const enum drbd_tags tag; int (*convert_function)(struct drbd_option *, struct drbd_tag_list *, char *); void (*show_function)(struct drbd_option *,unsigned short*); int (*usage_function)(struct drbd_option *, char*, int); void (*xml_function)(struct drbd_option *); union { struct { const long long min; const long long max; const long long def; const unsigned char unit_prefix; const char* unit; } numeric_param; // for conv_numeric struct { const char** handler_names; const int number_of_handlers; const int def; } handler_param; // conv_handler }; }; struct drbd_cmd { const char* cmd; const int packet_id; int (*function)(struct drbd_cmd *, unsigned, int, char **); void (*usage)(struct drbd_cmd *, enum usage_type); union { struct { struct drbd_argument *args; struct drbd_option *options; } cp; // for generic_config_cmd, config_usage struct { int (*show_function)(struct drbd_cmd *, unsigned, unsigned short* ); } gp; // for generic_get_cmd, get_usage struct { struct option *options; int (*proc_event)(unsigned int, int, struct drbd_nl_cfg_reply *); } ep; // for events_cmd, events_usage }; }; // Connector functions #define NL_TIME (COMM_TIMEOUT*1000) static int open_cn(); static int send_cn(int sk_nl, struct nlmsghdr* nl_hdr, int size); static int receive_cn(int sk_nl, struct nlmsghdr* nl_hdr, int size, int timeout_ms); static int call_drbd(int sk_nl, struct drbd_tag_list *tl, struct nlmsghdr* nl_hdr, int size, int timeout_ms); static void close_cn(int sk_nl); // other functions static int get_af_ssocks(int warn); static void print_command_usage(int i, const char *addinfo, enum usage_type); // command functions static int generic_config_cmd(struct drbd_cmd *cm, unsigned minor, int argc, char **argv); static int down_cmd(struct drbd_cmd *cm, unsigned minor, int argc, char **argv); static int generic_get_cmd(struct drbd_cmd *cm, unsigned minor, int argc, char **argv); static int events_cmd(struct drbd_cmd *cm, unsigned minor, int argc,char **argv); // usage functions static void config_usage(struct drbd_cmd *cm, enum usage_type); static void get_usage(struct drbd_cmd *cm, enum usage_type); static void events_usage(struct drbd_cmd *cm, enum usage_type); // sub usage functions for config_usage static int numeric_opt_usage(struct drbd_option *option, char* str, int strlen); static int handler_opt_usage(struct drbd_option *option, char* str, int strlen); static int bit_opt_usage(struct drbd_option *option, char* str, int strlen); static int string_opt_usage(struct drbd_option *option, char* str, int strlen); // sub usage function for config_usage as xml static void numeric_opt_xml(struct drbd_option *option); static void handler_opt_xml(struct drbd_option *option); static void bit_opt_xml(struct drbd_option *option); static void string_opt_xml(struct drbd_option *option); // sub commands for generic_get_cmd static int show_scmd(struct drbd_cmd *cm, unsigned minor, unsigned short *rtl); static int role_scmd(struct drbd_cmd *cm, unsigned minor, unsigned short *rtl); static int status_xml_scmd(struct drbd_cmd *cm, unsigned minor, unsigned short *rtl); static int sh_status_scmd(struct drbd_cmd *cm, unsigned minor, unsigned short *rtl); static int cstate_scmd(struct drbd_cmd *cm, unsigned minor, unsigned short *rtl); static int dstate_scmd(struct drbd_cmd *cm, unsigned minor, unsigned short *rtl); static int uuids_scmd(struct drbd_cmd *cm, unsigned minor, unsigned short *rtl); static int lk_bdev_scmd(struct drbd_cmd *cm, unsigned minor, unsigned short *rtl); // convert functions for arguments static int conv_block_dev(struct drbd_argument *ad, struct drbd_tag_list *tl, char* arg); static int conv_md_idx(struct drbd_argument *ad, struct drbd_tag_list *tl, char* arg); static int conv_address(struct drbd_argument *ad, struct drbd_tag_list *tl, char* arg); static int conv_protocol(struct drbd_argument *ad, struct drbd_tag_list *tl, char* arg); // convert functions for options static int conv_numeric(struct drbd_option *od, struct drbd_tag_list *tl, char* arg); static int conv_sndbuf(struct drbd_option *od, struct drbd_tag_list *tl, char* arg); static int conv_handler(struct drbd_option *od, struct drbd_tag_list *tl, char* arg); static int conv_bit(struct drbd_option *od, struct drbd_tag_list *tl, char* arg); static int conv_string(struct drbd_option *od, struct drbd_tag_list *tl, char* arg); // show functions for options (used by show_scmd) static void show_numeric(struct drbd_option *od, unsigned short* tp); static void show_handler(struct drbd_option *od, unsigned short* tp); static void show_bit(struct drbd_option *od, unsigned short* tp); static void show_string(struct drbd_option *od, unsigned short* tp); // sub functions for events_cmd static int print_broadcast_events(unsigned int seq, int, struct drbd_nl_cfg_reply *reply); static int w_connected_state(unsigned int seq, int, struct drbd_nl_cfg_reply *reply); static int w_synced_state(unsigned int seq, int, struct drbd_nl_cfg_reply *reply); const char *on_error[] = { [EP_PASS_ON] = "pass_on", [EP_CALL_HELPER] = "call-local-io-error", [EP_DETACH] = "detach", }; const char *fencing_n[] = { [FP_DONT_CARE] = "dont-care", [FP_RESOURCE] = "resource-only", [FP_STONITH] = "resource-and-stonith", }; const char *asb0p_n[] = { [ASB_DISCONNECT] = "disconnect", [ASB_DISCARD_YOUNGER_PRI] = "discard-younger-primary", [ASB_DISCARD_OLDER_PRI] = "discard-older-primary", [ASB_DISCARD_ZERO_CHG] = "discard-zero-changes", [ASB_DISCARD_LEAST_CHG] = "discard-least-changes", [ASB_DISCARD_LOCAL] = "discard-local", [ASB_DISCARD_REMOTE] = "discard-remote" }; const char *asb1p_n[] = { [ASB_DISCONNECT] = "disconnect", [ASB_CONSENSUS] = "consensus", [ASB_VIOLENTLY] = "violently-as0p", [ASB_DISCARD_SECONDARY] = "discard-secondary", [ASB_CALL_HELPER] = "call-pri-lost-after-sb" }; const char *asb2p_n[] = { [ASB_DISCONNECT] = "disconnect", [ASB_VIOLENTLY] = "violently-as0p", [ASB_CALL_HELPER] = "call-pri-lost-after-sb" }; const char *rrcf_n[] = { [ASB_DISCONNECT] = "disconnect", [ASB_VIOLENTLY] = "violently", [ASB_CALL_HELPER] = "call-pri-lost" }; const char *on_no_data_n[] = { [OND_IO_ERROR] = "io-error", [OND_SUSPEND_IO] = "suspend-io" }; const char *on_congestion_n[] = { [OC_BLOCK] = "block", [OC_PULL_AHEAD] = "pull-ahead", [OC_DISCONNECT] = "disconnect" }; struct option wait_cmds_options[] = { { "wfc-timeout",required_argument, 0, 't' }, { "degr-wfc-timeout",required_argument,0,'d'}, { "outdated-wfc-timeout",required_argument,0,'o'}, { "wait-after-sb",no_argument,0,'w'}, { 0, 0, 0, 0 } }; #define EN(N,U,UN) \ conv_numeric, show_numeric, numeric_opt_usage, numeric_opt_xml, \ { .numeric_param = { DRBD_ ## N ## _MIN, DRBD_ ## N ## _MAX, \ DRBD_ ## N ## _DEF ,U,UN } } #define EN_sndbuf(N,U,UN) \ conv_sndbuf, show_numeric, numeric_opt_usage, numeric_opt_xml, \ { .numeric_param = { DRBD_ ## N ## _MIN, DRBD_ ## N ## _MAX, \ DRBD_ ## N ## _DEF ,U,UN } } #define EH(N,D) \ conv_handler, show_handler, handler_opt_usage, handler_opt_xml, \ { .handler_param = { N, ARRAY_SIZE(N), \ DRBD_ ## D ## _DEF } } #define EB conv_bit, show_bit, bit_opt_usage, bit_opt_xml, { } #define ES conv_string, show_string, string_opt_usage, string_opt_xml, { } #define CLOSE_OPTIONS { NULL,0,0,NULL,NULL,NULL, NULL, { } } #define F_CONFIG_CMD generic_config_cmd, config_usage #define F_GET_CMD generic_get_cmd, get_usage #define F_EVENTS_CMD events_cmd, events_usage struct drbd_cmd commands[] = { {"primary", P_primary, F_CONFIG_CMD, {{ NULL, (struct drbd_option[]) { { "overwrite-data-of-peer",'o',T_primary_force, EB }, /* legacy name */ { "force",'f', T_primary_force, EB }, CLOSE_OPTIONS }} }, }, {"secondary", P_secondary, F_CONFIG_CMD, {{NULL, NULL}} }, {"disk", P_disk_conf, F_CONFIG_CMD, {{ (struct drbd_argument[]) { { "lower_dev", T_backing_dev, conv_block_dev }, { "meta_data_dev", T_meta_dev, conv_block_dev }, { "meta_data_index", T_meta_dev_idx, conv_md_idx }, { NULL, 0, NULL}, }, (struct drbd_option[]) { { "size",'d', T_disk_size, EN(DISK_SIZE_SECT,'s',"bytes") }, { "on-io-error",'e', T_on_io_error, EH(on_error,ON_IO_ERROR) }, { "fencing",'f', T_fencing, EH(fencing_n,FENCING) }, { "use-bmbv",'b', T_use_bmbv, EB }, { "no-disk-barrier",'a',T_no_disk_barrier,EB }, { "no-disk-flushes",'i',T_no_disk_flush,EB }, { "no-disk-drain",'D', T_no_disk_drain,EB }, { "no-md-flushes",'m', T_no_md_flush, EB }, { "max-bio-bvecs",'s', T_max_bio_bvecs,EN(MAX_BIO_BVECS,1,NULL) }, { "disk-timeout",'t', T_disk_timeout, EN(DISK_TIMEOUT,1,"1/10 seconds") }, CLOSE_OPTIONS }} }, }, {"detach", P_detach, F_CONFIG_CMD, {{NULL, (struct drbd_option[]) { { "force",'f', T_detach_force, EB }, CLOSE_OPTIONS }} }, }, {"net", P_net_conf, F_CONFIG_CMD, {{ (struct drbd_argument[]) { { "[af:]local_addr[:port]",T_my_addr, conv_address }, { "[af:]remote_addr[:port]",T_peer_addr,conv_address }, { "protocol", T_wire_protocol,conv_protocol }, { NULL, 0, NULL}, }, (struct drbd_option[]) { { "timeout",'t', T_timeout, EN(TIMEOUT,1,"1/10 seconds") }, { "max-epoch-size",'e',T_max_epoch_size,EN(MAX_EPOCH_SIZE,1,NULL) }, { "max-buffers",'b', T_max_buffers, EN(MAX_BUFFERS,1,NULL) }, { "unplug-watermark",'u',T_unplug_watermark, EN(UNPLUG_WATERMARK,1,NULL) }, { "connect-int",'c', T_try_connect_int, EN(CONNECT_INT,1,"seconds") }, { "ping-int",'i', T_ping_int, EN(PING_INT,1,"seconds") }, { "sndbuf-size",'S', T_sndbuf_size, EN_sndbuf(SNDBUF_SIZE,1,"bytes") }, { "rcvbuf-size",'r', T_rcvbuf_size, EN_sndbuf(RCVBUF_SIZE,1,"bytes") }, { "ko-count",'k', T_ko_count, EN(KO_COUNT,1,NULL) }, { "allow-two-primaries",'m',T_two_primaries, EB }, { "cram-hmac-alg",'a', T_cram_hmac_alg, ES }, { "shared-secret",'x', T_shared_secret, ES }, { "after-sb-0pri",'A', T_after_sb_0p,EH(asb0p_n,AFTER_SB_0P) }, { "after-sb-1pri",'B', T_after_sb_1p,EH(asb1p_n,AFTER_SB_1P) }, { "after-sb-2pri",'C', T_after_sb_2p,EH(asb2p_n,AFTER_SB_2P) }, { "always-asbp",'P', T_always_asbp, EB }, { "rr-conflict",'R', T_rr_conflict,EH(rrcf_n,RR_CONFLICT) }, { "ping-timeout",'p', T_ping_timeo, EN(PING_TIMEO,1,"1/10 seconds") }, { "discard-my-data",'D', T_want_lose, EB }, { "data-integrity-alg",'d', T_integrity_alg, ES }, { "no-tcp-cork",'o', T_no_cork, EB }, { "dry-run",'n', T_dry_run, EB }, { "on-congestion", 'g', T_on_congestion, EH(on_congestion_n,ON_CONGESTION) }, { "congestion-fill", 'f', T_cong_fill, EN(CONG_FILL,'s',"byte") }, { "congestion-extents", 'h', T_cong_extents, EN(CONG_EXTENTS,1,NULL) }, CLOSE_OPTIONS }} }, }, {"disconnect", P_disconnect, F_CONFIG_CMD, {{NULL, (struct drbd_option[]) { { "force", 'F', T_force, EB }, CLOSE_OPTIONS }} }, }, {"resize", P_resize, F_CONFIG_CMD, {{ NULL, (struct drbd_option[]) { { "size",'s',T_resize_size, EN(DISK_SIZE_SECT,'s',"bytes") }, { "assume-peer-has-space",'f',T_resize_force, EB }, { "assume-clean", 'c', T_no_resync, EB }, CLOSE_OPTIONS }} }, }, {"syncer", P_syncer_conf, F_CONFIG_CMD, {{ NULL, (struct drbd_option[]) { { "rate",'r',T_rate, EN(RATE,'k',"bytes/second") }, { "after",'a',T_after, EN(AFTER,1,NULL) }, { "al-extents",'e',T_al_extents, EN(AL_EXTENTS,1,NULL) }, { "csums-alg", 'C',T_csums_alg, ES }, { "verify-alg", 'v',T_verify_alg, ES }, { "cpu-mask",'c',T_cpu_mask, ES }, { "use-rle",'R',T_use_rle, EB }, { "on-no-data-accessible",'n', T_on_no_data, EH(on_no_data_n,ON_NO_DATA) }, { "c-plan-ahead", 'p', T_c_plan_ahead, EN(C_PLAN_AHEAD,1,"1/10 seconds") }, { "c-delay-target", 'd', T_c_delay_target, EN(C_DELAY_TARGET,1,"1/10 seconds") }, { "c-fill-target", 's', T_c_fill_target, EN(C_FILL_TARGET,'s',"bytes") }, { "c-max-rate", 'M', T_c_max_rate, EN(C_MAX_RATE,'k',"bytes/second") }, { "c-min-rate", 'm', T_c_min_rate, EN(C_MIN_RATE,'k',"bytes/second") }, CLOSE_OPTIONS }} }, }, {"new-current-uuid", P_new_c_uuid, F_CONFIG_CMD, {{NULL, (struct drbd_option[]) { { "clear-bitmap",'c',T_clear_bm, EB }, CLOSE_OPTIONS }} }, }, {"invalidate", P_invalidate, F_CONFIG_CMD, {{ NULL, NULL }} }, {"invalidate-remote", P_invalidate_peer, F_CONFIG_CMD, {{NULL, NULL}} }, {"pause-sync", P_pause_sync, F_CONFIG_CMD, {{ NULL, NULL }} }, {"resume-sync", P_resume_sync, F_CONFIG_CMD, {{ NULL, NULL }} }, {"suspend-io", P_suspend_io, F_CONFIG_CMD, {{ NULL, NULL }} }, {"resume-io", P_resume_io, F_CONFIG_CMD, {{ NULL, NULL }} }, {"outdate", P_outdate, F_CONFIG_CMD, {{ NULL, NULL }} }, {"verify", P_start_ov, F_CONFIG_CMD, {{ NULL, (struct drbd_option[]) { { "start",'s',T_start_sector, EN(DISK_SIZE_SECT,'s',"bytes") }, { "stop",'S',T_stop_sector, EN(DISK_SIZE_SECT,'s',"bytes") }, CLOSE_OPTIONS }} }, }, {"down", 0, down_cmd, get_usage, { {NULL, NULL }} }, {"state", P_get_state, F_GET_CMD, { .gp={ role_scmd} } }, {"role", P_get_state, F_GET_CMD, { .gp={ role_scmd} } }, {"status", P_get_state, F_GET_CMD, {.gp={ status_xml_scmd } } }, {"sh-status", P_get_state, F_GET_CMD, {.gp={ sh_status_scmd } } }, {"cstate", P_get_state, F_GET_CMD, {.gp={ cstate_scmd} } }, {"dstate", P_get_state, F_GET_CMD, {.gp={ dstate_scmd} } }, {"show-gi", P_get_uuids, F_GET_CMD, {.gp={ uuids_scmd} }}, {"get-gi", P_get_uuids, F_GET_CMD, {.gp={ uuids_scmd} } }, {"show", P_get_config, F_GET_CMD, {.gp={ show_scmd} } }, {"check-resize", P_get_config, F_GET_CMD, {.gp={ lk_bdev_scmd} } }, {"events", 0, F_EVENTS_CMD, { .ep = { (struct option[]) { { "unfiltered", no_argument, 0, 'u' }, { "all-devices",no_argument, 0, 'a' }, { 0, 0, 0, 0 } }, print_broadcast_events } } }, {"wait-connect", 0, F_EVENTS_CMD, { .ep = { wait_cmds_options, w_connected_state } } }, {"wait-sync", 0, F_EVENTS_CMD, { .ep = { wait_cmds_options, w_synced_state } } }, }; #define OTHER_ERROR 900 #define EM(C) [ C - ERR_CODE_BASE ] /* The EM(123) are used for old error messages. */ static const char *error_messages[] = { EM(NO_ERROR) = "No further Information available.", EM(ERR_LOCAL_ADDR) = "Local address(port) already in use.", EM(ERR_PEER_ADDR) = "Remote address(port) already in use.", EM(ERR_OPEN_DISK) = "Can not open backing device.", EM(ERR_OPEN_MD_DISK) = "Can not open meta device.", EM(106) = "Lower device already in use.", EM(ERR_DISK_NOT_BDEV) = "Lower device is not a block device.", EM(ERR_MD_NOT_BDEV) = "Meta device is not a block device.", EM(109) = "Open of lower device failed.", EM(110) = "Open of meta device failed.", EM(ERR_DISK_TOO_SMALL) = "Low.dev. smaller than requested DRBD-dev. size.", EM(ERR_MD_DISK_TOO_SMALL) = "Meta device too small.", EM(113) = "You have to use the disk command first.", EM(ERR_BDCLAIM_DISK) = "Lower device is already claimed. This usually means it is mounted.", EM(ERR_BDCLAIM_MD_DISK) = "Meta device is already claimed. This usually means it is mounted.", EM(ERR_MD_IDX_INVALID) = "Lower device / meta device / index combination invalid.", EM(117) = "Currently we only support devices up to 3.998TB.\n" "(up to 2TB in case you do not have CONFIG_LBD set)\n" "Contact office@linbit.com, if you need more.", EM(ERR_IO_MD_DISK) = "IO error(s) occurred during initial access to meta-data.\n", EM(ERR_MD_INVALID) = "No valid meta-data signature found.\n\n" "\t==> Use 'drbdadm create-md res' to initialize meta-data area. <==\n", EM(ERR_AUTH_ALG) = "The 'cram-hmac-alg' you specified is not known in " "the kernel. (Maybe you need to modprobe it, or modprobe hmac?)", EM(ERR_AUTH_ALG_ND) = "The 'cram-hmac-alg' you specified is not a digest.", EM(ERR_NOMEM) = "kmalloc() failed. Out of memory?", EM(ERR_DISCARD_IMPOSSIBLE) = "--discard-my-data not allowed when primary.", EM(ERR_DISK_CONFIGURED) = "Device is attached to a disk (use detach first)", EM(ERR_NET_CONFIGURED) = "Device has a net-config (use disconnect first)", EM(ERR_MANDATORY_TAG) = "UnknownMandatoryTag", EM(ERR_MINOR_INVALID) = "Device minor not allocated", EM(128) = "Resulting device state would be invalid", EM(ERR_INTR) = "Interrupted by Signal", EM(ERR_RESIZE_RESYNC) = "Resize not allowed during resync.", EM(ERR_NO_PRIMARY) = "Need one Primary node to resize.", EM(ERR_SYNC_AFTER) = "The sync-after minor number is invalid", EM(ERR_SYNC_AFTER_CYCLE) = "This would cause a sync-after dependency cycle", EM(ERR_PAUSE_IS_SET) = "Sync-pause flag is already set", EM(ERR_PAUSE_IS_CLEAR) = "Sync-pause flag is already cleared", EM(136) = "Disk state is lower than outdated", EM(ERR_PACKET_NR) = "Kernel does not know how to handle your request.\n" "Maybe API_VERSION mismatch?", EM(ERR_NO_DISK) = "Device does not have a disk-config", EM(ERR_NOT_PROTO_C) = "Protocol C required", EM(ERR_NOMEM_BITMAP) = "vmalloc() failed. Out of memory?", EM(ERR_INTEGRITY_ALG) = "The 'data-integrity-alg' you specified is not known in " "the kernel. (Maybe you need to modprobe it, or modprobe hmac?)", EM(ERR_INTEGRITY_ALG_ND) = "The 'data-integrity-alg' you specified is not a digest.", EM(ERR_CPU_MASK_PARSE) = "Invalid cpu-mask.", EM(ERR_VERIFY_ALG) = "VERIFYAlgNotAvail", EM(ERR_VERIFY_ALG_ND) = "VERIFYAlgNotDigest", EM(ERR_VERIFY_RUNNING) = "Can not change verify-alg while online verify runs", EM(ERR_DATA_NOT_CURRENT) = "Can only attach to the data we lost last (see kernel log).", EM(ERR_CONNECTED) = "Need to be StandAlone", EM(ERR_CSUMS_ALG) = "CSUMSAlgNotAvail", EM(ERR_CSUMS_ALG_ND) = "CSUMSAlgNotDigest", EM(ERR_CSUMS_RESYNC_RUNNING) = "Can not change csums-alg while resync is in progress", EM(ERR_PERM) = "Permission denied. CAP_SYS_ADMIN necessary", EM(ERR_NEED_APV_93) = "Protocol version 93 required to use --assume-clean", EM(ERR_STONITH_AND_PROT_A) = "Fencing policy resource-and-stonith only with prot B or C allowed", EM(ERR_CONG_NOT_PROTO_A) = "on-congestion policy pull-ahead only with prot A allowed", EM(ERR_PIC_AFTER_DEP) = "Sync-pause flag is already cleared.\n" "Note: Resync pause caused by a local sync-after dependency.", EM(ERR_PIC_PEER_DEP) = "Sync-pause flag is already cleared.\n" "Note: Resync pause caused by the peer node.", }; #define MAX_ERROR (sizeof(error_messages)/sizeof(*error_messages)) const char * error_to_string(int err_no) { const unsigned int idx = err_no - ERR_CODE_BASE; if (idx >= MAX_ERROR) return "Unknown... maybe API_VERSION mismatch?"; return error_messages[idx]; } #undef MAX_ERROR char *cmdname = NULL; /* "drbdsetup" for reporting in usage etc. */ char *devname = NULL; /* "/dev/drbd12" for reporting in print_config_error */ char *resname = NULL; /* for pretty printing in "status" only, taken from environment variable DRBD_RESOURCE */ int debug_dump_argv = 0; /* enabled by setting DRBD_DEBUG_DUMP_ARGV in the environment */ int lock_fd = -1; unsigned int cn_idx; static int dump_tag_list(unsigned short *tlc) { enum drbd_tags tag; unsigned int tag_nr; int len; int integer; char bit; uint64_t int64; const char* string; int found_unknown=0; while( (tag = *tlc++ ) != TT_END) { len = *tlc++; if(tag == TT_REMOVED) goto skip; tag_nr = tag_number(tag); if(tag_nrnl_header = malloc(NLMSG_SPACE( sizeof(struct cn_msg) + sizeof(struct drbd_nl_cfg_req) + size) ); tl->cn_header = NLMSG_DATA(tl->nl_header); tl->drbd_p_header = (struct drbd_nl_cfg_req*) tl->cn_header->data; tl->tag_list_start = tl->drbd_p_header->tag_list; tl->tag_list_cpos = tl->tag_list_start; tl->tag_size = size; return tl; } static void add_tag(struct drbd_tag_list *tl, short int tag, void *data, short int data_len) { if(data_len > tag_descriptions[tag_number(tag)].max_len) { fprintf(stderr, "The value for %s may only be %d byte long." " You requested %d.\n", tag_descriptions[tag_number(tag)].name, tag_descriptions[tag_number(tag)].max_len, data_len); exit(20); } if( (tl->tag_list_cpos - tl->tag_list_start) + data_len > tl->tag_size ) { fprintf(stderr, "Tag list size exceeded!\n"); exit(20); } put_unaligned(tag, tl->tag_list_cpos++); put_unaligned(data_len, tl->tag_list_cpos++); memcpy(tl->tag_list_cpos, data, data_len); tl->tag_list_cpos = (unsigned short*)((char*)tl->tag_list_cpos + data_len); } static void free_tag_list(struct drbd_tag_list *tl) { free(tl->nl_header); free(tl); } static int conv_block_dev(struct drbd_argument *ad, struct drbd_tag_list *tl, char* arg) { struct stat sb; int device_fd; if ((device_fd = open(arg,O_RDWR))==-1) { PERROR("Can not open device '%s'", arg); return OTHER_ERROR; } if (fstat(device_fd, &sb)) { PERROR("fstat(%s) failed", arg); return OTHER_ERROR; } if(!S_ISBLK(sb.st_mode)) { fprintf(stderr, "%s is not a block device!\n", arg); return OTHER_ERROR; } close(device_fd); add_tag(tl,ad->tag,arg,strlen(arg)+1); // include the null byte. return NO_ERROR; } static int conv_md_idx(struct drbd_argument *ad, struct drbd_tag_list *tl, char* arg) { int idx; if(!strcmp(arg,"internal")) idx = DRBD_MD_INDEX_FLEX_INT; else if(!strcmp(arg,"flexible")) idx = DRBD_MD_INDEX_FLEX_EXT; else idx = m_strtoll(arg,1); add_tag(tl,ad->tag,&idx,sizeof(idx)); return NO_ERROR; } static void resolv6(char *name, struct sockaddr_in6 *addr) { struct addrinfo hints, *res, *tmp; int err; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; err = getaddrinfo(name, 0, &hints, &res); if (err) { fprintf(stderr, "getaddrinfo %s: %s\n", name, gai_strerror(err)); exit(20); } /* Yes, it is a list. We use only the first result. The loop is only * there to document that we know it is a list */ for (tmp = res; tmp; tmp = tmp->ai_next) { memcpy(addr, tmp->ai_addr, sizeof(*addr)); break; } freeaddrinfo(res); if (0) { /* debug output */ char ip[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &addr->sin6_addr, ip, sizeof(ip)); fprintf(stderr, "%s -> %02x %04x %08x %s %08x\n", name, addr->sin6_family, addr->sin6_port, addr->sin6_flowinfo, ip, addr->sin6_scope_id); } } static unsigned long resolv(const char* name) { unsigned long retval; if((retval = inet_addr(name)) == INADDR_NONE ) { struct hostent *he; he = gethostbyname(name); if (!he) { fprintf(stderr, "can not resolve the hostname: gethostbyname(%s): %s\n", name, hstrerror(h_errno)); exit(20); } retval = ((struct in_addr *)(he->h_addr_list[0]))->s_addr; } return retval; } static void split_ipv6_addr(char **address, int *port) { /* ipv6:[fe80::0234:5678:9abc:def1]:8000; */ char *b = strrchr(*address,']'); if (address[0][0] != '[' || b == NULL || (b[1] != ':' && b[1] != '\0')) { fprintf(stderr, "unexpected ipv6 format: %s\n", *address); exit(20); } *b = 0; *address += 1; /* skip '[' */ if (b[1] == ':') *port = m_strtoll(b+2,1); /* b+2: "]:" */ else *port = 7788; /* will we ever get rid of that default port? */ } static void split_address(char* text, int *af, char** address, int* port) { static struct { char* text; int af; } afs[] = { { "ipv4:", AF_INET }, { "ipv6:", AF_INET6 }, { "sdp:", AF_INET_SDP }, { "ssocks:", -1 }, }; unsigned int i; char *b; *af=AF_INET; *address = text; for (i=0; itag,&addr6,sizeof(addr6)); } else { /* AF_INET, AF_SDP, AF_SSOCKS, * all use the IPv4 addressing scheme */ addr.sin_port = htons(port); addr.sin_family = af; addr.sin_addr.s_addr = resolv(address); add_tag(tl,ad->tag,&addr,sizeof(addr)); } return NO_ERROR; } static int conv_protocol(struct drbd_argument *ad, struct drbd_tag_list *tl, char* arg) { int prot; if(!strcmp(arg,"A") || !strcmp(arg,"a")) { prot=DRBD_PROT_A; } else if (!strcmp(arg,"B") || !strcmp(arg,"b")) { prot=DRBD_PROT_B; } else if (!strcmp(arg,"C") || !strcmp(arg,"c")) { prot=DRBD_PROT_C; } else { fprintf(stderr, "'%s' is no valid protocol.\n", arg); return OTHER_ERROR; } add_tag(tl,ad->tag,&prot,sizeof(prot)); return NO_ERROR; } static int conv_bit(struct drbd_option *od, struct drbd_tag_list *tl, char* arg __attribute((unused))) { char bit=1; add_tag(tl,od->tag,&bit,sizeof(bit)); return NO_ERROR; } /* It will only print the WARNING if the warn flag is set with the _first_ call! */ #define PROC_NET_AF_SCI_FAMILY "/proc/net/af_sci/family" #define PROC_NET_AF_SSOCKS_FAMILY "/proc/net/af_ssocks/family" static int get_af_ssocks(int warn_and_use_default) { char buf[16]; int c, fd; static int af = -1; if (af > 0) return af; fd = open(PROC_NET_AF_SSOCKS_FAMILY, O_RDONLY); if (fd < 0) fd = open(PROC_NET_AF_SCI_FAMILY, O_RDONLY); if (fd < 0) { if (warn_and_use_default) { fprintf(stderr, "open(" PROC_NET_AF_SSOCKS_FAMILY ") " "failed: %m\n WARNING: assuming AF_SSOCKS = 27. " "Socket creation may fail.\n"); af = 27; } return af; } c = read(fd, buf, sizeof(buf)-1); if (c > 0) { buf[c] = 0; if (buf[c-1] == '\n') buf[c-1] = 0; af = m_strtoll(buf,1); } else { if (warn_and_use_default) { fprintf(stderr, "read(" PROC_NET_AF_SSOCKS_FAMILY ") " "failed: %m\n WARNING: assuming AF_SSOCKS = 27. " "Socket creation may fail.\n"); af = 27; } } close(fd); return af; } static int conv_sndbuf(struct drbd_option *od, struct drbd_tag_list *tl, char* arg) { int err = conv_numeric(od, tl, arg); long long l = m_strtoll(arg, 0); char bit = 0; if (err != NO_ERROR || l != 0) return err; /* this is a mandatory bit, * to avoid newer userland to configure older modules with * a sndbuf size of zero, which would lead to Oops. */ add_tag(tl, T_auto_sndbuf_size, &bit, sizeof(bit)); return NO_ERROR; } static int conv_numeric(struct drbd_option *od, struct drbd_tag_list *tl, char* arg) { const long long min = od->numeric_param.min; const long long max = od->numeric_param.max; const unsigned char unit_prefix = od->numeric_param.unit_prefix; long long l; int i; char unit[] = {0,0}; l = m_strtoll(arg, unit_prefix); if (min > l || l > max) { unit[0] = unit_prefix > 1 ? unit_prefix : 0; fprintf(stderr,"%s %s => %llu%s out of range [%llu..%llu]%s\n", od->name, arg, l, unit, min, max, unit); return OTHER_ERROR; } switch(tag_type(od->tag)) { case TT_INT64: add_tag(tl,od->tag,&l,sizeof(l)); break; case TT_INTEGER: i=l; add_tag(tl,od->tag,&i,sizeof(i)); break; default: fprintf(stderr, "internal error in conv_numeric()\n"); } return NO_ERROR; } static int conv_handler(struct drbd_option *od, struct drbd_tag_list *tl, char* arg) { const char** handler_names = od->handler_param.handler_names; const int number_of_handlers = od->handler_param.number_of_handlers; int i; for(i=0;itag,&i,sizeof(i)); return NO_ERROR; } } fprintf(stderr, "%s-handler '%s' not known\n", od->name, arg); fprintf(stderr, "known %s-handlers:\n", od->name); for (i = 0; i < number_of_handlers; i++) { if (handler_names[i]) printf("\t%s\n", handler_names[i]); } return OTHER_ERROR; } static int conv_string(struct drbd_option *od, struct drbd_tag_list *tl, char* arg) { add_tag(tl,od->tag,arg,strlen(arg)+1); return NO_ERROR; } static struct option * make_longoptions(struct drbd_option* od) { /* room for up to N options, * plus set-defaults, create-device, and the terminating NULL */ #define N 30 static struct option buffer[N+3]; int i=0; while(od && od->name) { buffer[i].name = od->name; buffer[i].has_arg = tag_type(od->tag) == TT_BIT ? no_argument : required_argument ; buffer[i].flag = NULL; buffer[i].val = od->short_name; if (i++ == N) { /* we must not leave this loop with i > N */ fprintf(stderr,"buffer in make_longoptions to small.\n"); abort(); } od++; } #undef N // The two omnipresent options: buffer[i].name = "set-defaults"; buffer[i].has_arg = 0; buffer[i].flag = NULL; buffer[i].val = '('; i++; buffer[i].name = "create-device"; buffer[i].has_arg = 0; buffer[i].flag = NULL; buffer[i].val = ')'; i++; buffer[i].name = NULL; buffer[i].has_arg = 0; buffer[i].flag = NULL; buffer[i].val = 0; return buffer; } static struct drbd_option *find_opt_by_short_name(struct drbd_option *od, int c) { if(!od) return NULL; while(od->name) { if(od->short_name == c) return od; od++; } return NULL; } /* prepends global devname to output (if any) */ static int print_config_error(int err_no) { int rv=0; if (err_no == NO_ERROR || err_no == SS_SUCCESS) return 0; if (err_no == OTHER_ERROR) return 20; if ( ( err_no >= AFTER_LAST_ERR_CODE || err_no <= ERR_CODE_BASE ) && ( err_no > SS_CW_NO_NEED || err_no <= SS_AFTER_LAST_ERROR) ) { fprintf(stderr,"Error code %d unknown.\n" "You should update the drbd userland tools.\n",err_no); rv = 20; } else { if(err_no > ERR_CODE_BASE ) { fprintf(stderr,"%s: Failure: (%d) %s\n", devname, err_no, error_to_string(err_no)); rv = 10; } else if (err_no == SS_UNKNOWN_ERROR) { fprintf(stderr,"%s: State change failed: (%d)" "unknown error.\n", devname, err_no); rv = 11; } else if (err_no > SS_TWO_PRIMARIES) { // Ignore SS_SUCCESS, SS_NOTHING_TO_DO, SS_CW_Success... } else { fprintf(stderr,"%s: State change failed: (%d) %s\n", devname, err_no, drbd_set_st_err_str(err_no)); if (err_no == SS_NO_UP_TO_DATE_DISK) { /* all available disks are inconsistent, * or I am consistent, but cannot outdate the peer. */ rv = 17; } else if (err_no == SS_LOWER_THAN_OUTDATED) { /* was inconsistent anyways */ rv = 5; } else if (err_no == SS_NO_LOCAL_DISK) { /* Can not start resync, no local disks, try with drbdmeta */ rv = 16; } else { rv = 11; } } } return rv; } #define RCV_SIZE NLMSG_SPACE(sizeof(struct cn_msg)+sizeof(struct drbd_nl_cfg_reply)) static void warn_print_excess_args(int argc, char **argv, int i) { fprintf(stderr, "Excess arguments:"); for (; i < argc; i++) fprintf(stderr, " %s", argv[i]); printf("\n"); } static void dump_argv(int argc, char **argv, int first_non_option, int n_known_args) { int i; if (!debug_dump_argv) return; fprintf(stderr, ",-- ARGV dump (optind %d, known_args %d, argc %u):\n", first_non_option, n_known_args, argc); for (i = 0; i < argc; i++) { if (i == 1) fprintf(stderr, "-- consumed options:"); if (i == first_non_option) fprintf(stderr, "-- known args:"); if (i == (first_non_option + n_known_args)) fprintf(stderr, "-- unexpected args:"); fprintf(stderr, "| %2u: %s\n", i, argv[i]); } fprintf(stderr, "`--\n"); } static int _generic_config_cmd(struct drbd_cmd *cm, unsigned minor, int argc, char **argv) { char buffer[ RCV_SIZE ]; struct drbd_nl_cfg_reply *reply; struct drbd_argument *ad = cm->cp.args; struct drbd_option *od; struct option *lo; struct drbd_tag_list *tl; int c,i=1,rv=NO_ERROR,sk_nl; int flags=0; int n_args; tl = create_tag_list(4096); while(ad && ad->name) { if(argc < i+1) { fprintf(stderr,"Missing argument '%s'\n", ad->name); print_command_usage(cm-commands, "",FULL); rv = OTHER_ERROR; goto error; } rv = ad->convert_function(ad,tl,argv[i++]); if (rv != NO_ERROR) goto error; ad++; } n_args = i - 1; lo = make_longoptions(cm->cp.options); if (!lo) { static struct option none[] = { { } }; lo = none; } for(;;) { c = getopt_long(argc, argv, make_optstring(lo), lo, 0); if (c == -1) break; od = find_opt_by_short_name(cm->cp.options,c); if (od) rv = od->convert_function(od,tl,optarg); else { if(c=='(') flags |= DRBD_NL_SET_DEFAULTS; else if(c==')') flags |= DRBD_NL_CREATE_DEVICE; else { rv = OTHER_ERROR; goto error; } } if (rv != NO_ERROR) goto error; } /* argc should be cmd + n options + n args; * if it is more, we did not understand some */ if (n_args + optind < argc) { warn_print_excess_args(argc, argv, optind + n_args); rv = OTHER_ERROR; goto error; } dump_argv(argc, argv, optind, i - 1); add_tag(tl,TT_END,NULL,0); // close the tag list if(rv == NO_ERROR) { //dump_tag_list(tl->tag_list_start); int received; sk_nl = open_cn(); if (sk_nl < 0) { rv = OTHER_ERROR; goto error; } tl->drbd_p_header->packet_type = cm->packet_id; tl->drbd_p_header->drbd_minor = minor; tl->drbd_p_header->flags = flags; received = call_drbd(sk_nl,tl, (struct nlmsghdr*)buffer,RCV_SIZE,NL_TIME); close_cn(sk_nl); if (received >= 0) { reply = (struct drbd_nl_cfg_reply *) ((struct cn_msg *)NLMSG_DATA(buffer))->data; rv = reply->ret_code; } } error: free_tag_list(tl); return rv; } static int generic_config_cmd(struct drbd_cmd *cm, unsigned minor, int argc, char **argv) { return print_config_error(_generic_config_cmd(cm, minor, argc, argv)); } #define ASSERT(exp) if (!(exp)) \ fprintf(stderr,"ASSERT( " #exp " ) in %s:%d\n", __FILE__,__LINE__); static void show_numeric(struct drbd_option *od, unsigned short* tp) { long long val; const unsigned char unit_prefix = od->numeric_param.unit_prefix; switch(tag_type(get_unaligned(tp++))) { case TT_INTEGER: ASSERT( get_unaligned(tp++) == sizeof(int) ); val = get_unaligned((int*)tp); break; case TT_INT64: ASSERT( get_unaligned(tp++) == sizeof(uint64_t) ); val = get_unaligned((uint64_t*)tp); break; default: ASSERT(0); val=0; } if(unit_prefix == 1) printf("\t%-16s\t%lld",od->name,val); else printf("\t%-16s\t%lld%c",od->name,val,unit_prefix); if(val == (long long) od->numeric_param.def) printf(" _is_default"); if(od->numeric_param.unit) { printf("; # %s\n",od->numeric_param.unit); } else { printf(";\n"); } } static void show_handler(struct drbd_option *od, unsigned short* tp) { const char** handler_names = od->handler_param.handler_names; int i; ASSERT( tag_type(get_unaligned(tp++)) == TT_INTEGER ); ASSERT( get_unaligned(tp++) == sizeof(int) ); i = get_unaligned((int*)tp); printf("\t%-16s\t%s",od->name,handler_names[i]); if( i == (long long)od->numeric_param.def) printf(" _is_default"); printf(";\n"); } static void show_bit(struct drbd_option *od, unsigned short* tp) { ASSERT( tag_type(get_unaligned(tp++)) == TT_BIT ); ASSERT( get_unaligned(tp++) == sizeof(char) ); if(get_unaligned((char*)tp)) printf("\t%-16s;\n",od->name); } static void show_string(struct drbd_option *od, unsigned short* tp) { ASSERT( tag_type(get_unaligned(tp++)) == TT_STRING ); if( get_unaligned(tp++) > 0 && get_unaligned((char*)tp)) printf("\t%-16s\t\"%s\";\n",od->name,(char*)tp); } static unsigned short *look_for_tag(unsigned short *tlc, unsigned short tag) { enum drbd_tags t; int len; while( (t = get_unaligned(tlc)) != TT_END ) { if(t == tag) return tlc; tlc++; len = get_unaligned(tlc++); tlc = (unsigned short*)((char*)tlc + len); } return NULL; } static void print_options(struct drbd_option *od, unsigned short *tlc, const char* sect_name) { unsigned short *tp; int opened = 0; while(od->name) { tp = look_for_tag(tlc,od->tag); if(tp) { if(!opened) { opened=1; printf("%s {\n",sect_name); } od->show_function(od,tp); put_unaligned(TT_REMOVED, tp); } od++; } if(opened) { printf("}\n"); } } static void consume_everything(unsigned short *tlc) { enum drbd_tags t; int len; while( (t = get_unaligned(tlc)) != TT_END ) { put_unaligned(TT_REMOVED, tlc++); len = get_unaligned(tlc++); tlc = (unsigned short*)((char*)tlc + len); } } static int consume_tag_blob(enum drbd_tags tag, unsigned short *tlc, char** val, unsigned int* len) { unsigned short *tp; tp = look_for_tag(tlc,tag); if(tp) { put_unaligned(TT_REMOVED, tp++); *len = get_unaligned(tp++); *val = (char*)tp; return 1; } return 0; } static int consume_tag_string(enum drbd_tags tag, unsigned short *tlc, char** val) { unsigned short *tp; tp = look_for_tag(tlc,tag); if(tp) { put_unaligned(TT_REMOVED, tp++); if( get_unaligned(tp++) > 0 ) *val = (char*)tp; else *val = ""; return 1; } return 0; } static int consume_tag_int(enum drbd_tags tag, unsigned short *tlc, int* val) { unsigned short *tp; tp = look_for_tag(tlc,tag); if(tp) { put_unaligned(TT_REMOVED, tp++); tp++; *val = get_unaligned((int *)tp); return 1; } return 0; } static int consume_tag_u64(enum drbd_tags tag, unsigned short *tlc, unsigned long long* val) { unsigned short *tp; unsigned short len; tp = look_for_tag(tlc, tag); if(tp) { put_unaligned(TT_REMOVED, tp++); len = get_unaligned(tp++); /* check the data size. * actually it has to be long long, but I'm paranoid */ if (len == sizeof(int)) *val = get_unaligned((unsigned int*)tp); else if (len == sizeof(long)) *val = get_unaligned((unsigned long *)tp); else if (len == sizeof(long long)) *val = get_unaligned((unsigned long long *)tp); else { fprintf(stderr, "%s: unexpected tag len: %u\n", __func__ , len); return 0; } return 1; } return 0; } static int consume_tag_bit(enum drbd_tags tag, unsigned short *tlc, int* val) { unsigned short *tp; tp = look_for_tag(tlc,tag); if(tp) { put_unaligned(TT_REMOVED, tp++); tp++; *val = (int)(*(char *)tp); return 1; } return 0; } static int generic_get_cmd(struct drbd_cmd *cm, unsigned minor, int argc, char **argv __attribute((unused))) { char buffer[ 4096 ]; struct drbd_tag_list *tl; struct drbd_nl_cfg_reply *reply; int sk_nl,rv; int ignore_minor_not_known; int dummy; if (argc > 1) { warn_print_excess_args(argc, argv, 1); return 20; } dump_argv(argc, argv, 1, 0); tl = create_tag_list(2); add_tag(tl,TT_END,NULL,0); // close the tag list sk_nl = open_cn(); if(sk_nl < 0) return 20; tl->drbd_p_header->packet_type = cm->packet_id; tl->drbd_p_header->drbd_minor = minor; tl->drbd_p_header->flags = 0; memset(buffer,0,sizeof(buffer)); call_drbd(sk_nl,tl, (struct nlmsghdr*)buffer,4096,NL_TIME); close_cn(sk_nl); reply = (struct drbd_nl_cfg_reply *) ((struct cn_msg *)NLMSG_DATA(buffer))->data; /* if there was an error, report and abort -- * unless it was "this device is not there", * and command was "status" */ ignore_minor_not_known = cm->gp.show_function == status_xml_scmd || cm->gp.show_function == sh_status_scmd; if (reply->ret_code != NO_ERROR && !(reply->ret_code == ERR_MINOR_INVALID && ignore_minor_not_known)) return print_config_error(reply->ret_code); rv = cm->gp.show_function(cm,minor,reply->tag_list); /* in case cm->packet_id == P_get_state, and the gp.show_function did * nothing with the sync_progress info, consume it here, so it won't * confuse users because it gets dumped below. */ consume_tag_int(T_sync_progress, reply->tag_list, &dummy); if(dump_tag_list(reply->tag_list)) { printf("# Found unknown tags, you should update your\n" "# userland tools\n"); } return rv; } static char *af_to_str(int af) { if (af == AF_INET) return "ipv4"; else if (af == AF_INET6) return "ipv6"; /* AF_SSOCKS typically is 27, the same as AF_INET_SDP. * But with warn_and_use_default = 0, it will stay at -1 if not available. * Just keep the test on ssocks before the one on SDP (which is hard-coded), * and all should be fine. */ else if (af == get_af_ssocks(0)) return "ssocks"; else if (af == AF_INET_SDP) return "sdp"; else return "unknown"; } static void show_address(void* address, int addr_len) { union { struct sockaddr addr; struct sockaddr_in addr4; struct sockaddr_in6 addr6; } a; char buffer[INET6_ADDRSTRLEN]; /* avoid alignment issues on certain platforms (e.g. armel) */ memset(&a, 0, sizeof(a)); memcpy(&a.addr, address, addr_len); if (a.addr.sa_family == AF_INET || a.addr.sa_family == get_af_ssocks(0) || a.addr.sa_family == AF_INET_SDP) { printf("\taddress\t\t\t%s %s:%d;\n", af_to_str(a.addr4.sin_family), inet_ntoa(a.addr4.sin_addr), ntohs(a.addr4.sin_port)); } else if (a.addr.sa_family == AF_INET6) { printf("\taddress\t\t\t%s [%s]:%d;\n", af_to_str(a.addr6.sin6_family), inet_ntop(a.addr6.sin6_family, &a.addr6.sin6_addr, buffer, INET6_ADDRSTRLEN), ntohs(a.addr6.sin6_port)); } else { printf("\taddress\t\t\t[unknown af=%d, len=%d]\n", a.addr.sa_family, addr_len); } } static int show_scmd(struct drbd_cmd *cm, unsigned minor, unsigned short *rtl) { int idx = idx; char *str = NULL, *backing_dev, *address; unsigned int addr_len = 0; // find all commands that have options and print those... for ( cm = commands ; cm < commands + ARRAY_SIZE(commands) ; cm++ ) { if(cm->function == generic_config_cmd && cm->cp.options ) print_options(cm->cp.options, rtl, cm->cmd); } // start of spaghetti code... if(consume_tag_int(T_wire_protocol,rtl,&idx)) printf("protocol %c;\n",'A'+idx-1); backing_dev = address = NULL; consume_tag_string(T_backing_dev,rtl,&backing_dev); consume_tag_blob(T_my_addr, rtl, &address, &addr_len); if(backing_dev || address) { printf("_this_host {\n"); printf("\tdevice\t\t\tminor %d;\n",minor); if(backing_dev) { printf("\tdisk\t\t\t\"%s\";\n",backing_dev); consume_tag_int(T_meta_dev_idx,rtl,&idx); consume_tag_string(T_meta_dev,rtl,&str); switch(idx) { case DRBD_MD_INDEX_INTERNAL: case DRBD_MD_INDEX_FLEX_INT: printf("\tmeta-disk\t\tinternal;\n"); break; case DRBD_MD_INDEX_FLEX_EXT: printf("\tflexible-meta-disk\t\"%s\";\n",str); break; default: printf("\tmeta-disk\t\t\"%s\" [ %d ];\n",str, idx); } } if(address) show_address(address, addr_len); printf("}\n"); } if(consume_tag_blob(T_peer_addr, rtl, &address, &addr_len)) { printf("_remote_host {\n"); show_address(address, addr_len); printf("}\n"); } consume_tag_bit(T_mind_af, rtl, &idx); /* consume it, its value has no relevance */ consume_tag_bit(T_auto_sndbuf_size, rtl, &idx); /* consume it, its value has no relevance */ return 0; } static int lk_bdev_scmd(struct drbd_cmd *cm, unsigned minor, unsigned short *rtl) { struct bdev_info bd = { 0, }; char *backing_dev = NULL; uint64_t bd_size; int fd; int idx = idx; int index_valid = 0; consume_tag_string(T_backing_dev, rtl, &backing_dev); index_valid = consume_tag_int(T_meta_dev_idx, rtl, &idx); /* consume everything */ consume_everything(rtl); if (!backing_dev) { fprintf(stderr, "Has no disk config, try with drbdmeta.\n"); return 1; } if (!index_valid) { /* cannot happen, right? ;-) */ fprintf(stderr, "No meta data index!?\n"); return 1; } if (idx >= 0 || idx == DRBD_MD_INDEX_FLEX_EXT) { lk_bdev_delete(minor); return 0; } fd = open(backing_dev, O_RDONLY); if (fd == -1) { fprintf(stderr, "Could not open %s: %m.\n", backing_dev); return 1; } bd_size = bdev_size(fd); close(fd); if (lk_bdev_load(minor, &bd) == 0 && bd.bd_size == bd_size && bd.bd_name && !strcmp(bd.bd_name, backing_dev)) return 0; /* nothing changed. */ bd.bd_size = bd_size; bd.bd_name = backing_dev; lk_bdev_save(minor, &bd); return 0; } static int status_xml_scmd(struct drbd_cmd *cm __attribute((unused)), unsigned minor, unsigned short *rtl) { union drbd_state state = { .i = 0 }; int synced = 0; if (!consume_tag_int(T_state_i,rtl,(int*)&state.i)) { printf( "\n"); return 0; } printf("\n"); return 0; } printf( /* connection state */ " cs=\"%s\"" /* role */ " ro1=\"%s\" ro2=\"%s\"" /* disk state */ " ds1=\"%s\" ds2=\"%s\"", drbd_conn_str(state.conn), drbd_role_str(state.role), drbd_role_str(state.peer), drbd_disk_str(state.disk), drbd_disk_str(state.pdsk)); /* io suspended ? */ if (state.susp) printf(" suspended"); /* reason why sync is paused */ if (state.aftr_isp) printf(" aftr_isp"); if (state.peer_isp) printf(" peer_isp"); if (state.user_isp) printf(" user_isp"); if (consume_tag_int(T_sync_progress, rtl, &synced)) printf(" resynced_percent=\"%i.%i\"", synced / 10, synced % 10); printf(" />\n"); return 0; } static int sh_status_scmd(struct drbd_cmd *cm __attribute((unused)), unsigned minor, unsigned short *rtl) { /* variable prefix; maybe rather make that a command line parameter? * or use "drbd_sh_status"? */ #define _P "" union drbd_state state = { .i = 0 }; int available = 0; int synced = 0; printf("%s_minor=%u\n", _P, minor); printf("%s_res_name=%s\n", _P, shell_escape(resname ?: "UNKNOWN")); available = consume_tag_int(T_state_i,rtl,(int*)&state.i); if (state.conn == C_STANDALONE && state.disk == D_DISKLESS) { printf("%s_known=%s\n\n", _P, available ? "Unconfigured" : "NA # not available or not yet created"); printf("%s_cstate=Unconfigured\n", _P); printf("%s_role=\n", _P); printf("%s_peer=\n", _P); printf("%s_disk=\n", _P); printf("%s_pdsk=\n", _P); printf("%s_flags_susp=\n", _P); printf("%s_flags_aftr_isp=\n", _P); printf("%s_flags_peer_isp=\n", _P); printf("%s_flags_user_isp=\n", _P); printf("%s_resynced_percent=\n", _P); } else { printf( "%s_known=Configured\n\n" /* connection state */ "%s_cstate=%s\n" /* role */ "%s_role=%s\n" "%s_peer=%s\n" /* disk state */ "%s_disk=%s\n" "%s_pdsk=%s\n\n", _P, _P, drbd_conn_str(state.conn), _P, drbd_role_str(state.role), _P, drbd_role_str(state.peer), _P, drbd_disk_str(state.disk), _P, drbd_disk_str(state.pdsk)); /* io suspended ? */ printf("%s_flags_susp=%s\n", _P, state.susp ? "1" : ""); /* reason why sync is paused */ printf("%s_flags_aftr_isp=%s\n", _P, state.aftr_isp ? "1" : ""); printf("%s_flags_peer_isp=%s\n", _P, state.peer_isp ? "1" : ""); printf("%s_flags_user_isp=%s\n\n", _P, state.user_isp ? "1" : ""); printf("%s_resynced_percent=", _P); if (consume_tag_int(T_sync_progress, rtl, &synced)) printf("%i.%i\n", synced / 10, synced % 10); else printf("\n"); } printf("\n%s_sh_status_process\n\n\n", _P); fflush(stdout); return 0; #undef _P } static int role_scmd(struct drbd_cmd *cm __attribute((unused)), unsigned minor __attribute((unused)), unsigned short *rtl) { union drbd_state state = { .i = 0 }; if (!strcmp(cm->cmd, "state")) { fprintf(stderr, "'%s ... state' is deprecated, use '%s ... role' instead.\n", cmdname, cmdname); } consume_tag_int(T_state_i,rtl,(int*)&state.i); if ( state.conn == C_STANDALONE && state.disk == D_DISKLESS) { printf("Unconfigured\n"); } else { printf("%s/%s\n",drbd_role_str(state.role),drbd_role_str(state.peer)); } return 0; } static int cstate_scmd(struct drbd_cmd *cm __attribute((unused)), unsigned minor __attribute((unused)), unsigned short *rtl) { union drbd_state state = { .i = 0 }; consume_tag_int(T_state_i,rtl,(int*)&state.i); if ( state.conn == C_STANDALONE && state.disk == D_DISKLESS) { printf("Unconfigured\n"); } else { printf("%s\n",drbd_conn_str(state.conn)); } return 0; } static int dstate_scmd(struct drbd_cmd *cm __attribute((unused)), unsigned minor __attribute((unused)), unsigned short *rtl) { union drbd_state state = { .i = 0 }; consume_tag_int(T_state_i,rtl,(int*)&state.i); if ( state.conn == C_STANDALONE && state.disk == D_DISKLESS) { printf("Unconfigured\n"); } else { printf("%s/%s\n",drbd_disk_str(state.disk),drbd_disk_str(state.pdsk)); } return 0; } static int uuids_scmd(struct drbd_cmd *cm, unsigned minor __attribute((unused)), unsigned short *rtl) { uint64_t uuids[UI_SIZE]; char *tl_uuids; int flags = flags; unsigned int len; if (!consume_tag_blob(T_uuids, rtl, &tl_uuids, &len)) { fprintf(stderr,"Reply payload did not carry an uuid-tag,\n" "Probably the device has no disk!\n"); return 1; } consume_tag_int(T_uuids_flags,rtl,&flags); if( len == UI_SIZE * sizeof(uint64_t)) { memcpy(uuids, tl_uuids, len); if(!strcmp(cm->cmd,"show-gi")) { dt_pretty_print_uuids(uuids,flags); } else if(!strcmp(cm->cmd,"get-gi")) { dt_print_uuids(uuids,flags); } else { ASSERT( 0 ); } } else { fprintf(stderr, "Unexpected length of T_uuids tag. " "You should upgrade your userland tools\n"); } return 0; } static struct drbd_cmd *find_cmd_by_name(char *name) { unsigned int i; for (i = 0; i < ARRAY_SIZE(commands); i++) { if (!strcmp(name, commands[i].cmd)) { return commands + i; } } return NULL; } static int down_cmd(struct drbd_cmd *cm, unsigned minor, int argc, char **argv) { int rv; int success; if(argc > 1) { fprintf(stderr,"Ignoring excess arguments\n"); } cm = find_cmd_by_name("secondary"); rv = _generic_config_cmd(cm, minor, argc, argv); // No error messages if (rv == ERR_MINOR_INVALID) return 0; success = (rv >= SS_SUCCESS && rv < ERR_CODE_BASE) || rv == NO_ERROR; if (!success) return print_config_error(rv); cm = find_cmd_by_name("disconnect"); cm->function(cm,minor,argc,argv); cm = find_cmd_by_name("detach"); rv = cm->function(cm,minor,argc,argv); return rv; } static void print_digest(const char* label, const int len, const unsigned char *hash) { int i; printf("\t%s: ", label); for (i = 0; i < len; i++) printf("%02x",hash[i]); printf("\n"); } static char printable_or_dot(char c) { return (' ' < c && c <= '~') ? c : '.'; } static void print_hex_line(int offset, unsigned char *data) { printf( " %04x:" " %02x %02x %02x %02x %02x %02x %02x %02x " " %02x %02x %02x %02x %02x %02x %02x %02x" " %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n", offset, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], printable_or_dot(data[0]), printable_or_dot(data[1]), printable_or_dot(data[2]), printable_or_dot(data[3]), printable_or_dot(data[4]), printable_or_dot(data[5]), printable_or_dot(data[6]), printable_or_dot(data[7]), printable_or_dot(data[8]), printable_or_dot(data[9]), printable_or_dot(data[10]), printable_or_dot(data[11]), printable_or_dot(data[12]), printable_or_dot(data[13]), printable_or_dot(data[14]), printable_or_dot(data[15])); } /* successive identical lines are collapsed into just printing one star */ static void print_hex_dump(int len, void *data) { int i; int star = 0; for (i = 0; i < len-15; i += 16) { if (i == 0 || memcmp(data + i, data + i - 16, 16)) { print_hex_line(i, data + i); star = 0; } else if (!star) { printf(" *\n"); star = 1; } } /* yes, I ignore remainders of len not modulo 16 here. * so what, usage is currently to dump bios, which are * multiple of 512. */ /* for good measure, print the total size as offset now, * last line may have been a '*' */ printf(" %04x.\n", len); } static void print_dump_ee(struct drbd_nl_cfg_reply *reply) { unsigned long long sector = -1ULL; unsigned long long block_id = 0; char *reason = "UNKNOWN REASON"; char *dig_in = NULL; char *dig_vv = NULL; unsigned int dgs_in = 0, dgs_vv = 0; unsigned int size = 0; char *data = NULL; if (!consume_tag_string(T_dump_ee_reason, reply->tag_list, &reason)) printf("\tno reason?\n"); if (!consume_tag_blob(T_seen_digest, reply->tag_list, &dig_in, &dgs_in)) printf("\tno digest in?\n"); if (!consume_tag_blob(T_calc_digest, reply->tag_list, &dig_vv, &dgs_vv)) printf("\tno digest out?\n"); if (!consume_tag_u64(T_ee_sector, reply->tag_list, §or)) printf("\tno sector?\n"); if (!consume_tag_u64(T_ee_block_id, reply->tag_list, &block_id)) printf("\tno block_id?\n"); if (!consume_tag_blob(T_ee_data, reply->tag_list, &data, &size)) printf("\tno data?\n"); printf("\tdumping ee, reason: %s\n", reason); printf("\tsector: %llu block_id: 0x%llx size: %u\n", sector, block_id, size); /* "input sanitation". Did I mention yet that I'm paranoid? */ if (!data) size = 0; if (!dig_in) dgs_in = 0; if (!dig_vv) dgs_vv = 0; if (dgs_in > SHARED_SECRET_MAX) dgs_in = SHARED_SECRET_MAX; if (dgs_vv > SHARED_SECRET_MAX) dgs_vv = SHARED_SECRET_MAX; print_digest("received digest", dgs_in, (unsigned char*)dig_in); print_digest("verified digest", dgs_vv, (unsigned char*)dig_vv); /* dump at most 32 K */ if (size > 0x8000) { size = 0x8000; printf("\tWARNING truncating data to %u!\n", 0x8000); } print_hex_dump(size,data); } /* this is not pretty; but it's api... ;-( */ const char *pretty_print_return_code(int e) { return e == NO_ERROR ? "No error" : e > ERR_CODE_BASE ? error_to_string(e) : e > SS_AFTER_LAST_ERROR && e <= SS_TWO_PRIMARIES ? drbd_set_st_err_str(e) : e == SS_CW_NO_NEED ? "Cluster wide state change: nothing to do" : e == SS_CW_SUCCESS ? "Cluster wide state change successful" : e == SS_NOTHING_TO_DO ? "State change: nothing to do" : e == SS_SUCCESS ? "State change successful" : e == SS_UNKNOWN_ERROR ? "Unspecified error" : "Unknown return code"; } static int print_broadcast_events(unsigned int seq, int u __attribute((unused)), struct drbd_nl_cfg_reply *reply) { union drbd_state state; char* str; int synced = 0; switch (reply->packet_type) { case 0: /* used to be this way in drbd_nl.c for some responses :-( */ case P_return_code_only: /* used by drbd_nl.c for most "empty" responses */ printf("%u ZZ %d ret_code: %d %s\n", seq, reply->minor, reply->ret_code, pretty_print_return_code(reply->ret_code)); break; case P_get_state: if(consume_tag_int(T_state_i,reply->tag_list,(int*)&state.i)) { printf("%u ST %d { cs:%s ro:%s/%s ds:%s/%s %c%c%c%c }\n", seq, reply->minor, drbd_conn_str(state.conn), drbd_role_str(state.role), drbd_role_str(state.peer), drbd_disk_str(state.disk), drbd_disk_str(state.pdsk), state.susp ? 's' : 'r', state.aftr_isp ? 'a' : '-', state.peer_isp ? 'p' : '-', state.user_isp ? 'u' : '-' ); } else fprintf(stderr,"Missing tag !?\n"); break; case P_call_helper: if(consume_tag_string(T_helper,reply->tag_list,&str)) { printf("%u UH %d %s\n", seq, reply->minor, str); } else fprintf(stderr,"Missing tag !?\n"); break; case P_sync_progress: if (consume_tag_int(T_sync_progress, reply->tag_list, &synced)) { printf("%u SP %d %i.%i\n", seq, reply->minor, synced / 10, synced % 10); } else fprintf(stderr,"Missing tag !?\n"); break; case P_dump_ee: printf("%u DE %d\n", seq, reply->minor); print_dump_ee(reply); break; default: printf("%u ?? %d \n",seq, reply->minor, reply->packet_type); break; } fflush(stdout); return 1; } void print_failure_code(int ret_code) { if (ret_code > ERR_CODE_BASE) fprintf(stderr,"%s: Failure: (%d) %s\n", devname, ret_code, error_to_string(ret_code)); else fprintf(stderr,"%s: Failure: (ret_code=%d)\n", devname, ret_code); } static int w_connected_state(unsigned int seq __attribute((unused)), int wait_after_sb, struct drbd_nl_cfg_reply *reply) { union drbd_state state; if (reply->ret_code != NO_ERROR) { print_failure_code(reply->ret_code); return 0; } if(reply->packet_type == P_get_state) { if(consume_tag_int(T_state_i,reply->tag_list,(int*)&state.i)) { if(state.conn >= C_CONNECTED) return 0; if(!wait_after_sb && state.conn < C_UNCONNECTED) return 0; } else fprintf(stderr,"Missing tag !?\n"); } return 1; } static int w_synced_state(unsigned int seq __attribute((unused)), int wait_after_sb, struct drbd_nl_cfg_reply *reply) { union drbd_state state; if (reply->ret_code != NO_ERROR) { print_failure_code(reply->ret_code); return 0; } if(reply->packet_type == P_get_state) { if(consume_tag_int(T_state_i,reply->tag_list,(int*)&state.i)) { if(state.conn == C_CONNECTED) return 0; if(!wait_after_sb && state.conn < C_UNCONNECTED) return 0; } else fprintf(stderr,"Missing tag !?\n"); } return 1; } static int events_cmd(struct drbd_cmd *cm, unsigned minor, int argc ,char **argv) { void *buffer; struct cn_msg *cn_reply; struct drbd_nl_cfg_reply *reply; struct drbd_tag_list *tl; struct option *lo; unsigned int b_seq=0, r_seq=0; int sk_nl,c,cont=1,rr = rr,i,last; int unfiltered=0, all_devices=0, timeout_ms=0; int wfc_timeout=DRBD_WFC_TIMEOUT_DEF; int degr_wfc_timeout=DRBD_DEGR_WFC_TIMEOUT_DEF; int outdated_wfc_timeout=DRBD_OUTDATED_WFC_TIMEOUT_DEF; struct timeval before,after; int wasb=0; lo = cm->ep.options; if (!lo) { static struct option none[] = { { } }; lo = none; } for(;;) { c = getopt_long(argc, argv, make_optstring(lo), lo, 0); if (c == -1) break; switch(c) { default: case '?': return 20; case 'u': unfiltered=1; break; case 'a': all_devices=1; break; case 't': wfc_timeout=m_strtoll(optarg,1); if(DRBD_WFC_TIMEOUT_MIN > wfc_timeout || wfc_timeout > DRBD_WFC_TIMEOUT_MAX) { fprintf(stderr, "wfc_timeout => %d" " out of range [%d..%d]\n", wfc_timeout, DRBD_WFC_TIMEOUT_MIN, DRBD_WFC_TIMEOUT_MAX); return 20; } break; case 'd': degr_wfc_timeout=m_strtoll(optarg,1); if(DRBD_DEGR_WFC_TIMEOUT_MIN > degr_wfc_timeout || degr_wfc_timeout > DRBD_DEGR_WFC_TIMEOUT_MAX) { fprintf(stderr, "degr_wfc_timeout => %d" " out of range [%d..%d]\n", degr_wfc_timeout, DRBD_DEGR_WFC_TIMEOUT_MIN, DRBD_DEGR_WFC_TIMEOUT_MAX); return 20; } break; case 'o': outdated_wfc_timeout=m_strtoll(optarg,1); if(DRBD_OUTDATED_WFC_TIMEOUT_MIN > degr_wfc_timeout || degr_wfc_timeout > DRBD_OUTDATED_WFC_TIMEOUT_MAX) { fprintf(stderr, "degr_wfc_timeout => %d" " out of range [%d..%d]\n", outdated_wfc_timeout, DRBD_OUTDATED_WFC_TIMEOUT_MIN, DRBD_OUTDATED_WFC_TIMEOUT_MAX); return 20; } break; case 'w': wasb=1; break; } } if (optind < argc) { warn_print_excess_args(argc, argv, optind); return 20; } dump_argv(argc, argv, optind, 0); tl = create_tag_list(2); add_tag(tl,TT_END,NULL,0); // close the tag list sk_nl = open_cn(); if(sk_nl < 0) return 20; /* allocate 64k to be on the safe side. */ #define NL_BUFFER_SIZE (64 << 10) buffer = malloc(NL_BUFFER_SIZE); if (!buffer) { fprintf(stderr, "could not allocate buffer of %u bytes\n", NL_BUFFER_SIZE); exit(20); } /* drbdsetup events should not ask for timeout "type", * this is only useful with wait-sync and wait-connected callbacks. */ if (cm->ep.proc_event != print_broadcast_events) { // Find out which timeout value to use. tl->drbd_p_header->packet_type = P_get_timeout_flag; tl->drbd_p_header->drbd_minor = minor; tl->drbd_p_header->flags = 0; if (0 >= call_drbd(sk_nl,tl, buffer, NL_BUFFER_SIZE, NL_TIME)) exit(20); cn_reply = (struct cn_msg *)NLMSG_DATA(buffer); reply = (struct drbd_nl_cfg_reply *)cn_reply->data; if (reply->ret_code != NO_ERROR) return print_config_error(reply->ret_code); consume_tag_bit(T_use_degraded,reply->tag_list,&rr); if (rr != UT_DEFAULT) { if (0 < wfc_timeout && (wfc_timeout < degr_wfc_timeout || degr_wfc_timeout == 0)) { degr_wfc_timeout = wfc_timeout; fprintf(stderr, "degr-wfc-timeout has to be shorter than wfc-timeout\n" "degr-wfc-timeout implicitly set to wfc-timeout (%ds)\n", degr_wfc_timeout); } if (0 < degr_wfc_timeout && (degr_wfc_timeout < outdated_wfc_timeout || outdated_wfc_timeout == 0)) { outdated_wfc_timeout = wfc_timeout; fprintf(stderr, "outdated-wfc-timeout has to be shorter than degr-wfc-timeout\n" "outdated-wfc-timeout implicitly set to degr-wfc-timeout (%ds)\n", degr_wfc_timeout); } } switch (rr) { case UT_DEFAULT: timeout_ms = wfc_timeout; break; case UT_DEGRADED: timeout_ms = degr_wfc_timeout; break; case UT_PEER_OUTDATED: timeout_ms = outdated_wfc_timeout; break; } } timeout_ms = timeout_ms * 1000 - 1; /* 0 -> -1 "infinite", 1000 -> 999, nobody cares... */ // ask for the current state before waiting for state updates... if (all_devices) { i = 0; last = 255; } else { i = last = minor; } while (i <= last) { tl->drbd_p_header->packet_type = P_get_state; tl->drbd_p_header->drbd_minor = i; tl->drbd_p_header->flags = 0; send_cn(sk_nl,tl->nl_header,(char*)tl->tag_list_cpos-(char*)tl->nl_header); i++; } dt_unlock_drbd(lock_fd); lock_fd=-1; do { gettimeofday(&before,NULL); rr = receive_cn(sk_nl, buffer, NL_BUFFER_SIZE, timeout_ms); gettimeofday(&after,NULL); if(rr == -2) break; // timeout expired. if(timeout_ms > 0 ) { timeout_ms -= ( (after.tv_sec - before.tv_sec) * 1000 + (after.tv_usec - before.tv_usec) / 1000 ); } cn_reply = (struct cn_msg *)NLMSG_DATA(buffer); reply = (struct drbd_nl_cfg_reply *)cn_reply->data; // dump_tag_list(reply->tag_list); /* There are two value spaces for sequence numbers. The first is the one created by this drbdsetup instance, the kernel's reply packets simply echo those sequence numbers. The second is created by the kernel's broadcast packets. */ if (!unfiltered) { if (cn_reply->ack == 0) { // broadcasts /* Careful, potential wrap around! * Will skip a lot of packets if you * unload/reload the module in between, * but keep this drbdsetup events running. * So don't do that. */ if ((int)(cn_reply->seq - b_seq) <= 0) continue; b_seq = cn_reply->seq; } else if ((all_devices || minor == reply->minor) && cn_reply->ack == (uint32_t)getpid() + 1) { // replies to drbdsetup packets and for this device. if ((int)(cn_reply->seq - r_seq) <= 0) continue; r_seq = cn_reply->seq; } else { /* or reply to configuration request of other drbdsetup */ continue; } } if( all_devices || minor == reply->minor ) { cont=cm->ep.proc_event(cn_reply->seq, wasb, reply); } } while(cont); free(buffer); close_cn(sk_nl); /* return code becomes exit code. * timeout? => exit 5 * else => exit 0 */ return (rr == -2) ? 5 : 0; } static int numeric_opt_usage(struct drbd_option *option, char* str, int strlen) { return snprintf(str,strlen," [{--%s|-%c} %lld ... %lld]", option->name, option->short_name, option->numeric_param.min, option->numeric_param.max); } static int handler_opt_usage(struct drbd_option *option, char* str, int strlen) { const char** handlers; int i, chars=0,first=1; chars += snprintf(str,strlen," [{--%s|-%c} {", option->name, option->short_name); handlers = option->handler_param.handler_names; for(i=0;ihandler_param.number_of_handlers;i++) { if(handlers[i]) { if(!first) chars += snprintf(str+chars,strlen,"|"); first=0; chars += snprintf(str+chars,strlen, "%s",handlers[i]); } } chars += snprintf(str+chars,strlen,"}]"); return chars; } static int bit_opt_usage(struct drbd_option *option, char* str, int strlen) { return snprintf(str,strlen," [{--%s|-%c}]", option->name, option->short_name); } static int string_opt_usage(struct drbd_option *option, char* str, int strlen) { return snprintf(str,strlen," [{--%s|-%c} ]", option->name, option->short_name); } static void numeric_opt_xml(struct drbd_option *option) { printf("\t\n"); } static void handler_opt_xml(struct drbd_option *option) { const char** handlers; int i; printf("\t\n"); } static void bit_opt_xml(struct drbd_option *option) { printf("\t\n"); } static void string_opt_xml(struct drbd_option *option) { printf("\t\n"); } static void config_usage(struct drbd_cmd *cm, enum usage_type ut) { struct drbd_argument *args; struct drbd_option *options; static char line[300]; int maxcol,col,prevcol,startcol,toolong; char *colstr; if(ut == XML) { printf("\n",cm->cmd); if( (args = cm->cp.args) ) { while (args->name) { printf("\t%s\n", args->name); args++; } } options = cm->cp.options; while (options && options->name) { options->xml_function(options); options++; } printf("\n"); return; } prevcol=col=0; maxcol=100; if((colstr=getenv("COLUMNS"))) maxcol=atoi(colstr)-1; col += snprintf(line+col, maxcol-col, " %s", cm->cmd); if( (args = cm->cp.args) ) { if(ut == BRIEF) { col += snprintf(line+col, maxcol-col, " [args...]"); } else { while (args->name) { col += snprintf(line+col, maxcol-col, " %s", args->name); args++; } } } if (col > maxcol) { printf("%s\n",line); col=0; } startcol=prevcol=col; options = cm->cp.options; if(ut == BRIEF) { if(options) col += snprintf(line+col, maxcol-col, " [opts...]"); printf("%-40s",line); return; } while (options && options->name) { col += options->usage_function(options, line+col, maxcol-col); if (col >= maxcol) { toolong = (prevcol == startcol); if( !toolong ) line[prevcol]=0; printf("%s\n",line); startcol=prevcol=col = sprintf(line," "); if( toolong) options++; } else { prevcol=col; options++; } } line[col]=0; printf("%s\n",line); } static void get_usage(struct drbd_cmd *cm, enum usage_type ut) { if(ut == BRIEF) { printf(" %-39s", cm->cmd); } else { printf(" %s\n", cm->cmd); } } static void events_usage(struct drbd_cmd *cm, enum usage_type ut) { struct option *lo; char line[41]; if(ut == BRIEF) { sprintf(line,"%s [opts...]", cm->cmd); printf(" %-39s",line); } else { printf(" %s", cm->cmd); lo = cm->ep.options; while(lo && lo->name) { printf(" [{--%s|-%c}]",lo->name,lo->val); lo++; } printf("\n"); } } static void print_command_usage(int i, const char *addinfo, enum usage_type ut) { if(ut != XML) printf("USAGE:\n"); commands[i].usage(commands+i,ut); if (addinfo) { printf("%s\n",addinfo); exit(20); } } static void print_usage(const char* addinfo) { size_t i; printf("\nUSAGE: %s device command arguments options\n\n" "Device is usually /dev/drbdX or /dev/drbd/X.\n" "General options: --create-device, --set-defaults\n" "\nCommands are:\n",cmdname); for (i = 0; i < ARRAY_SIZE(commands); i++) { commands[i].usage(commands+i,BRIEF); if(i%2==1) printf("\n"); } printf("\n\n" "To get more details about a command issue " "'drbdsetup help cmd'.\n" "\n"); /* printf("\n\nVersion: "PACKAGE_VERSION" (api:%d)\n%s\n", API_VERSION, drbd_buildtag()); */ if (addinfo) printf("\n%s\n",addinfo); exit(20); } static int open_cn() { int sk_nl; int err; struct sockaddr_nl my_nla; sk_nl = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); if (sk_nl == -1) { perror("socket() failed"); return -1; } my_nla.nl_family = AF_NETLINK; my_nla.nl_groups = -1; //cn_idx my_nla.nl_pid = getpid(); err = bind(sk_nl, (struct sockaddr *)&my_nla, sizeof(my_nla)); if (err == -1) { err = errno; perror("bind() failed"); switch(err) { case ENOENT: fprintf(stderr,"Connector module not loaded? Try 'modprobe cn'.\n"); break; case EPERM: fprintf(stderr,"Missing privileges? You should run this as root.\n"); break; } return -1; } return sk_nl; } static void prepare_nl_header(struct nlmsghdr* nl_hdr, int size) { static uint32_t cn_seq = 1; struct cn_msg *cn_hdr; cn_hdr = (struct cn_msg *)NLMSG_DATA(nl_hdr); /* fill the netlink header */ nl_hdr->nlmsg_len = NLMSG_LENGTH(size - sizeof(struct nlmsghdr)); nl_hdr->nlmsg_type = NLMSG_DONE; nl_hdr->nlmsg_flags = 0; nl_hdr->nlmsg_seq = cn_seq; nl_hdr->nlmsg_pid = getpid(); /* fill the connector header */ cn_hdr->id.val = CN_VAL_DRBD; cn_hdr->id.idx = cn_idx; cn_hdr->seq = cn_seq++; cn_hdr->ack = getpid(); cn_hdr->len = size - sizeof(struct nlmsghdr) - sizeof(struct cn_msg); } static int send_cn(int sk_nl, struct nlmsghdr* nl_hdr, int size) { int rr; prepare_nl_header(nl_hdr,size); rr = send(sk_nl,nl_hdr,nl_hdr->nlmsg_len,0); if( rr != (ssize_t)nl_hdr->nlmsg_len) { perror("send() failed"); return -1; } return rr; } static int receive_cn(int sk_nl, struct nlmsghdr* nl_hdr, int size, int timeout_ms) { struct pollfd pfd; int rr; pfd.fd = sk_nl; pfd.events = POLLIN; rr = poll(&pfd,1,timeout_ms); if(rr == 0) return -2; // timeout expired. rr = recv(sk_nl,nl_hdr,size,0); if( rr < 0 ) { perror("recv() failed"); return -1; } return rr; } int receive_reply_cn(int sk_nl, struct drbd_tag_list *tl, struct nlmsghdr* nl_hdr, int size, int timeout_ms) { struct cn_msg *request_cn_hdr; struct cn_msg *reply_cn_hdr; int rr; request_cn_hdr = (struct cn_msg *)NLMSG_DATA(tl->nl_header); reply_cn_hdr = (struct cn_msg *)NLMSG_DATA(nl_hdr); while(1) { rr = receive_cn(sk_nl,nl_hdr,size,timeout_ms); if( rr < 0 ) return rr; if(reply_cn_hdr->seq == request_cn_hdr->seq && reply_cn_hdr->ack == request_cn_hdr->ack+1 ) return rr; /* printf("INFO: got other message \n" "got seq: %d ; ack %d \n" "exp seq: %d ; ack %d \n", reply_cn_hdr->seq,reply_cn_hdr->ack, request_cn_hdr->seq,request_cn_hdr->ack); */ } return rr; } static int call_drbd(int sk_nl, struct drbd_tag_list *tl, struct nlmsghdr* nl_hdr, int size, int timeout_ms) { int rr; prepare_nl_header(tl->nl_header, (char*)tl->tag_list_cpos - (char*)tl->nl_header); rr = send(sk_nl,tl->nl_header,tl->nl_header->nlmsg_len,0); if( rr != (ssize_t)tl->nl_header->nlmsg_len) { perror("send() failed"); return -1; } rr = receive_reply_cn(sk_nl,tl,nl_hdr,size,timeout_ms); if( rr == -2) { fprintf(stderr,"No response from the DRBD driver!" " Is the module loaded?\n"); } return rr; } static void close_cn(int sk_nl) { close(sk_nl); } static int is_drbd_driver_missing(void) { struct stat sb; FILE *cn_idx_file; int err; cn_idx = CN_IDX_DRBD; cn_idx_file = fopen("/sys/module/drbd/parameters/cn_idx", "r"); if (cn_idx_file) { unsigned int idx; /* gcc is picky */ if (fscanf(cn_idx_file, "%u", &idx)) cn_idx = idx; fclose(cn_idx_file); } err = stat("/proc/drbd", &sb); if (!err) return 0; if (err == ENOENT) fprintf(stderr, "DRBD driver appears to be missing\n"); else fprintf(stderr, "Could not stat(\"/proc/drbd\"): %m\n"); return 1; } int main(int argc, char** argv) { unsigned minor; struct drbd_cmd *cmd; int rv=0; if (chdir("/")) { /* highly unlikely, but gcc is picky */ perror("cannot chdir /"); return -111; } cmdname = strrchr(argv[0],'/'); if (cmdname) argv[0] = ++cmdname; else cmdname = argv[0]; /* == '-' catches -h, --help, and similar */ if (argc > 1 && (!strcmp(argv[1],"help") || argv[1][0] == '-')) { if(argc >= 3) { cmd=find_cmd_by_name(argv[2]); if(cmd) print_command_usage(cmd-commands,NULL,FULL); else print_usage("unknown command"); exit(0); } } /* * The v83 drbdsetup takes the object to operate on as its first argument, * followed by the command. For forward compatibility, check if we got the * command name first. */ if (argc >= 3 && !find_cmd_by_name(argv[2]) && find_cmd_by_name(argv[1])) { char *swap = argv[1]; argv[1] = argv[2]; argv[2] = swap; } /* it is enough to set it, value is ignored */ if (getenv("DRBD_DEBUG_DUMP_ARGV")) debug_dump_argv = 1; resname = getenv("DRBD_RESOURCE"); if (argc > 1 && (!strcmp(argv[1],"xml"))) { if(argc >= 3) { cmd=find_cmd_by_name(argv[2]); if(cmd) print_command_usage(cmd-commands,NULL,XML); else print_usage("unknown command"); exit(0); } } if (argc < 3) print_usage(argc==1 ? 0 : " Insufficient arguments"); cmd=find_cmd_by_name(argv[2]); if (is_drbd_driver_missing()) { if (!strcmp(argv[2], "down") || !strcmp(argv[2], "secondary") || !strcmp(argv[2], "disconnect") || !strcmp(argv[2], "detach")) return 0; /* "down" succeeds even if drbd is missing */ fprintf(stderr, "do you need to load the module?\n" "try: modprobe drbd\n"); return 20; } if(cmd) { minor = dt_minor_of_dev(argv[1]); if (minor < 0) { fprintf(stderr, "Cannot determine minor device number of " "drbd device '%s'", argv[1]); exit(20); } lock_fd = dt_lock_drbd(minor); /* maybe rather canonicalize, using asprintf? */ devname = argv[1]; // by passing argc-2, argv+2 the function has the command name // in argv[0], e.g. "syncer" rv = cmd->function(cmd,minor,argc-2,argv+2); dt_unlock_drbd(lock_fd); } else { print_usage("invalid command"); } return rv; } drbd-utils-8.9.10/user/v83/drbdadm_minor_table.c0000644000175000017500000001030212466702074021274 0ustar apoikosapoikos/* drbdadm_minor_table.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. It was written by Johannes Thoma Copyright (C) 2002-2008, LINBIT Information Technologies GmbH. Copyright (C) 2002-2008, Philipp Reisner . Copyright (C) 2002-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This keeps track of which DRBD minor was configured in which * config file. This is required to have alternative config files * (-c switch) and userland event handlers. */ #include #include #include #include #include #include #include #include #include #include "config.h" #define MAX_MINOR 256 #define MAX_REGISTER_PATH_LEN 1024 /* buf has to be big enough to hold that path. * it is assumed that sprintf cannot fail :-] */ void linkname_from_minor(char *buf, int minor) { sprintf(buf, "%s/drbd-minor-%d.conf", DRBD_LIB_DIR, minor); } int unregister_minor(int minor) { char buf[255]; if (minor >= MAX_MINOR || minor < 0) { fprintf(stderr, "unregister_minor: minor too big (%d).\n", minor); return -1; } linkname_from_minor(buf, minor); if (unlink(buf) < 0) { if (errno != ENOENT) { perror("unlink"); return -1; } } return 0; } int register_minor(int minor, const char *path) { char buf[255]; struct stat stat_buf; int err = -1; if (minor >= MAX_MINOR || minor < 0) { fprintf(stderr, "register_minor: minor too big (%d).\n", minor); return -1; } linkname_from_minor(buf, minor); if (!path || !path[0]) fprintf(stderr, "Cannot register an empty path.\n"); else if (path[0] != '/') fprintf(stderr, "Absolute path expected, " "won't register relative path (%s).\n", path); else if (strlen(path) >= MAX_REGISTER_PATH_LEN) fprintf(stderr, "path (%s):\ntoo long to be registered, " "max path len supported: %u\n", path, MAX_REGISTER_PATH_LEN-1); else if (stat(path, &stat_buf) < 0) fprintf(stderr, "stat(%s): %m\n", path); else if (unlink(buf) < 0 && errno != ENOENT) fprintf(stderr, "unlink(%s): %m\n", buf); else if (symlink(path, buf) < 0) fprintf(stderr, "symlink(%s, %s): %m\n", path, buf); else /* it did work out after all! */ err = 0; return err; } /* This returns a static buffer containing the real * configuration file known to be used last for this minor. * If you need the return value longer, stuff it away with strdup. */ char *lookup_minor(int minor) { static char buf[255]; static char resolved_path[MAX_REGISTER_PATH_LEN+1]; struct stat stat_buf; ssize_t len; if (minor >= MAX_MINOR || minor < 0) { fprintf(stderr, "register_minor: minor too big (%d).\n", minor); return NULL; } linkname_from_minor(buf, minor); if (stat(buf, &stat_buf) < 0) { if (errno != ENOENT) fprintf(stderr, "stat(%s): %m\n", buf); return NULL; } len = readlink(buf, resolved_path, sizeof(resolved_path)-1); if (len < 0) { perror("readlink"); return NULL; } if (len >= MAX_REGISTER_PATH_LEN) fprintf(stderr, "readlink(%s): result has probably been truncated\n", buf); resolved_path[len] = '\0'; return resolved_path; } #ifdef TEST int main(int argc, char ** argv) { register_minor(1, "/etc/drbd-xy.conf"); register_minor(15, "/etc/drbd-82.conf"); register_minor(14, "/../../../../../../etc/drbd-82.conf"); printf("Minor 1 is %s.\n", lookup_minor(1)); printf("Minor 2 is %s.\n", lookup_minor(2)); printf("Minor 14 is %s.\n", lookup_minor(14)); printf("Minor 15 is %s.\n", lookup_minor(15)); return 0; } #endif drbd-utils-8.9.10/user/v83/drbdadm_scanner.fl0000644000175000017500000002035713002347271020613 0ustar apoikosapoikos%{ #include #include #include #include "drbdadm_parser.h" #include "drbdadm.h" #include "drbdtool_common.h" void long_string(char* text); void long_dqstring(char* text); void err_dqstring(char* text); #if 0 #define DP printf("'%s' ",yytext) #else #define DP #endif #define CP yylval.txt = strdup(yytext); yylval.rc = R_NO_CHECK #define RC(N) yylval.rc = R_ ## N #define YY_NO_INPUT 1 #define YY_NO_UNPUT 1 #ifndef YY_FLEX_SUBMINOR_VERSION #define MAX_INCLUDE_DEPTH 10 YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; #endif %} %option noyywrap %option nounput NUM [0-9]{1,8}[MKGs]? SNUMB [0-9]{1,3} IPV4ADDR ({SNUMB}"."){3}{SNUMB} HEX4 [0-9a-fA-F]{1,4} IPV6ADDR ((({HEX4}":"){0,5}{HEX4})?":"{HEX4}?":"({HEX4}(":"{HEX4}){0,5})?("%"{STRING})?)|("::"[fF]{4}":"{IPV4ADDR}) WS [ \t\r] OPCHAR [{};\[\]:] DQSTRING \"([^\"\\\n]|\\[^\n]){0,255}\" LONG_DQSTRING \"([^\"\\\n]|\\[^\n]){255}. ERR_DQSTRING \"([^\"\\\n]|\\[^\n]){0,255}[\\\n] STRING [a-zA-Z0-9/._-]{1,80} LONG_STRING [a-zA-Z0-9/._-]{81} %% \n { line++; } \#.* /* ignore comments */ {WS} /* ignore whitespaces */ {OPCHAR} { DP; return yytext[0]; } on { DP; return TK_ON; } ignore-on { DP; return TK_IGNORE; } stacked-on-top-of { DP; return TK_STACKED; } floating { DP; return TK_FLOATING; } no { DP; return TK_NO; } net { DP; return TK_NET; } yes { DP; return TK_YES; } ask { DP; return TK_ASK; } skip { DP; return TK_SKIP; } disk { DP; return TK_DISK; } proxy { DP; return TK_PROXY; } minor { DP; return TK_MINOR; } inside { DP; return TK_INSIDE; } syncer { DP; return TK_SYNCER; } device { DP; return TK_DEVICE; } global { DP; return TK_GLOBAL; } common { DP; return TK_COMMON; } outside { DP; return TK_OUTSIDE; } address { DP; return TK_ADDRESS; } startup { DP; return TK_STARTUP; } include { DP; return TK_INCLUDE; } handlers { DP; return TK_HANDLER; } protocol { DP; return TK_PROTOCOL; } minor-count { DP; return TK_MINOR_COUNT; } disable-ip-verification { DP; return TK_DISABLE_IP_VERIFICATION;} dialog-refresh { DP; return TK_DIALOG_REFRESH; } resource { DP; return TK_RESOURCE; } meta-disk { DP; return TK_META_DISK; } flexible-meta-disk { DP; return TK_FLEX_META_DISK; } usage-count { DP; return TK_USAGE_COUNT; } _is_default { DP; return TK__IS_DEFAULT; } _this_host { DP; return TK__THIS_HOST; } _remote_host { DP; return TK__REMOTE_HOST; } sci { DP; CP; return TK_SCI; } ssocks { DP; CP; return TK_SSOCKS; } sdp { DP; CP; return TK_SDP; } ipv4 { DP; CP; return TK_IPV4; } ipv6 { DP; CP; return TK_IPV6; } size { DP; CP; RC(DISK_SIZE); return TK_DISK_OPTION; } on-io-error { DP; CP; return TK_DISK_OPTION; } fencing { DP; CP; return TK_DISK_OPTION; } max-bio-bvecs { DP; CP; return TK_DISK_OPTION; } disk-timeout { DP; CP; return TK_DISK_OPTION; } use-bmbv { DP; CP; return TK_DISK_SWITCH; } no-disk-barrier { DP; CP; return TK_DISK_SWITCH; } no-disk-flushes { DP; CP; return TK_DISK_SWITCH; } no-disk-drain { DP; CP; return TK_DISK_SWITCH; } no-md-flushes { DP; CP; return TK_DISK_SWITCH; } timeout { DP; CP; RC(TIMEOUT); return TK_NET_OPTION; } ko-count { DP; CP; RC(KO_COUNT); return TK_NET_OPTION; } ping-int { DP; CP; RC(PING_INT); return TK_NET_OPTION; } max-buffers { DP; CP; RC(MAX_BUFFERS); return TK_NET_OPTION;} sndbuf-size { DP; CP; RC(SNDBUF_SIZE); return TK_NET_OPTION | TK_PROXY_GROUP;} rcvbuf-size { DP; CP; RC(RCVBUF_SIZE); return TK_NET_OPTION | TK_PROXY_GROUP;} connect-int { DP; CP; RC(CONNECT_INT); return TK_NET_OPTION;} cram-hmac-alg { DP; CP; return TK_NET_OPTION; } shared-secret { DP; CP; return TK_NET_OPTION; } max-epoch-size { DP; CP; RC(MAX_EPOCH_SIZE); return TK_NET_OPTION;} after-sb-[012]pri { DP; CP; return TK_NET_OPTION; } rr-conflict { DP; CP; return TK_NET_OPTION; } ping-timeout { DP; CP; return TK_NET_OPTION | TK_PROXY_GROUP;} unplug-watermark { DP; CP; return TK_NET_OPTION; } data-integrity-alg { DP; CP; return TK_NET_OPTION; } on-congestion { DP; CP; return TK_NET_OPTION; } congestion-fill { DP; CP; RC(CONG_FILL); return TK_NET_OPTION; } congestion-extents { DP; CP; RC(CONG_EXTENTS); return TK_NET_OPTION;} allow-two-primaries { DP; CP; return TK_NET_SWITCH; } always-asbp { DP; CP; return TK_NET_SWITCH; } no-tcp-cork { DP; CP; return TK_NET_SWITCH; } discard-my-data { DP; CP; return TK_NET_DELEGATE; } rate { DP; CP; RC(RATE); return TK_SYNCER_OPTION; } after { DP; CP; return TK_SYNCER_OPTION; } verify-alg { DP; CP; return TK_SYNCER_OPTION; } csums-alg { DP; CP; return TK_SYNCER_OPTION; } al-extents { DP; CP; RC(AL_EXTENTS); return TK_SYNCER_OPTION;} cpu-mask { DP; CP; return TK_SYNCER_OPTION; } use-rle { DP; CP; return TK_SYNCER_SWITCH; } delay-probe-volume { DP; CP; return TK_DEPRECATED_OPTION; } delay-probe-interval { DP; CP; return TK_DEPRECATED_OPTION; } c-plan-ahead { DP; CP; RC(C_PLAN_AHEAD); return TK_SYNCER_OPTION; } c-delay-target { DP; CP; RC(C_DELAY_TARGET); return TK_SYNCER_OPTION; } c-fill-target { DP; CP; RC(C_FILL_TARGET); return TK_SYNCER_OPTION; } c-max-rate { DP; CP; RC(C_MAX_RATE); return TK_SYNCER_OPTION; } c-min-rate { DP; CP; RC(C_MIN_RATE); return TK_SYNCER_OPTION; } throttle-threshold { DP; CP; return TK_DEPRECATED_OPTION; } hold-off-threshold { DP; CP; return TK_DEPRECATED_OPTION; } on-no-data-accessible { DP; CP; return TK_SYNCER_OPTION; } wfc-timeout { DP; CP; RC(WFC_TIMEOUT); return TK_STARTUP_OPTION;} degr-wfc-timeout { DP; CP; RC(DEGR_WFC_TIMEOUT); return TK_STARTUP_OPTION;} outdated-wfc-timeout { DP; CP; RC(OUTDATED_WFC_TIMEOUT); return TK_STARTUP_OPTION;} stacked-timeouts { DP; return TK_STARTUP_DELEGATE; } become-primary-on { DP; return TK_STARTUP_DELEGATE; } wait-after-sb { DP; CP; return TK_STARTUP_SWITCH; } pri-on-incon-degr { DP; CP; return TK_HANDLER_OPTION; } pri-lost-after-sb { DP; CP; return TK_HANDLER_OPTION; } pri-lost { DP; CP; return TK_HANDLER_OPTION; } initial-split-brain { DP; CP; return TK_HANDLER_OPTION; } split-brain { DP; CP; return TK_HANDLER_OPTION; } outdate-peer { DP; CP; return TK_HANDLER_OPTION; } fence-peer { DP; CP; return TK_HANDLER_OPTION; } local-io-error { DP; CP; return TK_HANDLER_OPTION; } before-resync-target { DP; CP; return TK_HANDLER_OPTION; } after-resync-target { DP; CP; return TK_HANDLER_OPTION; } before-resync-source { DP; CP; return TK_HANDLER_OPTION; } memlimit { DP; CP; return TK_PROXY_OPTION | TK_PROXY_GROUP; } read-loops { DP; CP; return TK_PROXY_OPTION | TK_PROXY_GROUP; } compression { DP; CP; return TK_PROXY_OPTION | TK_PROXY_GROUP; } plugin { DP; CP; return TK_PROXY_DELEGATE; } out-of-sync { DP; CP; return TK_HANDLER_OPTION; } {IPV4ADDR} { DP; CP; return TK_IPADDR; } {IPV6ADDR} { DP; CP; return TK_IPADDR6; } {NUM} { DP; CP; return TK_INTEGER; } {DQSTRING} { unescape(yytext); DP; CP; return TK_STRING; } {STRING} { DP; CP; return TK_STRING; } {LONG_STRING} { return TK_ERR_STRING_TOO_LONG; } {LONG_DQSTRING} { return TK_ERR_DQSTRING_TOO_LONG; } {ERR_DQSTRING} { return TK_ERR_DQSTRING; } . { DP; return TK_ELSE; } %% /* Compatibility cruft for flex version 2.5.4a */ #ifndef YY_FLEX_SUBMINOR_VERSION /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) { fprintf( stderr, "Includes nested too deeply" ); exit( 1 ); } include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER; yy_switch_to_buffer(new_buffer); BEGIN(INITIAL); } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void yypop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; if ( --include_stack_ptr < 0 ) { fprintf( stderr, "error in flex compat code\n" ); exit( 1 ); } yy_delete_buffer(YY_CURRENT_BUFFER ); yy_switch_to_buffer(include_stack[include_stack_ptr]); } #endif void my_yypush_buffer_state(FILE *f) { /* Since we do not have YY_BUF_SIZE outside of the flex generated file.*/ yypush_buffer_state(yy_create_buffer(f, YY_BUF_SIZE)); } drbd-utils-8.9.10/user/v83/drbdadm_parser.c0000644000175000017500000012404313016771235020302 0ustar apoikosapoikos/* * drbdadm_parser.c a hand crafted parser This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2006-2008, LINBIT Information Technologies GmbH Copyright (C) 2006-2008, Philipp Reisner Copyright (C) 2006-2008, Lars Ellenberg drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include "drbdadm.h" #include "linux/drbd_limits.h" #include "drbdtool_common.h" #include "drbdadm_parser.h" #include "shared_parser.h" YYSTYPE yylval; ///////////////////// static int c_section_start; void my_parse(void); struct d_name *names_from_str(char* str) { struct d_name *names; names = malloc(sizeof(struct d_name)); names->next = NULL; names->name = strdup(str); return names; } char *_names_to_str_c(char* buffer, struct d_name *names, char c) { int n = 0; if (!names) return buffer; while (1) { n += snprintf(buffer + n, NAMES_STR_SIZE - n, "%s", names->name); names = names->next; if (!names) return buffer; n += snprintf(buffer + n, NAMES_STR_SIZE - n, "%c", c); } } char *_names_to_str(char* buffer, struct d_name *names) { return _names_to_str_c(buffer, names, ' '); } int name_in_names(char *name, struct d_name *names) { while (names) { if (!strcmp(names->name, name)) return 1; names = names->next; } return 0; } void free_names(struct d_name *names) { struct d_name *nf; while (names) { nf = names->next; free(names->name); free(names); names = nf; } } static void append_names(struct d_name **head, struct d_name ***last, struct d_name *to_copy) { struct d_name *new; while (to_copy) { new = malloc(sizeof(struct d_name)); if (!*head) *head = new; new->name = strdup(to_copy->name); new->next = NULL; if (*last) **last = new; *last = &new->next; to_copy = to_copy->next; } } struct d_name *concat_names(struct d_name *to_copy1, struct d_name *to_copy2) { struct d_name *head = NULL, **last = NULL; append_names(&head, &last, to_copy1); append_names(&head, &last, to_copy2); return head; } void m_strtoll_range(const char *s, char def_unit, const char *name, unsigned long long min, unsigned long long max) { unsigned long long r = m_strtoll(s, def_unit); char unit[] = { def_unit > '1' ? def_unit : 0, 0 }; if (min > r || r > max) { fprintf(stderr, "%s:%d: %s %s => %llu%s out of range [%llu..%llu]%s.\n", config_file, fline, name, s, r, unit, min, max, unit); if (config_valid <= 1) { config_valid = 0; return; } } if (DEBUG_RANGE_CHECK) { fprintf(stderr, "%s:%d: %s %s => %llu%s in range [%llu..%llu]%s.\n", config_file, fline, name, s, r, unit, min, max, unit); } } void range_check(const enum range_checks what, const char *name, const char *value) { switch (what) { case R_NO_CHECK: break; default: fprintf(stderr, "%s:%d: unknown range for %s => %s\n", config_file, fline, name, value); break; case R_MINOR_COUNT: m_strtoll_range(value, 1, name, DRBD_MINOR_COUNT_MIN, DRBD_MINOR_COUNT_MAX); break; case R_DIALOG_REFRESH: m_strtoll_range(value, 1, name, DRBD_DIALOG_REFRESH_MIN, DRBD_DIALOG_REFRESH_MAX); break; case R_DISK_SIZE: m_strtoll_range(value, 's', name, DRBD_DISK_SIZE_SECT_MIN, DRBD_DISK_SIZE_SECT_MAX); break; case R_TIMEOUT: m_strtoll_range(value, 1, name, DRBD_TIMEOUT_MIN, DRBD_TIMEOUT_MAX); break; case R_CONNECT_INT: m_strtoll_range(value, 1, name, DRBD_CONNECT_INT_MIN, DRBD_CONNECT_INT_MAX); break; case R_PING_INT: m_strtoll_range(value, 1, name, DRBD_PING_INT_MIN, DRBD_PING_INT_MAX); break; case R_MAX_BUFFERS: m_strtoll_range(value, 1, name, DRBD_MAX_BUFFERS_MIN, DRBD_MAX_BUFFERS_MAX); break; case R_MAX_EPOCH_SIZE: m_strtoll_range(value, 1, name, DRBD_MAX_EPOCH_SIZE_MIN, DRBD_MAX_EPOCH_SIZE_MAX); break; case R_SNDBUF_SIZE: m_strtoll_range(value, 1, name, DRBD_SNDBUF_SIZE_MIN, DRBD_SNDBUF_SIZE_MAX); break; case R_RCVBUF_SIZE: m_strtoll_range(value, 1, name, DRBD_RCVBUF_SIZE_MIN, DRBD_RCVBUF_SIZE_MAX); break; case R_KO_COUNT: m_strtoll_range(value, 1, name, DRBD_KO_COUNT_MIN, DRBD_KO_COUNT_MAX); break; case R_RATE: m_strtoll_range(value, 'K', name, DRBD_RATE_MIN, DRBD_RATE_MAX); break; case R_AL_EXTENTS: m_strtoll_range(value, 1, name, DRBD_AL_EXTENTS_MIN, DRBD_AL_EXTENTS_MAX); break; case R_PORT: m_strtoll_range(value, 1, name, DRBD_PORT_MIN, DRBD_PORT_MAX); break; /* FIXME not yet implemented! case R_META_IDX: m_strtoll_range(value, 1, name, DRBD_META_IDX_MIN, DRBD_META_IDX_MAX); break; */ case R_WFC_TIMEOUT: m_strtoll_range(value, 1, name, DRBD_WFC_TIMEOUT_MIN, DRBD_WFC_TIMEOUT_MAX); break; case R_DEGR_WFC_TIMEOUT: m_strtoll_range(value, 1, name, DRBD_DEGR_WFC_TIMEOUT_MIN, DRBD_DEGR_WFC_TIMEOUT_MAX); break; case R_OUTDATED_WFC_TIMEOUT: m_strtoll_range(value, 1, name, DRBD_OUTDATED_WFC_TIMEOUT_MIN, DRBD_OUTDATED_WFC_TIMEOUT_MAX); break; case R_C_PLAN_AHEAD: m_strtoll_range(value, 1, name, DRBD_C_PLAN_AHEAD_MIN, DRBD_C_PLAN_AHEAD_MAX); break; case R_C_DELAY_TARGET: m_strtoll_range(value, 1, name, DRBD_C_DELAY_TARGET_MIN, DRBD_C_DELAY_TARGET_MAX); break; case R_C_FILL_TARGET: m_strtoll_range(value, 's', name, DRBD_C_FILL_TARGET_MIN, DRBD_C_FILL_TARGET_MAX); break; case R_C_MAX_RATE: m_strtoll_range(value, 'k', name, DRBD_C_MAX_RATE_MIN, DRBD_C_MAX_RATE_MAX); break; case R_C_MIN_RATE: m_strtoll_range(value, 'k', name, DRBD_C_MIN_RATE_MIN, DRBD_C_MIN_RATE_MAX); break; case R_CONG_FILL: m_strtoll_range(value, 's', name, DRBD_CONG_FILL_MIN, DRBD_CONG_FILL_MAX); break; case R_CONG_EXTENTS: m_strtoll_range(value, 1, name, DRBD_CONG_EXTENTS_MIN, DRBD_CONG_EXTENTS_MAX); break; } } struct d_option *new_opt(char *name, char *value) { struct d_option *cn = malloc(sizeof(struct d_option)); /* fprintf(stderr,"%s:%d: %s = %s\n",config_file,line,name,value); */ cn->name = name; cn->value = value; cn->mentioned = 0; cn->is_default = 0; cn->is_escaped = 0; return cn; } static void derror(struct d_host_info *host, struct d_resource *res, char *text) { config_valid = 0; fprintf(stderr, "%s:%d: in resource %s, on %s { ... }:" " '%s' keyword missing.\n", config_file, c_section_start, res->name, names_to_str(host->on_hosts), text); } void pdperror(char *text) { config_valid = 0; fprintf(stderr, "%s:%d: in proxy plugin section: %s.\n", config_file, line, text); exit(E_CONFIG_INVALID); } static void pperror(struct d_host_info *host, struct d_proxy_info *proxy, char *text) { config_valid = 0; fprintf(stderr, "%s:%d: in section: on %s { proxy on %s { ... } }:" " '%s' keyword missing.\n", config_file, c_section_start, names_to_str(host->on_hosts), names_to_str(proxy->on_hosts), text); } #define typecheck(type,x) \ ({ type __dummy; \ typeof(x) __dummy2; \ (void)(&__dummy == &__dummy2); \ 1; \ }) #define for_each_host(h_,hosts_) \ for ( ({ typecheck(struct d_name*, h_); \ h_ = hosts_; }); \ h_; h_ = h_->next) /* * for check_uniq: check uniqueness of * resource names, ip:port, node:disk and node:device combinations * as well as resource:section ... * hash table to test for uniqueness of these values... * 256 (max minors) * *( * 2 (host sections) * 4 (res ip:port node:disk node:device) * + 4 (other sections) * + some more, * if we want to check for scoped uniqueness of *every* option * ) * since nobody (?) will actually use more than a dozen minors, * this should be more than enough. */ struct hsearch_data global_htable; void check_uniq_init(void) { memset(&global_htable, 0, sizeof(global_htable)); if (!hcreate_r(256 * ((2 * 4) + 4), &global_htable)) { fprintf(stderr, "Insufficient memory.\n"); exit(E_EXEC_ERROR); }; } /* some settings need only be unique within one resource definition. * we need currently about 8 + (number of host) * 8 entries, * 200 should be much more than enough. */ struct hsearch_data per_resource_htable; void check_upr_init(void) { static int created = 0; if (config_valid >= 2) return; if (created) hdestroy_r(&per_resource_htable); memset(&per_resource_htable, 0, sizeof(per_resource_htable)); if (!hcreate_r(256, &per_resource_htable)) { fprintf(stderr, "Insufficient memory.\n"); exit(E_EXEC_ERROR); }; created = 1; } /* FIXME * strictly speaking we don't need to check for uniqueness of disk and device names, * but for uniqueness of their major:minor numbers ;-) */ int vcheck_uniq(struct hsearch_data *ht, const char *what, const char *fmt, va_list ap) { int rv; ENTRY e, *ep; e.key = e.data = ep = NULL; /* if we are done parsing the config file, * switch off this paranoia */ if (config_valid >= 2) return 1; rv = vasprintf(&e.key, fmt, ap); if (rv < 0) { perror("vasprintf"); exit(E_THINKO); } if (EXIT_ON_CONFLICT && !what) { fprintf(stderr, "Oops, unset argument in %s:%d.\n", __FILE__, __LINE__); exit(E_THINKO); } m_asprintf((char **)&e.data, "%s:%u", config_file, fline); hsearch_r(e, FIND, &ep, ht); //fprintf(stderr, "FIND %s: %p\n", e.key, ep); if (ep) { if (what) { fprintf(stderr, "%s: conflicting use of %s '%s' ...\n" "%s: %s '%s' first used here.\n", (char *)e.data, what, ep->key, (char *)ep->data, what, ep->key); } free(e.key); free(e.data); config_valid = 0; } else { //fprintf(stderr, "ENTER %s\t=>\t%s\n", e.key, (char *)e.data); hsearch_r(e, ENTER, &ep, ht); if (!ep) { fprintf(stderr, "hash table entry (%s => %s) failed\n", e.key, (char *)e.data); exit(E_THINKO); } ep = NULL; } if (EXIT_ON_CONFLICT && ep) exit(E_CONFIG_INVALID); return !ep; } void check_meta_disk(struct d_host_info *host) { struct d_name *h; if (strcmp(host->meta_disk, "internal") != 0) { /* external */ if (host->meta_index == NULL) { fprintf(stderr, "%s:%d: expected 'meta-disk = %s [index]'.\n", config_file, fline, host->meta_disk); } /* index either some number, or "flexible" */ for_each_host(h, host->on_hosts) check_uniq("meta-disk", "%s:%s[%s]", h->name, host->meta_disk, host->meta_index); } else if (host->meta_index) { /* internal */ if (strcmp(host->meta_index, "flexible") != 0) { /* internal, not flexible, but index given: no sir! */ fprintf(stderr, "%s:%d: no index allowed with 'meta-disk = internal'.\n", config_file, fline); } /* else internal, flexible: fine */ } else { /* internal, not flexible */ host->meta_index = strdup("internal"); } } static void pe_expected(const char *exp) { const char *s = yytext; fprintf(stderr, "%s:%u: Parse error: '%s' expected,\n\t" "but got '%.20s%s'\n", config_file, line, exp, s, strlen(s) > 20 ? "..." : ""); exit(E_CONFIG_INVALID); } static void check_string_error(int got) { const char *msg; switch(got) { case TK_ERR_STRING_TOO_LONG: msg = "Token too long"; break; case TK_ERR_DQSTRING_TOO_LONG: msg = "Double quoted string too long"; break; case TK_ERR_DQSTRING: msg = "Unterminated double quoted string\n we don't allow embedded newlines\n "; break; default: return; } fprintf(stderr,"%s:%u: %s >>>%.20s...<<<\n", config_file, line, msg, yytext); exit(E_CONFIG_INVALID); } static void pe_expected_got(const char *exp, int got) { static char tmp[2] = "\0"; const char *s = yytext; if (exp[0] == '\'' && exp[1] && exp[2] == '\'' && exp[3] == 0) { tmp[0] = exp[1]; } fprintf(stderr, "%s:%u: Parse error: '%s' expected,\n\t" "but got '%.20s%s' (TK %d)\n", config_file, line, tmp[0] ? tmp : exp, s, strlen(s) > 20 ? "..." : "", got); exit(E_CONFIG_INVALID); } #define EXP(TOKEN1) \ ({ \ int token; \ token = yylex(); \ if (token != TOKEN1) { \ if (TOKEN1 == TK_STRING) \ check_string_error(token); \ pe_expected_got( #TOKEN1, token); \ } \ token; \ }) static void expect_STRING_or_INT(void) { int token = yylex(); switch(token) { case TK_INTEGER: case TK_STRING: break; case TK_ON: yylval.txt = strdup(yytext); break; default: check_string_error(token); pe_expected_got("TK_STRING | TK_INTEGER", token); } } static void parse_global(void) { fline = line; check_uniq("global section", "global"); if (config) { fprintf(stderr, "%s:%u: You should put the global {} section\n\t" "in front of any resource {} section\n", config_file, line); } EXP('{'); while (1) { int token = yylex(); fline = line; switch (token) { case TK_DISABLE_IP_VERIFICATION: global_options.disable_ip_verification = 1; break; case TK_MINOR_COUNT: EXP(TK_INTEGER); range_check(R_MINOR_COUNT, "minor-count", yylval.txt); global_options.minor_count = atoi(yylval.txt); break; case TK_DIALOG_REFRESH: EXP(TK_INTEGER); range_check(R_DIALOG_REFRESH, "dialog-refresh", yylval.txt); global_options.dialog_refresh = atoi(yylval.txt); break; case TK_USAGE_COUNT: switch (yylex()) { case TK_YES: global_options.usage_count = UC_YES; break; case TK_NO: global_options.usage_count = UC_NO; break; case TK_ASK: global_options.usage_count = UC_ASK; break; default: pe_expected("yes | no | ask"); } break; case '}': return; default: pe_expected("dialog-refresh | minor-count | " "disable-ip-verification"); } EXP(';'); } } static void check_and_change_deprecated_alias(char **name, int token_option) { if (token_option == TK_HANDLER_OPTION) { if (!strcmp(*name, "outdate-peer")) { /* fprintf(stder, "config file:line: name is deprecated ...\n") */ free(*name); *name = strdup("fence-peer"); } } } static struct d_option *parse_options_d(int token_switch, int token_option, int token_delegate, void (*delegate)(void*), void *ctx) { char *opt_name; int token, token_group; enum range_checks rc; struct d_option *options = NULL, *ro = NULL; c_section_start = line; fline = line; while (1) { token_group = yylex(); /* Keep the higher bits in token_option, remove them from token. */ token = REMOVE_GROUP_FROM_TOKEN(token_group); fline = line; if (token == token_switch) { options = APPEND(options, new_opt(yylval.txt, NULL)); } else if (token == token_option || GET_TOKEN_GROUP(token_option & token_group)) { opt_name = yylval.txt; check_and_change_deprecated_alias(&opt_name, token_option); rc = yylval.rc; expect_STRING_or_INT(); range_check(rc, opt_name, yylval.txt); ro = new_opt(opt_name, yylval.txt); options = APPEND(options, ro); } else if (token == token_delegate || GET_TOKEN_GROUP(token_delegate & token_group)) { delegate(ctx); continue; } else if (token == TK_DEPRECATED_OPTION) { /* fprintf(stderr, "Warn: Ignoring deprecated option '%s'\n", yylval.txt); */ expect_STRING_or_INT(); } else if (token == '}') { return options; } else { pe_expected("an option keyword"); } switch (yylex()) { case TK__IS_DEFAULT: ro->is_default = 1; EXP(';'); break; case ';': break; default: pe_expected("_is_default | ;"); } } } static struct d_option *parse_options(int token_switch, int token_option) { return parse_options_d(token_switch, token_option, 0, NULL, NULL); } static void __parse_address(char** addr, char** port, char** af) { switch(yylex()) { case TK_SCI: /* 'ssocks' was names 'sci' before. */ if (af) *af = strdup("ssocks"); EXP(TK_IPADDR); break; case TK_SSOCKS: case TK_SDP: case TK_IPV4: if (af) *af = yylval.txt; EXP(TK_IPADDR); break; case TK_IPV6: if (af) *af = yylval.txt; EXP('['); EXP(TK_IPADDR6); break; case TK_IPADDR: if (af) *af = strdup("ipv4"); break; /* case '[': // Do not foster people's laziness ;) EXP(TK_IPADDR6); *af = strdup("ipv6"); break; */ default: pe_expected("ssocks | sdp | ipv4 | ipv6 | "); } if (addr) *addr = yylval.txt; if (af && !strcmp(*af, "ipv6")) EXP(']'); EXP(':'); EXP(TK_INTEGER); if (port) *port = yylval.txt; range_check(R_PORT, "port", yylval.txt); } static void parse_address(struct d_name *on_hosts, char** addr, char** port, char** af) { struct d_name *h; __parse_address(addr, port, af); if (addr_scope_local(*addr)) for_each_host(h, on_hosts) check_uniq("IP", "%s:%s:%s", h->name, *addr, *port); else check_uniq("IP", "%s:%s", *addr, *port); EXP(';'); } static void parse_hosts(struct d_name **pnp, char delimeter) { char errstr[20]; struct d_name *name; int hosts = 0; int token; while (1) { token = yylex(); switch (token) { case TK_STRING: name = malloc(sizeof(struct d_name)); name->name = yylval.txt; name->next = NULL; *pnp = name; pnp = &name->next; hosts++; break; default: if (token == delimeter) { if (!hosts) pe_expected_got("TK_STRING", token); return; } else { sprintf(errstr, "TK_STRING | '%c'", delimeter); pe_expected_got(errstr, token); } } } } static void parse_proxy_section(struct d_host_info *host) { struct d_proxy_info *proxy; proxy=calloc(1,sizeof(struct d_proxy_info)); host->proxy = proxy; EXP(TK_ON); parse_hosts(&proxy->on_hosts, '{'); while (1) { switch (yylex()) { case TK_INSIDE: parse_address(proxy->on_hosts, &proxy->inside_addr, &proxy->inside_port, &proxy->inside_af); break; case TK_OUTSIDE: parse_address(proxy->on_hosts, &proxy->outside_addr, &proxy->outside_port, &proxy->outside_af); break; case '}': goto break_loop; default: pe_expected("inside | outside"); } } break_loop: if (!proxy->inside_addr) pperror(host, proxy, "inside"); if (!proxy->outside_addr) pperror(host, proxy, "outside"); return; } static void parse_meta_disk(char **disk, char** index) { EXP(TK_STRING); *disk = yylval.txt; if (strcmp("internal", yylval.txt)) { EXP('['); EXP(TK_INTEGER); *index = yylval.txt; EXP(']'); EXP(';'); } else { EXP(';'); } } static void check_minor_nonsense(const char *devname, const int explicit_minor) { if (!devname) return; /* if devname is set, it starts with /dev/drbd */ if (only_digits(devname + 9)) { int m = strtol(devname + 9, NULL, 10); if (m == explicit_minor) return; fprintf(stderr, "%s:%d: explicit minor number must match with device name\n" "\tTry \"device /dev/drbd%u minor %u;\",\n" "\tor leave off either device name or explicit minor.\n" "\tArbitrary device names must start with /dev/drbd_\n" "\tmind the '_'! (/dev/ is optional, but drbd_ is required)\n", config_file, fline, explicit_minor, explicit_minor); config_valid = 0; return; } else if (devname[9] == '_') return; fprintf(stderr, "%s:%d: arbitrary device name must start with /dev/drbd_\n" "\tmind the '_'! (/dev/ is optional, but drbd_ is required)\n", config_file, fline); config_valid = 0; return; } static void parse_device(struct d_name* on_hosts, unsigned *minor, char **device) { struct d_name *h; int m; switch (yylex()) { case TK_STRING: if (!strncmp("drbd", yylval.txt, 4)) { m_asprintf(device, "/dev/%s", yylval.txt); free(yylval.txt); } else *device = yylval.txt; if (strncmp("/dev/drbd", *device, 9)) { fprintf(stderr, "%s:%d: device name must start with /dev/drbd\n" "\t(/dev/ is optional, but drbd is required)\n", config_file, fline); config_valid = 0; /* no goto out yet, * as that would additionally throw a parse error */ } switch (yylex()) { default: pe_expected("minor | ;"); /* fall through */ case ';': m = dt_minor_of_dev(*device); if (m < 0) { fprintf(stderr, "%s:%d: no minor given nor device name contains a minor number\n", config_file, fline); config_valid = 0; } *minor = m; goto out; case TK_MINOR: ; /* double fall through */ } case TK_MINOR: EXP(TK_INTEGER); *minor = atoi(yylval.txt); EXP(';'); /* if both device name and minor number are explicitly given, * force /dev/drbd or /dev/drbd_ */ check_minor_nonsense(*device, *minor); } out: for_each_host(h, on_hosts) { check_uniq("device-minor", "device-minor:%s:%u", h->name, *minor); if (*device) check_uniq("device", "device:%s:%s", h->name, *device); } } enum parse_host_section_flags { REQUIRE_ALL = 1, BY_ADDRESS = 2, }; static void parse_host_section(struct d_resource *res, struct d_name* on_hosts, enum parse_host_section_flags flags) { struct d_host_info *host; struct d_name *h; int in_braces = 1; c_section_start = line; fline = line; host=calloc(1,sizeof(struct d_host_info)); host->on_hosts = on_hosts; host->config_line = c_section_start; host->device_minor = -1; if (flags & BY_ADDRESS) { /* floating
{} */ char *fake_uname = NULL; int token; host->by_address = 1; __parse_address(&host->address, &host->port, &host->address_family); check_uniq("IP", "%s:%s", host->address, host->port); if (!strcmp(host->address_family, "ipv6")) m_asprintf(&fake_uname, "ipv6 [%s]:%s", host->address, host->port); else m_asprintf(&fake_uname, "%s:%s", host->address, host->port); on_hosts = names_from_str(fake_uname); host->on_hosts = on_hosts; token = yylex(); switch(token) { case '{': break; case ';': in_braces = 0; break; default: pe_expected_got("{ | ;", token); } } for_each_host(h, on_hosts) check_upr("host section", "%s: on %s", res->name, h->name); res->all_hosts = APPEND(res->all_hosts, host); while (in_braces) { int token = yylex(); fline = line; switch (token) { case TK_DISK: for_each_host(h, on_hosts) check_upr("disk statement", "%s:%s:disk", res->name, h->name); EXP(TK_STRING); host->disk = yylval.txt; for_each_host(h, on_hosts) check_uniq("disk", "disk:%s:%s", h->name, yylval.txt); EXP(';'); break; case TK_DEVICE: for_each_host(h, on_hosts) check_upr("device statement", "%s:%s:device", res->name, h->name); parse_device(on_hosts, &host->device_minor, &host->device); break; case TK_ADDRESS: if (host->by_address) { fprintf(stderr, "%s:%d: address statement not allowed for floating {} host sections\n", config_file, fline); config_valid = 0; exit(E_CONFIG_INVALID); } for_each_host(h, on_hosts) check_upr("address statement", "%s:%s:address", res->name, h->name); parse_address(on_hosts, &host->address, &host->port, &host->address_family); range_check(R_PORT, "port", host->port); break; case TK_META_DISK: for_each_host(h, on_hosts) check_upr("meta-disk statement", "%s:%s:meta-disk", res->name, h->name); parse_meta_disk(&host->meta_disk, &host->meta_index); check_meta_disk(host); break; case TK_FLEX_META_DISK: for_each_host(h, on_hosts) check_upr("meta-disk statement", "%s:%s:meta-disk", res->name, h->name); EXP(TK_STRING); host->meta_disk = yylval.txt; if (strcmp("internal", yylval.txt)) { host->meta_index = strdup("flexible"); } check_meta_disk(host); EXP(';'); break; case TK_PROXY: parse_proxy_section(host); break; case '}': in_braces = 0; break; default: pe_expected("disk | device | address | meta-disk " "| flexible-meta-disk"); } } /* Inherit device, disk, meta_disk and meta_index from the resource. */ if(!host->disk && res->disk) { host->disk = strdup(res->disk); for_each_host(h, on_hosts) check_uniq("disk", "disk:%s:%s", h->name, host->disk); } if(!host->device && res->device) { host->device = strdup(res->device); } if (host->device_minor == -1U && res->device_minor != -1U) { host->device_minor = res->device_minor; for_each_host(h, on_hosts) check_uniq("device-minor", "device-minor:%s:%d", h->name, host->device_minor); } if(!host->meta_disk && res->meta_disk) { host->meta_disk = strdup(res->meta_disk); if(res->meta_index) host->meta_index = strdup(res->meta_index); check_meta_disk(host); } if (!(flags & REQUIRE_ALL)) return; if (!host->device && host->device_minor == -1U) derror(host, res, "device"); if (!host->disk) derror(host, res, "disk"); if (!host->address) derror(host, res, "address"); if (!host->meta_disk) derror(host, res, "meta-disk"); } void parse_skip() { int level; int token; fline = line; token = yylex(); switch (token) { case TK_STRING: EXP('{'); break; case '{': break; default: check_string_error(token); pe_expected("[ some_text ] {"); } level = 1; while (level) { switch (yylex()) { case '{': /* if you really want to, you can wrap this with a GB size config file :) */ level++; break; case '}': level--; break; case 0: fprintf(stderr, "%s:%u: reached eof " "while parsing this skip block.\n", config_file, fline); exit(E_CONFIG_INVALID); } } while (level) ; } static void parse_stacked_section(struct d_resource* res) { struct d_host_info *host; struct d_name *h; c_section_start = line; fline = line; host=calloc(1,sizeof(struct d_host_info)); host->device_minor = -1; res->all_hosts = APPEND(res->all_hosts, host); EXP(TK_STRING); check_uniq("stacked-on-top-of", "stacked:%s", yylval.txt); host->lower_name = yylval.txt; m_asprintf(&host->meta_disk, "%s", "internal"); m_asprintf(&host->meta_index, "%s", "internal"); EXP('{'); while (1) { switch(yylex()) { case TK_DEVICE: for_each_host(h, host->on_hosts) check_upr("device statement", "%s:%s:device", res->name, h->name); parse_device(host->on_hosts, &host->device_minor, &host->device); break; case TK_ADDRESS: for_each_host(h, host->on_hosts) check_upr("address statement", "%s:%s:address", res->name, h->name); parse_address(NULL, &host->address, &host->port, &host->address_family); range_check(R_PORT, "port", yylval.txt); break; case TK_PROXY: parse_proxy_section(host); break; case '}': goto break_loop; default: pe_expected("device | address | proxy"); } } break_loop: res->stacked_on_one = 1; /* inherit device */ if (!host->device && res->device) { host->device = strdup(res->device); for_each_host(h, host->on_hosts) { if (host->device) check_uniq("device", "device:%s:%s", h->name, host->device); } } if (host->device_minor == -1U && res->device_minor != -1U) { host->device_minor = res->device_minor; for_each_host(h, host->on_hosts) check_uniq("device-minor", "device-minor:%s:%d", h->name, host->device_minor); } if (!host->device && host->device_minor == -1U) derror(host, res, "device"); if (!host->address) derror(host,res,"address"); if (!host->meta_disk) derror(host,res,"meta-disk"); } void startup_delegate(void *ctx) { struct d_resource *res = (struct d_resource *)ctx; if (!strcmp(yytext, "become-primary-on")) { parse_hosts(&res->become_primary_on, ';'); } else if (!strcmp(yytext, "stacked-timeouts")) { res->stacked_timeouts = 1; EXP(';'); } else pe_expected(" | become-primary-on | stacked-timeouts"); } void net_delegate(void *ctx) { enum pr_flags flags = (enum pr_flags)ctx; if (!strcmp(yytext, "discard-my-data") && flags & IgnDiscardMyData) EXP(';'); else pe_expected("an option keyword"); } void set_me_in_resource(struct d_resource* res, int match_on_proxy) { struct d_host_info *host; /* Determine the local host section */ for (host = res->all_hosts; host; host=host->next) { /* do we match this host? */ if (match_on_proxy) { if (!host->proxy || !name_in_names(nodeinfo.nodename, host->proxy->on_hosts)) continue; } else if (host->by_address) { if (!have_ip(host->address_family, host->address) && /* for debugging only, e.g. __DRBD_NODE__=10.0.0.1 */ strcmp(nodeinfo.nodename, host->address)) continue; } else if (host->lower) { if (!host->lower->me) continue; } else if (!host->on_hosts) { /* huh? a resource without hosts to run on?! */ continue; } else { if (!name_in_names(nodeinfo.nodename, host->on_hosts) && strcmp("_this_host", host->on_hosts->name)) continue; } /* we matched. */ if (res->ignore) { config_valid = 0; fprintf(stderr, "%s:%d: in resource %s, %s %s { ... }:\n" "\tYou cannot ignore and define at the same time.\n", res->config_file, host->config_line, res->name, host->lower ? "stacked-on-top-of" : "on", host->lower ? host->lower->name : names_to_str(host->on_hosts)); } if (res->me) { config_valid = 0; fprintf(stderr, "%s:%d: in resource %s, %s %s { ... } ... %s %s { ... }:\n" "\tThere are multiple host sections for this node.\n", res->config_file, host->config_line, res->name, res->me->lower ? "stacked-on-top-of" : "on", res->me->lower ? res->me->lower->name : names_to_str(res->me->on_hosts), host->lower ? "stacked-on-top-of" : "on", host->lower ? host->lower->name : names_to_str(host->on_hosts)); } res->me = host; if (host->lower) res->stacked = 1; } /* If there is no me, implicitly ignore that resource */ if (!res->me) { res->ignore = 1; return; } } void set_peer_in_resource(struct d_resource* res, int peer_required) { struct d_host_info *host = NULL; if (res->ignore) return; /* me must be already set */ if (!res->me) { /* should have been implicitly ignored. */ fprintf(stderr, "%s:%d: in resource %s:\n" "\tcannot determine the peer, don't even know myself!\n", res->config_file, res->start_line, res->name); exit(E_THINKO); } /* only one host section? */ if (!res->all_hosts->next) { if (peer_required) { fprintf(stderr, "%s:%d: in resource %s:\n" "\tMissing section 'on { ... }'.\n", res->config_file, res->start_line, res->name); config_valid = 0; } return; } /* short cut for exactly two host sections. * silently ignore any --peer connect_to_host option. */ if (res->all_hosts->next->next == NULL) { res->peer = res->all_hosts == res->me ? res->all_hosts->next : res->all_hosts; if (dry_run > 1 && connect_to_host) fprintf(stderr, "%s:%d: in resource %s:\n" "\tIgnoring --peer '%s': there are only two host sections.\n", res->config_file, res->start_line, res->name, connect_to_host); return; } /* Multiple peer hosts to choose from. * we need some help! */ if (!connect_to_host) { if (peer_required) { fprintf(stderr, "%s:%d: in resource %s:\n" "\tThere are multiple host sections for the peer node.\n" "\tUse the --peer option to select which peer section to use.\n", res->config_file, res->start_line, res->name); config_valid = 0; } return; } for (host = res->all_hosts; host; host=host->next) { if (host->by_address && strcmp(connect_to_host, host->address)) continue; if (host->proxy && !name_in_names(nodeinfo.nodename, host->proxy->on_hosts)) continue; if (!name_in_names(connect_to_host, host->on_hosts)) continue; if (host == res->me) { fprintf(stderr, "%s:%d: in resource %s\n" "\tInvoked with --peer '%s', but that matches myself!\n", res->config_file, res->start_line, res->name, connect_to_host); res->peer = NULL; break; } if (res->peer) { fprintf(stderr, "%s:%d: in resource %s:\n" "\tInvoked with --peer '%s', but that matches multiple times!\n", res->config_file, res->start_line, res->name, connect_to_host); res->peer = NULL; break; } res->peer = host; } if (peer_required && !res->peer) { config_valid = 0; if (!host) fprintf(stderr, "%s:%d: in resource %s:\n" "\tNo host ('on' or 'floating') section matches --peer '%s'\n", res->config_file, res->start_line, res->name, connect_to_host); } } void set_on_hosts_in_res(struct d_resource *res) { struct d_resource *l_res, *tmp; struct d_host_info *host, *host2; struct d_name *h, **last; for (host = res->all_hosts; host; host=host->next) { if (host->lower_name) { for_each_resource(l_res, tmp, config) { if (!strcmp(l_res->name, host->lower_name)) break; } if (l_res == NULL) { fprintf(stderr, "%s:%d: in resource %s, " "referenced resource '%s' not defined.\n", res->config_file, res->start_line, res->name, host->lower_name); config_valid = 0; continue; } /* Simple: host->on_hosts = concat_names(l_res->me->on_hosts, l_res->peer->on_hosts); */ last = NULL; for (host2 = l_res->all_hosts; host2; host2 = host2->next) if (!host2->lower_name) { append_names(&host->on_hosts, &last, host2->on_hosts); for_each_host(h, host2->on_hosts) { check_uniq("device-minor", "device-minor:%s:%u", h->name, host->device_minor); if (host->device) check_uniq("device", "device:%s:%s", h->name, host->device); } } host->lower = l_res; /* */ if (addr_scope_local(host->address)) for_each_host(h, host->on_hosts) check_uniq("IP", "%s:%s:%s", h->name, host->address, host->port); } } } void set_disk_in_res(struct d_resource *res) { struct d_host_info *host; if (res->ignore) return; for (host = res->all_hosts; host; host=host->next) { if (host->lower) { if (res->stacked && host->lower->stacked) { fprintf(stderr, "%s:%d: in resource %s, stacked-on-top-of %s { ... }:\n" "\tFIXME. I won't stack stacked resources.\n", res->config_file, res->start_line, res->name, host->lower_name); config_valid = 0; } if (host->lower->ignore) continue; if (host->lower->me->device) m_asprintf(&host->disk, "%s", host->lower->me->device); else m_asprintf(&host->disk, "/dev/drbd%u", host->lower->me->device_minor); if (!host->disk) derror(host,res,"disk"); } } } void proxy_delegate(void *ctx) { struct d_resource *res = (struct d_resource *)ctx; int token; struct d_option *options, *opt; struct d_name *line, *word, **pnp; opt = NULL; token = yylex(); if (token != '{') { fprintf(stderr, "%s:%d: expected \"{\" after \"proxy\" keyword\n", config_file, fline); exit(E_CONFIG_INVALID); } options = NULL; while (1) { pnp = &line; while (1) { token = yylex(); if (token == ';') break; if (token == '}') { if (pnp == &line) goto out; fprintf(stderr, "%s:%d: Missing \";\" before \"}\"\n", config_file, fline); exit(E_CONFIG_INVALID); } word = malloc(sizeof(struct d_name)); if (!word) pdperror("out of memory."); word->name = yylval.txt; word->next = NULL; *pnp = word; pnp = &word->next; } opt = calloc(1, sizeof(struct d_option)); if (!opt) pdperror("out of memory."); opt->name = strdup(names_to_str(line)); options = APPEND(options, opt); free_names(line); } out: res->proxy_plugins = options; } int parse_proxy_settings(struct d_resource *res, int flags) { int token; if (flags & PARSER_CHECK_PROXY_KEYWORD) { token = yylex(); if (token != TK_PROXY) { if (flags & PARSER_STOP_IF_INVALID) { yyrestart(yyin); /* flushes flex's buffers */ return 1; } pe_expected_got("proxy", token); } } EXP('{'); res->proxy_options = parse_options_d(TK_PROXY_SWITCH, TK_PROXY_OPTION | TK_PROXY_GROUP, TK_PROXY_DELEGATE, proxy_delegate, res); return 0; } struct d_resource* parse_resource(char* res_name, enum pr_flags flags) { struct d_resource* res; struct d_name *host_names; int token; check_upr_init(); check_uniq("resource section", res_name); res=calloc(1,sizeof(struct d_resource)); res->name = res_name; res->device_minor = -1; res->config_file = config_file; res->start_line = line; while(1) { token = yylex(); fline = line; switch(token) { case TK_PROTOCOL: check_upr("protocol statement","%s: protocol",res->name); EXP(TK_STRING); res->protocol=yylval.txt; EXP(';'); break; case TK_ON: parse_hosts(&host_names, '{'); parse_host_section(res, host_names, REQUIRE_ALL); break; case TK_STACKED: parse_stacked_section(res); break; case TK_IGNORE: if (res->me || res->peer) { fprintf(stderr, "%s:%d: in resource %s, " "'ignore-on' statement must precede any real host section (on ... { ... }).\n", config_file, line, res->name); exit(E_CONFIG_INVALID); } EXP(TK_STRING); fprintf(stderr, "%s:%d: in resource %s, " "WARN: The 'ignore-on' keyword is deprecated.\n", config_file, line, res->name); EXP(';'); break; case TK__THIS_HOST: EXP('{'); host_names = names_from_str("_this_host"); parse_host_section(res, host_names, 0); break; case TK__REMOTE_HOST: EXP('{'); host_names = names_from_str("_remote_host"); parse_host_section(res, host_names, 0); break; case TK_FLOATING: parse_host_section(res, NULL, REQUIRE_ALL + BY_ADDRESS); break; case TK_DISK: switch (token=yylex()) { case TK_STRING: res->disk = yylval.txt; EXP(';'); break; case '{': check_upr("disk section", "%s:disk", res->name); res->disk_options = parse_options(TK_DISK_SWITCH, TK_DISK_OPTION); break; default: check_string_error(token); pe_expected_got( "TK_STRING | {", token); } break; case TK_NET: check_upr("net section", "%s:net", res->name); EXP('{'); res->net_options = parse_options_d(TK_NET_SWITCH, TK_NET_OPTION, TK_NET_DELEGATE, &net_delegate, (void *)flags); break; case TK_SYNCER: check_upr("syncer section", "%s:syncer", res->name); EXP('{'); res->sync_options = parse_options(TK_SYNCER_SWITCH, TK_SYNCER_OPTION); break; case TK_STARTUP: check_upr("startup section", "%s:startup", res->name); EXP('{'); res->startup_options=parse_options_d(TK_STARTUP_SWITCH, TK_STARTUP_OPTION, TK_STARTUP_DELEGATE, &startup_delegate, res); break; case TK_HANDLER: check_upr("handlers section", "%s:handlers", res->name); EXP('{'); res->handlers = parse_options(0, TK_HANDLER_OPTION); break; case TK_PROXY: check_upr("proxy section", "%s:proxy", res->name); parse_proxy_settings(res, 0); break; case TK_DEVICE: check_upr("device statement", "%s:device", res->name); parse_device(NULL, &res->device_minor, &res->device); break; case TK_META_DISK: parse_meta_disk(&res->meta_disk, &res->meta_index); break; case TK_FLEX_META_DISK: EXP(TK_STRING); res->meta_disk = yylval.txt; if (strcmp("internal", yylval.txt)) { res->meta_index = strdup("flexible"); } EXP(';'); break; case '}': case 0: goto exit_loop; default: pe_expected_got("protocol | on | disk | net | syncer |" " startup | handlers |" " ignore-on | stacked-on-top-of",token); } } exit_loop: if (flags == NoneHAllowed && res->all_hosts) { config_valid = 0; fprintf(stderr, "%s:%d: in the %s section, there are no host sections" " allowed.\n", config_file, c_section_start, res->name); } return res; } void post_parse(struct d_resource *config, enum pp_flags flags) { struct d_resource *res,*tmp; for_each_resource(res, tmp, config) if (res->stacked_on_one) set_on_hosts_in_res(res); /* sets on_hosts and host->lower */ /* Needs "on_hosts" and host->lower already set */ for_each_resource(res, tmp, config) if (!res->stacked_on_one) set_me_in_resource(res, flags & MATCH_ON_PROXY); /* Needs host->lower->me already set */ for_each_resource(res, tmp, config) if (res->stacked_on_one) set_me_in_resource(res, flags & MATCH_ON_PROXY); // Needs "me" set already for_each_resource(res, tmp, config) if (res->stacked_on_one) set_disk_in_res(res); } void include_stmt(char *str) { char *last_slash, *tmp; glob_t glob_buf; int cwd_fd; FILE *f; size_t i; int r; /* in order to allow relative paths in include statements we change directory to the location of the current configuration file. */ cwd_fd = open(".", O_RDONLY | O_CLOEXEC); if (cwd_fd < 0) { fprintf(stderr, "open(\".\") failed: %m\n"); exit(E_USAGE); } tmp = strdupa(config_save); last_slash = strrchr(tmp, '/'); if (last_slash) *last_slash = 0; if (chdir(tmp)) { fprintf(stderr, "chdir(\"%s\") failed: %m\n", tmp); exit(E_USAGE); } r = glob(str, 0, NULL, &glob_buf); if (r == 0) { for (i=0; i Copyright (C) 2006-2008, Lars Ellenberg drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ enum range_checks { R_NO_CHECK, R_MINOR_COUNT, R_DIALOG_REFRESH, R_DISK_SIZE, R_TIMEOUT, R_CONNECT_INT, R_PING_INT, R_MAX_BUFFERS, R_MAX_EPOCH_SIZE, R_SNDBUF_SIZE, R_RCVBUF_SIZE, R_KO_COUNT, R_RATE, R_GROUP, R_AL_EXTENTS, R_PORT, R_META_IDX, R_WFC_TIMEOUT, R_DEGR_WFC_TIMEOUT, R_OUTDATED_WFC_TIMEOUT, R_C_PLAN_AHEAD, R_C_DELAY_TARGET, R_C_FILL_TARGET, R_C_MAX_RATE, R_C_MIN_RATE, R_CONG_FILL, R_CONG_EXTENTS, }; enum yytokentype { TK_GLOBAL = 258, TK_RESOURCE, TK_ON, TK_STACKED, TK_IGNORE, TK_NET, TK_DISK, TK_SKIP, TK_SYNCER, TK_STARTUP, TK_DISABLE_IP_VERIFICATION, TK_DIALOG_REFRESH, TK_PROTOCOL, TK_HANDLER, TK_COMMON, TK_ADDRESS, TK_DEVICE, TK_MINOR, TK_META_DISK, TK_FLEX_META_DISK, TK_MINOR_COUNT, TK_IPADDR, TK_INTEGER, TK_STRING, TK_ELSE, TK_DISK_SWITCH, TK_DISK_OPTION, TK_NET_SWITCH, TK_NET_OPTION, TK_SYNCER_SWITCH, TK_SYNCER_OPTION, TK_STARTUP_SWITCH, TK_STARTUP_OPTION, TK_STARTUP_DELEGATE, TK_HANDLER_OPTION, TK_USAGE_COUNT, TK_ASK, TK_YES, TK_NO, TK__IS_DEFAULT, TK__THIS_HOST, TK__REMOTE_HOST, TK_PROXY, TK_INSIDE, TK_OUTSIDE, TK_MEMLIMIT, TK_PROXY_OPTION, TK_PROXY_SWITCH, TK_PROXY_DELEGATE, TK_ERR_STRING_TOO_LONG, TK_ERR_DQSTRING_TOO_LONG, TK_ERR_DQSTRING, TK_SCI, TK_SDP, TK_SSOCKS, TK_IPV4, TK_IPV6, TK_IPADDR6, TK_NET_DELEGATE, TK_INCLUDE, TK_FLOATING, TK_DEPRECATED_OPTION, TK__GROUPING_BASE = 0x1000, TK_PROXY_GROUP = 0x2000, /* Gets or'ed to some options */ }; /* The higher bits define one or more token groups. */ #define GET_TOKEN_GROUP(__x) ((__x) & ~(TK__GROUPING_BASE - 1)) #define REMOVE_GROUP_FROM_TOKEN(__x) ((__x) & (TK__GROUPING_BASE - 1)) typedef struct YYSTYPE { char* txt; enum range_checks rc; } YYSTYPE; #define yystype YYSTYPE /* obsolescent; will be withdrawn */ #define YYSTYPE_IS_DECLARED 1 #define YYSTYPE_IS_TRIVIAL 1 extern yystype yylval; extern char* yytext; extern FILE* yyin; /* avoid compiler warnings about implicit declaration */ int yylex(void); void my_yypush_buffer_state(FILE *f); void yypop_buffer_state (void ); void yyrestart(FILE *input_file); drbd-utils-8.9.10/user/v83/unaligned.h0000644000175000017500000000336712466702074017314 0ustar apoikosapoikos#ifndef UNALIGNED_H #define UNALIGNED_H #include #if defined(__i386__) || defined(__x86_64__) #define UNALIGNED_ACCESS_SUPPORTED #endif #ifndef UNALIGNED_ACCESS_SUPPORTED #warning "Assuming that your architecture can not do unaligned memory accesses." #warning "Enabling extra code for unaligned memory accesses." #endif #ifdef UNALIGNED_ACCESS_SUPPORTED /* On some architectures the hardware (or microcode) does it */ #define get_unaligned(ptr) *(ptr) #define put_unaligned(val, ptr) *(ptr) = (val) #else /* on some architectures we have to do it in program code */ /* Better not use memcpy(). gcc generates broken code an ARM at higher optimisation levels */ #define __bad_unaligned_access_size() ({ \ fprintf(stderr, "bad unaligned access. abort()\n"); \ abort(); \ }) #define get_unaligned(ptr) ((typeof(*(ptr)))({ \ typeof(*(ptr)) v; \ unsigned char *s = (unsigned char*)(ptr); \ unsigned char *d = (unsigned char*)&v; \ switch (sizeof(v)) { \ case 8: *d++ = *s++; \ *d++ = *s++; \ *d++ = *s++; \ *d++ = *s++; \ case 4: *d++ = *s++; \ *d++ = *s++; \ case 2: *d++ = *s++; \ case 1: *d++ = *s++; \ break; \ default: \ __bad_unaligned_access_size(); \ break; \ } \ v; })) #define put_unaligned(val, ptr) ({ \ typeof(*(ptr)) v = (val); \ unsigned char *d = (unsigned char*)(ptr); \ unsigned char *s = (unsigned char*)&v; \ switch (sizeof(v)) { \ case 8: *d++ = *s++; \ *d++ = *s++; \ *d++ = *s++; \ *d++ = *s++; \ case 4: *d++ = *s++; \ *d++ = *s++; \ case 2: *d++ = *s++; \ case 1: *d++ = *s++; \ break; \ default: \ __bad_unaligned_access_size(); \ break; \ } \ (void)0; }) #endif #endif drbd-utils-8.9.10/user/v83/drbdtool_common.c0000644000175000017500000000507712466702074020522 0ustar apoikosapoikos#define _GNU_SOURCE #define _XOPEN_SOURCE 600 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for BLKGETSIZE64 */ #include #include "drbdtool_common.h" #include "config.h" int force = 0; int confirmed(const char *text) { const char yes[] = "yes"; const ssize_t N = sizeof(yes); char *answer = NULL; size_t n = 0; int ok; printf("\n%s\n", text); if (force) { printf("*** confirmation forced via --force option ***\n"); ok = 1; } else { printf("[need to type '%s' to confirm] ", yes); ok = getline(&answer,&n,stdin) == N && strncmp(answer,yes,N-1) == 0; if (answer) free(answer); printf("\n"); } return ok; } /* input size is expected to be in KB */ void dt_pretty_print_uuids(const uint64_t* uuid, unsigned int flags) { printf( "\n" " +--< Current data generation UUID >-\n" " | +--< Bitmap's base data generation UUID >-\n" " | | +--< younger history UUID >-\n" " | | | +-< older history >-\n" " V V V V\n"); dt_print_uuids(uuid, flags); printf( " ^ ^ ^ ^ ^ ^ ^\n" " -< Data consistency flag >--+ | | | | | |\n" " -< Data was/is currently up-to-date >--+ | | | | |\n" " -< Node was/is currently primary >--+ | | | |\n" " -< Node was/is currently connected >--+ | | |\n" " -< Node was in the progress of setting all bits in the bitmap >--+ | |\n" " -< The peer's disk was out-dated or inconsistent >--+ |\n" " -< This node was a crashed primary, and has not seen its peer since >--+\n" "\n"); printf("flags:%s %s, %s, %s%s%s\n", (flags & MDF_CRASHED_PRIMARY) ? " crashed" : "", (flags & MDF_PRIMARY_IND) ? "Primary" : "Secondary", (flags & MDF_CONNECTED_IND) ? "Connected" : "StandAlone", (flags & MDF_CONSISTENT) ? ((flags & MDF_WAS_UP_TO_DATE) ? "UpToDate" : "Outdated") : "Inconsistent", (flags & MDF_FULL_SYNC) ? ", need full sync" : "", (flags & MDF_PEER_OUT_DATED) ? ", peer Outdated" : ""); } drbd-utils-8.9.10/user/v83/drbdtool_common.h0000644000175000017500000000164112466702074020520 0ustar apoikosapoikos#ifndef DRBDTOOL_COMMON_H #define DRBDTOOL_COMMON_H #include "drbd_endian.h" #include #include #include #include #include "shared_tool.h" #define LANANA_DRBD_MAJOR 147 /* we should get this into linux/major.h */ #ifndef DRBD_MAJOR #define DRBD_MAJOR LANANA_DRBD_MAJOR #elif (DRBD_MAJOR != LANANA_DRBD_MAJOR) # error "FIXME unexpected DRBD_MAJOR" #endif #ifndef __packed #define __packed __attribute__((packed)) #endif #ifndef ARRAY_SIZE #define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0])) #endif struct option; extern void dt_release_lockfile(int drbd_fd); extern void dt_print_uuids(const uint64_t* uuid, unsigned int flags); extern void dt_pretty_print_uuids(const uint64_t* uuid, unsigned int flags); extern int fget_token(char *s, int size, FILE* stream); extern int force; /* global option to force implicit confirmation */ extern int confirmed(const char *text); #endif drbd-utils-8.9.10/user/v83/drbdadm.h0000644000175000017500000001456212477305373016745 0ustar apoikosapoikos#ifndef DRBDADM_H #define DRBDADM_H #include #include #include #include #include #include #include #ifndef O_CLOEXEC #warning "O_CLOEXEC undefined, redefining to 0" #define O_CLOEXEC 0 #endif #include "config.h" #include "shared_main.h" struct d_name { char *name; struct d_name *next; }; struct d_proxy_info { struct d_name *on_hosts; char* inside_addr; char* inside_port; char* inside_af; char* outside_addr; char* outside_port; char* outside_af; }; struct d_host_info { struct d_name *on_hosts; char* device; unsigned device_minor; char* disk; char* address; char* port; char* meta_disk; char* address_family; int meta_major; int meta_minor; char* meta_index; struct d_proxy_info *proxy; struct d_host_info* next; struct d_resource* lower; /* for device stacking */ char *lower_name; /* for device stacking, before bind_stacked_res() */ int config_line; unsigned int by_address:1; /* Match to machines by address, not by names (=on_hosts) */ }; struct d_option { char* name; char* value; struct d_option* next; unsigned int mentioned :1 ; // for the adjust command. unsigned int is_default :1 ; // for the adjust command. unsigned int is_escaped :1 ; }; struct d_resource { char* name; char* protocol; /* these get propagated to host_info sections later. */ char* device; unsigned device_minor; char* disk; char* meta_disk; char* meta_index; struct d_host_info* me; struct d_host_info* peer; struct d_host_info* all_hosts; struct d_option* net_options; struct d_option* disk_options; struct d_option* sync_options; struct d_option* startup_options; struct d_option* handlers; struct d_option* proxy_options; struct d_option* proxy_plugins; struct d_resource* next; struct d_name *become_primary_on; char *config_file; /* The config file this resource is define in.*/ int start_line; unsigned int stacked_timeouts:1; unsigned int ignore:1; unsigned int stacked:1; /* Stacked on this node */ unsigned int stacked_on_one:1; /* Stacked either on me or on peer */ }; extern char *canonify_path(char *path); extern int adm_attach(struct d_resource* ,const char* ); extern int adm_connect(struct d_resource* ,const char* ); extern int adm_resize(struct d_resource* ,const char* ); extern int adm_syncer(struct d_resource* ,const char* ); extern int adm_generic_s(struct d_resource* ,const char* ); extern int _admm_generic(struct d_resource* ,const char*, int flags); extern struct d_option* find_opt(struct d_option*,char*); extern void validate_resource(struct d_resource *); extern void schedule_dcmd( int (* function)(struct d_resource*,const char* ), struct d_resource* res, char* arg, int order); extern int version_code_kernel(void); extern int version_code_userland(void); extern void warn_on_version_mismatch(void); extern void uc_node(enum usage_count_type type); extern int adm_create_md(struct d_resource* res ,const char* cmd); extern void convert_discard_opt(struct d_resource* res); extern void convert_after_option(struct d_resource* res); extern int have_ip(const char *af, const char *ip); /* See drbdadm_minor_table.c */ extern int register_minor(int minor, const char *path); extern int unregister_minor(int minor); extern char *lookup_minor(int minor); enum pr_flags { NoneHAllowed = 4, IgnDiscardMyData = 8 }; extern struct d_resource* parse_resource(char*, enum pr_flags); extern void post_parse(struct d_resource *config, enum pp_flags); extern struct d_option *new_opt(char *name, char *value); extern int name_in_names(char *name, struct d_name *names); extern char *_names_to_str(char* buffer, struct d_name *names); extern char *_names_to_str_c(char* buffer, struct d_name *names, char c); #define NAMES_STR_SIZE 255 #define names_to_str(N) _names_to_str(alloca(NAMES_STR_SIZE+1), N) #define names_to_str_c(N, C) _names_to_str_c(alloca(NAMES_STR_SIZE+1), N, C) extern void free_names(struct d_name *names); extern void set_me_in_resource(struct d_resource* res, int match_on_proxy); extern void set_peer_in_resource(struct d_resource* res, int peer_required); extern void set_on_hosts_in_res(struct d_resource *res); extern void set_disk_in_res(struct d_resource *res); extern char *proxy_connection_name(struct d_resource *res); int parse_proxy_settings(struct d_resource *res, int check_proxy_token); /* conn_name is optional and mostly for compatibility with dcmd */ int do_proxy_conn_up(struct d_resource *res, const char *conn_name); int do_proxy_conn_down(struct d_resource *res, const char *conn_name); int do_proxy_conn_plugins(struct d_resource *res, const char *conn_name); extern char *config_file; extern char *config_save; extern int config_valid; extern struct d_resource* config; extern struct d_resource* common; extern int line, fline; extern struct hsearch_data global_htable; extern int no_tty; extern int dry_run; extern int verbose; extern char* drbdsetup; extern char* drbd_proxy_ctl; extern char ss_buffer[1024]; extern struct utsname nodeinfo; extern char* setup_opts[10]; extern char* connect_to_host; extern int soi; /* ssprintf() places the result of the printf in the current stack frame and sets ptr to the resulting string. If the current stack frame is destroyed (=function returns), the allocated memory is freed automatically */ /* // This is the nicer version, that does not need the ss_buffer. // But it only works with very new glibcs. #define ssprintf(...) \ ({ int _ss_size = snprintf(0, 0, ##__VA_ARGS__); \ char *_ss_ret = __builtin_alloca(_ss_size+1); \ snprintf(_ss_ret, _ss_size+1, ##__VA_ARGS__); \ _ss_ret; }) */ #define ssprintf(ptr,...) \ ptr=strcpy(alloca(snprintf(ss_buffer,sizeof(ss_buffer),##__VA_ARGS__)+1),ss_buffer) /* CAUTION: arguments may not have side effects! */ #define for_each_resource(res,tmp,config) \ for (res = (config); res && (tmp = res->next, 1); res = tmp) #endif #define APPEND(LIST,ITEM) ({ \ typeof((LIST)) _l = (LIST); \ typeof((ITEM)) _i = (ITEM); \ typeof((ITEM)) _t; \ _i->next = NULL; \ if (_l == NULL) { _l = _i; } \ else { \ for (_t = _l; _t->next; _t = _t->next); \ _t->next = _i; \ }; \ _l; \ }) #define PARSER_CHECK_PROXY_KEYWORD (1) #define PARSER_STOP_IF_INVALID (2) drbd-utils-8.9.10/user/v83/linux/0000755000175000017500000000000013027242657016323 5ustar apoikosapoikosdrbd-utils-8.9.10/user/v83/linux/drbd_config.h0000644000175000017500000000153212466702074020735 0ustar apoikosapoikos/* drbd_config.h DRBD's compile time configuration. drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef DRBD_LEGACY_83_CONFIG_H #define DRBD_LEGACY_83_CONFIG_H extern const char *drbd_buildtag(void); #define API_VERSION 88 #endif drbd-utils-8.9.10/user/v83/linux/drbd_nl.h0000644000175000017500000001173012466702074020102 0ustar apoikosapoikos/* PAKET( name, TYPE ( pn, pr, member ) ... ) You may never reissue one of the pn arguments */ #if !defined(NL_PACKET) || !defined(NL_STRING) || !defined(NL_INTEGER) || !defined(NL_BIT) || !defined(NL_INT64) #error "The macros NL_PACKET, NL_STRING, NL_INTEGER, NL_INT64 and NL_BIT needs to be defined" #endif NL_PACKET(primary, 1, NL_BIT( 1, T_MAY_IGNORE, primary_force) ) NL_PACKET(secondary, 2, ) NL_PACKET(disk_conf, 3, NL_INT64( 2, T_MAY_IGNORE, disk_size) NL_STRING( 3, T_MANDATORY, backing_dev, 128) NL_STRING( 4, T_MANDATORY, meta_dev, 128) NL_INTEGER( 5, T_MANDATORY, meta_dev_idx) NL_INTEGER( 6, T_MAY_IGNORE, on_io_error) NL_INTEGER( 7, T_MAY_IGNORE, fencing) NL_BIT( 37, T_MAY_IGNORE, use_bmbv) NL_BIT( 53, T_MAY_IGNORE, no_disk_flush) NL_BIT( 54, T_MAY_IGNORE, no_md_flush) /* 55 max_bio_size was available in 8.2.6rc2 */ NL_INTEGER( 56, T_MAY_IGNORE, max_bio_bvecs) NL_BIT( 57, T_MAY_IGNORE, no_disk_barrier) NL_BIT( 58, T_MAY_IGNORE, no_disk_drain) NL_INTEGER( 89, T_MAY_IGNORE, disk_timeout) ) NL_PACKET(detach, 4, NL_BIT( 88, T_MANDATORY, detach_force) ) NL_PACKET(net_conf, 5, NL_STRING( 8, T_MANDATORY, my_addr, 128) NL_STRING( 9, T_MANDATORY, peer_addr, 128) NL_STRING( 10, T_MAY_IGNORE, shared_secret, SHARED_SECRET_MAX) NL_STRING( 11, T_MAY_IGNORE, cram_hmac_alg, SHARED_SECRET_MAX) NL_STRING( 44, T_MAY_IGNORE, integrity_alg, SHARED_SECRET_MAX) NL_INTEGER( 14, T_MAY_IGNORE, timeout) NL_INTEGER( 15, T_MANDATORY, wire_protocol) NL_INTEGER( 16, T_MAY_IGNORE, try_connect_int) NL_INTEGER( 17, T_MAY_IGNORE, ping_int) NL_INTEGER( 18, T_MAY_IGNORE, max_epoch_size) NL_INTEGER( 19, T_MAY_IGNORE, max_buffers) NL_INTEGER( 20, T_MAY_IGNORE, unplug_watermark) NL_INTEGER( 21, T_MAY_IGNORE, sndbuf_size) NL_INTEGER( 22, T_MAY_IGNORE, ko_count) NL_INTEGER( 24, T_MAY_IGNORE, after_sb_0p) NL_INTEGER( 25, T_MAY_IGNORE, after_sb_1p) NL_INTEGER( 26, T_MAY_IGNORE, after_sb_2p) NL_INTEGER( 39, T_MAY_IGNORE, rr_conflict) NL_INTEGER( 40, T_MAY_IGNORE, ping_timeo) NL_INTEGER( 67, T_MAY_IGNORE, rcvbuf_size) NL_INTEGER( 81, T_MAY_IGNORE, on_congestion) NL_INTEGER( 82, T_MAY_IGNORE, cong_fill) NL_INTEGER( 83, T_MAY_IGNORE, cong_extents) /* 59 addr_family was available in GIT, never released */ NL_BIT( 60, T_MANDATORY, mind_af) NL_BIT( 27, T_MAY_IGNORE, want_lose) NL_BIT( 28, T_MAY_IGNORE, two_primaries) NL_BIT( 41, T_MAY_IGNORE, always_asbp) NL_BIT( 61, T_MAY_IGNORE, no_cork) NL_BIT( 62, T_MANDATORY, auto_sndbuf_size) NL_BIT( 70, T_MANDATORY, dry_run) ) NL_PACKET(disconnect, 6, NL_BIT( 84, T_MAY_IGNORE, force) ) NL_PACKET(resize, 7, NL_INT64( 29, T_MAY_IGNORE, resize_size) NL_BIT( 68, T_MAY_IGNORE, resize_force) NL_BIT( 69, T_MANDATORY, no_resync) ) NL_PACKET(syncer_conf, 8, NL_INTEGER( 30, T_MAY_IGNORE, rate) NL_INTEGER( 31, T_MAY_IGNORE, after) NL_INTEGER( 32, T_MAY_IGNORE, al_extents) /* NL_INTEGER( 71, T_MAY_IGNORE, dp_volume) */ /* NL_INTEGER( 72, T_MAY_IGNORE, dp_interval) */ /* NL_INTEGER( 73, T_MAY_IGNORE, throttle_th) removed */ /* NL_INTEGER( 74, T_MAY_IGNORE, hold_off_th) removed */ NL_STRING( 52, T_MAY_IGNORE, verify_alg, SHARED_SECRET_MAX) NL_STRING( 51, T_MAY_IGNORE, cpu_mask, 32) NL_STRING( 64, T_MAY_IGNORE, csums_alg, SHARED_SECRET_MAX) NL_BIT( 65, T_MAY_IGNORE, use_rle) NL_INTEGER( 75, T_MAY_IGNORE, on_no_data) NL_INTEGER( 76, T_MAY_IGNORE, c_plan_ahead) NL_INTEGER( 77, T_MAY_IGNORE, c_delay_target) NL_INTEGER( 78, T_MAY_IGNORE, c_fill_target) NL_INTEGER( 79, T_MAY_IGNORE, c_max_rate) NL_INTEGER( 80, T_MAY_IGNORE, c_min_rate) ) NL_PACKET(invalidate, 9, ) NL_PACKET(invalidate_peer, 10, ) NL_PACKET(pause_sync, 11, ) NL_PACKET(resume_sync, 12, ) NL_PACKET(suspend_io, 13, ) NL_PACKET(resume_io, 14, ) NL_PACKET(outdate, 15, ) NL_PACKET(get_config, 16, ) NL_PACKET(get_state, 17, NL_INTEGER( 33, T_MAY_IGNORE, state_i) ) NL_PACKET(get_uuids, 18, NL_STRING( 34, T_MAY_IGNORE, uuids, (UI_SIZE*sizeof(__u64))) NL_INTEGER( 35, T_MAY_IGNORE, uuids_flags) ) NL_PACKET(get_timeout_flag, 19, NL_BIT( 36, T_MAY_IGNORE, use_degraded) ) NL_PACKET(call_helper, 20, NL_STRING( 38, T_MAY_IGNORE, helper, 32) ) /* Tag nr 42 already allocated in drbd-8.1 development. */ NL_PACKET(sync_progress, 23, NL_INTEGER( 43, T_MAY_IGNORE, sync_progress) ) NL_PACKET(dump_ee, 24, NL_STRING( 45, T_MAY_IGNORE, dump_ee_reason, 32) NL_STRING( 46, T_MAY_IGNORE, seen_digest, SHARED_SECRET_MAX) NL_STRING( 47, T_MAY_IGNORE, calc_digest, SHARED_SECRET_MAX) NL_INT64( 48, T_MAY_IGNORE, ee_sector) NL_INT64( 49, T_MAY_IGNORE, ee_block_id) NL_STRING( 50, T_MAY_IGNORE, ee_data, 32 << 10) ) NL_PACKET(start_ov, 25, NL_INT64( 66, T_MAY_IGNORE, start_sector) NL_INT64( 90, T_MANDATORY, stop_sector) ) NL_PACKET(new_c_uuid, 26, NL_BIT( 63, T_MANDATORY, clear_bm) ) #ifdef NL_RESPONSE NL_RESPONSE(return_code_only, 27) #endif #undef NL_PACKET #undef NL_INTEGER #undef NL_INT64 #undef NL_BIT #undef NL_STRING #undef NL_RESPONSE drbd-utils-8.9.10/user/v83/linux/drbd_limits.h0000644000175000017500000001210512466702074020767 0ustar apoikosapoikos/* drbd_limits.h This file is part of DRBD by Philipp Reisner and Lars Ellenberg. */ /* * Our current limitations. * Some of them are hard limits, * some of them are arbitrary range limits, that make it easier to provide * feedback about nonsense settings for certain configurable values. */ #ifndef DRBD_LIMITS_H #define DRBD_LIMITS_H 1 #define DEBUG_RANGE_CHECK 0 #define DRBD_MINOR_COUNT_MIN 1 #define DRBD_MINOR_COUNT_MAX 256 #define DRBD_MINOR_COUNT_DEF 32 #define DRBD_DIALOG_REFRESH_MIN 0 #define DRBD_DIALOG_REFRESH_MAX 600 /* valid port number */ #define DRBD_PORT_MIN 1 #define DRBD_PORT_MAX 0xffff /* startup { */ /* if you want more than 3.4 days, disable */ #define DRBD_WFC_TIMEOUT_MIN 0 #define DRBD_WFC_TIMEOUT_MAX 300000 #define DRBD_WFC_TIMEOUT_DEF 0 #define DRBD_DEGR_WFC_TIMEOUT_MIN 0 #define DRBD_DEGR_WFC_TIMEOUT_MAX 300000 #define DRBD_DEGR_WFC_TIMEOUT_DEF 0 #define DRBD_OUTDATED_WFC_TIMEOUT_MIN 0 #define DRBD_OUTDATED_WFC_TIMEOUT_MAX 300000 #define DRBD_OUTDATED_WFC_TIMEOUT_DEF 0 /* }*/ /* net { */ /* timeout, unit centi seconds * more than one minute timeout is not usefull */ #define DRBD_TIMEOUT_MIN 1 #define DRBD_TIMEOUT_MAX 600 #define DRBD_TIMEOUT_DEF 60 /* 6 seconds */ /* If backing disk takes longer than disk_timeout, mark the disk as failed */ #define DRBD_DISK_TIMEOUT_MIN 0 /* 0 = disabled */ #define DRBD_DISK_TIMEOUT_MAX 6000 /* 10 Minutes */ #define DRBD_DISK_TIMEOUT_DEF 0 /* disabled */ /* active connection retries when C_WF_CONNECTION */ #define DRBD_CONNECT_INT_MIN 1 #define DRBD_CONNECT_INT_MAX 120 #define DRBD_CONNECT_INT_DEF 10 /* seconds */ /* keep-alive probes when idle */ #define DRBD_PING_INT_MIN 1 #define DRBD_PING_INT_MAX 120 #define DRBD_PING_INT_DEF 10 /* timeout for the ping packets.*/ #define DRBD_PING_TIMEO_MIN 1 #define DRBD_PING_TIMEO_MAX 100 #define DRBD_PING_TIMEO_DEF 5 /* max number of write requests between write barriers */ #define DRBD_MAX_EPOCH_SIZE_MIN 1 #define DRBD_MAX_EPOCH_SIZE_MAX 20000 #define DRBD_MAX_EPOCH_SIZE_DEF 2048 /* I don't think that a tcp send buffer of more than 10M is usefull */ #define DRBD_SNDBUF_SIZE_MIN 0 #define DRBD_SNDBUF_SIZE_MAX (10<<20) #define DRBD_SNDBUF_SIZE_DEF 0 #define DRBD_RCVBUF_SIZE_MIN 0 #define DRBD_RCVBUF_SIZE_MAX (10<<20) #define DRBD_RCVBUF_SIZE_DEF 0 /* @4k PageSize -> 128kB - 512MB */ #define DRBD_MAX_BUFFERS_MIN 32 #define DRBD_MAX_BUFFERS_MAX 131072 #define DRBD_MAX_BUFFERS_DEF 2048 /* @4k PageSize -> 4kB - 512MB */ #define DRBD_UNPLUG_WATERMARK_MIN 1 #define DRBD_UNPLUG_WATERMARK_MAX 131072 #define DRBD_UNPLUG_WATERMARK_DEF (DRBD_MAX_BUFFERS_DEF/16) /* 0 is disabled. * 200 should be more than enough even for very short timeouts */ #define DRBD_KO_COUNT_MIN 0 #define DRBD_KO_COUNT_MAX 200 #define DRBD_KO_COUNT_DEF 0 /* } */ /* syncer { */ /* FIXME allow rate to be zero? */ #define DRBD_RATE_MIN 1 /* channel bonding 10 GbE, or other hardware */ #define DRBD_RATE_MAX (4 << 20) #define DRBD_RATE_DEF 250 /* kb/second */ /* less than 7 would hit performance unneccessarily. * 3833 is the largest prime that still does fit * into 64 sectors of activity log */ #define DRBD_AL_EXTENTS_MIN 7 #define DRBD_AL_EXTENTS_MAX 3833 #define DRBD_AL_EXTENTS_DEF 127 #define DRBD_AFTER_MIN -1 #define DRBD_AFTER_MAX 255 #define DRBD_AFTER_DEF -1 /* } */ /* drbdsetup XY resize -d Z * you are free to reduce the device size to nothing, if you want to. * the upper limit with 64bit kernel, enough ram and flexible meta data * is 16 TB, currently. */ /* DRBD_MAX_SECTORS */ #define DRBD_DISK_SIZE_SECT_MIN 0 #define DRBD_DISK_SIZE_SECT_MAX (16 * (2LLU << 30)) #define DRBD_DISK_SIZE_SECT_DEF 0 /* = disabled = no user size... */ #define DRBD_ON_IO_ERROR_DEF EP_PASS_ON #define DRBD_FENCING_DEF FP_DONT_CARE #define DRBD_AFTER_SB_0P_DEF ASB_DISCONNECT #define DRBD_AFTER_SB_1P_DEF ASB_DISCONNECT #define DRBD_AFTER_SB_2P_DEF ASB_DISCONNECT #define DRBD_RR_CONFLICT_DEF ASB_DISCONNECT #define DRBD_ON_NO_DATA_DEF OND_IO_ERROR #define DRBD_ON_CONGESTION_DEF OC_BLOCK #define DRBD_MAX_BIO_BVECS_MIN 0 #define DRBD_MAX_BIO_BVECS_MAX 128 #define DRBD_MAX_BIO_BVECS_DEF 0 #define DRBD_C_PLAN_AHEAD_MIN 0 #define DRBD_C_PLAN_AHEAD_MAX 300 #define DRBD_C_PLAN_AHEAD_DEF 0 /* RS rate controller disabled by default */ #define DRBD_C_DELAY_TARGET_MIN 1 #define DRBD_C_DELAY_TARGET_MAX 100 #define DRBD_C_DELAY_TARGET_DEF 10 #define DRBD_C_FILL_TARGET_MIN 0 #define DRBD_C_FILL_TARGET_MAX (1<<20) /* 500MByte in sec */ #define DRBD_C_FILL_TARGET_DEF 0 /* By default disabled -> controlled by delay_target */ #define DRBD_C_MAX_RATE_MIN 250 /* kByte/sec */ #define DRBD_C_MAX_RATE_MAX (4 << 20) #define DRBD_C_MAX_RATE_DEF 102400 #define DRBD_C_MIN_RATE_MIN 0 /* kByte/sec */ #define DRBD_C_MIN_RATE_MAX (4 << 20) #define DRBD_C_MIN_RATE_DEF 4096 #define DRBD_CONG_FILL_MIN 0 #define DRBD_CONG_FILL_MAX (10<<21) /* 10GByte in sectors */ #define DRBD_CONG_FILL_DEF 0 #define DRBD_CONG_EXTENTS_MIN DRBD_AL_EXTENTS_MIN #define DRBD_CONG_EXTENTS_MAX DRBD_AL_EXTENTS_MAX #define DRBD_CONG_EXTENTS_DEF DRBD_AL_EXTENTS_DEF #endif drbd-utils-8.9.10/user/v83/linux/drbd.h0000644000175000017500000002454712466702074017423 0ustar apoikosapoikos/* drbd.h Kernel module for 2.6.x Kernels This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. Copyright (C) 2001-2008, Philipp Reisner . Copyright (C) 2001-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef DRBD_H #define DRBD_H #include #include #ifdef __KERNEL__ #include #include #else #include #include #include /* Altough the Linux source code makes a difference between generic endianness and the bitfields' endianness, there is no architecture as of Linux-2.6.24-rc4 where the bitfileds' endianness does not match the generic endianness. */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define __LITTLE_ENDIAN_BITFIELD #elif __BYTE_ORDER == __BIG_ENDIAN #define __BIG_ENDIAN_BITFIELD #else # error "sorry, weird endianness on this box" #endif #endif enum drbd_io_error_p { EP_PASS_ON, /* FIXME should the better be named "Ignore"? */ EP_CALL_HELPER, EP_DETACH }; enum drbd_fencing_p { FP_DONT_CARE, FP_RESOURCE, FP_STONITH }; enum drbd_disconnect_p { DP_RECONNECT, DP_DROP_NET_CONF, DP_FREEZE_IO }; enum drbd_after_sb_p { ASB_DISCONNECT, ASB_DISCARD_YOUNGER_PRI, ASB_DISCARD_OLDER_PRI, ASB_DISCARD_ZERO_CHG, ASB_DISCARD_LEAST_CHG, ASB_DISCARD_LOCAL, ASB_DISCARD_REMOTE, ASB_CONSENSUS, ASB_DISCARD_SECONDARY, ASB_CALL_HELPER, ASB_VIOLENTLY }; enum drbd_on_no_data { OND_IO_ERROR, OND_SUSPEND_IO }; enum drbd_on_congestion { OC_BLOCK, OC_PULL_AHEAD, OC_DISCONNECT, }; /* KEEP the order, do not delete or insert. Only append. */ enum drbd_ret_code { ERR_CODE_BASE = 100, NO_ERROR = 101, ERR_LOCAL_ADDR = 102, ERR_PEER_ADDR = 103, ERR_OPEN_DISK = 104, ERR_OPEN_MD_DISK = 105, ERR_DISK_NOT_BDEV = 107, ERR_MD_NOT_BDEV = 108, ERR_DISK_TOO_SMALL = 111, ERR_MD_DISK_TOO_SMALL = 112, ERR_BDCLAIM_DISK = 114, ERR_BDCLAIM_MD_DISK = 115, ERR_MD_IDX_INVALID = 116, ERR_IO_MD_DISK = 118, ERR_MD_INVALID = 119, ERR_AUTH_ALG = 120, ERR_AUTH_ALG_ND = 121, ERR_NOMEM = 122, ERR_DISCARD_IMPOSSIBLE = 123, ERR_DISK_CONFIGURED = 124, ERR_NET_CONFIGURED = 125, ERR_MANDATORY_TAG = 126, ERR_MINOR_INVALID = 127, ERR_INTR = 129, /* EINTR */ ERR_RESIZE_RESYNC = 130, ERR_NO_PRIMARY = 131, ERR_SYNC_AFTER = 132, ERR_SYNC_AFTER_CYCLE = 133, ERR_PAUSE_IS_SET = 134, ERR_PAUSE_IS_CLEAR = 135, ERR_PACKET_NR = 137, ERR_NO_DISK = 138, ERR_NOT_PROTO_C = 139, ERR_NOMEM_BITMAP = 140, ERR_INTEGRITY_ALG = 141, /* DRBD 8.2 only */ ERR_INTEGRITY_ALG_ND = 142, /* DRBD 8.2 only */ ERR_CPU_MASK_PARSE = 143, /* DRBD 8.2 only */ ERR_CSUMS_ALG = 144, /* DRBD 8.2 only */ ERR_CSUMS_ALG_ND = 145, /* DRBD 8.2 only */ ERR_VERIFY_ALG = 146, /* DRBD 8.2 only */ ERR_VERIFY_ALG_ND = 147, /* DRBD 8.2 only */ ERR_CSUMS_RESYNC_RUNNING= 148, /* DRBD 8.2 only */ ERR_VERIFY_RUNNING = 149, /* DRBD 8.2 only */ ERR_DATA_NOT_CURRENT = 150, ERR_CONNECTED = 151, /* DRBD 8.3 only */ ERR_PERM = 152, ERR_NEED_APV_93 = 153, ERR_STONITH_AND_PROT_A = 154, ERR_CONG_NOT_PROTO_A = 155, ERR_PIC_AFTER_DEP = 156, ERR_PIC_PEER_DEP = 157, /* insert new ones above this line */ AFTER_LAST_ERR_CODE }; #define DRBD_PROT_A 1 #define DRBD_PROT_B 2 #define DRBD_PROT_C 3 enum drbd_role { R_UNKNOWN = 0, R_PRIMARY = 1, /* role */ R_SECONDARY = 2, /* role */ R_MASK = 3, }; /* The order of these constants is important. * The lower ones (=C_WF_REPORT_PARAMS ==> There is a socket */ enum drbd_conns { C_STANDALONE, C_DISCONNECTING, /* Temporal state on the way to StandAlone. */ C_UNCONNECTED, /* >= C_UNCONNECTED -> inc_net() succeeds */ /* These temporal states are all used on the way * from >= C_CONNECTED to Unconnected. * The 'disconnect reason' states * I do not allow to change beween them. */ C_TIMEOUT, C_BROKEN_PIPE, C_NETWORK_FAILURE, C_PROTOCOL_ERROR, C_TEAR_DOWN, C_WF_CONNECTION, C_WF_REPORT_PARAMS, /* we have a socket */ C_CONNECTED, /* we have introduced each other */ C_STARTING_SYNC_S, /* starting full sync by admin request. */ C_STARTING_SYNC_T, /* stariing full sync by admin request. */ C_WF_BITMAP_S, C_WF_BITMAP_T, C_WF_SYNC_UUID, /* All SyncStates are tested with this comparison * xx >= C_SYNC_SOURCE && xx <= C_PAUSED_SYNC_T */ C_SYNC_SOURCE, C_SYNC_TARGET, C_VERIFY_S, C_VERIFY_T, C_PAUSED_SYNC_S, C_PAUSED_SYNC_T, C_AHEAD, C_BEHIND, C_MASK = 31 }; enum drbd_disk_state { D_DISKLESS, D_ATTACHING, /* In the process of reading the meta-data */ D_FAILED, /* Becomes D_DISKLESS as soon as we told it the peer */ /* when >= D_FAILED it is legal to access mdev->bc */ D_NEGOTIATING, /* Late attaching state, we need to talk to the peer */ D_INCONSISTENT, D_OUTDATED, D_UNKNOWN, /* Only used for the peer, never for myself */ D_CONSISTENT, /* Might be D_OUTDATED, might be D_UP_TO_DATE ... */ D_UP_TO_DATE, /* Only this disk state allows applications' IO ! */ D_MASK = 15 }; union drbd_state { /* According to gcc's docs is the ... * The order of allocation of bit-fields within a unit (C90 6.5.2.1, C99 6.7.2.1). * Determined by ABI. * pointed out by Maxim Uvarov q * even though we transmit as "cpu_to_be32(state)", * the offsets of the bitfields still need to be swapped * on different endianess. */ struct { #if defined(__LITTLE_ENDIAN_BITFIELD) unsigned role:2 ; /* 3/4 primary/secondary/unknown */ unsigned peer:2 ; /* 3/4 primary/secondary/unknown */ unsigned conn:5 ; /* 17/32 cstates */ unsigned disk:4 ; /* 8/16 from D_DISKLESS to D_UP_TO_DATE */ unsigned pdsk:4 ; /* 8/16 from D_DISKLESS to D_UP_TO_DATE */ unsigned susp:1 ; /* 2/2 IO suspended no/yes (by user) */ unsigned aftr_isp:1 ; /* isp .. imposed sync pause */ unsigned peer_isp:1 ; unsigned user_isp:1 ; unsigned susp_nod:1 ; /* IO suspended because no data */ unsigned susp_fen:1 ; /* IO suspended because fence peer handler runs*/ unsigned _pad:9; /* 0 unused */ #elif defined(__BIG_ENDIAN_BITFIELD) unsigned _pad:9; unsigned susp_fen:1 ; unsigned susp_nod:1 ; unsigned user_isp:1 ; unsigned peer_isp:1 ; unsigned aftr_isp:1 ; /* isp .. imposed sync pause */ unsigned susp:1 ; /* 2/2 IO suspended no/yes */ unsigned pdsk:4 ; /* 8/16 from D_DISKLESS to D_UP_TO_DATE */ unsigned disk:4 ; /* 8/16 from D_DISKLESS to D_UP_TO_DATE */ unsigned conn:5 ; /* 17/32 cstates */ unsigned peer:2 ; /* 3/4 primary/secondary/unknown */ unsigned role:2 ; /* 3/4 primary/secondary/unknown */ #else # error "this endianess is not supported" #endif #ifndef DRBD_DEBUG_STATE_CHANGES # ifdef CONFIG_DYNAMIC_DEBUG # define DRBD_DEBUG_STATE_CHANGES 1 # else # define DRBD_DEBUG_STATE_CHANGES 0 # endif #endif #if DRBD_DEBUG_STATE_CHANGES unsigned int line; const char *func; unsigned long long seq; #endif }; unsigned int i; }; enum drbd_state_rv { SS_CW_NO_NEED = 4, SS_CW_SUCCESS = 3, SS_NOTHING_TO_DO = 2, SS_SUCCESS = 1, SS_UNKNOWN_ERROR = 0, /* Used to sleep longer in _drbd_request_state */ SS_TWO_PRIMARIES = -1, SS_NO_UP_TO_DATE_DISK = -2, SS_NO_LOCAL_DISK = -4, SS_NO_REMOTE_DISK = -5, SS_CONNECTED_OUTDATES = -6, SS_PRIMARY_NOP = -7, SS_RESYNC_RUNNING = -8, SS_ALREADY_STANDALONE = -9, SS_CW_FAILED_BY_PEER = -10, SS_IS_DISKLESS = -11, SS_DEVICE_IN_USE = -12, SS_NO_NET_CONFIG = -13, SS_NO_VERIFY_ALG = -14, /* drbd-8.2 only */ SS_NEED_CONNECTION = -15, /* drbd-8.2 only */ SS_LOWER_THAN_OUTDATED = -16, SS_NOT_SUPPORTED = -17, /* drbd-8.2 only */ SS_IN_TRANSIENT_STATE = -18, /* Retry after the next state change */ SS_CONCURRENT_ST_CHG = -19, /* Concurrent cluster side state change! */ SS_AFTER_LAST_ERROR = -20, /* Keep this at bottom */ }; /* from drbd_strings.c */ extern const char *drbd_conn_str(enum drbd_conns); extern const char *drbd_role_str(enum drbd_role); extern const char *drbd_disk_str(enum drbd_disk_state); extern const char *drbd_set_st_err_str(enum drbd_state_rv); #define SHARED_SECRET_MAX 64 #define MDF_CONSISTENT (1 << 0) #define MDF_PRIMARY_IND (1 << 1) #define MDF_CONNECTED_IND (1 << 2) #define MDF_FULL_SYNC (1 << 3) #define MDF_WAS_UP_TO_DATE (1 << 4) #define MDF_PEER_OUT_DATED (1 << 5) #define MDF_CRASHED_PRIMARY (1 << 6) enum drbd_uuid_index { UI_CURRENT, UI_BITMAP, UI_HISTORY_START, UI_HISTORY_END, UI_SIZE, /* nl-packet: number of dirty bits */ UI_FLAGS, /* nl-packet: flags */ UI_EXTENDED_SIZE /* Everything. */ }; enum drbd_timeout_flag { UT_DEFAULT = 0, UT_DEGRADED = 1, UT_PEER_OUTDATED = 2, }; #define UUID_JUST_CREATED ((__u64)4) #define DRBD_MAGIC 0x83740267 #define BE_DRBD_MAGIC __constant_cpu_to_be32(DRBD_MAGIC) #define DRBD_MAGIC_BIG 0x835a #define BE_DRBD_MAGIC_BIG __constant_cpu_to_be16(DRBD_MAGIC_BIG) /* these are of type "int" */ #define DRBD_MD_INDEX_INTERNAL -1 #define DRBD_MD_INDEX_FLEX_EXT -2 #define DRBD_MD_INDEX_FLEX_INT -3 /* Start of the new netlink/connector stuff */ #define DRBD_NL_CREATE_DEVICE 0x01 #define DRBD_NL_SET_DEFAULTS 0x02 /* The following line should be moved over to linux/connector.h * when the time comes */ #ifndef CN_IDX_DRBD # define CN_IDX_DRBD 0x4 /* Ubuntu "intrepid ibex" release defined CN_IDX_DRBD as 0x6 */ #endif #define CN_VAL_DRBD 0x1 /* For searching a vacant cn_idx value */ #define CN_IDX_STEP 6977 struct drbd_nl_cfg_req { int packet_type; unsigned int drbd_minor; int flags; unsigned short tag_list[]; }; struct drbd_nl_cfg_reply { int packet_type; unsigned int minor; /* FIXME: This is super ugly. */ int ret_code; /* enum drbd_ret_code or enum drbd_state_rv */ unsigned short tag_list[]; /* only used with get_* calls */ }; #endif drbd-utils-8.9.10/user/v83/linux/drbd_tag_magic.h0000644000175000017500000000540512466702074021406 0ustar apoikosapoikos#ifndef DRBD_TAG_MAGIC_H #define DRBD_TAG_MAGIC_H #define TT_END 0 #define TT_REMOVED 0xE000 /* declare packet_type enums */ enum packet_types { #define NL_PACKET(name, number, fields) P_ ## name = number, #define NL_RESPONSE(name, number) P_ ## name = number, #define NL_INTEGER(pn, pr, member) #define NL_INT64(pn, pr, member) #define NL_BIT(pn, pr, member) #define NL_STRING(pn, pr, member, len) #include "drbd_nl.h" P_nl_after_last_packet, }; /* These struct are used to deduce the size of the tag lists: */ #define NL_PACKET(name, number, fields) \ struct name ## _tag_len_struct { fields }; #define NL_INTEGER(pn, pr, member) \ int member; int tag_and_len ## member; #define NL_INT64(pn, pr, member) \ __u64 member; int tag_and_len ## member; #define NL_BIT(pn, pr, member) \ unsigned char member:1; int tag_and_len ## member; #define NL_STRING(pn, pr, member, len) \ unsigned char member[len]; int member ## _len; \ int tag_and_len ## member; #include "drbd_nl.h" /* declare tag-list-sizes */ static const int tag_list_sizes[] = { #define NL_PACKET(name, number, fields) 2 fields , #define NL_INTEGER(pn, pr, member) + 4 + 4 #define NL_INT64(pn, pr, member) + 4 + 8 #define NL_BIT(pn, pr, member) + 4 + 1 #define NL_STRING(pn, pr, member, len) + 4 + (len) #include "drbd_nl.h" }; /* The two highest bits are used for the tag type */ #define TT_MASK 0xC000 #define TT_INTEGER 0x0000 #define TT_INT64 0x4000 #define TT_BIT 0x8000 #define TT_STRING 0xC000 /* The next bit indicates if processing of the tag is mandatory */ #define T_MANDATORY 0x2000 #define T_MAY_IGNORE 0x0000 #define TN_MASK 0x1fff /* The remaining 13 bits are used to enumerate the tags */ #define tag_type(T) ((T) & TT_MASK) #define tag_number(T) ((T) & TN_MASK) /* declare tag enums */ #define NL_PACKET(name, number, fields) fields enum drbd_tags { #define NL_INTEGER(pn, pr, member) T_ ## member = pn | TT_INTEGER | pr , #define NL_INT64(pn, pr, member) T_ ## member = pn | TT_INT64 | pr , #define NL_BIT(pn, pr, member) T_ ## member = pn | TT_BIT | pr , #define NL_STRING(pn, pr, member, len) T_ ## member = pn | TT_STRING | pr , #include "drbd_nl.h" }; struct tag { const char *name; int type_n_flags; int max_len; }; /* declare tag names */ #define NL_PACKET(name, number, fields) fields static const struct tag tag_descriptions[] = { #define NL_INTEGER(pn, pr, member) [ pn ] = { #member, TT_INTEGER | pr, sizeof(int) }, #define NL_INT64(pn, pr, member) [ pn ] = { #member, TT_INT64 | pr, sizeof(__u64) }, #define NL_BIT(pn, pr, member) [ pn ] = { #member, TT_BIT | pr, sizeof(int) }, #define NL_STRING(pn, pr, member, len) [ pn ] = { #member, TT_STRING | pr, (len) }, #include "drbd_nl.h" }; #endif drbd-utils-8.9.10/user/v83/drbd_strings.c0000644000175000017500000001025312466702074020015 0ustar apoikosapoikos/* drbd.h This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2003-2008, LINBIT Information Technologies GmbH. Copyright (C) 2003-2008, Philipp Reisner . Copyright (C) 2003-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include static const char *drbd_conn_s_names[] = { [C_STANDALONE] = "StandAlone", [C_DISCONNECTING] = "Disconnecting", [C_UNCONNECTED] = "Unconnected", [C_TIMEOUT] = "Timeout", [C_BROKEN_PIPE] = "BrokenPipe", [C_NETWORK_FAILURE] = "NetworkFailure", [C_PROTOCOL_ERROR] = "ProtocolError", [C_WF_CONNECTION] = "WFConnection", [C_WF_REPORT_PARAMS] = "WFReportParams", [C_TEAR_DOWN] = "TearDown", [C_CONNECTED] = "Connected", [C_STARTING_SYNC_S] = "StartingSyncS", [C_STARTING_SYNC_T] = "StartingSyncT", [C_WF_BITMAP_S] = "WFBitMapS", [C_WF_BITMAP_T] = "WFBitMapT", [C_WF_SYNC_UUID] = "WFSyncUUID", [C_SYNC_SOURCE] = "SyncSource", [C_SYNC_TARGET] = "SyncTarget", [C_PAUSED_SYNC_S] = "PausedSyncS", [C_PAUSED_SYNC_T] = "PausedSyncT", [C_VERIFY_S] = "VerifyS", [C_VERIFY_T] = "VerifyT", [C_AHEAD] = "Ahead", [C_BEHIND] = "Behind", }; static const char *drbd_role_s_names[] = { [R_PRIMARY] = "Primary", [R_SECONDARY] = "Secondary", [R_UNKNOWN] = "Unknown" }; static const char *drbd_disk_s_names[] = { [D_DISKLESS] = "Diskless", [D_ATTACHING] = "Attaching", [D_FAILED] = "Failed", [D_NEGOTIATING] = "Negotiating", [D_INCONSISTENT] = "Inconsistent", [D_OUTDATED] = "Outdated", [D_UNKNOWN] = "DUnknown", [D_CONSISTENT] = "Consistent", [D_UP_TO_DATE] = "UpToDate", }; static const char *drbd_state_sw_errors[] = { [-SS_TWO_PRIMARIES] = "Multiple primaries not allowed by config", [-SS_NO_UP_TO_DATE_DISK] = "Need access to UpToDate data", [-SS_NO_LOCAL_DISK] = "Can not resync without local disk", [-SS_NO_REMOTE_DISK] = "Can not resync without remote disk", [-SS_CONNECTED_OUTDATES] = "Refusing to be Outdated while Connected", [-SS_PRIMARY_NOP] = "Refusing to be Primary while peer is not outdated", [-SS_RESYNC_RUNNING] = "Can not start OV/resync since it is already active", [-SS_ALREADY_STANDALONE] = "Can not disconnect a StandAlone device", [-SS_CW_FAILED_BY_PEER] = "State change was refused by peer node", [-SS_IS_DISKLESS] = "Device is diskless, the requested operation requires a disk", [-SS_DEVICE_IN_USE] = "Device is held open by someone", [-SS_NO_NET_CONFIG] = "Have no net/connection configuration", [-SS_NO_VERIFY_ALG] = "Need a verify algorithm to start online verify", [-SS_NEED_CONNECTION] = "Need a connection to start verify or resync", [-SS_NOT_SUPPORTED] = "Peer does not support protocol", [-SS_LOWER_THAN_OUTDATED] = "Disk state is lower than outdated", [-SS_IN_TRANSIENT_STATE] = "In transient state, retry after next state change", [-SS_CONCURRENT_ST_CHG] = "Concurrent state changes detected and aborted", }; const char *drbd_conn_str(enum drbd_conns s) { /* enums are unsigned... */ return s > C_BEHIND ? "TOO_LARGE" : drbd_conn_s_names[s]; } const char *drbd_role_str(enum drbd_role s) { return s > R_SECONDARY ? "TOO_LARGE" : drbd_role_s_names[s]; } const char *drbd_disk_str(enum drbd_disk_state s) { return s > D_UP_TO_DATE ? "TOO_LARGE" : drbd_disk_s_names[s]; } const char *drbd_set_st_err_str(enum drbd_state_rv err) { return err <= SS_AFTER_LAST_ERROR ? "TOO_SMALL" : err > SS_TWO_PRIMARIES ? "TOO_LARGE" : drbd_state_sw_errors[-err]; } drbd-utils-8.9.10/user/v83/drbdadm_usage_cnt.c0000644000175000017500000004346213016771235020763 0ustar apoikosapoikos/* drbdadm_usage_cnt.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2006-2008, LINBIT Information Technologies GmbH Copyright (C) 2006-2008, Philipp Reisner Copyright (C) 2006-2008, Lars Ellenberg drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drbdadm.h" #include "drbdtool_common.h" #include "drbd_endian.h" #include "linux/drbd.h" /* only use DRBD_MAGIC from here! */ #define HTTP_PORT 80 #define HTTP_HOST "usage.drbd.org" #define HTTP_ADDR "212.69.161.111" #define NODE_ID_FILE DRBD_LIB_DIR"/node_id" #define GIT_HASH_BYTE 20 #define SRCVERSION_BYTE 12 /* actually 11 and a half. */ #define SRCVERSION_PAD (GIT_HASH_BYTE - SRCVERSION_BYTE) #define SVN_STYLE_OD 16 struct vcs_rel { uint32_t svn_revision; char git_hash[GIT_HASH_BYTE]; struct { unsigned major, minor, sublvl; } version; unsigned version_code; }; struct node_info { uint64_t node_uuid; struct vcs_rel rev; }; struct node_info_od { uint32_t magic; struct node_info ni; } __packed; /* For our purpose (finding the revision) SLURP_SIZE is always enough. */ static char* slurp_proc_drbd() { const int SLURP_SIZE = 4096; char* buffer; int rr, fd; fd = open("/proc/drbd",O_RDONLY); if( fd == -1) return 0; buffer = malloc(SLURP_SIZE); if(!buffer) return 0; rr = read(fd, buffer, SLURP_SIZE-1); if( rr == -1) { free(buffer); return 0; } buffer[rr]=0; close(fd); return buffer; } void read_hex(char* dst, char* src, int dst_size, int src_size) { int dst_i, u, src_i=0; for(dst_i=0;dst_i= src_size) break; if(src[src_i] == 0) break; if(++src_i >= src_size) break; } } void vcs_ver_from_str(struct vcs_rel *rel, const char *token) { char *dot; long maj, min, sub; maj = strtol(token, &dot, 10); if (*dot != '.') return; min = strtol(dot+1, &dot, 10); if (*dot != '.') return; sub = strtol(dot+1, &dot, 10); /* don't check on *dot == 0, * we may want to add some extraversion tag sometime if (*dot != 0) return; */ rel->version.major = maj; rel->version.minor = min; rel->version.sublvl = sub; rel->version_code = (maj << 16) + (min << 8) + sub; } void vcs_from_str(struct vcs_rel *rel, const char *text) { char token[80]; int plus=0; enum { begin, f_ver, f_svn, f_rev, f_git, f_srcv } ex = begin; while (sget_token(token, sizeof(token), &text) != EOF) { switch(ex) { case begin: if(!strcmp(token,"version:")) ex = f_ver; if(!strcmp(token,"SVN")) ex = f_svn; if(!strcmp(token,"GIT-hash:")) ex = f_git; if(!strcmp(token,"srcversion:")) ex = f_srcv; break; case f_ver: if(!strcmp(token,"plus")) plus = 1; /* still waiting for version */ else { vcs_ver_from_str(rel, token); ex = begin; } break; case f_svn: if(!strcmp(token,"Revision:")) ex = f_rev; break; case f_rev: rel->svn_revision = atol(token) * 10; if( plus ) rel->svn_revision += 1; memset(rel->git_hash, 0, GIT_HASH_BYTE); return; case f_git: read_hex(rel->git_hash, token, GIT_HASH_BYTE, strlen(token)); rel->svn_revision = 0; return; case f_srcv: memset(rel->git_hash, 0, SRCVERSION_PAD); read_hex(rel->git_hash + SRCVERSION_PAD, token, SRCVERSION_BYTE, strlen(token)); rel->svn_revision = 0; return; } } } static int current_vcs_is_from_proc_drbd; static struct vcs_rel current_vcs_rel; static struct vcs_rel userland_version; static void vcs_get_current(void) { char* version_txt; if (current_vcs_rel.version_code) return; version_txt = slurp_proc_drbd(); if(version_txt) { vcs_from_str(¤t_vcs_rel, version_txt); current_vcs_is_from_proc_drbd = 1; free(version_txt); } else { vcs_from_str(¤t_vcs_rel, drbd_buildtag()); vcs_ver_from_str(¤t_vcs_rel, PACKAGE_VERSION); } } static void vcs_get_userland(void) { if (userland_version.version_code) return; vcs_ver_from_str(&userland_version, PACKAGE_VERSION); } int version_code_kernel(void) { vcs_get_current(); return current_vcs_is_from_proc_drbd ? current_vcs_rel.version_code : 0; } int version_code_userland(void) { vcs_get_userland(); return userland_version.version_code; } static int vcs_eq(struct vcs_rel *rev1, struct vcs_rel *rev2) { if( rev1->svn_revision || rev2->svn_revision ) { return rev1->svn_revision == rev2->svn_revision; } else { return !memcmp(rev1->git_hash,rev2->git_hash,GIT_HASH_BYTE); } } static int vcs_ver_cmp(struct vcs_rel *rev1, struct vcs_rel *rev2) { return rev1->version_code - rev2->version_code; } void warn_on_version_mismatch(void) { char *msg; int cmp; /* get the kernel module version from /proc/drbd */ vcs_get_current(); /* get the userland version from PACKAGE_VERSION */ vcs_get_userland(); cmp = vcs_ver_cmp(&userland_version, ¤t_vcs_rel); /* no message if equal */ if (cmp == 0) return; if (cmp > 0xffff || cmp < -0xffff) /* major version differs! */ msg = "mixing different major numbers will not work!"; else if (cmp < 0) /* userland is older. always warn. */ msg = "you should upgrade your drbd tools!"; else if (cmp & 0xff00) /* userland is newer minor version */ msg = "please don't mix different DRBD series."; else /* userland is newer, but only differ in sublevel. */ msg = "preferably kernel and userland versions should match."; fprintf(stderr, "DRBD module version: %u.%u.%u\n" " userland version: %u.%u.%u\n%s\n", current_vcs_rel.version.major, current_vcs_rel.version.minor, current_vcs_rel.version.sublvl, userland_version.version.major, userland_version.version.minor, userland_version.version.sublvl, msg); } static char *vcs_to_str(struct vcs_rel *rev) { static char buffer[80]; // Not generic, sufficient for the purpose. if( rev->svn_revision ) { snprintf(buffer,80,"nv="U32,rev->svn_revision); } else { int len=20,p; unsigned char *bytes; p = sprintf(buffer,"git="); bytes = (unsigned char*)rev->git_hash; while(len--) p += sprintf(buffer+p,"%02x",*bytes++); } return buffer; } static void write_node_id(struct node_info *ni) { int fd; struct node_info_od on_disk; int size; fd = open(NODE_ID_FILE,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR); if( fd == -1 && errno == ENOENT) { mkdir(DRBD_LIB_DIR,S_IRWXU); fd = open(NODE_ID_FILE,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR); } if( fd == -1) { perror("Creation of "NODE_ID_FILE" failed."); exit(20); } if(ni->rev.svn_revision != 0) { // SVN style (old) on_disk.magic = cpu_to_be32(DRBD_MAGIC); on_disk.ni.node_uuid = cpu_to_be64(ni->node_uuid); on_disk.ni.rev.svn_revision = cpu_to_be32(ni->rev.svn_revision); memset(on_disk.ni.rev.git_hash,0,GIT_HASH_BYTE); size = SVN_STYLE_OD; } else { on_disk.magic = cpu_to_be32(DRBD_MAGIC+1); on_disk.ni.node_uuid = cpu_to_be64(ni->node_uuid); on_disk.ni.rev.svn_revision = 0; memcpy(on_disk.ni.rev.git_hash,ni->rev.git_hash,GIT_HASH_BYTE); size = sizeof(on_disk); } if( write(fd,&on_disk, size) != size) { perror("Write to "NODE_ID_FILE" failed."); exit(20); } close(fd); } static int read_node_id(struct node_info *ni) { int rr; int fd; struct node_info_od on_disk; fd = open(NODE_ID_FILE, O_RDONLY); if (fd == -1) { return 0; } rr = read(fd, &on_disk, sizeof(on_disk)); if (rr != sizeof(on_disk) && rr != SVN_STYLE_OD) { close(fd); return 0; } switch (be32_to_cpu(on_disk.magic)) { case DRBD_MAGIC: ni->node_uuid = be64_to_cpu(on_disk.ni.node_uuid); ni->rev.svn_revision = be32_to_cpu(on_disk.ni.rev.svn_revision); memset(ni->rev.git_hash, 0, GIT_HASH_BYTE); break; case DRBD_MAGIC+1: ni->node_uuid = be64_to_cpu(on_disk.ni.node_uuid); ni->rev.svn_revision = 0; memcpy(ni->rev.git_hash, on_disk.ni.rev.git_hash, GIT_HASH_BYTE); break; default: return 0; } close(fd); return 1; } /* to interrupt gethostbyname, * we not only need a signal, * but also the long jump: * gethostbyname would otherwise just restart the syscall * and timeout again. */ static jmp_buf timed_out; static void gethostbyname_timeout(int __attribute((unused)) signo) { longjmp(timed_out, 1); } #define DNS_TIMEOUT 3 /* seconds */ #define SOCKET_TIMEOUT 3 /* seconds */ struct hostent *my_gethostbyname(const char *name) { struct sigaction sa; struct sigaction so; struct hostent *h; alarm(0); sa.sa_handler = &gethostbyname_timeout; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGALRM, &sa, &so); if (!setjmp(timed_out)) { alarm(DNS_TIMEOUT); h = gethostbyname(name); } else /* timed out, longjmp of SIGALRM jumped here */ h = NULL; alarm(0); sigaction(SIGALRM, &so, NULL); return h; } /** * insert_usage_with_socket: * * Return codes: * * 0 - success * 1 - failed to create socket * 2 - unknown server * 3 - cannot connect to server * 5 - other error */ static int make_get_request(char *uri) { struct sockaddr_in server; struct hostent *host_info; unsigned long addr; int sock; char *req_buf; char *http_host = HTTP_HOST; int buf_len = 1024; char buffer[buf_len]; FILE *sockfd; int writeit; struct timeval timeout = { .tv_sec = SOCKET_TIMEOUT }; sock = socket( PF_INET, SOCK_STREAM, 0); if (sock < 0) return 1; setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); memset (&server, 0, sizeof(server)); /* convert host name to ip */ host_info = my_gethostbyname(http_host); if (host_info == NULL) { /* unknown host, try with ip */ if ((addr = inet_addr( HTTP_ADDR )) != INADDR_NONE) memcpy((char *)&server.sin_addr, &addr, sizeof(addr)); else { close(sock); return 2; } } else { memcpy((char *)&server.sin_addr, host_info->h_addr, host_info->h_length); } ssprintf(req_buf, "GET %s HTTP/1.0\r\n" "Host: "HTTP_HOST"\r\n" "User-Agent: drbdadm/"PACKAGE_VERSION" (%s; %s; %s; %s)\r\n" "\r\n", uri, nodeinfo.sysname, nodeinfo.release, nodeinfo.version, nodeinfo.machine); server.sin_family = AF_INET; server.sin_port = htons(HTTP_PORT); if (connect(sock, (struct sockaddr*)&server, sizeof(server))<0) { /* cannot connect to server */ close(sock); return 3; } if ((sockfd = fdopen(sock, "r+")) == NULL) { close(sock); return 5; } if (fputs(req_buf, sockfd) == EOF) { fclose(sockfd); close(sock); return 5; } writeit = 0; while (fgets(buffer, buf_len, sockfd) != NULL) { /* ignore http headers */ if (writeit == 0) { if (buffer[0] == '\r' || buffer[0] == '\n') writeit = 1; } else { fprintf(stderr,"%s", buffer); } } fclose(sockfd); close(sock); return 0; } static void url_encode(char *in, char *out) { char *h = "0123456789abcdef"; unsigned char c; while ((c = *in++) != 0) { if (c == '\n') break; if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '-' || c == '_' || c == '.') *out++ = c; else if (c == ' ') *out++ = '+'; else { *out++ = '%'; *out++ = h[c >> 4]; *out++ = h[c & 0x0f]; } } *out = 0; } /* Ensure that the node is counted on http://usage.drbd.org */ #define ANSWER_SIZE 80 void uc_node(enum usage_count_type type) { struct node_info ni; char *uri; int send = 0; int update = 0; char answer[ANSWER_SIZE]; char n_comment[ANSWER_SIZE*3]; char *r; if( type == UC_NO ) return; if( getuid() != 0 ) return; /* not when running directly from init, * or if stdout is no tty. * you do not want to have the "user information message" * as output from `drbdadm sh-resources all` */ if (getenv("INIT_VERSION")) return; if (no_tty) return; vcs_get_current(); if( ! read_node_id(&ni) ) { get_random_bytes(&ni.node_uuid,sizeof(ni.node_uuid)); ni.rev = current_vcs_rel; send = 1; } else if (current_vcs_is_from_proc_drbd == 0) { /* Avoid flapping between drbd-utils git-hash and * kernel module git-hash. */ send = 0; } else { // read_node_id() was successful if (!vcs_eq(&ni.rev,¤t_vcs_rel)) { ni.rev = current_vcs_rel; update = 1; send = 1; } } if(!send) return; n_comment[0]=0; if (type == UC_ASK ) { fprintf(stderr, "\n" "\t\t--== This is %s of DRBD ==--\n" "Please take part in the global DRBD usage count at http://"HTTP_HOST".\n\n" "The counter works anonymously. It creates a random number to identify\n" "your machine and sends that random number, along with the kernel and\n" "DRBD version, to "HTTP_HOST".\n\n" "The benefits for you are:\n" " * In response to your submission, the server ("HTTP_HOST") will tell you\n" " how many users before you have installed this version (%s).\n" " * With a high counter LINBIT has a strong motivation to\n" " continue funding DRBD's development.\n\n" "http://"HTTP_HOST"/cgi-bin/insert_usage.pl?nu="U64"&%s\n\n" "In case you want to participate but know that this machine is firewalled,\n" "simply issue the query string with your favorite web browser or wget.\n" "You can control all of this by setting 'usage-count' in your drbd.conf.\n\n" "* You may enter a free form comment about your machine, that gets\n" " used on "HTTP_HOST" instead of the big random number.\n" "* If you wish to opt out entirely, simply enter 'no'.\n" "* To count this node without comment, just press [RETURN]\n", update ? "an update" : "a new installation", PACKAGE_VERSION,ni.node_uuid, vcs_to_str(&ni.rev)); r = fgets(answer, ANSWER_SIZE, stdin); if(r && !strcmp(answer,"no\n")) send = 0; url_encode(answer,n_comment); } ssprintf(uri,"http://"HTTP_HOST"/cgi-bin/insert_usage.pl?nu="U64"&%s%s%s", ni.node_uuid, vcs_to_str(&ni.rev), n_comment[0] ? "&nc=" : "", n_comment); if (send) { write_node_id(&ni); fprintf(stderr, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" " --== Thank you for participating in the global usage survey ==--\n" "The server's response is:\n\n"); make_get_request(uri); if (type == UC_ASK) { fprintf(stderr, "\n" "From now on, drbdadm will contact "HTTP_HOST" only when you update\n" "DRBD or when you use 'drbdadm create-md'. Of course it will continue\n" "to ask you for confirmation as long as 'usage-count' is at its default\n" "value of 'ask'.\n\n" "Just press [RETURN] to continue: "); r = fgets(answer, 9, stdin); } } } /* For our purpose (finding the revision) SLURP_SIZE is always enough. */ char* run_admm_generic(struct d_resource* res ,const char* cmd) { const int SLURP_SIZE = 4096; int rr,pipes[2]; char* buffer; pid_t pid; buffer = malloc(SLURP_SIZE); if(!buffer) return 0; if(pipe(pipes)) return 0; pid = fork(); if(pid == -1) { fprintf(stderr,"Can not fork\n"); exit(E_EXEC_ERROR); } if(pid == 0) { // child close(pipes[0]); // close reading end dup2(pipes[1],1); // 1 = stdout close(pipes[1]); exit(_admm_generic(res,cmd, SLEEPS_VERY_LONG| DONT_REPORT_FAILED)); } close(pipes[1]); // close writing end rr = read(pipes[0], buffer, SLURP_SIZE-1); if( rr == -1) { free(buffer); // FIXME cleanup return 0; } buffer[rr]=0; close(pipes[0]); waitpid(pid,0,0); return buffer; } int adm_create_md(struct d_resource* res ,const char* cmd) { char answer[ANSWER_SIZE]; struct node_info ni; uint64_t device_uuid=0; uint64_t device_size=0; char *uri; int send=0; char *tb; int rv,fd; int soi_tmp; char *setup_opts_0_tmp; char *r; tb = run_admm_generic(res, "read-dev-uuid"); device_uuid = strto_u64(tb,NULL,16); free(tb); rv = _admm_generic(res, cmd, SLEEPS_VERY_LONG); // cmd is "create-md". if(rv || dry_run) return rv; fd = open(res->me->disk,O_RDONLY); if( fd != -1) { device_size = bdev_size(fd); close(fd); } if( read_node_id(&ni) && device_size && !device_uuid) { get_random_bytes(&device_uuid, sizeof(uint64_t)); if( global_options.usage_count == UC_YES ) send = 1; if( global_options.usage_count == UC_ASK ) { fprintf(stderr, "\n" "\t\t--== Creating metadata ==--\n" "As with nodes, we count the total number of devices mirrored by DRBD\n" "at http://"HTTP_HOST".\n\n" "The counter works anonymously. It creates a random number to identify\n" "the device and sends that random number, along with the kernel and\n" "DRBD version, to "HTTP_HOST".\n\n" "http://"HTTP_HOST"/cgi-bin/insert_usage.pl?nu="U64"&ru="U64"&rs="U64"\n\n" "* If you wish to opt out entirely, simply enter 'no'.\n" "* To continue, just press [RETURN]\n", ni.node_uuid,device_uuid,device_size ); r = fgets(answer, ANSWER_SIZE, stdin); if(r && strcmp(answer,"no\n")) send = 1; } } if(!device_uuid) { get_random_bytes(&device_uuid, sizeof(uint64_t)); } if (send) { ssprintf(uri,"http://"HTTP_HOST"/cgi-bin/insert_usage.pl?" "nu="U64"&ru="U64"&rs="U64, ni.node_uuid, device_uuid, device_size); make_get_request(uri); } /* HACK */ soi_tmp = soi; setup_opts_0_tmp = setup_opts[0]; setup_opts[0] = NULL; ssprintf( setup_opts[0], X64(016), device_uuid); soi=1; _admm_generic(res, "write-dev-uuid", SLEEPS_VERY_LONG); setup_opts[0] = setup_opts_0_tmp; soi = soi_tmp; return rv; } drbd-utils-8.9.10/user/v83/drbdadm_adjust.c0000644000175000017500000003247612466702074020313 0ustar apoikosapoikos/* drbdadm_adjust.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2003-2008, LINBIT Information Technologies GmbH. Copyright (C) 2003-2008, Philipp Reisner . Copyright (C) 2003-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include "drbdadm.h" #include "drbdtool_common.h" #include "drbdadm_parser.h" /* drbdsetup show might complain that the device minor does not exist at all. Redirect stderr to /dev/null therefore. */ static FILE *m_popen(int *pid,char** argv) { int mpid; int pipes[2]; int dev_null; if(pipe(pipes)) { perror("Creation of pipes failed"); exit(E_EXEC_ERROR); } dev_null = open("/dev/null", O_WRONLY); if (dev_null == -1) { perror("Opening /dev/null failed"); exit(E_EXEC_ERROR); } mpid = fork(); if(mpid == -1) { fprintf(stderr,"Can not fork"); exit(E_EXEC_ERROR); } if(mpid == 0) { close(pipes[0]); // close reading end dup2(pipes[1], fileno(stdout)); close(pipes[1]); dup2(dev_null, fileno(stderr)); close(dev_null); execvp(argv[0],argv); fprintf(stderr,"Can not exec"); exit(E_EXEC_ERROR); } close(pipes[1]); // close writing end close(dev_null); *pid=mpid; return fdopen(pipes[0],"r"); } /* option value equal? */ static int ov_eq(char* val1, char* val2) { unsigned long long v1,v2; if(val1 == NULL && val2 == NULL) return 1; if(val1 == NULL || val2 == NULL) return 0; if(new_strtoll(val1,0,&v1) == MSE_OK && new_strtoll(val2,0,&v2) == MSE_OK) return v1 == v2; return !strcmp(val1,val2); } static int opts_equal(struct d_option* conf, struct d_option* running) { struct d_option* opt; while(running) { if((opt=find_opt(conf,running->name))) { if(!ov_eq(running->value,opt->value)) { /* printf("Value of '%s' differs: r=%s c=%s\n", opt->name,running->value,opt->value); */ return 0; } opt->mentioned=1; } else { if(!running->is_default) { /*printf("Only in running config %s: %s\n", running->name,running->value);*/ return 0; } } running=running->next; } while(conf) { if(conf->mentioned==0) { /*printf("Only in config file %s: %s\n", conf->name,conf->value);*/ return 0; } conf=conf->next; } return 1; } static int addr_equal(struct d_resource* conf, struct d_resource* running) { int equal; if (conf->peer == NULL && running->peer == NULL) return 1; if (running->peer == NULL) return 0; equal = !strcmp(conf->me->address, running->me->address) && !strcmp(conf->me->port, running->me->port) && !strcmp(conf->me->address_family, running->me->address_family); if(conf->me->proxy) equal = equal && !strcmp(conf->me->proxy->inside_addr, running->peer->address) && !strcmp(conf->me->proxy->inside_port, running->peer->port) && !strcmp(conf->me->proxy->inside_af, running->peer->address_family); else equal = equal && conf->peer && !strcmp(conf->peer->address, running->peer->address) && !strcmp(conf->peer->port, running->peer->port) && !strcmp(conf->peer->address_family, running->peer->address_family); return equal; } static int proto_equal(struct d_resource* conf, struct d_resource* running) { if (conf->protocol == NULL && running->protocol == NULL) return 1; if (conf->protocol == NULL || running->protocol == NULL) return 0; return !strcmp(conf->protocol, running->protocol); } /* Are both internal, or are both not internal. */ static int int_eq(char* m_conf, char* m_running) { return !strcmp(m_conf,"internal") == !strcmp(m_running,"internal"); } static int disk_equal(struct d_host_info* conf, struct d_host_info* running) { int eq = 1; if (conf->disk == NULL && running->disk == NULL) return 1; if (conf->disk == NULL || running->disk == NULL) return 0; eq &= !strcmp(conf->disk,running->disk); eq &= int_eq(conf->meta_disk,running->meta_disk); if(!strcmp(conf->meta_disk,"internal")) return eq; eq &= !strcmp(conf->meta_disk,running->meta_disk); return eq; } /* NULL terminated */ static void find_option_in_resources(char *name, struct d_option *list, struct d_option **opt, ...) { va_list va; va_start(va, opt); /* We need to keep setting *opt to NULL, even if a list == NULL. */ while (list || opt) { while (list) { if (strcmp(list->name, name) == 0) break; list = list->next; } *opt = list; list = va_arg(va, struct d_option*); opt = va_arg(va, struct d_option**); } } static int do_proxy_reconf(struct d_resource *res, const char *cmd) { int rv; char *argv[4] = { drbd_proxy_ctl, "-c", (char*)cmd, NULL }; rv = m_system_ex(argv, SLEEPS_SHORT, res->name); return rv; } #define MAX_PLUGINS (10) #define MAX_PLUGIN_NAME (16) /* The new name is appended to the alist. */ int _is_plugin_in_list(char *string, char slist[MAX_PLUGINS][MAX_PLUGIN_NAME], char alist[MAX_PLUGINS][MAX_PLUGIN_NAME], int list_len) { int word_len, i; char *copy; for(word_len=0; string[word_len]; word_len++) if (isspace(string[word_len])) break; if (word_len+1 >= MAX_PLUGIN_NAME) { fprintf(stderr, "Wrong proxy plugin name %*.*s", word_len, word_len, string); exit(E_CONFIG_INVALID); } copy = alist[list_len]; strncpy(copy, string, word_len); copy[word_len] = 0; for(i=0; i= MAX_PLUGINS) { fprintf(stderr, "Too many proxy plugins."); exit(E_CONFIG_INVALID); } return 0; } static int proxy_reconf(struct d_resource *res, struct d_resource *running) { int reconn = 0; struct d_option* res_o, *run_o; unsigned long long v1, v2, minimum; char *plugin_changes[MAX_PLUGINS], *cp, *conn_name; /* It's less memory usage when we're storing char[]. malloc overhead for * the few bytes + pointers is much more. */ char p_res[MAX_PLUGINS][MAX_PLUGIN_NAME], p_run[MAX_PLUGINS][MAX_PLUGIN_NAME]; int used, i, re_do; reconn = 0; find_option_in_resources("memlimit", res->proxy_options, &res_o, running->proxy_options, &run_o, NULL, NULL); v1 = res_o ? m_strtoll(res_o->value, 1) : 0; v2 = run_o ? m_strtoll(run_o->value, 1) : 0; minimum = v1 < v2 ? v1 : v2; /* We allow an Ñ” [epsilon] of 2%, so that small (rounding) deviations do * not cause the connection to be re-established. */ if (res_o && (!run_o || abs(v1-v2)/(float)minimum > 0.02)) { redo_whole_conn: /* As the memory is in use while the connection is allocated we have to * completely destroy and rebuild the connection. */ schedule_dcmd( do_proxy_conn_down, res, NULL, 0); schedule_dcmd( do_proxy_conn_up, res, NULL, 1); schedule_dcmd( do_proxy_conn_plugins, res, NULL, 2); /* With connection cleanup and reopen everything is rebuild anyway, and * DRBD will get a reconnect too. */ return 0; } res_o = res->proxy_plugins; run_o = running->proxy_plugins; used = 0; conn_name = proxy_connection_name(res); for(i=0; i= sizeof(plugin_changes)-1) { fprintf(stderr, "Too many proxy plugin changes"); exit(E_CONFIG_INVALID); } /* Now we can be sure that we can store another pointer. */ if (!res_o) { if (run_o) { /* More plugins running than configured - just stop here. */ m_asprintf(&cp, "set plugin %s %d end", conn_name, i); plugin_changes[used++] = cp; } else { /* Both at the end? ok, quit loop */ } break; } /* res_o != NULL. */ if (!run_o) { p_run[i][0] = 0; if (_is_plugin_in_list(res_o->name, p_run, p_res, i)) { /* Current plugin was already active, just at another position. * Redo the whole connection. */ goto redo_whole_conn; } /* More configured than running - just add it, if it's not already * somewhere else. */ m_asprintf(&cp, "set plugin %s %d %s", conn_name, i, res_o->name); plugin_changes[used++] = cp; } else { /* If we get here, both lists have been filled in parallel, so we * can simply use the common counter. */ re_do = _is_plugin_in_list(res_o->name, p_run, p_res, i) || _is_plugin_in_list(run_o->name, p_res, p_run, i); if (re_do) { /* Plugin(s) were moved, not simple reconfigured. * Re-do the whole connection. */ goto redo_whole_conn; } /* TODO: We don't (yet) account for possible different ordering of * the parameters to the plugin. * plugin A 1 B 2 * should be treated as equal to * plugin B 2 A 1. */ if (strcmp(run_o->name, res_o->name) != 0) { /* Either a different plugin, or just different settings * - plugin can be overwritten. */ m_asprintf(&cp, "set plugin %s %d %s", conn_name, i, res_o->name); plugin_changes[used++] = cp; } } if (res_o) res_o = res_o->next; if (run_o) run_o = run_o->next; } /* change only a few plugin settings. */ for(i=0; iname); err = stat("/dev/drbd/by-res", &sbuf); if (err) /* probably no udev rules in use */ return 0; err = stat(link_name, &sbuf); if (err) /* resource link cannot be stat()ed. */ return 1; /* double check device information */ if (!S_ISBLK(sbuf.st_mode)) return 1; if (major(sbuf.st_rdev) != DRBD_MAJOR) return 1; if (minor(sbuf.st_rdev) != res->me->device_minor) return 1; /* Link exists, and is expected block major:minor. * Do nothing. */ return 0; } /* * CAUTION this modifies global static char * config_file! */ int adm_adjust(struct d_resource* res,char* unused __attribute((unused))) { char* argv[20]; int pid,argc, i; struct d_resource* running; int do_attach=0,do_connect=0,do_syncer=0; int have_disk=0,have_net=0,can_do_proxy=1; char config_file_dummy[250], *conn_name, show_conn[128]; /* disable check_uniq, so it won't interfere * with parsing of drbdsetup show output */ config_valid = 2; /* setup error reporting context for the parsing routines */ line = 1; sprintf(config_file_dummy,"drbdsetup %u show", res->me->device_minor); config_file = config_file_dummy; argc=0; argv[argc++]=drbdsetup; argv[argc++]=res->me->device; argv[argc++]="show"; argv[argc++]=0; /* actually parse drbdsetup show output */ yyin = m_popen(&pid,argv); running = parse_resource(res->name, IgnDiscardMyData); fclose(yyin); waitpid(pid,0,0); /* Sets "me" and "peer" pointer */ post_parse(running, 0); set_peer_in_resource(running, 0); /* Parse proxy settings, if this host has a proxy definition */ if (res->me->proxy) { line = 1; conn_name = proxy_connection_name(res); i=snprintf(show_conn, sizeof(show_conn), "show proxy-settings %s", conn_name); if (i>= sizeof(show_conn)-1) { fprintf(stderr,"connection name too long"); exit(E_THINKO); } sprintf(config_file_dummy,"drbd-proxy-ctl -c '%s'", show_conn); config_file = config_file_dummy; argc=0; argv[argc++]=drbd_proxy_ctl; argv[argc++]="-c"; argv[argc++]=show_conn; argv[argc++]=0; /* actually parse "drbd-proxy-ctl show" output */ yyin = m_popen(&pid,argv); can_do_proxy = !parse_proxy_settings(running, PARSER_CHECK_PROXY_KEYWORD | PARSER_STOP_IF_INVALID); fclose(yyin); waitpid(pid,0,0); } do_attach = !opts_equal(res->disk_options, running->disk_options); if(running->me) { do_attach |= (res->me->device_minor != running->me->device_minor); do_attach |= !disk_equal(res->me, running->me); have_disk = (running->me->disk != NULL); } else do_attach |= 1; do_connect = !opts_equal(res->net_options, running->net_options); do_connect |= !addr_equal(res,running); do_connect |= !proto_equal(res,running); /* No adjust support for drbd proxy version 1. */ if (res->me->proxy && can_do_proxy) do_connect |= proxy_reconf(res,running); have_net = (running->protocol != NULL); do_syncer = !opts_equal(res->sync_options, running->sync_options); /* Special case: nothing changed, but the resource name. * Trigger a no-op syncer request, which will cause a KOBJ_CHANGE * to be broadcast, so udev may pick up the resource name change * and update its symlinks. */ if (!(do_attach || do_syncer || do_connect)) do_syncer = need_trigger_kobj_change(running); if(do_attach) { if(have_disk) schedule_dcmd(adm_generic_s,res,"detach",0); schedule_dcmd(adm_attach,res,"attach",0); } if(do_syncer) schedule_dcmd(adm_syncer,res,"syncer",1); if(do_connect) { if (have_net && res->peer) schedule_dcmd(adm_generic_s,res,"disconnect",0); schedule_dcmd(adm_connect,res,"connect",2); } return 0; } drbd-utils-8.9.10/user/v84/0000755000175000017500000000000013027242657015165 5ustar apoikosapoikosdrbd-utils-8.9.10/user/v84/Makefile.in0000644000175000017500000000762513011114651017225 0ustar apoikosapoikos# Makefile for drbd.o # # This file is part of DRBD by Philipp Reisner and Lars Ellenberg. # # drbd is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. # VPATH = ../shared # variables set by configure DISTRO = @DISTRO@ prefix = @prefix@ exec_prefix = @exec_prefix@ localstatedir = @localstatedir@ datarootdir = @datarootdir@ datadir = @datadir@ sbindir = @sbindir@ sysconfdir = @sysconfdir@ BASH_COMPLETION_SUFFIX = @BASH_COMPLETION_SUFFIX@ UDEV_RULE_SUFFIX = @UDEV_RULE_SUFFIX@ INITDIR = @INITDIR@ LIBDIR = @prefix@/lib/@PACKAGE_TARNAME@ CC = @CC@ CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ LN_S = @LN_S@ # features enabled or disabled by configure WITH_84_SUPPORT = @WITH_84_SUPPORT@ WITH_UDEV = @WITH_UDEV@ WITH_XEN = @WITH_XEN@ WITH_PACEMAKER = @WITH_PACEMAKER@ WITH_RGMANAGER = @WITH_RGMANAGER@ WITH_BASHCOMPLETION = @WITH_BASHCOMPLETION@ # variables meant to be overridden from the make command line DESTDIR ?= / CFLAGS += -Wall -I. -I../shared CFLAGS += $(EXTRA_CFLAGS) GENETLINK_H := /usr/include/linux/genetlink.h libgenl.o: CFLAGS += $(shell for w in CTRL_ATTR_VERSION CTRL_ATTR_HDRSIZE CTRL_ATTR_MCAST_GROUPS; do grep -qw $$w $(GENETLINK_H) && echo -DHAVE_$$w; done) drbdadm-obj = drbdadm_scanner.o drbdadm_parser.o drbdadm_main.o \ drbdadm_adjust.o drbdtool_common.o drbdadm_usage_cnt.o \ drbd_buildtag.o registry.o config_flags.o libgenl.o \ drbd_nla.o shared_tool.o shared_main.o shared_parser.o drbdsetup-obj = libgenl.o registry.o drbdsetup.o drbdtool_common.o \ drbd_buildtag.o drbd_strings.o config_flags.o drbd_nla.o \ wrap_printf.o drbdsetup_colors.o shared_tool.o all-obj := $(drbdadm-obj) $(drbdsetup-obj) all: tools ../shared_prereqs.mk: ; include ../shared_prereqs.mk ifeq ($(WITH_84_SUPPORT),yes) tools: drbdadm-84 drbdsetup-84 else tools: endif .PHONY: drbdadm drbdsetup drbdadm drbdsetup: echo >&2 "You meant to ask for $@-84" ; exit 1 drbdadm-84: $(drbdadm-obj) $(LINK.c) $(LDFLAGS) -o $@ $^ drbdadm_scanner.c: drbdadm_scanner.fl drbdadm_parser.h flex -s -odrbdadm_scanner.c drbdadm_scanner.fl drbdsetup-84: $(drbdsetup-obj) $(LINK.c) $(LDFLAGS) -o $@ $^ clean: rm -f drbdadm_scanner.c rm -f drbdsetup-84 drbdadm-84 $(all-obj) rm -f *~ distclean: clean rm -f $(all-dep) install: ifeq ($(WITH_84_SUPPORT),yes) install -d $(DESTDIR)$(localstatedir)/lib/drbd install -d $(DESTDIR)$(localstatedir)/lock install -d $(DESTDIR)/lib/drbd/ if getent group haclient > /dev/null 2> /dev/null ; then \ install -g haclient -m 4750 drbdsetup-84 $(DESTDIR)/lib/drbd/ ; \ install -m 755 drbdadm-84 $(DESTDIR)/lib/drbd/ ; \ else \ install -m 755 drbdsetup-84 $(DESTDIR)/lib/drbd/ ; \ install -m 755 drbdadm-84 $(DESTDIR)/lib/drbd/ ; \ fi endif uninstall: rm -f $(DESTDIR)/lib/drbd/drbdsetup-84 rm -f $(DESTDIR)/lib/drbd/drbdadm-84 spell: for f in drbdadm_adjust.c drbdadm_main.c drbdadm_parser.c drbdadm_usage_cnt.c drbdsetup.c drbdtool_common.c; do \ aspell --save-repl --dont-backup --personal=./../documentation/aspell.en.per check $$f; \ done .PHONY: install uninstall clean distclean ../../configure: @echo "please (re-)run ./autogen.sh with appropriate arguments"; exit 1 ../../config.status: ../../configure @echo "please (re-)run ./configure with appropriate arguments"; exit 1 Makefile.in: ; Makefile: Makefile.in ../../config.status cd ../.. && ./config.status user/v84/Makefile drbd-utils-8.9.10/user/v84/config_flags.c0000644000175000017500000006645112634271674017772 0ustar apoikosapoikos#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "libgenl.h" #include #include #include #include #include "drbd_nla.h" #include #include "drbdtool_common.h" #include "config_flags.h" #ifndef AF_INET_SDP #define AF_INET_SDP 27 #define PF_INET_SDP AF_INET_SDP #endif #ifndef ARRAY_SIZE #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #endif #define NLA_POLICY(p) \ .nla_policy = p ## _nl_policy, \ .nla_policy_size = ARRAY_SIZE(p ## _nl_policy) /* ============================================================================================== */ static int enum_string_to_int(const char **map, int size, const char *value, int (*strcmp)(const char *, const char *)) { int n; if (!value) return -1; for (n = 0; n < size; n++) { if (map[n] && !strcmp(value, map[n])) return n; } return -1; } static bool enum_is_default(struct field_def *field, const char *value) { int n; n = enum_string_to_int(field->u.e.map, field->u.e.size, value, strcmp); return n == field->u.e.def; } static bool enum_is_equal(struct field_def *field, const char *a, const char *b) { return !strcmp(a, b); } static int type_of_field(struct context_def *ctx, struct field_def *field) { return ctx->nla_policy[__nla_type(field->nla_type)].type; } static int len_of_field(struct context_def *ctx, struct field_def *field) { return ctx->nla_policy[__nla_type(field->nla_type)].len; } static const char *get_enum(struct context_def *ctx, struct field_def *field, struct nlattr *nla) { int i; assert(type_of_field(ctx, field) == NLA_U32); i = nla_get_u32(nla); if (i < 0 || i >= field->u.e.size) return NULL; return field->u.e.map[i]; } static bool put_enum(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { int n; n = enum_string_to_int(field->u.e.map, field->u.e.size, value, strcmp); if (n == -1) return false; assert(type_of_field(ctx, field) == NLA_U32); nla_put_u32(msg, field->nla_type, n); return true; } static int enum_usage(struct field_def *field, char *str, int size) { const char** map = field->u.e.map; char sep = '{'; int n, len = 0, l; l = snprintf(str, size, "[--%s=", field->name); len += l; size -= l; for (n = 0; n < field->u.e.size; n++) { if (!map[n]) continue; l = snprintf(str + len, size, "%c%s", sep, map[n]); len += l; size -= l; sep = '|'; } assert (sep != '{'); l = snprintf(str+len, size, "}]"); len += l; size -= l; return len; } static bool enum_is_default_nocase(struct field_def *field, const char *value) { int n; n = enum_string_to_int(field->u.e.map, field->u.e.size, value, strcasecmp); return n == field->u.e.def; } static bool enum_is_equal_nocase(struct field_def *field, const char *a, const char *b) { return !strcasecmp(a, b); } static bool put_enum_nocase(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { int n; n = enum_string_to_int(field->u.e.map, field->u.e.size, value, strcasecmp); if (n == -1) return false; assert(type_of_field(ctx, field) == NLA_U32); nla_put_u32(msg, field->nla_type, n); return true; } static void enum_describe_xml(struct field_def *field) { const char **map = field->u.e.map; int n; printf("\t\n"); } /* ---------------------------------------------------------------------------------------------- */ static bool numeric_is_default(struct field_def *field, const char *value) { long long l; /* FIXME: unsigned long long values are broken. */ l = m_strtoll(value, field->u.n.scale); return l == field->u.n.def; } static bool numeric_is_equal(struct field_def *field, const char *a, const char *b) { long long la, lb; /* FIXME: unsigned long long values are broken. */ la = m_strtoll(a, field->u.n.scale); lb = m_strtoll(b, field->u.n.scale); return la == lb; } static const char *get_numeric(struct context_def *ctx, struct field_def *field, struct nlattr *nla) { static char buffer[1 + 20 + 2]; char scale = field->u.n.scale; unsigned long long l; int n; switch(type_of_field(ctx, field)) { case NLA_U8: l = nla_get_u8(nla); break; case NLA_U16: l = nla_get_u16(nla); break; case NLA_U32: l = nla_get_u32(nla); break; case NLA_U64: l = nla_get_u64(nla); break; default: return NULL; } if (field->u.n.is_signed) { /* Sign extend. */ switch(type_of_field(ctx, field)) { case NLA_U8: l = (int8_t)l; break; case NLA_U16: l = (int16_t)l; break; case NLA_U32: l = (int32_t)l; break; case NLA_U64: l = (int64_t)l; break; } n = snprintf(buffer, sizeof(buffer), "%lld%c", l, scale == '1' ? 0 : scale); } else n = snprintf(buffer, sizeof(buffer), "%llu%c", l, scale == '1' ? 0 : scale); assert(n < sizeof(buffer)); return buffer; } static bool put_numeric(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { long long l; /* FIXME: unsigned long long values are broken. */ l = m_strtoll(value, field->u.n.scale); switch(type_of_field(ctx, field)) { case NLA_U8: nla_put_u8(msg, field->nla_type, l); break; case NLA_U16: nla_put_u16(msg, field->nla_type, l); break; case NLA_U32: nla_put_u32(msg, field->nla_type, l); break; case NLA_U64: nla_put_u64(msg, field->nla_type, l); break; default: return false; } return true; } static int numeric_usage(struct field_def *field, char *str, int size) { return snprintf(str, size,"[--%s=(%lld ... %lld)]", field->name, field->u.n.min, field->u.n.max); } static void numeric_describe_xml(struct field_def *field) { printf("\t\n"); } /* ---------------------------------------------------------------------------------------------- */ static int boolean_string_to_int(const char *value) { if (!value || !strcmp(value, "yes")) return 1; else if (!strcmp(value, "no")) return 0; else return -1; } static bool boolean_is_default(struct field_def *field, const char *value) { int yesno; yesno = boolean_string_to_int(value); return yesno == field->u.b.def; } static bool boolean_is_equal(struct field_def *field, const char *a, const char *b) { return boolean_string_to_int(a) == boolean_string_to_int(b); } static const char *get_boolean(struct context_def *ctx, struct field_def *field, struct nlattr *nla) { int i; assert(type_of_field(ctx, field) == NLA_U8); i = nla_get_u8(nla); return i ? "yes" : "no"; } static bool put_boolean(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { int yesno; yesno = boolean_string_to_int(value); if (yesno == -1) return false; assert(type_of_field(ctx, field) == NLA_U8); nla_put_u8(msg, field->nla_type, yesno); return true; } static bool put_flag(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { int yesno; yesno = boolean_string_to_int(value); if (yesno == -1) return false; assert(type_of_field(ctx, field) == NLA_U8); if (yesno) nla_put_u8(msg, field->nla_type, yesno); return true; } static int boolean_usage(struct field_def *field, char *str, int size) { return snprintf(str, size,"[--%s={yes|no}]", field->name); } static void boolean_describe_xml(struct field_def *field) { printf("\t\n", field->name, field->u.b.def ? "yes" : "no"); } /* ---------------------------------------------------------------------------------------------- */ static bool string_is_default(struct field_def *field, const char *value) { return value && !strcmp(value, ""); } static bool string_is_equal(struct field_def *field, const char *a, const char *b) { return !strcmp(a, b); } static const char *get_string(struct context_def *ctx, struct field_def *field, struct nlattr *nla) { char *str; int len; assert(type_of_field(ctx, field) == NLA_NUL_STRING); str = (char *)nla_data(nla); len = len_of_field(ctx, field); assert(memchr(str, 0, len + 1) != NULL); return str; } static bool put_string(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { assert(type_of_field(ctx, field) == NLA_NUL_STRING); nla_put_string(msg, field->nla_type, value); return true; } static int string_usage(struct field_def *field, char *str, int size) { return snprintf(str, size,"[--%s=]", field->name); } static void string_describe_xml(struct field_def *field) { printf("\t\n", field->name); } const char *double_quote_string(const char *str) { static char *buffer; const char *s; char *b; int len = 0; for (s = str; *s; s++) { if (*s == '\\' || *s == '"') len++; len++; } b = realloc(buffer, len + 3); if (!b) return NULL; buffer = b; *b++ = '"'; for (s = str; *s; s++) { if (*s == '\\' || *s == '"') *b++ = '\\'; *b++ = *s; } *b++ = '"'; *b++ = 0; return buffer; } /* ---------------------------------------------------------------------------------------------- */ static bool address_is_default(struct field_def *field, const char *value) { return true; } static bool address_is_equal(struct field_def *field, const char *a, const char *b) { return !strcmp(a, b); } /* It will only print the WARNING if the warn flag is set with the _first_ call! */ #define PROC_NET_AF_SCI_FAMILY "/proc/net/af_sci/family" #define PROC_NET_AF_SSOCKS_FAMILY "/proc/net/af_ssocks/family" int get_af_ssocks(int warn_and_use_default) { char buf[16]; int c, fd; static int af = -1; if (af > 0) return af; fd = open(PROC_NET_AF_SSOCKS_FAMILY, O_RDONLY); if (fd < 0) fd = open(PROC_NET_AF_SCI_FAMILY, O_RDONLY); if (fd < 0) { if (warn_and_use_default) { fprintf(stderr, "open(" PROC_NET_AF_SSOCKS_FAMILY ") " "failed: %m\n WARNING: assuming AF_SSOCKS = 27. " "Socket creation may fail.\n"); af = 27; } return af; } c = read(fd, buf, sizeof(buf)-1); if (c > 0) { buf[c] = 0; if (buf[c-1] == '\n') buf[c-1] = 0; af = m_strtoll(buf,1); } else { if (warn_and_use_default) { fprintf(stderr, "read(" PROC_NET_AF_SSOCKS_FAMILY ") " "failed: %m\n WARNING: assuming AF_SSOCKS = 27. " "Socket creation may fail.\n"); af = 27; } } close(fd); return af; } static char *af_to_str(int af) { if (af == AF_INET) return "ipv4"; else if (af == AF_INET6) return "ipv6"; /* AF_SSOCKS typically is 27, the same as AF_INET_SDP. * But with warn_and_use_default = 0, it will stay at -1 if not available. * Just keep the test on ssocks before the one on SDP (which is hard-coded), * and all should be fine. */ else if (af == get_af_ssocks(0)) return "ssocks"; else if (af == AF_INET_SDP) return "sdp"; else return "unknown"; } void sprint_address(char *buffer, void *address, int addr_len) { union { struct sockaddr addr; struct sockaddr_in addr4; struct sockaddr_in6 addr6; } a; memset(&a, 0, sizeof(a)); memcpy(&a.addr, address, addr_len); if (a.addr.sa_family == AF_INET || a.addr.sa_family == get_af_ssocks(0) || a.addr.sa_family == AF_INET_SDP) { sprintf(buffer, "%s %s:%d", af_to_str(a.addr4.sin_family), inet_ntoa(a.addr4.sin_addr), ntohs(a.addr4.sin_port)); } else if (a.addr.sa_family == AF_INET6) { char buf2[ADDRESS_STR_MAX]; int n; buf2[0] = 0; getnameinfo(&a.addr, addr_len, buf2, sizeof(buf2), NULL, 0, NI_NUMERICHOST|NI_NUMERICSERV); n = snprintf(buffer, ADDRESS_STR_MAX, "%s [%s]:%d", af_to_str(a.addr6.sin6_family), buf2, ntohs(a.addr6.sin6_port)); assert(n > 0); assert(n < ADDRESS_STR_MAX); /* there should be no need to truncate */ } else { sprintf(buffer, "[unknown af=%d, len=%d]", a.addr.sa_family, addr_len); } } static const char *get_address(struct context_def *ctx, struct field_def *field, struct nlattr *nla) { static char buffer[ADDRESS_STR_MAX]; sprint_address(buffer, nla_data(nla), nla_len(nla)); return buffer; } static void resolv6(const char *name, struct sockaddr_in6 *addr) { struct addrinfo hints, *res, *tmp; int err; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; err = getaddrinfo(name, 0, &hints, &res); if (err) { fprintf(stderr, "getaddrinfo %s: %s\n", name, gai_strerror(err)); exit(20); } /* Yes, it is a list. We use only the first result. The loop is only * there to document that we know it is a list */ for (tmp = res; tmp; tmp = tmp->ai_next) { memcpy(addr, tmp->ai_addr, sizeof(*addr)); break; } freeaddrinfo(res); if (0) { /* debug output */ char ip[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &addr->sin6_addr, ip, sizeof(ip)); fprintf(stderr, "%s -> %02x %04x %08x %s %08x\n", name, addr->sin6_family, addr->sin6_port, addr->sin6_flowinfo, ip, addr->sin6_scope_id); } } static unsigned long resolv(const char* name) { unsigned long retval; if((retval = inet_addr(name)) == INADDR_NONE ) { struct hostent *he; he = gethostbyname(name); if (!he) { fprintf(stderr, "can not resolve the hostname: gethostbyname(%s): %s\n", name, hstrerror(h_errno)); exit(20); } retval = ((struct in_addr *)(he->h_addr_list[0]))->s_addr; } return retval; } static void split_ipv6_addr(char **address, int *port) { /* ipv6:[fe80::0234:5678:9abc:def1]:8000; */ char *b = strrchr(*address,']'); if (address[0][0] != '[' || b == NULL || (b[1] != ':' && b[1] != '\0')) { fprintf(stderr, "unexpected ipv6 format: %s\n", *address); exit(20); } *b = 0; *address += 1; /* skip '[' */ if (b[1] == ':') *port = m_strtoll(b+2,1); /* b+2: "]:" */ else *port = 7788; /* will we ever get rid of that default port? */ } static void split_address(int *af, char** address, int* port) { static struct { char* text; int af; } afs[] = { { "ipv4:", AF_INET }, { "ipv6:", AF_INET6 }, { "sdp:", AF_INET_SDP }, { "ssocks:", -1 }, }; unsigned int i; char *b; *af=AF_INET; for (i=0; inla_type, value); return true; } static int address_usage(struct field_def *field, char *str, int size) { return snprintf(str, size,"[--%s=[{af}:]{local_addr}[:{port}]]", field->name); } static void address_describe_xml(struct field_def *field) { printf("\t\n", field->name); } /* ============================================================================================== */ #define ENUM(f, d) \ .nla_type = T_ ## f, \ .is_default = enum_is_default, \ .is_equal = enum_is_equal, \ .get = get_enum, \ .put = put_enum, \ .usage = enum_usage, \ .describe_xml = enum_describe_xml, \ .u = { .e = { \ .map = f ## _map, \ .size = ARRAY_SIZE(f ## _map), \ .def = DRBD_ ## d ## _DEF } } #define ENUM_NOCASE(f, d) \ .nla_type = T_ ## f, \ .is_default = enum_is_default_nocase, \ .is_equal = enum_is_equal_nocase, \ .get = get_enum, \ .put = put_enum_nocase, \ .usage = enum_usage, \ .describe_xml = enum_describe_xml, \ .u = { .e = { \ .map = f ## _map, \ .size = ARRAY_SIZE(f ## _map), \ .def = DRBD_ ## d ## _DEF } } #define NUMERIC(f, d) \ .nla_type = T_ ## f, \ .is_default = numeric_is_default, \ .is_equal = numeric_is_equal, \ .get = get_numeric, \ .put = put_numeric, \ .usage = numeric_usage, \ .describe_xml = numeric_describe_xml, \ .u = { .n = { \ .min = DRBD_ ## d ## _MIN, \ .max = DRBD_ ## d ## _MAX, \ .def = DRBD_ ## d ## _DEF, \ .is_signed = F_ ## f ## _IS_SIGNED, \ .scale = DRBD_ ## d ## _SCALE } } #define BOOLEAN(f, d) \ .nla_type = T_ ## f, \ .is_default = boolean_is_default, \ .is_equal = boolean_is_equal, \ .get = get_boolean, \ .put = put_boolean, \ .usage = boolean_usage, \ .describe_xml = boolean_describe_xml, \ .u = { .b = { \ .def = DRBD_ ## d ## _DEF } }, \ .argument_is_optional = true #define FLAG(f) \ .nla_type = T_ ## f, \ .is_default = boolean_is_default, \ .is_equal = boolean_is_equal, \ .get = get_boolean, \ .put = put_flag, \ .usage = boolean_usage, \ .describe_xml = boolean_describe_xml, \ .u = { .b = { \ .def = false } }, \ .argument_is_optional = true #define STRING(f) \ .nla_type = T_ ## f, \ .is_default = string_is_default, \ .is_equal = string_is_equal, \ .get = get_string, \ .put = put_string, \ .usage = string_usage, \ .describe_xml = string_describe_xml, \ .needs_double_quoting = true #define ADDRESS(f) \ .nla_type = T_ ## f, \ .is_default = address_is_default, \ .is_equal = address_is_equal, \ .get = get_address, \ .put = put_address, \ .usage = address_usage, \ .describe_xml = address_describe_xml, \ .needs_double_quoting = false /* ============================================================================================== */ const char *wire_protocol_map[] = { [DRBD_PROT_A] = "A", [DRBD_PROT_B] = "B", [DRBD_PROT_C] = "C", }; const char *on_io_error_map[] = { [EP_PASS_ON] = "pass_on", [EP_CALL_HELPER] = "call-local-io-error", [EP_DETACH] = "detach", }; const char *fencing_map[] = { [FP_DONT_CARE] = "dont-care", [FP_RESOURCE] = "resource-only", [FP_STONITH] = "resource-and-stonith", }; const char *after_sb_0p_map[] = { [ASB_DISCONNECT] = "disconnect", [ASB_DISCARD_YOUNGER_PRI] = "discard-younger-primary", [ASB_DISCARD_OLDER_PRI] = "discard-older-primary", [ASB_DISCARD_ZERO_CHG] = "discard-zero-changes", [ASB_DISCARD_LEAST_CHG] = "discard-least-changes", [ASB_DISCARD_LOCAL] = "discard-local", [ASB_DISCARD_REMOTE] = "discard-remote", }; const char *after_sb_1p_map[] = { [ASB_DISCONNECT] = "disconnect", [ASB_CONSENSUS] = "consensus", [ASB_VIOLENTLY] = "violently-as0p", [ASB_DISCARD_SECONDARY] = "discard-secondary", [ASB_CALL_HELPER] = "call-pri-lost-after-sb", }; const char *after_sb_2p_map[] = { [ASB_DISCONNECT] = "disconnect", [ASB_VIOLENTLY] = "violently-as0p", [ASB_CALL_HELPER] = "call-pri-lost-after-sb", }; const char *rr_conflict_map[] = { [ASB_DISCONNECT] = "disconnect", [ASB_VIOLENTLY] = "violently", [ASB_CALL_HELPER] = "call-pri-lost", }; const char *on_no_data_map[] = { [OND_IO_ERROR] = "io-error", [OND_SUSPEND_IO] = "suspend-io", }; const char *on_congestion_map[] = { [OC_BLOCK] = "block", [OC_PULL_AHEAD] = "pull-ahead", [OC_DISCONNECT] = "disconnect", }; const char *read_balancing_map[] = { [RB_PREFER_LOCAL] = "prefer-local", [RB_PREFER_REMOTE] = "prefer-remote", [RB_ROUND_ROBIN] = "round-robin", [RB_LEAST_PENDING] = "least-pending", [RB_CONGESTED_REMOTE] = "when-congested-remote", [RB_32K_STRIPING] = "32K-striping", [RB_64K_STRIPING] = "64K-striping", [RB_128K_STRIPING] = "128K-striping", [RB_256K_STRIPING] = "256K-striping", [RB_512K_STRIPING] = "512K-striping", [RB_1M_STRIPING] = "1M-striping" }; #define CHANGEABLE_DISK_OPTIONS \ { "on-io-error", ENUM(on_io_error, ON_IO_ERROR) }, \ { "fencing", ENUM(fencing, FENCING) }, \ { "disk-barrier", BOOLEAN(disk_barrier, DISK_BARRIER) }, \ { "disk-flushes", BOOLEAN(disk_flushes, DISK_FLUSHES) }, \ { "disk-drain", BOOLEAN(disk_drain, DISK_DRAIN) }, \ { "md-flushes", BOOLEAN(md_flushes, MD_FLUSHES) }, \ { "resync-rate", NUMERIC(resync_rate, RESYNC_RATE), \ .unit = "bytes/second" }, \ { "resync-after", NUMERIC(resync_after, MINOR_NUMBER) }, \ { "al-extents", NUMERIC(al_extents, AL_EXTENTS) }, \ { "al-updates", BOOLEAN(al_updates, AL_UPDATES) }, \ { "discard-zeroes-if-aligned", \ BOOLEAN(discard_zeroes_if_aligned, DISCARD_ZEROES_IF_ALIGNED) }, \ { "c-plan-ahead", NUMERIC(c_plan_ahead, C_PLAN_AHEAD), \ .unit = "1/10 seconds" }, \ { "c-delay-target", NUMERIC(c_delay_target, C_DELAY_TARGET), \ .unit = "1/10 seconds" }, \ { "c-fill-target", NUMERIC(c_fill_target, C_FILL_TARGET), \ .unit = "bytes" }, \ { "c-max-rate", NUMERIC(c_max_rate, C_MAX_RATE), \ .unit = "bytes/second" }, \ { "c-min-rate", NUMERIC(c_min_rate, C_MIN_RATE), \ .unit = "bytes/second" }, \ { "disk-timeout", NUMERIC(disk_timeout, DISK_TIMEOUT), \ .unit = "1/10 seconds" }, \ { "read-balancing", ENUM(read_balancing, READ_BALANCING) }, \ { "rs-discard-granularity", \ NUMERIC(rs_discard_granularity, RS_DISCARD_GRANULARITY) } \ #define CHANGEABLE_NET_OPTIONS \ { "protocol", ENUM_NOCASE(wire_protocol, PROTOCOL) }, \ { "timeout", NUMERIC(timeout, TIMEOUT), \ .unit = "1/10 seconds" }, \ { "max-epoch-size", NUMERIC(max_epoch_size, MAX_EPOCH_SIZE) }, \ { "max-buffers", NUMERIC(max_buffers, MAX_BUFFERS) }, \ { "unplug-watermark", NUMERIC(unplug_watermark, UNPLUG_WATERMARK) }, \ { "connect-int", NUMERIC(connect_int, CONNECT_INT), \ .unit = "seconds" }, \ { "ping-int", NUMERIC(ping_int, PING_INT), \ .unit = "seconds" }, \ { "sndbuf-size", NUMERIC(sndbuf_size, SNDBUF_SIZE), \ .unit = "bytes" }, \ { "rcvbuf-size", NUMERIC(rcvbuf_size, RCVBUF_SIZE), \ .unit = "bytes" }, \ { "ko-count", NUMERIC(ko_count, KO_COUNT) }, \ { "allow-two-primaries", BOOLEAN(two_primaries, ALLOW_TWO_PRIMARIES) }, \ { "cram-hmac-alg", STRING(cram_hmac_alg) }, \ { "shared-secret", STRING(shared_secret) }, \ { "after-sb-0pri", ENUM(after_sb_0p, AFTER_SB_0P) }, \ { "after-sb-1pri", ENUM(after_sb_1p, AFTER_SB_1P) }, \ { "after-sb-2pri", ENUM(after_sb_2p, AFTER_SB_2P) }, \ { "always-asbp", BOOLEAN(always_asbp, ALWAYS_ASBP) }, \ { "rr-conflict", ENUM(rr_conflict, RR_CONFLICT) }, \ { "ping-timeout", NUMERIC(ping_timeo, PING_TIMEO), \ .unit = "1/10 seconds" }, \ { "data-integrity-alg", STRING(integrity_alg) }, \ { "tcp-cork", BOOLEAN(tcp_cork, TCP_CORK) }, \ { "on-congestion", ENUM(on_congestion, ON_CONGESTION) }, \ { "congestion-fill", NUMERIC(cong_fill, CONG_FILL), \ .unit = "bytes" }, \ { "congestion-extents", NUMERIC(cong_extents, CONG_EXTENTS) }, \ { "csums-alg", STRING(csums_alg) }, \ { "csums-after-crash-only", BOOLEAN(csums_after_crash_only, \ CSUMS_AFTER_CRASH_ONLY) }, \ { "verify-alg", STRING(verify_alg) }, \ { "use-rle", BOOLEAN(use_rle, USE_RLE) }, \ { "socket-check-timeout", NUMERIC(sock_check_timeo, SOCKET_CHECK_TIMEO) } struct context_def disk_options_ctx = { NLA_POLICY(disk_conf), .fields = { CHANGEABLE_DISK_OPTIONS, { } }, }; struct context_def net_options_ctx = { NLA_POLICY(net_conf), .fields = { CHANGEABLE_NET_OPTIONS, { } }, }; struct context_def primary_cmd_ctx = { NLA_POLICY(set_role_parms), .fields = { { "force", FLAG(assume_uptodate) }, { } }, }; struct context_def attach_cmd_ctx = { NLA_POLICY(disk_conf), .fields = { { "size", NUMERIC(disk_size, DISK_SIZE), .unit = "bytes" }, { "max-bio-bvecs", NUMERIC(max_bio_bvecs, MAX_BIO_BVECS) }, CHANGEABLE_DISK_OPTIONS, /* { "*", STRING(backing_dev) }, */ /* { "*", STRING(meta_dev) }, */ /* { "*", NUMERIC(meta_dev_idx, MINOR_NUMBER) }, */ { } }, }; struct context_def detach_cmd_ctx = { NLA_POLICY(detach_parms), .fields = { { "force", FLAG(force_detach) }, { } }, }; struct context_def connect_cmd_ctx = { NLA_POLICY(net_conf), .fields = { { "tentative", FLAG(tentative) }, { "discard-my-data", FLAG(discard_my_data) }, { "alternate-address", ADDRESS(my_addr2) }, { "alternate-peer-address", ADDRESS(peer_addr2) }, CHANGEABLE_NET_OPTIONS, { } }, }; struct context_def disconnect_cmd_ctx = { NLA_POLICY(disconnect_parms), .fields = { { "force", FLAG(force_disconnect) }, { } }, }; struct context_def resize_cmd_ctx = { NLA_POLICY(resize_parms), .fields = { { "size", NUMERIC(resize_size, DISK_SIZE), .unit = "bytes" }, { "assume-peer-has-space", FLAG(resize_force) }, { "assume-clean", FLAG(no_resync) }, { "al-stripes", NUMERIC(al_stripes, AL_STRIPES) }, { "al-stripe-size-kB", NUMERIC(al_stripe_size, AL_STRIPE_SIZE) }, { } }, }; struct context_def resource_options_cmd_ctx = { NLA_POLICY(res_opts), .fields = { { "cpu-mask", STRING(cpu_mask) }, { "on-no-data-accessible", ENUM(on_no_data, ON_NO_DATA) }, { } }, }; struct context_def new_current_uuid_cmd_ctx = { NLA_POLICY(new_c_uuid_parms), .fields = { { "clear-bitmap", FLAG(clear_bm) }, { } }, }; struct context_def verify_cmd_ctx = { NLA_POLICY(start_ov_parms), .fields = { { "start", NUMERIC(ov_start_sector, DISK_SIZE), .unit = "bytes" }, { "stop", NUMERIC(ov_stop_sector, DISK_SIZE), .unit = "bytes" }, { } }, }; struct context_def new_minor_cmd_ctx = { NLA_POLICY(drbd_cfg_context), .fields = { /* { "*", STRING(ctx_resource_name) }, */ /* { "*", NUMERIC(ctx_volume, >= 0) }, */ /* { "*", BINARY(ctx_my_addr) }, */ /* { "*", BINARY(ctx_peer_addr) }, */ { } }, }; drbd-utils-8.9.10/user/v84/drbdadm_main.c0000644000175000017500000027051613011114651017726 0ustar apoikosapoikos/* drbdadm_main.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2002-2008, LINBIT Information Technologies GmbH. Copyright (C) 2002-2008, Philipp Reisner . Copyright (C) 2002-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #define _XOPEN_SOURCE 600 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linux/drbd_limits.h" #include "drbdtool_common.h" #include "drbdadm.h" #include "registry.h" #include "config_flags.h" #include "shared_main.h" #define MAX_ARGS 40 static int indent = 0; #define INDENT_WIDTH 4 #define BFMT "%s;\n" #define IPV4FMT "%-16s %s %s:%s;\n" #define IPV6FMT "%-16s %s [%s]:%s;\n" #define MDISK "%-16s %s;\n" #define MDISKI "%-16s %s [%s];\n" #define printI(fmt, args... ) printf("%*s" fmt,INDENT_WIDTH * indent,"" , ## args ) #define printA(name, val ) \ printf("%*s%*s %3s;\n", \ INDENT_WIDTH * indent,"" , \ -24+INDENT_WIDTH * indent, \ name, val ) char *progname; struct adm_cmd { const char *name; int (*function) (struct cfg_ctx *); /* which level this command is for. * 0: don't show this command, ever * 1: normal administrative commands, shown in normal help * 2-4: shown on "drbdadm hidden-commands" * 2: useful for shell scripts * 3: callbacks potentially called from kernel module on certain events * 4: advanced, experts and developers only */ unsigned int show_in_usage:3; /* if set, command requires an explicit resource name */ unsigned int res_name_required:1; /* if set, command requires an explicit volume number as well */ unsigned int vol_id_required:1; /* most commands need to iterate over all volumes in the resource */ unsigned int iterate_volumes:1; /* error out if the ip specified is not available/active now */ unsigned int verify_ips:1; /* if set, use the "cache" in /var/lib/drbd to figure out * which config file to use. * This is necessary for handlers (callbacks from kernel) to work * when using "drbdadm -c /some/other/config/file" */ unsigned int use_cached_config_file:1; unsigned int need_peer:1; unsigned int is_proxy_cmd:1; unsigned int uc_dialog:1; /* May show usage count dialog */ unsigned int test_config:1; /* Allow -t option */ const struct context_def *drbdsetup_ctx; }; struct deferred_cmd { int (*function) (struct cfg_ctx *); struct cfg_ctx ctx; struct deferred_cmd *next; }; struct option general_admopt[] = { {"stacked", no_argument, 0, 'S'}, {"dry-run", no_argument, 0, 'd'}, {"verbose", no_argument, 0, 'v'}, {"config-file", required_argument, 0, 'c'}, {"config-to-test", required_argument, 0, 't'}, {"drbdsetup", required_argument, 0, 's'}, {"drbdmeta", required_argument, 0, 'm'}, {"drbd-proxy-ctl", required_argument, 0, 'p'}, {"sh-varname", required_argument, 0, 'n'}, {"peer", required_argument, 0, 'P'}, {"version", no_argument, 0, 'V'}, {"setup-option", required_argument, 0, 'W'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; struct option *admopt = general_admopt; extern void my_parse(); extern int yydebug; extern FILE *yyin; static int adm_generic_l(struct cfg_ctx *); static int adm_up(struct cfg_ctx *); static int adm_dump(struct cfg_ctx *); static int adm_dump_xml(struct cfg_ctx *); static int adm_wait_c(struct cfg_ctx *); static int adm_wait_ci(struct cfg_ctx *); static int adm_proxy_up(struct cfg_ctx *); static int adm_proxy_down(struct cfg_ctx *); static int sh_nop(struct cfg_ctx *); static int sh_resources(struct cfg_ctx *); static int sh_resource(struct cfg_ctx *); static int sh_mod_parms(struct cfg_ctx *); static int sh_dev(struct cfg_ctx *); static int sh_udev(struct cfg_ctx *); static int sh_minor(struct cfg_ctx *); static int sh_ip(struct cfg_ctx *); static int sh_lres(struct cfg_ctx *); static int sh_ll_dev(struct cfg_ctx *); static int sh_md_dev(struct cfg_ctx *); static int sh_md_idx(struct cfg_ctx *); static int sh_b_pri(struct cfg_ctx *); static int sh_status(struct cfg_ctx *); static int admm_generic(struct cfg_ctx *); static int adm_khelper(struct cfg_ctx *); static int adm_generic_b(struct cfg_ctx *); static int hidden_cmds(struct cfg_ctx *); static int adm_outdate(struct cfg_ctx *); static int adm_chk_resize(struct cfg_ctx *); static void dump_options(char *name, struct d_option *opts); struct d_volume *volume_by_vnr(struct d_volume *volumes, int vnr); struct d_resource *res_by_name(const char *name); int ctx_by_name(struct cfg_ctx *ctx, const char *id); int ctx_set_implicit_volume(struct cfg_ctx *ctx); static char *get_opt_val(struct d_option *, const char *, char *); char ss_buffer[1024]; struct utsname nodeinfo; int line = 1; int fline; char *config_file = NULL; char *config_save = NULL; char *config_test = NULL; struct d_resource *config = NULL; struct d_resource *common = NULL; struct ifreq *ifreq_list = NULL; int is_drbd_top; enum { NORMAL, STACKED, IGNORED, __N_RESOURCE_TYPES }; int nr_resources[__N_RESOURCE_TYPES]; int nr_volumes[__N_RESOURCE_TYPES]; int number_of_minors = 0; int config_from_stdin = 0; int config_valid = 1; int no_tty; int dry_run = 0; int verbose = 0; int adjust_with_progress = 0; bool help; int do_verify_ips = 0; int do_register = 1; /* whether drbdadm was called with "all" instead of resource name(s) */ int all_resources = 0; char *drbdsetup = NULL; char *drbdmeta = NULL; char *drbdadm_83 = NULL; char *drbd_proxy_ctl; char *sh_varname = NULL; struct setup_option *setup_options; char *connect_to_host = NULL; struct deferred_cmd *deferred_cmds[__CFG_LAST] = { NULL, }; struct deferred_cmd *deferred_cmds_tail[__CFG_LAST] = { NULL, }; void add_setup_option(bool explicit, char *option) { int n = 0; if (setup_options) { while (setup_options[n].option) n++; } setup_options = realloc(setup_options, (n + 2) * sizeof(*setup_options)); if (!setup_options) { /* ... */ } setup_options[n].explicit = explicit; setup_options[n].option = option; n++; setup_options[n].option = NULL; } int adm_adjust_wp(struct cfg_ctx *ctx) { if (!verbose && !dry_run) adjust_with_progress = 1; return adm_adjust(ctx); } /* DRBD adm_cmd flags shortcuts, * to avoid merge conflicts and unreadable diffs * when we add the next flag */ #define DRBD_acf1_default \ .show_in_usage = 1, \ .res_name_required = 1, \ .iterate_volumes = 1, \ .verify_ips = 0, \ .uc_dialog = 1, \ #define DRBD_acf1_resname \ .show_in_usage = 1, \ .res_name_required = 1, \ .uc_dialog = 1, \ #define DRBD_acf1_connect \ .show_in_usage = 1, \ .res_name_required = 1, \ .iterate_volumes = 0, \ .verify_ips = 1, \ .need_peer = 1, \ .uc_dialog = 1, \ #define DRBD_acf1_up \ .show_in_usage = 1, \ .res_name_required = 1, \ .iterate_volumes = 1, \ .verify_ips = 1, \ .need_peer = 1, \ .uc_dialog = 1, \ #define DRBD_acf1_defnet \ .show_in_usage = 1, \ .res_name_required = 1, \ .iterate_volumes = 1, \ .verify_ips = 1, \ .uc_dialog = 1, \ #define DRBD_acf3_handler \ .show_in_usage = 3, \ .res_name_required = 1, \ .iterate_volumes = 0, \ .vol_id_required = 1, \ .verify_ips = 0, \ .use_cached_config_file = 1, \ #define DRBD_acf3_res_handler \ .show_in_usage = 3, \ .res_name_required = 1, \ .iterate_volumes = 0, \ .vol_id_required = 0, \ .verify_ips = 0, \ .use_cached_config_file = 1, \ #define DRBD_acf4_advanced \ .show_in_usage = 4, \ .res_name_required = 1, \ .iterate_volumes = 1, \ .verify_ips = 0, \ .uc_dialog = 1, \ #define DRBD_acf4_advanced_need_vol \ .show_in_usage = 4, \ .res_name_required = 1, \ .iterate_volumes = 1, \ .vol_id_required = 1, \ .verify_ips = 0, \ .uc_dialog = 1, \ #define DRBD_acf1_dump \ .show_in_usage = 1, \ .res_name_required = 1, \ .verify_ips = 1, \ .uc_dialog = 1, \ .test_config = 1, \ #define DRBD_acf2_shell \ .show_in_usage = 2, \ .iterate_volumes = 1, \ .res_name_required = 1, \ .verify_ips = 0, \ #define DRBD_acf2_sh_resname \ .show_in_usage = 2, \ .iterate_volumes = 0, \ .res_name_required = 1, \ .verify_ips = 0, \ #define DRBD_acf2_proxy \ .show_in_usage = 2, \ .res_name_required = 1, \ .verify_ips = 0, \ .need_peer = 1, \ .is_proxy_cmd = 1, \ #define DRBD_acf2_hook \ .show_in_usage = 2, \ .res_name_required = 1, \ .verify_ips = 0, \ .use_cached_config_file = 1, \ #define DRBD_acf2_gen_shell \ .show_in_usage = 2, \ .res_name_required = 0, \ .verify_ips = 0, \ struct adm_cmd cmds[] = { /* name, function, flags * sort order: * - normal config commands, * - normal meta data manipulation * - sh-* * - handler * - advanced ***/ {"attach", adm_attach, DRBD_acf1_default .drbdsetup_ctx = &attach_cmd_ctx, }, {"disk-options", adm_disk_options, DRBD_acf1_default .drbdsetup_ctx = &disk_options_ctx, }, {"detach", adm_generic_l, DRBD_acf1_default .drbdsetup_ctx = &detach_cmd_ctx, }, {"connect", adm_connect, DRBD_acf1_connect .drbdsetup_ctx = &connect_cmd_ctx, }, {"net-options", adm_net_options, DRBD_acf1_connect .drbdsetup_ctx = &net_options_ctx, }, {"disconnect", adm_disconnect, DRBD_acf1_resname .drbdsetup_ctx = &disconnect_cmd_ctx, }, {"up", adm_up, DRBD_acf1_up}, {"resource-options", adm_res_options, DRBD_acf1_resname .drbdsetup_ctx = &resource_options_cmd_ctx, }, {"down", adm_generic_l, DRBD_acf1_resname}, {"primary", adm_generic_l, DRBD_acf1_default .drbdsetup_ctx = &primary_cmd_ctx, }, {"secondary", adm_generic_l, DRBD_acf1_default}, {"invalidate", adm_generic_b, DRBD_acf1_default}, {"invalidate-remote", adm_generic_l, DRBD_acf1_defnet}, {"outdate", adm_outdate, DRBD_acf1_default}, {"resize", adm_resize, DRBD_acf1_defnet}, {"verify", adm_generic_s, DRBD_acf1_defnet}, {"pause-sync", adm_generic_s, DRBD_acf1_defnet}, {"resume-sync", adm_generic_s, DRBD_acf1_defnet}, {"adjust", adm_adjust, DRBD_acf1_connect}, {"adjust-with-progress", adm_adjust_wp, DRBD_acf1_connect}, {"wait-connect", adm_wait_c, DRBD_acf1_defnet}, {"wait-con-int", adm_wait_ci, .show_in_usage = 1,.verify_ips = 1,}, {"role", adm_generic_s, DRBD_acf1_default}, {"cstate", adm_generic_s, DRBD_acf1_default}, {"dstate", adm_generic_b, DRBD_acf1_default}, {"dump", adm_dump, DRBD_acf1_dump}, {"dump-xml", adm_dump_xml, DRBD_acf1_dump}, {"create-md", adm_create_md, DRBD_acf1_default}, {"show-gi", adm_generic_b, DRBD_acf1_default}, {"get-gi", adm_generic_b, DRBD_acf1_default}, {"dump-md", admm_generic, DRBD_acf1_default}, {"wipe-md", admm_generic, DRBD_acf1_default}, {"apply-al", admm_generic, DRBD_acf1_default}, {"hidden-commands", hidden_cmds,.show_in_usage = 1,}, {"sh-nop", sh_nop, DRBD_acf2_gen_shell .uc_dialog = 1, .test_config = 1}, {"sh-resources", sh_resources, DRBD_acf2_gen_shell}, {"sh-resource", sh_resource, DRBD_acf2_sh_resname}, {"sh-mod-parms", sh_mod_parms, DRBD_acf2_gen_shell}, {"sh-dev", sh_dev, DRBD_acf2_shell}, {"sh-udev", sh_udev, .vol_id_required = 1, DRBD_acf2_hook}, {"sh-minor", sh_minor, DRBD_acf2_shell}, {"sh-ll-dev", sh_ll_dev, DRBD_acf2_shell}, {"sh-md-dev", sh_md_dev, DRBD_acf2_shell}, {"sh-md-idx", sh_md_idx, DRBD_acf2_shell}, {"sh-ip", sh_ip, DRBD_acf2_shell}, {"sh-lr-of", sh_lres, DRBD_acf2_shell}, {"sh-b-pri", sh_b_pri, DRBD_acf2_shell}, {"sh-status", sh_status, DRBD_acf2_gen_shell}, {"proxy-up", adm_proxy_up, DRBD_acf2_proxy}, {"proxy-down", adm_proxy_down, DRBD_acf2_proxy}, {"new-resource", adm_new_resource, DRBD_acf2_sh_resname}, {"sh-new-minor", adm_new_minor, DRBD_acf4_advanced}, {"new-minor", adm_new_minor, DRBD_acf4_advanced}, /* alias for sh-new-minor */ {"before-resync-target", adm_khelper, DRBD_acf3_handler}, {"after-resync-target", adm_khelper, DRBD_acf3_handler}, {"before-resync-source", adm_khelper, DRBD_acf3_handler}, {"pri-on-incon-degr", adm_khelper, DRBD_acf3_handler}, {"pri-lost-after-sb", adm_khelper, DRBD_acf3_handler}, {"fence-peer", adm_khelper, DRBD_acf3_res_handler}, {"unfence-peer", adm_khelper, DRBD_acf3_res_handler}, {"local-io-error", adm_khelper, DRBD_acf3_handler}, {"pri-lost", adm_khelper, DRBD_acf3_handler}, {"initial-split-brain", adm_khelper, DRBD_acf3_handler}, {"split-brain", adm_khelper, DRBD_acf3_handler}, {"out-of-sync", adm_khelper, DRBD_acf3_handler}, {"suspend-io", adm_generic_s, DRBD_acf4_advanced}, {"resume-io", adm_generic_s, DRBD_acf4_advanced}, {"set-gi", admm_generic, DRBD_acf4_advanced_need_vol}, {"new-current-uuid", adm_generic_s, DRBD_acf4_advanced_need_vol .drbdsetup_ctx = &new_current_uuid_cmd_ctx, }, {"check-resize", adm_chk_resize, DRBD_acf4_advanced}, }; void schedule_deferred_cmd(int (*function) (struct cfg_ctx *), struct cfg_ctx *ctx, const char *arg, enum drbd_cfg_stage stage) { struct deferred_cmd *d, *t; d = calloc(1, sizeof(struct deferred_cmd)); if (d == NULL) { perror("calloc"); exit(E_EXEC_ERROR); } d->function = function; d->ctx.res = ctx->res; d->ctx.vol = ctx->vol; d->ctx.arg = arg; /* first to come is head */ if (!deferred_cmds[stage]) deferred_cmds[stage] = d; /* link it in at tail */ t = deferred_cmds_tail[stage]; if (t) t->next = d; /* advance tail */ deferred_cmds_tail[stage] = d; } enum on_error { KEEP_RUNNING, EXIT_ON_FAIL }; int call_cmd_fn(int (*function) (struct cfg_ctx *), struct cfg_ctx *ctx, enum on_error on_error) { int rv; rv = function(ctx); if (rv >= 20) { if (on_error == EXIT_ON_FAIL) exit(rv); } return rv; } /* If ctx->vol is NULL, and cmd->iterate_volumes is set, * iterate over all volumes in ctx->res. * Else, just pass it on. * */ int call_cmd(struct adm_cmd *cmd, struct cfg_ctx *ctx, enum on_error on_error) { struct cfg_ctx tmp_ctx = *ctx; struct d_resource *res = ctx->res; struct d_volume *vol; int ret; if (!res->peer) set_peer_in_resource(res, cmd->need_peer); if (!cmd->iterate_volumes || ctx->vol != NULL) return call_cmd_fn(cmd->function, &tmp_ctx, on_error); for_each_volume(vol, res->me->volumes) { tmp_ctx.vol = vol; ret = call_cmd_fn(cmd->function, &tmp_ctx, on_error); /* FIXME: Do we want to keep running? * When? * How would we determine which return value to return? */ if (ret) return ret; } return 0; } static char *drbd_cfg_stage_string[] = { [CFG_PREREQ] = "create res", [CFG_RESOURCE] = "adjust res", [CFG_DISK_PREREQ] = "prepare disk", [CFG_DISK] = "adjust disk", [CFG_NET_PREREQ] = "prepare net", [CFG_NET] = "adjust net", }; int _run_deferred_cmds(enum drbd_cfg_stage stage) { struct d_resource *last_res = NULL; struct deferred_cmd *d = deferred_cmds[stage]; struct deferred_cmd *t; int r; int rv = 0; if (d && adjust_with_progress) { printf("\n%15s:", drbd_cfg_stage_string[stage]); fflush(stdout); } while (d) { if (d->ctx.res->skip_further_deferred_command) { if (adjust_with_progress) { if (d->ctx.res != last_res) printf(" [skipped:%s]", d->ctx.res->name); } else err("%s: %s %s: skipped due to earlier error\n", progname, d->ctx.arg, d->ctx.res->name); r = 0; } else { if (adjust_with_progress) { if (d->ctx.res != last_res) printf(" %s", d->ctx.res->name); } r = call_cmd_fn(d->function, &d->ctx, KEEP_RUNNING); if (r) { /* If something in the "prerequisite" stages failed, * there is no point in trying to continue. * However if we just failed to adjust some * options, or failed to attach, we still want * to adjust other options, or try to connect. */ if (stage == CFG_PREREQ || stage == CFG_DISK_PREREQ) d->ctx.res->skip_further_deferred_command = 1; if (adjust_with_progress) printf(":failed(%s:%u)", d->ctx.arg, r); } } last_res = d->ctx.res; t = d->next; free(d); d = t; if (r > rv) rv = r; } return rv; } int run_deferred_cmds(void) { enum drbd_cfg_stage stage; int r; int ret = 0; if (adjust_with_progress) printf("["); for (stage = CFG_PREREQ; stage < __CFG_LAST; stage++) { r = _run_deferred_cmds(stage); if (r) { if (!adjust_with_progress) return 1; /* FIXME r? */ ret = 1; } } if (adjust_with_progress) printf("\n]\n"); return ret; } /*** These functions are used to the print the config ***/ static void dump_options2(char *name, struct d_option *opts, void(*within)(void*), void *ctx) { if (!opts && !(within && ctx)) return; printI("%s {\n", name); ++indent; while (opts) { if (opts->value) printA(opts->name, opts->is_escaped ? opts->value : esc(opts-> value)); else printI(BFMT, opts->name); opts = opts->next; } if (within) within(ctx); --indent; printI("}\n"); } static void dump_options(char *name, struct d_option *opts) { dump_options2(name, opts, NULL, NULL); } void dump_proxy_plugins(void *ctx) { struct d_option *opt = ctx; dump_options("plugin", opt); } static void dump_global_info() { static const char * const yes_no_ask[] = { [UC_YES] = "yes", [UC_NO] = "no", [UC_ASK] = "ask", }; if (!global_options.minor_count && !global_options.disable_ip_verification && global_options.dialog_refresh == 1 && global_options.usage_count == UC_ASK && !verbose) return; printI("global {\n"); ++indent; if (global_options.disable_ip_verification) printI("disable-ip-verification;\n"); if (global_options.minor_count) printI("minor-count %i;\n", global_options.minor_count); if (global_options.dialog_refresh != 1) printI("dialog-refresh %i;\n", global_options.dialog_refresh); if (global_options.usage_count != UC_ASK) printI("usage-count %s;\n", yes_no_ask[global_options.usage_count]); if (global_options.cmd_timeout_short != CMD_TIMEOUT_SHORT_DEF) printI("cmd-timeout-short %u;\n", global_options.cmd_timeout_short); if (global_options.cmd_timeout_medium != CMD_TIMEOUT_MEDIUM_DEF) printI("cmd-timeout-medium %u;\n", global_options.cmd_timeout_medium); if (global_options.cmd_timeout_long != CMD_TIMEOUT_LONG_DEF) printI("cmd-timeout-long %u;\n", global_options.cmd_timeout_long); --indent; printI("}\n\n"); } static void fake_startup_options(struct d_resource *res); static void dump_common_info() { if (!common) return; printI("common {\n"); ++indent; fake_startup_options(common); dump_options("options", common->res_options); dump_options("net", common->net_options); dump_options("disk", common->disk_options); dump_options("startup", common->startup_options); dump_options2("proxy", common->proxy_options, dump_proxy_plugins, common->proxy_plugins); dump_options("handlers", common->handlers); --indent; printf("}\n\n"); } static void dump_address(char *name, char *addr, char *port, char *af) { if (!strcmp(af, "ipv6")) printI(IPV6FMT, name, af, addr, port); else printI(IPV4FMT, name, af, addr, port); } static void dump_proxy_info(struct d_proxy_info *pi) { printI("proxy on %s {\n", names_to_str(pi->on_hosts)); ++indent; dump_address("inside", pi->inside_addr, pi->inside_port, pi->inside_af); dump_address("outside", pi->outside_addr, pi->outside_port, pi->outside_af); dump_options2("options", pi->options, dump_proxy_plugins, pi->plugins); --indent; printI("}\n"); } static void dump_volume(int has_lower, struct d_volume *vol) { if (!vol->implicit) { printI("volume %d {\n", vol->vnr); ++indent; } /* Handle volume of '_remote_host' */ if (!vol->device && !vol->disk && !vol->meta_disk && !vol->meta_index) goto out; dump_options("disk", vol->disk_options); printI("device%*s", -19 + INDENT_WIDTH * indent, ""); if (vol->device) printf("%s ", esc(vol->device)); printf("minor %d;\n", vol->device_minor); if (!has_lower) printA("disk", esc(vol->disk)); if (!has_lower) { if (!strcmp(vol->meta_index, "flexible")) printI(MDISK, "meta-disk", esc(vol->meta_disk)); else if (!strcmp(vol->meta_index, "internal")) printA("meta-disk", "internal"); else printI(MDISKI, "meta-disk", esc(vol->meta_disk), vol->meta_index); } if (!vol->implicit) { out: --indent; printI("}\n"); } } static void dump_host_info(struct d_host_info *hi) { struct d_volume *vol; if (!hi) { printI(" # No host section data available.\n"); return; } if (hi->lower) { printI("stacked-on-top-of %s {\n", esc(hi->lower->name)); ++indent; printI("# on %s \n", names_to_str(hi->on_hosts)); } else if (hi->by_address) { if (!strcmp(hi->address_family, "ipv6")) printI("floating ipv6 [%s]:%s {\n", hi->address, hi->port); else printI("floating %s %s:%s {\n", hi->address_family, hi->address, hi->port); ++indent; } else { printI("on %s {\n", names_to_str(hi->on_hosts)); ++indent; } dump_options("options", hi->res_options); for_each_volume(vol, hi->volumes) dump_volume(!!hi->lower, vol); if (!hi->by_address) dump_address("address", hi->address, hi->port, hi->address_family); if (hi->alt_address) dump_address("alternate-link-address", hi->alt_address, hi->alt_port, hi->alt_address_family); if (hi->proxy) dump_proxy_info(hi->proxy); --indent; printI("}\n"); } static void dump_options_xml2(char *name, struct d_option *opts, void(*within)(void*), void *ctx) { if (!opts && !(within && ctx)) return; printI("
\n", name); ++indent; while (opts) { if (opts->value) printI("
\n"); } static void dump_options_xml(char *name, struct d_option *opts) { dump_options_xml2(name, opts, NULL, NULL); } void dump_proxy_plugins_xml(void *ctx) { struct d_option *opt = ctx; dump_options_xml("plugin", opt); } static void dump_global_info_xml() { if (!global_options.minor_count && !global_options.disable_ip_verification && global_options.dialog_refresh == 1) return; printI("\n"); ++indent; if (global_options.disable_ip_verification) printI("\n"); if (global_options.minor_count) printI("\n", global_options.minor_count); if (global_options.dialog_refresh != 1) printI("\n", global_options.dialog_refresh); --indent; printI("\n"); } static void dump_common_info_xml() { if (!common) return; printI("\n"); ++indent; fake_startup_options(common); dump_options_xml("options", common->res_options); dump_options_xml("net", common->net_options); dump_options_xml("disk", common->disk_options); dump_options_xml("startup", common->startup_options); dump_options_xml2("proxy", common->proxy_options, dump_proxy_plugins_xml, common->proxy_plugins); dump_options_xml("handlers", common->handlers); --indent; printI("\n"); } static void dump_proxy_info_xml(struct d_proxy_info *pi) { printI("\n", names_to_str(pi->on_hosts)); ++indent; printI("%s\n", pi->inside_af, pi->inside_port, pi->inside_addr); printI("%s\n", pi->outside_af, pi->outside_port, pi->outside_addr); dump_options_xml2("options", pi->options, dump_proxy_plugins_xml, pi->plugins); --indent; printI("\n"); } static void dump_volume_xml(struct d_volume *vol) { printI("\n", vol->vnr); ++indent; dump_options_xml("disk", vol->disk_options); printI("%s\n", vol->device_minor, esc_xml(vol->device)); printI("%s\n", esc_xml(vol->disk)); if (!strcmp(vol->meta_index, "flexible")) printI("%s\n", esc_xml(vol->meta_disk)); else if (!strcmp(vol->meta_index, "internal")) printI("internal\n"); else { printI("%s\n", vol->meta_index, esc_xml(vol->meta_disk)); } --indent; printI("\n"); } static void dump_host_info_xml(struct d_host_info *hi) { struct d_volume *vol; if (!hi) { printI("\n"); return; } if (hi->by_address) printI("\n"); else printI("\n", names_to_str(hi->on_hosts)); ++indent; dump_options_xml("options", hi->res_options); for_each_volume(vol, hi->volumes) dump_volume_xml(vol); printI("
%s
\n", hi->address_family, hi->port, hi->address); if (hi->proxy) dump_proxy_info_xml(hi->proxy); --indent; printI("
\n"); } static void fake_startup_options(struct d_resource *res) { struct d_option *opt; char *val; if (res->stacked_timeouts) { opt = new_opt(strdup("stacked-timeouts"), NULL); res->startup_options = APPEND(res->startup_options, opt); } if (res->become_primary_on) { val = strdup(names_to_str(res->become_primary_on)); opt = new_opt(strdup("become-primary-on"), val); opt->is_escaped = 1; res->startup_options = APPEND(res->startup_options, opt); } } static int adm_dump(struct cfg_ctx *ctx) { struct d_host_info *host; struct d_resource *res = ctx->res; if (!res) { printI("# no resources configured\n"); return 0; } printI("# resource %s on %s: %s, %s\n", esc(res->name), nodeinfo.nodename, res->ignore ? "ignored" : "not ignored", res->stacked ? "stacked" : "not stacked"); printI("# defined at %s:%u\n", res->config_file, res->start_line); printI("resource %s {\n", esc(res->name)); ++indent; for (host = res->all_hosts; host; host = host->next) dump_host_info(host); fake_startup_options(res); dump_options("options", res->res_options); dump_options("net", res->net_options); dump_options("disk", res->disk_options); dump_options("startup", res->startup_options); dump_options2("proxy", res->proxy_options, dump_proxy_plugins, res->proxy_plugins); dump_options("handlers", res->handlers); --indent; printf("}\n\n"); return 0; } static int adm_dump_xml(struct cfg_ctx *ctx) { struct d_host_info *host; struct d_resource *res = ctx->res; if (!res) { printI("\n"); return 0; } printI("\n", esc_xml(res->name), esc_xml(res->config_file), res->start_line); ++indent; // else if (common && common->protocol) printA("# common protocol", common->protocol); for (host = res->all_hosts; host; host = host->next) dump_host_info_xml(host); fake_startup_options(res); dump_options_xml("options", res->res_options); dump_options_xml("net", res->net_options); dump_options_xml("disk", res->disk_options); dump_options_xml("startup", res->startup_options); dump_options_xml2("proxy", res->proxy_options, dump_proxy_plugins_xml, res->proxy_plugins); dump_options_xml("handlers", res->handlers); --indent; printI("\n"); return 0; } static int sh_nop(struct cfg_ctx *ctx) { return 0; } static int sh_resources(struct cfg_ctx *ctx) { struct d_resource *res, *t; int first = 1; for_each_resource(res, t, config) { if (res->ignore) continue; if (is_drbd_top != res->stacked) continue; printf(first ? "%s" : " %s", esc(res->name)); first = 0; } if (!first) printf("\n"); return 0; } static int sh_resource(struct cfg_ctx *ctx) { printf("%s\n", ctx->res->name); return 0; } static int sh_dev(struct cfg_ctx *ctx) { printf("%s\n", ctx->vol->device); return 0; } static int sh_udev(struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; struct d_volume *vol = ctx->vol; /* No shell escape necessary. Udev does not handle it anyways... */ if (!vol) { err("volume not specified\n"); return 1; } if (vol->implicit) printf("RESOURCE=%s\n", res->name); else printf("RESOURCE=%s/%u\n", res->name, vol->vnr); if (!strncmp(vol->device, "/dev/drbd", 9)) printf("DEVICE=%s\n", vol->device + 5); else printf("DEVICE=drbd%u\n", vol->device_minor); if (!strncmp(vol->disk, "/dev/", 5)) printf("DISK=%s\n", vol->disk + 5); else printf("DISK=%s\n", vol->disk); return 0; } static int sh_minor(struct cfg_ctx *ctx) { printf("%d\n", ctx->vol->device_minor); return 0; } static int sh_ip(struct cfg_ctx *ctx) { printf("%s\n", ctx->res->me->address); return 0; } static int sh_lres(struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; if (!is_drbd_top) { err("sh-lower-resource only available in stacked mode\n"); exit(E_USAGE); } if (!res->stacked) { err("'%s' is not stacked on this host (%s)\n", res->name, nodeinfo.nodename); exit(E_USAGE); } printf("%s\n", res->me->lower->name); return 0; } static int sh_ll_dev(struct cfg_ctx *ctx) { printf("%s\n", ctx->vol->disk); return 0; } static int sh_md_dev(struct cfg_ctx *ctx) { struct d_volume *vol = ctx->vol; char *r; if (strcmp("internal", vol->meta_disk) == 0) r = vol->disk; else r = vol->meta_disk; printf("%s\n", r); return 0; } static int sh_md_idx(struct cfg_ctx *ctx) { printf("%s\n", ctx->vol->meta_index); return 0; } static int sh_b_pri(struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; int i, rv; if (name_in_names(nodeinfo.nodename, res->become_primary_on) || name_in_names("both", res->become_primary_on)) { /* upon connect resync starts, and both sides become primary at the same time. One's try might be declined since an other state transition happens. Retry. */ for (i = 0; i < 5; i++) { const char *old_arg = ctx->arg; ctx->arg = "primary"; rv = adm_generic_s(ctx); ctx->arg = old_arg; if (rv == 0) return rv; sleep(1); } return rv; } return 0; } /* FIXME this module parameter will go */ static int sh_mod_parms(struct cfg_ctx *ctx) { int mc = global_options.minor_count; if (mc == 0) { mc = number_of_minors + 3; if (mc > DRBD_MINOR_COUNT_MAX) mc = DRBD_MINOR_COUNT_MAX; if (mc < DRBD_MINOR_COUNT_DEF) mc = DRBD_MINOR_COUNT_DEF; } printf("minor_count=%d\n", mc); return 0; } static void free_volume(struct d_volume *vol) { if (!vol) return; free(vol->device); free(vol->disk); free(vol->meta_disk); free(vol->meta_index); free(vol); } static void free_host_info(struct d_host_info *hi) { struct d_volume *vol; if (!hi) return; free_names(hi->on_hosts); while ((vol = hi->volumes)) { hi->volumes = vol->next; free_volume(vol); } free(hi->address); free(hi->address_family); free(hi->port); free(hi); } static void free_options(struct d_option *opts) { struct d_option *f; while (opts) { free(opts->name); free(opts->value); f = opts; opts = opts->next; free(f); } } static void free_config(struct d_resource *res) { struct d_resource *f; struct d_host_info *host; while ((f = res)) { res = f->next; free(f->name); free_volume(f->volumes); while ((host = f->all_hosts)) { f->all_hosts = host->next; free_host_info(host); } free_options(f->net_options); free_options(f->disk_options); free_options(f->startup_options); free_options(f->proxy_options); free_options(f->handlers); free(f); } if (common) { free_options(common->net_options); free_options(common->disk_options); free_options(common->startup_options); free_options(common->proxy_options); free_options(common->handlers); free(common); } if (ifreq_list) free(ifreq_list); } static void expand_opts(struct d_option *co, struct d_option **opts) { struct d_option *no; while (co) { if (!find_opt(*opts, co->name)) { // prepend new item to opts no = new_opt(strdup(co->name), co->value ? strdup(co->value) : NULL); no->next = *opts; *opts = no; } co = co->next; } } static void expand_common(void) { struct d_resource *res, *tmp; struct d_volume *vol, *host_vol; struct d_host_info *h; /* make sure vol->device is non-NULL */ for_each_resource(res, tmp, config) { for (h = res->all_hosts; h; h = h->next) { for_each_volume(vol, h->volumes) { if (!vol->device) m_asprintf(&vol->device, "/dev/drbd%u", vol->device_minor); } } } for_each_resource(res, tmp, config) { if (!common) break; expand_opts(common->net_options, &res->net_options); expand_opts(common->disk_options, &res->disk_options); expand_opts(common->startup_options, &res->startup_options); expand_opts(common->proxy_options, &res->proxy_options); expand_opts(common->handlers, &res->handlers); expand_opts(common->res_options, &res->res_options); if (common->stacked_timeouts) res->stacked_timeouts = 1; if (!res->become_primary_on) res->become_primary_on = common->become_primary_on; if (common->proxy_plugins && !res->proxy_plugins) expand_opts(common->proxy_plugins, &res->proxy_plugins); } /* now that common disk options (if any) have been propagated to the * resource level, further propagate them to the volume level. */ for_each_resource(res, tmp, config) { for (h = res->all_hosts; h; h = h->next) { for_each_volume(vol, h->volumes) { expand_opts(res->disk_options, &vol->disk_options); } if (h->proxy) { expand_opts(res->proxy_options, &h->proxy->options); expand_opts(res->proxy_plugins, &h->proxy->plugins); } } } /* now from all volume/disk-options on resource level to host level */ for_each_resource(res, tmp, config) { for_each_volume(vol, res->volumes) { for (h = res->all_hosts; h; h = h->next) { host_vol = volume_by_vnr(h->volumes, vol->vnr); expand_opts(vol->disk_options, &host_vol->disk_options); } } } } static void find_drbdcmd(char **cmd, char **pathes) { char **path; path = pathes; while (*path) { if (access(*path, X_OK) == 0) { *cmd = *path; return; } path++; } err("Can not find command (drbdsetup/drbdmeta)\n"); exit(E_EXEC_ERROR); } #define NA(ARGC) \ ({ if((ARGC) >= MAX_ARGS) { err("MAX_ARGS too small\n"); \ exit(E_THINKO); \ } \ (ARGC)++; \ }) static void add_setup_options(char **argv, int *argcp) { int argc = *argcp; int i; if (!setup_options) return; for (i = 0; setup_options[i].option; i++) argv[NA(argc)] = setup_options[i].option; *argcp = argc; } #define make_options(OPT) \ while(OPT) { \ if(OPT->value) { \ ssprintf(argv[NA(argc)],"--%s=%s",OPT->name,OPT->value); \ } else { \ ssprintf(argv[NA(argc)],"--%s",OPT->name); \ } \ OPT=OPT->next; \ } /* FIXME: Don't leak the memory allocated by asprintf. */ #define make_address(ADDR, PORT, AF) \ if (!strcmp(AF, "ipv6")) { \ m_asprintf(&argv[NA(argc)], "%s:[%s]:%s", AF, ADDR, PORT); \ } else { \ m_asprintf(&argv[NA(argc)], "%s:%s:%s", AF, ADDR, PORT); \ } static int adm_attach_or_disk_options(struct cfg_ctx *ctx, bool do_attach, bool reset) { struct d_volume *vol = ctx->vol; char *argv[MAX_ARGS]; struct d_option *opt; int argc = 0; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = do_attach ? "attach" : "disk-options"; ssprintf(argv[NA(argc)], "%d", vol->device_minor); if (do_attach) { argv[NA(argc)] = vol->disk; if (!strcmp(vol->meta_disk, "internal")) { argv[NA(argc)] = vol->disk; } else { argv[NA(argc)] = vol->meta_disk; } argv[NA(argc)] = vol->meta_index; } if (reset) argv[NA(argc)] = "--set-defaults"; if (reset || do_attach) { opt = ctx->vol->disk_options; if (!do_attach) { while (opt && opt->adj_skip) opt = opt->next; } make_options(opt); } add_setup_options(argv, &argc); argv[NA(argc)] = 0; return m_system_ex(argv, SLEEPS_LONG, ctx->res->name); } int adm_attach(struct cfg_ctx *ctx) { int rv; ctx->arg = "apply-al"; rv = admm_generic(ctx); if (rv) return rv; ctx->arg = "attach"; return adm_attach_or_disk_options(ctx, true, false); } int adm_disk_options(struct cfg_ctx *ctx) { return adm_attach_or_disk_options(ctx, false, false); } int adm_set_default_disk_options(struct cfg_ctx *ctx) { return adm_attach_or_disk_options(ctx, false, true); } struct d_option *find_opt(struct d_option *base, char *name) { while (base) { if (!strcmp(base->name, name)) { return base; } base = base->next; } return 0; } int adm_new_minor(struct cfg_ctx *ctx) { char *argv[MAX_ARGS]; int argc = 0, ex; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = "new-minor"; ssprintf(argv[NA(argc)], "%s", ctx->res->name); ssprintf(argv[NA(argc)], "%u", ctx->vol->device_minor); ssprintf(argv[NA(argc)], "%u", ctx->vol->vnr); argv[NA(argc)] = NULL; ex = m_system_ex(argv, SLEEPS_SHORT, ctx->res->name); if (!ex && do_register) register_minor(ctx->vol->device_minor, config_save); return ex; } static int adm_new_resource_or_res_options(struct cfg_ctx *ctx, bool do_new_resource, bool reset) { char *argv[MAX_ARGS]; int argc = 0, ex; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = do_new_resource ? "new-resource" : "resource-options"; ssprintf(argv[NA(argc)], "%s", ctx->res->name); if (reset) argv[NA(argc)] = "--set-defaults"; if (reset || do_new_resource) make_options(ctx->res->res_options); add_setup_options(argv, &argc); argv[NA(argc)] = NULL; ex = m_system_ex(argv, SLEEPS_SHORT, ctx->res->name); if (!ex && do_new_resource && do_register) register_resource(ctx->res->name, config_save); return ex; } int adm_new_resource(struct cfg_ctx *ctx) { return adm_new_resource_or_res_options(ctx, true, false); } int adm_res_options(struct cfg_ctx *ctx) { return adm_new_resource_or_res_options(ctx, false, false); } int adm_set_default_res_options(struct cfg_ctx *ctx) { return adm_new_resource_or_res_options(ctx, false, true); } int adm_resize(struct cfg_ctx *ctx) { char *argv[MAX_ARGS]; struct d_option *opt; int argc = 0; int silent; int ex; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = "resize"; ssprintf(argv[NA(argc)], "%d", ctx->vol->device_minor); opt = find_opt(ctx->vol->disk_options, "size"); if (!opt) opt = find_opt(ctx->res->disk_options, "size"); if (opt) ssprintf(argv[NA(argc)], "--%s=%s", opt->name, opt->value); add_setup_options(argv, &argc); argv[NA(argc)] = 0; /* if this is not "resize", but "check-resize", be silent! */ silent = !strcmp(ctx->arg, "check-resize") ? SUPRESS_STDERR : 0; ex = m_system_ex(argv, SLEEPS_SHORT | silent, ctx->res->name); if (ex) return ex; /* Record last-known bdev info. * Unfortunately drbdsetup did not have enough information * when doing the "resize", and in theory, _our_ information * about the backing device may even be wrong. * Call drbdsetup again, tell it to ask the kernel for * current config, and update the last known bdev info * according to that. */ /* argv[0] = drbdsetup; */ argv[1] = "check-resize"; /* argv[2] = minor; */ argv[3] = NULL; /* ignore exit code */ m_system_ex(argv, SLEEPS_SHORT | silent, ctx->res->name); return 0; } int _admm_generic(struct cfg_ctx *ctx, int flags) { struct d_volume *vol = ctx->vol; char *argv[MAX_ARGS]; int argc = 0; argv[NA(argc)] = drbdmeta; ssprintf(argv[NA(argc)], "%d", vol->device_minor); argv[NA(argc)] = "v08"; if (!strcmp(vol->meta_disk, "internal")) { argv[NA(argc)] = vol->disk; } else { argv[NA(argc)] = vol->meta_disk; } if (!strcmp(vol->meta_index, "flexible")) { if (!strcmp(vol->meta_disk, "internal")) { argv[NA(argc)] = "flex-internal"; } else { argv[NA(argc)] = "flex-external"; } } else { argv[NA(argc)] = vol->meta_index; } argv[NA(argc)] = (char *)ctx->arg; add_setup_options(argv, &argc); argv[NA(argc)] = 0; return m_system_ex(argv, flags, ctx->res->name); } static int admm_generic(struct cfg_ctx *ctx) { return _admm_generic(ctx, SLEEPS_VERY_LONG); } static void _adm_generic(struct cfg_ctx *ctx, int flags, pid_t *pid, int *fd, int *ex) { char *argv[MAX_ARGS]; int argc = 0; if (!ctx->res) { /* ASSERT */ err("sorry, need at least a resource name to call drbdsetup\n"); abort(); } argv[NA(argc)] = drbdsetup; argv[NA(argc)] = (char *)ctx->arg; if (ctx->vol) ssprintf(argv[NA(argc)], "%d", ctx->vol->device_minor); else ssprintf(argv[NA(argc)], "%s", ctx->res->name); add_setup_options(argv, &argc); argv[NA(argc)] = 0; setenv("DRBD_RESOURCE", ctx->res->name, 1); m__system(argv, flags, ctx->res->name, pid, fd, ex); } static int adm_generic(struct cfg_ctx *ctx, int flags) { int ex; _adm_generic(ctx, flags, NULL, NULL, &ex); return ex; } int adm_generic_s(struct cfg_ctx *ctx) { return adm_generic(ctx, SLEEPS_SHORT); } int sh_status(struct cfg_ctx *ctx) { struct d_resource *r, *t; struct d_volume *vol, *lower_vol; int rv = 0; if (!dry_run) { printf("_drbd_version=%s\n_drbd_api=%u\n", shell_escape(PACKAGE_VERSION), API_VERSION); printf("_config_file=%s\n\n\n", shell_escape(config_save)); } for_each_resource(r, t, config) { if (r->ignore) continue; ctx->res = r; printf("_conf_res_name=%s\n", shell_escape(r->name)); printf("_conf_file_line=%s:%u\n\n", shell_escape(r->config_file), r->start_line); if (r->stacked && r->me->lower) { printf("_stacked_on=%s\n", shell_escape(r->me->lower->name)); lower_vol = r->me->lower->me->volumes; } else { /* reset stuff */ printf("_stacked_on=\n"); printf("_stacked_on_device=\n"); printf("_stacked_on_minor=\n"); lower_vol = NULL; } /* TODO: remove this loop, have drbdsetup use dump * and optionally filter on resource name. * "stacked" information is not directly known to drbdsetup, though. */ for_each_volume(vol, r->me->volumes) { /* do not continue in this loop, * or lower_vol will get out of sync */ if (lower_vol) { printf("_stacked_on_device=%s\n", shell_escape(lower_vol->device)); printf("_stacked_on_minor=%d\n", lower_vol->device_minor); } else if (r->stacked && r->me->lower) { /* ASSERT */ err("in %s: stacked volume[%u] without lower volume\n", r->name, vol->vnr); abort(); } printf("_conf_volume=%d\n", vol->vnr); ctx->vol = vol; rv = adm_generic(ctx, SLEEPS_SHORT); if (rv) return rv; if (lower_vol) lower_vol = lower_vol->next; /* vol is advanced by for_each_volume */ } } return 0; } int adm_generic_l(struct cfg_ctx *ctx) { return adm_generic(ctx, SLEEPS_LONG); } static int adm_outdate(struct cfg_ctx *ctx) { int rv; rv = adm_generic(ctx, SLEEPS_SHORT | SUPRESS_STDERR); /* special cases for outdate: * 17: drbdsetup outdate, but is primary and thus cannot be outdated. * 5: drbdsetup outdate, and is inconsistent or worse anyways. */ if (rv == 17) return rv; if (rv == 5) { /* That might mean it is diskless. */ rv = admm_generic(ctx); if (rv) rv = 5; return rv; } if (rv || dry_run) { rv = admm_generic(ctx); } return rv; } /* shell equivalent: * ( drbdsetup resize && drbdsetup check-resize ) || drbdmeta check-resize */ static int adm_chk_resize(struct cfg_ctx *ctx) { /* drbdsetup resize && drbdsetup check-resize */ int ex = adm_resize(ctx); if (ex == 0) return 0; /* try drbdmeta check-resize */ return admm_generic(ctx); } static int adm_generic_b(struct cfg_ctx *ctx) { char buffer[4096]; int fd, status, rv = 0, rr, s = 0; pid_t pid; _adm_generic(ctx, SLEEPS_SHORT | RETURN_STDERR_FD, &pid, &fd, NULL); if (!dry_run) { if (fd < 0) { err("Strange: got negative fd.\n"); exit(E_THINKO); } while (1) { rr = read(fd, buffer + s, 4096 - s); if (rr <= 0) break; s += rr; } close(fd); rr = waitpid(pid, &status, 0); alarm(0); if (WIFEXITED(status)) rv = WEXITSTATUS(status); if (alarm_raised) { rv = 0x100; } } /* see drbdsetup.c, print_config_error(): * 11: some unspecific state change error * 17: SS_NO_UP_TO_DATE_DISK * In both cases, we don't need to retry with drbdmeta, * it would fail anyways with "Device is configured!" */ if (rv == 11 || rv == 17) { /* Some state transition error, report it ... */ rr = write(fileno(stderr), buffer, s); return rv; } if (rv || dry_run) { /* On other errors rv = 10 .. no minor allocated rv = 20 .. module not loaded rv = 16 .. we are diskless here retry with drbdmeta. */ rv = admm_generic(ctx); } return rv; } static int adm_khelper(struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; struct d_volume *vol = ctx->vol; int rv = 0; char *sh_cmd; char minor_string[8]; char volume_string[8]; char *argv[] = { "/bin/sh", "-c", NULL, NULL }; if (!res->peer) { /* Since 8.3.2 we get DRBD_PEER_AF and DRBD_PEER_ADDRESS from the kernel. If we do not know the peer by now, use these to find the peer. */ struct d_host_info *host; char *peer_address = getenv("DRBD_PEER_ADDRESS"); char *peer_af = getenv("DRBD_PEER_AF"); if (peer_address && peer_af) { for (host = res->all_hosts; host; host = host->next) { if (!strcmp(host->address_family, peer_af) && !strcmp(host->address, peer_address)) { res->peer = host; break; } } } } if (res->peer) { setenv("DRBD_PEER_AF", res->peer->address_family, 1); /* since 8.3.0 */ setenv("DRBD_PEER_ADDRESS", res->peer->address, 1); /* since 8.3.0 */ setenv("DRBD_PEER", res->peer->on_hosts->name, 1); /* deprecated */ setenv("DRBD_PEERS", names_to_str(res->peer->on_hosts), 1); /* since 8.3.0, but not usable when using a config with "floating" statements. */ } if (vol) { snprintf(minor_string, sizeof(minor_string), "%u", vol->device_minor); snprintf(volume_string, sizeof(volume_string), "%u", vol->vnr); setenv("DRBD_MINOR", minor_string, 1); setenv("DRBD_VOLUME", volume_string, 1); setenv("DRBD_LL_DISK", vol->disk, 1); } else { char *minor_list; char *separator = ""; char *pos; int volumes = 0; int bufsize; int n; for_each_volume(vol, res->me->volumes) volumes++; /* max minor number is 2**20 - 1, which is 7 decimal digits. * plus separator respective trailing zero. */ bufsize = volumes * 8 + 1; minor_list = alloca(bufsize); pos = minor_list; for_each_volume(vol, res->me->volumes) { n = snprintf(pos, bufsize, "%s%d", separator, vol->device_minor); if (n >= bufsize) { /* "can not happen" */ err("buffer too small when generating the minor list\n"); abort(); break; } bufsize -= n; pos += n; separator = " "; } setenv("DRBD_MINOR", minor_list, 1); } setenv("DRBD_RESOURCE", res->name, 1); setenv("DRBD_CONF", config_save, 1); if ((sh_cmd = get_opt_val(res->handlers, ctx->arg, NULL))) { argv[2] = sh_cmd; rv = m_system_ex(argv, SLEEPS_VERY_LONG, res->name); } return rv; } // need to convert discard-node-nodename to discard-local or discard-remote. void convert_discard_opt(struct d_resource *res) { struct d_option *opt; if (res == NULL) return; if ((opt = find_opt(res->net_options, "after-sb-0pri"))) { if (!strncmp(opt->value, "discard-node-", 13)) { if (!strcmp(nodeinfo.nodename, opt->value + 13)) { free(opt->value); opt->value = strdup("discard-local"); } else { free(opt->value); opt->value = strdup("discard-remote"); } } } } static int add_connection_endpoints(char **argv, int *argcp, struct d_resource *res) { int argc = *argcp; make_address(res->me->address, res->me->port, res->me->address_family); if (res->me->proxy) { make_address(res->me->proxy->inside_addr, res->me->proxy->inside_port, res->me->proxy->inside_af); } else if (res->peer) { make_address(res->peer->address, res->peer->port, res->peer->address_family); } else if (dry_run) { argv[NA(argc)] = "N/A"; } else { err("resource %s: cannot configure network without knowing my peer.\n", res->name); return 20; } *argcp = argc; return 0; } static int adm_connect_or_net_options(struct cfg_ctx *ctx, bool do_connect, bool reset) { struct d_resource *res = ctx->res; char *argv[MAX_ARGS]; struct d_option *opt; int argc = 0; int ret; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = do_connect ? "connect" : "net-options"; if (do_connect) ssprintf(argv[NA(argc)], "%s", res->name); ret = add_connection_endpoints(argv, &argc, res); if (ret) return ret; if (do_connect && res->me->alt_address) { ssprintf(argv[NA(argc)], "--alternate-address"); make_address(res->me->alt_address, res->me->alt_port, res->me->alt_address_family); ssprintf(argv[NA(argc)], "--alternate-peer-address"); make_address(res->peer->alt_address, res->peer->alt_port, res->peer->alt_address_family); } if (reset) argv[NA(argc)] = "--set-defaults"; if (reset || do_connect) { opt = res->net_options; make_options(opt); } add_setup_options(argv, &argc); argv[NA(argc)] = 0; return m_system_ex(argv, SLEEPS_SHORT, res->name); } int adm_connect(struct cfg_ctx *ctx) { return adm_connect_or_net_options(ctx, true, false); } int adm_net_options(struct cfg_ctx *ctx) { return adm_connect_or_net_options(ctx, false, false); } int adm_set_default_net_options(struct cfg_ctx *ctx) { return adm_connect_or_net_options(ctx, false, true); } int adm_disconnect(struct cfg_ctx *ctx) { char *argv[MAX_ARGS]; int argc = 0; if (!ctx->res) { /* ASSERT */ err("sorry, need at least a resource name to call drbdsetup\n"); abort(); } argv[NA(argc)] = drbdsetup; argv[NA(argc)] = (char *)ctx->arg; add_connection_endpoints(argv, &argc, ctx->res); add_setup_options(argv, &argc); argv[NA(argc)] = 0; setenv("DRBD_RESOURCE", ctx->res->name, 1); return m_system_ex(argv, SLEEPS_SHORT, ctx->res->name); } struct d_option *del_opt(struct d_option *base, struct d_option *item) { struct d_option *i; if (base == item) { base = item->next; free(item->name); free(item->value); free(item); return base; } for (i = base; i; i = i->next) { if (i->next == item) { i->next = item->next; free(item->name); free(item->value); free(item); return base; } } return base; } // Need to convert after from resourcename to minor_number. void _convert_after_option(struct d_resource *res, struct d_volume *vol) { struct d_option *opt, *next; struct cfg_ctx depends_on_ctx = { }; int volumes; if (res == NULL) return; opt = vol->disk_options; while ((opt = find_opt(opt, "resync-after"))) { next = opt->next; ctx_by_name(&depends_on_ctx, opt->value); volumes = ctx_set_implicit_volume(&depends_on_ctx); if (volumes > 1) { err("%s:%d: in resource %s:\n\t" "resync-after contains '%s', which is ambiguous, since it contains %d volumes\n", res->config_file, res->start_line, res->name, opt->value, volumes); config_valid = 0; return; } if (!depends_on_ctx.res || depends_on_ctx.res->ignore) { vol->disk_options = del_opt(vol->disk_options, opt); } else { free(opt->value); m_asprintf(&opt->value, "%d", depends_on_ctx.vol->device_minor); } opt = next; } } // Need to convert after from resourcename/volume to minor_number. void convert_after_option(struct d_resource *res) { struct d_volume *vol; struct d_host_info *h; for (h = res->all_hosts; h; h = h->next) for_each_volume(vol, h->volumes) _convert_after_option(res, vol); } int _proxy_connect_name_len(struct d_resource *res) { return strlen(res->name) + strlen(names_to_str_c(res->peer->proxy->on_hosts, '_')) + strlen(names_to_str_c(res->me->proxy->on_hosts, '_')) + 3 /* for the two dashes and the trailing 0 character */; } char *_proxy_connection_name(char *conn_name, struct d_resource *res) { sprintf(conn_name, "%s-%s-%s", res->name, names_to_str_c(res->peer->proxy->on_hosts, '_'), names_to_str_c(res->me->proxy->on_hosts, '_')); return conn_name; } int do_proxy_conn_up(struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; char *argv[4] = { drbd_proxy_ctl, "-c", NULL, NULL }; char *conn_name; int rv; conn_name = proxy_connection_name(res); ssprintf(argv[2], "add connection %s %s:%s %s:%s %s:%s %s:%s", conn_name, res->me->proxy->inside_addr, res->me->proxy->inside_port, res->peer->proxy->outside_addr, res->peer->proxy->outside_port, res->me->proxy->outside_addr, res->me->proxy->outside_port, res->me->address, res->me->port); rv = m_system_ex(argv, SLEEPS_SHORT, res->name); return rv; } int do_proxy_conn_plugins(struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; char *argv[MAX_ARGS]; char *conn_name; int argc = 0; struct d_option *opt; int counter; conn_name = proxy_connection_name(res); argc = 0; argv[NA(argc)] = drbd_proxy_ctl; opt = res->me->proxy->options; while (opt) { argv[NA(argc)] = "-c"; ssprintf(argv[NA(argc)], "set %s %s %s", opt->name, conn_name, opt->value); opt = opt->next; } counter = 0; opt = res->me->proxy->plugins; /* Don't send the "set plugin ... END" line if no plugins are defined * - that's incompatible with the drbd proxy version 1. */ if (opt) { while (1) { argv[NA(argc)] = "-c"; ssprintf(argv[NA(argc)], "set plugin %s %d %s", conn_name, counter, opt ? opt->name : "END"); if (!opt) break; opt = opt->next; counter ++; } } argv[NA(argc)] = 0; if (argc > 2) return m_system_ex(argv, SLEEPS_SHORT, res->name); return 0; } int do_proxy_conn_down(struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; char *conn_name; char *argv[4] = { drbd_proxy_ctl, "-c", NULL, NULL}; int rv; conn_name = proxy_connection_name(res); ssprintf(argv[2], "del connection %s", conn_name); rv = m_system_ex(argv, SLEEPS_SHORT, res->name); return rv; } static int check_proxy(struct cfg_ctx *ctx, int do_up) { struct d_resource *res = ctx->res; int rv; if (!res->me->proxy) { if (all_resources) return 0; err("There is no proxy config for host %s in resource %s.\n", nodeinfo.nodename, res->name); exit(E_CONFIG_INVALID); } if (!name_in_names(nodeinfo.nodename, res->me->proxy->on_hosts)) { if (all_resources) return 0; err("The proxy config in resource %s is not for %s.\n", res->name, nodeinfo.nodename); exit(E_CONFIG_INVALID); } if (!res->peer) { err("Cannot determine the peer in resource %s.\n", res->name); exit(E_CONFIG_INVALID); } if (!res->peer->proxy) { err("There is no proxy config for the peer in resource %s.\n", res->name); if (all_resources) return 0; exit(E_CONFIG_INVALID); } if (do_up) { rv = do_proxy_conn_up(ctx); if (!rv) rv = do_proxy_conn_plugins(ctx); } else rv = do_proxy_conn_down(ctx); return rv; } static int adm_proxy_up(struct cfg_ctx *ctx) { return check_proxy(ctx, 1); } static int adm_proxy_down(struct cfg_ctx *ctx) { return check_proxy(ctx, 0); } /* The "main" loop iterates over resources. * This "sorts" the drbdsetup commands to bring those up * so we will later first create all objects, * then attach all local disks, * adjust various settings, * and then configure the network part */ static int adm_up(struct cfg_ctx *ctx) { static char *current_res_name; if (!current_res_name || strcmp(current_res_name, ctx->res->name)) { free(current_res_name); current_res_name = strdup(ctx->res->name); schedule_deferred_cmd(adm_new_resource, ctx, "new-resource", CFG_PREREQ); schedule_deferred_cmd(adm_connect, ctx, "connect", CFG_NET); } schedule_deferred_cmd(adm_new_minor, ctx, "new-minor", CFG_PREREQ); schedule_deferred_cmd(adm_attach, ctx, "attach", CFG_DISK); return 0; } /* The stacked-timeouts switch in the startup sections allows us to enforce the use of the specified timeouts instead the use of a sane value. Should only be used if the third node should never become primary. */ static int adm_wait_c(struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; struct d_volume *vol = ctx->vol; char *argv[MAX_ARGS]; struct d_option *opt; int argc = 0, rv; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = "wait-connect"; ssprintf(argv[NA(argc)], "%d", vol->device_minor); if (is_drbd_top && !res->stacked_timeouts) { unsigned long timeout = 20; if ((opt = find_opt(res->net_options, "connect-int"))) { timeout = strtoul(opt->value, NULL, 10); // one connect-interval? two? timeout *= 2; } argv[argc++] = "--wfc-timeout"; ssprintf(argv[argc], "%lu", timeout); argc++; } else { opt = res->startup_options; make_options(opt); } argv[NA(argc)] = 0; rv = m_system_ex(argv, SLEEPS_FOREVER, res->name); return rv; } int ctx_by_minor(struct cfg_ctx *ctx, const char *id) { struct d_resource *res, *t; struct d_volume *vol; unsigned int mm; mm = minor_by_id(id); if (mm == -1U) return -ENOENT; for_each_resource(res, t, config) { if (res->ignore) continue; for_each_volume(vol, res->me->volumes) { if (mm == vol->device_minor) { is_drbd_top = res->stacked; ctx->res = res; ctx->vol = vol; return 0; } } } return -ENOENT; } struct d_resource *res_by_name(const char *name) { struct d_resource *res, *t; for_each_resource(res, t, config) { if (strcmp(name, res->name) == 0) return res; } return NULL; } struct d_volume *volume_by_vnr(struct d_volume *volumes, int vnr) { struct d_volume *vol; for_each_volume(vol, volumes) if (vnr == vol->vnr) return vol; return NULL; } int ctx_by_name(struct cfg_ctx *ctx, const char *id) { struct d_resource *res, *t; struct d_volume *vol; char *name = strdupa(id); char *vol_id = strchr(name, '/'); unsigned vol_nr = ~0U; if (vol_id) { *vol_id++ = '\0'; vol_nr = m_strtoll(vol_id, 0); } for_each_resource(res, t, config) { if (res->ignore) continue; if (strcmp(name, res->name) == 0) break; } if (!res) return -ENOENT; if (!vol_id) { /* We could assign implicit volumes here. * But that broke "drbdadm up specific-resource". */ ctx->res = res; ctx->vol = NULL; return 0; } vol = volume_by_vnr(res->me->volumes, vol_nr); if (vol) { ctx->res = res; ctx->vol = vol; return 0; } return -ENOENT; } int ctx_set_implicit_volume(struct cfg_ctx *ctx) { struct d_volume *vol, *v = NULL; int volumes = 0; if (ctx->vol || !ctx->res) return 0; if (!ctx->res->me) { return 0; } for_each_volume(vol, ctx->res->me->volumes) { volumes++; v = vol; } if (volumes == 1) ctx->vol = v; return volumes; } /* In case a child exited, or exits, its return code is stored as negative number in the pids[i] array */ static int childs_running(pid_t * pids, int opts) { int i = 0, wr, rv = 0, status; int N = nr_volumes[is_drbd_top ? STACKED : NORMAL]; for (i = 0; i < N; i++) { if (pids[i] <= 0) continue; wr = waitpid(pids[i], &status, opts); if (wr == -1) { // Wait error. if (errno == ECHILD) { printf("No exit code for %d\n", pids[i]); pids[i] = 0; // Child exited before ? continue; } perror("waitpid"); exit(E_EXEC_ERROR); } if (wr == 0) rv = 1; // Child still running. if (wr > 0) { pids[i] = 0; if (WIFEXITED(status)) pids[i] = -WEXITSTATUS(status); if (WIFSIGNALED(status)) pids[i] = -1000; } } return rv; } static void kill_childs(pid_t * pids) { int i; int N = nr_volumes[is_drbd_top ? STACKED : NORMAL]; for (i = 0; i < N; i++) { if (pids[i] <= 0) continue; kill(pids[i], SIGINT); } } /* returns: -1 ... all childs terminated 0 ... timeout expired 1 ... a string was read */ int gets_timeout(pid_t * pids, char *s, int size, int timeout) { int pr, rr, n = 0; struct pollfd pfd; if (s) { pfd.fd = fileno(stdin); pfd.events = POLLIN | POLLHUP | POLLERR | POLLNVAL; n = 1; } redo_without_fd: if (!childs_running(pids, WNOHANG)) { pr = -1; goto out; } do { pr = poll(&pfd, n, timeout); if (pr == -1) { // Poll error. if (errno == EINTR) { if (childs_running(pids, WNOHANG)) continue; goto out; // pr = -1 here. } perror("poll"); exit(E_EXEC_ERROR); } } while (pr == -1); if (pr == 1) { // Input available. rr = read(fileno(stdin), s, size - 1); if (rr == -1) { perror("read"); exit(E_EXEC_ERROR); } else if (size > 1 && rr == 0) { /* WTF. End-of-file... avoid busy loop. */ s[0] = 0; n = 0; goto redo_without_fd; } s[rr] = 0; } out: return pr; } static char *get_opt_val(struct d_option *base, const char *name, char *def) { while (base) { if (!strcmp(base->name, name)) { return base->value; } base = base->next; } return def; } static int check_exit_codes(pid_t * pids) { struct d_resource *res, *t; int i = 0, rv = 0; for_each_resource(res, t, config) { if (res->ignore) continue; if (is_drbd_top != res->stacked) continue; if (pids[i] == -5 || pids[i] == -1000) { pids[i] = 0; } if (pids[i] == -20) rv = 20; i++; } return rv; } static int adm_wait_ci(struct cfg_ctx *ctx) { struct d_resource *res, *t; char *argv[20], answer[40]; pid_t *pids; struct d_option *opt; int rr, wtime, argc, i = 0; time_t start; int saved_stdin, saved_stdout, fd; int N; struct sigaction so, sa; int have_tty = 1; saved_stdin = -1; saved_stdout = -1; if (no_tty) { err("WARN: stdin/stdout is not a TTY; using /dev/console"); fprintf(stdout, "WARN: stdin/stdout is not a TTY; using /dev/console"); saved_stdin = dup(fileno(stdin)); if (saved_stdin == -1) perror("dup(stdin)"); saved_stdout = dup(fileno(stdout)); if (saved_stdin == -1) perror("dup(stdout)"); fd = open("/dev/console", O_RDONLY); if (fd == -1) { perror("open('/dev/console, O_RDONLY)"); have_tty = 0; } else { dup2(fd, fileno(stdin)); fd = open("/dev/console", O_WRONLY); if (fd == -1) perror("open('/dev/console, O_WRONLY)"); dup2(fd, fileno(stdout)); } } sa.sa_handler = chld_sig_hand; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &sa, &so); N = nr_volumes[is_drbd_top ? STACKED : NORMAL]; pids = alloca(N * sizeof(pid_t)); /* alloca can not fail, it can "only" overflow the stack :) * but it needs to be initialized anyways! */ memset(pids, 0, N * sizeof(pid_t)); for_each_resource(res, t, config) { struct d_volume *vol; if (res->ignore) continue; if (is_drbd_top != res->stacked) continue; for_each_volume(vol, res->me->volumes) { /* ctx is not used */ argc = 0; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = "wait-connect"; ssprintf(argv[NA(argc)], "%u", vol->device_minor); opt = res->startup_options; make_options(opt); argv[NA(argc)] = 0; m__system(argv, RETURN_PID, res->name, &pids[i++], NULL, NULL); } } wtime = global_options.dialog_refresh ? : -1; start = time(0); for (i = 0; i < 10; i++) { // no string, but timeout rr = gets_timeout(pids, 0, 0, 1 * 1000); if (rr < 0) break; putchar('.'); fflush(stdout); check_exit_codes(pids); } if (rr == 0) { /* track a "yes", as well as ctrl-d and ctrl-c, * in case our tty is stuck in "raw" mode, and * we get it one character a time (-icanon) */ char yes_string[] = "yes\n"; char *yes_expect = yes_string; int ctrl_c_count = 0; int ctrl_d_count = 0; /* Just in case, if plymouth or usplash is running, * tell them to step aside. * Also try to force canonical tty mode. */ printf ("\n***************************************************************\n" " DRBD's startup script waits for the peer node(s) to appear.\n" " - If this node was already a degraded cluster before the\n" " reboot, the timeout is %s seconds. [degr-wfc-timeout]\n" " - If the peer was available before the reboot, the timeout\n" " is %s seconds. [wfc-timeout]\n" " (These values are for resource '%s'; 0 sec -> wait forever)\n", get_opt_val(config->startup_options, "degr-wfc-timeout", "0"), get_opt_val(config->startup_options, "wfc-timeout", "0"), config->name); if (!have_tty) { printf(" To abort waiting for DRBD connections, kill this process: kill %u\n", getpid()); fflush(stdout); /* wait untill killed, or all drbdsetup children have finished. */ do { rr = poll(NULL, 0, -1); if (rr == -1) { if (errno == EINTR) { if (childs_running(pids, WNOHANG)) continue; break; } perror("poll"); exit(E_EXEC_ERROR); } } while (rr != -1); kill_childs(pids); childs_running(pids, 0); check_exit_codes(pids); return 0; } if (system("exec > /dev/null 2>&1; plymouth quit ; usplash_write QUIT ; " "stty echo icanon icrnl")) /* Ignore return value. Cannot do anything about it anyways. */; printf(" To abort waiting enter 'yes' [ -- ]: "); do { printf("\e[s\e[31G[%4d]:\e[u", (int)(time(0) - start)); // Redraw sec. fflush(stdout); rr = gets_timeout(pids, answer, 40, wtime * 1000); check_exit_codes(pids); if (rr != 1) continue; /* If our tty is in "sane" or "canonical" mode, * we get whole lines. * If it still is in "raw" mode, even though we * tried to set ICANON above, possibly some other * "boot splash thingy" is in operation. * We may be lucky to get single characters. * If a sysadmin sees things stuck during boot, * I expect that ctrl-c or ctrl-d will be one * of the first things that are tried. * In raw mode, we get these characters directly. * But I want them to try that three times ;) */ if (answer[0] && answer[1] == 0) { if (answer[0] == '\3') ++ctrl_c_count; if (answer[0] == '\4') ++ctrl_d_count; if (yes_expect && answer[0] == *yes_expect) ++yes_expect; else if (answer[0] == '\n') yes_expect = yes_string; else yes_expect = NULL; } if (!strcmp(answer, "yes\n") || (yes_expect && *yes_expect == '\0') || ctrl_c_count >= 3 || ctrl_d_count >= 3) { kill_childs(pids); childs_running(pids, 0); check_exit_codes(pids); break; } printf(" To abort waiting enter 'yes' [ -- ]:"); } while (rr != -1); printf("\n"); } if (saved_stdin != -1) { dup2(saved_stdin, fileno(stdin)); dup2(saved_stdout, fileno(stdout)); } return 0; } static void print_cmds(int level) { size_t i; int j = 0; for (i = 0; i < ARRAY_SIZE(cmds); i++) { if (cmds[i].show_in_usage != level) continue; if (j++ % 2) { printf("%-35s\n", cmds[i].name); } else { printf(" %-35s", cmds[i].name); } } if (j % 2) printf("\n"); } static int hidden_cmds(struct cfg_ctx *ignored __attribute((unused))) { printf("\nThese additional commands might be useful for writing\n" "nifty shell scripts around drbdadm:\n\n"); print_cmds(2); printf("\nThese commands are used by the kernel part of DRBD to\n" "invoke user mode helper programs:\n\n"); print_cmds(3); printf ("\nThese commands ought to be used by experts and developers:\n\n"); print_cmds(4); printf("\n"); exit(0); } static void field_to_option(const struct field_def *field, struct option *option) { option->name = field->name; option->has_arg = field->argument_is_optional ? optional_argument : required_argument; option->flag = NULL; option->val = 257; } static void print_option(struct option *opt) { if (opt->has_arg == required_argument) { printf(" --%s=...", opt->name); if (opt->val > 1 && opt->val < 256) printf(", -%c ...", opt->val); printf("\n"); } else if (opt->has_arg == optional_argument) { printf(" --%s[=...]", opt->name); if (opt->val > 1 && opt->val < 256) printf(", -%c...", opt->val); printf("\n"); } else { printf(" --%s", opt->name); if (opt->val > 1 && opt->val < 256) printf(", -%c", opt->val); printf("\n"); } } void print_usage_and_exit(struct adm_cmd *cmd, const char *addinfo, int status) { struct option *opt; printf("\nUSAGE: %s %s [OPTION...] {all|RESOURCE...}\n\n" "GENERAL OPTIONS:\n", progname, cmd ? cmd->name : "COMMAND"); for (opt = general_admopt; opt->name; opt++) print_option(opt); if (cmd && cmd->drbdsetup_ctx) { const struct field_def *field; printf("\nOPTIONS FOR %s:\n", cmd->name); for (field = cmd->drbdsetup_ctx->fields; field->name; field++) { struct option opt; field_to_option(field, &opt); print_option(&opt); } } if (!cmd) { printf("\nCOMMANDS:\n"); print_cmds(1); } printf("\nVersion: " PACKAGE_VERSION " (api:%d)\n%s\n", API_VERSION, drbd_buildtag()); if (addinfo) printf("\n%s\n", addinfo); exit(status); } void verify_ips(struct d_resource *res) { if (global_options.disable_ip_verification) return; if (dry_run == 1 || do_verify_ips == 0) return; if (res->ignore) return; if (res->stacked && !is_drbd_top) return; if (!have_ip(res->me->address_family, res->me->address)) { ENTRY e, *ep; e.key = e.data = ep = NULL; m_asprintf(&e.key, "%s:%s", res->me->address, res->me->port); hsearch_r(e, FIND, &ep, &global_htable); err("%s: in resource %s, on %s:\n\t" "IP %s not found on this host.\n", ep ? (char *)ep->data : res->config_file, res->name, names_to_str(res->me->on_hosts), res->me->address); if (INVALID_IP_IS_INVALID_CONF) config_valid = 0; } } static char *conf_file[] = { DRBD_CONFIG_DIR "/drbd-84.conf", DRBD_CONFIG_DIR "/drbd-83.conf", DRBD_CONFIG_DIR "/drbd-82.conf", DRBD_CONFIG_DIR "/drbd-08.conf", DRBD_CONFIG_DIR "/drbd.conf", 0 }; int sanity_check_abs_cmd(char *cmd_name) { struct stat sb; if (stat(cmd_name, &sb)) { /* If stat fails, just ignore this sanity check, * we are still iterating over $PATH probably. */ return 0; } if (!(sb.st_mode & S_ISUID) || sb.st_mode & S_IXOTH || sb.st_gid == 0) { static int did_header = 0; if (!did_header) err( "WARN:\n" " You are using the 'drbd-peer-outdater' as fence-peer program.\n" " If you use that mechanism the dopd heartbeat plugin program needs\n" " to be able to call drbdsetup and drbdmeta with root privileges.\n\n" " You need to fix this with these commands:\n"); did_header = 1; err( " chgrp haclient %s\n" " chmod o-x %s\n" " chmod u+s %s\n\n", cmd_name, cmd_name, cmd_name); } return 1; } void sanity_check_cmd(char *cmd_name) { char *path, *pp, *c; char abs_path[100]; if (strchr(cmd_name, '/')) { sanity_check_abs_cmd(cmd_name); } else { path = pp = c = strdup(getenv("PATH")); while (1) { c = strchr(pp, ':'); if (c) *c = 0; snprintf(abs_path, 100, "%s/%s", pp, cmd_name); if (sanity_check_abs_cmd(abs_path)) break; if (!c) break; c++; if (!*c) break; pp = c; } free(path); } } /* if the config file is not readable by haclient, * dopd cannot work. * NOTE: we assume that any gid != 0 will be the group dopd will run as, * typically haclient. */ void sanity_check_conf(char *c) { struct stat sb; /* if we cannot stat the config file, * we have other things to worry about. */ if (stat(c, &sb)) return; /* permissions are funny: if it is world readable, * but not group readable, and it belongs to my group, * I am denied access. * For the file to be readable by dopd (hacluster:haclient), * it is not enough to be world readable. */ /* ok if world readable, and NOT group haclient (see NOTE above) */ if (sb.st_mode & S_IROTH && sb.st_gid == 0) return; /* ok if group readable, and group haclient (see NOTE above) */ if (sb.st_mode & S_IRGRP && sb.st_gid != 0) return; err( "WARN:\n" " You are using the 'drbd-peer-outdater' as fence-peer program.\n" " If you use that mechanism the dopd heartbeat plugin program needs\n" " to be able to read the drbd.config file.\n\n" " You need to fix this with these commands:\n" " chgrp haclient %s\n" " chmod g+r %s\n\n", c, c); } void sanity_check_perm() { static int checked = 0; if (checked) return; sanity_check_cmd(drbdsetup); sanity_check_cmd(drbdmeta); sanity_check_conf(config_file); checked = 1; } void validate_resource(struct d_resource *res, enum pp_flags flags) { struct d_option *opt, *next; struct d_name *bpo; /* there may be more than one "resync-after" statement, * see commit 89cd0585 */ opt = res->disk_options; while ((opt = find_opt(opt, "resync-after"))) { struct d_resource *rs_after_res = res_by_name(opt->value); next = opt->next; if (rs_after_res == NULL || (rs_after_res->ignore && !(flags & MATCH_ON_PROXY))) { err( "%s:%d: in resource %s:\n\tresource '%s' mentioned in " "'resync-after' option is not known%s.\n", res->config_file, res->start_line, res->name, opt->value, rs_after_res ? " on this host" : ""); /* Non-fatal if run from some script. * When deleting resources, it is an easily made * oversight to leave references to the deleted * resources in resync-after statements. Don't fail on * every pacemaker-induced action, as it would * ultimately lead to all nodes committing suicide. */ if (no_tty) res->disk_options = del_opt(res->disk_options, opt); else config_valid = 0; } opt = next; } if (res->ignore) return; if (!res->me) { err( "%s:%d: in resource %s:\n\tmissing section 'on %s { ... }'.\n", res->config_file, res->start_line, res->name, nodeinfo.nodename); config_valid = 0; } // need to verify that in the discard-node-nodename options only known // nodenames are mentioned. if ((opt = find_opt(res->net_options, "after-sb-0pri"))) { if (!strncmp(opt->value, "discard-node-", 13)) { if (res->peer && !name_in_names(opt->value + 13, res->peer->on_hosts) && !name_in_names(opt->value + 13, res->me->on_hosts)) { err( "%s:%d: in resource %s:\n\t" "the nodename in the '%s' option is " "not known.\n\t" "valid nodenames are: '%s %s'.\n", res->config_file, res->start_line, res->name, opt->value, names_to_str(res->me->on_hosts), names_to_str(res->peer->on_hosts)); config_valid = 0; } } } if ((opt = find_opt(res->handlers, "fence-peer"))) { if (strstr(opt->value, "drbd-peer-outdater")) sanity_check_perm(); } opt = find_opt(res->net_options, "allow-two-primaries"); if (name_in_names("both", res->become_primary_on) && opt == NULL) { err( "%s:%d: in resource %s:\n" "become-primary-on is set to both, but allow-two-primaries " "is not set.\n", res->config_file, res->start_line, res->name); config_valid = 0; } if (!res->peer) set_peer_in_resource(res, 0); if (res->peer && ((res->me->proxy == NULL) != (res->peer->proxy == NULL))) { err( "%s:%d: in resource %s:\n\t" "Either both 'on' sections must contain a proxy subsection, or none.\n", res->config_file, res->start_line, res->name); config_valid = 0; } for (bpo = res->become_primary_on; bpo; bpo = bpo->next) { if (res->peer && !name_in_names(bpo->name, res->me->on_hosts) && !name_in_names(bpo->name, res->peer->on_hosts) && strcmp(bpo->name, "both")) { err( "%s:%d: in resource %s:\n\t" "become-primary-on contains '%s', which is not named with the 'on' sections.\n", res->config_file, res->start_line, res->name, bpo->name); config_valid = 0; } } } static void global_validate_maybe_expand_die_if_invalid(int expand, enum pp_flags flags) { struct d_resource *res, *tmp; for_each_resource(res, tmp, config) { validate_resource(res, flags); if (!config_valid) exit(E_CONFIG_INVALID); if (expand) { convert_after_option(res); convert_discard_opt(res); } if (!config_valid) exit(E_CONFIG_INVALID); } } /* * returns a pointer to an malloced area that contains * an absolute, canonical, version of path. * aborts if any allocation or syscall fails. * return value should be free()d, once no longer needed. */ char *canonify_path(char *path) { int cwd_fd = -1; char *last_slash; char *tmp; char *that_wd; char *abs_path; if (!path || !path[0]) { err("cannot canonify an empty path\n"); exit(E_USAGE); } tmp = strdupa(path); last_slash = strrchr(tmp, '/'); if (last_slash) { *last_slash++ = '\0'; cwd_fd = open(".", O_RDONLY | O_CLOEXEC); if (cwd_fd < 0) { err("open(\".\") failed: %m\n"); exit(E_USAGE); } if (chdir(tmp)) { err("chdir(\"%s\") failed: %m\n", tmp); exit(E_USAGE); } } else { last_slash = tmp; } that_wd = getcwd(NULL, 0); if (!that_wd) { err("getcwd() failed: %m\n"); exit(E_USAGE); } if (!strcmp("/", that_wd)) m_asprintf(&abs_path, "/%s", last_slash); else m_asprintf(&abs_path, "%s/%s", that_wd, last_slash); free(that_wd); if (cwd_fd >= 0) { if (fchdir(cwd_fd) < 0) { err("fchdir() failed: %m\n"); exit(E_USAGE); } close(cwd_fd); } return abs_path; } void assign_command_names_from_argv0(char **argv) { struct cmd_helper { char *name; char **var; }; static struct cmd_helper helpers[] = { {"drbdsetup-84", &drbdsetup}, {"drbdmeta", &drbdmeta}, {"drbd-proxy-ctl", &drbd_proxy_ctl}, {"drbdadm-83", &drbdadm_83}, {NULL, NULL} }; struct cmd_helper *c; /* in case drbdadm is called with an absolute or relative pathname * look for the drbdsetup binary in the same location, * otherwise, just let execvp sort it out... */ if ((progname = strrchr(argv[0], '/')) == NULL) { progname = argv[0]; for (c = helpers; c->name; ++c) *(c->var) = strdup(c->name); } else { size_t len_dir, l; ++progname; len_dir = progname - argv[0]; for (c = helpers; c->name; ++c) { l = len_dir + strlen(c->name) + 1; *(c->var) = malloc(l); if (*(c->var)) { strncpy(*(c->var), argv[0], len_dir); strcpy(*(c->var) + len_dir, c->name); if (access(*(c->var), X_OK)) strcpy(*(c->var), c->name); /* see add_lib_drbd_to_path() */ } } /* for pretty printing, truncate to basename */ argv[0] = progname; } } static void recognize_all_drbdsetup_options(void) { int i; for (i = 0; i < ARRAY_SIZE(cmds); i++) { const struct adm_cmd *cmd = &cmds[i]; const struct field_def *field; if (!cmd->drbdsetup_ctx) continue; for (field = cmd->drbdsetup_ctx->fields; field->name; field++) { struct option opt; int n; field_to_option(field, &opt); for (n = 0; admopt[n].name; n++) { if (!strcmp(admopt[n].name, field->name)) { if (admopt[n].val == 257) assert (admopt[n].has_arg == opt.has_arg); else { err("Warning: drbdsetup %s option --%s " "can only be passed as -W--%s\n", cmd->name, admopt[n].name, admopt[n].name); goto skip; } } } if (admopt == general_admopt) { admopt = malloc((n + 2) * sizeof(*admopt)); memcpy(admopt, general_admopt, (n + 1) * sizeof(*admopt)); } else admopt = realloc(admopt, (n + 2) * sizeof(*admopt)); memcpy(&admopt[n+1], &admopt[n], sizeof(*admopt)); admopt[n] = opt; skip: /* dummy statement required because of label */ ; } } } struct adm_cmd *find_cmd(char *cmdname); int parse_options(int argc, char **argv, struct adm_cmd **cmd, char ***resource_names) { const char *optstring = make_optstring(admopt); int longindex, first_arg_index; int i; *cmd = NULL; *resource_names = malloc(sizeof(char *)); (*resource_names)[0] = NULL; opterr = 1; optind = 0; while (1) { int c; c = getopt_long(argc, argv, optstring, admopt, &longindex); if (c == -1) break; switch (c) { case 257: /* drbdsetup option */ { struct option *option = &admopt[longindex]; char *opt; int len; len = strlen(option->name) + 2; if (optarg) len += 1 + strlen(optarg); opt = malloc(len + 1); if (optarg) sprintf(opt, "--%s=%s", option->name, optarg); else sprintf(opt, "--%s", option->name); add_setup_option(false, opt); } break; case 'S': is_drbd_top = 1; break; case 'v': verbose++; break; case 'd': dry_run++; break; case 'c': if (!strcmp(optarg, "-")) { yyin = stdin; if (asprintf(&config_file, "STDIN") < 0) { err("asprintf(config_file): %m\n"); return 20; } config_from_stdin = 1; } else { yyin = fopen(optarg, "r"); if (!yyin) { err("Can not open '%s'.\n.", optarg); exit(E_EXEC_ERROR); } if (asprintf(&config_file, "%s", optarg) < 0) { err("asprintf(config_file): %m\n"); return 20; } } break; case 't': config_test = optarg; break; case 's': { char *pathes[2]; pathes[0] = optarg; pathes[1] = 0; find_drbdcmd(&drbdsetup, pathes); } break; case 'm': { char *pathes[2]; pathes[0] = optarg; pathes[1] = 0; find_drbdcmd(&drbdmeta, pathes); } break; case 'p': { char *pathes[2]; pathes[0] = optarg; pathes[1] = 0; find_drbdcmd(&drbd_proxy_ctl, pathes); } break; case 'n': { char *c; int shell_var_name_ok = 1; for (c = optarg; *c && shell_var_name_ok; c++) { switch (*c) { case 'a'...'z': case 'A'...'Z': case '0'...'9': case '_': break; default: shell_var_name_ok = 0; } } if (shell_var_name_ok) sh_varname = optarg; else err("ignored --sh-varname=%s: " "contains suspect characters, allowed set is [a-zA-Z0-9_]\n", optarg); } break; case 'V': printf("DRBDADM_BUILDTAG=%s\n", shell_escape(drbd_buildtag())); printf("DRBDADM_API_VERSION=%u\n", API_VERSION); printf("DRBD_KERNEL_VERSION_CODE=0x%06x\n", version_code_kernel()); printf("DRBDADM_VERSION_CODE=0x%06x\n", version_code_userland()); printf("DRBDADM_VERSION=%s\n", shell_escape(PACKAGE_VERSION)); exit(0); break; case 'P': connect_to_host = optarg; break; case 'W': add_setup_option(true, optarg); break; case 'h': help = true; break; case '?': goto help; } } first_arg_index = optind; for (; optind < argc; optind++) { optarg = argv[optind]; if (*cmd) { int n; ensure_sanity_of_res_name(optarg); for (n = 0; (*resource_names)[n]; n++) /* do nothing */ ; *resource_names = realloc(*resource_names, (n + 2) * sizeof(char *)); (*resource_names)[n++] = optarg; (*resource_names)[n] = NULL; } else if (!strcmp(optarg, "help")) help = true; else { *cmd = find_cmd(optarg); if (!*cmd) { /* Passing drbdsetup options like this is discouraged! */ add_setup_option(true, optarg); } } } if (help) print_usage_and_exit(*cmd, NULL, 0); if (*cmd == NULL) { if (first_arg_index < argc) { err("%s: Unknown command '%s'\n", progname, argv[first_arg_index]); return E_USAGE; } print_usage_and_exit(*cmd, "No command specified", E_USAGE); } if (setup_options) { /* * The drbdsetup options are command specific. Make sure that only * setup options that this command recognizes are used. */ for (i = 0; setup_options[i].option; i++) { const struct field_def *field; const char *option; int len; if (setup_options[i].explicit) continue; option = setup_options[i].option; for (len = 0; option[len]; len++) if (option[len] == '=') break; field = NULL; if (option[0] == '-' && option[1] == '-' && (*cmd)->drbdsetup_ctx) { for (field = (*cmd)->drbdsetup_ctx->fields; field->name; field++) { if (strlen(field->name) == len - 2 && !strncmp(option + 2, field->name, len - 2)) break; } if (!field->name) field = NULL; } if (!field) { err("%s: unrecognized option '%.*s'\n", progname, len, option); goto help; } } } return 0; help: if (*cmd) err("try '%s help %s'\n", progname, (*cmd)->name); else err("try '%s help'\n", progname); return E_USAGE; } struct adm_cmd *find_cmd(char *cmdname) { struct adm_cmd *cmd = NULL; unsigned int i; if (!strcmp("hidden-commands", cmdname)) { // before parsing the configuration file... hidden_cmds(NULL); exit(0); } /* R_PRIMARY / R_SECONDARY is not a state, but a role. Whatever that * means, actually. But anyways, we decided to start using _role_ as * the terminus of choice, and deprecate "state". */ substitute_deprecated_cmd(&cmdname, "state", "role"); /* "outdate-peer" got renamed to fence-peer, * it is not required to actually outdate the peer, * depending on situation it may be sufficient to power-reset it * or do some other fencing action, or even call out to "meatware". * The name of the handler should not imply something that is not done. */ substitute_deprecated_cmd(&cmdname, "outdate-peer", "fence-peer"); for (i = 0; i < ARRAY_SIZE(cmds); i++) { if (!strcmp(cmds[i].name, cmdname)) { cmd = cmds + i; break; } } return cmd; } char *config_file_from_arg(char *arg) { char *f; int minor = minor_by_id(arg); if (minor >= 0) { f = lookup_minor(minor); if (!f) { err("Don't know which config file belongs to minor %d, trying default ones...\n", minor); return NULL; } } else { f = lookup_resource(arg); if (!f) { err("Don't know which config file belongs to resource %s, trying default ones...\n", arg); return NULL; } } yyin = fopen(f, "r"); if (yyin == NULL) { err("Couldn't open file %s for reading, reason: %m\ntrying default config file...\n", config_file); return NULL; } return f; } void assign_default_config_file(void) { int i; for (i = 0; conf_file[i]; i++) { yyin = fopen(conf_file[i], "r"); if (yyin) { config_file = conf_file[i]; break; } } if (!config_file) { err("Can not open '%s': %m\n", conf_file[i - 1]); exit(E_CONFIG_INVALID); } } void count_resources(void) { struct d_resource *res, *tmp; struct d_volume *vol; number_of_minors = 0; for_each_resource(res, tmp, config) { if (res->ignore) { nr_resources[IGNORED]++; /* How can we count ignored volumes? * Do we want to? */ continue; } else if (res->stacked) nr_resources[STACKED]++; else nr_resources[NORMAL]++; for_each_volume(vol, res->me->volumes) { number_of_minors++; if (res->stacked) nr_volumes[STACKED]++; /* res->ignored won't come here */ else nr_volumes[NORMAL]++; } } } void die_if_no_resources(void) { if (!is_drbd_top && nr_resources[IGNORED] > 0 && nr_resources[NORMAL] == 0) { err("WARN: no normal resources defined for this host (%s)!?\n" "Misspelled name of the local machine with the 'on' keyword ?\n", nodeinfo.nodename); exit(E_USAGE); } if (!is_drbd_top && nr_resources[NORMAL] == 0) { err("WARN: no normal resources defined for this host (%s)!?\n", nodeinfo.nodename); exit(E_USAGE); } if (is_drbd_top && nr_resources[STACKED] == 0) { err("WARN: nothing stacked for this host (%s), " "nothing to do in stacked mode!\n", nodeinfo.nodename); exit(E_USAGE); } } void print_dump_xml_header(void) { printf("\n", config_save); ++indent; dump_global_info_xml(); dump_common_info_xml(); } void print_dump_header(void) { printf("# %s\n", config_save); dump_global_info(); dump_common_info(); } int main(int argc, char **argv) { size_t i; int rv = 0, r; struct adm_cmd *cmd = NULL; char **resource_names = NULL; struct d_resource *res, *tmp; char *env_drbd_nodename = NULL; int is_dump_xml; int is_dump; struct cfg_ctx ctx = { .arg = NULL }; initialize_err(); yyin = NULL; uname(&nodeinfo); /* FIXME maybe fold to lower case ? */ no_tty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout))); env_drbd_nodename = getenv("__DRBD_NODE__"); if (env_drbd_nodename && *env_drbd_nodename) { strncpy(nodeinfo.nodename, env_drbd_nodename, sizeof(nodeinfo.nodename) - 1); nodeinfo.nodename[sizeof(nodeinfo.nodename) - 1] = 0; err("\n" " found __DRBD_NODE__ in environment\n" " PRETENDING that I am >>%s<<\n\n", nodeinfo.nodename); } assign_command_names_from_argv0(argv); if (drbdsetup == NULL || drbdmeta == NULL || drbd_proxy_ctl == NULL) { err("could not strdup argv[0].\n"); exit(E_EXEC_ERROR); } if (!getenv("DRBD_DONT_WARN_ON_VERSION_MISMATCH")) warn_on_version_mismatch(); maybe_exec_drbdadm_83(argv); recognize_all_drbdsetup_options(); rv = parse_options(argc, argv, &cmd, &resource_names); if (rv) return rv; if (config_test && !cmd->test_config) { err("The --config-to-test (-t) option is only allowed " "with the dump and sh-nop commands\n"); exit(E_USAGE); } do_verify_ips = cmd->verify_ips; is_dump_xml = (cmd->function == adm_dump_xml); is_dump = (is_dump_xml || cmd->function == adm_dump); if (!resource_names[0]) { if (is_dump) all_resources = 1; else if (cmd->res_name_required) print_usage_and_exit(cmd, "No resource names specified", E_USAGE); } else if (resource_names[0]) { if (!cmd->res_name_required) err("This command will ignore resource names!\n"); else if (resource_names[1] && cmd->use_cached_config_file) err("You should not use this command with multiple resources!\n"); } if (!config_file && cmd->use_cached_config_file) config_file = config_file_from_arg(resource_names[0]); if (!config_file) /* may exit if no config file can be used! */ assign_default_config_file(); /* for error-reporting reasons config_file may be re-assigned by adm_adjust, * we need the current value for register_minor, though. * save that. */ if (config_from_stdin) config_save = config_file; else config_save = canonify_path(config_file); my_parse(); if (config_test) { char *saved_config_file = config_file; char *saved_config_save = config_save; config_file = config_test; config_save = canonify_path(config_test); fclose(yyin); yyin = fopen(config_test, "r"); if (!yyin) { err("Can not open '%s'.\n.", config_test); exit(E_EXEC_ERROR); } my_parse(); config_file = saved_config_file; config_save = saved_config_save; } if (!config_valid) exit(E_CONFIG_INVALID); post_parse(config, cmd->is_proxy_cmd ? MATCH_ON_PROXY : 0); if (!is_dump || dry_run || verbose) expand_common(); if (dry_run || config_from_stdin) do_register = 0; count_resources(); if (cmd->uc_dialog) uc_node(global_options.usage_count); ctx.arg = cmd->name; if (cmd->res_name_required) { if (config == NULL && !is_dump) { err("no resources defined!\n"); exit(E_USAGE); } global_validate_maybe_expand_die_if_invalid(!is_dump, cmd->is_proxy_cmd ? MATCH_ON_PROXY : 0); if (!resource_names[0] || !strcmp(resource_names[0], "all")) { /* either no resource arguments at all, * but command is dump / dump-xml, so implicit "all", * or an explicit "all" argument is given */ all_resources = 1; if (!is_dump) die_if_no_resources(); /* verify ips first, for all of them */ for_each_resource(res, tmp, config) { verify_ips(res); } if (!config_valid) exit(E_CONFIG_INVALID); if (is_dump_xml) print_dump_xml_header(); else if (is_dump) print_dump_header(); for_each_resource(res, tmp, config) { if (!is_dump && res->ignore) continue; if (!is_dump && is_drbd_top != res->stacked) continue; ctx.res = res; ctx.vol = NULL; r = call_cmd(cmd, &ctx, EXIT_ON_FAIL); /* does exit for r >= 20! */ /* this super positioning of return values is soo ugly * anyone any better idea? */ if (r > rv) rv = r; } if (is_dump_xml) { --indent; printf("\n"); } } else { /* explicit list of resources to work on */ for (i = 0; resource_names[i]; i++) { ctx.res = NULL; ctx.vol = NULL; ctx_by_name(&ctx, resource_names[i]); if (!ctx.res) ctx_by_minor(&ctx, resource_names[i]); if (!ctx.res) { err("'%s' not defined in your config (for this host).\n", resource_names[i]); exit(E_USAGE); } if (!cmd->vol_id_required && !cmd->iterate_volumes && ctx.vol != NULL) { if (ctx.vol->implicit) ctx.vol = NULL; else { err("%s operates on whole resources, but you specified a specific volume!\n", cmd->name); exit(E_USAGE); } } if (cmd->vol_id_required && !ctx.vol && ctx.res->me->volumes->implicit) ctx.vol = ctx.res->me->volumes; if (cmd->vol_id_required && !ctx.vol) { err("%s requires a specific volume id, but none is specified.\n" "Try '%s minor-' or '%s %s/'\n", cmd->name, cmd->name, cmd->name, resource_names[i]); exit(E_USAGE); } if (ctx.res->ignore && !is_dump) { err("'%s' ignored, since this host (%s) is not mentioned with an 'on' keyword.\n", ctx.res->name, nodeinfo.nodename); rv = E_USAGE; continue; } if (is_drbd_top != ctx.res->stacked && !is_dump) { err("'%s' is a %s resource, and not available in %s mode.\n", ctx.res->name, ctx.res->stacked ? "stacked" : "normal", is_drbd_top ? "stacked" : "normal"); rv = E_USAGE; continue; } verify_ips(ctx.res); if (!is_dump && !config_valid) exit(E_CONFIG_INVALID); r = call_cmd(cmd, &ctx, EXIT_ON_FAIL); /* does exit for rv >= 20! */ if (r > rv) rv = r; } } } else { // Commands which do not need a resource name /* no call_cmd, as that implies register_minor, * which does not make sense for resource independent commands. * It does also not need to iterate over volumes: it does not even know the resource. */ rv = cmd->function(&ctx); if (rv >= 10) { /* why do we special case the "generic sh-*" commands? */ err("command %s exited with code %d\n", cmd->name, rv); exit(rv); } } r = run_deferred_cmds(); if (r > rv) rv = r; free_config(config); free(resource_names); if (admopt != general_admopt) free(admopt); return rv; } void yyerror(char *text) { err("%s:%d: %s\n", config_file, line, text); exit(E_SYNTAX); } drbd-utils-8.9.10/user/v84/drbdsetup.c0000644000175000017500000031021713011114651017312 0ustar apoikosapoikos/* * DRBD setup via genetlink * * This file is part of DRBD by Philipp Reisner and Lars Ellenberg. * * Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. * Copyright (C) 1999-2008, Philipp Reisner . * Copyright (C) 2002-2008, Lars Ellenberg . * * drbd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #define _XOPEN_SOURCE 600 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EXIT_NOMEM 20 #define EXIT_NO_FAMILY 20 #define EXIT_SEND_ERR 20 #define EXIT_RECV_ERR 20 #define EXIT_TIMED_OUT 20 #define EXIT_NOSOCK 30 #define EXIT_THINKO 42 /* * We are not using libnl, * using its API for the few things we want to do * ends up being almost as much lines of code as * coding the necessary bits right here. */ #include "libgenl.h" #include "drbd_nla.h" #include #include #include #include #include "drbdtool_common.h" #include "registry.h" #include "config.h" #include "config_flags.h" #include "wrap_printf.h" #include "drbdsetup_colors.h" #include "drbd_strings.h" char *progname; /* for parsing of messages */ static struct nlattr *global_attrs[128]; /* there is an other table, nested_attr_tb, defined in genl_magic_func.h, * which can be used after _from_attrs, * to check for presence of struct fields. */ #define ntb(t) nested_attr_tb[__nla_type(t)] #ifdef PRINT_NLMSG_LEN /* I'm to lazy to check the maximum possible nlmsg length by hand */ int main(void) { static __u16 nla_attr_minlen[NLA_TYPE_MAX+1] __read_mostly = { [NLA_U8] = sizeof(__u8), [NLA_U16] = sizeof(__u16), [NLA_U32] = sizeof(__u32), [NLA_U64] = sizeof(__u64), [NLA_NESTED] = NLA_HDRLEN, }; int i; int sum_total = 0; #define LEN__(policy) do { \ int sum = 0; \ for (i = 0; i < ARRAY_SIZE(policy); i++) { \ sum += nla_total_size(policy[i].len ?: \ nla_attr_minlen[policy[i].type]); \ \ } \ sum += 4; \ sum_total += sum; \ printf("%-30s %4u [%4u]\n", \ #policy ":", sum, sum_total); \ } while (0) #define LEN_(p) LEN__(p ## _nl_policy) LEN_(disk_conf); LEN_(syncer_conf); LEN_(net_conf); LEN_(set_role_parms); LEN_(resize_parms); LEN_(state_info); LEN_(start_ov_parms); LEN_(new_c_uuid_parms); sum_total += sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr) + sizeof(struct drbd_genlmsghdr); printf("sum total inclusive hdr overhead: %4u\n", sum_total); return 0; } #else #ifndef AF_INET_SDP #define AF_INET_SDP 27 #define PF_INET_SDP AF_INET_SDP #endif /* pretty print helpers */ static int indent = 0; #define INDENT_WIDTH 4 #define printI(fmt, args... ) printf("%*s" fmt,INDENT_WIDTH * indent,"" , ## args ) enum usage_type { BRIEF, FULL, XML, }; struct drbd_argument { const char* name; __u16 nla_type; int (*convert_function)(struct drbd_argument *, struct msg_buff *, struct drbd_genlmsghdr *dhdr, char *); }; /* Configuration requests typically need a context to operate on. * Possible keys are device minor/volume id (both fit in the drbd_genlmsghdr), * the replication link (aka connection) name, * and/or the replication group (aka resource) name */ enum cfg_ctx_key { /* Only one of these can be present in a command: */ CTX_MINOR = 1, CTX_RESOURCE = 2, CTX_ALL = 4, CTX_CONNECTION = 8, CTX_RESOURCE_AND_CONNECTION = 16, }; struct drbd_cmd { const char* cmd; const enum cfg_ctx_key ctx_key; const int cmd_id; const int tla_id; /* top level attribute id */ int (*function)(const struct drbd_cmd *, int, char **); struct drbd_argument *drbd_args; int (*show_function)(const struct drbd_cmd*, struct genl_info *, void *u_prt); struct option *options; bool missing_ok; bool warn_on_missing; bool continuous_poll; bool wait_for_connect_timeouts; bool set_defaults; bool lockless; struct context_def *ctx; }; // other functions static void print_command_usage(const struct drbd_cmd *cm, enum usage_type); // command functions static int generic_config_cmd(const struct drbd_cmd *cm, int argc, char **argv); static int down_cmd(const struct drbd_cmd *cm, int argc, char **argv); static int generic_get_cmd(const struct drbd_cmd *cm, int argc, char **argv); static int del_minor_cmd(const struct drbd_cmd *cm, int argc, char **argv); static int del_resource_cmd(const struct drbd_cmd *cm, int argc, char **argv); static int status_cmd(const struct drbd_cmd *cm, int argc, char **argv); // sub commands for generic_get_cmd static int print_notifications(const struct drbd_cmd *, struct genl_info *, void *u_ptr); static int show_scmd(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr); static int role_scmd(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr); static int sh_status_scmd(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr); static int cstate_scmd(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr); static int dstate_scmd(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr); static int uuids_scmd(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr); static int lk_bdev_scmd(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr); static int print_broadcast_events(const struct drbd_cmd *, struct genl_info *, void *u_ptr); static int w_connected_state(const struct drbd_cmd *, struct genl_info *, void *u_ptr); static int w_synced_state(const struct drbd_cmd *, struct genl_info *, void *u_ptr); // convert functions for arguments static int conv_block_dev(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg); static int conv_md_idx(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg); static int conv_resource_name(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg); static int conv_volume(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg); static int conv_minor(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg); struct resources_list { struct resources_list *next; char *name; struct nlattr *res_opts; struct resource_info info; struct resource_statistics statistics; }; static struct resources_list *list_resources(void); static struct resources_list *sort_resources(struct resources_list *); static void free_resources(struct resources_list *); struct devices_list { struct devices_list *next; unsigned minor; struct drbd_cfg_context ctx; struct disk_conf disk_conf; struct device_info info; struct device_statistics statistics; }; static struct devices_list *list_devices(char *); static void free_devices(struct devices_list *); struct connections_list { struct connections_list *next; struct drbd_cfg_context ctx; struct nlattr *net_conf; struct connection_info info; struct connection_statistics statistics; }; static struct connections_list *sort_connections(struct connections_list *); static struct connections_list *list_connections(char *); static void free_connections(struct connections_list *); struct peer_devices_list { struct peer_devices_list *next; struct drbd_cfg_context ctx; struct peer_device_info info; struct peer_device_statistics statistics; struct devices_list *device; int timeout_ms; /* used only by wait_for_family() */ }; static struct peer_devices_list *list_peer_devices(char *); static void free_peer_devices(struct peer_devices_list *); struct option wait_cmds_options[] = { { "wfc-timeout",required_argument, 0, 't' }, { "degr-wfc-timeout",required_argument,0,'d'}, { "outdated-wfc-timeout",required_argument,0,'o'}, { "wait-after-sb",optional_argument,0,'w'}, { 0, 0, 0, 0 } }; struct option events_cmd_options[] = { { "timestamps", no_argument, 0, 'T' }, { "statistics", no_argument, 0, 's' }, { "now", no_argument, 0, 'n' }, { } }; struct option show_cmd_options[] = { { "show-defaults", no_argument, 0, 'D' }, { } }; static struct option status_cmd_options[] = { { "verbose", no_argument, 0, 'v' }, { "statistics", no_argument, 0, 's' }, { "color", optional_argument, 0, 'c' }, { } }; #define F_CONFIG_CMD generic_config_cmd #define NO_PAYLOAD 0 #define F_GET_CMD(scmd) DRBD_ADM_GET_STATUS, NO_PAYLOAD, generic_get_cmd, \ .show_function = scmd #define F_NEW_EVENTS_CMD(scmd) DRBD_ADM_GET_INITIAL_STATE, NO_PAYLOAD, generic_get_cmd, \ .show_function = scmd const struct drbd_cmd commands[] = { {"primary", CTX_MINOR, DRBD_ADM_PRIMARY, DRBD_NLA_SET_ROLE_PARMS, F_CONFIG_CMD, .ctx = &primary_cmd_ctx }, {"secondary", CTX_MINOR, DRBD_ADM_SECONDARY, NO_PAYLOAD, F_CONFIG_CMD }, {"attach", CTX_MINOR, DRBD_ADM_ATTACH, DRBD_NLA_DISK_CONF, F_CONFIG_CMD, .drbd_args = (struct drbd_argument[]) { { "lower_dev", T_backing_dev, conv_block_dev }, { "meta_data_dev", T_meta_dev, conv_block_dev }, { "meta_data_index", T_meta_dev_idx, conv_md_idx }, { } }, .ctx = &attach_cmd_ctx }, {"disk-options", CTX_MINOR, DRBD_ADM_CHG_DISK_OPTS, DRBD_NLA_DISK_CONF, F_CONFIG_CMD, .set_defaults = true, .ctx = &disk_options_ctx }, {"detach", CTX_MINOR, DRBD_ADM_DETACH, DRBD_NLA_DETACH_PARMS, F_CONFIG_CMD, .ctx = &detach_cmd_ctx }, {"connect", CTX_RESOURCE_AND_CONNECTION, DRBD_ADM_CONNECT, DRBD_NLA_NET_CONF, F_CONFIG_CMD, .ctx = &connect_cmd_ctx }, {"net-options", CTX_CONNECTION, DRBD_ADM_CHG_NET_OPTS, DRBD_NLA_NET_CONF, F_CONFIG_CMD, .set_defaults = true, .ctx = &net_options_ctx }, {"disconnect", CTX_CONNECTION, DRBD_ADM_DISCONNECT, DRBD_NLA_DISCONNECT_PARMS, F_CONFIG_CMD, .ctx = &disconnect_cmd_ctx }, {"resize", CTX_MINOR, DRBD_ADM_RESIZE, DRBD_NLA_RESIZE_PARMS, F_CONFIG_CMD, .ctx = &resize_cmd_ctx }, {"resource-options", CTX_RESOURCE, DRBD_ADM_RESOURCE_OPTS, DRBD_NLA_RESOURCE_OPTS, F_CONFIG_CMD, .set_defaults = true, .ctx = &resource_options_cmd_ctx }, {"new-current-uuid", CTX_MINOR, DRBD_ADM_NEW_C_UUID, DRBD_NLA_NEW_C_UUID_PARMS, F_CONFIG_CMD, .ctx = &new_current_uuid_cmd_ctx }, {"invalidate", CTX_MINOR, DRBD_ADM_INVALIDATE, NO_PAYLOAD, F_CONFIG_CMD, }, {"invalidate-remote", CTX_MINOR, DRBD_ADM_INVAL_PEER, NO_PAYLOAD, F_CONFIG_CMD, }, {"pause-sync", CTX_MINOR, DRBD_ADM_PAUSE_SYNC, NO_PAYLOAD, F_CONFIG_CMD, }, {"resume-sync", CTX_MINOR, DRBD_ADM_RESUME_SYNC, NO_PAYLOAD, F_CONFIG_CMD, }, {"suspend-io", CTX_MINOR, DRBD_ADM_SUSPEND_IO, NO_PAYLOAD, F_CONFIG_CMD, }, {"resume-io", CTX_MINOR, DRBD_ADM_RESUME_IO, NO_PAYLOAD, F_CONFIG_CMD, }, {"outdate", CTX_MINOR, DRBD_ADM_OUTDATE, NO_PAYLOAD, F_CONFIG_CMD, }, {"verify", CTX_MINOR, DRBD_ADM_START_OV, DRBD_NLA_START_OV_PARMS, F_CONFIG_CMD, .ctx = &verify_cmd_ctx }, {"down", CTX_RESOURCE, DRBD_ADM_DOWN, NO_PAYLOAD, down_cmd, .missing_ok = true, .warn_on_missing = true, }, {"state", CTX_MINOR, F_GET_CMD(role_scmd) }, {"role", CTX_MINOR, F_GET_CMD(role_scmd), .lockless = true, }, {"sh-status", CTX_MINOR | CTX_RESOURCE | CTX_ALL, F_GET_CMD(sh_status_scmd), .missing_ok = true, .lockless = true, }, {"cstate", CTX_MINOR, F_GET_CMD(cstate_scmd), .lockless = true, }, {"dstate", CTX_MINOR, F_GET_CMD(dstate_scmd), .lockless = true, }, {"show-gi", CTX_MINOR, F_GET_CMD(uuids_scmd), .lockless = true, }, {"get-gi", CTX_MINOR, F_GET_CMD(uuids_scmd), .lockless = true, }, {"show", CTX_MINOR | CTX_RESOURCE | CTX_ALL, F_GET_CMD(show_scmd), .options = show_cmd_options, .lockless = true, }, {"status", CTX_RESOURCE | CTX_ALL, 0, 0, status_cmd, .options = status_cmd_options, .lockless = true, }, {"check-resize", CTX_MINOR, F_GET_CMD(lk_bdev_scmd), .lockless = true, }, {"events", CTX_MINOR | CTX_RESOURCE | CTX_ALL, F_GET_CMD(print_broadcast_events), .missing_ok = true, .continuous_poll = true, .lockless = true, }, {"events2", CTX_RESOURCE | CTX_ALL, F_NEW_EVENTS_CMD(print_notifications), .options = events_cmd_options, .missing_ok = true, .continuous_poll = true, .lockless = true }, {"wait-connect", CTX_MINOR, F_GET_CMD(w_connected_state), .options = wait_cmds_options, .continuous_poll = true, .wait_for_connect_timeouts = true, .lockless = true, }, {"wait-sync", CTX_MINOR, F_GET_CMD(w_synced_state), .options = wait_cmds_options, .continuous_poll = true, .wait_for_connect_timeouts = true, .lockless = true, }, {"new-resource", CTX_RESOURCE, DRBD_ADM_NEW_RESOURCE, DRBD_NLA_RESOURCE_OPTS, F_CONFIG_CMD, .ctx = &resource_options_cmd_ctx }, /* only payload is resource name and volume number */ {"new-minor", 0, DRBD_ADM_NEW_MINOR, DRBD_NLA_CFG_CONTEXT, F_CONFIG_CMD, .drbd_args = (struct drbd_argument[]) { { "resource", T_ctx_resource_name, conv_resource_name }, { "minor", 0, conv_minor }, { "volume", T_ctx_volume, conv_volume }, { } }, .ctx = &new_minor_cmd_ctx }, {"del-minor", CTX_MINOR, DRBD_ADM_DEL_MINOR, NO_PAYLOAD, del_minor_cmd, }, {"del-resource", CTX_RESOURCE, DRBD_ADM_DEL_RESOURCE, NO_PAYLOAD, del_resource_cmd, } }; bool show_defaults; bool wait_after_split_brain; #define OTHER_ERROR 900 #define EM(C) [ C - ERR_CODE_BASE ] /* The EM(123) are used for old error messages. */ static const char *error_messages[] = { EM(NO_ERROR) = "No further Information available.", EM(ERR_LOCAL_ADDR) = "Local address(port) already in use.", EM(ERR_PEER_ADDR) = "Remote address(port) already in use.", EM(ERR_OPEN_DISK) = "Can not open backing device.", EM(ERR_OPEN_MD_DISK) = "Can not open meta device.", EM(106) = "Lower device already in use.", EM(ERR_DISK_NOT_BDEV) = "Lower device is not a block device.", EM(ERR_MD_NOT_BDEV) = "Meta device is not a block device.", EM(109) = "Open of lower device failed.", EM(110) = "Open of meta device failed.", EM(ERR_DISK_TOO_SMALL) = "Low.dev. smaller than requested DRBD-dev. size.", EM(ERR_MD_DISK_TOO_SMALL) = "Meta device too small.", EM(113) = "You have to use the disk command first.", EM(ERR_BDCLAIM_DISK) = "Lower device is already claimed. This usually means it is mounted.", EM(ERR_BDCLAIM_MD_DISK) = "Meta device is already claimed. This usually means it is mounted.", EM(ERR_MD_IDX_INVALID) = "Lower device / meta device / index combination invalid.", EM(117) = "Currently we only support devices up to 3.998TB.\n" "(up to 2TB in case you do not have CONFIG_LBD set)\n" "Contact office@linbit.com, if you need more.", EM(ERR_IO_MD_DISK) = "IO error(s) occurred during initial access to meta-data.\n", EM(ERR_MD_UNCLEAN) = "Unclean meta-data found.\nYou need to 'drbdadm apply-al res'\n", EM(ERR_MD_INVALID) = "No valid meta-data signature found.\n\n" "\t==> Use 'drbdadm create-md res' to initialize meta-data area. <==\n", EM(ERR_AUTH_ALG) = "The 'cram-hmac-alg' you specified is not known in " "the kernel. (Maybe you need to modprobe it, or modprobe hmac?)", EM(ERR_AUTH_ALG_ND) = "The 'cram-hmac-alg' you specified is not a digest.", EM(ERR_NOMEM) = "kmalloc() failed. Out of memory?", EM(ERR_DISCARD_IMPOSSIBLE) = "--discard-my-data not allowed when primary.", EM(ERR_DISK_CONFIGURED) = "Device is attached to a disk (use detach first)", EM(ERR_NET_CONFIGURED) = "Device has a net-config (use disconnect first)", EM(ERR_MANDATORY_TAG) = "UnknownMandatoryTag", EM(ERR_MINOR_INVALID) = "Device minor not allocated", EM(128) = "Resulting device state would be invalid", EM(ERR_INTR) = "Interrupted by Signal", EM(ERR_RESIZE_RESYNC) = "Resize not allowed during resync.", EM(ERR_NO_PRIMARY) = "Need one Primary node to resize.", EM(ERR_RESYNC_AFTER) = "The resync-after minor number is invalid", EM(ERR_RESYNC_AFTER_CYCLE) = "This would cause a resync-after dependency cycle", EM(ERR_PAUSE_IS_SET) = "Sync-pause flag is already set", EM(ERR_PAUSE_IS_CLEAR) = "Sync-pause flag is already cleared", EM(136) = "Disk state is lower than outdated", EM(ERR_PACKET_NR) = "Kernel does not know how to handle your request.\n" "Maybe API_VERSION mismatch?", EM(ERR_NO_DISK) = "Device does not have a disk-config", EM(ERR_NOT_PROTO_C) = "Protocol C required", EM(ERR_NOMEM_BITMAP) = "vmalloc() failed. Out of memory?", EM(ERR_INTEGRITY_ALG) = "The 'data-integrity-alg' you specified is not known in " "the kernel. (Maybe you need to modprobe it, or modprobe hmac?)", EM(ERR_INTEGRITY_ALG_ND) = "The 'data-integrity-alg' you specified is not a digest.", EM(ERR_CPU_MASK_PARSE) = "Invalid cpu-mask.", EM(ERR_VERIFY_ALG) = "VERIFYAlgNotAvail", EM(ERR_VERIFY_ALG_ND) = "VERIFYAlgNotDigest", EM(ERR_VERIFY_RUNNING) = "Can not change verify-alg while online verify runs", EM(ERR_DATA_NOT_CURRENT) = "Can only attach to the data we lost last (see kernel log).", EM(ERR_CONNECTED) = "Need to be StandAlone", EM(ERR_CSUMS_ALG) = "CSUMSAlgNotAvail", EM(ERR_CSUMS_ALG_ND) = "CSUMSAlgNotDigest", EM(ERR_CSUMS_RESYNC_RUNNING) = "Can not change csums-alg while resync is in progress", EM(ERR_PERM) = "Permission denied. CAP_SYS_ADMIN necessary", EM(ERR_NEED_APV_93) = "Protocol version 93 required to use --assume-clean", EM(ERR_STONITH_AND_PROT_A) = "Fencing policy resource-and-stonith only with prot B or C allowed", EM(ERR_CONG_NOT_PROTO_A) = "on-congestion policy pull-ahead only with prot A allowed", EM(ERR_PIC_AFTER_DEP) = "Sync-pause flag is already cleared.\n" "Note: Resync pause caused by a local resync-after dependency.", EM(ERR_PIC_PEER_DEP) = "Sync-pause flag is already cleared.\n" "Note: Resync pause caused by the peer node.", EM(ERR_RES_NOT_KNOWN) = "Unknown resource", EM(ERR_RES_IN_USE) = "Resource still in use (delete all minors first)", EM(ERR_MINOR_CONFIGURED) = "Minor still configured (down it first)", EM(ERR_MINOR_OR_VOLUME_EXISTS) = "Minor or volume exists already (delete it first)", EM(ERR_INVALID_REQUEST) = "Invalid configuration request", EM(ERR_NEED_APV_100) = "Prot version 100 required in order to change\n" "these network options while connected", EM(ERR_NEED_ALLOW_TWO_PRI) = "Can not clear allow_two_primaries as long as\n" "there a primaries on both sides", EM(ERR_MD_LAYOUT_CONNECTED) = "DRBD need to be connected for online MD layout change\n", EM(ERR_MD_LAYOUT_TOO_BIG) = "Resulting AL area too big\n", EM(ERR_MD_LAYOUT_TOO_SMALL) = "Resulting AL are too small\n", EM(ERR_MD_LAYOUT_NO_FIT) = "Resulting AL does not fit into available meta data space\n", EM(ERR_IMPLICIT_SHRINK) = "Implicit device shrinking not allowed. See kernel log.\n", }; #define MAX_ERROR (sizeof(error_messages)/sizeof(*error_messages)) const char * error_to_string(int err_no) { const unsigned int idx = err_no - ERR_CODE_BASE; if (idx >= MAX_ERROR) return "Unknown... maybe API_VERSION mismatch?"; return error_messages[idx]; } #undef MAX_ERROR char *cmdname = NULL; /* "drbdsetup" for reporting in usage etc. */ /* * In CTX_MINOR, CTX_RESOURCE, CTX_ALL, objname and minor refer to the object * the command operates on. */ char *objname; unsigned minor = -1U; enum cfg_ctx_key context; char *opt_local_addr, *opt_peer_addr; int lock_fd; struct genl_sock *drbd_sock = NULL; int try_genl = 1; struct genl_family drbd_genl_family = { .name = "drbd", .version = GENL_MAGIC_VERSION, .hdrsize = GENL_MAGIC_FAMILY_HDRSZ, }; static bool endpoints_equal(struct drbd_cfg_context *a, struct drbd_cfg_context *b) { return a->ctx_my_addr_len == b->ctx_my_addr_len && a->ctx_peer_addr_len == b->ctx_peer_addr_len && !memcmp(a->ctx_my_addr, b->ctx_my_addr, a->ctx_my_addr_len) && !memcmp(a->ctx_peer_addr, b->ctx_peer_addr, a->ctx_peer_addr_len); } static int conv_block_dev(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg) { struct stat sb; int device_fd; if ((device_fd = open(arg,O_RDWR))==-1) { PERROR("Can not open device '%s'", arg); return OTHER_ERROR; } if (fstat(device_fd, &sb)) { PERROR("fstat(%s) failed", arg); return OTHER_ERROR; } if(!S_ISBLK(sb.st_mode)) { fprintf(stderr, "%s is not a block device!\n", arg); return OTHER_ERROR; } close(device_fd); nla_put_string(msg, ad->nla_type, arg); return NO_ERROR; } static int conv_md_idx(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg) { int idx; if(!strcmp(arg,"internal")) idx = DRBD_MD_INDEX_FLEX_INT; else if(!strcmp(arg,"flexible")) idx = DRBD_MD_INDEX_FLEX_EXT; else idx = m_strtoll(arg,1); nla_put_u32(msg, ad->nla_type, idx); return NO_ERROR; } static int conv_resource_name(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg) { /* additional sanity checks? */ nla_put_string(msg, T_ctx_resource_name, arg); return NO_ERROR; } static int conv_volume(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg) { unsigned vol = m_strtoll(arg,1); /* sanity check on vol < 256? */ nla_put_u32(msg, T_ctx_volume, vol); return NO_ERROR; } static int conv_minor(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg) { unsigned minor = dt_minor_of_dev(arg); if (minor == -1U) { fprintf(stderr, "Cannot determine minor device number of " "device '%s'\n", arg); return OTHER_ERROR; } dhdr->minor = minor; return NO_ERROR; } static struct option *make_longoptions(const struct drbd_cmd *cm) { static struct option buffer[47]; int i = 0; int primary_force_index = -1; int connect_tentative_index = -1; if (cm->ctx) { struct field_def *field; /* * Make sure to keep cm->ctx->fields first: we use the index * returned by getopt_long() to access cm->ctx->fields. */ for (field = cm->ctx->fields; field->name; field++) { assert(i < ARRAY_SIZE(buffer)); buffer[i].name = field->name; buffer[i].has_arg = field->argument_is_optional ? optional_argument : required_argument; buffer[i].flag = NULL; buffer[i].val = 0; if (!strcmp(cm->cmd, "primary") && !strcmp(field->name, "force")) primary_force_index = i; if (!strcmp(cm->cmd, "connect") && !strcmp(field->name, "tentative")) connect_tentative_index = i; i++; } assert(field - cm->ctx->fields == i); } if (cm->options) { struct option *option; for (option = cm->options; option->name; option++) { assert(i < ARRAY_SIZE(buffer)); buffer[i] = *option; i++; } } if (primary_force_index != -1) { /* * For backward compatibility, add --overwrite-data-of-peer as * an alias to --force. */ assert(i < ARRAY_SIZE(buffer)); buffer[i] = buffer[primary_force_index]; buffer[i].name = "overwrite-data-of-peer"; buffer[i].val = 1000 + primary_force_index; i++; } if (connect_tentative_index != -1) { /* * For backward compatibility, add --dry-run as an alias to * --tentative. */ assert(i < ARRAY_SIZE(buffer)); buffer[i] = buffer[connect_tentative_index]; buffer[i].name = "dry-run"; buffer[i].val = 1000 + connect_tentative_index; i++; } if (cm->set_defaults) { assert(i < ARRAY_SIZE(buffer)); buffer[i].name = "set-defaults"; buffer[i].has_arg = 0; buffer[i].flag = NULL; buffer[i].val = '('; i++; } assert(i < ARRAY_SIZE(buffer)); buffer[i].name = NULL; buffer[i].has_arg = 0; buffer[i].flag = NULL; buffer[i].val = 0; return buffer; } /* prepends global objname to output (if any) */ static int print_config_error(int err_no, char *desc) { int rv=0; if (err_no == NO_ERROR || err_no == SS_SUCCESS) return 0; if (err_no == OTHER_ERROR) { if (desc) fprintf(stderr,"%s: %s\n", objname, desc); return 20; } if ( ( err_no >= AFTER_LAST_ERR_CODE || err_no <= ERR_CODE_BASE ) && ( err_no > SS_CW_NO_NEED || err_no <= SS_AFTER_LAST_ERROR) ) { fprintf(stderr,"%s: Error code %d unknown.\n" "You should update the drbd userland tools.\n", objname, err_no); rv = 20; } else { if(err_no > ERR_CODE_BASE ) { fprintf(stderr,"%s: Failure: (%d) %s\n", objname, err_no, desc ?: error_to_string(err_no)); rv = 10; } else if (err_no == SS_UNKNOWN_ERROR) { fprintf(stderr,"%s: State change failed: (%d)" "unknown error.\n", objname, err_no); rv = 11; } else if (err_no > SS_TWO_PRIMARIES) { // Ignore SS_SUCCESS, SS_NOTHING_TO_DO, SS_CW_Success... } else { fprintf(stderr,"%s: State change failed: (%d) %s\n", objname, err_no, drbd_set_st_err_str(err_no)); if (err_no == SS_NO_UP_TO_DATE_DISK) { /* all available disks are inconsistent, * or I am consistent, but cannot outdate the peer. */ rv = 17; } else if (err_no == SS_LOWER_THAN_OUTDATED) { /* was inconsistent anyways */ rv = 5; } else if (err_no == SS_NO_LOCAL_DISK) { /* Can not start resync, no local disks, try with drbdmeta */ rv = 16; } else { rv = 11; } } } if (global_attrs[DRBD_NLA_CFG_REPLY] && global_attrs[DRBD_NLA_CFG_REPLY]->nla_len) { struct nlattr *nla; int rem; fprintf(stderr, "additional info from kernel:\n"); nla_for_each_nested(nla, global_attrs[DRBD_NLA_CFG_REPLY], rem) { if (nla_type(nla) == __nla_type(T_info_text)) fprintf(stderr, "%s\n", (char*)nla_data(nla)); } } return rv; } static void warn_print_excess_args(int argc, char **argv, int i) { fprintf(stderr, "Excess arguments:"); for (; i < argc; i++) fprintf(stderr, " %s", argv[i]); printf("\n"); } int drbd_tla_parse(struct nlmsghdr *nlh) { return nla_parse(global_attrs, ARRAY_SIZE(drbd_tla_nl_policy)-1, nlmsg_attrdata(nlh, GENL_HDRLEN + drbd_genl_family.hdrsize), nlmsg_attrlen(nlh, GENL_HDRLEN + drbd_genl_family.hdrsize), drbd_tla_nl_policy); } #define ASSERT(exp) if (!(exp)) \ fprintf(stderr,"ASSERT( " #exp " ) in %s:%d\n", __FILE__,__LINE__); static int _generic_config_cmd(const struct drbd_cmd *cm, int argc, char **argv, int quiet) { struct drbd_argument *ad = cm->drbd_args; struct nlattr *nla; struct option *options; int c, i; int rv = NO_ERROR; char *desc = NULL; /* error description from kernel reply message */ struct drbd_genlmsghdr *dhdr; struct msg_buff *smsg; struct iovec iov; /* pre allocate request message and reply buffer */ iov.iov_len = DEFAULT_MSG_SIZE; iov.iov_base = malloc(iov.iov_len); smsg = msg_new(DEFAULT_MSG_SIZE); if (!smsg || !iov.iov_base) { desc = "could not allocate netlink messages"; rv = OTHER_ERROR; goto error; } dhdr = genlmsg_put(smsg, &drbd_genl_family, 0, cm->cmd_id); dhdr->minor = -1; dhdr->flags = 0; i = 1; if (context & (CTX_RESOURCE | CTX_CONNECTION)) { nla = nla_nest_start(smsg, DRBD_NLA_CFG_CONTEXT); if (context & CTX_RESOURCE) nla_put_string(smsg, T_ctx_resource_name, objname); if (context & CTX_CONNECTION) { nla_put_address(smsg, T_ctx_my_addr, opt_local_addr); nla_put_address(smsg, T_ctx_peer_addr, opt_peer_addr); } nla_nest_end(smsg, nla); } else if (context & CTX_MINOR) { dhdr->minor = minor; i++; } nla = NULL; options = make_longoptions(cm); optind = 0; /* reset getopt_long() */ for (;;) { int idx; c = getopt_long(argc, argv, "(", options, &idx); if (c == -1) break; if (c >= 1000) { /* This is a field alias. */ idx = c - 1000; c = 0; } if (c == 0) { struct field_def *field = &cm->ctx->fields[idx]; assert (field->name == options[idx].name); if (!nla) { assert (cm->tla_id != NO_PAYLOAD); nla = nla_nest_start(smsg, cm->tla_id); } if (!field->put(cm->ctx, field, smsg, optarg)) { fprintf(stderr, "Option --%s: invalid " "argument '%s'\n", field->name, optarg); rv = OTHER_ERROR; goto error; } } else if (c == '(') dhdr->flags |= DRBD_GENL_F_SET_DEFAULTS; else { rv = OTHER_ERROR; goto error; } } for (i = optind, ad = cm->drbd_args; ad && ad->name; i++) { if (argc < i + 1) { fprintf(stderr, "Missing argument '%s'\n", ad->name); print_command_usage(cm, FULL); rv = OTHER_ERROR; goto error; } if (!nla) { assert (cm->tla_id != NO_PAYLOAD); nla = nla_nest_start(smsg, cm->tla_id); } rv = ad->convert_function(ad, smsg, dhdr, argv[i]); if (rv != NO_ERROR) goto error; ad++; } /* dhdr->minor may have been set by one of the convert functions. */ minor = dhdr->minor; /* argc should be cmd + n options + n args; * if it is more, we did not understand some */ if (i < argc) { warn_print_excess_args(argc, argv, i); rv = OTHER_ERROR; goto error; } if (rv == NO_ERROR) { int received; if (nla) nla_nest_end(smsg, nla); if (genl_send(drbd_sock, smsg)) { desc = "error sending config command"; rv = OTHER_ERROR; goto error; } retry_recv: /* reduce timeout! limit retries */ received = genl_recv_msgs(drbd_sock, &iov, &desc, 120000); if (received > 0) { struct nlmsghdr *nlh = (struct nlmsghdr*)iov.iov_base; struct drbd_genlmsghdr *dh = genlmsg_data(nlmsg_data(nlh)); ASSERT(dh->minor == minor); rv = dh->ret_code; if (rv == ERR_RES_NOT_KNOWN) { if (cm->warn_on_missing && isatty(STDERR_FILENO)) fprintf(stderr, "Resource unknown\n"); if (cm->missing_ok) rv = NO_ERROR; } drbd_tla_parse(nlh); } else { if (received == -E_RCV_ERROR_REPLY && !errno) goto retry_recv; if (!desc) desc = "error receiving config reply"; rv = OTHER_ERROR; } } error: msg_free(smsg); if (!quiet) rv = print_config_error(rv, desc); free(iov.iov_base); return rv; } static int generic_config_cmd(const struct drbd_cmd *cm, int argc, char **argv) { return _generic_config_cmd(cm, argc, argv, 0); } static int del_minor_cmd(const struct drbd_cmd *cm, int argc, char **argv) { int rv; rv = generic_config_cmd(cm, argc, argv); if (!rv) unregister_minor(minor); return rv; } static int del_resource_cmd(const struct drbd_cmd *cm, int argc, char **argv) { int rv; rv = generic_config_cmd(cm, argc, argv); if (!rv) unregister_resource(objname); return rv; } static const struct drbd_cmd *find_cmd_by_name(const char *name) { unsigned int i; for (i = 0; i < ARRAY_SIZE(commands); i++) { if (!strcmp(name, commands[i].cmd)) { return commands + i; } } return NULL; } static void print_options(const char *cmd_name, const char *sect_name) { const struct drbd_cmd *cmd; struct field_def *field; int opened = 0; cmd = find_cmd_by_name(cmd_name); if (!cmd) { fprintf(stderr, "%s internal error, no such cmd %s\n", cmdname, cmd_name); abort(); } if (!global_attrs[cmd->tla_id]) return; if (drbd_nla_parse_nested(nested_attr_tb, cmd->ctx->nla_policy_size - 1, global_attrs[cmd->tla_id], cmd->ctx->nla_policy)) { fprintf(stderr, "nla_policy violation for %s payload!\n", sect_name); /* still, print those that validated ok */ } if (!cmd->ctx) return; for (field = cmd->ctx->fields; field->name; field++) { struct nlattr *nlattr; const char *str; bool is_default; nlattr = ntb(field->nla_type); if (!nlattr) continue; if (!opened) { opened=1; printI("%s {\n",sect_name); ++indent; } str = field->get(cmd->ctx, field, nlattr); is_default = field->is_default(field, str); if (is_default && !show_defaults) continue; if (field->needs_double_quoting) str = double_quote_string(str); printI("%-16s\t%s;",field->name, str); if (field->unit || is_default) { printf(" # "); if (field->unit) printf("%s", field->unit); if (field->unit && is_default) printf(", "); if (is_default) printf("default"); } printf("\n"); } if(opened) { --indent; printI("}\n"); } } struct choose_timo_ctx { unsigned minor; struct msg_buff *smsg; struct iovec *iov; int timeout; int wfc_timeout; int degr_wfc_timeout; int outdated_wfc_timeout; }; int choose_timeout(struct choose_timo_ctx *ctx) { char *desc = NULL; struct drbd_genlmsghdr *dhdr; int rr; if (0 < ctx->wfc_timeout && (ctx->wfc_timeout < ctx->degr_wfc_timeout || ctx->degr_wfc_timeout == 0)) { ctx->degr_wfc_timeout = ctx->wfc_timeout; fprintf(stderr, "degr-wfc-timeout has to be shorter than wfc-timeout\n" "degr-wfc-timeout implicitly set to wfc-timeout (%ds)\n", ctx->degr_wfc_timeout); } if (0 < ctx->degr_wfc_timeout && (ctx->degr_wfc_timeout < ctx->outdated_wfc_timeout || ctx->outdated_wfc_timeout == 0)) { ctx->outdated_wfc_timeout = ctx->wfc_timeout; fprintf(stderr, "outdated-wfc-timeout has to be shorter than degr-wfc-timeout\n" "outdated-wfc-timeout implicitly set to degr-wfc-timeout (%ds)\n", ctx->degr_wfc_timeout); } dhdr = genlmsg_put(ctx->smsg, &drbd_genl_family, 0, DRBD_ADM_GET_TIMEOUT_TYPE); dhdr->minor = ctx->minor; dhdr->flags = 0; if (genl_send(drbd_sock, ctx->smsg)) { desc = "error sending config command"; goto error; } rr = genl_recv_msgs(drbd_sock, ctx->iov, &desc, 120000); if (rr > 0) { struct nlmsghdr *nlh = (struct nlmsghdr*)ctx->iov->iov_base; struct genl_info info = { .seq = nlh->nlmsg_seq, .nlhdr = nlh, .genlhdr = nlmsg_data(nlh), .userhdr = genlmsg_data(nlmsg_data(nlh)), .attrs = global_attrs, }; struct drbd_genlmsghdr *dh = info.userhdr; struct timeout_parms parms; ASSERT(dh->minor == ctx->minor); rr = dh->ret_code; if (rr == ERR_MINOR_INVALID) { desc = "minor not available"; goto error; } if (rr != NO_ERROR) goto error; if (drbd_tla_parse(nlh) || timeout_parms_from_attrs(&parms, &info)) { desc = "reply did not validate - " "do you need to upgrade your userland tools?"; goto error; } rr = parms.timeout_type; ctx->timeout = (rr == UT_DEGRADED) ? ctx->degr_wfc_timeout : (rr == UT_PEER_OUTDATED) ? ctx->outdated_wfc_timeout : ctx->wfc_timeout; return 0; } error: if (!desc) desc = "error receiving netlink reply"; fprintf(stderr, "error determining which timeout to use: %s\n", desc); return 20; } #include static bool kernel_older_than(int version, int patchlevel, int sublevel) { struct utsname utsname; char *rel; int l; if (uname(&utsname) != 0) return false; rel = utsname.release; l = strtol(rel, &rel, 10); if (l > version) return false; else if (l < version || *rel == 0) return true; l = strtol(rel + 1, &rel, 10); if (l > patchlevel) return false; else if (l < patchlevel || *rel == 0) return true; l = strtol(rel + 1, &rel, 10); if (l >= sublevel) return false; return true; } static bool opt_now; static bool opt_verbose; static bool opt_statistics; static bool opt_timestamps; static int generic_get(const struct drbd_cmd *cm, int timeout_arg, void *u_ptr) { char *desc = NULL; struct drbd_genlmsghdr *dhdr; struct msg_buff *smsg; struct iovec iov; int timeout_ms, flags; int rv = NO_ERROR; int err = 0; /* pre allocate request message and reply buffer */ iov.iov_len = DEFAULT_MSG_SIZE; iov.iov_base = malloc(iov.iov_len); smsg = msg_new(DEFAULT_MSG_SIZE); if (!smsg || !iov.iov_base) { desc = "could not allocate netlink messages"; rv = OTHER_ERROR; goto out; } if (cm->continuous_poll) { /* also always (try to) listen to nlctrl notify, * so we have a chance to notice rmmod. */ int id = GENL_ID_CTRL; setsockopt(drbd_sock->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &id, sizeof(id)); if (genl_join_mc_group(drbd_sock, "events") && !kernel_older_than(2, 6, 23)) { desc = "unable to join drbd events multicast group"; rv = OTHER_ERROR; goto out2; } } flags = 0; if (minor == -1U) flags |= NLM_F_DUMP; dhdr = genlmsg_put(smsg, &drbd_genl_family, flags, cm->cmd_id); dhdr->minor = minor; dhdr->flags = 0; if (minor == -1U && strcmp(objname, "all")) { /* Restrict the dump to a single resource. */ struct nlattr *nla; nla = nla_nest_start(smsg, DRBD_NLA_CFG_CONTEXT); nla_put_string(smsg, T_ctx_resource_name, objname); nla_nest_end(smsg, nla); } if (genl_send(drbd_sock, smsg)) { desc = "error sending config command"; rv = OTHER_ERROR; goto out2; } /* disable sequence number check in genl_recv_msgs */ drbd_sock->s_seq_expect = 0; for (;;) { int received, rem, ret; struct nlmsghdr *nlh = (struct nlmsghdr *)iov.iov_base; struct timeval before; struct pollfd pollfds[2] = { [0] = { .fd = 1, .events = POLLHUP, }, [1] = { .fd = drbd_sock->s_fd, .events = POLLIN, }, }; gettimeofday(&before, NULL); timeout_ms = timeout_arg; ret = poll(pollfds, 2, timeout_arg); if (ret == 0) { err = 5; goto out2; } if (pollfds[0].revents == POLLERR || pollfds[0].revents == POLLHUP) goto out2; received = genl_recv_msgs(drbd_sock, &iov, &desc, -1); if (received < 0) { switch(received) { case E_RCV_TIMEDOUT: err = 5; goto out2; case -E_RCV_FAILED: err = 20; goto out2; case -E_RCV_NO_SOURCE_ADDR: continue; /* ignore invalid message */ case -E_RCV_SEQ_MISMATCH: /* we disabled it, so it should not happen */ err = 20; goto out2; case -E_RCV_MSG_TRUNC: continue; case -E_RCV_UNEXPECTED_TYPE: continue; case -E_RCV_NLMSG_DONE: if (cm->continuous_poll) continue; err = cm->show_function(cm, NULL, u_ptr); if (err) goto out2; err = -*(int*)nlmsg_data(nlh); if (err && (err != ENODEV || !cm->missing_ok)) { fprintf(stderr, "received netlink error reply: %s\n", strerror(err)); err = 20; } goto out2; case -E_RCV_ERROR_REPLY: if (!errno) /* positive ACK message */ continue; if (!desc) desc = strerror(errno); fprintf(stderr, "received netlink error reply: %s\n", desc); err = 20; goto out2; default: if (!desc) desc = "error receiving config reply"; err = 20; goto out2; } } if (timeout_ms != -1) { struct timeval after; int elapsed_ms; bool exit; gettimeofday(&after, NULL); elapsed_ms = (after.tv_sec - before.tv_sec) * 1000 + (after.tv_usec - before.tv_usec) / 1000; timeout_ms -= elapsed_ms; exit = timeout_ms <= 0; if (exit) { err = 5; goto out2; } } /* There may be multiple messages in one datagram (for dump replies). */ nlmsg_for_each_msg(nlh, nlh, received, rem) { struct drbd_genlmsghdr *dh = genlmsg_data(nlmsg_data(nlh)); struct genl_info info = (struct genl_info){ .seq = nlh->nlmsg_seq, .nlhdr = nlh, .genlhdr = nlmsg_data(nlh), .userhdr = genlmsg_data(nlmsg_data(nlh)), .attrs = global_attrs, }; dbg(3, "received type:%x\n", nlh->nlmsg_type); if (nlh->nlmsg_type < NLMSG_MIN_TYPE) { /* Ignore netlink control messages. */ continue; } if (nlh->nlmsg_type == GENL_ID_CTRL) { #ifdef HAVE_CTRL_CMD_DELMCAST_GRP dbg(3, "received cmd:%x\n", info.genlhdr->cmd); if (info.genlhdr->cmd == CTRL_CMD_DELMCAST_GRP) { struct nlattr *nla = nlmsg_find_attr(nlh, GENL_HDRLEN, CTRL_ATTR_FAMILY_ID); if (nla && nla_get_u16(nla) == drbd_genl_family.id) { /* FIXME: We could wait for the multicast group to be recreated ... */ goto out2; } } #endif /* Ignore other generic netlink control messages. */ continue; } if (nlh->nlmsg_type != drbd_genl_family.id) { /* Ignore messages for all other netlink families. */ continue; } /* parse early, otherwise drbd_cfg_context_from_attrs * can not work */ if (drbd_tla_parse(nlh)) { /* FIXME * should continuous_poll continue? */ desc = "reply did not validate - " "do you need to upgrade your userland tools?"; rv = OTHER_ERROR; goto out2; } if (cm->continuous_poll) { struct drbd_cfg_context ctx; /* * We will receive all events and have to * filter for what we want ourself. */ /* FIXME * Do we want to ignore broadcasts until the * initial get/dump requests is done? */ if (!drbd_cfg_context_from_attrs(&ctx, &info)) { switch (context) { case CTX_MINOR: /* Assert that, for an unicast reply, * reply minor matches request minor. * "unsolicited" kernel broadcasts are "pid=0" (netlink "port id") * (and expected to be genlmsghdr.cmd == DRBD_EVENT) */ if (minor != dh->minor) { if (info.nlhdr->nlmsg_pid != 0) dbg(1, "received netlink packet for minor %u, while expecting %u\n", dh->minor, minor); continue; } break; case CTX_ALL: break; case CTX_RESOURCE: if (strcmp(objname, ctx.ctx_resource_name)) continue; break; #if 0 case CTX_CONNECTION: case CTX_CONNECTION | CTX_RESOURCE: if (!endpoints_equal(&ctx, &global_ctx)) continue; break; #endif #if 0 case CTX_PEER_DEVICE: if (!endpoints_equal(&ctx, &global_ctx) || ctx.ctx_volume != global_ctx.ctx_volume) continue; break; #endif default: assert(0); } } } rv = dh->ret_code; if (rv == ERR_MINOR_INVALID) { if (cm->warn_on_missing) fprintf(stderr, "Minor invalid"); if (cm->missing_ok) rv = NO_ERROR; } if (rv != NO_ERROR) goto out2; err = cm->show_function(cm, &info, u_ptr); if (err) { if (err < 0) err = 0; goto out2; } } if (!cm->continuous_poll && !(flags & NLM_F_DUMP)) { /* There will be no more reply packets. */ err = cm->show_function(cm, NULL, u_ptr); goto out2; } } out2: msg_free(smsg); out: if (!err) err = print_config_error(rv, desc); free(iov.iov_base); return err; } static int generic_get_cmd(const struct drbd_cmd *cm, int argc, char **argv) { static struct option no_options[] = { { } }; struct choose_timo_ctx timeo_ctx = { .wfc_timeout = DRBD_WFC_TIMEOUT_DEF, .degr_wfc_timeout = DRBD_DEGR_WFC_TIMEOUT_DEF, .outdated_wfc_timeout = DRBD_OUTDATED_WFC_TIMEOUT_DEF, }; int timeout_ms = -1; /* "infinite" */ struct option *options = cm->options ? cm->options : no_options; const char *opts = make_optstring(options); optind = 0; /* reset getopt_long() */ for(;;) { int c = getopt_long(argc, argv, opts, options, 0); if (c == -1) break; switch(c) { default: case '?': return 20; case 't': timeo_ctx.wfc_timeout = m_strtoll(optarg, 1); if(DRBD_WFC_TIMEOUT_MIN > timeo_ctx.wfc_timeout || timeo_ctx.wfc_timeout > DRBD_WFC_TIMEOUT_MAX) { fprintf(stderr, "wfc_timeout => %d" " out of range [%d..%d]\n", timeo_ctx.wfc_timeout, DRBD_WFC_TIMEOUT_MIN, DRBD_WFC_TIMEOUT_MAX); return 20; } break; case 'd': timeo_ctx.degr_wfc_timeout = m_strtoll(optarg, 1); if(DRBD_DEGR_WFC_TIMEOUT_MIN > timeo_ctx.degr_wfc_timeout || timeo_ctx.degr_wfc_timeout > DRBD_DEGR_WFC_TIMEOUT_MAX) { fprintf(stderr, "degr_wfc_timeout => %d" " out of range [%d..%d]\n", timeo_ctx.degr_wfc_timeout, DRBD_DEGR_WFC_TIMEOUT_MIN, DRBD_DEGR_WFC_TIMEOUT_MAX); return 20; } break; case 'o': timeo_ctx.outdated_wfc_timeout = m_strtoll(optarg, 1); if(DRBD_OUTDATED_WFC_TIMEOUT_MIN > timeo_ctx.outdated_wfc_timeout || timeo_ctx.outdated_wfc_timeout > DRBD_OUTDATED_WFC_TIMEOUT_MAX) { fprintf(stderr, "outdated_wfc_timeout => %d" " out of range [%d..%d]\n", timeo_ctx.outdated_wfc_timeout, DRBD_OUTDATED_WFC_TIMEOUT_MIN, DRBD_OUTDATED_WFC_TIMEOUT_MAX); return 20; } break; case 'n': opt_now = true; break; case 's': opt_verbose = true; opt_statistics = true; break; case 'w': if (!optarg || !strcmp(optarg, "yes")) wait_after_split_brain = true; break; case 'D': show_defaults = true; break; case 'T': opt_timestamps = true; break; } } if (optind < argc) { warn_print_excess_args(argc, argv, optind); return 20; } if (cm->wait_for_connect_timeouts) { /* wait-connect, wait-sync */ struct msg_buff *smsg; struct iovec iov; int rr; iov.iov_len = 8192; iov.iov_base = malloc(iov.iov_len); smsg = msg_new(DEFAULT_MSG_SIZE); if (!smsg || !iov.iov_base) { fprintf(stderr, "could not allocate netlink messages\n"); return 20; } timeo_ctx.minor = minor; timeo_ctx.smsg = smsg; timeo_ctx.iov = &iov; rr = choose_timeout(&timeo_ctx); if (rr) return rr; if (timeo_ctx.timeout) timeout_ms = timeo_ctx.timeout * 1000; msg_free(smsg); free(iov.iov_base); } else if (!cm->continuous_poll) /* normal "get" request, or "show" */ timeout_ms = 120000; /* else: events command, defaults to "infinity" */ return generic_get(cm, timeout_ms, NULL); } static void show_address(void* address, int addr_len) { char buffer[ADDRESS_STR_MAX]; sprint_address(buffer, address, addr_len); printI("address\t\t\t%s;\n", buffer); } struct minors_list { struct minors_list *next; unsigned minor; }; struct minors_list *__remembered_minors; static int remember_minor(const struct drbd_cmd *cmd, struct genl_info *info, void *u_ptr) { struct drbd_cfg_context cfg = { .ctx_volume = -1U }; if (!info) return 0; drbd_cfg_context_from_attrs(&cfg, info); if (cfg.ctx_volume != -1U) { unsigned minor = ((struct drbd_genlmsghdr*)(info->userhdr))->minor; struct minors_list *m = malloc(sizeof(*m)); m->next = __remembered_minors; m->minor = minor; __remembered_minors = m; } return 0; } static void free_minors(struct minors_list *minors) { while (minors) { struct minors_list *m = minors; minors = minors->next; free(m); } } /* * Expects objname to be set to the resource name or "all". */ static struct minors_list *enumerate_minors(void) { struct drbd_cmd cmd = { .cmd_id = DRBD_ADM_GET_STATUS, .show_function = remember_minor, .missing_ok = true, }; struct minors_list *m; int err; err = generic_get_cmd(&cmd, 0, NULL); m = __remembered_minors; __remembered_minors = NULL; if (err) { free_minors(m); m = NULL; } return m; } static int remember_resource(const struct drbd_cmd *cmd, struct genl_info *info, void *u_ptr) { struct resources_list ***tail = u_ptr; struct drbd_cfg_context cfg = { .ctx_volume = -1U }; if (!info) return 0; drbd_cfg_context_from_attrs(&cfg, info); if (cfg.ctx_resource_name) { struct resources_list *r = calloc(1, sizeof(*r)); struct nlattr *res_opts = global_attrs[DRBD_NLA_RESOURCE_OPTS]; r->name = strdup(cfg.ctx_resource_name); if (res_opts) { int size = nla_total_size(nla_len(res_opts)); r->res_opts = malloc(size); memcpy(r->res_opts, res_opts, size); } resource_info_from_attrs(&r->info, info); memset(&r->statistics, -1, sizeof(r->statistics)); resource_statistics_from_attrs(&r->statistics, info); **tail = r; *tail = &r->next; } return 0; } static void free_resources(struct resources_list *resources) { while (resources) { struct resources_list *r = resources; resources = resources->next; free(r->name); free(r->res_opts); free(r); } } static int resource_name_cmp(const struct resources_list * const *a, const struct resources_list * const *b) { return strcmp((*a)->name, (*b)->name); } static struct resources_list *sort_resources(struct resources_list *resources) { struct resources_list *r; int n; for (r = resources, n = 0; r; r = r->next) n++; if (n > 1) { struct resources_list **array; array = malloc(sizeof(*array) * n); for (r = resources, n = 0; r; r = r->next) array[n++] = r; qsort(array, n, sizeof(*array), (int (*)(const void *, const void *)) resource_name_cmp); n--; array[n]->next = NULL; for (; n > 0; n--) array[n - 1]->next = array[n]; resources = array[0]; free(array); } return resources; } /* * Expects objname to be set to the resource name or "all". */ static struct resources_list *list_resources(void) { struct drbd_cmd cmd = { .cmd_id = DRBD_ADM_GET_RESOURCES, .show_function = remember_resource, .missing_ok = false, }; struct resources_list *list = NULL, **tail = &list; char *old_objname = objname; unsigned old_minor = minor; int err; objname = "all"; minor = -1; err = generic_get(&cmd, 120000, &tail); objname = old_objname; minor = old_minor; if (err) { free_resources(list); list = NULL; } return list; } static int remember_device(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr) { struct devices_list ***tail = u_ptr; struct drbd_cfg_context ctx = { .ctx_volume = -1U }; if (!info) return 0; drbd_cfg_context_from_attrs(&ctx, info); if (ctx.ctx_volume != -1U) { struct devices_list *d = calloc(1, sizeof(*d)); d->minor = ((struct drbd_genlmsghdr*)(info->userhdr))->minor; d->ctx = ctx; disk_conf_from_attrs(&d->disk_conf, info); d->info.dev_disk_state = D_DISKLESS; device_info_from_attrs(&d->info, info); memset(&d->statistics, -1, sizeof(d->statistics)); device_statistics_from_attrs(&d->statistics, info); **tail = d; *tail = &d->next; } return 0; } /* * Expects objname to be set to the resource name or "all". */ static struct devices_list *list_devices(char *resource_name) { struct drbd_cmd cmd = { .cmd_id = DRBD_ADM_GET_DEVICES, .show_function = remember_device, .missing_ok = false, }; struct devices_list *list = NULL, **tail = &list; char *old_objname = objname; unsigned old_minor = minor; int err; objname = resource_name ? resource_name : "all"; minor = -1; err = generic_get(&cmd, 120000, &tail); objname = old_objname; minor = old_minor; if (err) { free_devices(list); list = NULL; } return list; } static void free_devices(struct devices_list *devices) { while (devices) { struct devices_list *d = devices; devices = devices->next; free(d); } } static int remember_connection(const struct drbd_cmd *cmd, struct genl_info *info, void *u_ptr) { struct connections_list ***tail = u_ptr; struct drbd_cfg_context ctx = { .ctx_volume = -1U }; if (!info) return 0; drbd_cfg_context_from_attrs(&ctx, info); if (ctx.ctx_resource_name) { struct connections_list *c = calloc(1, sizeof(*c)); struct nlattr *net_conf = global_attrs[DRBD_NLA_NET_CONF]; c->ctx = ctx; if (net_conf) { int size = nla_total_size(nla_len(net_conf)); c->net_conf = malloc(size); memcpy(c->net_conf, net_conf, size); } connection_info_from_attrs(&c->info, info); memset(&c->statistics, -1, sizeof(c->statistics)); connection_statistics_from_attrs(&c->statistics, info); **tail = c; *tail = &c->next; } return 0; } #if 0 static int connection_name_cmp(const struct connections_list * const *a, const struct connections_list * const *b) { if (!(*a)->ctx.ctx_conn_name_len != !(*b)->ctx.ctx_conn_name_len) return !(*b)->ctx.ctx_conn_name_len; return strcmp((*a)->ctx.ctx_conn_name, (*b)->ctx.ctx_conn_name); } #endif static struct connections_list *sort_connections(struct connections_list *connections) { struct connections_list *c; int n; for (c = connections, n = 0; c; c = c->next) n++; if (n > 1) { struct connections_list **array; array = malloc(sizeof(*array) * n); for (c = connections, n = 0; c; c = c->next) array[n++] = c; #if 0 qsort(array, n, sizeof(*array), (int (*)(const void *, const void *)) connection_name_cmp); #endif n--; array[n]->next = NULL; for (; n > 0; n--) array[n - 1]->next = array[n]; connections = array[0]; free(array); } return connections; } /* * Expects objname to be set to the resource name or "all". */ static struct connections_list *list_connections(char *resource_name) { struct drbd_cmd cmd = { .cmd_id = DRBD_ADM_GET_CONNECTIONS, .show_function = remember_connection, .missing_ok = true, }; struct connections_list *list = NULL, **tail = &list; char *old_objname = objname; unsigned old_minor = minor; int err; objname = resource_name ? resource_name : "all"; minor = -1; err = generic_get(&cmd, 120000, &tail); objname = old_objname; minor = old_minor; if (err) { free_connections(list); list = NULL; } return list; } static void free_connections(struct connections_list *connections) { while (connections) { struct connections_list *l = connections; connections = connections->next; free(l); } } static int remember_peer_device(const struct drbd_cmd *cmd, struct genl_info *info, void *u_ptr) { struct peer_devices_list ***tail = u_ptr; struct drbd_cfg_context ctx = { .ctx_volume = -1U }; if (!info) return 0; drbd_cfg_context_from_attrs(&ctx, info); if (ctx.ctx_resource_name) { struct peer_devices_list *p = calloc(1, sizeof(*p)); if (!p) exit(20); p->ctx = ctx; peer_device_info_from_attrs(&p->info, info); memset(&p->statistics, -1, sizeof(p->statistics)); peer_device_statistics_from_attrs(&p->statistics, info); **tail = p; *tail = &p->next; } return 0; } /* * Expects objname to be set to the resource name or "all". */ static struct peer_devices_list *list_peer_devices(char *resource_name) { struct drbd_cmd cmd = { .cmd_id = DRBD_ADM_GET_PEER_DEVICES, .show_function = remember_peer_device, .missing_ok = false, }; struct peer_devices_list *list = NULL, **tail = &list; char *old_objname = objname; unsigned old_minor = minor; int err; objname = resource_name ? resource_name : "all"; minor = -1; err = generic_get(&cmd, 120000, &tail); objname = old_objname; minor = old_minor; if (err) { free_peer_devices(list); list = NULL; } return list; } static void free_peer_devices(struct peer_devices_list *peer_devices) { while (peer_devices) { struct peer_devices_list *p = peer_devices; peer_devices = peer_devices->next; free(p); } } /* may be called for a "show" of a single minor device. * prints all available configuration information in that case. * * may also be called iteratively for a "show-all", which should try to not * print redundant configuration information for the same resource (tconn). */ static int show_scmd(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr) { /* FIXME need some define for max len here */ static char last_ctx_resource_name[128]; static int call_count; struct drbd_cfg_context cfg = { .ctx_volume = -1U }; struct disk_conf dc = { .disk_size = 0, }; struct net_conf nc = { .timeout = 0, };; if (!info) { if (call_count) { --indent; printI("}\n"); /* close _this_host */ --indent; printI("}\n"); /* close resource */ } fflush(stdout); return 0; } call_count++; /* FIXME: Is the folowing check needed? */ if (!global_attrs[DRBD_NLA_CFG_CONTEXT]) dbg(1, "unexpected packet, configuration context missing!\n"); drbd_cfg_context_from_attrs(&cfg, info); disk_conf_from_attrs(&dc, info); net_conf_from_attrs(&nc, info); if (strncmp(last_ctx_resource_name, cfg.ctx_resource_name, sizeof(last_ctx_resource_name))) { if (strncmp(last_ctx_resource_name, "", sizeof(last_ctx_resource_name))) { --indent; printI("}\n"); /* close _this_host */ --indent; printI("}\n\n"); } strncpy(last_ctx_resource_name, cfg.ctx_resource_name, sizeof(last_ctx_resource_name)); printI("resource %s {\n", cfg.ctx_resource_name); ++indent; print_options("resource-options", "options"); print_options("net-options", "net"); if (cfg.ctx_peer_addr_len) { printI("_remote_host {\n"); ++indent; show_address(cfg.ctx_peer_addr, cfg.ctx_peer_addr_len); --indent; printI("}\n"); } printI("_this_host {\n"); ++indent; if (cfg.ctx_my_addr_len) show_address(cfg.ctx_my_addr, cfg.ctx_my_addr_len); } if (cfg.ctx_volume != -1U) { unsigned minor = ((struct drbd_genlmsghdr*)(info->userhdr))->minor; printI("volume %d {\n", cfg.ctx_volume); ++indent; printI("device\t\t\tminor %d;\n", minor); if (global_attrs[DRBD_NLA_DISK_CONF]) { if (dc.backing_dev[0]) { printI("disk\t\t\t\"%s\";\n", dc.backing_dev); printI("meta-disk\t\t\t"); switch(dc.meta_dev_idx) { case DRBD_MD_INDEX_INTERNAL: case DRBD_MD_INDEX_FLEX_INT: printf("internal;\n"); break; case DRBD_MD_INDEX_FLEX_EXT: printf("%s;\n", double_quote_string(dc.meta_dev)); break; default: printf("%s [ %d ];\n", double_quote_string(dc.meta_dev), dc.meta_dev_idx); } } } print_options("attach", "disk"); --indent; printI("}\n"); /* close volume */ } return 0; } static int lk_bdev_scmd(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr) { unsigned minor; struct disk_conf dc = { .disk_size = 0, }; struct bdev_info bd = { 0, }; uint64_t bd_size; int fd; if (!info) return 0; minor = ((struct drbd_genlmsghdr*)(info->userhdr))->minor; disk_conf_from_attrs(&dc, info); if (!dc.backing_dev) { fprintf(stderr, "Has no disk config, try with drbdmeta.\n"); return 1; } if (dc.meta_dev_idx >= 0 || dc.meta_dev_idx == DRBD_MD_INDEX_FLEX_EXT) { lk_bdev_delete(minor); return 0; } fd = open(dc.backing_dev, O_RDONLY); if (fd == -1) { fprintf(stderr, "Could not open %s: %m.\n", dc.backing_dev); return 1; } bd_size = bdev_size(fd); close(fd); if (lk_bdev_load(minor, &bd) == 0 && bd.bd_size == bd_size && bd.bd_name && !strcmp(bd.bd_name, dc.backing_dev)) return 0; /* nothing changed. */ bd.bd_size = bd_size; bd.bd_name = dc.backing_dev; lk_bdev_save(minor, &bd); return 0; } static int sh_status_scmd(const struct drbd_cmd *cm __attribute((unused)), struct genl_info *info, void *u_ptr) { unsigned minor; struct drbd_cfg_context cfg = { .ctx_volume = -1U }; struct state_info si = { .current_state = 0, }; union drbd_state state; int available = 0; if (!info) return 0; minor = ((struct drbd_genlmsghdr*)(info->userhdr))->minor; /* variable prefix; maybe rather make that a command line parameter? * or use "drbd_sh_status"? */ #define _P "" printf("%s_minor=%u\n", _P, minor); drbd_cfg_context_from_attrs(&cfg, info); if (cfg.ctx_resource_name) printf("%s_res_name=%s\n", _P, shell_escape(cfg.ctx_resource_name)); printf("%s_volume=%d\n", _P, cfg.ctx_volume); if (state_info_from_attrs(&si, info) == 0) available = 1; state.i = si.current_state; if (state.conn == C_STANDALONE && state.disk == D_DISKLESS && state.role != R_PRIMARY) { printf("%s_known=%s\n\n", _P, available ? "Unconfigured" : "NA # not available or not yet created"); printf("%s_cstate=Unconfigured\n", _P); printf("%s_role=\n", _P); printf("%s_peer=\n", _P); printf("%s_disk=\n", _P); printf("%s_pdsk=\n", _P); printf("%s_flags_susp=\n", _P); printf("%s_flags_aftr_isp=\n", _P); printf("%s_flags_peer_isp=\n", _P); printf("%s_flags_user_isp=\n", _P); printf("%s_resynced_percent=\n", _P); } else { printf( "%s_known=Configured\n\n" /* connection state */ "%s_cstate=%s\n" /* role */ "%s_role=%s\n" "%s_peer=%s\n" /* disk state */ "%s_disk=%s\n" "%s_pdsk=%s\n\n", _P, _P, drbd_conn_str(state.conn), _P, drbd_role_str(state.role), _P, drbd_role_str(state.peer), _P, drbd_disk_str(state.disk), _P, drbd_disk_str(state.pdsk)); /* io suspended ? */ printf("%s_flags_susp=%s\n", _P, state.susp ? "1" : ""); /* reason why sync is paused */ printf("%s_flags_aftr_isp=%s\n", _P, state.aftr_isp ? "1" : ""); printf("%s_flags_peer_isp=%s\n", _P, state.peer_isp ? "1" : ""); printf("%s_flags_user_isp=%s\n\n", _P, state.user_isp ? "1" : ""); printf("%s_resynced_percent=", _P); if (ntb(T_bits_rs_total)) { uint32_t shift = si.bits_rs_total >= (1ULL << 32) ? 16 : 10; uint64_t left = (si.bits_oos - si.bits_rs_failed) >> shift; uint64_t total = 1UL + (si.bits_rs_total >> shift); uint64_t tmp = 1000UL - left * 1000UL/total; unsigned synced = tmp; printf("%i.%i\n", synced / 10, synced % 10); /* what else? everything available! */ } else printf("\n"); } printf("\n%s_sh_status_process\n\n\n", _P); fflush(stdout); return 0; #undef _P } static int role_scmd(const struct drbd_cmd *cm __attribute((unused)), struct genl_info *info, void *u_ptr) { union drbd_state state = { .i = 0 }; if (!strcmp(cm->cmd, "state")) { fprintf(stderr, "'%s ... state' is deprecated, use '%s ... role' instead.\n", cmdname, cmdname); } if (!info) return 0; if (global_attrs[DRBD_NLA_STATE_INFO]) { drbd_nla_parse_nested(nested_attr_tb, ARRAY_SIZE(state_info_nl_policy) - 1, global_attrs[DRBD_NLA_STATE_INFO], state_info_nl_policy); if (ntb(T_current_state)) state.i = nla_get_u32(ntb(T_current_state)); } if (state.conn == C_STANDALONE && state.disk == D_DISKLESS) { printf("Unconfigured\n"); } else { printf("%s/%s\n",drbd_role_str(state.role),drbd_role_str(state.peer)); } return 0; } static int cstate_scmd(const struct drbd_cmd *cm __attribute((unused)), struct genl_info *info, void *u_ptr) { union drbd_state state = { .i = 0 }; if (!info) return 0; if (global_attrs[DRBD_NLA_STATE_INFO]) { drbd_nla_parse_nested(nested_attr_tb, ARRAY_SIZE(state_info_nl_policy) - 1, global_attrs[DRBD_NLA_STATE_INFO], state_info_nl_policy); if (ntb(T_current_state)) state.i = nla_get_u32(ntb(T_current_state)); } if (state.conn == C_STANDALONE && state.disk == D_DISKLESS) { printf("Unconfigured\n"); } else { printf("%s\n",drbd_conn_str(state.conn)); } return 0; } static int dstate_scmd(const struct drbd_cmd *cm __attribute((unused)), struct genl_info *info, void *u_ptr) { union drbd_state state = { .i = 0 }; if (!info) return 0; if (global_attrs[DRBD_NLA_STATE_INFO]) { drbd_nla_parse_nested(nested_attr_tb, ARRAY_SIZE(state_info_nl_policy)-1, global_attrs[DRBD_NLA_STATE_INFO], state_info_nl_policy); if (ntb(T_current_state)) state.i = nla_get_u32(ntb(T_current_state)); } if ( state.conn == C_STANDALONE && state.disk == D_DISKLESS) { printf("Unconfigured\n"); } else { printf("%s/%s\n",drbd_disk_str(state.disk),drbd_disk_str(state.pdsk)); } return 0; } static int uuids_scmd(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr) { union drbd_state state = { .i = 0 }; uint64_t ed_uuid; uint64_t *uuids = NULL; int flags = flags; if (!info) return 0; if (global_attrs[DRBD_NLA_STATE_INFO]) { drbd_nla_parse_nested(nested_attr_tb, ARRAY_SIZE(state_info_nl_policy)-1, global_attrs[DRBD_NLA_STATE_INFO], state_info_nl_policy); if (ntb(T_current_state)) state.i = nla_get_u32(ntb(T_current_state)); if (ntb(T_uuids)) uuids = nla_data(ntb(T_uuids)); if (ntb(T_disk_flags)) flags = nla_get_u32(ntb(T_disk_flags)); if (ntb(T_ed_uuid)) ed_uuid = nla_get_u64(ntb(T_ed_uuid)); } if (state.conn == C_STANDALONE && state.disk == D_DISKLESS) { fprintf(stderr, "Device is unconfigured\n"); return 1; } if (state.disk == D_DISKLESS) { /* XXX we could print the ed_uuid anyways: */ if (0) printf(X64(016)"\n", ed_uuid); fprintf(stderr, "Device has no disk\n"); return 1; } if (uuids) { if(!strcmp(cm->cmd,"show-gi")) { dt_pretty_print_uuids(uuids,flags); } else if(!strcmp(cm->cmd,"get-gi")) { dt_print_uuids(uuids,flags); } else { ASSERT( 0 ); } } else { fprintf(stderr, "No uuids found in reply!\n" "Maybe you need to upgrade your userland tools?\n"); } return 0; } static int down_cmd(const struct drbd_cmd *cm, int argc, char **argv) { struct minors_list *minors, *m; int rv; int success; if(argc > 2) { warn_print_excess_args(argc, argv, 2); return OTHER_ERROR; } minors = enumerate_minors(); rv = _generic_config_cmd(cm, argc, argv, 1); success = (rv >= SS_SUCCESS && rv < ERR_CODE_BASE) || rv == NO_ERROR; if (success) { for (m = minors; m; m = m->next) unregister_minor(m->minor); free_minors(minors); unregister_resource(objname); } else { free_minors(minors); return print_config_error(rv, NULL); } return 0; } static const char *susp_str(struct resource_info *info) { static char buffer[32]; *buffer = 0; if (info->res_susp) strcat(buffer, ",user" + (*buffer == 0)); if (info->res_susp_nod) strcat(buffer, ",no-data" + (*buffer == 0)); if (info->res_susp_fen) strcat(buffer, ",fencing" + (*buffer == 0)); if (*buffer == 0) strcat(buffer, "no"); return buffer; } int nowrap_printf(int indent, const char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = vprintf(format, ap); va_end(ap); return ret; } void print_resource_statistics(int indent, struct resource_statistics *old, struct resource_statistics *new, int (*wrap_printf)(int, const char *, ...)) { static const char *write_ordering_str[] = { [WO_NONE] = "none", [WO_DRAIN_IO] = "drain", [WO_BDEV_FLUSH] = "flush", [WO_BIO_BARRIER] = "barrier", }; uint32_t wo = new->res_stat_write_ordering; if ((!old || old->res_stat_write_ordering != wo) && wo < ARRAY_SIZE(write_ordering_str) && write_ordering_str[wo]) { wrap_printf(indent, " write-ordering:%s", write_ordering_str[wo]); } } void print_device_statistics(int indent, struct device_statistics *old, struct device_statistics *new, int (*wrap_printf)(int, const char *, ...)) { if (opt_statistics) { if (opt_verbose) wrap_printf(indent, " size:" U64, (uint64_t)new->dev_size / 2); wrap_printf(indent, " read:" U64, (uint64_t)new->dev_read / 2); wrap_printf(indent, " written:" U64, (uint64_t)new->dev_write / 2); if (opt_verbose) { wrap_printf(indent, " al-writes:" U64, (uint64_t)new->dev_al_writes); wrap_printf(indent, " bm-writes:" U64, (uint64_t)new->dev_bm_writes); wrap_printf(indent, " upper-pending:" U32, new->dev_upper_pending); wrap_printf(indent, " lower-pending:" U32, new->dev_lower_pending); if (!old || old->dev_al_suspended != new->dev_al_suspended) wrap_printf(indent, " al-suspended:%s", new->dev_al_suspended ? "yes" : "no"); } } if ((!old || old->dev_upper_blocked != new->dev_upper_blocked || old->dev_lower_blocked != new->dev_lower_blocked) && new->dev_size != -1 && (opt_verbose || new->dev_upper_blocked || new->dev_lower_blocked)) { const char *x1 = "", *x2 = ""; bool first = true; if (new->dev_upper_blocked) { x1 = ",upper" + first; first = false; } if (new->dev_lower_blocked) { x2 = ",lower" + first; first = false; } if (first) x1 = "no"; wrap_printf(indent, " blocked:%s%s", x1, x2); } } void print_connection_statistics(int indent, struct connection_statistics *old, struct connection_statistics *new, int (*wrap_printf)(int, const char *, ...)) { if (!old || old->conn_congested != new->conn_congested) wrap_printf(indent, " congested:%s", new->conn_congested ? "yes" : "no"); } void print_peer_device_statistics(int indent, struct peer_device_statistics *old, struct peer_device_statistics *new, int (*wrap_printf)(int, const char *, ...)) { wrap_printf(indent, " received:" U64, (uint64_t)new->peer_dev_received / 2); wrap_printf(indent, " sent:" U64, (uint64_t)new->peer_dev_sent / 2); if (opt_verbose || new->peer_dev_out_of_sync) wrap_printf(indent, " out-of-sync:" U64, (uint64_t)new->peer_dev_out_of_sync / 2); if (opt_verbose) { wrap_printf(indent, " pending:" U32, new->peer_dev_pending); wrap_printf(indent, " unacked:" U32, new->peer_dev_unacked); } } void resource_status(struct resources_list *resource) { enum drbd_role role = resource->info.res_role; wrap_printf(0, "%s", resource->name); #if 0 if (opt_verbose) { struct nlattr *nla; nla = nla_find_nested(resource->res_opts, __nla_type(T_node_id)); if (nla) wrap_printf(4, " node-id:%d", *(uint32_t *)nla_data(nla)); } #endif wrap_printf(4, " role:%s%s%s", role_color_start(role, true), drbd_role_str(role), role_color_stop(role, true)); if (opt_verbose || resource->info.res_susp || resource->info.res_susp_nod || resource->info.res_susp_fen) wrap_printf(4, " suspended:%s", susp_str(&resource->info)); #if 0 if (opt_verbose || resource->info.res_weak) wrap_printf(4, " weak:%s", resource->info.res_weak ? "yes" : "no"); #endif if (opt_statistics && opt_verbose) { wrap_printf(4, "\n"); print_resource_statistics(4, NULL, &resource->statistics, wrap_printf); } wrap_printf(0, "\n"); } static void device_status(struct devices_list *device, bool single_device) { enum drbd_disk_state disk_state = device->info.dev_disk_state; int indent = 2; if (opt_verbose || !(single_device && device->ctx.ctx_volume == 0)) { wrap_printf(indent, "volume:%u", device->ctx.ctx_volume); indent = 6; if (opt_verbose) wrap_printf(indent, " minor:%u", device->minor); } wrap_printf(indent, " disk:%s%s%s", disk_state_color_start(disk_state, true), drbd_disk_str(disk_state), disk_state_color_stop(disk_state, true)); indent = 6; if (device->statistics.dev_size != -1) { if (opt_statistics) wrap_printf(indent, "\n"); print_device_statistics(indent, NULL, &device->statistics, wrap_printf); } wrap_printf(indent, "\n"); } static const char *resync_susp_str(struct peer_device_info *info) { static char buffer[64]; *buffer = 0; if (info->peer_resync_susp_user) strcat(buffer, ",user" + (*buffer == 0)); if (info->peer_resync_susp_peer) strcat(buffer, ",peer" + (*buffer == 0)); if (info->peer_resync_susp_dependency) strcat(buffer, ",dependency" + (*buffer == 0)); if (*buffer == 0) strcat(buffer, "no"); return buffer; } const char *drbd_repl_str9(enum drbd_conns s) { static const char *n[] = { [C_WF_REPORT_PARAMS] = "Off", [C_CONNECTED] = "Established", }; return (s == C_WF_REPORT_PARAMS || s == C_CONNECTED) ? n[s] : drbd_conn_str(s); } const char *drbd_conn_str9(enum drbd_conns s) { static const char *n[] = { [C_WF_CONNECTION] = "Connecting", [C_WF_REPORT_PARAMS] = "Connected", }; return (s == C_WF_CONNECTION || s == C_WF_REPORT_PARAMS) ? n[s] : drbd_conn_str(s); } static void peer_device_status(struct peer_devices_list *peer_device, bool single_device) { int indent = 4; if (opt_verbose || !(single_device && peer_device->ctx.ctx_volume == 0)) { wrap_printf(indent, "volume:%d", peer_device->ctx.ctx_volume); indent = 8; } /* this > C_WF_REPORT_PARAMS is > L_ESTABLISHED in DRBD 9 */ if (opt_verbose || peer_device->info.peer_repl_state > C_WF_REPORT_PARAMS) { enum drbd_conns repl_state = peer_device->info.peer_repl_state; wrap_printf(indent, " replication:%s%s%s", repl_state_color_start(repl_state), drbd_repl_str9(repl_state), repl_state_color_stop(repl_state)); indent = 8; } /* this C_WF_REPORT_PARAMS is C_CONNECTED resp. L_OFF in DRBD 9 */ if (opt_verbose || opt_statistics || peer_device->info.peer_repl_state != C_WF_REPORT_PARAMS || peer_device->info.peer_disk_state != D_UNKNOWN) { enum drbd_disk_state disk_state = peer_device->info.peer_disk_state; wrap_printf(indent, " peer-disk:%s%s%s", disk_state_color_start(disk_state, false), drbd_disk_str(disk_state), disk_state_color_stop(disk_state, false)); indent = 8; if (peer_device->info.peer_repl_state >= C_SYNC_SOURCE && peer_device->info.peer_repl_state <= C_PAUSED_SYNC_T) { wrap_printf(indent, " done:%.2f", 100 * (1 - (double)peer_device->statistics.peer_dev_out_of_sync / (double)peer_device->device->statistics.dev_size)); } if (opt_verbose || peer_device->info.peer_resync_susp_user || peer_device->info.peer_resync_susp_peer || peer_device->info.peer_resync_susp_dependency) wrap_printf(indent, " resync-suspended:%s", resync_susp_str(&peer_device->info)); if (opt_statistics && peer_device->statistics.peer_dev_received != -1) { wrap_printf(indent, "\n"); print_peer_device_statistics(indent, NULL, &peer_device->statistics, wrap_printf); } } wrap_printf(0, "\n"); } static void peer_devices_status(struct drbd_cfg_context *ctx, struct peer_devices_list *peer_devices, bool single_device) { struct peer_devices_list *peer_device; for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { if (!endpoints_equal(ctx, &peer_device->ctx)) continue; peer_device_status(peer_device, single_device); } } static void connection_status(struct connections_list *connection, struct peer_devices_list *peer_devices, bool single_device) { wrap_printf(2, "%s", "peer" /* connection->ctx.ctx_conn_name */); /* We do not want the IP-pair information */ /* We do not have any node-id information */ /* this C_WF_REPORT_PARAMS is C_CONNECTED in DRBD 9 */ if (opt_verbose || connection->info.conn_connection_state < C_WF_REPORT_PARAMS) { enum drbd_conns cstate = connection->info.conn_connection_state; wrap_printf(6, " connection:%s%s%s", cstate_color_start(cstate), drbd_conn_str9(cstate), cstate_color_stop(cstate)); } if (opt_verbose || connection->info.conn_connection_state == C_WF_REPORT_PARAMS) { enum drbd_role role = connection->info.conn_role; wrap_printf(6, " role:%s%s%s", role_color_start(role, false), drbd_role_str(role), role_color_stop(role, false)); } if (opt_verbose || connection->statistics.conn_congested > 0) print_connection_statistics(6, NULL, &connection->statistics, wrap_printf); wrap_printf(0, "\n"); if (opt_verbose || opt_statistics || connection->info.conn_connection_state == C_WF_REPORT_PARAMS) peer_devices_status(&connection->ctx, peer_devices, single_device); } static void stop_colors(int sig) { printf("%s", stop_color_code()); signal(sig, SIG_DFL); raise(sig); } static void link_peer_devices_to_devices(struct peer_devices_list *peer_devices, struct devices_list *devices) { struct peer_devices_list *peer_device; struct devices_list *device; for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { for (device = devices; device; device = device->next) { if (peer_device->ctx.ctx_volume == device->ctx.ctx_volume) { peer_device->device = device; break; } } } } static void print_usage_and_exit(const char *addinfo); static int status_cmd(const struct drbd_cmd *cm, int argc, char **argv) { struct resources_list *resources, *resource; struct sigaction sa = { .sa_handler = stop_colors, .sa_flags = SA_RESETHAND, }; bool found = false; int c; optind = 0; /* reset getopt_long() */ for (;;) { c = getopt_long(argc, argv, make_optstring(cm->options), cm->options, 0); if (c == -1) break; switch(c) { default: case '?': return 20; case 'v': opt_verbose = true; break; case 's': opt_statistics = true; break; case 'c': if (!optarg || !strcmp(optarg, "always")) opt_color = ALWAYS_COLOR; else if (!strcmp(optarg, "never")) opt_color = NEVER_COLOR; else if (!strcmp(optarg, "auto")) opt_color = AUTO_COLOR; else print_usage_and_exit("unknown --color argument"); break; } } resources = sort_resources(list_resources()); sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTERM, &sa, NULL); for (resource = resources; resource; resource = resource->next) { struct devices_list *devices, *device; struct connections_list *connections, *connection; struct peer_devices_list *peer_devices = NULL; bool single_device; if (strcmp(objname, "all") && strcmp(objname, resource->name)) continue; devices = list_devices(resource->name); connections = sort_connections(list_connections(resource->name)); if (devices && connections) peer_devices = list_peer_devices(resource->name); link_peer_devices_to_devices(peer_devices, devices); resource_status(resource); single_device = devices && !devices->next; for (device = devices; device; device = device->next) device_status(device, single_device); for (connection = connections; connection; connection = connection->next) connection_status(connection, peer_devices, single_device); wrap_printf(0, "\n"); free_connections(connections); free_devices(devices); free_peer_devices(peer_devices); found = true; } free_resources(resources); if (!found && strcmp(objname, "all")) { fprintf(stderr, "%s: No such resource\n", objname); return 10; } return 0; } static int event_key(char *key, int size, const char *name, unsigned minor, struct drbd_cfg_context *ctx) { int ret, pos = 0; ret = snprintf(key + pos, size, "%s", name); if (ret < 0) return ret; pos += ret; if (size) size -= ret; if (ctx->ctx_resource_name) { ret = snprintf(key + pos, size, " name:%s", ctx->ctx_resource_name); if (ret < 0) return ret; pos += ret; if (size) size -= ret; } /* 8.4 drbd_cfg_context does not provide ctx->ctx_peer_node_id * check the corresponding name and fake it to 0 */ if (!strcmp(name, "connection") || !strcmp(name, "peer-device") || !strcmp(name, "helper")) { ret = snprintf(key + pos, size, " peer-node-id:%d", 0); if (ret < 0) return ret; pos += ret; if (size) size -= ret; } /* Always use "peer" as connection name, * and print it if ctx has peer address set. * Do not show IP address pairs */ if (ctx->ctx_peer_addr_len) { ret = snprintf(key + pos, size, " conn-name:%s", "peer"); if (ret < 0) return ret; pos += ret; if (size) size -= ret; } if (ctx->ctx_volume != -1U) { ret = snprintf(key + pos, size, " volume:%u", ctx->ctx_volume); if (ret < 0) return ret; pos += ret; if (size) size -= ret; } if (minor != -1U) { ret = snprintf(key + pos, size, " minor:%u", minor); if (ret < 0) return ret; pos += ret; /* if (size) */ /* size -= ret; */ } return pos; } static int known_objects_cmp(const void *a, const void *b) { return strcmp(((const struct entry *)a)->key, ((const struct entry *)b)->key); } static void *update_info(char **key, void *value, size_t size) { static void *known_objects; struct entry entry = { .key = *key }, **found; if (value) { void *old_value = NULL; found = tsearch(&entry, &known_objects, known_objects_cmp); if (*found != &entry) old_value = (*found)->data; else { *found = malloc(sizeof(**found)); if (!*found) goto fail; (*found)->key = *key; *key = NULL; } (*found)->data = malloc(size); if (!(*found)->data) goto fail; memcpy((*found)->data, value, size); return old_value; } else { found = tfind(&entry, &known_objects, known_objects_cmp); if (found) { struct entry *entry = *found; tdelete(entry, &known_objects, known_objects_cmp); free(entry->data); free(entry->key); free(entry); } return NULL; } fail: perror(progname); exit(20); } static int print_notifications(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr) { static const char *action_name[] = { [NOTIFY_EXISTS] = "exists", [NOTIFY_CREATE] = "create", [NOTIFY_CHANGE] = "change", [NOTIFY_DESTROY] = "destroy", [NOTIFY_CALL] = "call", [NOTIFY_RESPONSE] = "response", }; static char *object_name[] = { [DRBD_RESOURCE_STATE] = "resource", [DRBD_DEVICE_STATE] = "device", [DRBD_CONNECTION_STATE] = "connection", [DRBD_PEER_DEVICE_STATE] = "peer-device", [DRBD_HELPER] = "helper", }; static uint32_t last_seq; static bool last_seq_known; static struct timeval tv; static bool keep_tv; struct drbd_cfg_context ctx = { .ctx_volume = -1U }; struct drbd_notification_header nh = { .nh_type = -1U }; enum drbd_notification_type action; struct drbd_genlmsghdr *dh; char *key = NULL; if (!info) { keep_tv = false; return 0; } dh = info->userhdr; if (dh->ret_code == ERR_MINOR_INVALID && cm->missing_ok) return 0; if (dh->ret_code != NO_ERROR) return dh->ret_code; if (drbd_notification_header_from_attrs(&nh, info)) return 0; action = nh.nh_type & ~NOTIFY_FLAGS; if (action >= ARRAY_SIZE(action_name) || !action_name[action]) { dbg(1, "unknown notification type\n"); goto out; } if (opt_now && action != NOTIFY_EXISTS) return 0; if (info->genlhdr->cmd != DRBD_INITIAL_STATE_DONE) { if (drbd_cfg_context_from_attrs(&ctx, info)) return 0; if (info->genlhdr->cmd >= ARRAY_SIZE(object_name) || !object_name[info->genlhdr->cmd]) { dbg(1, "unknown notification\n"); goto out; } } if (action != NOTIFY_EXISTS) { if (last_seq_known) { int skipped = info->nlhdr->nlmsg_seq - (last_seq + 1); if (skipped) printf("- skipped %d\n", skipped); } last_seq = info->nlhdr->nlmsg_seq; last_seq_known = true; } if (opt_timestamps) { struct tm *tm; if (!keep_tv) gettimeofday(&tv, NULL); keep_tv = !!(nh.nh_type & NOTIFY_CONTINUES); tm = localtime(&tv.tv_sec); printf("%04u-%02u-%02uT%02u:%02u:%02u.%06u%+03d:%02u ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)tv.tv_usec, (int)(tm->tm_gmtoff / 3600), (int)((abs(tm->tm_gmtoff) / 60) % 60)); } if (info->genlhdr->cmd != DRBD_INITIAL_STATE_DONE) { const char *name = object_name[info->genlhdr->cmd]; int size; size = event_key(NULL, 0, name, dh->minor, &ctx); if (size < 0) goto fail; key = malloc(size + 1); if (!key) goto fail; event_key(key, size + 1, name, dh->minor, &ctx); } printf("%s %s", action_name[action], key ? key : "-"); switch(info->genlhdr->cmd) { case DRBD_RESOURCE_STATE: if (action != NOTIFY_DESTROY) { struct { struct resource_info i; struct resource_statistics s; } *old, new; if (resource_info_from_attrs(&new.i, info)) { dbg(1, "resource info missing\n"); goto nl_out; } old = update_info(&key, &new, sizeof(new)); if (!old || new.i.res_role != old->i.res_role) printf(" role:%s", drbd_role_str(new.i.res_role)); if (!old || new.i.res_susp != old->i.res_susp || new.i.res_susp_nod != old->i.res_susp_nod || new.i.res_susp_fen != old->i.res_susp_fen) printf(" suspended:%s", susp_str(&new.i)); if (opt_statistics) { if (resource_statistics_from_attrs(&new.s, info)) { dbg(1, "resource statistics missing\n"); if (old) new.s = old->s; } else print_resource_statistics(0, old ? &old->s : NULL, &new.s, nowrap_printf); } free(old); } else update_info(&key, NULL, 0); break; case DRBD_DEVICE_STATE: if (action != NOTIFY_DESTROY) { struct { struct device_info i; struct device_statistics s; } *old, new; if (device_info_from_attrs(&new.i, info)) { dbg(1, "device info missing\n"); goto nl_out; } old = update_info(&key, &new, sizeof(new)); if (!old || new.i.dev_disk_state != old->i.dev_disk_state) printf(" disk:%s", drbd_disk_str(new.i.dev_disk_state)); if (opt_statistics) { if (device_statistics_from_attrs(&new.s, info)) { dbg(1, "device statistics missing\n"); if (old) new.s = old->s; } else print_device_statistics(0, old ? &old->s : NULL, &new.s, nowrap_printf); } free(old); } else update_info(&key, NULL, 0); break; case DRBD_CONNECTION_STATE: if (action != NOTIFY_DESTROY) { struct { struct connection_info i; struct connection_statistics s; } *old, new; if (connection_info_from_attrs(&new.i, info)) { dbg(1, "connection info missing\n"); goto nl_out; } old = update_info(&key, &new, sizeof(new)); if (!old || new.i.conn_connection_state != old->i.conn_connection_state) printf(" connection:%s", drbd_conn_str9(new.i.conn_connection_state)); if (!old || new.i.conn_role != old->i.conn_role) printf(" role:%s", drbd_role_str(new.i.conn_role)); if (opt_statistics) { if (connection_statistics_from_attrs(&new.s, info)) { dbg(1, "connection statistics missing\n"); if (old) new.s = old->s; } else print_connection_statistics(0, old ? &old->s : NULL, &new.s, nowrap_printf); } free(old); } else update_info(&key, NULL, 0); break; case DRBD_PEER_DEVICE_STATE: if (action != NOTIFY_DESTROY) { struct { struct peer_device_info i; struct peer_device_statistics s; } *old, new; if (peer_device_info_from_attrs(&new.i, info)) { dbg(1, "peer device info missing\n"); goto nl_out; } old = update_info(&key, &new, sizeof(new)); if (!old || new.i.peer_repl_state != old->i.peer_repl_state) printf(" replication:%s", drbd_repl_str9(new.i.peer_repl_state)); if (!old || new.i.peer_disk_state != old->i.peer_disk_state) printf(" peer-disk:%s", drbd_disk_str(new.i.peer_disk_state)); if (!old || new.i.peer_resync_susp_user != old->i.peer_resync_susp_user || new.i.peer_resync_susp_peer != old->i.peer_resync_susp_peer || new.i.peer_resync_susp_dependency != old->i.peer_resync_susp_dependency) printf(" resync-suspended:%s", resync_susp_str(&new.i)); if (opt_statistics) { if (peer_device_statistics_from_attrs(&new.s, info)) { dbg(1, "peer device statistics missing\n"); if (old) new.s = old->s; } else print_peer_device_statistics(0, old ? &old->s : NULL, &new.s, nowrap_printf); } free(old); } else update_info(&key, NULL, 0); break; case DRBD_HELPER: { struct drbd_helper_info helper_info; if (!drbd_helper_info_from_attrs(&helper_info, info)) { printf(" helper:%s", helper_info.helper_name); if (action == NOTIFY_RESPONSE) printf(" status:%u", helper_info.helper_status); } else { dbg(1, "helper info missing\n"); goto nl_out; } } break; case DRBD_INITIAL_STATE_DONE: break; } nl_out: printf("\n"); out: free(key); fflush(stdout); if (opt_now && info->genlhdr->cmd == DRBD_INITIAL_STATE_DONE) return -1; return 0; fail: perror(progname); exit(20); } /* printf format for minor, resource name, volume */ #define MNV_FMT "%d,%s[%d]" static void print_state(char *tag, unsigned seq, unsigned minor, const char *resource_name, unsigned vnr, __u32 state_i) { union drbd_state s = { .i = state_i }; printf("%u %s " MNV_FMT " { cs:%s ro:%s/%s ds:%s/%s %c%c%c%c }\n", seq, tag, minor, resource_name, vnr, drbd_conn_str(s.conn), drbd_role_str(s.role), drbd_role_str(s.peer), drbd_disk_str(s.disk), drbd_disk_str(s.pdsk), s.susp ? 's' : 'r', s.aftr_isp ? 'a' : '-', s.peer_isp ? 'p' : '-', s.user_isp ? 'u' : '-' ); } static int print_broadcast_events(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr) { struct drbd_cfg_context cfg = { .ctx_volume = -1U }; struct state_info si = { .current_state = 0 }; struct disk_conf dc = { .disk_size = 0, }; struct net_conf nc = { .timeout = 0, }; struct drbd_genlmsghdr *dh; /* End of initial dump. Ignore. Maybe: print some marker? */ if (!info) return 0; dh = info->userhdr; if (dh->ret_code == ERR_MINOR_INVALID && cm->missing_ok) return 0; if (drbd_cfg_context_from_attrs(&cfg, info)) { dbg(1, "unexpected packet, configuration context missing!\n"); /* keep running anyways. */ struct nlattr *nla = NULL; if (info->attrs[DRBD_NLA_CFG_REPLY]) nla = drbd_nla_find_nested(ARRAY_SIZE(drbd_cfg_reply_nl_policy) - 1, info->attrs[DRBD_NLA_CFG_REPLY], T_info_text); if (nla) { char *txt = nla_data(nla); char *c; for (c = txt; *c; c++) if (*c == '\n') *c = '_'; printf("%u # %s\n", info->seq, txt); } goto out; } if (state_info_from_attrs(&si, info)) { /* this is a DRBD_ADM_GET_STATUS reply * with information about a resource without any volumes */ printf("%u R - %s\n", info->seq, cfg.ctx_resource_name); goto out; } disk_conf_from_attrs(&dc, info); net_conf_from_attrs(&nc, info); switch (si.sib_reason) { case SIB_STATE_CHANGE: print_state("ST-prev", info->seq, dh->minor, cfg.ctx_resource_name, cfg.ctx_volume, si.prev_state); print_state("ST-new", info->seq, dh->minor, cfg.ctx_resource_name, cfg.ctx_volume, si.new_state); /* fall through */ case SIB_GET_STATUS_REPLY: print_state("ST", info->seq, dh->minor, cfg.ctx_resource_name, cfg.ctx_volume, si.current_state); break; case SIB_HELPER_PRE: printf("%u UH " MNV_FMT " %s\n", info->seq, dh->minor, cfg.ctx_resource_name, cfg.ctx_volume, si.helper); break; case SIB_HELPER_POST: printf("%u UH-post " MNV_FMT " %s 0x%04x\n", info->seq, dh->minor, cfg.ctx_resource_name, cfg.ctx_volume, si.helper, si.helper_exit_code); break; case SIB_SYNC_PROGRESS: { uint32_t shift = si.bits_rs_total >= (1ULL << 32) ? 16 : 10; uint64_t left = (si.bits_oos - si.bits_rs_failed) >> shift; uint64_t total = 1UL + (si.bits_rs_total >> shift); uint64_t tmp = 1000UL - left * 1000UL/total; unsigned synced = tmp; printf("%u SP " MNV_FMT " %i.%i\n", info->seq, dh->minor, cfg.ctx_resource_name, cfg.ctx_volume, synced / 10, synced % 10); } break; default: /* we could add the si.reason */ printf("%u ?? " MNV_FMT " \n", info->seq, dh->minor, cfg.ctx_resource_name, cfg.ctx_volume, si.sib_reason); break; } out: fflush(stdout); return 0; } static int w_connected_state(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr) { struct state_info si = { .current_state = 0 }; union drbd_state state; if (!info) return 0; if (!global_attrs[DRBD_NLA_STATE_INFO]) return 0; if (state_info_from_attrs(&si, info)) { fprintf(stderr,"nla_policy violation!?\n"); return 0; } if (si.sib_reason != SIB_STATE_CHANGE && si.sib_reason != SIB_GET_STATUS_REPLY) return 0; state.i = si.current_state; if (state.conn >= C_CONNECTED) return -1; /* done waiting */ if (state.conn < C_UNCONNECTED) { struct drbd_genlmsghdr *dhdr = info->userhdr; struct drbd_cfg_context cfg = { .ctx_volume = -1U }; if (!wait_after_split_brain) return -1; /* done waiting */ drbd_cfg_context_from_attrs(&cfg, info); fprintf(stderr, "\ndrbd%u (%s[%u]) is %s, " "but I'm configured to wait anways (--wait-after-sb)\n", dhdr->minor, cfg.ctx_resource_name, cfg.ctx_volume, drbd_conn_str(state.conn)); } return 0; } static int w_synced_state(const struct drbd_cmd *cm, struct genl_info *info, void *u_ptr) { struct state_info si = { .current_state = 0 }; union drbd_state state; if (!info) return 0; if (!global_attrs[DRBD_NLA_STATE_INFO]) return 0; if (state_info_from_attrs(&si, info)) { fprintf(stderr,"nla_policy violation!?\n"); return 0; } if (si.sib_reason != SIB_STATE_CHANGE && si.sib_reason != SIB_GET_STATUS_REPLY) return 0; state.i = si.current_state; if (state.conn == C_CONNECTED) return -1; /* done waiting */ if (!wait_after_split_brain && state.conn < C_UNCONNECTED) return -1; /* done waiting */ return 0; } /* * Check if an integer is a power of two. */ static bool power_of_two(int i) { return i && !(i & (i - 1)); } static void print_command_usage(const struct drbd_cmd *cm, enum usage_type ut) { struct drbd_argument *args; if(ut == XML) { enum cfg_ctx_key ctx = cm->ctx_key; printf("\n", cm->cmd); if (ctx & CTX_RESOURCE_AND_CONNECTION) ctx = CTX_RESOURCE | CTX_CONNECTION; if (ctx & (CTX_RESOURCE | CTX_MINOR | CTX_ALL)) { bool more_than_one_choice = !power_of_two(ctx & (CTX_RESOURCE | CTX_MINOR | CTX_ALL)); const char *indent = "\t\t" + !more_than_one_choice; if (more_than_one_choice) printf("\t\n"); if (ctx & CTX_RESOURCE) printf("%sresource\n", indent); if (ctx & CTX_MINOR) printf("%sminor\n", indent); if (ctx & CTX_ALL) printf("%sall\n", indent); if (more_than_one_choice) printf("\t\n"); } if (ctx & CTX_CONNECTION) { printf("\tlocal_addr\n"); printf("\tremote_addr\n"); } if(cm->drbd_args) { for (args = cm->drbd_args; args->name; args++) { printf("\t%s\n", args->name); } } if (cm->options) { struct option *option; for (option = cm->options; option->name; option++) { /* * The "string" options here really are * timeouts, but we can't describe them * in a resonable way here. */ printf("\t\n", option->name, option->has_arg == no_argument ? "flag" : "string"); } } if (cm->set_defaults) printf("\t\n"); if (cm->ctx) { struct field_def *field; for (field = cm->ctx->fields; field->name; field++) field->describe_xml(field); } printf("\n"); return; } if (ut == BRIEF) wrap_printf(4, "%-18s ", cm->cmd); else { wrap_printf(0, "USAGE:\n"); wrap_printf(1, "%s %s", progname, cm->cmd); if (cm->ctx_key && ut != BRIEF) { enum cfg_ctx_key ctx = cm->ctx_key; if (ctx & CTX_RESOURCE_AND_CONNECTION) ctx = CTX_RESOURCE | CTX_CONNECTION; if (ctx & (CTX_RESOURCE | CTX_MINOR | CTX_ALL)) { bool first = true; wrap_printf(4, " {"); if (ctx & CTX_RESOURCE) { wrap_printf(4, "%s", "|resource" + first); first = false; } if (ctx & CTX_MINOR) { wrap_printf(4, "%s", "|minor" + first); first = false; } if (ctx & CTX_ALL) { wrap_printf(4, "%s", "|all" + first); first = false; } wrap_printf(4, "}"); } if (ctx & CTX_CONNECTION) { wrap_printf(4, " [{af}:]{local_addr}[:{port}]"); wrap_printf(4, " [{af}:]{remote_addr}[:{port}]"); } } if (cm->drbd_args) { for (args = cm->drbd_args; args->name; args++) wrap_printf(4, " {%s}", args->name); } if (cm->options) { struct option *option; for (option = cm->options; option->name; option++) wrap_printf(4, " [--%s%s]", option->name, option->has_arg == no_argument ? "" : "=..."); } if (cm->set_defaults) wrap_printf(4, " [--set-defaults]"); if (cm->ctx) { struct field_def *field; for (field = cm->ctx->fields; field->name; field++) { char buffer[300]; int n; n = field->usage(field, buffer, sizeof(buffer)); assert(n < sizeof(buffer)); wrap_printf(4, " %s", buffer); } } wrap_printf(4, "\n"); } } static void print_usage_and_exit(const char *addinfo) { size_t i; printf("\nUSAGE: %s command device arguments options\n\n" "Device is usually /dev/drbdX or /dev/drbd/X.\n" "\nCommands are:\n",cmdname); for (i = 0; i < ARRAY_SIZE(commands); i++) print_command_usage(&commands[i], BRIEF); printf("\n\n" "To get more details about a command issue " "'drbdsetup help cmd'.\n" "\n"); /* printf("\n\nVersion: "PACKAGE_VERSION" (api:%d)\n%s\n", API_VERSION, drbd_buildtag()); */ if (addinfo) printf("\n%s\n", addinfo); exit(20); } static int modprobe_drbd(void) { struct stat sb; int ret, retries = 10; ret = stat("/proc/drbd", &sb); if (ret && errno == ENOENT && 0 == system("/sbin/modprobe drbd")) { for(;;) { struct timespec ts = { .tv_nsec = 1000000, }; ret = stat("/proc/drbd", &sb); if (!ret || retries-- == 0) break; nanosleep(&ts, NULL); } } if (ret) { fprintf(stderr, "Could not stat /proc/drbd: %m\n"); fprintf(stderr, "Make sure that the DRBD kernel module is installed " "and can be loaded!\n"); } return ret == 0; } void exec_legacy_drbdsetup(char **argv) { #ifdef DRBD_LEGACY_83 static const char * const legacy_drbdsetup = "drbdsetup-83"; char *progname, *drbdsetup; /* in case drbdsetup is called with an absolute or relative pathname * look for the v83 drbdsetup binary in the same location, * otherwise, just let execvp sort it out... */ if ((progname = strrchr(argv[0], '/')) == 0) { drbdsetup = strdup(legacy_drbdsetup); } else { size_t len_dir, l; ++progname; len_dir = progname - argv[0]; l = len_dir + strlen(legacy_drbdsetup) + 1; drbdsetup = malloc(l); if (!drbdsetup) { fprintf(stderr, "Malloc() failed\n"); exit(20); } strncpy(drbdsetup, argv[0], len_dir); strcpy(drbdsetup + len_dir, legacy_drbdsetup); } execvp(drbdsetup, argv); #else fprintf(stderr, "This drbdsetup was not built with support for drbd-8.3\n" "Consider to rebuild with ./configure --with-83-support\n"); #endif } int main(int argc, char **argv) { const struct drbd_cmd *cmd; struct option *options; int c, rv = 0; int longindex, first_optind; progname = basename(argv[0]); if (chdir("/")) { /* highly unlikely, but gcc is picky */ perror("cannot chdir /"); return -111; } cmdname = strrchr(argv[0],'/'); if (cmdname) argv[0] = ++cmdname; else cmdname = argv[0]; if (argc > 2 && (!strcmp(argv[2], "--help") || !strcmp(argv[2], "-h"))) { char *swap = argv[1]; argv[1] = argv[2]; argv[2] = swap; } if (argc > 1 && (!strcmp(argv[1], "help") || !strcmp(argv[1], "xml-help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) { enum usage_type usage_type = !strcmp(argv[1], "xml-help") ? XML : FULL; if(argc > 2) { cmd = find_cmd_by_name(argv[2]); if(cmd) { print_command_usage(cmd, usage_type); exit(0); } else print_usage_and_exit("unknown command"); } else print_usage_and_exit(NULL); } /* * drbdsetup previously took the object to operate on as its first argument, * followed by the command. For backwards compatibility, still support his. */ if (argc >= 3 && !find_cmd_by_name(argv[1]) && find_cmd_by_name(argv[2])) { char *swap = argv[1]; argv[1] = argv[2]; argv[2] = swap; } if (argc < 2) print_usage_and_exit(NULL); cmd = find_cmd_by_name(argv[1]); if (!cmd) print_usage_and_exit("invalid command"); if (!modprobe_drbd()) { if (!strcmp(argv[0], "down") || !strcmp(argv[0], "secondary") || !strcmp(argv[0], "disconnect") || !strcmp(argv[0], "detach")) return 0; /* "down" succeeds even if drbd is missing */ return 20; } if (try_genl) { if (cmd->continuous_poll && kernel_older_than(2, 6, 23)) drbd_genl_family.nl_groups = -1; drbd_sock = genl_connect_to_family(&drbd_genl_family); if (!drbd_sock) { try_genl = 0; exec_legacy_drbdsetup(argv); /* Only reached in case exec() failed... */ fprintf(stderr, "Could not connect to 'drbd' generic netlink family\n"); return 20; } if (drbd_genl_family.version != API_VERSION || drbd_genl_family.hdrsize != sizeof(struct drbd_genlmsghdr)) { fprintf(stderr, "API mismatch!\n\t" "API version drbdsetup: %u kernel: %u\n\t" "header size drbdsetup: %u kernel: %u\n", API_VERSION, drbd_genl_family.version, (unsigned)sizeof(struct drbd_genlmsghdr), drbd_genl_family.hdrsize); return 20; } } /* Make argv[0] the command name so that getopt_long() will leave it in * the first position. */ argv++; argc--; options = make_longoptions(cmd); for (;;) { c = getopt_long(argc, argv, "(", options, &longindex); if (c == -1) break; if (c == '?' || c == ':') print_usage_and_exit(NULL); } /* All non-option arguments now are in argv[optind .. argc - 1]. */ first_optind = optind; context = 0; if (cmd->ctx_key & (CTX_MINOR | CTX_RESOURCE | CTX_ALL | CTX_RESOURCE_AND_CONNECTION)) { if (argc == optind && !(cmd->ctx_key & (CTX_RESOURCE_AND_CONNECTION | CTX_CONNECTION)) && (cmd->ctx_key & CTX_ALL)) { context |= CTX_ALL; /* assume "all" if no argument is given */ objname = "all"; } else { if (argc <= optind) { fprintf(stderr, "Missing first argument\n"); print_command_usage(cmd, FULL); exit(20); } objname = argv[optind++]; ensure_sanity_of_res_name(objname); if (!strcmp(objname, "all")) { if (!(cmd->ctx_key & CTX_ALL)) print_usage_and_exit("command does not accept argument 'all'"); context = CTX_ALL; } else if (cmd->ctx_key & CTX_MINOR) { minor = dt_minor_of_dev(objname); if (minor != -1U) context = CTX_MINOR; else if (!(cmd->ctx_key & (CTX_RESOURCE | CTX_RESOURCE_AND_CONNECTION))) { fprintf(stderr, "Cannot determine minor device number of " "device '%s'\n", objname); exit(20); } } /* It could have been "all", but was not. * It could have been a minor number (or device node name), but was not. * So it has to be a resource, * or a resource and possibly connection specification. * (CTX_CONNECTION alone will not enter this branch). */ if (!context) context = CTX_RESOURCE; } } if (cmd->ctx_key & (CTX_CONNECTION | CTX_RESOURCE_AND_CONNECTION)) { if (argc <= optind + 1) { fprintf(stderr, "Missing connection endpoint argument\n"); print_command_usage(cmd, FULL); exit(20); } opt_local_addr = argv[optind++]; opt_peer_addr = argv[optind++]; context |= CTX_CONNECTION; } /* Remove the options we have already processed from argv */ if (first_optind != optind) { int n; for (n = 0; n < argc - optind; n++) argv[first_optind + n] = argv[optind + n]; argc -= optind - first_optind; } if (objname == NULL) objname = "??"; if ((context & CTX_MINOR) && !cmd->lockless) lock_fd = dt_lock_drbd(minor); rv = cmd->function(cmd, argc, argv); if ((context & CTX_MINOR) && !cmd->lockless) dt_unlock_drbd(lock_fd); return rv; } #endif drbd-utils-8.9.10/user/v84/drbdsetup_colors.h0000644000175000017500000000135712466702074020721 0ustar apoikosapoikos#ifndef DRBDSETUP_COLORS_H #define DRBDSETUP_COLORS_H #include enum when_color { NEVER_COLOR = -1, AUTO_COLOR = 0, ALWAYS_COLOR = 1 }; extern enum when_color opt_color; extern const char *stop_color_code(void); extern const char *role_color_start(enum drbd_role, bool); extern const char *role_color_stop(enum drbd_role, bool); extern const char *cstate_color_start(enum drbd_conns); extern const char *cstate_color_stop(enum drbd_conns); extern const char *repl_state_color_start(enum drbd_conns); extern const char *repl_state_color_stop(enum drbd_conns); extern const char *disk_state_color_start(enum drbd_disk_state, bool); extern const char *disk_state_color_stop(enum drbd_disk_state, bool); #endif /* DRBDSETUP_COLORS_H */ drbd-utils-8.9.10/user/v84/drbdadm_scanner.fl0000644000175000017500000002300113002347271020601 0ustar apoikosapoikos%{ #include #include #include #include "drbdadm_parser.h" #include "drbdadm.h" #include "drbdtool_common.h" void long_string(char* text); void long_dqstring(char* text); void err_dqstring(char* text); #if 0 #define DP printf("'%s' ",yytext) #else #define DP #endif #define CP yylval.txt = strdup(yytext); yylval.rc = R_NO_CHECK #define RC(N) yylval.rc = R_ ## N #define YY_NO_INPUT 1 #define YY_NO_UNPUT 1 #ifndef YY_FLEX_SUBMINOR_VERSION #define MAX_INCLUDE_DEPTH 10 YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr = 0; #endif %} %option noyywrap %option nounput NUM [0-9]{1,8}[MKGs]? SNUMB [0-9]{1,3} IPV4ADDR ({SNUMB}"."){3}{SNUMB} HEX4 [0-9a-fA-F]{1,4} IPV6ADDR ((({HEX4}":"){0,5}{HEX4})?":"{HEX4}?":"({HEX4}(":"{HEX4}){0,5})?("%"{STRING})?)|("::"[fF]{4}":"{IPV4ADDR}) WS [ \t\r] OPCHAR [{};\[\]:] DQSTRING \"([^\"\\\n]|\\[^\n]){0,255}\" LONG_DQSTRING \"([^\"\\\n]|\\[^\n]){255}. ERR_DQSTRING \"([^\"\\\n]|\\[^\n]){0,255}[\\\n] STRING [a-zA-Z0-9/._-]{1,128} LONG_STRING [a-zA-Z0-9/._-]{129} %% \n { line++; } \#.* /* ignore comments */ {WS} /* ignore whitespaces */ {OPCHAR} { DP; return yytext[0]; } on { DP; return TK_ON; } ignore-on { DP; return TK_IGNORE; } stacked-on-top-of { DP; return TK_STACKED; } floating { DP; return TK_FLOATING; } no { DP; return TK_NO; } net { DP; return TK_NET; } yes { DP; return TK_YES; } ask { DP; return TK_ASK; } skip { DP; return TK_SKIP; } disk { DP; return TK_DISK; } proxy { DP; return TK_PROXY; } minor { DP; return TK_MINOR; } inside { DP; return TK_INSIDE; } volume { DP; return TK_VOLUME; } cmd-timeout-short { DP; return TK_CMD_TIMEOUT_SHORT; } cmd-timeout-medium { DP; return TK_CMD_TIMEOUT_MEDIUM; } cmd-timeout-long { DP; return TK_CMD_TIMEOUT_LONG; } syncer { DP; return TK_SYNCER; } device { DP; return TK_DEVICE; } global { DP; return TK_GLOBAL; } common { DP; return TK_COMMON; } options { DP; return TK_OPTIONS; } outside { DP; return TK_OUTSIDE; } address { DP; return TK_ADDRESS; } alternate-link-address { DP; return TK_ALT_ADDRESS; } startup { DP; return TK_STARTUP; } include { DP; return TK_INCLUDE; } handlers { DP; return TK_HANDLER; } minor-count { DP; return TK_MINOR_COUNT; } disable-ip-verification { DP; return TK_DISABLE_IP_VERIFICATION;} dialog-refresh { DP; return TK_DIALOG_REFRESH; } resource { DP; return TK_RESOURCE; } meta-disk { DP; return TK_META_DISK; } flexible-meta-disk { DP; return TK_FLEX_META_DISK; } usage-count { DP; return TK_USAGE_COUNT; } _this_host { DP; return TK__THIS_HOST; } _remote_host { DP; return TK__REMOTE_HOST; } sci { DP; CP; return TK_SCI; } ssocks { DP; CP; return TK_SSOCKS; } sdp { DP; CP; return TK_SDP; } ipv4 { DP; CP; return TK_IPV4; } ipv6 { DP; CP; return TK_IPV6; } size { DP; CP; RC(DISK_SIZE); return TK_DISK_OPTION; } on-io-error { DP; CP; return TK_DISK_OPTION; } fencing { DP; CP; return TK_DISK_OPTION; } max-bio-bvecs { DP; CP; return TK_DISK_OPTION; } disk-timeout { DP; CP; return TK_DISK_OPTION; } read-balancing { DP; CP; return TK_DISK_OPTION; } rs-discard-granularity { DP; CP; return TK_DISK_OPTION; } use-bmbv { DP; CP; return TK_DISK_FLAG; } disk-barrier { DP; CP; return TK_DISK_FLAG; } disk-flushes { DP; CP; return TK_DISK_FLAG; } disk-drain { DP; CP; return TK_DISK_FLAG; } md-flushes { DP; CP; return TK_DISK_FLAG; } no-disk-barrier { DP; CP; return TK_DISK_NO_FLAG; } no-disk-flushes { DP; CP; return TK_DISK_NO_FLAG; } no-disk-drain { DP; CP; return TK_DISK_NO_FLAG; } no-md-flushes { DP; CP; return TK_DISK_NO_FLAG; } timeout { DP; CP; RC(TIMEOUT); return TK_NET_OPTION; } protocol { DP; CP; RC(PROTOCOL); return TK_NET_OPTION; } ko-count { DP; CP; RC(KO_COUNT); return TK_NET_OPTION; } ping-int { DP; CP; RC(PING_INT); return TK_NET_OPTION; } max-buffers { DP; CP; RC(MAX_BUFFERS); return TK_NET_OPTION;} sndbuf-size { DP; CP; RC(SNDBUF_SIZE); return TK_NET_OPTION | TK_PROXY_GROUP;} rcvbuf-size { DP; CP; RC(RCVBUF_SIZE); return TK_NET_OPTION | TK_PROXY_GROUP;} connect-int { DP; CP; RC(CONNECT_INT); return TK_NET_OPTION;} cram-hmac-alg { DP; CP; return TK_NET_OPTION; } shared-secret { DP; CP; return TK_NET_OPTION; } max-epoch-size { DP; CP; RC(MAX_EPOCH_SIZE); return TK_NET_OPTION;} after-sb-[012]pri { DP; CP; return TK_NET_OPTION; } rr-conflict { DP; CP; return TK_NET_OPTION; } ping-timeout { DP; CP; return TK_NET_OPTION | TK_PROXY_GROUP;} unplug-watermark { DP; CP; return TK_NET_OPTION; } data-integrity-alg { DP; CP; return TK_NET_OPTION; } on-congestion { DP; CP; return TK_NET_OPTION; } socket-check-timeout { DP; CP; return TK_NET_OPTION; } congestion-fill { DP; CP; RC(CONG_FILL); return TK_NET_OPTION; } congestion-extents { DP; CP; RC(CONG_EXTENTS); return TK_NET_OPTION;} allow-two-primaries { DP; CP; return TK_NET_FLAG; } always-asbp { DP; CP; return TK_NET_FLAG; } no-tcp-cork { DP; CP; return TK_NET_NO_FLAG; } tcp-cork { DP; CP; return TK_NET_FLAG; } discard-my-data { DP; CP; return TK_NET_DELEGATE; } rate { DP; CP; RC(RATE); return TK_SYNCER_OLD_OPT | TK_DISK_OPTION; } resync-rate { DP; CP; RC(RATE); return TK_DISK_OPTION; } after { DP; CP; return TK_SYNCER_OLD_OPT | TK_DISK_OPTION; } resync-after { DP; CP; return TK_DISK_OPTION; } verify-alg { DP; CP; return TK_SYNCER_OLD_OPT | TK_NET_OPTION; } csums-alg { DP; CP; return TK_SYNCER_OLD_OPT | TK_NET_OPTION; } csums-after-crash-only { DP; CP; return TK_NET_FLAG; } al-extents { DP; CP; RC(AL_EXTENTS); return TK_SYNCER_OLD_OPT | TK_DISK_OPTION;} al-updates { DP; CP; return TK_DISK_FLAG; } discard-zeroes-if-aligned { DP; CP; return TK_DISK_FLAG; } cpu-mask { DP; CP; return TK_SYNCER_OLD_OPT | TK_RES_OPTION; } use-rle { DP; CP; return TK_SYNCER_OLD_OPT | TK_NET_FLAG; } delay-probe-volume { DP; CP; return TK_DEPRECATED_OPTION; } delay-probe-interval { DP; CP; return TK_DEPRECATED_OPTION; } c-plan-ahead { DP; CP; RC(C_PLAN_AHEAD); return TK_SYNCER_OLD_OPT | TK_DISK_OPTION; } c-delay-target { DP; CP; RC(C_DELAY_TARGET); return TK_SYNCER_OLD_OPT | TK_DISK_OPTION; } c-fill-target { DP; CP; RC(C_FILL_TARGET); return TK_SYNCER_OLD_OPT | TK_DISK_OPTION; } c-max-rate { DP; CP; RC(C_MAX_RATE); return TK_SYNCER_OLD_OPT | TK_DISK_OPTION; } c-min-rate { DP; CP; RC(C_MIN_RATE); return TK_SYNCER_OLD_OPT | TK_DISK_OPTION; } throttle-threshold { DP; CP; return TK_DEPRECATED_OPTION; } hold-off-threshold { DP; CP; return TK_DEPRECATED_OPTION; } on-no-data-accessible { DP; CP; return TK_SYNCER_OLD_OPT | TK_RES_OPTION; } wfc-timeout { DP; CP; RC(WFC_TIMEOUT); return TK_STARTUP_OPTION;} degr-wfc-timeout { DP; CP; RC(DEGR_WFC_TIMEOUT); return TK_STARTUP_OPTION;} outdated-wfc-timeout { DP; CP; RC(OUTDATED_WFC_TIMEOUT); return TK_STARTUP_OPTION;} stacked-timeouts { DP; return TK_STARTUP_DELEGATE; } become-primary-on { DP; return TK_STARTUP_DELEGATE; } wait-after-sb { DP; CP; return TK_STARTUP_FLAG; } pri-on-incon-degr { DP; CP; return TK_HANDLER_OPTION; } pri-lost-after-sb { DP; CP; return TK_HANDLER_OPTION; } pri-lost { DP; CP; return TK_HANDLER_OPTION; } initial-split-brain { DP; CP; return TK_HANDLER_OPTION; } split-brain { DP; CP; return TK_HANDLER_OPTION; } outdate-peer { DP; CP; return TK_HANDLER_OPTION; } fence-peer { DP; CP; return TK_HANDLER_OPTION; } unfence-peer { DP; CP; return TK_HANDLER_OPTION; } local-io-error { DP; CP; return TK_HANDLER_OPTION; } before-resync-target { DP; CP; return TK_HANDLER_OPTION; } after-resync-target { DP; CP; return TK_HANDLER_OPTION; } before-resync-source { DP; CP; return TK_HANDLER_OPTION; } memlimit { DP; CP; return TK_PROXY_OPTION | TK_PROXY_GROUP; } read-loops { DP; CP; return TK_PROXY_OPTION | TK_PROXY_GROUP; } compression { DP; CP; return TK_PROXY_OPTION | TK_PROXY_GROUP; } bwlimit { DP; CP; return TK_PROXY_OPTION | TK_PROXY_GROUP; } plugin { DP; CP; return TK_PROXY_DELEGATE; } out-of-sync { DP; CP; return TK_HANDLER_OPTION; } {IPV4ADDR} { DP; CP; return TK_IPADDR; } {IPV6ADDR} { DP; CP; return TK_IPADDR6; } {NUM} { DP; CP; return TK_INTEGER; } {DQSTRING} { unescape(yytext); DP; CP; return TK_STRING; } {STRING} { DP; CP; return TK_STRING; } {LONG_STRING} { return TK_ERR_STRING_TOO_LONG; } {LONG_DQSTRING} { return TK_ERR_DQSTRING_TOO_LONG; } {ERR_DQSTRING} { return TK_ERR_DQSTRING; } . { DP; return TK_ELSE; } %% /* Compatibility cruft for flex version 2.5.4a */ #ifndef YY_FLEX_SUBMINOR_VERSION /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void yypush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) { fprintf( stderr, "Includes nested too deeply" ); exit( 1 ); } include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER; yy_switch_to_buffer(new_buffer); BEGIN(INITIAL); } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void yypop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; if ( --include_stack_ptr < 0 ) { fprintf( stderr, "error in flex compat code\n" ); exit( 1 ); } yy_delete_buffer(YY_CURRENT_BUFFER ); yy_switch_to_buffer(include_stack[include_stack_ptr]); } #endif void my_yypush_buffer_state(FILE *f) { /* Since we do not have YY_BUF_SIZE outside of the flex generated file.*/ yypush_buffer_state(yy_create_buffer(f, YY_BUF_SIZE)); } drbd-utils-8.9.10/user/v84/drbdadm_parser.c0000644000175000017500000015144713016771235020313 0ustar apoikosapoikos/* * drbdadm_parser.c a hand crafted parser This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2006-2008, LINBIT Information Technologies GmbH Copyright (C) 2006-2008, Philipp Reisner Copyright (C) 2006-2008, Lars Ellenberg drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #define _XOPEN_SOURCE 600 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include "drbdadm.h" #include "linux/drbd_limits.h" #include "drbdtool_common.h" #include "drbdadm_parser.h" #include "shared_parser.h" YYSTYPE yylval; ///////////////////// static int c_section_start; static int parse_proxy_options(struct d_option **, struct d_option **); void my_parse(void); struct d_name *names_from_str(char* str) { struct d_name *names; names = malloc(sizeof(struct d_name)); names->next = NULL; names->name = strdup(str); return names; } char *_names_to_str_c(char* buffer, struct d_name *names, char c) { int n = 0; if (!names) { snprintf(buffer, NAMES_STR_SIZE, "UNKNOWN"); return buffer; } while (1) { n += snprintf(buffer + n, NAMES_STR_SIZE - n, "%s", names->name); names = names->next; if (!names) return buffer; n += snprintf(buffer + n, NAMES_STR_SIZE - n, "%c", c); } } char *_names_to_str(char* buffer, struct d_name *names) { return _names_to_str_c(buffer, names, ' '); } int name_in_names(char *name, struct d_name *names) { while (names) { if (!strcmp(names->name, name)) return 1; names = names->next; } return 0; } void free_names(struct d_name *names) { struct d_name *nf; while (names) { nf = names->next; free(names->name); free(names); names = nf; } } static void append_names(struct d_name **head, struct d_name ***last, struct d_name *to_copy) { struct d_name *new; while (to_copy) { new = malloc(sizeof(struct d_name)); if (!*head) *head = new; new->name = strdup(to_copy->name); new->next = NULL; if (*last) **last = new; *last = &new->next; to_copy = to_copy->next; } } struct d_name *concat_names(struct d_name *to_copy1, struct d_name *to_copy2) { struct d_name *head = NULL, **last = NULL; append_names(&head, &last, to_copy1); append_names(&head, &last, to_copy2); return head; } void m_strtoll_range(const char *s, char def_unit, const char *name, unsigned long long min, unsigned long long max) { unsigned long long r = m_strtoll(s, def_unit); char unit[] = { def_unit != '1' ? def_unit : 0, 0 }; if (min > r || r > max) { err("%s:%d: %s %s => %llu%s out of range [%llu..%llu]%s.\n", config_file, fline, name, s, r, unit, min, max, unit); if (config_valid <= 1) { config_valid = 0; return; } } if (DEBUG_RANGE_CHECK) { err("%s:%d: %s %s => %llu%s in range [%llu..%llu]%s.\n", config_file, fline, name, s, r, unit, min, max, unit); } } void range_check(const enum range_checks what, const char *name, char *value) { char proto = 0; /* * FIXME: Handle signed/unsigned values correctly by checking the * F_field_name_IS_SIGNED defines. */ #define M_STRTOLL_RANGE(x) \ m_strtoll_range(value, DRBD_ ## x ## _SCALE, name, \ DRBD_ ## x ## _MIN, \ DRBD_ ## x ## _MAX) switch (what) { case R_NO_CHECK: break; default: err("%s:%d: unknown range for %s => %s\n", config_file, fline, name, value); break; case R_MINOR_COUNT: M_STRTOLL_RANGE(MINOR_COUNT); break; case R_DIALOG_REFRESH: M_STRTOLL_RANGE(DIALOG_REFRESH); break; case R_DISK_SIZE: M_STRTOLL_RANGE(DISK_SIZE); break; case R_TIMEOUT: M_STRTOLL_RANGE(TIMEOUT); break; case R_CONNECT_INT: M_STRTOLL_RANGE(CONNECT_INT); break; case R_PING_INT: M_STRTOLL_RANGE(PING_INT); break; case R_MAX_BUFFERS: M_STRTOLL_RANGE(MAX_BUFFERS); break; case R_MAX_EPOCH_SIZE: M_STRTOLL_RANGE(MAX_EPOCH_SIZE); break; case R_SNDBUF_SIZE: M_STRTOLL_RANGE(SNDBUF_SIZE); break; case R_RCVBUF_SIZE: M_STRTOLL_RANGE(RCVBUF_SIZE); break; case R_KO_COUNT: M_STRTOLL_RANGE(KO_COUNT); break; case R_RATE: M_STRTOLL_RANGE(RESYNC_RATE); break; case R_AL_EXTENTS: /* ignore; auto-clamped by kernel. * M_STRTOLL_RANGE(AL_EXTENTS); */ break; case R_PORT: M_STRTOLL_RANGE(PORT); break; /* FIXME not yet implemented! case R_META_IDX: M_STRTOLL_RANGE(META_IDX); break; */ case R_WFC_TIMEOUT: M_STRTOLL_RANGE(WFC_TIMEOUT); break; case R_DEGR_WFC_TIMEOUT: M_STRTOLL_RANGE(DEGR_WFC_TIMEOUT); break; case R_OUTDATED_WFC_TIMEOUT: M_STRTOLL_RANGE(OUTDATED_WFC_TIMEOUT); break; case R_C_PLAN_AHEAD: M_STRTOLL_RANGE(C_PLAN_AHEAD); break; case R_C_DELAY_TARGET: M_STRTOLL_RANGE(C_DELAY_TARGET); break; case R_C_FILL_TARGET: M_STRTOLL_RANGE(C_FILL_TARGET); break; case R_C_MAX_RATE: M_STRTOLL_RANGE(C_MAX_RATE); break; case R_C_MIN_RATE: M_STRTOLL_RANGE(C_MIN_RATE); break; case R_CONG_FILL: M_STRTOLL_RANGE(CONG_FILL); break; case R_CONG_EXTENTS: M_STRTOLL_RANGE(CONG_EXTENTS); break; case R_PROTOCOL: if (value && value[0] && value[1] == 0) { proto = value[0] & ~0x20; /* toupper */ if (proto == 'A' || proto == 'B' || proto == 'C') value[0] = proto; else proto = 0; } if (!proto && config_valid <= 1) { config_valid = 0; err("unknown protocol '%s', should be one of A,B,C\n", value); } break; } } struct d_option *new_opt(char *name, char *value) { struct d_option *cn = calloc(1, sizeof(struct d_option)); /* err("%s:%d: %s = %s\n",config_file,line,name,value); */ cn->name = name; cn->value = value; return cn; } static void derror(struct d_host_info *host, struct d_resource *res, char *text) { config_valid = 0; err("%s:%d: in resource %s, on %s { ... }:" " '%s' keyword missing.\n", config_file, c_section_start, res->name, names_to_str(host->on_hosts), text); } void pdperror(char *text) { config_valid = 0; err("%s:%d: in proxy plugin section: %s.\n", config_file, line, text); exit(E_CONFIG_INVALID); } static void pperror(struct d_host_info *host, struct d_proxy_info *proxy, char *text) { config_valid = 0; err("%s:%d: in section: on %s { proxy on %s { ... } }: '%s' keyword missing.\n", config_file, c_section_start, names_to_str(host->on_hosts), names_to_str(proxy->on_hosts), text); } #define typecheck(type,x) \ ({ type __dummy; \ typeof(x) __dummy2; \ (void)(&__dummy == &__dummy2); \ 1; \ }) #define for_each_host(h_,hosts_) \ for ( ({ typecheck(struct d_name*, h_); \ h_ = hosts_; }); \ h_; h_ = h_->next) /* * for check_uniq: check uniqueness of * resource names, ip:port, node:disk and node:device combinations * as well as resource:section ... * hash table to test for uniqueness of these values... * 256 (max minors) * *( * 2 (host sections) * 4 (res ip:port node:disk node:device) * + 4 (other sections) * + some more, * if we want to check for scoped uniqueness of *every* option * ) * since nobody (?) will actually use more than a dozen minors, * this should be more than enough. * * Furthermore, the names of files that have been read are * registered here, to avoid reading the same file multiple times. */ struct hsearch_data global_htable; void check_uniq_init(void) { memset(&global_htable, 0, sizeof(global_htable)); if (!hcreate_r(256 * ((2 * 4) + 4), &global_htable)) { err("Insufficient memory.\n"); exit(E_EXEC_ERROR); }; } /* some settings need only be unique within one resource definition. * we need currently about 8 + (number of host) * 8 entries, * 200 should be much more than enough. */ struct hsearch_data per_resource_htable; void check_upr_init(void) { static int created = 0; if (config_valid >= 2) return; if (created) hdestroy_r(&per_resource_htable); memset(&per_resource_htable, 0, sizeof(per_resource_htable)); if (!hcreate_r(256, &per_resource_htable)) { err("Insufficient memory.\n"); exit(E_EXEC_ERROR); }; created = 1; } /* FIXME * strictly speaking we don't need to check for uniqueness of disk and device names, * but for uniqueness of their major:minor numbers ;-) */ int vcheck_uniq(struct hsearch_data *ht, const char *what, const char *fmt, va_list ap) { int rv; ENTRY e, *ep; e.key = e.data = ep = NULL; /* if we are done parsing the config file, * switch off this paranoia */ if (config_valid >= 2) return 1; rv = vasprintf(&e.key, fmt, ap); if (rv < 0) { err("vasprintf: %m\n"); exit(E_THINKO); } if (EXIT_ON_CONFLICT && !what) { err("Oops, unset argument in %s:%d.\n", __FILE__, __LINE__); exit(E_THINKO); } m_asprintf((char **)&e.data, "%s:%u", config_file, fline); hsearch_r(e, FIND, &ep, ht); //err("FIND %s: %p\n", e.key, ep); if (ep) { if (what) { err("%s: conflicting use of %s '%s' ...\n%s: %s '%s' first used here.\n", (char *)e.data, what, ep->key, (char *)ep->data, what, ep->key); } free(e.key); free(e.data); config_valid = 0; } else { //err("ENTER %s\t=>\t%s\n", e.key, (char *)e.data); hsearch_r(e, ENTER, &ep, ht); if (!ep) { err("hash table entry (%s => %s) failed\n", e.key, (char *)e.data); exit(E_THINKO); } ep = NULL; } if (EXIT_ON_CONFLICT && ep) exit(E_CONFIG_INVALID); return !ep; } void check_meta_disk(struct d_volume *vol, struct d_host_info *host) { struct d_name *h; /* when parsing "drbdsetup show[-all]" output, * a detached volume will only have device/minor, * but no disk or meta disk. */ if (vol->meta_disk == NULL) return; if (strcmp(vol->meta_disk, "internal") != 0) { /* index either some number, or "flexible" */ for_each_host(h, host->on_hosts) check_uniq("meta-disk", "%s:%s[%s]", h->name, vol->meta_disk, vol->meta_index); } } static void pe_expected(const char *exp) { const char *s = yytext; err("%s:%u: Parse error: '%s' expected,\n\tbut got '%.20s%s'\n", config_file, line, exp, s, strlen(s) > 20 ? "..." : ""); exit(E_CONFIG_INVALID); } static void check_string_error(int got) { const char *msg; switch(got) { case TK_ERR_STRING_TOO_LONG: msg = "Token too long"; break; case TK_ERR_DQSTRING_TOO_LONG: msg = "Double quoted string too long"; break; case TK_ERR_DQSTRING: msg = "Unterminated double quoted string\n we don't allow embedded newlines\n "; break; default: return; } err("%s:%u: %s >>>%.20s...<<<\n", config_file, line, msg, yytext); exit(E_CONFIG_INVALID); } static void pe_expected_got(const char *exp, int got) { static char tmp[2] = "\0"; const char *s = yytext; if (exp[0] == '\'' && exp[1] && exp[2] == '\'' && exp[3] == 0) { tmp[0] = exp[1]; } err("%s:%u: Parse error: '%s' expected,\n\tbut got '%.20s%s' (TK %d)\n", config_file, line, tmp[0] ? tmp : exp, s, strlen(s) > 20 ? "..." : "", got); exit(E_CONFIG_INVALID); } #define EXP(TOKEN1) \ ({ \ int token; \ token = yylex(); \ if (token != TOKEN1) { \ if (TOKEN1 == TK_STRING) \ check_string_error(token); \ pe_expected_got( #TOKEN1, token); \ } \ token; \ }) static void expect_STRING_or_INT(void) { int token = yylex(); switch(token) { case TK_INTEGER: case TK_STRING: break; case TK_ON: yylval.txt = strdup(yytext); break; default: check_string_error(token); pe_expected_got("TK_STRING | TK_INTEGER", token); } } static void parse_global(void) { fline = line; check_uniq("global section", "global"); if (config) { err("%s:%u: You should put the global {} section\n\tin front of any resource {} section\n", config_file, line); } EXP('{'); while (1) { int token = yylex(); fline = line; switch (token) { case TK_DISABLE_IP_VERIFICATION: global_options.disable_ip_verification = 1; break; case TK_MINOR_COUNT: EXP(TK_INTEGER); range_check(R_MINOR_COUNT, "minor-count", yylval.txt); global_options.minor_count = atoi(yylval.txt); break; case TK_DIALOG_REFRESH: EXP(TK_INTEGER); range_check(R_DIALOG_REFRESH, "dialog-refresh", yylval.txt); global_options.dialog_refresh = atoi(yylval.txt); break; case TK_CMD_TIMEOUT_SHORT: EXP(TK_INTEGER); m_strtoll_range(yylval.txt, '1', "cmd-timeout-short", 0, 900); global_options.cmd_timeout_short = atoi(yylval.txt); break; case TK_CMD_TIMEOUT_MEDIUM: EXP(TK_INTEGER); m_strtoll_range(yylval.txt, '1', "cmd-timeout-medium", 0, 900); global_options.cmd_timeout_medium = atoi(yylval.txt); break; case TK_CMD_TIMEOUT_LONG: EXP(TK_INTEGER); m_strtoll_range(yylval.txt, '1', "cmd-timeout-long", 0, 900); global_options.cmd_timeout_long = atoi(yylval.txt); break; case TK_USAGE_COUNT: switch (yylex()) { case TK_YES: global_options.usage_count = UC_YES; break; case TK_NO: global_options.usage_count = UC_NO; break; case TK_ASK: global_options.usage_count = UC_ASK; break; default: pe_expected("yes | no | ask"); } break; case '}': return; default: pe_expected("dialog-refresh | minor-count | " "disable-ip-verification"); } EXP(';'); } } static void check_and_change_deprecated_alias(char **name, int token) { int i; static struct { enum yytokentype token; char *old_name, *new_name; } table[] = { { TK_HANDLER_OPTION, "outdate-peer", "fence-peer" }, { TK_DISK_OPTION, "rate", "resync-rate" }, { TK_DISK_OPTION, "after", "resync-after" }, }; for (i = 0; i < ARRAY_SIZE(table); i++) { if (table[i].token == token && !strcmp(table[i].old_name, *name)) { free(*name); *name = strdup(table[i].new_name); } } } /* The syncer section is deprecated. Distribute the options to the disk or net options. */ void parse_options_syncer(struct d_resource *res) { char *opt_name; int token; enum range_checks rc; struct d_option **options = NULL, *current_option = NULL; c_section_start = line; fline = line; while (1) { token = yylex(); fline = line; if (token >= TK_GLOBAL && !(token & TK_SYNCER_OLD_OPT)) pe_expected("a syncer option keyword"); token &= ~TK_SYNCER_OLD_OPT; switch (token) { case TK_NET_FLAG: case TK_NET_NO_FLAG: case TK_NET_OPTION: options = &res->net_options; break; case TK_DISK_FLAG: case TK_DISK_NO_FLAG: case TK_DISK_OPTION: options = &res->disk_options; break; case TK_RES_OPTION: options = &res->res_options; break; case '}': return; default: pe_expected("a syncer option keyword"); } opt_name = yylval.txt; switch (token) { case TK_NET_FLAG: case TK_DISK_FLAG: token = yylex(); switch(token) { case TK_NO: current_option = new_opt(opt_name, strdup("no")); *options = APPEND(*options, current_option); token = yylex(); break; default: current_option = new_opt(opt_name, strdup("yes")); *options = APPEND(*options, current_option); if (token == TK_YES) token = yylex(); break; } break; case TK_NET_NO_FLAG: case TK_DISK_NO_FLAG: /* Backward compatibility with the old config file syntax. */ assert(!strncmp(opt_name, "no-", 3)); current_option = new_opt(strdup(opt_name + 3), strdup("no")); *options = APPEND(*options, current_option); free(opt_name); token = yylex(); break; case TK_NET_OPTION: case TK_DISK_OPTION: case TK_RES_OPTION: check_and_change_deprecated_alias(&opt_name, token); rc = yylval.rc; expect_STRING_or_INT(); range_check(rc, opt_name, yylval.txt); current_option = new_opt(opt_name, yylval.txt); *options = APPEND(*options, current_option); token = yylex(); break; } switch (token) { case ';': break; default: pe_expected(";"); } } } static struct d_option *parse_options_d(int token_flag, int token_no_flag, int token_option, int token_delegate, void (*delegate)(void*), void *ctx) { char *opt_name; int token, token_group; enum range_checks rc; struct d_option *options = NULL, *current_option = NULL; c_section_start = line; fline = line; while (1) { token_group = yylex(); /* Keep the higher bits in token_option, remove them from token. */ token = REMOVE_GROUP_FROM_TOKEN(token_group); fline = line; opt_name = yylval.txt; if (token <= 0) { pe_expected("an option"); } else if (token == token_flag) { switch(yylex()) { case TK_YES: current_option = new_opt(opt_name, strdup("yes")); options = APPEND(options, current_option); break; case TK_NO: current_option = new_opt(opt_name, strdup("no")); options = APPEND(options, current_option); break; case ';': /* Flag value missing; assume yes. */ options = APPEND(options, new_opt(opt_name, strdup("yes"))); continue; default: pe_expected("yes | no | ;"); } } else if (token == token_no_flag) { /* Backward compatibility with the old config file syntax. */ assert(!strncmp(opt_name, "no-", 3)); current_option = new_opt(strdup(opt_name + 3), strdup("no")); options = APPEND(options, current_option); free(opt_name); } else if (token == token_option || GET_TOKEN_GROUP(token_option & token_group)) { check_and_change_deprecated_alias(&opt_name, token_option); rc = yylval.rc; expect_STRING_or_INT(); range_check(rc, opt_name, yylval.txt); current_option = new_opt(opt_name, yylval.txt); options = APPEND(options, current_option); } else if (ctx && (token == token_delegate || GET_TOKEN_GROUP(token_delegate & token_group))) { delegate(ctx); continue; } else if (token == TK_DEPRECATED_OPTION) { /* err("Warn: Ignoring deprecated option '%s'\n", yylval.txt); */ expect_STRING_or_INT(); } else if (token == '}') { return options; } else { pe_expected("an option keyword"); } EXP(';'); } } static struct d_option *parse_options(int token_flag, int token_no_flag, int token_option) { return parse_options_d(token_flag, token_no_flag, token_option, 0, NULL, NULL); } static void __parse_address(char** addr, char** port, char** af) { switch(yylex()) { case TK_SCI: /* 'ssocks' was names 'sci' before. */ if (af) *af = strdup("ssocks"); EXP(TK_IPADDR); break; case TK_SSOCKS: case TK_SDP: case TK_IPV4: if (af) *af = yylval.txt; EXP(TK_IPADDR); break; case TK_IPV6: if (af) *af = yylval.txt; EXP('['); EXP(TK_IPADDR6); break; case TK_IPADDR: if (af) *af = strdup("ipv4"); break; /* case '[': // Do not foster people's laziness ;) EXP(TK_IPADDR6); *af = strdup("ipv6"); break; */ default: pe_expected("ssocks | sdp | ipv4 | ipv6 | "); } if (addr) *addr = yylval.txt; if (af && !strcmp(*af, "ipv6")) EXP(']'); EXP(':'); EXP(TK_INTEGER); if (port) *port = yylval.txt; range_check(R_PORT, "port", yylval.txt); } static void parse_address(struct d_name *on_hosts, char** addr, char** port, char** af) { struct d_name *h; __parse_address(addr, port, af); if (addr_scope_local(*addr)) for_each_host(h, on_hosts) check_uniq("IP", "%s:%s:%s", h->name, *addr, *port); else check_uniq("IP", "%s:%s", *addr, *port); EXP(';'); } static void parse_hosts(struct d_name **pnp, char delimeter) { char errstr[20]; struct d_name *name; int hosts = 0; int token; while (1) { token = yylex(); switch (token) { case TK_STRING: name = malloc(sizeof(struct d_name)); name->name = yylval.txt; name->next = NULL; *pnp = name; pnp = &name->next; hosts++; break; default: if (token == delimeter) { if (!hosts) pe_expected_got("TK_STRING", token); return; } else { sprintf(errstr, "TK_STRING | '%c'", delimeter); pe_expected_got(errstr, token); } } } } static void parse_proxy_section(struct d_host_info *host) { struct d_proxy_info *proxy; proxy=calloc(1,sizeof(struct d_proxy_info)); host->proxy = proxy; EXP(TK_ON); parse_hosts(&proxy->on_hosts, '{'); while (1) { switch (yylex()) { case TK_INSIDE: parse_address(proxy->on_hosts, &proxy->inside_addr, &proxy->inside_port, &proxy->inside_af); break; case TK_OUTSIDE: parse_address(proxy->on_hosts, &proxy->outside_addr, &proxy->outside_port, &proxy->outside_af); break; case TK_OPTIONS: parse_proxy_options(&proxy->options, &proxy->plugins); break; case '}': goto break_loop; default: pe_expected("inside | outside"); } } break_loop: if (!proxy->inside_addr) pperror(host, proxy, "inside"); if (!proxy->outside_addr) pperror(host, proxy, "outside"); return; } void parse_meta_disk(struct d_volume *vol) { EXP(TK_STRING); vol->meta_disk = yylval.txt; if (strcmp("internal", yylval.txt) == 0) { /* internal, flexible size */ vol->meta_index = strdup("internal"); EXP(';'); } else { switch(yylex()) { case '[': EXP(TK_INTEGER); /* external, static size */ vol->meta_index = yylval.txt; EXP(']'); EXP(';'); break; case ';': /* external, flexible size */ vol->meta_index = strdup("flexible"); break; default: pe_expected("[ | ;"); } } } static void check_minor_nonsense(const char *devname, const int explicit_minor) { if (!devname) return; /* if devname is set, it starts with /dev/drbd */ if (only_digits(devname + 9)) { int m = strtol(devname + 9, NULL, 10); if (m == explicit_minor) return; err("%s:%d: explicit minor number must match with device name\n" "\tTry \"device /dev/drbd%u minor %u;\",\n" "\tor leave off either device name or explicit minor.\n" "\tArbitrary device names must start with /dev/drbd_\n" "\tmind the '_'! (/dev/ is optional, but drbd_ is required)\n", config_file, fline, explicit_minor, explicit_minor); config_valid = 0; return; } else if (devname[9] == '_') return; err("%s:%d: arbitrary device name must start with /dev/drbd_\n" "\tmind the '_'! (/dev/ is optional, but drbd_ is required)\n", config_file, fline); config_valid = 0; return; } static void parse_device(struct d_name* on_hosts, struct d_volume *vol) { struct d_name *h; int m; switch (yylex()) { case TK_STRING: if (!strncmp("drbd", yylval.txt, 4)) { m_asprintf(&vol->device, "/dev/%s", yylval.txt); free(yylval.txt); } else vol->device = yylval.txt; if (strncmp("/dev/drbd", vol->device, 9)) { err("%s:%d: device name must start with /dev/drbd\n" "\t(/dev/ is optional, but drbd is required)\n", config_file, fline); config_valid = 0; /* no goto out yet, * as that would additionally throw a parse error */ } switch (yylex()) { default: pe_expected("minor | ;"); /* fall through */ case ';': m = dt_minor_of_dev(vol->device); if (m < 0) { err("%s:%d: no minor given nor device name contains a minor number\n", config_file, fline); config_valid = 0; } vol->device_minor = m; goto out; case TK_MINOR: ; /* double fall through */ } case TK_MINOR: EXP(TK_INTEGER); vol->device_minor = atoi(yylval.txt); EXP(';'); /* if both device name and minor number are explicitly given, * force /dev/drbd or /dev/drbd_ */ check_minor_nonsense(vol->device, vol->device_minor); } out: for_each_host(h, on_hosts) { check_uniq("device-minor", "device-minor:%s:%u", h->name, vol->device_minor); if (vol->device) check_uniq("device", "device:%s:%s", h->name, vol->device); } } struct d_volume *find_volume(struct d_volume *vol, int vnr) { while (vol) { if (vol->vnr == vnr) return vol; vol = vol->next; } return NULL; } struct d_volume *volume0(struct d_volume **volp) { struct d_volume *vol; if (!*volp) { vol = calloc(1, sizeof(struct d_volume)); vol->device_minor = -1; *volp = vol; vol->implicit = 1; return vol; } else { vol = *volp; if (vol->vnr == 0 && vol->next == NULL && vol->implicit) return vol; config_valid = 0; err("%s:%d: Explicit and implicit volumes not allowed\n", config_file, line); return vol; } } int parse_volume_stmt(struct d_volume *vol, struct d_name* on_hosts, int token) { switch (token) { case TK_DISK: token = yylex(); switch (token) { case TK_STRING: vol->disk = yylval.txt; EXP(';'); break; case '{': vol->disk_options = parse_options(TK_DISK_FLAG, TK_DISK_NO_FLAG, TK_DISK_OPTION); break; default: check_string_error(token); pe_expected_got( "TK_STRING | {", token); } break; case TK_DEVICE: parse_device(on_hosts, vol); break; case TK_META_DISK: parse_meta_disk(vol); break; case TK_FLEX_META_DISK: EXP(TK_STRING); vol->meta_disk = yylval.txt; if (strcmp("internal", yylval.txt) != 0) { /* external, flexible ize */ vol->meta_index = strdup("flexible"); } else { /* internal, flexible size */ vol->meta_index = strdup("internal"); } EXP(';'); break; default: return 0; } return 1; } struct d_volume *parse_volume(int vnr, struct d_name* on_hosts) { struct d_volume *vol; int token; vol = calloc(1,sizeof(struct d_volume)); vol->device_minor = -1; vol->vnr = vnr; EXP('{'); while (1) { token = yylex(); if (token == '}') break; if (!parse_volume_stmt(vol, on_hosts, token)) pe_expected_got("device | disk | meta-disk | flex-meta-disk | }", token); } return vol; } struct d_volume *parse_stacked_volume(int vnr) { struct d_volume *vol; vol = calloc(1,sizeof(struct d_volume)); vol->device_minor = -1; vol->vnr = vnr; EXP('{'); EXP(TK_DEVICE); parse_device(NULL, vol); EXP('}'); vol->meta_disk = strdup("internal"); vol->meta_index = strdup("internal"); return vol; } void inherit_volumes(struct d_volume *from, struct d_host_info *host) { struct d_volume *s, *t; struct d_name *h; for (s = from; s != NULL ; s = s->next) { t = find_volume(host->volumes, s->vnr); if (!t) { t = calloc(1, sizeof(struct d_volume)); t->device_minor = -1; t->vnr = s->vnr; host->volumes = INSERT_SORTED(host->volumes, t, vnr); } if (!t->disk && s->disk) { t->disk = strdup(s->disk); for_each_host(h, host->on_hosts) check_uniq("disk", "disk:%s:%s", h->name, t->disk); } if (!t->device && s->device) t->device = strdup(s->device); if (t->device_minor == -1U && s->device_minor != -1U) { t->device_minor = s->device_minor; for_each_host(h, host->on_hosts) check_uniq("device-minor", "device-minor:%s:%d", h->name, t->device_minor); } if (!t->meta_disk && s->meta_disk) { t->meta_disk = strdup(s->meta_disk); if (s->meta_index) t->meta_index = strdup(s->meta_index); } } } void check_volume_complete(struct d_resource *res, struct d_host_info *host, struct d_volume *vol) { if (!vol->device && vol->device_minor == -1U) derror(host, res, "device"); if (!vol->disk) derror(host, res, "disk"); if (!vol->meta_disk) derror(host, res, "meta-disk"); if (!vol->meta_index) derror(host, res, "meta-index"); } void check_volumes_complete(struct d_resource *res, struct d_host_info *host) { struct d_volume *vol = host->volumes; unsigned vnr = -1U; while (vol) { if (vnr == -1U || vnr < vol->vnr) vnr = vol->vnr; else err("internal error: in %s: unsorted volumes list\n", res->name); check_volume_complete(res, host, vol); vol = vol->next; } } void check_volume_sets_equal(struct d_resource *res, struct d_host_info *host1, struct d_host_info *host2) { struct d_volume *a, *b; /* change the error output, if we have been called to * compare stacked with lower resource volumes */ int compare_stacked = host1->lower && host1->lower->me == host2; a = host1->volumes; b = host2->volumes; /* volume lists are supposed to be sorted on vnr */ while (a || b) { while (a && (!b || a->vnr < b->vnr)) { err("%s:%d: in resource %s, on %s { ... }: volume %d not defined on %s\n", config_file, line, res->name, names_to_str(host1->on_hosts), a->vnr, compare_stacked ? host1->lower->name : names_to_str(host2->on_hosts)); a = a->next; config_valid = 0; } while (b && (!a || a->vnr > b->vnr)) { /* Though unusual, it is "legal" for a lower resource * to have more volumes than the resource stacked on * top of it. Warn (if we have a terminal), * but consider it as valid. */ if (!(compare_stacked && no_tty)) err("%s:%d: in resource %s, on %s { ... }: " "volume %d missing (present on %s)\n", config_file, line, res->name, names_to_str(host1->on_hosts), b->vnr, compare_stacked ? host1->lower->name : names_to_str(host2->on_hosts)); if (!compare_stacked) config_valid = 0; b = b->next; } if (a && b && a->vnr == b->vnr) { a = a->next; b = b->next; } } } /* Ensure that in all host sections the same volumes are defined */ void check_volumes_hosts(struct d_resource *res) { struct d_host_info *host1, *host2; host1 = res->all_hosts; if (!host1) return; for (host2 = host1->next; host2; host2 = host2->next) check_volume_sets_equal(res, host1, host2); } enum parse_host_section_flags { REQUIRE_ALL = 1, BY_ADDRESS = 2, }; void parse_host_section(struct d_resource *res, struct d_name* on_hosts, enum parse_host_section_flags flags) { struct d_host_info *host; struct d_volume *vol; struct d_name *h; int in_braces = 1; c_section_start = line; fline = line; host = calloc(1,sizeof(struct d_host_info)); host->on_hosts = on_hosts; host->config_line = c_section_start; if (flags & BY_ADDRESS) { /* floating
{} */ char *fake_uname = NULL; int token; host->by_address = 1; __parse_address(&host->address, &host->port, &host->address_family); check_uniq("IP", "%s:%s", host->address, host->port); if (!strcmp(host->address_family, "ipv6")) m_asprintf(&fake_uname, "ipv6 [%s]:%s", host->address, host->port); else m_asprintf(&fake_uname, "%s:%s", host->address, host->port); on_hosts = names_from_str(fake_uname); host->on_hosts = on_hosts; token = yylex(); switch(token) { case '{': break; case ';': in_braces = 0; break; default: pe_expected_got("{ | ;", token); } } for_each_host(h, on_hosts) check_upr("host section", "%s: on %s", res->name, h->name); res->all_hosts = APPEND(res->all_hosts, host); while (in_braces) { int token = yylex(); fline = line; switch (token) { case TK_DISK: for_each_host(h, on_hosts) check_upr("disk statement", "%s:%s:disk", res->name, h->name); goto vol0stmt; /* for_each_host(h, on_hosts) check_uniq("disk", "disk:%s:%s", h->name, yylval.txt); */ case TK_DEVICE: for_each_host(h, on_hosts) check_upr("device statement", "%s:%s:device", res->name, h->name); goto vol0stmt; case TK_META_DISK: for_each_host(h, on_hosts) check_upr("meta-disk statement", "%s:%s:meta-disk", res->name, h->name); goto vol0stmt; case TK_FLEX_META_DISK: for_each_host(h, on_hosts) check_upr("meta-disk statement", "%s:%s:meta-disk", res->name, h->name); goto vol0stmt; break; case TK_ADDRESS: if (host->by_address) { err("%s:%d: address statement not allowed for floating {} host sections\n", config_file, fline); config_valid = 0; exit(E_CONFIG_INVALID); } for_each_host(h, on_hosts) check_upr("address statement", "%s:%s:address", res->name, h->name); parse_address(on_hosts, &host->address, &host->port, &host->address_family); range_check(R_PORT, "port", host->port); break; case TK_ALT_ADDRESS: if (host->by_address) { err("%s:%d: address statement not allowed for floating {} host sections\n", config_file, fline); config_valid = 0; exit(E_CONFIG_INVALID); } for_each_host(h, on_hosts) check_upr("alt-address statement", "%s:%s:alt-address", res->name, h->name); parse_address(on_hosts, &host->alt_address, &host->alt_port, &host->alt_address_family); range_check(R_PORT, "port", host->alt_port); break; case TK_PROXY: parse_proxy_section(host); break; case TK_VOLUME: EXP(TK_INTEGER); host->volumes = INSERT_SORTED(host->volumes, parse_volume(atoi(yylval.txt), on_hosts), vnr); break; case TK_OPTIONS: EXP('{'); host->res_options = parse_options(0, 0, TK_RES_OPTION); break; case '}': in_braces = 0; break; vol0stmt: if (parse_volume_stmt(volume0(&host->volumes), on_hosts, token)) break; /* else fall through */ default: pe_expected("disk | device | address | meta-disk " "| flexible-meta-disk"); } } inherit_volumes(res->volumes, host); for_each_volume(vol, host->volumes) check_meta_disk(vol, host); if (!(flags & REQUIRE_ALL)) return; if (!host->address) derror(host, res, "address"); check_volumes_complete(res, host); } void parse_skip() { int level; int token; fline = line; token = yylex(); switch (token) { case TK_STRING: EXP('{'); break; case '{': break; default: check_string_error(token); pe_expected("[ some_text ] {"); } level = 1; while (level) { switch (yylex()) { case '{': /* if you really want to, you can wrap this with a GB size config file :) */ level++; break; case '}': level--; break; case 0: err("%s:%u: reached eof while parsing this skip block.\n", config_file, fline); exit(E_CONFIG_INVALID); } } while (level) ; } void parse_stacked_section(struct d_resource* res) { struct d_host_info *host; struct d_name *h; c_section_start = line; fline = line; host=calloc(1,sizeof(struct d_host_info)); res->all_hosts = APPEND(res->all_hosts, host); EXP(TK_STRING); check_uniq("stacked-on-top-of", "stacked:%s", yylval.txt); host->lower_name = yylval.txt; EXP('{'); while (1) { switch(yylex()) { case TK_DEVICE: /* for_each_host(h, host->on_hosts) check_upr("device statement", "%s:%s:device", res->name, h->name); */ parse_device(host->on_hosts, volume0(&host->volumes)); volume0(&host->volumes)->meta_disk = strdup("internal"); volume0(&host->volumes)->meta_index = strdup("internal"); break; case TK_ADDRESS: for_each_host(h, host->on_hosts) check_upr("address statement", "%s:%s:address", res->name, h->name); parse_address(NULL, &host->address, &host->port, &host->address_family); range_check(R_PORT, "port", yylval.txt); break; case TK_ALT_ADDRESS: for_each_host(h, host->on_hosts) check_upr("alt-address statement", "%s:%s:alt-address", res->name, h->name); parse_address(NULL, &host->alt_address, &host->alt_port, &host->alt_address_family); range_check(R_PORT, "port", yylval.txt); break; case TK_PROXY: parse_proxy_section(host); break; case TK_VOLUME: EXP(TK_INTEGER); host->volumes = INSERT_SORTED(host->volumes, parse_stacked_volume(atoi(yylval.txt)), vnr); break; case '}': goto break_loop; default: pe_expected("device | address | proxy"); } } break_loop: res->stacked_on_one = 1; inherit_volumes(res->volumes, host); if (!host->address) derror(host,res,"address"); } void startup_delegate(void *ctx) { struct d_resource *res = (struct d_resource *)ctx; if (!strcmp(yytext, "become-primary-on")) { parse_hosts(&res->become_primary_on, ';'); } else if (!strcmp(yytext, "stacked-timeouts")) { res->stacked_timeouts = 1; EXP(';'); } else pe_expected(" | become-primary-on | stacked-timeouts"); } void net_delegate(void *ctx) { enum pr_flags flags = (enum pr_flags)ctx; if (!strcmp(yytext, "discard-my-data") && flags & PARSE_FOR_ADJUST) { switch(yylex()) { case TK_YES: case TK_NO: /* Ignore this option. */ EXP(';'); break; case ';': /* Ignore this option. */ return; default: pe_expected("yes | no | ;"); } } else pe_expected("an option keyword"); } void set_me_in_resource(struct d_resource* res, int match_on_proxy) { struct d_host_info *host; /* Determine the local host section */ for (host = res->all_hosts; host; host=host->next) { /* do we match this host? */ if (match_on_proxy) { if (!host->proxy || !name_in_names(nodeinfo.nodename, host->proxy->on_hosts)) continue; } else if (host->by_address) { if (!have_ip(host->address_family, host->address) && /* for debugging only, e.g. __DRBD_NODE__=10.0.0.1 */ strcmp(nodeinfo.nodename, host->address)) continue; } else if (host->lower) { if (!host->lower->me) continue; } else if (!host->on_hosts) { /* huh? a resource without hosts to run on?! */ continue; } else { if (!name_in_names(nodeinfo.nodename, host->on_hosts) && strcmp("_this_host", host->on_hosts->name)) continue; } /* we matched. */ if (res->ignore) { config_valid = 0; err("%s:%d: in resource %s, %s %s { ... }:\n" "\tYou cannot ignore and define at the same time.\n", res->config_file, host->config_line, res->name, host->lower ? "stacked-on-top-of" : "on", host->lower ? host->lower->name : names_to_str(host->on_hosts)); } if (res->me) { config_valid = 0; err("%s:%d: in resource %s, %s %s { ... } ... %s %s { ... }:\n" "\tThere are multiple host sections for this node.\n", res->config_file, host->config_line, res->name, res->me->lower ? "stacked-on-top-of" : "on", res->me->lower ? res->me->lower->name : names_to_str(res->me->on_hosts), host->lower ? "stacked-on-top-of" : "on", host->lower ? host->lower->name : names_to_str(host->on_hosts)); } res->me = host; if (host->lower) res->stacked = 1; } /* If there is no me, implicitly ignore that resource */ if (!res->me) { res->ignore = 1; return; } } void set_peer_in_resource(struct d_resource* res, int peer_required) { struct d_host_info *host = NULL; if (res->ignore) return; /* me must be already set */ if (!res->me) { /* should have been implicitly ignored. */ err("%s:%d: in resource %s:\n" "\tcannot determine the peer, don't even know myself!\n", res->config_file, res->start_line, res->name); exit(E_THINKO); } /* only one host section? */ if (!res->all_hosts->next) { if (peer_required) { fprintf(stderr, "%s:%d: in resource %s:\n" "\tMissing section 'on { ... }'.\n", res->config_file, res->start_line, res->name); config_valid = 0; } return; } /* short cut for exactly two host sections. * silently ignore any --peer connect_to_host option. */ if (res->all_hosts->next->next == NULL) { res->peer = res->all_hosts == res->me ? res->all_hosts->next : res->all_hosts; if (dry_run > 1 && connect_to_host) err("%s:%d: in resource %s:\n" "\tIgnoring --peer '%s': there are only two host sections.\n", res->config_file, res->start_line, res->name, connect_to_host); return; } /* Multiple peer hosts to choose from. * we need some help! */ if (!connect_to_host) { if (peer_required) { err("%s:%d: in resource %s:\n" "\tThere are multiple host sections for the peer node.\n" "\tUse the --peer option to select which peer section to use.\n", res->config_file, res->start_line, res->name); config_valid = 0; } return; } for (host = res->all_hosts; host; host=host->next) { if (host->by_address && strcmp(connect_to_host, host->address)) continue; if (host->proxy && !name_in_names(nodeinfo.nodename, host->proxy->on_hosts)) continue; if (!name_in_names(connect_to_host, host->on_hosts)) continue; if (host == res->me) { err("%s:%d: in resource %s\n" "\tInvoked with --peer '%s', but that matches myself!\n", res->config_file, res->start_line, res->name, connect_to_host); res->peer = NULL; break; } if (res->peer) { err("%s:%d: in resource %s:\n" "\tInvoked with --peer '%s', but that matches multiple times!\n", res->config_file, res->start_line, res->name, connect_to_host); res->peer = NULL; break; } res->peer = host; } if (peer_required && !res->peer) { config_valid = 0; if (!host) err("%s:%d: in resource %s:\n" "\tNo host ('on' or 'floating') section matches --peer '%s'\n", res->config_file, res->start_line, res->name, connect_to_host); } } void set_on_hosts_in_res(struct d_resource *res) { struct d_resource *l_res, *tmp; struct d_host_info *host, *host2; struct d_name *h, **last; for (host = res->all_hosts; host; host=host->next) { if (host->lower_name) { for_each_resource(l_res, tmp, config) { if (!strcmp(l_res->name, host->lower_name)) break; } if (l_res == NULL) { err("%s:%d: in resource %s, " "referenced resource '%s' not defined.\n", res->config_file, res->start_line, res->name, host->lower_name); config_valid = 0; continue; } /* Simple: host->on_hosts = concat_names(l_res->me->on_hosts, l_res->peer->on_hosts); */ last = NULL; for (host2 = l_res->all_hosts; host2; host2 = host2->next) if (!host2->lower_name) { append_names(&host->on_hosts, &last, host2->on_hosts); for_each_host(h, host2->on_hosts) { struct d_volume *vol; for_each_volume(vol, host->volumes) check_uniq("device-minor", "device-minor:%s:%u", h->name, vol->device_minor); for_each_volume(vol, host->volumes) if (vol->device) check_uniq("device", "device:%s:%s", h->name, vol->device); } } host->lower = l_res; /* */ if (addr_scope_local(host->address)) for_each_host(h, host->on_hosts) check_uniq("IP", "%s:%s:%s", h->name, host->address, host->port); } } } void set_disk_in_res(struct d_resource *res) { struct d_host_info *host; struct d_volume *a, *b; if (res->ignore) return; for (host = res->all_hosts; host; host=host->next) { if (!host->lower) continue; if (host->lower->ignore) continue; check_volume_sets_equal(res, host, host->lower->me); if (!config_valid) /* don't even bother for broken config. */ continue; /* volume lists are sorted on vnr */ a = host->volumes; b = host->lower->me->volumes; while (a) { while (b && a->vnr > b->vnr) { /* Lower resource has more volumes. * Probably unusual, but we decided * that it should be legal. * Skip those that do not match */ b = b->next; } if (a && b && a->vnr == b->vnr) { if (b->device) m_asprintf(&a->disk, "%s", b->device); else m_asprintf(&a->disk, "/dev/drbd%u", b->device_minor); /* stacked implicit volumes need internal meta data, too */ if (!a->meta_disk) m_asprintf(&a->meta_disk, "internal"); if (!a->meta_index) m_asprintf(&a->meta_index, "internal"); a = a->next; b = b->next; } else { /* config_invalid should have been set * by check_volume_sets_equal */ assert(0); } } } } void proxy_delegate(void *ctx) { struct d_option **proxy_plugins = (struct d_option **)ctx; int token; struct d_option *options, *opt; struct d_name *line, *word, **pnp; opt = NULL; token = yylex(); if (token != '{') { err("%s:%d: expected \"{\" after \"proxy\" keyword\n", config_file, fline); exit(E_CONFIG_INVALID); } options = NULL; while (1) { line = NULL; pnp = &line; while (1) { yylval.txt = NULL; token = yylex(); if (token <= 0) { err("%s:%d: Unexpected end-of-file\n", config_file, fline); exit(E_CONFIG_INVALID); } if (token == ';') break; if (token == '}') { if (pnp == &line) goto out; err("%s:%d: Missing \";\" before \"}\"\n", config_file, fline); exit(E_CONFIG_INVALID); } word = malloc(sizeof(struct d_name)); if (!word) pdperror("out of memory."); word->name = yylval.txt ? yylval.txt : strdup(yytext); word->next = NULL; *pnp = word; pnp = &word->next; } opt = calloc(1, sizeof(struct d_option)); if (!opt) pdperror("out of memory."); opt->name = strdup(names_to_str(line)); options = APPEND(options, opt); free_names(line); } out: if (proxy_plugins) *proxy_plugins = options; } static int parse_proxy_options(struct d_option **proxy_options, struct d_option **proxy_plugins) { struct d_option *opts; EXP('{'); opts = parse_options_d(0, 0, TK_PROXY_OPTION | TK_PROXY_GROUP, TK_PROXY_DELEGATE, proxy_delegate, proxy_plugins); if (proxy_options) *proxy_options = opts; return 0; } int parse_proxy_options_section(struct d_resource *res) { int token; struct d_resource dummy_res = { "dummy", }; token = yylex(); if (token != TK_PROXY) { yyrestart(yyin); /* flushes flex's buffers */ return 1; } if (!res) res = &dummy_res; return parse_proxy_options(&res->proxy_options, &res->proxy_plugins); } struct d_resource* parse_resource(char* res_name, enum pr_flags flags) { struct d_resource* res; struct d_name *host_names; char *opt_name; int token; check_upr_init(); check_uniq("resource section", res_name); res=calloc(1,sizeof(struct d_resource)); res->name = res_name; res->config_file = config_save; res->start_line = line; while(1) { token = yylex(); fline = line; switch(token) { case TK_NET_OPTION: if (strcmp(yylval.txt, "protocol")) goto goto_default; check_upr("protocol statement","%s: protocol",res->name); opt_name = yylval.txt; EXP(TK_STRING); range_check(R_PROTOCOL, opt_name, yylval.txt); res->net_options = APPEND(res->net_options, new_opt(opt_name, yylval.txt)); EXP(';'); break; case TK_ON: parse_hosts(&host_names, '{'); parse_host_section(res, host_names, REQUIRE_ALL); break; case TK_STACKED: parse_stacked_section(res); break; case TK_IGNORE: if (res->me || res->peer) { err("%s:%d: in resource %s, " "'ignore-on' statement must precede any real host section (on ... { ... }).\n", config_file, line, res->name); exit(E_CONFIG_INVALID); } EXP(TK_STRING); err("%s:%d: in resource %s, WARN: The 'ignore-on' keyword is deprecated.\n", config_file, line, res->name); EXP(';'); break; case TK__THIS_HOST: EXP('{'); host_names = names_from_str("_this_host"); parse_host_section(res, host_names, 0); break; case TK__REMOTE_HOST: EXP('{'); host_names = names_from_str("_remote_host"); parse_host_section(res, host_names, 0); break; case TK_FLOATING: parse_host_section(res, NULL, REQUIRE_ALL + BY_ADDRESS); break; case TK_DISK: switch (token=yylex()) { case TK_STRING: /* open coded parse_volume_stmt() */ volume0(&res->volumes)->disk = yylval.txt; EXP(';'); break; case '{': check_upr("disk section", "%s:disk", res->name); res->disk_options = SPLICE(res->disk_options, parse_options(TK_DISK_FLAG, TK_DISK_NO_FLAG, TK_DISK_OPTION)); break; default: check_string_error(token); pe_expected_got( "TK_STRING | {", token); } break; case TK_NET: check_upr("net section", "%s:net", res->name); EXP('{'); res->net_options = SPLICE(res->net_options, parse_options_d(TK_NET_FLAG, TK_NET_NO_FLAG, TK_NET_OPTION, TK_NET_DELEGATE, &net_delegate, (void *)flags)); break; case TK_SYNCER: check_upr("syncer section", "%s:syncer", res->name); EXP('{'); parse_options_syncer(res); break; case TK_STARTUP: check_upr("startup section", "%s:startup", res->name); EXP('{'); res->startup_options = parse_options_d(TK_STARTUP_FLAG, 0, TK_STARTUP_OPTION, TK_STARTUP_DELEGATE, &startup_delegate, res); break; case TK_HANDLER: check_upr("handlers section", "%s:handlers", res->name); EXP('{'); res->handlers = parse_options(0, 0, TK_HANDLER_OPTION); break; case TK_PROXY: check_upr("proxy section", "%s:proxy", res->name); parse_proxy_options(&res->proxy_options, &res->proxy_plugins); break; case TK_DEVICE: check_upr("device statement", "%s:device", res->name); case TK_META_DISK: case TK_FLEX_META_DISK: parse_volume_stmt(volume0(&res->volumes), NULL, token); break; case TK_VOLUME: EXP(TK_INTEGER); res->volumes = INSERT_SORTED(res->volumes, parse_volume(atoi(yylval.txt), NULL), vnr); break; case TK_OPTIONS: check_upr("resource options section", "%s:res_options", res->name); EXP('{'); res->res_options = SPLICE(res->res_options, parse_options(0, 0, TK_RES_OPTION)); break; case '}': case 0: goto exit_loop; default: goto_default: pe_expected_got("protocol | on | disk | net | syncer |" " startup | handlers |" " ignore-on | stacked-on-top-of",token); } } exit_loop: if (flags == NoneHAllowed && res->all_hosts) { config_valid = 0; err("%s:%d: in the %s section, there are no host sections allowed.\n", config_file, c_section_start, res->name); } if (!(flags & PARSE_FOR_ADJUST)) check_volumes_hosts(res); return res; } struct d_resource* parse_resource_for_adjust(struct cfg_ctx *ctx) { int token; token = yylex(); if (token != TK_RESOURCE) return NULL; token = yylex(); if (token != TK_STRING) return NULL; /* FIXME assert that string and ctx->res->name match? */ token = yylex(); if (token != '{') return NULL; return parse_resource(ctx->res->name, PARSE_FOR_ADJUST); } void post_parse(struct d_resource *config, enum pp_flags flags) { struct d_resource *res,*tmp; for_each_resource(res, tmp, config) if (res->stacked_on_one) set_on_hosts_in_res(res); /* sets on_hosts and host->lower */ /* Needs "on_hosts" and host->lower already set */ for_each_resource(res, tmp, config) if (!res->stacked_on_one) set_me_in_resource(res, flags & MATCH_ON_PROXY); /* Needs host->lower->me already set */ for_each_resource(res, tmp, config) if (res->stacked_on_one) set_me_in_resource(res, flags & MATCH_ON_PROXY); // Needs "me" set already for_each_resource(res, tmp, config) if (res->stacked_on_one) set_disk_in_res(res); } /* Returns the "previous" count, ie. 0 if this file wasn't seen before. */ int was_file_already_seen(char *fn) { ENTRY e, *ep; char *real_path; real_path = realpath(fn, NULL); if (!real_path) real_path = fn; ep = NULL; e.key = real_path; e.data = real_path; hsearch_r(e, FIND, &ep, &global_htable); if (ep) { /* Can be freed, it's just a queried key. */ if (real_path != fn) free(real_path); return 1; } e.key = real_path; e.data = real_path; hsearch_r(e, ENTER, &ep, &global_htable); if (!ep) { err("hash table entry (%s => %s) failed\n", e.key, (char *)e.data); exit(E_THINKO); } /* Must not be freed, because it's still referenced by the hash table. */ /* free(real_path); */ return 0; } void include_stmt(char *str) { char *last_slash, *tmp; glob_t glob_buf; int cwd_fd; FILE *f; size_t i; int r; /* in order to allow relative paths in include statements we change directory to the location of the current configuration file. */ cwd_fd = open(".", O_RDONLY | O_CLOEXEC); if (cwd_fd < 0) { err("open(\".\") failed: %m\n"); exit(E_USAGE); } tmp = strdupa(config_save); last_slash = strrchr(tmp, '/'); if (last_slash) *last_slash = 0; if (chdir(tmp)) { err("chdir(\"%s\") failed: %m\n", tmp); exit(E_USAGE); } r = glob(str, 0, NULL, &glob_buf); if (r == 0) { for (i=0; i Copyright (C) 2006-2008, Lars Ellenberg drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ enum range_checks { R_NO_CHECK, R_MINOR_COUNT, R_DIALOG_REFRESH, R_DISK_SIZE, R_TIMEOUT, R_CONNECT_INT, R_PING_INT, R_MAX_BUFFERS, R_MAX_EPOCH_SIZE, R_SNDBUF_SIZE, R_RCVBUF_SIZE, R_KO_COUNT, R_RATE, R_GROUP, R_AL_EXTENTS, R_PORT, R_META_IDX, R_WFC_TIMEOUT, R_DEGR_WFC_TIMEOUT, R_OUTDATED_WFC_TIMEOUT, R_C_PLAN_AHEAD, R_C_DELAY_TARGET, R_C_FILL_TARGET, R_C_MAX_RATE, R_C_MIN_RATE, R_CONG_FILL, R_CONG_EXTENTS, R_PROTOCOL, }; enum yytokentype { TK_GLOBAL = 258, TK_RESOURCE, TK_ON, TK_STACKED, TK_IGNORE, TK_NET, TK_DISK, TK_SKIP, TK_SYNCER, /* depricated after 8.3 */ TK_STARTUP, TK_DISABLE_IP_VERIFICATION, TK_DIALOG_REFRESH, TK_PROTOCOL, TK_HANDLER, TK_COMMON, TK_ADDRESS, TK_ALT_ADDRESS, TK_DEVICE, TK_MINOR, TK_META_DISK, TK_FLEX_META_DISK, TK_MINOR_COUNT, TK_IPADDR, TK_INTEGER, TK_STRING, TK_ELSE, TK_DISK_FLAG, TK_DISK_NO_FLAG, TK_DISK_OPTION, TK_NET_FLAG, TK_NET_NO_FLAG, TK_NET_OPTION, TK_SYNCER_FLAG, TK_SYNCER_OPTION, TK_STARTUP_FLAG, TK_STARTUP_OPTION, TK_STARTUP_DELEGATE, TK_HANDLER_OPTION, TK_USAGE_COUNT, TK_ASK, TK_YES, TK_NO, TK__THIS_HOST, TK__REMOTE_HOST, TK_PROXY, TK_INSIDE, TK_OUTSIDE, TK_MEMLIMIT, TK_PROXY_OPTION, TK_PROXY_DELEGATE, TK_ERR_STRING_TOO_LONG, TK_ERR_DQSTRING_TOO_LONG, TK_ERR_DQSTRING, TK_SCI, TK_SDP, TK_SSOCKS, TK_IPV4, TK_IPV6, TK_IPADDR6, TK_NET_DELEGATE, TK_INCLUDE, TK_BWLIMIT, TK_FLOATING, TK_DEPRECATED_OPTION, TK_VOLUME, TK_CMD_TIMEOUT_SHORT, TK_CMD_TIMEOUT_MEDIUM, TK_CMD_TIMEOUT_LONG, TK_RES_OPTION, TK_OPTIONS, TK__GROUPING_BASE = 0x1000, TK_SYNCER_OLD_OPT = 0x2000, /* Might be or'ed to TK_[NET|DISK]_[OPTION|SWITCH] */ TK_PROXY_GROUP = 0x3000, /* Gets or'ed to some options */ }; /* The higher bits define one or more token groups. */ #define GET_TOKEN_GROUP(__x) ((__x) & ~(TK__GROUPING_BASE - 1)) #define REMOVE_GROUP_FROM_TOKEN(__x) ((__x) & (TK__GROUPING_BASE - 1)) typedef struct YYSTYPE { char* txt; enum range_checks rc; } YYSTYPE; #define yystype YYSTYPE /* obsolescent; will be withdrawn */ #define YYSTYPE_IS_DECLARED 1 #define YYSTYPE_IS_TRIVIAL 1 extern yystype yylval; extern char* yytext; extern FILE* yyin; /* avoid compiler warnings about implicit declaration */ int yylex(void); void my_yypush_buffer_state(FILE *f); void yypop_buffer_state (void ); void yyrestart(FILE *input_file); drbd-utils-8.9.10/user/v84/drbd_nla.h0000644000175000017500000000044012466702074017101 0ustar apoikosapoikos#ifndef __DRBD_NLA_H #define __DRBD_NLA_H extern int drbd_nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, const struct nla_policy *policy); extern struct nlattr *drbd_nla_find_nested(int maxtype, struct nlattr *nla, int attrtype); #endif /* __DRBD_NLA_H */ drbd-utils-8.9.10/user/v84/drbd_nla.c0000644000175000017500000000232512466702074017100 0ustar apoikosapoikos#include #include "libgenl.h" #include #include "drbd_nla.h" static int drbd_nla_check_mandatory(int maxtype, struct nlattr *nla) { struct nlattr *head = nla_data(nla); int len = nla_len(nla); int rem; /* * validate_nla (called from nla_parse_nested) ignores attributes * beyond maxtype, and does not understand the DRBD_GENLA_F_MANDATORY flag. * In order to have it validate attributes with the DRBD_GENLA_F_MANDATORY * flag set also, check and remove that flag before calling * nla_parse_nested. */ nla_for_each_attr(nla, head, len, rem) { if (nla->nla_type & DRBD_GENLA_F_MANDATORY) { nla->nla_type &= ~DRBD_GENLA_F_MANDATORY; if (nla_type(nla) > maxtype) return -EOPNOTSUPP; } } return 0; } int drbd_nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, const struct nla_policy *policy) { int err; err = drbd_nla_check_mandatory(maxtype, nla); if (!err) err = nla_parse_nested(tb, maxtype, nla, policy); return err; } struct nlattr *drbd_nla_find_nested(int maxtype, struct nlattr *nla, int attrtype) { int err; err = drbd_nla_check_mandatory(maxtype, nla); if (err) /* ignore */; return nla_find_nested(nla, attrtype); } drbd-utils-8.9.10/user/v84/drbdtool_common.c0000644000175000017500000000417712466702074020523 0ustar apoikosapoikos#define _GNU_SOURCE #define _XOPEN_SOURCE 600 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for BLKGETSIZE64 */ #include #include "drbdtool_common.h" #include "config.h" void dt_pretty_print_uuids(const uint64_t* uuid, unsigned int flags) { printf( "\n" " +--< Current data generation UUID >-\n" " | +--< Bitmap's base data generation UUID >-\n" " | | +--< younger history UUID >-\n" " | | | +-< older history >-\n" " V V V V\n"); dt_print_uuids(uuid, flags); printf( " ^ ^ ^ ^ ^ ^ ^\n" " -< Data consistency flag >--+ | | | | | |\n" " -< Data was/is currently up-to-date >--+ | | | | |\n" " -< Node was/is currently primary >--+ | | | |\n" " -< Node was/is currently connected >--+ | | |\n" " -< Node was in the progress of setting all bits in the bitmap >--+ | |\n" " -< The peer's disk was out-dated or inconsistent >--+ |\n" " -< This node was a crashed primary, and has not seen its peer since >--+\n" "\n"); printf("flags:%s %s, %s, %s%s%s\n", (flags & MDF_CRASHED_PRIMARY) ? " crashed" : "", (flags & MDF_PRIMARY_IND) ? "Primary" : "Secondary", (flags & MDF_CONNECTED_IND) ? "Connected" : "StandAlone", (flags & MDF_CONSISTENT) ? ((flags & MDF_WAS_UP_TO_DATE) ? "UpToDate" : "Outdated") : "Inconsistent", (flags & MDF_FULL_SYNC) ? ", need full sync" : "", (flags & MDF_PEER_OUT_DATED) ? ", peer Outdated" : ""); printf("meta-data: %s\n", (flags & MDF_AL_CLEAN) ? "clean" : "need apply-al"); } drbd-utils-8.9.10/user/v84/drbdsetup_colors.c0000644000175000017500000000703212511151661020677 0ustar apoikosapoikos#include #include "drbdtool_common.h" #include "drbdsetup_colors.h" enum when_color opt_color; enum colors { COLOR_TRANSITIONAL, /* default */ COLOR_NORMAL, COLOR_PRIMARY, COLOR_GOOD, COLOR_BAD, }; #define LC "\033[" #define RC "m" const char *local_color_codes[] = { [COLOR_TRANSITIONAL] = LC "1" RC, /* bold */ [COLOR_NORMAL] = NULL, [COLOR_PRIMARY] = LC "1;36" RC, /* cyan */ [COLOR_GOOD] = LC "1;32" RC, /* green */ [COLOR_BAD] = LC "1;31" RC, /* red */ }; const char *peer_color_codes[] = { [COLOR_TRANSITIONAL] = NULL, [COLOR_NORMAL] = NULL, [COLOR_PRIMARY] = LC "36" RC, /* cyan */ [COLOR_GOOD] = LC "32" RC, /* green */ [COLOR_BAD] = LC "31" RC, /* red */ }; int role_colors[] = { [R_PRIMARY] = COLOR_PRIMARY, [R_SECONDARY] = COLOR_NORMAL, [R_UNKNOWN] = COLOR_TRANSITIONAL, }; int cstate_colors[] = { [C_STANDALONE] = COLOR_BAD, [C_WF_CONNECTION] = COLOR_BAD, [C_WF_REPORT_PARAMS] = COLOR_NORMAL, }; int repl_state_colors[] = { [C_WF_REPORT_PARAMS] = COLOR_TRANSITIONAL, [C_CONNECTED] = COLOR_NORMAL, [C_SYNC_SOURCE] = COLOR_BAD, [C_SYNC_TARGET] = COLOR_BAD, [C_VERIFY_S] = COLOR_NORMAL, [C_VERIFY_T] = COLOR_NORMAL, [C_PAUSED_SYNC_S] = COLOR_NORMAL, [C_PAUSED_SYNC_T] = COLOR_BAD, [C_AHEAD] = COLOR_NORMAL, [C_BEHIND] = COLOR_TRANSITIONAL, }; int disk_state_colors[] = { [D_DISKLESS] = COLOR_BAD, [D_INCONSISTENT] = COLOR_BAD, [D_OUTDATED] = COLOR_BAD, [D_CONSISTENT] = COLOR_TRANSITIONAL, [D_UP_TO_DATE] = COLOR_GOOD, }; const char *stop_color_code(void) { return LC "0" RC; } static const char *color_code(int index, int *array, int size, bool start, bool local) { const char **color_codes = local ? local_color_codes : peer_color_codes; int i; if (opt_color == AUTO_COLOR) opt_color = isatty(fileno(stdout)) ? ALWAYS_COLOR : NEVER_COLOR; if (opt_color == NEVER_COLOR) return ""; if (index < size) i = array[index]; else i = COLOR_TRANSITIONAL; if (color_codes[i]) return start ? color_codes[i] : stop_color_code(); else return ""; } const char *role_color_start(enum drbd_role role, bool local) { return color_code(role, role_colors, ARRAY_SIZE(role_colors), true, local); } const char *role_color_stop(enum drbd_role role, bool local) { return color_code(role, role_colors, ARRAY_SIZE(role_colors), false, local); } const char *cstate_color_start(enum drbd_conns cstate) { return color_code(cstate, cstate_colors, ARRAY_SIZE(cstate_colors), true, true); } const char *cstate_color_stop(enum drbd_conns cstate) { return color_code(cstate, cstate_colors, ARRAY_SIZE(cstate_colors), false, true); } static bool is_local_repl_state(enum drbd_conns repl_state) { switch(repl_state) { case C_SYNC_TARGET: case C_PAUSED_SYNC_T: case C_BEHIND: return true; default: return false; } } const char *repl_state_color_start(enum drbd_conns repl_state) { return color_code(repl_state, repl_state_colors, ARRAY_SIZE(repl_state_colors), true, is_local_repl_state(repl_state)); } const char *repl_state_color_stop(enum drbd_conns repl_state) { return color_code(repl_state, repl_state_colors, ARRAY_SIZE(repl_state_colors), false, is_local_repl_state(repl_state)); } const char *disk_state_color_start(enum drbd_disk_state disk_state, bool local) { return color_code(disk_state, disk_state_colors, ARRAY_SIZE(disk_state_colors), true, local); } const char *disk_state_color_stop(enum drbd_disk_state disk_state, bool local) { return color_code(disk_state, disk_state_colors, ARRAY_SIZE(disk_state_colors), false, local); } drbd-utils-8.9.10/user/v84/drbdtool_common.h0000644000175000017500000000146412466702074020524 0ustar apoikosapoikos#ifndef DRBDTOOL_COMMON_H #define DRBDTOOL_COMMON_H #include "drbd_endian.h" #include #include #include #include #include "shared_tool.h" #define LANANA_DRBD_MAJOR 147 /* we should get this into linux/major.h */ #ifndef DRBD_MAJOR #define DRBD_MAJOR LANANA_DRBD_MAJOR #elif (DRBD_MAJOR != LANANA_DRBD_MAJOR) # error "FIXME unexpected DRBD_MAJOR" #endif #ifndef __packed #define __packed __attribute__((packed)) #endif #ifndef ARRAY_SIZE #define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0])) #endif struct option; extern void dt_release_lockfile(int drbd_fd); extern void dt_print_uuids(const uint64_t* uuid, unsigned int flags); extern void dt_pretty_print_uuids(const uint64_t* uuid, unsigned int flags); extern int fget_token(char *s, int size, FILE* stream); #endif drbd-utils-8.9.10/user/v84/registry.h0000644000175000017500000000056112466702074017210 0ustar apoikosapoikos#ifndef __REGISTRY_H #define __REGISTRY_H extern int register_minor(int minor, const char *path); extern int unregister_minor(int minor); extern char *lookup_minor(int minor); extern int unregister_resource(const char *name); extern int register_resource(const char *name, const char *path); extern char *lookup_resource(const char *name); #endif /* __REGISTRY_H */ drbd-utils-8.9.10/user/v84/registry.c0000644000175000017500000001136012466702074017202 0ustar apoikosapoikos/* drbdadm_registry.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. It was written by Johannes Thoma Copyright (C) 2002-2008, LINBIT Information Technologies GmbH. Copyright (C) 2002-2008, Philipp Reisner . Copyright (C) 2002-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This keeps track of which DRBD minor was configured in which * config file. This is required to have alternative config files * (-c switch) and userland event handlers. */ #include #include #include #include #include #include #include #include #include #include #include "config.h" #include "registry.h" static void linkname_from_minor(char *linkname, int minor) { sprintf(linkname, "%s/drbd-minor-%d.conf", DRBD_RUN_DIR, minor); } int unregister_minor(int minor) { char linkname[PATH_MAX]; linkname_from_minor(linkname, minor); if (unlink(linkname) < 0) { if (errno != ENOENT) { perror("unlink"); return -1; } } return 0; } static ssize_t __readlink(const char *path, char *buf, size_t bufsiz) { ssize_t ret; ret = readlink(path, buf, bufsiz); if (ret >= 0) { if (ret >= bufsiz) { errno = ENAMETOOLONG; return -1; } buf[ret] = 0; } return ret; } static int register_path(const char *linkname, const char *path) { char target[PATH_MAX]; if (path[0] != '/') { fprintf(stderr, "File %s: absolute path expected; won't " "register relative path.", path); return -1; } /* safeguard against symlink loops in DRBD_RUN_DIR */ if (!strncmp(path, DRBD_RUN_DIR "/", strlen(DRBD_RUN_DIR "/"))) return -1; if (__readlink(linkname, target, sizeof(target)) >= 0 && !strcmp(target, path)) return 0; if (unlink(linkname) != 0 && errno != ENOENT) { perror(linkname); return -1; } if (mkdir(DRBD_RUN_DIR, S_IRWXU) != 0 && errno != EEXIST) { perror(DRBD_RUN_DIR); return -1; } if (symlink(path, linkname) != 0) { fprintf(stderr, "symlink(%s, %s): %m\n", path, linkname); return -1; } return 0; } int register_minor(int minor, const char *path) { char linkname[PATH_MAX]; linkname_from_minor(linkname, minor); return register_path(linkname, path); } static char *resolve_symlink(const char *linkname) { static char target[PATH_MAX]; if (__readlink(linkname, target, sizeof(target)) < 0) return NULL; return target; } char *lookup_minor(int minor) { static char linkname[PATH_MAX]; struct stat stat_buf; linkname_from_minor(linkname, minor); if (stat(linkname, &stat_buf) != 0) { if (errno != ENOENT) perror(linkname); return NULL; } return resolve_symlink(linkname); } static void linkname_from_resource_name(char *linkname, const char *name) { sprintf(linkname, "%s/drbd-resource-%s.conf", DRBD_RUN_DIR, name); } int unregister_resource(const char *name) { char linkname[PATH_MAX]; linkname_from_resource_name(linkname, name); if (unlink(linkname) != 0) { if (errno != ENOENT) { perror(linkname); return -1; } } return 0; } int register_resource(const char *name, const char *path) { char linkname[PATH_MAX]; linkname_from_resource_name(linkname, name); return register_path(linkname, path); } /* This returns a static buffer containing the real * configuration file known to be used last for this minor. * If you need the return value longer, stuff it away with strdup. */ char *lookup_resource(const char *name) { static char linkname[PATH_MAX]; struct stat stat_buf; linkname_from_resource_name(linkname, name); if (stat(linkname, &stat_buf) != 0) { if (errno != ENOENT) perror(linkname); return NULL; } return resolve_symlink(linkname); } #ifdef TEST int main(int argc, char ** argv) { register_minor(1, "/etc/drbd-xy.conf"); register_minor(15, "/etc/drbd-82.conf"); register_minor(14, "/../../../../../../etc/drbd-82.conf"); printf("Minor 1 is %s.\n", lookup_minor(1)); printf("Minor 2 is %s.\n", lookup_minor(2)); printf("Minor 14 is %s.\n", lookup_minor(14)); printf("Minor 15 is %s.\n", lookup_minor(15)); return 0; } #endif drbd-utils-8.9.10/user/v84/drbdadm.h0000644000175000017500000002247312477305373016746 0ustar apoikosapoikos#ifndef DRBDADM_H #define DRBDADM_H #include #include #include #include #include #include #include #ifndef O_CLOEXEC #warning "O_CLOEXEC undefined, redefining to 0" #define O_CLOEXEC 0 #endif #include "config.h" #include "shared_main.h" #define API_VERSION 1 struct d_name { char *name; struct d_name *next; }; struct d_proxy_info { struct d_name *on_hosts; char* inside_addr; char* inside_port; char* inside_af; char* outside_addr; char* outside_port; char* outside_af; struct d_option *options; /* named proxy_options in other places */ struct d_option *plugins; /* named proxy_plugins in other places */ }; struct d_volume { unsigned vnr; char* device; unsigned device_minor; char* disk; char* meta_disk; char* meta_index; int meta_major; int meta_minor; struct d_volume *next; struct d_option* disk_options; /* Additional per volume options */ /* Do not dump an explicit volume section */ unsigned int implicit :1 ; /* flags for "drbdadm adjust" */ unsigned int adj_del_minor :1; unsigned int adj_add_minor :1; unsigned int adj_detach :1; unsigned int adj_attach :1; unsigned int adj_resize :1; unsigned int adj_disk_opts :1; }; struct d_host_info { struct d_name *on_hosts; struct d_volume *volumes; char* address; char* port; char* address_family; char* alt_address; char* alt_port; char* alt_address_family; struct d_proxy_info *proxy; struct d_host_info* next; struct d_resource* lower; /* for device stacking */ char *lower_name; /* for device stacking, before bind_stacked_res() */ int config_line; unsigned int by_address:1; /* Match to machines by address, not by names (=on_hosts) */ struct d_option* res_options; /* Additional per host options */ }; struct d_option { char* name; char* value; struct d_option* next; unsigned int mentioned :1 ; // for the adjust command. unsigned int is_escaped :1 ; unsigned int adj_skip :1; }; struct d_resource { char* name; struct d_volume *volumes; /* gets propagated to host_info sections later. */ struct d_host_info* me; struct d_host_info* peer; struct d_host_info* all_hosts; struct d_option* net_options; struct d_option* disk_options; struct d_option* res_options; struct d_option* startup_options; struct d_option* handlers; struct d_option* proxy_options; struct d_option* proxy_plugins; struct d_resource* next; struct d_name *become_primary_on; char *config_file; /* The config file this resource is define in.*/ int start_line; unsigned int stacked_timeouts:1; unsigned int ignore:1; unsigned int stacked:1; /* Stacked on this node */ unsigned int stacked_on_one:1; /* Stacked either on me or on peer */ /* if a prerequisite command failed, don't try any further commands. * see run_deferred_cmds() */ unsigned int skip_further_deferred_command:1; }; struct adm_cmd; struct cfg_ctx { /* res == NULL: does not care for resources, or iterates over all * resources in the global "struct d_resource *config" */ struct d_resource *res; /* vol == NULL: operate on the resource itself, or iterates over all * volumes in res */ struct d_volume *vol; const char *arg; }; extern char *canonify_path(char *path); extern int adm_adjust(struct cfg_ctx *); extern int adm_new_minor(struct cfg_ctx *ctx); extern int adm_new_resource(struct cfg_ctx *); extern int adm_res_options(struct cfg_ctx *); extern int adm_set_default_res_options(struct cfg_ctx *); extern int adm_attach(struct cfg_ctx *); extern int adm_disk_options(struct cfg_ctx *); extern int adm_set_default_disk_options(struct cfg_ctx *); extern int adm_resize(struct cfg_ctx *); extern int adm_connect(struct cfg_ctx *); extern int adm_net_options(struct cfg_ctx *); extern int adm_set_default_net_options(struct cfg_ctx *); extern int adm_disconnect(struct cfg_ctx *); extern int adm_generic_s(struct cfg_ctx *); extern int adm_create_md(struct cfg_ctx *); extern int _admm_generic(struct cfg_ctx *, int flags); extern struct d_option* find_opt(struct d_option*,char*); extern void validate_resource(struct d_resource *, enum pp_flags); /* stages of configuration, as performed on "drbdadm up" * or "drbdadm adjust": */ enum drbd_cfg_stage { /* prerequisite stage: create objects, start daemons, ... */ CFG_PREREQ, /* run time changeable settings of resources */ CFG_RESOURCE, /* detach/attach local disks, */ CFG_DISK_PREREQ, CFG_DISK, /* The stage to discard network configuration, during adjust. * This is after the DISK stage, because we don't want to cut access to * good data while in primary role. And before the SETTINGS stage, as * some proxy or syncer settings may cause side effects and additional * handshakes while we have an established connection. */ CFG_NET_PREREQ, /* discard/set connection parameters */ CFG_NET, __CFG_LAST }; extern void schedule_deferred_cmd( int (*function)(struct cfg_ctx *), struct cfg_ctx *ctx, const char *arg, enum drbd_cfg_stage stage); extern int version_code_kernel(void); extern int version_code_userland(void); extern void warn_on_version_mismatch(void); extern void maybe_exec_drbdadm_83(char **argv); extern void uc_node(enum usage_count_type type); extern void convert_discard_opt(struct d_resource* res); extern void convert_after_option(struct d_resource* res); extern int have_ip(const char *af, const char *ip); enum pr_flags { NoneHAllowed = 4, PARSE_FOR_ADJUST = 8 }; extern struct d_resource* parse_resource_for_adjust(struct cfg_ctx *ctx); extern struct d_resource* parse_resource(char*, enum pr_flags); extern void post_parse(struct d_resource *config, enum pp_flags); extern struct d_option *new_opt(char *name, char *value); extern int name_in_names(char *name, struct d_name *names); extern char *_names_to_str(char* buffer, struct d_name *names); extern char *_names_to_str_c(char* buffer, struct d_name *names, char c); #define NAMES_STR_SIZE 255 #define names_to_str(N) _names_to_str(alloca(NAMES_STR_SIZE+1), N) #define names_to_str_c(N, C) _names_to_str_c(alloca(NAMES_STR_SIZE+1), N, C) extern void free_names(struct d_name *names); extern void set_me_in_resource(struct d_resource* res, int match_on_proxy); extern void set_peer_in_resource(struct d_resource* res, int peer_required); extern void set_on_hosts_in_res(struct d_resource *res); extern void set_disk_in_res(struct d_resource *res); extern int _proxy_connect_name_len(struct d_resource *res); extern char *_proxy_connection_name(char *conn_name, struct d_resource *res); #define proxy_connection_name(RES) \ _proxy_connection_name(alloca(_proxy_connect_name_len(RES)), RES) int parse_proxy_options_section(struct d_resource *res); /* conn_name is optional and mostly for compatibility with dcmd */ int do_proxy_conn_up(struct cfg_ctx *ctx); int do_proxy_conn_down(struct cfg_ctx *ctx); int do_proxy_conn_plugins(struct cfg_ctx *ctx); extern char *config_file; extern char *config_save; extern int config_valid; extern struct d_resource* config; extern struct d_resource* common; extern int line, fline; extern struct hsearch_data global_htable; extern int no_tty; extern int dry_run; extern int verbose; extern char* drbdsetup; extern char* drbd_proxy_ctl; extern char* drbdadm_83; extern char ss_buffer[1024]; extern struct utsname nodeinfo; extern char* connect_to_host; struct setup_option { bool explicit; char *option; }; struct setup_option *setup_options; extern void add_setup_option(bool explicit, char *option); /* ssprintf() places the result of the printf in the current stack frame and sets ptr to the resulting string. If the current stack frame is destroyed (=function returns), the allocated memory is freed automatically */ /* // This is the nicer version, that does not need the ss_buffer. // But it only works with very new glibcs. #define ssprintf(...) \ ({ int _ss_size = snprintf(0, 0, ##__VA_ARGS__); \ char *_ss_ret = __builtin_alloca(_ss_size+1); \ snprintf(_ss_ret, _ss_size+1, ##__VA_ARGS__); \ _ss_ret; }) */ #define ssprintf(ptr,...) \ ptr=strcpy(alloca(snprintf(ss_buffer,sizeof(ss_buffer),##__VA_ARGS__)+1),ss_buffer) /* CAUTION: arguments may not have side effects! */ #define for_each_resource(res,tmp,config) \ for (res = (config); res && (tmp = res->next, 1); res = tmp) #define for_each_volume(v_,volumes_) \ for (v_ = volumes_; v_; v_ = v_->next) #define APPEND(LIST,ITEM) ({ \ typeof((LIST)) _l = (LIST); \ typeof((ITEM)) _i = (ITEM); \ typeof((ITEM)) _t; \ _i->next = NULL; \ if (_l == NULL) { _l = _i; } \ else { \ for (_t = _l; _t->next; _t = _t->next); \ _t->next = _i; \ }; \ _l; \ }) #define INSERT_SORTED(LIST,ITEM,SORT) ({ \ typeof((LIST)) _l = (LIST); \ typeof((ITEM)) _i = (ITEM); \ typeof((ITEM)) _t, _p = NULL; \ for (_t = _l; _t && _t->SORT <= _i->SORT; _p = _t, _t = _t->next); \ if (_p) \ _p->next = _i; \ else \ _l = _i; \ _i->next = _t; \ _l; \ }) #define SPLICE(LIST,ITEMS) ({ \ typeof((LIST)) _l = (LIST); \ typeof((ITEMS)) _i = (ITEMS); \ typeof((ITEMS)) _t; \ if (_l == NULL) { _l = _i; } \ else { \ for (_t = _l; _t->next; _t = _t->next); \ _t->next = _i; \ }; \ _l; \ }) #endif drbd-utils-8.9.10/user/v84/linux/0000755000175000017500000000000013027242657016324 5ustar apoikosapoikosdrbd-utils-8.9.10/user/v84/linux/drbd_config.h0000644000175000017500000000153112466702074020735 0ustar apoikosapoikos/* drbd_config.h DRBD's compile time configuration. drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef DRBD_LEGACY_84_CONFIG_H #define DRBD_LEGACY_84_CONFIG_H extern const char *drbd_buildtag(void); #define API_VERSION 1 #endif drbd-utils-8.9.10/user/v84/linux/genl_magic_struct.h0000644000175000017500000001673012466702074022175 0ustar apoikosapoikos#ifndef GENL_MAGIC_STRUCT_H #define GENL_MAGIC_STRUCT_H #ifndef GENL_MAGIC_FAMILY # error "you need to define GENL_MAGIC_FAMILY before inclusion" #endif #ifndef GENL_MAGIC_VERSION # error "you need to define GENL_MAGIC_VERSION before inclusion" #endif #ifndef GENL_MAGIC_INCLUDE_FILE # error "you need to define GENL_MAGIC_INCLUDE_FILE before inclusion" #endif #include #include #include #define CONCAT__(a,b) a ## b #define CONCAT_(a,b) CONCAT__(a,b) extern int CONCAT_(GENL_MAGIC_FAMILY, _genl_register)(void); extern void CONCAT_(GENL_MAGIC_FAMILY, _genl_unregister)(void); /* * Extension of genl attribute validation policies {{{2 */ /* * @DRBD_GENLA_F_MANDATORY: By default, netlink ignores attributes it does not * know about. This flag can be set in nlattr->nla_type to indicate that this * attribute must not be ignored. * * We check and remove this flag in drbd_nla_check_mandatory() before * validating the attribute types and lengths via nla_parse_nested(). */ #define DRBD_GENLA_F_MANDATORY (1 << 14) /* * Flags specific to drbd and not visible at the netlink layer, used in * _from_attrs and _to_skb: * * @DRBD_F_REQUIRED: Attribute is required; a request without this attribute is * invalid. * * @DRBD_F_SENSITIVE: Attribute includes sensitive information and must not be * included in unpriviledged get requests or broadcasts. * * @DRBD_F_INVARIANT: Attribute is set when an object is initially created, but * cannot subsequently be changed. */ #define DRBD_F_REQUIRED (1 << 0) #define DRBD_F_SENSITIVE (1 << 1) #define DRBD_F_INVARIANT (1 << 2) #define __nla_type(x) ((__u16)((x) & NLA_TYPE_MASK & ~DRBD_GENLA_F_MANDATORY)) /* }}}1 * MAGIC * multi-include macro expansion magic starts here */ /* MAGIC helpers {{{2 */ /* possible field types */ #define __flg_field(attr_nr, attr_flag, name) \ __field(attr_nr, attr_flag, name, NLA_U8, char, \ nla_get_u8, nla_put_u8, false) #define __u8_field(attr_nr, attr_flag, name) \ __field(attr_nr, attr_flag, name, NLA_U8, unsigned char, \ nla_get_u8, nla_put_u8, false) #define __u16_field(attr_nr, attr_flag, name) \ __field(attr_nr, attr_flag, name, NLA_U16, __u16, \ nla_get_u16, nla_put_u16, false) #define __u32_field(attr_nr, attr_flag, name) \ __field(attr_nr, attr_flag, name, NLA_U32, __u32, \ nla_get_u32, nla_put_u32, false) #define __s32_field(attr_nr, attr_flag, name) \ __field(attr_nr, attr_flag, name, NLA_U32, __s32, \ nla_get_u32, nla_put_u32, true) #define __u64_field(attr_nr, attr_flag, name) \ __field(attr_nr, attr_flag, name, NLA_U64, __u64, \ nla_get_u64, nla_put_u64, false) #define __str_field(attr_nr, attr_flag, name, maxlen) \ __array(attr_nr, attr_flag, name, NLA_NUL_STRING, char, maxlen, \ nla_strlcpy, nla_put, false) #define __bin_field(attr_nr, attr_flag, name, maxlen) \ __array(attr_nr, attr_flag, name, NLA_BINARY, char, maxlen, \ nla_memcpy, nla_put, false) /* fields with default values */ #define __flg_field_def(attr_nr, attr_flag, name, default) \ __flg_field(attr_nr, attr_flag, name) #define __u32_field_def(attr_nr, attr_flag, name, default) \ __u32_field(attr_nr, attr_flag, name) #define __s32_field_def(attr_nr, attr_flag, name, default) \ __s32_field(attr_nr, attr_flag, name) #define __str_field_def(attr_nr, attr_flag, name, maxlen) \ __str_field(attr_nr, attr_flag, name, maxlen) #define GENL_op_init(args...) args #define GENL_doit(handler) \ .doit = handler, \ .flags = GENL_ADMIN_PERM, #define GENL_dumpit(handler) \ .dumpit = handler, \ .flags = GENL_ADMIN_PERM, /* }}}1 * Magic: define the enum symbols for genl_ops * Magic: define the enum symbols for top level attributes * Magic: define the enum symbols for nested attributes * {{{2 */ #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) #undef GENL_mc_group #define GENL_mc_group(group) #undef GENL_notification #define GENL_notification(op_name, op_num, mcast_group, tla_list) \ op_name = op_num, #undef GENL_op #define GENL_op(op_name, op_num, handler, tla_list) \ op_name = op_num, enum { #include GENL_MAGIC_INCLUDE_FILE }; #undef GENL_notification #define GENL_notification(op_name, op_num, mcast_group, tla_list) #undef GENL_op #define GENL_op(op_name, op_num, handler, attr_list) #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) \ tag_name = tag_number, enum { #include GENL_MAGIC_INCLUDE_FILE }; #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) \ enum { \ s_fields \ }; #undef __field #define __field(attr_nr, attr_flag, name, nla_type, type, \ __get, __put, __is_signed) \ T_ ## name = (__u16)(attr_nr | ((attr_flag) & DRBD_GENLA_F_MANDATORY)), #undef __array #define __array(attr_nr, attr_flag, name, nla_type, type, \ maxlen, __get, __put, __is_signed) \ T_ ## name = (__u16)(attr_nr | ((attr_flag) & DRBD_GENLA_F_MANDATORY)), #include GENL_MAGIC_INCLUDE_FILE /* }}}1 * Magic: compile time assert unique numbers for operations * Magic: -"- unique numbers for top level attributes * Magic: -"- unique numbers for nested attributes * {{{2 */ #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) #undef GENL_op #define GENL_op(op_name, op_num, handler, attr_list) \ case op_name: #undef GENL_notification #define GENL_notification(op_name, op_num, mcast_group, tla_list) \ case op_name: static inline void ct_assert_unique_operations(void) { switch (0) { #include GENL_MAGIC_INCLUDE_FILE ; } } #undef GENL_op #define GENL_op(op_name, op_num, handler, attr_list) #undef GENL_notification #define GENL_notification(op_name, op_num, mcast_group, tla_list) #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) \ case tag_number: static inline void ct_assert_unique_top_level_attributes(void) { switch (0) { #include GENL_MAGIC_INCLUDE_FILE ; } } #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) \ static inline void ct_assert_unique_ ## s_name ## _attributes(void) \ { \ switch (0) { \ s_fields \ ; \ } \ } #undef __field #define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put, \ __is_signed) \ case attr_nr: #undef __array #define __array(attr_nr, attr_flag, name, nla_type, type, maxlen, \ __get, __put, __is_signed) \ case attr_nr: #include GENL_MAGIC_INCLUDE_FILE /* }}}1 * Magic: declare structs * struct { * fields * }; * {{{2 */ #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) \ struct s_name { s_fields }; #undef __field #define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put, \ __is_signed) \ type name; #undef __array #define __array(attr_nr, attr_flag, name, nla_type, type, maxlen, \ __get, __put, __is_signed) \ type name[maxlen]; \ __u32 name ## _len; #include GENL_MAGIC_INCLUDE_FILE #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) \ enum { \ s_fields \ }; #undef __field #define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put, \ is_signed) \ F_ ## name ## _IS_SIGNED = is_signed, #undef __array #define __array(attr_nr, attr_flag, name, nla_type, type, maxlen, \ __get, __put, is_signed) \ F_ ## name ## _IS_SIGNED = is_signed, #include GENL_MAGIC_INCLUDE_FILE /* }}}1 */ #endif /* GENL_MAGIC_STRUCT_H */ /* vim: set foldmethod=marker nofoldenable : */ drbd-utils-8.9.10/user/v84/linux/drbd_limits.h0000644000175000017500000001713712632556027021002 0ustar apoikosapoikos/* drbd_limits.h This file is part of DRBD by Philipp Reisner and Lars Ellenberg. */ /* * Our current limitations. * Some of them are hard limits, * some of them are arbitrary range limits, that make it easier to provide * feedback about nonsense settings for certain configurable values. */ #ifndef DRBD_LIMITS_H #define DRBD_LIMITS_H 1 #define DEBUG_RANGE_CHECK 0 #define DRBD_MINOR_COUNT_MIN 1 #define DRBD_MINOR_COUNT_MAX 255 #define DRBD_MINOR_COUNT_DEF 32 #define DRBD_MINOR_COUNT_SCALE '1' #define DRBD_VOLUME_MAX 65535 #define DRBD_DIALOG_REFRESH_MIN 0 #define DRBD_DIALOG_REFRESH_MAX 600 #define DRBD_DIALOG_REFRESH_SCALE '1' /* valid port number */ #define DRBD_PORT_MIN 1 #define DRBD_PORT_MAX 0xffff #define DRBD_PORT_SCALE '1' /* startup { */ /* if you want more than 3.4 days, disable */ #define DRBD_WFC_TIMEOUT_MIN 0 #define DRBD_WFC_TIMEOUT_MAX 300000 #define DRBD_WFC_TIMEOUT_DEF 0 #define DRBD_WFC_TIMEOUT_SCALE '1' #define DRBD_DEGR_WFC_TIMEOUT_MIN 0 #define DRBD_DEGR_WFC_TIMEOUT_MAX 300000 #define DRBD_DEGR_WFC_TIMEOUT_DEF 0 #define DRBD_DEGR_WFC_TIMEOUT_SCALE '1' #define DRBD_OUTDATED_WFC_TIMEOUT_MIN 0 #define DRBD_OUTDATED_WFC_TIMEOUT_MAX 300000 #define DRBD_OUTDATED_WFC_TIMEOUT_DEF 0 #define DRBD_OUTDATED_WFC_TIMEOUT_SCALE '1' /* }*/ /* net { */ /* timeout, unit centi seconds * more than one minute timeout is not useful */ #define DRBD_TIMEOUT_MIN 1 #define DRBD_TIMEOUT_MAX 600 #define DRBD_TIMEOUT_DEF 60 /* 6 seconds */ #define DRBD_TIMEOUT_SCALE '1' /* If backing disk takes longer than disk_timeout, mark the disk as failed */ #define DRBD_DISK_TIMEOUT_MIN 0 /* 0 = disabled */ #define DRBD_DISK_TIMEOUT_MAX 6000 /* 10 Minutes */ #define DRBD_DISK_TIMEOUT_DEF 0 /* disabled */ #define DRBD_DISK_TIMEOUT_SCALE '1' /* active connection retries when C_WF_CONNECTION */ #define DRBD_CONNECT_INT_MIN 1 #define DRBD_CONNECT_INT_MAX 120 #define DRBD_CONNECT_INT_DEF 10 /* seconds */ #define DRBD_CONNECT_INT_SCALE '1' /* keep-alive probes when idle */ #define DRBD_PING_INT_MIN 1 #define DRBD_PING_INT_MAX 120 #define DRBD_PING_INT_DEF 10 #define DRBD_PING_INT_SCALE '1' /* timeout for the ping packets.*/ #define DRBD_PING_TIMEO_MIN 1 #define DRBD_PING_TIMEO_MAX 300 #define DRBD_PING_TIMEO_DEF 5 #define DRBD_PING_TIMEO_SCALE '1' /* max number of write requests between write barriers */ #define DRBD_MAX_EPOCH_SIZE_MIN 1 #define DRBD_MAX_EPOCH_SIZE_MAX 20000 #define DRBD_MAX_EPOCH_SIZE_DEF 2048 #define DRBD_MAX_EPOCH_SIZE_SCALE '1' /* I don't think that a tcp send buffer of more than 10M is useful */ #define DRBD_SNDBUF_SIZE_MIN 0 #define DRBD_SNDBUF_SIZE_MAX (10<<20) #define DRBD_SNDBUF_SIZE_DEF 0 #define DRBD_SNDBUF_SIZE_SCALE '1' #define DRBD_RCVBUF_SIZE_MIN 0 #define DRBD_RCVBUF_SIZE_MAX (10<<20) #define DRBD_RCVBUF_SIZE_DEF 0 #define DRBD_RCVBUF_SIZE_SCALE '1' /* @4k PageSize -> 128kB - 512MB */ #define DRBD_MAX_BUFFERS_MIN 32 #define DRBD_MAX_BUFFERS_MAX 131072 #define DRBD_MAX_BUFFERS_DEF 2048 #define DRBD_MAX_BUFFERS_SCALE '1' /* @4k PageSize -> 4kB - 512MB */ #define DRBD_UNPLUG_WATERMARK_MIN 1 #define DRBD_UNPLUG_WATERMARK_MAX 131072 #define DRBD_UNPLUG_WATERMARK_DEF (DRBD_MAX_BUFFERS_DEF/16) #define DRBD_UNPLUG_WATERMARK_SCALE '1' /* 0 is disabled. * 200 should be more than enough even for very short timeouts */ #define DRBD_KO_COUNT_MIN 0 #define DRBD_KO_COUNT_MAX 200 #define DRBD_KO_COUNT_DEF 7 #define DRBD_KO_COUNT_SCALE '1' /* } */ /* syncer { */ /* FIXME allow rate to be zero? */ #define DRBD_RESYNC_RATE_MIN 1 /* channel bonding 10 GbE, or other hardware */ #define DRBD_RESYNC_RATE_MAX (4 << 20) #define DRBD_RESYNC_RATE_DEF 250 #define DRBD_RESYNC_RATE_SCALE 'k' /* kilobytes */ #define DRBD_AL_EXTENTS_MIN 67 /* we use u16 as "slot number", (u16)~0 is "FREE". * If you use >= 292 kB on-disk ring buffer, * this is the maximum you can use: */ #define DRBD_AL_EXTENTS_MAX 0xfffe #define DRBD_AL_EXTENTS_DEF 1237 #define DRBD_AL_EXTENTS_SCALE '1' #define DRBD_MINOR_NUMBER_MIN -1 #define DRBD_MINOR_NUMBER_MAX ((1 << 20) - 1) #define DRBD_MINOR_NUMBER_DEF -1 #define DRBD_MINOR_NUMBER_SCALE '1' /* } */ /* drbdsetup XY resize -d Z * you are free to reduce the device size to nothing, if you want to. * the upper limit with 64bit kernel, enough ram and flexible meta data * is 1 PiB, currently. */ /* DRBD_MAX_SECTORS */ #define DRBD_DISK_SIZE_MIN 0 #define DRBD_DISK_SIZE_MAX (1 * (2LLU << 40)) #define DRBD_DISK_SIZE_DEF 0 /* = disabled = no user size... */ #define DRBD_DISK_SIZE_SCALE 's' /* sectors */ #define DRBD_ON_IO_ERROR_DEF EP_DETACH #define DRBD_FENCING_DEF FP_DONT_CARE #define DRBD_AFTER_SB_0P_DEF ASB_DISCONNECT #define DRBD_AFTER_SB_1P_DEF ASB_DISCONNECT #define DRBD_AFTER_SB_2P_DEF ASB_DISCONNECT #define DRBD_RR_CONFLICT_DEF ASB_DISCONNECT #define DRBD_ON_NO_DATA_DEF OND_IO_ERROR #define DRBD_ON_CONGESTION_DEF OC_BLOCK #define DRBD_READ_BALANCING_DEF RB_PREFER_LOCAL #define DRBD_MAX_BIO_BVECS_MIN 0 #define DRBD_MAX_BIO_BVECS_MAX 128 #define DRBD_MAX_BIO_BVECS_DEF 0 #define DRBD_MAX_BIO_BVECS_SCALE '1' #define DRBD_C_PLAN_AHEAD_MIN 0 #define DRBD_C_PLAN_AHEAD_MAX 300 #define DRBD_C_PLAN_AHEAD_DEF 20 #define DRBD_C_PLAN_AHEAD_SCALE '1' #define DRBD_C_DELAY_TARGET_MIN 1 #define DRBD_C_DELAY_TARGET_MAX 100 #define DRBD_C_DELAY_TARGET_DEF 10 #define DRBD_C_DELAY_TARGET_SCALE '1' #define DRBD_C_FILL_TARGET_MIN 0 #define DRBD_C_FILL_TARGET_MAX (1<<20) /* 500MByte in sec */ #define DRBD_C_FILL_TARGET_DEF 100 /* Try to place 50KiB in socket send buffer during resync */ #define DRBD_C_FILL_TARGET_SCALE 's' /* sectors */ #define DRBD_C_MAX_RATE_MIN 250 #define DRBD_C_MAX_RATE_MAX (4 << 20) #define DRBD_C_MAX_RATE_DEF 102400 #define DRBD_C_MAX_RATE_SCALE 'k' /* kilobytes */ #define DRBD_C_MIN_RATE_MIN 0 #define DRBD_C_MIN_RATE_MAX (4 << 20) #define DRBD_C_MIN_RATE_DEF 250 #define DRBD_C_MIN_RATE_SCALE 'k' /* kilobytes */ #define DRBD_CONG_FILL_MIN 0 #define DRBD_CONG_FILL_MAX (10<<21) /* 10GByte in sectors */ #define DRBD_CONG_FILL_DEF 0 #define DRBD_CONG_FILL_SCALE 's' /* sectors */ #define DRBD_CONG_EXTENTS_MIN DRBD_AL_EXTENTS_MIN #define DRBD_CONG_EXTENTS_MAX DRBD_AL_EXTENTS_MAX #define DRBD_CONG_EXTENTS_DEF DRBD_AL_EXTENTS_DEF #define DRBD_CONG_EXTENTS_SCALE DRBD_AL_EXTENTS_SCALE #define DRBD_PROTOCOL_DEF DRBD_PROT_C #define DRBD_DISK_BARRIER_DEF 0 #define DRBD_DISK_FLUSHES_DEF 1 #define DRBD_DISK_DRAIN_DEF 1 #define DRBD_MD_FLUSHES_DEF 1 #define DRBD_TCP_CORK_DEF 1 #define DRBD_AL_UPDATES_DEF 1 /* We used to ignore the discard_zeroes_data setting. * To not change established (and expected) behaviour, * by default assume that, for discard_zeroes_data=0, * we can make that an effective discard_zeroes_data=1, * if we only explicitly zero-out unaligned partial chunks. */ #define DRBD_DISCARD_ZEROES_IF_ALIGNED_DEF 1 #define DRBD_ALLOW_TWO_PRIMARIES_DEF 0 #define DRBD_ALWAYS_ASBP_DEF 0 #define DRBD_USE_RLE_DEF 1 #define DRBD_CSUMS_AFTER_CRASH_ONLY_DEF 0 #define DRBD_AL_STRIPES_MIN 1 #define DRBD_AL_STRIPES_MAX 1024 #define DRBD_AL_STRIPES_DEF 1 #define DRBD_AL_STRIPES_SCALE '1' #define DRBD_AL_STRIPE_SIZE_MIN 4 #define DRBD_AL_STRIPE_SIZE_MAX 16777216 #define DRBD_AL_STRIPE_SIZE_DEF 32 #define DRBD_AL_STRIPE_SIZE_SCALE 'k' /* kilobytes */ #define DRBD_SOCKET_CHECK_TIMEO_MIN 0 #define DRBD_SOCKET_CHECK_TIMEO_MAX DRBD_PING_TIMEO_MAX #define DRBD_SOCKET_CHECK_TIMEO_DEF 0 #define DRBD_SOCKET_CHECK_TIMEO_SCALE '1' #define DRBD_RS_DISCARD_GRANULARITY_MIN 0 #define DRBD_RS_DISCARD_GRANULARITY_MAX (1<<20) /* 1MiByte */ #define DRBD_RS_DISCARD_GRANULARITY_DEF 0 /* disabled by default */ #define DRBD_RS_DISCARD_GRANULARITY_SCALE '1' /* sectors */ #endif drbd-utils-8.9.10/user/v84/linux/drbd.h0000644000175000017500000002470712466702074017422 0ustar apoikosapoikos/* drbd.h Kernel module for 2.6.x Kernels This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. Copyright (C) 2001-2008, Philipp Reisner . Copyright (C) 2001-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef DRBD_H #define DRBD_H #include #include #ifdef __KERNEL__ #include #include #else #include #include #include /* Although the Linux source code makes a difference between generic endianness and the bitfields' endianness, there is no architecture as of Linux-2.6.24-rc4 where the bitfields' endianness does not match the generic endianness. */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define __LITTLE_ENDIAN_BITFIELD #elif __BYTE_ORDER == __BIG_ENDIAN #define __BIG_ENDIAN_BITFIELD #else # error "sorry, weird endianness on this box" #endif #endif enum drbd_io_error_p { EP_PASS_ON, /* FIXME should the better be named "Ignore"? */ EP_CALL_HELPER, EP_DETACH }; enum drbd_fencing_p { FP_NOT_AVAIL = -1, /* Not a policy */ FP_DONT_CARE = 0, FP_RESOURCE, FP_STONITH }; enum drbd_disconnect_p { DP_RECONNECT, DP_DROP_NET_CONF, DP_FREEZE_IO }; enum drbd_after_sb_p { ASB_DISCONNECT, ASB_DISCARD_YOUNGER_PRI, ASB_DISCARD_OLDER_PRI, ASB_DISCARD_ZERO_CHG, ASB_DISCARD_LEAST_CHG, ASB_DISCARD_LOCAL, ASB_DISCARD_REMOTE, ASB_CONSENSUS, ASB_DISCARD_SECONDARY, ASB_CALL_HELPER, ASB_VIOLENTLY }; enum drbd_on_no_data { OND_IO_ERROR, OND_SUSPEND_IO }; enum drbd_on_congestion { OC_BLOCK, OC_PULL_AHEAD, OC_DISCONNECT, }; enum drbd_read_balancing { RB_PREFER_LOCAL, RB_PREFER_REMOTE, RB_ROUND_ROBIN, RB_LEAST_PENDING, RB_CONGESTED_REMOTE, RB_32K_STRIPING, RB_64K_STRIPING, RB_128K_STRIPING, RB_256K_STRIPING, RB_512K_STRIPING, RB_1M_STRIPING, }; /* KEEP the order, do not delete or insert. Only append. */ enum drbd_ret_code { ERR_CODE_BASE = 100, NO_ERROR = 101, ERR_LOCAL_ADDR = 102, ERR_PEER_ADDR = 103, ERR_OPEN_DISK = 104, ERR_OPEN_MD_DISK = 105, ERR_DISK_NOT_BDEV = 107, ERR_MD_NOT_BDEV = 108, ERR_DISK_TOO_SMALL = 111, ERR_MD_DISK_TOO_SMALL = 112, ERR_BDCLAIM_DISK = 114, ERR_BDCLAIM_MD_DISK = 115, ERR_MD_IDX_INVALID = 116, ERR_IO_MD_DISK = 118, ERR_MD_INVALID = 119, ERR_AUTH_ALG = 120, ERR_AUTH_ALG_ND = 121, ERR_NOMEM = 122, ERR_DISCARD_IMPOSSIBLE = 123, ERR_DISK_CONFIGURED = 124, ERR_NET_CONFIGURED = 125, ERR_MANDATORY_TAG = 126, ERR_MINOR_INVALID = 127, ERR_INTR = 129, /* EINTR */ ERR_RESIZE_RESYNC = 130, ERR_NO_PRIMARY = 131, ERR_RESYNC_AFTER = 132, ERR_RESYNC_AFTER_CYCLE = 133, ERR_PAUSE_IS_SET = 134, ERR_PAUSE_IS_CLEAR = 135, ERR_PACKET_NR = 137, ERR_NO_DISK = 138, ERR_NOT_PROTO_C = 139, ERR_NOMEM_BITMAP = 140, ERR_INTEGRITY_ALG = 141, /* DRBD 8.2 only */ ERR_INTEGRITY_ALG_ND = 142, /* DRBD 8.2 only */ ERR_CPU_MASK_PARSE = 143, /* DRBD 8.2 only */ ERR_CSUMS_ALG = 144, /* DRBD 8.2 only */ ERR_CSUMS_ALG_ND = 145, /* DRBD 8.2 only */ ERR_VERIFY_ALG = 146, /* DRBD 8.2 only */ ERR_VERIFY_ALG_ND = 147, /* DRBD 8.2 only */ ERR_CSUMS_RESYNC_RUNNING= 148, /* DRBD 8.2 only */ ERR_VERIFY_RUNNING = 149, /* DRBD 8.2 only */ ERR_DATA_NOT_CURRENT = 150, ERR_CONNECTED = 151, /* DRBD 8.3 only */ ERR_PERM = 152, ERR_NEED_APV_93 = 153, ERR_STONITH_AND_PROT_A = 154, ERR_CONG_NOT_PROTO_A = 155, ERR_PIC_AFTER_DEP = 156, ERR_PIC_PEER_DEP = 157, ERR_RES_NOT_KNOWN = 158, ERR_RES_IN_USE = 159, ERR_MINOR_CONFIGURED = 160, ERR_MINOR_OR_VOLUME_EXISTS = 161, ERR_INVALID_REQUEST = 162, ERR_NEED_APV_100 = 163, ERR_NEED_ALLOW_TWO_PRI = 164, ERR_MD_UNCLEAN = 165, ERR_MD_LAYOUT_CONNECTED = 166, ERR_MD_LAYOUT_TOO_BIG = 167, ERR_MD_LAYOUT_TOO_SMALL = 168, ERR_MD_LAYOUT_NO_FIT = 169, ERR_IMPLICIT_SHRINK = 170, /* insert new ones above this line */ AFTER_LAST_ERR_CODE }; #define DRBD_PROT_A 1 #define DRBD_PROT_B 2 #define DRBD_PROT_C 3 enum drbd_role { R_UNKNOWN = 0, R_PRIMARY = 1, /* role */ R_SECONDARY = 2, /* role */ R_MASK = 3, }; /* The order of these constants is important. * The lower ones (=C_WF_REPORT_PARAMS ==> There is a socket */ enum drbd_conns { C_STANDALONE, C_DISCONNECTING, /* Temporal state on the way to StandAlone. */ C_UNCONNECTED, /* >= C_UNCONNECTED -> inc_net() succeeds */ /* These temporal states are all used on the way * from >= C_CONNECTED to Unconnected. * The 'disconnect reason' states * I do not allow to change between them. */ C_TIMEOUT, C_BROKEN_PIPE, C_NETWORK_FAILURE, C_PROTOCOL_ERROR, C_TEAR_DOWN, C_WF_CONNECTION, C_WF_REPORT_PARAMS, /* we have a socket */ C_CONNECTED, /* we have introduced each other */ C_STARTING_SYNC_S, /* starting full sync by admin request. */ C_STARTING_SYNC_T, /* starting full sync by admin request. */ C_WF_BITMAP_S, C_WF_BITMAP_T, C_WF_SYNC_UUID, /* All SyncStates are tested with this comparison * xx >= C_SYNC_SOURCE && xx <= C_PAUSED_SYNC_T */ C_SYNC_SOURCE, C_SYNC_TARGET, C_VERIFY_S, C_VERIFY_T, C_PAUSED_SYNC_S, C_PAUSED_SYNC_T, C_AHEAD, C_BEHIND, C_MASK = 31 }; enum drbd_disk_state { D_DISKLESS, D_ATTACHING, /* In the process of reading the meta-data */ D_FAILED, /* Becomes D_DISKLESS as soon as we told it the peer */ /* when >= D_FAILED it is legal to access mdev->ldev */ D_NEGOTIATING, /* Late attaching state, we need to talk to the peer */ D_INCONSISTENT, D_OUTDATED, D_UNKNOWN, /* Only used for the peer, never for myself */ D_CONSISTENT, /* Might be D_OUTDATED, might be D_UP_TO_DATE ... */ D_UP_TO_DATE, /* Only this disk state allows applications' IO ! */ D_MASK = 15 }; union drbd_state { /* According to gcc's docs is the ... * The order of allocation of bit-fields within a unit (C90 6.5.2.1, C99 6.7.2.1). * Determined by ABI. * pointed out by Maxim Uvarov q * even though we transmit as "cpu_to_be32(state)", * the offsets of the bitfields still need to be swapped * on different endianness. */ struct { #if defined(__LITTLE_ENDIAN_BITFIELD) unsigned role:2 ; /* 3/4 primary/secondary/unknown */ unsigned peer:2 ; /* 3/4 primary/secondary/unknown */ unsigned conn:5 ; /* 17/32 cstates */ unsigned disk:4 ; /* 8/16 from D_DISKLESS to D_UP_TO_DATE */ unsigned pdsk:4 ; /* 8/16 from D_DISKLESS to D_UP_TO_DATE */ unsigned susp:1 ; /* 2/2 IO suspended no/yes (by user) */ unsigned aftr_isp:1 ; /* isp .. imposed sync pause */ unsigned peer_isp:1 ; unsigned user_isp:1 ; unsigned susp_nod:1 ; /* IO suspended because no data */ unsigned susp_fen:1 ; /* IO suspended because fence peer handler runs*/ unsigned _pad:9; /* 0 unused */ #elif defined(__BIG_ENDIAN_BITFIELD) unsigned _pad:9; unsigned susp_fen:1 ; unsigned susp_nod:1 ; unsigned user_isp:1 ; unsigned peer_isp:1 ; unsigned aftr_isp:1 ; /* isp .. imposed sync pause */ unsigned susp:1 ; /* 2/2 IO suspended no/yes */ unsigned pdsk:4 ; /* 8/16 from D_DISKLESS to D_UP_TO_DATE */ unsigned disk:4 ; /* 8/16 from D_DISKLESS to D_UP_TO_DATE */ unsigned conn:5 ; /* 17/32 cstates */ unsigned peer:2 ; /* 3/4 primary/secondary/unknown */ unsigned role:2 ; /* 3/4 primary/secondary/unknown */ #else # error "this endianness is not supported" #endif }; unsigned int i; }; enum drbd_state_rv { SS_CW_NO_NEED = 4, SS_CW_SUCCESS = 3, SS_NOTHING_TO_DO = 2, SS_SUCCESS = 1, SS_UNKNOWN_ERROR = 0, /* Used to sleep longer in _drbd_request_state */ SS_TWO_PRIMARIES = -1, SS_NO_UP_TO_DATE_DISK = -2, SS_NO_LOCAL_DISK = -4, SS_NO_REMOTE_DISK = -5, SS_CONNECTED_OUTDATES = -6, SS_PRIMARY_NOP = -7, SS_RESYNC_RUNNING = -8, SS_ALREADY_STANDALONE = -9, SS_CW_FAILED_BY_PEER = -10, SS_IS_DISKLESS = -11, SS_DEVICE_IN_USE = -12, SS_NO_NET_CONFIG = -13, SS_NO_VERIFY_ALG = -14, /* drbd-8.2 only */ SS_NEED_CONNECTION = -15, /* drbd-8.2 only */ SS_LOWER_THAN_OUTDATED = -16, SS_NOT_SUPPORTED = -17, /* drbd-8.2 only */ SS_IN_TRANSIENT_STATE = -18, /* Retry after the next state change */ SS_CONCURRENT_ST_CHG = -19, /* Concurrent cluster side state change! */ SS_O_VOL_PEER_PRI = -20, SS_OUTDATE_WO_CONN = -21, SS_AFTER_LAST_ERROR = -22, /* Keep this at bottom */ }; #define SHARED_SECRET_MAX 64 #define MDF_CONSISTENT (1 << 0) #define MDF_PRIMARY_IND (1 << 1) #define MDF_CONNECTED_IND (1 << 2) #define MDF_FULL_SYNC (1 << 3) #define MDF_WAS_UP_TO_DATE (1 << 4) #define MDF_PEER_OUT_DATED (1 << 5) #define MDF_CRASHED_PRIMARY (1 << 6) #define MDF_AL_CLEAN (1 << 7) #define MDF_AL_DISABLED (1 << 8) #define MAX_PEERS 32 enum drbd_uuid_index { UI_CURRENT, UI_BITMAP, UI_HISTORY_START, UI_HISTORY_END, UI_SIZE, /* nl-packet: number of dirty bits */ UI_FLAGS, /* nl-packet: flags */ UI_EXTENDED_SIZE /* Everything. */ }; #define HISTORY_UUIDS MAX_PEERS enum drbd_timeout_flag { UT_DEFAULT = 0, UT_DEGRADED = 1, UT_PEER_OUTDATED = 2, }; #define UUID_JUST_CREATED ((__u64)4) enum write_ordering_e { WO_NONE, WO_DRAIN_IO, WO_BDEV_FLUSH, WO_BIO_BARRIER }; enum drbd_notification_type { NOTIFY_EXISTS, NOTIFY_CREATE, NOTIFY_CHANGE, NOTIFY_DESTROY, NOTIFY_CALL, NOTIFY_RESPONSE, NOTIFY_CONTINUES = 0x8000, NOTIFY_FLAGS = NOTIFY_CONTINUES, }; /* magic numbers used in meta data and network packets */ #define DRBD_MAGIC 0x83740267 #define DRBD_MAGIC_BIG 0x835a #define DRBD_MAGIC_100 0x8620ec20 #define DRBD_MD_MAGIC_07 (DRBD_MAGIC+3) #define DRBD_MD_MAGIC_08 (DRBD_MAGIC+4) #define DRBD_MD_MAGIC_84_UNCLEAN (DRBD_MAGIC+5) /* how I came up with this magic? * base64 decode "actlog==" ;) */ #define DRBD_AL_MAGIC 0x69cb65a2 /* these are of type "int" */ #define DRBD_MD_INDEX_INTERNAL -1 #define DRBD_MD_INDEX_FLEX_EXT -2 #define DRBD_MD_INDEX_FLEX_INT -3 #define DRBD_CPU_MASK_SIZE 32 #endif drbd-utils-8.9.10/user/v84/linux/genl_magic_func.h0000644000175000017500000003027012466702074021577 0ustar apoikosapoikos#ifndef GENL_MAGIC_FUNC_H #define GENL_MAGIC_FUNC_H #include /* * Magic: declare tla policy {{{1 * Magic: declare nested policies * {{{2 */ #undef GENL_mc_group #define GENL_mc_group(group) #undef GENL_notification #define GENL_notification(op_name, op_num, mcast_group, tla_list) #undef GENL_op #define GENL_op(op_name, op_num, handler, tla_list) #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) \ [tag_name] = { .type = NLA_NESTED }, static struct nla_policy CONCAT_(GENL_MAGIC_FAMILY, _tla_nl_policy)[] \ __attribute__((unused)) = { #include GENL_MAGIC_INCLUDE_FILE }; #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) \ static struct nla_policy s_name ## _nl_policy[] __read_mostly = \ { s_fields }; #undef __field #define __field(attr_nr, attr_flag, name, nla_type, _type, __get, \ __put, __is_signed) \ [attr_nr] = { .type = nla_type }, #undef __array #define __array(attr_nr, attr_flag, name, nla_type, _type, maxlen, \ __get, __put, __is_signed) \ [attr_nr] = { .type = nla_type, \ .len = maxlen - (nla_type == NLA_NUL_STRING) }, #include GENL_MAGIC_INCLUDE_FILE #ifndef __KERNEL__ #ifndef pr_info #define pr_info(args...) fprintf(stderr, args); #endif #endif #ifdef GENL_MAGIC_DEBUG static void dprint_field(const char *dir, int nla_type, const char *name, void *valp) { __u64 val = valp ? *(__u32 *)valp : 1; switch (nla_type) { case NLA_U8: val = (__u8)val; case NLA_U16: val = (__u16)val; case NLA_U32: val = (__u32)val; pr_info("%s attr %s: %d 0x%08x\n", dir, name, (int)val, (unsigned)val); break; case NLA_U64: val = *(__u64*)valp; pr_info("%s attr %s: %lld 0x%08llx\n", dir, name, (long long)val, (unsigned long long)val); break; case NLA_FLAG: if (val) pr_info("%s attr %s: set\n", dir, name); break; } } static void dprint_array(const char *dir, int nla_type, const char *name, const char *val, unsigned len) { switch (nla_type) { case NLA_NUL_STRING: if (len && val[len-1] == '\0') len--; pr_info("%s attr %s: [len:%u] '%s'\n", dir, name, len, val); break; default: /* we can always show 4 byte, * thats what nlattr are aligned to. */ pr_info("%s attr %s: [len:%u] %02x%02x%02x%02x ...\n", dir, name, len, val[0], val[1], val[2], val[3]); } } #define DPRINT_TLA(a, op, b) pr_info("%s %s %s\n", a, op, b); /* Name is a member field name of the struct s. * If s is NULL (only parsing, no copy requested in *_from_attrs()), * nla is supposed to point to the attribute containing the information * corresponding to that struct member. */ #define DPRINT_FIELD(dir, nla_type, name, s, nla) \ do { \ if (s) \ dprint_field(dir, nla_type, #name, &s->name); \ else if (nla) \ dprint_field(dir, nla_type, #name, \ (nla_type == NLA_FLAG) ? NULL \ : nla_data(nla)); \ } while (0) #define DPRINT_ARRAY(dir, nla_type, name, s, nla) \ do { \ if (s) \ dprint_array(dir, nla_type, #name, \ s->name, s->name ## _len); \ else if (nla) \ dprint_array(dir, nla_type, #name, \ nla_data(nla), nla_len(nla)); \ } while (0) #else #define DPRINT_TLA(a, op, b) do {} while (0) #define DPRINT_FIELD(dir, nla_type, name, s, nla) do {} while (0) #define DPRINT_ARRAY(dir, nla_type, name, s, nla) do {} while (0) #endif /* * Magic: provide conversion functions {{{1 * populate struct from attribute table: * {{{2 */ /* processing of generic netlink messages is serialized. * use one static buffer for parsing of nested attributes */ static struct nlattr *nested_attr_tb[128]; #ifndef BUILD_BUG_ON /* Force a compilation error if condition is true */ #define BUILD_BUG_ON(condition) ((void)BUILD_BUG_ON_ZERO(condition)) /* Force a compilation error if condition is true, but also produce a result (of value 0 and type size_t), so the expression can be used e.g. in a structure initializer (or where-ever else comma expressions aren't permitted). */ #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); })) #define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); })) #endif #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) \ static int __ ## s_name ## _from_attrs(struct s_name *s, \ struct genl_info *info, bool exclude_invariants) \ { \ const int maxtype = ARRAY_SIZE(s_name ## _nl_policy)-1; \ struct nlattr *tla = info->attrs[tag_number]; \ struct nlattr **ntb = nested_attr_tb; \ struct nlattr *nla; \ int err; \ BUILD_BUG_ON(ARRAY_SIZE(s_name ## _nl_policy) > ARRAY_SIZE(nested_attr_tb)); \ if (!tla) \ return -ENOMSG; \ DPRINT_TLA(#s_name, "<=-", #tag_name); \ err = drbd_nla_parse_nested(ntb, maxtype, tla, s_name ## _nl_policy); \ if (err) \ return err; \ \ s_fields \ return 0; \ } __attribute__((unused)) \ static int s_name ## _from_attrs(struct s_name *s, \ struct genl_info *info) \ { \ return __ ## s_name ## _from_attrs(s, info, false); \ } __attribute__((unused)) \ static int s_name ## _from_attrs_for_change(struct s_name *s, \ struct genl_info *info) \ { \ return __ ## s_name ## _from_attrs(s, info, true); \ } __attribute__((unused)) \ #define __assign(attr_nr, attr_flag, name, nla_type, type, assignment...) \ nla = ntb[attr_nr]; \ if (nla) { \ if (exclude_invariants && ((attr_flag) & DRBD_F_INVARIANT)) { \ pr_info("<< must not change invariant attr: %s\n", #name); \ return -EEXIST; \ } \ assignment; \ } else if (exclude_invariants && ((attr_flag) & DRBD_F_INVARIANT)) { \ /* attribute missing from payload, */ \ /* which was expected */ \ } else if ((attr_flag) & DRBD_F_REQUIRED) { \ pr_info("<< missing attr: %s\n", #name); \ return -ENOMSG; \ } #undef __field #define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put, \ __is_signed) \ __assign(attr_nr, attr_flag, name, nla_type, type, \ if (s) \ s->name = __get(nla); \ DPRINT_FIELD("<<", nla_type, name, s, nla)) /* validate_nla() already checked nla_len <= maxlen appropriately. */ #undef __array #define __array(attr_nr, attr_flag, name, nla_type, type, maxlen, \ __get, __put, __is_signed) \ __assign(attr_nr, attr_flag, name, nla_type, type, \ if (s) \ s->name ## _len = \ __get(s->name, nla, maxlen); \ DPRINT_ARRAY("<<", nla_type, name, s, nla)) #include GENL_MAGIC_INCLUDE_FILE #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) /* * Magic: define op number to op name mapping {{{1 * {{{2 */ static const char *CONCAT_(GENL_MAGIC_FAMILY, _genl_cmd_to_str)(__u8 cmd) __attribute__ ((unused)); static const char *CONCAT_(GENL_MAGIC_FAMILY, _genl_cmd_to_str)(__u8 cmd) { switch (cmd) { #undef GENL_op #define GENL_op(op_name, op_num, handler, tla_list) \ case op_num: return #op_name; #include GENL_MAGIC_INCLUDE_FILE default: return "unknown"; } } #ifdef __KERNEL__ #include /* * Magic: define genl_ops {{{1 * {{{2 */ #undef GENL_op #define GENL_op(op_name, op_num, handler, tla_list) \ { \ handler \ .cmd = op_name, \ .policy = CONCAT_(GENL_MAGIC_FAMILY, _tla_nl_policy), \ }, #define ZZZ_genl_ops CONCAT_(GENL_MAGIC_FAMILY, _genl_ops) static struct genl_ops ZZZ_genl_ops[] __read_mostly = { #include GENL_MAGIC_INCLUDE_FILE }; #undef GENL_op #define GENL_op(op_name, op_num, handler, tla_list) /* * Define the genl_family, multicast groups, {{{1 * and provide register/unregister functions. * {{{2 */ #define ZZZ_genl_family CONCAT_(GENL_MAGIC_FAMILY, _genl_family) static struct genl_family ZZZ_genl_family __read_mostly = { .id = GENL_ID_GENERATE, .name = __stringify(GENL_MAGIC_FAMILY), .version = GENL_MAGIC_VERSION, #ifdef GENL_MAGIC_FAMILY_HDRSZ .hdrsize = NLA_ALIGN(GENL_MAGIC_FAMILY_HDRSZ), #endif .maxattr = ARRAY_SIZE(drbd_tla_nl_policy)-1, }; /* * Magic: define multicast groups * Magic: define multicast group registration helper */ #undef GENL_mc_group #define GENL_mc_group(group) \ static struct genl_multicast_group \ CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group) __read_mostly = { \ .name = #group, \ }; \ static int CONCAT_(GENL_MAGIC_FAMILY, _genl_multicast_ ## group)( \ struct sk_buff *skb, gfp_t flags) \ { \ unsigned int group_id = \ CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group).id; \ if (!group_id) \ return -EINVAL; \ return genlmsg_multicast(skb, 0, group_id, flags); \ } #include GENL_MAGIC_INCLUDE_FILE int CONCAT_(GENL_MAGIC_FAMILY, _genl_register)(void) { int err = genl_register_family_with_ops(&ZZZ_genl_family, ZZZ_genl_ops, ARRAY_SIZE(ZZZ_genl_ops)); if (err) return err; #undef GENL_mc_group #define GENL_mc_group(group) \ err = genl_register_mc_group(&ZZZ_genl_family, \ &CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group)); \ if (err) \ goto fail; \ else \ pr_info("%s: mcg %s: %u\n", #group, \ __stringify(GENL_MAGIC_FAMILY), \ CONCAT_(GENL_MAGIC_FAMILY, _mcg_ ## group).id); #include GENL_MAGIC_INCLUDE_FILE #undef GENL_mc_group #define GENL_mc_group(group) return 0; fail: genl_unregister_family(&ZZZ_genl_family); return err; } void CONCAT_(GENL_MAGIC_FAMILY, _genl_unregister)(void) { genl_unregister_family(&ZZZ_genl_family); } /* * Magic: provide conversion functions {{{1 * populate skb from struct. * {{{2 */ #undef GENL_op #define GENL_op(op_name, op_num, handler, tla_list) #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) \ static int s_name ## _to_skb(struct sk_buff *skb, struct s_name *s, \ const bool exclude_sensitive) \ { \ struct nlattr *tla = nla_nest_start(skb, tag_number); \ if (!tla) \ goto nla_put_failure; \ DPRINT_TLA(#s_name, "-=>", #tag_name); \ s_fields \ nla_nest_end(skb, tla); \ return 0; \ \ nla_put_failure: \ if (tla) \ nla_nest_cancel(skb, tla); \ return -EMSGSIZE; \ } \ static inline int s_name ## _to_priv_skb(struct sk_buff *skb, \ struct s_name *s) \ { \ return s_name ## _to_skb(skb, s, 0); \ } \ static inline int s_name ## _to_unpriv_skb(struct sk_buff *skb, \ struct s_name *s) \ { \ return s_name ## _to_skb(skb, s, 1); \ } #undef __field #define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put, \ __is_signed) \ if (!exclude_sensitive || !((attr_flag) & DRBD_F_SENSITIVE)) { \ DPRINT_FIELD(">>", nla_type, name, s, NULL); \ if (__put(skb, attr_nr, s->name)) \ goto nla_put_failure; \ } #undef __array #define __array(attr_nr, attr_flag, name, nla_type, type, maxlen, \ __get, __put, __is_signed) \ if (!exclude_sensitive || !((attr_flag) & DRBD_F_SENSITIVE)) { \ DPRINT_ARRAY(">>",nla_type, name, s, NULL); \ if (__put(skb, attr_nr, min_t(int, maxlen, \ s->name ## _len + (nla_type == NLA_NUL_STRING)),\ s->name)) \ goto nla_put_failure; \ } #include GENL_MAGIC_INCLUDE_FILE /* Functions for initializing structs to default values. */ #undef __field #define __field(attr_nr, attr_flag, name, nla_type, type, __get, __put, \ __is_signed) #undef __array #define __array(attr_nr, attr_flag, name, nla_type, type, maxlen, \ __get, __put, __is_signed) #undef __u32_field_def #define __u32_field_def(attr_nr, attr_flag, name, default) \ x->name = default; #undef __s32_field_def #define __s32_field_def(attr_nr, attr_flag, name, default) \ x->name = default; #undef __flg_field_def #define __flg_field_def(attr_nr, attr_flag, name, default) \ x->name = default; #undef __str_field_def #define __str_field_def(attr_nr, attr_flag, name, maxlen) \ memset(x->name, 0, sizeof(x->name)); \ x->name ## _len = 0; #undef GENL_struct #define GENL_struct(tag_name, tag_number, s_name, s_fields) \ static void set_ ## s_name ## _defaults(struct s_name *x) __attribute__((unused)); \ static void set_ ## s_name ## _defaults(struct s_name *x) { \ s_fields \ } #include GENL_MAGIC_INCLUDE_FILE #endif /* __KERNEL__ */ /* }}}1 */ #endif /* GENL_MAGIC_FUNC_H */ /* vim: set foldmethod=marker foldlevel=1 nofoldenable : */ drbd-utils-8.9.10/user/v84/linux/drbd_genl_api.h0000644000175000017500000000335112466702074021250 0ustar apoikosapoikos#ifndef DRBD_GENL_STRUCT_H #define DRBD_GENL_STRUCT_H /** * struct drbd_genlmsghdr - DRBD specific header used in NETLINK_GENERIC requests * @minor: * For admin requests (user -> kernel): which minor device to operate on. * For (unicast) replies or informational (broadcast) messages * (kernel -> user): which minor device the information is about. * If we do not operate on minors, but on connections or resources, * the minor value shall be (~0), and the attribute DRBD_NLA_CFG_CONTEXT * is used instead. * @flags: possible operation modifiers (relevant only for user->kernel): * DRBD_GENL_F_SET_DEFAULTS * @volume: * When creating a new minor (adding it to a resource), the resource needs * to know which volume number within the resource this is supposed to be. * The volume number corresponds to the same volume number on the remote side, * whereas the minor number on the remote side may be different * (union with flags). * @ret_code: kernel->userland unicast cfg reply return code (union with flags); */ struct drbd_genlmsghdr { __u32 minor; union { __u32 flags; __s32 ret_code; }; }; /* To be used in drbd_genlmsghdr.flags */ enum { DRBD_GENL_F_SET_DEFAULTS = 1, }; enum drbd_state_info_bcast_reason { SIB_GET_STATUS_REPLY = 1, SIB_STATE_CHANGE = 2, SIB_HELPER_PRE = 3, SIB_HELPER_POST = 4, SIB_SYNC_PROGRESS = 5, }; /* hack around predefined gcc/cpp "linux=1", * we cannot possibly include <1/drbd_genl.h> */ #undef linux #include #define GENL_MAGIC_VERSION API_VERSION #define GENL_MAGIC_FAMILY drbd #define GENL_MAGIC_FAMILY_HDRSZ sizeof(struct drbd_genlmsghdr) #define GENL_MAGIC_INCLUDE_FILE #include #endif drbd-utils-8.9.10/user/v84/linux/drbd_genl.h0000644000175000017500000005264712634301641020423 0ustar apoikosapoikos/* * General overview: * full generic netlink message: * |nlmsghdr|genlmsghdr| * * payload: * |optional fixed size family header| * * sequence of netlink attributes: * I chose to have all "top level" attributes NLA_NESTED, * corresponding to some real struct. * So we have a sequence of |tla, len| * * nested nla sequence: * may be empty, or contain a sequence of netlink attributes * representing the struct fields. * * The tag number of any field (regardless of containing struct) * will be available as T_ ## field_name, * so you cannot have the same field name in two differnt structs. * * The tag numbers themselves are per struct, though, * so should always begin at 1 (not 0, that is the special "NLA_UNSPEC" type, * which we won't use here). * The tag numbers are used as index in the respective nla_policy array. * * GENL_struct(tag_name, tag_number, struct name, struct fields) - struct and policy * genl_magic_struct.h * generates the struct declaration, * generates an entry in the tla enum, * genl_magic_func.h * generates an entry in the static tla policy * with .type = NLA_NESTED * generates the static _nl_policy definition, * and static conversion functions * * genl_magic_func.h * * GENL_mc_group(group) * genl_magic_struct.h * does nothing * genl_magic_func.h * defines and registers the mcast group, * and provides a send helper * * GENL_notification(op_name, op_num, mcast_group, tla list) * These are notifications to userspace. * * genl_magic_struct.h * generates an entry in the genl_ops enum, * genl_magic_func.h * does nothing * * mcast group: the name of the mcast group this notification should be * expected on * tla list: the list of expected top level attributes, * for documentation and sanity checking. * * GENL_op(op_name, op_num, flags and handler, tla list) - "genl operations" * These are requests from userspace. * * _op and _notification share the same "number space", * op_nr will be assigned to "genlmsghdr->cmd" * * genl_magic_struct.h * generates an entry in the genl_ops enum, * genl_magic_func.h * generates an entry in the static genl_ops array, * and static register/unregister functions to * genl_register_family_with_ops(). * * flags and handler: * GENL_op_init( .doit = x, .dumpit = y, .flags = something) * GENL_doit(x) => .dumpit = NULL, .flags = GENL_ADMIN_PERM * tla list: the list of expected top level attributes, * for documentation and sanity checking. */ /* * STRUCTS */ /* this is sent kernel -> userland on various error conditions, and contains * informational textual info, which is supposedly human readable. * The computer relevant return code is in the drbd_genlmsghdr. */ GENL_struct(DRBD_NLA_CFG_REPLY, 1, drbd_cfg_reply, /* "arbitrary" size strings, nla_policy.len = 0 */ __str_field(1, DRBD_GENLA_F_MANDATORY, info_text, 0) ) /* Configuration requests typically need a context to operate on. * Possible keys are device minor (fits in the drbd_genlmsghdr), * the replication link (aka connection) name, * and/or the replication group (aka resource) name, * and the volume id within the resource. */ GENL_struct(DRBD_NLA_CFG_CONTEXT, 2, drbd_cfg_context, __u32_field(1, DRBD_GENLA_F_MANDATORY, ctx_volume) __str_field(2, DRBD_GENLA_F_MANDATORY, ctx_resource_name, 128) __bin_field(3, DRBD_GENLA_F_MANDATORY, ctx_my_addr, 128) __bin_field(4, DRBD_GENLA_F_MANDATORY, ctx_peer_addr, 128) ) GENL_struct(DRBD_NLA_DISK_CONF, 3, disk_conf, __str_field(1, DRBD_F_REQUIRED | DRBD_F_INVARIANT, backing_dev, 128) __str_field(2, DRBD_F_REQUIRED | DRBD_F_INVARIANT, meta_dev, 128) __s32_field(3, DRBD_F_REQUIRED | DRBD_F_INVARIANT, meta_dev_idx) /* use the resize command to try and change the disk_size */ __u64_field(4, DRBD_GENLA_F_MANDATORY | DRBD_F_INVARIANT, disk_size) /* we could change the max_bio_bvecs, * but it won't propagate through the stack */ __u32_field(5, DRBD_GENLA_F_MANDATORY | DRBD_F_INVARIANT, max_bio_bvecs) __u32_field_def(6, DRBD_GENLA_F_MANDATORY, on_io_error, DRBD_ON_IO_ERROR_DEF) __u32_field_def(7, DRBD_GENLA_F_MANDATORY, fencing, DRBD_FENCING_DEF) __u32_field_def(8, DRBD_GENLA_F_MANDATORY, resync_rate, DRBD_RESYNC_RATE_DEF) __s32_field_def(9, DRBD_GENLA_F_MANDATORY, resync_after, DRBD_MINOR_NUMBER_DEF) __u32_field_def(10, DRBD_GENLA_F_MANDATORY, al_extents, DRBD_AL_EXTENTS_DEF) __u32_field_def(11, DRBD_GENLA_F_MANDATORY, c_plan_ahead, DRBD_C_PLAN_AHEAD_DEF) __u32_field_def(12, DRBD_GENLA_F_MANDATORY, c_delay_target, DRBD_C_DELAY_TARGET_DEF) __u32_field_def(13, DRBD_GENLA_F_MANDATORY, c_fill_target, DRBD_C_FILL_TARGET_DEF) __u32_field_def(14, DRBD_GENLA_F_MANDATORY, c_max_rate, DRBD_C_MAX_RATE_DEF) __u32_field_def(15, DRBD_GENLA_F_MANDATORY, c_min_rate, DRBD_C_MIN_RATE_DEF) __u32_field_def(20, DRBD_GENLA_F_MANDATORY, disk_timeout, DRBD_DISK_TIMEOUT_DEF) __u32_field_def(21, 0 /* OPTIONAL */, read_balancing, DRBD_READ_BALANCING_DEF) __u32_field_def(25, 0 /* OPTIONAL */, rs_discard_granularity, DRBD_RS_DISCARD_GRANULARITY_DEF) __flg_field_def(16, DRBD_GENLA_F_MANDATORY, disk_barrier, DRBD_DISK_BARRIER_DEF) __flg_field_def(17, DRBD_GENLA_F_MANDATORY, disk_flushes, DRBD_DISK_FLUSHES_DEF) __flg_field_def(18, DRBD_GENLA_F_MANDATORY, disk_drain, DRBD_DISK_DRAIN_DEF) __flg_field_def(19, DRBD_GENLA_F_MANDATORY, md_flushes, DRBD_MD_FLUSHES_DEF) __flg_field_def(23, 0 /* OPTIONAL */, al_updates, DRBD_AL_UPDATES_DEF) __flg_field_def(24, 0 /* OPTIONAL */, discard_zeroes_if_aligned, DRBD_DISCARD_ZEROES_IF_ALIGNED) ) GENL_struct(DRBD_NLA_RESOURCE_OPTS, 4, res_opts, __str_field_def(1, DRBD_GENLA_F_MANDATORY, cpu_mask, 32) __u32_field_def(2, DRBD_GENLA_F_MANDATORY, on_no_data, DRBD_ON_NO_DATA_DEF) ) GENL_struct(DRBD_NLA_NET_CONF, 5, net_conf, __str_field_def(1, DRBD_GENLA_F_MANDATORY | DRBD_F_SENSITIVE, shared_secret, SHARED_SECRET_MAX) __str_field_def(2, DRBD_GENLA_F_MANDATORY, cram_hmac_alg, SHARED_SECRET_MAX) __str_field_def(3, DRBD_GENLA_F_MANDATORY, integrity_alg, SHARED_SECRET_MAX) __str_field_def(4, DRBD_GENLA_F_MANDATORY, verify_alg, SHARED_SECRET_MAX) __str_field_def(5, DRBD_GENLA_F_MANDATORY, csums_alg, SHARED_SECRET_MAX) __u32_field_def(6, DRBD_GENLA_F_MANDATORY, wire_protocol, DRBD_PROTOCOL_DEF) __u32_field_def(7, DRBD_GENLA_F_MANDATORY, connect_int, DRBD_CONNECT_INT_DEF) __u32_field_def(8, DRBD_GENLA_F_MANDATORY, timeout, DRBD_TIMEOUT_DEF) __u32_field_def(9, DRBD_GENLA_F_MANDATORY, ping_int, DRBD_PING_INT_DEF) __u32_field_def(10, DRBD_GENLA_F_MANDATORY, ping_timeo, DRBD_PING_TIMEO_DEF) __u32_field_def(11, DRBD_GENLA_F_MANDATORY, sndbuf_size, DRBD_SNDBUF_SIZE_DEF) __u32_field_def(12, DRBD_GENLA_F_MANDATORY, rcvbuf_size, DRBD_RCVBUF_SIZE_DEF) __u32_field_def(13, DRBD_GENLA_F_MANDATORY, ko_count, DRBD_KO_COUNT_DEF) __u32_field_def(14, DRBD_GENLA_F_MANDATORY, max_buffers, DRBD_MAX_BUFFERS_DEF) __u32_field_def(15, DRBD_GENLA_F_MANDATORY, max_epoch_size, DRBD_MAX_EPOCH_SIZE_DEF) __u32_field_def(16, DRBD_GENLA_F_MANDATORY, unplug_watermark, DRBD_UNPLUG_WATERMARK_DEF) __u32_field_def(17, DRBD_GENLA_F_MANDATORY, after_sb_0p, DRBD_AFTER_SB_0P_DEF) __u32_field_def(18, DRBD_GENLA_F_MANDATORY, after_sb_1p, DRBD_AFTER_SB_1P_DEF) __u32_field_def(19, DRBD_GENLA_F_MANDATORY, after_sb_2p, DRBD_AFTER_SB_2P_DEF) __u32_field_def(20, DRBD_GENLA_F_MANDATORY, rr_conflict, DRBD_RR_CONFLICT_DEF) __u32_field_def(21, DRBD_GENLA_F_MANDATORY, on_congestion, DRBD_ON_CONGESTION_DEF) __u32_field_def(22, DRBD_GENLA_F_MANDATORY, cong_fill, DRBD_CONG_FILL_DEF) __u32_field_def(23, DRBD_GENLA_F_MANDATORY, cong_extents, DRBD_CONG_EXTENTS_DEF) __flg_field_def(24, DRBD_GENLA_F_MANDATORY, two_primaries, DRBD_ALLOW_TWO_PRIMARIES_DEF) __flg_field(25, DRBD_GENLA_F_MANDATORY | DRBD_F_INVARIANT, discard_my_data) __flg_field_def(26, DRBD_GENLA_F_MANDATORY, tcp_cork, DRBD_TCP_CORK_DEF) __flg_field_def(27, DRBD_GENLA_F_MANDATORY, always_asbp, DRBD_ALWAYS_ASBP_DEF) __flg_field(28, DRBD_GENLA_F_MANDATORY | DRBD_F_INVARIANT, tentative) __flg_field_def(29, DRBD_GENLA_F_MANDATORY, use_rle, DRBD_USE_RLE_DEF) /* 9: __u32_field_def(30, DRBD_GENLA_F_MANDATORY, fencing_policy, DRBD_FENCING_DEF) */ /* 9: __str_field_def(31, DRBD_GENLA_F_MANDATORY, name, SHARED_SECRET_MAX) */ /* 9: __u32_field(32, DRBD_F_REQUIRED | DRBD_F_INVARIANT, peer_node_id) */ __flg_field_def(33, 0 /* OPTIONAL */, csums_after_crash_only, DRBD_CSUMS_AFTER_CRASH_ONLY_DEF) __u32_field_def(34, 0 /* OPTIONAL */, sock_check_timeo, DRBD_SOCKET_CHECK_TIMEO_DEF) __bin_field(35, 0 /* OPTIONAL */, my_addr2, 128) __bin_field(36, 0 /* OPTIONAL */, peer_addr2, 128) ) GENL_struct(DRBD_NLA_SET_ROLE_PARMS, 6, set_role_parms, __flg_field(1, DRBD_GENLA_F_MANDATORY, assume_uptodate) ) GENL_struct(DRBD_NLA_RESIZE_PARMS, 7, resize_parms, __u64_field(1, DRBD_GENLA_F_MANDATORY, resize_size) __flg_field(2, DRBD_GENLA_F_MANDATORY, resize_force) __flg_field(3, DRBD_GENLA_F_MANDATORY, no_resync) __u32_field_def(4, 0 /* OPTIONAL */, al_stripes, DRBD_AL_STRIPES_DEF) __u32_field_def(5, 0 /* OPTIONAL */, al_stripe_size, DRBD_AL_STRIPE_SIZE_DEF) ) GENL_struct(DRBD_NLA_STATE_INFO, 8, state_info, /* the reason of the broadcast, * if this is an event triggered broadcast. */ __u32_field(1, DRBD_GENLA_F_MANDATORY, sib_reason) __u32_field(2, DRBD_F_REQUIRED, current_state) __u64_field(3, DRBD_GENLA_F_MANDATORY, capacity) __u64_field(4, DRBD_GENLA_F_MANDATORY, ed_uuid) /* These are for broadcast from after state change work. * prev_state and new_state are from the moment the state change took * place, new_state is not neccessarily the same as current_state, * there may have been more state changes since. Which will be * broadcasted soon, in their respective after state change work. */ __u32_field(5, DRBD_GENLA_F_MANDATORY, prev_state) __u32_field(6, DRBD_GENLA_F_MANDATORY, new_state) /* if we have a local disk: */ __bin_field(7, DRBD_GENLA_F_MANDATORY, uuids, (UI_SIZE*sizeof(__u64))) __u32_field(8, DRBD_GENLA_F_MANDATORY, disk_flags) __u64_field(9, DRBD_GENLA_F_MANDATORY, bits_total) __u64_field(10, DRBD_GENLA_F_MANDATORY, bits_oos) /* and in case resync or online verify is active */ __u64_field(11, DRBD_GENLA_F_MANDATORY, bits_rs_total) __u64_field(12, DRBD_GENLA_F_MANDATORY, bits_rs_failed) /* for pre and post notifications of helper execution */ __str_field(13, DRBD_GENLA_F_MANDATORY, helper, 32) __u32_field(14, DRBD_GENLA_F_MANDATORY, helper_exit_code) __u64_field(15, 0, send_cnt) __u64_field(16, 0, recv_cnt) __u64_field(17, 0, read_cnt) __u64_field(18, 0, writ_cnt) __u64_field(19, 0, al_writ_cnt) __u64_field(20, 0, bm_writ_cnt) __u32_field(21, 0, ap_bio_cnt) __u32_field(22, 0, ap_pending_cnt) __u32_field(23, 0, rs_pending_cnt) ) GENL_struct(DRBD_NLA_START_OV_PARMS, 9, start_ov_parms, __u64_field(1, DRBD_GENLA_F_MANDATORY, ov_start_sector) __u64_field(2, DRBD_GENLA_F_MANDATORY, ov_stop_sector) ) GENL_struct(DRBD_NLA_NEW_C_UUID_PARMS, 10, new_c_uuid_parms, __flg_field(1, DRBD_GENLA_F_MANDATORY, clear_bm) ) GENL_struct(DRBD_NLA_TIMEOUT_PARMS, 11, timeout_parms, __u32_field(1, DRBD_F_REQUIRED, timeout_type) ) GENL_struct(DRBD_NLA_DISCONNECT_PARMS, 12, disconnect_parms, __flg_field(1, DRBD_GENLA_F_MANDATORY, force_disconnect) ) GENL_struct(DRBD_NLA_DETACH_PARMS, 13, detach_parms, __flg_field(1, DRBD_GENLA_F_MANDATORY, force_detach) ) GENL_struct(DRBD_NLA_RESOURCE_INFO, 15, resource_info, __u32_field(1, 0, res_role) __flg_field(2, 0, res_susp) __flg_field(3, 0, res_susp_nod) __flg_field(4, 0, res_susp_fen) /* __flg_field(5, 0, res_weak) */ ) GENL_struct(DRBD_NLA_DEVICE_INFO, 16, device_info, __u32_field(1, 0, dev_disk_state) ) GENL_struct(DRBD_NLA_CONNECTION_INFO, 17, connection_info, __u32_field(1, 0, conn_connection_state) __u32_field(2, 0, conn_role) ) GENL_struct(DRBD_NLA_PEER_DEVICE_INFO, 18, peer_device_info, __u32_field(1, 0, peer_repl_state) __u32_field(2, 0, peer_disk_state) __u32_field(3, 0, peer_resync_susp_user) __u32_field(4, 0, peer_resync_susp_peer) __u32_field(5, 0, peer_resync_susp_dependency) ) GENL_struct(DRBD_NLA_RESOURCE_STATISTICS, 19, resource_statistics, __u32_field(1, 0, res_stat_write_ordering) ) GENL_struct(DRBD_NLA_DEVICE_STATISTICS, 20, device_statistics, __u64_field(1, 0, dev_size) /* (sectors) */ __u64_field(2, 0, dev_read) /* (sectors) */ __u64_field(3, 0, dev_write) /* (sectors) */ __u64_field(4, 0, dev_al_writes) /* activity log writes (count) */ __u64_field(5, 0, dev_bm_writes) /* bitmap writes (count) */ __u32_field(6, 0, dev_upper_pending) /* application requests in progress */ __u32_field(7, 0, dev_lower_pending) /* backing device requests in progress */ __flg_field(8, 0, dev_upper_blocked) __flg_field(9, 0, dev_lower_blocked) __flg_field(10, 0, dev_al_suspended) /* activity log suspended */ __u64_field(11, 0, dev_exposed_data_uuid) __u64_field(12, 0, dev_current_uuid) __u32_field(13, 0, dev_disk_flags) __bin_field(14, 0, history_uuids, HISTORY_UUIDS * sizeof(__u64)) ) GENL_struct(DRBD_NLA_CONNECTION_STATISTICS, 21, connection_statistics, __flg_field(1, 0, conn_congested) ) GENL_struct(DRBD_NLA_PEER_DEVICE_STATISTICS, 22, peer_device_statistics, __u64_field(1, 0, peer_dev_received) /* sectors */ __u64_field(2, 0, peer_dev_sent) /* sectors */ __u32_field(3, 0, peer_dev_pending) /* number of requests */ __u32_field(4, 0, peer_dev_unacked) /* number of requests */ __u64_field(5, 0, peer_dev_out_of_sync) /* sectors */ __u64_field(6, 0, peer_dev_resync_failed) /* sectors */ __u64_field(7, 0, peer_dev_bitmap_uuid) __u32_field(9, 0, peer_dev_flags) ) GENL_struct(DRBD_NLA_NOTIFICATION_HEADER, 23, drbd_notification_header, __u32_field(1, DRBD_GENLA_F_MANDATORY, nh_type) ) GENL_struct(DRBD_NLA_HELPER, 24, drbd_helper_info, __str_field(1, DRBD_GENLA_F_MANDATORY, helper_name, 32) __u32_field(2, DRBD_GENLA_F_MANDATORY, helper_status) ) /* * Notifications and commands (genlmsghdr->cmd) */ GENL_mc_group(events) /* kernel -> userspace announcement of changes */ GENL_notification( DRBD_EVENT, 1, events, GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_STATE_INFO, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_NET_CONF, DRBD_GENLA_F_MANDATORY) GENL_tla_expected(DRBD_NLA_DISK_CONF, DRBD_GENLA_F_MANDATORY) GENL_tla_expected(DRBD_NLA_SYNCER_CONF, DRBD_GENLA_F_MANDATORY) ) /* query kernel for specific or all info */ GENL_op( DRBD_ADM_GET_STATUS, 2, GENL_op_init( .doit = drbd_adm_get_status, .dumpit = drbd_adm_get_status_all, /* anyone may ask for the status, * it is broadcasted anyways */ ), /* To select the object .doit. * Or a subset of objects in .dumpit. */ GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_GENLA_F_MANDATORY) ) /* add DRBD minor devices as volumes to resources */ GENL_op(DRBD_ADM_NEW_MINOR, 5, GENL_doit(drbd_adm_add_minor), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_DEL_MINOR, 6, GENL_doit(drbd_adm_delete_minor), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) /* add or delete resources */ GENL_op(DRBD_ADM_NEW_RESOURCE, 7, GENL_doit(drbd_adm_new_resource), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_DEL_RESOURCE, 8, GENL_doit(drbd_adm_del_resource), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_RESOURCE_OPTS, 9, GENL_doit(drbd_adm_resource_opts), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_RESOURCE_OPTS, DRBD_GENLA_F_MANDATORY) ) GENL_op( DRBD_ADM_CONNECT, 10, GENL_doit(drbd_adm_connect), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_NET_CONF, DRBD_F_REQUIRED) ) GENL_op( DRBD_ADM_CHG_NET_OPTS, 29, GENL_doit(drbd_adm_net_opts), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_NET_CONF, DRBD_F_REQUIRED) ) GENL_op(DRBD_ADM_DISCONNECT, 11, GENL_doit(drbd_adm_disconnect), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_ATTACH, 12, GENL_doit(drbd_adm_attach), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_DISK_CONF, DRBD_F_REQUIRED) ) GENL_op(DRBD_ADM_CHG_DISK_OPTS, 28, GENL_doit(drbd_adm_disk_opts), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_DISK_OPTS, DRBD_F_REQUIRED) ) GENL_op( DRBD_ADM_RESIZE, 13, GENL_doit(drbd_adm_resize), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_RESIZE_PARMS, DRBD_GENLA_F_MANDATORY) ) GENL_op( DRBD_ADM_PRIMARY, 14, GENL_doit(drbd_adm_set_role), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_SET_ROLE_PARMS, DRBD_F_REQUIRED) ) GENL_op( DRBD_ADM_SECONDARY, 15, GENL_doit(drbd_adm_set_role), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_SET_ROLE_PARMS, DRBD_F_REQUIRED) ) GENL_op( DRBD_ADM_NEW_C_UUID, 16, GENL_doit(drbd_adm_new_c_uuid), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_NEW_C_UUID_PARMS, DRBD_GENLA_F_MANDATORY) ) GENL_op( DRBD_ADM_START_OV, 17, GENL_doit(drbd_adm_start_ov), GENL_tla_expected(DRBD_NLA_START_OV_PARMS, DRBD_GENLA_F_MANDATORY) ) GENL_op(DRBD_ADM_DETACH, 18, GENL_doit(drbd_adm_detach), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_DETACH_PARMS, DRBD_GENLA_F_MANDATORY)) GENL_op(DRBD_ADM_INVALIDATE, 19, GENL_doit(drbd_adm_invalidate), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_INVAL_PEER, 20, GENL_doit(drbd_adm_invalidate_peer), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_PAUSE_SYNC, 21, GENL_doit(drbd_adm_pause_sync), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_RESUME_SYNC, 22, GENL_doit(drbd_adm_resume_sync), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_SUSPEND_IO, 23, GENL_doit(drbd_adm_suspend_io), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_RESUME_IO, 24, GENL_doit(drbd_adm_resume_io), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_OUTDATE, 25, GENL_doit(drbd_adm_outdate), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_GET_TIMEOUT_TYPE, 26, GENL_doit(drbd_adm_get_timeout_type), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_DOWN, 27, GENL_doit(drbd_adm_down), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED)) GENL_op(DRBD_ADM_GET_RESOURCES, 30, GENL_op_init( .dumpit = drbd_adm_dump_resources, ), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_GENLA_F_MANDATORY) GENL_tla_expected(DRBD_NLA_RESOURCE_INFO, DRBD_GENLA_F_MANDATORY) GENL_tla_expected(DRBD_NLA_RESOURCE_STATISTICS, DRBD_GENLA_F_MANDATORY)) GENL_op(DRBD_ADM_GET_DEVICES, 31, GENL_op_init( .dumpit = drbd_adm_dump_devices, .done = drbd_adm_dump_devices_done, ), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_GENLA_F_MANDATORY) GENL_tla_expected(DRBD_NLA_DEVICE_INFO, DRBD_GENLA_F_MANDATORY) GENL_tla_expected(DRBD_NLA_DEVICE_STATISTICS, DRBD_GENLA_F_MANDATORY)) GENL_op(DRBD_ADM_GET_CONNECTIONS, 32, GENL_op_init( .dumpit = drbd_adm_dump_connections, .done = drbd_adm_dump_connections_done, ), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_GENLA_F_MANDATORY) GENL_tla_expected(DRBD_NLA_CONNECTION_INFO, DRBD_GENLA_F_MANDATORY) GENL_tla_expected(DRBD_NLA_CONNECTION_STATISTICS, DRBD_GENLA_F_MANDATORY)) GENL_op(DRBD_ADM_GET_PEER_DEVICES, 33, GENL_op_init( .dumpit = drbd_adm_dump_peer_devices, .done = drbd_adm_dump_peer_devices_done, ), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_GENLA_F_MANDATORY) GENL_tla_expected(DRBD_NLA_PEER_DEVICE_INFO, DRBD_GENLA_F_MANDATORY) GENL_tla_expected(DRBD_NLA_PEER_DEVICE_STATISTICS, DRBD_GENLA_F_MANDATORY)) GENL_notification( DRBD_RESOURCE_STATE, 34, events, GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_NOTIFICATION_HEADER, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_RESOURCE_INFO, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_RESOURCE_STATISTICS, DRBD_F_REQUIRED)) GENL_notification( DRBD_DEVICE_STATE, 35, events, GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_NOTIFICATION_HEADER, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_DEVICE_INFO, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_DEVICE_STATISTICS, DRBD_F_REQUIRED)) GENL_notification( DRBD_CONNECTION_STATE, 36, events, GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_NOTIFICATION_HEADER, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_CONNECTION_INFO, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_CONNECTION_STATISTICS, DRBD_F_REQUIRED)) GENL_notification( DRBD_PEER_DEVICE_STATE, 37, events, GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_NOTIFICATION_HEADER, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_PEER_DEVICE_INFO, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_PEER_DEVICE_STATISTICS, DRBD_F_REQUIRED)) GENL_op( DRBD_ADM_GET_INITIAL_STATE, 38, GENL_op_init( .dumpit = drbd_adm_get_initial_state, ), GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_GENLA_F_MANDATORY)) GENL_notification( DRBD_HELPER, 40, events, GENL_tla_expected(DRBD_NLA_CFG_CONTEXT, DRBD_F_REQUIRED) GENL_tla_expected(DRBD_NLA_HELPER, DRBD_F_REQUIRED)) GENL_notification( DRBD_INITIAL_STATE_DONE, 41, events, GENL_tla_expected(DRBD_NLA_NOTIFICATION_HEADER, DRBD_F_REQUIRED)) drbd-utils-8.9.10/user/v84/drbd_strings.h0000644000175000017500000000045412466702074020025 0ustar apoikosapoikos#ifndef __DRBD_STRINGS_H #define __DRBD_STRINGS_H extern const char *drbd_conn_str(enum drbd_conns); extern const char *drbd_role_str(enum drbd_role); extern const char *drbd_disk_str(enum drbd_disk_state); extern const char *drbd_set_st_err_str(enum drbd_state_rv); #endif /* __DRBD_STRINGS_H */ drbd-utils-8.9.10/user/v84/drbd_strings.c0000644000175000017500000001054512466702074020022 0ustar apoikosapoikos/* drbd.h This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2003-2008, LINBIT Information Technologies GmbH. Copyright (C) 2003-2008, Philipp Reisner . Copyright (C) 2003-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include "drbd_strings.h" static const char *drbd_conn_s_names[] = { [C_STANDALONE] = "StandAlone", [C_DISCONNECTING] = "Disconnecting", [C_UNCONNECTED] = "Unconnected", [C_TIMEOUT] = "Timeout", [C_BROKEN_PIPE] = "BrokenPipe", [C_NETWORK_FAILURE] = "NetworkFailure", [C_PROTOCOL_ERROR] = "ProtocolError", [C_WF_CONNECTION] = "WFConnection", [C_WF_REPORT_PARAMS] = "WFReportParams", [C_TEAR_DOWN] = "TearDown", [C_CONNECTED] = "Connected", [C_STARTING_SYNC_S] = "StartingSyncS", [C_STARTING_SYNC_T] = "StartingSyncT", [C_WF_BITMAP_S] = "WFBitMapS", [C_WF_BITMAP_T] = "WFBitMapT", [C_WF_SYNC_UUID] = "WFSyncUUID", [C_SYNC_SOURCE] = "SyncSource", [C_SYNC_TARGET] = "SyncTarget", [C_PAUSED_SYNC_S] = "PausedSyncS", [C_PAUSED_SYNC_T] = "PausedSyncT", [C_VERIFY_S] = "VerifyS", [C_VERIFY_T] = "VerifyT", [C_AHEAD] = "Ahead", [C_BEHIND] = "Behind", }; static const char *drbd_role_s_names[] = { [R_PRIMARY] = "Primary", [R_SECONDARY] = "Secondary", [R_UNKNOWN] = "Unknown" }; static const char *drbd_disk_s_names[] = { [D_DISKLESS] = "Diskless", [D_ATTACHING] = "Attaching", [D_FAILED] = "Failed", [D_NEGOTIATING] = "Negotiating", [D_INCONSISTENT] = "Inconsistent", [D_OUTDATED] = "Outdated", [D_UNKNOWN] = "DUnknown", [D_CONSISTENT] = "Consistent", [D_UP_TO_DATE] = "UpToDate", }; static const char *drbd_state_sw_errors[] = { [-SS_TWO_PRIMARIES] = "Multiple primaries not allowed by config", [-SS_NO_UP_TO_DATE_DISK] = "Need access to UpToDate data", [-SS_NO_LOCAL_DISK] = "Can not resync without local disk", [-SS_NO_REMOTE_DISK] = "Can not resync without remote disk", [-SS_CONNECTED_OUTDATES] = "Refusing to be Outdated while Connected", [-SS_PRIMARY_NOP] = "Refusing to be Primary while peer is not outdated", [-SS_RESYNC_RUNNING] = "Can not start OV/resync since it is already active", [-SS_ALREADY_STANDALONE] = "Can not disconnect a StandAlone device", [-SS_CW_FAILED_BY_PEER] = "State change was refused by peer node", [-SS_IS_DISKLESS] = "Device is diskless, the requested operation requires a disk", [-SS_DEVICE_IN_USE] = "Device is held open by someone", [-SS_NO_NET_CONFIG] = "Have no net/connection configuration", [-SS_NO_VERIFY_ALG] = "Need a verify algorithm to start online verify", [-SS_NEED_CONNECTION] = "Need a connection to start verify or resync", [-SS_NOT_SUPPORTED] = "Peer does not support protocol", [-SS_LOWER_THAN_OUTDATED] = "Disk state is lower than outdated", [-SS_IN_TRANSIENT_STATE] = "In transient state, retry after next state change", [-SS_CONCURRENT_ST_CHG] = "Concurrent state changes detected and aborted", [-SS_OUTDATE_WO_CONN] = "Need a connection for a graceful disconnect/outdate peer", [-SS_O_VOL_PEER_PRI] = "Other vol primary on peer not allowed by config", }; const char *drbd_conn_str(enum drbd_conns s) { /* enums are unsigned... */ return s > C_BEHIND ? "TOO_LARGE" : drbd_conn_s_names[s]; } const char *drbd_role_str(enum drbd_role s) { return s > R_SECONDARY ? "TOO_LARGE" : drbd_role_s_names[s]; } const char *drbd_disk_str(enum drbd_disk_state s) { return s > D_UP_TO_DATE ? "TOO_LARGE" : drbd_disk_s_names[s]; } const char *drbd_set_st_err_str(enum drbd_state_rv err) { return err <= SS_AFTER_LAST_ERROR ? "TOO_SMALL" : err > SS_TWO_PRIMARIES ? "TOO_LARGE" : drbd_state_sw_errors[-err]; } drbd-utils-8.9.10/user/v84/drbdadm_usage_cnt.c0000644000175000017500000004547513016771235020772 0ustar apoikosapoikos/* drbdadm_usage_cnt.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2006-2008, LINBIT Information Technologies GmbH Copyright (C) 2006-2008, Philipp Reisner Copyright (C) 2006-2008, Lars Ellenberg drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drbdadm.h" #include "drbdtool_common.h" #include "drbd_endian.h" #include "linux/drbd.h" /* only use DRBD_MAGIC from here! */ #define HTTP_PORT 80 #define HTTP_HOST "usage.drbd.org" #define HTTP_ADDR "212.69.161.111" #define NODE_ID_FILE DRBD_LIB_DIR"/node_id" #define GIT_HASH_BYTE 20 #define SRCVERSION_BYTE 12 /* actually 11 and a half. */ #define SRCVERSION_PAD (GIT_HASH_BYTE - SRCVERSION_BYTE) #define SVN_STYLE_OD 16 struct vcs_rel { uint32_t svn_revision; char git_hash[GIT_HASH_BYTE]; struct { unsigned major, minor, sublvl; } version; unsigned version_code; }; struct node_info { uint64_t node_uuid; struct vcs_rel rev; }; struct node_info_od { uint32_t magic; struct node_info ni; } __packed; /* For our purpose (finding the revision) SLURP_SIZE is always enough. */ static char* slurp_proc_drbd() { const int SLURP_SIZE = 4096; char* buffer; int rr, fd; fd = open("/proc/drbd",O_RDONLY); if( fd == -1) return 0; buffer = malloc(SLURP_SIZE); if(!buffer) return 0; rr = read(fd, buffer, SLURP_SIZE-1); if( rr == -1) { free(buffer); return 0; } buffer[rr]=0; close(fd); return buffer; } void read_hex(char* dst, char* src, int dst_size, int src_size) { int dst_i, u, src_i=0; for(dst_i=0;dst_i= src_size) break; if(src[src_i] == 0) break; if(++src_i >= src_size) break; } } void vcs_ver_from_str(struct vcs_rel *rel, const char *token) { char *dot; long maj, min, sub; maj = strtol(token, &dot, 10); if (*dot != '.') return; min = strtol(dot+1, &dot, 10); if (*dot != '.') return; sub = strtol(dot+1, &dot, 10); /* don't check on *dot == 0, * we may want to add some extraversion tag sometime if (*dot != 0) return; */ rel->version.major = maj; rel->version.minor = min; rel->version.sublvl = sub; rel->version_code = (maj << 16) + (min << 8) + sub; } void vcs_from_str(struct vcs_rel *rel, const char *text) { char token[80]; int plus=0; enum { begin, f_ver, f_svn, f_rev, f_git, f_srcv } ex = begin; while (sget_token(token, sizeof(token), &text) != EOF) { switch(ex) { case begin: if(!strcmp(token,"version:")) ex = f_ver; if(!strcmp(token,"SVN")) ex = f_svn; if(!strcmp(token,"GIT-hash:")) ex = f_git; if(!strcmp(token,"srcversion:")) ex = f_srcv; break; case f_ver: if(!strcmp(token,"plus")) plus = 1; /* still waiting for version */ else { vcs_ver_from_str(rel, token); ex = begin; } break; case f_svn: if(!strcmp(token,"Revision:")) ex = f_rev; break; case f_rev: rel->svn_revision = atol(token) * 10; if( plus ) rel->svn_revision += 1; memset(rel->git_hash, 0, GIT_HASH_BYTE); return; case f_git: read_hex(rel->git_hash, token, GIT_HASH_BYTE, strlen(token)); rel->svn_revision = 0; return; case f_srcv: memset(rel->git_hash, 0, SRCVERSION_PAD); read_hex(rel->git_hash + SRCVERSION_PAD, token, SRCVERSION_BYTE, strlen(token)); rel->svn_revision = 0; return; } } } static int current_vcs_is_from_proc_drbd; static struct vcs_rel current_vcs_rel; static struct vcs_rel userland_version; static void vcs_get_current(void) { char* version_txt; if (current_vcs_rel.version_code) return; version_txt = slurp_proc_drbd(); if(version_txt) { vcs_from_str(¤t_vcs_rel, version_txt); current_vcs_is_from_proc_drbd = 1; free(version_txt); } else { vcs_from_str(¤t_vcs_rel, drbd_buildtag()); vcs_ver_from_str(¤t_vcs_rel, PACKAGE_VERSION); } } static void vcs_get_userland(void) { if (userland_version.version_code) return; vcs_ver_from_str(&userland_version, PACKAGE_VERSION); } int version_code_kernel(void) { vcs_get_current(); return current_vcs_is_from_proc_drbd ? current_vcs_rel.version_code : 0; } int version_code_userland(void) { vcs_get_userland(); return userland_version.version_code; } static int vcs_eq(struct vcs_rel *rev1, struct vcs_rel *rev2) { if( rev1->svn_revision || rev2->svn_revision ) { return rev1->svn_revision == rev2->svn_revision; } else { return !memcmp(rev1->git_hash,rev2->git_hash,GIT_HASH_BYTE); } } static int vcs_ver_cmp(struct vcs_rel *rev1, struct vcs_rel *rev2) { return rev1->version_code - rev2->version_code; } void warn_on_version_mismatch(void) { char *msg; int cmp; /* get the kernel module version from /proc/drbd */ vcs_get_current(); /* get the userland version from PACKAGE_VERSION */ vcs_get_userland(); cmp = vcs_ver_cmp(&userland_version, ¤t_vcs_rel); /* no message if equal */ if (cmp == 0) return; if (cmp > 0xffff || cmp < -0xffff) /* major version differs! */ msg = "mixing different major numbers will not work!"; else if (cmp < 0) /* userland is older. always warn. */ msg = "you should upgrade your drbd tools!"; else if (cmp & 0xff00) /* userland is newer minor version */ msg = "please don't mix different DRBD series."; else /* userland is newer, but only differ in sublevel. */ msg = "preferably kernel and userland versions should match."; fprintf(stderr, "DRBD module version: %u.%u.%u\n" " userland version: %u.%u.%u\n%s\n", current_vcs_rel.version.major, current_vcs_rel.version.minor, current_vcs_rel.version.sublvl, userland_version.version.major, userland_version.version.minor, userland_version.version.sublvl, msg); } void add_lib_drbd_to_path(void) { char *new_path = NULL; char *old_path = getenv("PATH"); m_asprintf(&new_path, "%s%s%s", old_path, old_path ? ":" : "", "/lib/drbd"); setenv("PATH", new_path, 1); } void maybe_exec_drbdadm_83(char **argv) { if (current_vcs_rel.version.major == 8 && current_vcs_rel.version.minor == 3) { #ifdef DRBD_LEGACY_83 /* This drbdadm warned already... */ setenv("DRBD_DONT_WARN_ON_VERSION_MISMATCH", "1", 0); add_lib_drbd_to_path(); execvp(drbdadm_83, argv); fprintf(stderr, "execvp() failed to exec %s: %m\n", drbdadm_83); #else fprintf(stderr, "This drbdadm was not built with support for drbd-8.3\n" "Consider to rebuild with ./configure --with-83-support\n"); #endif exit(E_EXEC_ERROR); } } static char *vcs_to_str(struct vcs_rel *rev) { static char buffer[80]; // Not generic, sufficient for the purpose. if( rev->svn_revision ) { snprintf(buffer,80,"nv="U32,rev->svn_revision); } else { int len=20,p; unsigned char *bytes; p = sprintf(buffer,"git="); bytes = (unsigned char*)rev->git_hash; while(len--) p += sprintf(buffer+p,"%02x",*bytes++); } return buffer; } static void write_node_id(struct node_info *ni) { int fd; struct node_info_od on_disk; int size; fd = open(NODE_ID_FILE,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR); if( fd == -1 && errno == ENOENT) { mkdir(DRBD_LIB_DIR,S_IRWXU); fd = open(NODE_ID_FILE,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR); } if( fd == -1) { perror("Creation of "NODE_ID_FILE" failed."); exit(20); } if(ni->rev.svn_revision != 0) { // SVN style (old) on_disk.magic = cpu_to_be32(DRBD_MAGIC); on_disk.ni.node_uuid = cpu_to_be64(ni->node_uuid); on_disk.ni.rev.svn_revision = cpu_to_be32(ni->rev.svn_revision); memset(on_disk.ni.rev.git_hash,0,GIT_HASH_BYTE); size = SVN_STYLE_OD; } else { on_disk.magic = cpu_to_be32(DRBD_MAGIC+1); on_disk.ni.node_uuid = cpu_to_be64(ni->node_uuid); on_disk.ni.rev.svn_revision = 0; memcpy(on_disk.ni.rev.git_hash,ni->rev.git_hash,GIT_HASH_BYTE); size = sizeof(on_disk); } if( write(fd,&on_disk, size) != size) { perror("Write to "NODE_ID_FILE" failed."); exit(20); } close(fd); } static int read_node_id(struct node_info *ni) { int rr; int fd; struct node_info_od on_disk; fd = open(NODE_ID_FILE, O_RDONLY); if (fd == -1) { return 0; } rr = read(fd, &on_disk, sizeof(on_disk)); if (rr != sizeof(on_disk) && rr != SVN_STYLE_OD) { close(fd); return 0; } switch (be32_to_cpu(on_disk.magic)) { case DRBD_MAGIC: ni->node_uuid = be64_to_cpu(on_disk.ni.node_uuid); ni->rev.svn_revision = be32_to_cpu(on_disk.ni.rev.svn_revision); memset(ni->rev.git_hash, 0, GIT_HASH_BYTE); break; case DRBD_MAGIC+1: ni->node_uuid = be64_to_cpu(on_disk.ni.node_uuid); ni->rev.svn_revision = 0; memcpy(ni->rev.git_hash, on_disk.ni.rev.git_hash, GIT_HASH_BYTE); break; default: return 0; } close(fd); return 1; } /* to interrupt gethostbyname, * we not only need a signal, * but also the long jump: * gethostbyname would otherwise just restart the syscall * and timeout again. */ static jmp_buf timed_out; static void gethostbyname_timeout(int __attribute((unused)) signo) { longjmp(timed_out, 1); } #define DNS_TIMEOUT 3 /* seconds */ #define SOCKET_TIMEOUT 3 /* seconds */ struct hostent *my_gethostbyname(const char *name) { struct sigaction sa; struct sigaction so; struct hostent *h; alarm(0); sa.sa_handler = &gethostbyname_timeout; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGALRM, &sa, &so); if (!setjmp(timed_out)) { alarm(DNS_TIMEOUT); h = gethostbyname(name); } else /* timed out, longjmp of SIGALRM jumped here */ h = NULL; alarm(0); sigaction(SIGALRM, &so, NULL); return h; } /** * insert_usage_with_socket: * * Return codes: * * 0 - success * 1 - failed to create socket * 2 - unknown server * 3 - cannot connect to server * 5 - other error */ static int make_get_request(char *uri) { struct sockaddr_in server; struct hostent *host_info; unsigned long addr; int sock; char *req_buf; char *http_host = HTTP_HOST; int buf_len = 1024; char buffer[buf_len]; FILE *sockfd; int writeit; struct timeval timeout = { .tv_sec = SOCKET_TIMEOUT }; sock = socket( PF_INET, SOCK_STREAM, 0); if (sock < 0) return 1; setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); memset (&server, 0, sizeof(server)); /* convert host name to ip */ host_info = my_gethostbyname(http_host); if (host_info == NULL) { /* unknown host, try with ip */ if ((addr = inet_addr( HTTP_ADDR )) != INADDR_NONE) memcpy((char *)&server.sin_addr, &addr, sizeof(addr)); else { close(sock); return 2; } } else { memcpy((char *)&server.sin_addr, host_info->h_addr, host_info->h_length); } ssprintf(req_buf, "GET %s HTTP/1.0\r\n" "Host: "HTTP_HOST"\r\n" "User-Agent: drbdadm/"PACKAGE_VERSION" (%s; %s; %s; %s)\r\n" "\r\n", uri, nodeinfo.sysname, nodeinfo.release, nodeinfo.version, nodeinfo.machine); server.sin_family = AF_INET; server.sin_port = htons(HTTP_PORT); if (connect(sock, (struct sockaddr*)&server, sizeof(server))<0) { /* cannot connect to server */ close(sock); return 3; } if ((sockfd = fdopen(sock, "r+")) == NULL) { close(sock); return 5; } if (fputs(req_buf, sockfd) == EOF) { fclose(sockfd); close(sock); return 5; } writeit = 0; while (fgets(buffer, buf_len, sockfd) != NULL) { /* ignore http headers */ if (writeit == 0) { if (buffer[0] == '\r' || buffer[0] == '\n') writeit = 1; } else { fprintf(stderr,"%s", buffer); } } fclose(sockfd); close(sock); return 0; } static void url_encode(char *in, char *out) { char *h = "0123456789abcdef"; unsigned char c; while ((c = *in++) != 0) { if (c == '\n') break; if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '-' || c == '_' || c == '.') *out++ = c; else if (c == ' ') *out++ = '+'; else { *out++ = '%'; *out++ = h[c >> 4]; *out++ = h[c & 0x0f]; } } *out = 0; } /* Ensure that the node is counted on http://usage.drbd.org */ #define ANSWER_SIZE 80 void uc_node(enum usage_count_type type) { struct node_info ni; char *uri; int send = 0; int update = 0; char answer[ANSWER_SIZE]; char n_comment[ANSWER_SIZE*3]; char *r; if( type == UC_NO ) return; if( getuid() != 0 ) return; /* not when running directly from init, * or if stdout is no tty. * you do not want to have the "user information message" * as output from `drbdadm sh-resources all` */ if (getenv("INIT_VERSION")) return; if (no_tty) return; vcs_get_current(); if( ! read_node_id(&ni) ) { get_random_bytes(&ni.node_uuid,sizeof(ni.node_uuid)); ni.rev = current_vcs_rel; send = 1; } else if (current_vcs_is_from_proc_drbd == 0) { /* Avoid flapping between drbd-utils git-hash and * kernel module git-hash. */ send = 0; } else { // read_node_id() was successful if (!vcs_eq(&ni.rev,¤t_vcs_rel)) { ni.rev = current_vcs_rel; update = 1; send = 1; } } if(!send) return; n_comment[0]=0; if (type == UC_ASK ) { fprintf(stderr, "\n" "\t\t--== This is %s of DRBD ==--\n" "Please take part in the global DRBD usage count at http://"HTTP_HOST".\n\n" "The counter works anonymously. It creates a random number to identify\n" "your machine and sends that random number, along with the kernel and\n" "DRBD version, to "HTTP_HOST".\n\n" "The benefits for you are:\n" " * In response to your submission, the server ("HTTP_HOST") will tell you\n" " how many users before you have installed this version (%s).\n" " * With a high counter LINBIT has a strong motivation to\n" " continue funding DRBD's development.\n\n" "http://"HTTP_HOST"/cgi-bin/insert_usage.pl?nu="U64"&%s\n\n" "In case you want to participate but know that this machine is firewalled,\n" "simply issue the query string with your favorite web browser or wget.\n" "You can control all of this by setting 'usage-count' in your drbd.conf.\n\n" "* You may enter a free form comment about your machine, that gets\n" " used on "HTTP_HOST" instead of the big random number.\n" "* If you wish to opt out entirely, simply enter 'no'.\n" "* To count this node without comment, just press [RETURN]\n", update ? "an update" : "a new installation", PACKAGE_VERSION,ni.node_uuid, vcs_to_str(&ni.rev)); r = fgets(answer, ANSWER_SIZE, stdin); if(r && !strcmp(answer,"no\n")) send = 0; url_encode(answer,n_comment); } ssprintf(uri,"http://"HTTP_HOST"/cgi-bin/insert_usage.pl?nu="U64"&%s%s%s", ni.node_uuid, vcs_to_str(&ni.rev), n_comment[0] ? "&nc=" : "", n_comment); if (send) { write_node_id(&ni); fprintf(stderr, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" " --== Thank you for participating in the global usage survey ==--\n" "The server's response is:\n\n"); make_get_request(uri); if (type == UC_ASK) { fprintf(stderr, "\n" "From now on, drbdadm will contact "HTTP_HOST" only when you update\n" "DRBD or when you use 'drbdadm create-md'. Of course it will continue\n" "to ask you for confirmation as long as 'usage-count' is at its default\n" "value of 'ask'.\n\n" "Just press [RETURN] to continue: "); r = fgets(answer, 9, stdin); } } } /* For our purpose (finding the revision) SLURP_SIZE is always enough. */ static char* run_admm_generic(struct cfg_ctx *ctx, const char *arg_override) { const int SLURP_SIZE = 4096; int rr,pipes[2]; char* buffer; pid_t pid; buffer = malloc(SLURP_SIZE); if(!buffer) return 0; if(pipe(pipes)) return 0; pid = fork(); if(pid == -1) { fprintf(stderr,"Can not fork\n"); exit(E_EXEC_ERROR); } if(pid == 0) { // child close(pipes[0]); // close reading end dup2(pipes[1],1); // 1 = stdout close(pipes[1]); /* local modification in child, * no propagation to parent */ ctx->arg = arg_override; rr = _admm_generic(ctx, SLEEPS_VERY_LONG| DONT_REPORT_FAILED); exit(rr); } close(pipes[1]); // close writing end rr = read(pipes[0], buffer, SLURP_SIZE-1); if( rr == -1) { free(buffer); // FIXME cleanup return 0; } buffer[rr]=0; close(pipes[0]); waitpid(pid,0,0); return buffer; } int adm_create_md(struct cfg_ctx *ctx) { char answer[ANSWER_SIZE]; struct node_info ni; uint64_t device_uuid=0; uint64_t device_size=0; char *uri; int send=0; char *tb; int rv, fd, verbose_tmp; char *r; verbose_tmp = verbose; verbose = 0; tb = run_admm_generic(ctx, "read-dev-uuid"); verbose = verbose_tmp; device_uuid = strto_u64(tb,NULL,16); free(tb); /* this is "drbdmeta ... create-md" */ rv = _admm_generic(ctx, SLEEPS_VERY_LONG); if(rv || dry_run) return rv; fd = open(ctx->vol->disk,O_RDONLY); if( fd != -1) { device_size = bdev_size(fd); close(fd); } if( read_node_id(&ni) && device_size && !device_uuid) { get_random_bytes(&device_uuid, sizeof(uint64_t)); if( global_options.usage_count == UC_YES ) send = 1; if( global_options.usage_count == UC_ASK ) { fprintf(stderr, "\n" "\t\t--== Creating metadata ==--\n" "As with nodes, we count the total number of devices mirrored by DRBD\n" "at http://"HTTP_HOST".\n\n" "The counter works anonymously. It creates a random number to identify\n" "the device and sends that random number, along with the kernel and\n" "DRBD version, to "HTTP_HOST".\n\n" "http://"HTTP_HOST"/cgi-bin/insert_usage.pl?nu="U64"&ru="U64"&rs="U64"\n\n" "* If you wish to opt out entirely, simply enter 'no'.\n" "* To continue, just press [RETURN]\n", ni.node_uuid,device_uuid,device_size ); r = fgets(answer, ANSWER_SIZE, stdin); if(r && strcmp(answer,"no\n")) send = 1; } } if(!device_uuid) { get_random_bytes(&device_uuid, sizeof(uint64_t)); } if (send) { ssprintf(uri,"http://"HTTP_HOST"/cgi-bin/insert_usage.pl?" "nu="U64"&ru="U64"&rs="U64, ni.node_uuid, device_uuid, device_size); make_get_request(uri); } /* HACK */ { struct cfg_ctx local_ctx = *ctx; struct setup_option *old_setup_options; char *opt; ssprintf(opt, X64(016), device_uuid); old_setup_options = setup_options; setup_options = NULL; add_setup_option(false, opt); local_ctx.arg = "write-dev-uuid"; _admm_generic(&local_ctx, SLEEPS_VERY_LONG); free(setup_options); setup_options = old_setup_options; } return rv; } drbd-utils-8.9.10/user/v84/drbdadm_adjust.c0000644000175000017500000005105112736712235020302 0ustar apoikosapoikos/* drbdadm_adjust.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2003-2008, LINBIT Information Technologies GmbH. Copyright (C) 2003-2008, Philipp Reisner . Copyright (C) 2003-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #define _XOPEN_SOURCE 600 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include "drbdadm.h" #include "drbdtool_common.h" #include "drbdadm_parser.h" #include "config_flags.h" /* drbdsetup show might complain that the device minor does not exist at all. Redirect stderr to /dev/null therefore. */ static FILE *m_popen(int *pid,char** argv) { int mpid; int pipes[2]; int dev_null; if(pipe(pipes)) { err("Creation of pipes failed: %m\n"); exit(E_EXEC_ERROR); } dev_null = open("/dev/null", O_WRONLY); if (dev_null == -1) { err("Opening /dev/null failed: %m\n"); exit(E_EXEC_ERROR); } mpid = fork(); if(mpid == -1) { err("Can not fork"); exit(E_EXEC_ERROR); } if(mpid == 0) { close(pipes[0]); // close reading end dup2(pipes[1], fileno(stdout)); close(pipes[1]); dup2(dev_null, fileno(stderr)); close(dev_null); execvp(argv[0],argv); err("Can not exec"); exit(E_EXEC_ERROR); } close(pipes[1]); // close writing end close(dev_null); *pid=mpid; return fdopen(pipes[0],"r"); } static int is_equal(struct context_def *ctx, struct d_option *a, struct d_option *b) { struct field_def *field; for (field = ctx->fields; field->name; field++) { if (!strcmp(field->name, a->name)) return field->is_equal(field, a->value, b->value); } err("Internal error: option '%s' not known in this context\n", a->name); abort(); } static bool is_default(struct context_def *ctx, struct d_option *opt) { struct field_def *field; for (field = ctx->fields; field->name; field++) { if (strcmp(field->name, opt->name)) continue; return field->is_default(field, opt->value); } return false; } static int opts_equal(struct context_def *ctx, struct d_option* conf, struct d_option* running) { struct d_option* opt; while(running) { if((opt=find_opt(conf,running->name))) { if(!is_equal(ctx, running, opt)) { if (verbose > 2) err("Value of '%s' differs: r=%s c=%s\n", opt->name,running->value,opt->value); return 0; } if (verbose > 3) err("Value of '%s' equal: r=%s c=%s\n", opt->name,running->value,opt->value); opt->mentioned=1; } else { if(!is_default(ctx, running)) { if (verbose > 2) err("Only in running config %s: %s\n", running->name,running->value); return 0; } if (verbose > 3) err("Is default: '%s' equal: r=%s\n", running->name,running->value); } running=running->next; } while(conf) { if(conf->mentioned==0 && !is_default(ctx, conf)) { if (verbose > 2) err("Only in config file %s: %s\n", conf->name,conf->value); return 0; } conf=conf->next; } return 1; } static int addr_equal(struct d_resource* conf, struct d_resource* running) { int equal; char *peer_addr, *peer_af, *peer_port; if (conf->peer == NULL && running->peer == NULL) return 1; if (running->peer == NULL) return 0; equal = !strcmp(conf->me->address, running->me->address) && !strcmp(conf->me->port, running->me->port) && !strcmp(conf->me->address_family, running->me->address_family); if(conf->me->proxy) { peer_addr = conf->me->proxy->inside_addr; peer_port = conf->me->proxy->inside_port; peer_af = conf->me->proxy->inside_af; } else { peer_addr = conf->peer->address; peer_port = conf->peer->port; peer_af = conf->peer->address_family; } equal = equal && conf->peer && !strcmp(peer_addr, running->peer->address) && !strcmp(peer_port, running->peer->port) && !strcmp(peer_af, running->peer->address_family); if (!equal && verbose > 2) err("Network addresses differ:\n" "\trunning: %s:%s:%s -- %s:%s:%s\n" "\t config: %s:%s:%s -- %s:%s:%s\n", running->me->address_family, running->me->address, running->me->port, running->peer->address_family, running->peer->address, running->peer->port, conf->me->address_family, conf->me->address, conf->me->port, peer_af, peer_addr, peer_port); return equal; } /* Are both internal, or are both not internal. */ static int int_eq(char* m_conf, char* m_running) { return !strcmp(m_conf,"internal") == !strcmp(m_running,"internal"); } static int disk_equal(struct d_volume *conf, struct d_volume *running) { int eq = 1; if (conf->disk == NULL && running->disk == NULL) return 1; if (conf->disk == NULL || running->disk == NULL) return 0; eq &= !strcmp(conf->disk, running->disk); eq &= int_eq(conf->meta_disk, running->meta_disk); if (!strcmp(conf->meta_disk, "internal")) return eq; eq &= !strcmp(conf->meta_disk, running->meta_disk); return eq; } /* NULL terminated */ static void find_option_in_resources(char *name, struct d_option *list, struct d_option **opt, ...) { va_list va; va_start(va, opt); /* We need to keep setting *opt to NULL, even if a list == NULL. */ while (list || opt) { while (list) { if (strcmp(list->name, name) == 0) break; list = list->next; } *opt = list; list = va_arg(va, struct d_option*); opt = va_arg(va, struct d_option**); } va_end(va); } static int do_proxy_reconf(struct cfg_ctx *ctx) { int rv; char *argv[4] = { drbd_proxy_ctl, "-c", (char*)ctx->arg, NULL }; rv = m_system_ex(argv, SLEEPS_SHORT, ctx->res->name); return rv; } #define MAX_PLUGINS (10) #define MAX_PLUGIN_NAME (16) /* The new name is appended to the alist. */ int _is_plugin_in_list(char *string, char slist[MAX_PLUGINS][MAX_PLUGIN_NAME], char alist[MAX_PLUGINS][MAX_PLUGIN_NAME], int list_len) { int word_len, i; char *copy; for(word_len=0; string[word_len]; word_len++) if (isspace(string[word_len])) break; if (word_len+1 >= MAX_PLUGIN_NAME) { err("Wrong proxy plugin name %*.*s", word_len, word_len, string); exit(E_CONFIG_INVALID); } copy = alist[list_len]; strncpy(copy, string, word_len); copy[word_len] = 0; for(i=0; i= MAX_PLUGINS) { err("Too many proxy plugins."); exit(E_CONFIG_INVALID); } return 0; } static int proxy_reconf(struct cfg_ctx *ctx, struct d_resource *running) { int reconn = 0; struct d_resource *res = ctx->res; struct d_option* res_o, *run_o; unsigned long long v1, v2, minimum; char *plugin_changes[MAX_PLUGINS], *cp, *conn_name; /* It's less memory usage when we're storing char[]. malloc overhead for * the few bytes + pointers is much more. */ char p_res[MAX_PLUGINS][MAX_PLUGIN_NAME], p_run[MAX_PLUGINS][MAX_PLUGIN_NAME]; int used, i, re_do; reconn = 0; if (!running) goto redo_whole_conn; find_option_in_resources("memlimit", res->me->proxy->options, &res_o, running->proxy_options, &run_o, NULL, NULL); v1 = res_o ? m_strtoll(res_o->value, 1) : 0; v2 = run_o ? m_strtoll(run_o->value, 1) : 0; minimum = v1 < v2 ? v1 : v2; /* We allow an Ñ” [epsilon] of 2%, so that small (rounding) deviations do * not cause the connection to be re-established. */ if (res_o && (!run_o || abs(v1-v2)/(float)minimum > 0.02)) { redo_whole_conn: /* As the memory is in use while the connection is allocated we have to * completely destroy and rebuild the connection. */ schedule_deferred_cmd( do_proxy_conn_down, ctx, NULL, CFG_NET_PREREQ); schedule_deferred_cmd( do_proxy_conn_up, ctx, NULL, CFG_NET_PREREQ); schedule_deferred_cmd( do_proxy_conn_plugins, ctx, NULL, CFG_NET_PREREQ); /* With connection cleanup and reopen everything is rebuild anyway, and * DRBD will get a reconnect too. */ return 0; } res_o = res->me->proxy->plugins; run_o = running->proxy_plugins; used = 0; conn_name = proxy_connection_name(res); for(i=0; i= sizeof(plugin_changes)-1) { err("Too many proxy plugin changes"); exit(E_CONFIG_INVALID); } /* Now we can be sure that we can store another pointer. */ if (!res_o) { if (run_o) { /* More plugins running than configured - just stop here. */ m_asprintf(&cp, "set plugin %s %d end", conn_name, i); plugin_changes[used++] = cp; } else { /* Both at the end? ok, quit loop */ } break; } /* res_o != NULL. */ if (!run_o) { p_run[i][0] = 0; if (_is_plugin_in_list(res_o->name, p_run, p_res, i)) { /* Current plugin was already active, just at another position. * Redo the whole connection. */ goto redo_whole_conn; } /* More configured than running - just add it, if it's not already * somewhere else. */ m_asprintf(&cp, "set plugin %s %d %s", conn_name, i, res_o->name); plugin_changes[used++] = cp; } else { /* If we get here, both lists have been filled in parallel, so we * can simply use the common counter. */ re_do = _is_plugin_in_list(res_o->name, p_run, p_res, i) || _is_plugin_in_list(run_o->name, p_res, p_run, i); if (re_do) { /* Plugin(s) were moved, not simple reconfigured. * Re-do the whole connection. */ goto redo_whole_conn; } /* TODO: We don't (yet) account for possible different ordering of * the parameters to the plugin. * plugin A 1 B 2 * should be treated as equal to * plugin B 2 A 1. */ if (strcmp(run_o->name, res_o->name) != 0) { /* Either a different plugin, or just different settings * - plugin can be overwritten. */ m_asprintf(&cp, "set plugin %s %d %s", conn_name, i, res_o->name); plugin_changes[used++] = cp; } } if (res_o) res_o = res_o->next; if (run_o) run_o = run_o->next; } /* change only a few plugin settings. */ for(i=0; iname); err = stat("/dev/drbd/by-res", &sbuf); if (err) /* probably no udev rules in use */ return 0; err = stat(link_name, &sbuf); if (err) /* resource link cannot be stat()ed. */ return 1; /* double check device information */ if (!S_ISBLK(sbuf.st_mode)) return 1; if (major(sbuf.st_rdev) != DRBD_MAJOR) return 1; if (minor(sbuf.st_rdev) != res->me->volumes->device_minor) return 1; /* Link exists, and is expected block major:minor. * Do nothing. */ return 0; } /* moves option to the head of the single linked option list, * and marks it as to be skiped for "adjust only" commands * like disk-options see e.g. adm_attach_and_or_disk_options(). */ static void move_opt_to_head(struct d_option **head, struct d_option *o) { struct d_option *t; if (!o) return; o->adj_skip = 1; if (o == *head) return; for (t = *head; t->next != o; t = t->next) ; t->next = o->next; o->next = *head; *head = o; } void compare_max_bio_bvecs(struct d_volume *conf, struct d_volume *kern) { struct d_option *c = find_opt(conf->disk_options, "max-bio-bvecs"); struct d_option *k = find_opt(kern->disk_options, "max-bio-bvecs"); /* move to front of list, so we can skip it * for the following opts_equal */ move_opt_to_head(&conf->disk_options, c); move_opt_to_head(&kern->disk_options, k); /* simplify logic below, would otherwise have to * (!x || is_default(x) all the time. */ if (k && is_default(&attach_cmd_ctx, k)) k = NULL; /* there was a bvec restriction set, * but it is no longer in config, or vice versa */ if (!k != !c) conf->adj_attach = 1; /* restrictions differ */ if (k && c && !is_equal(&attach_cmd_ctx, k, c)) conf->adj_attach = 1; } /* similar to compare_max_bio_bvecs above */ void compare_size(struct d_volume *conf, struct d_volume *kern) { struct d_option *c = find_opt(conf->disk_options, "size"); struct d_option *k = find_opt(kern->disk_options, "size"); /* Special-case "max-bio-bvecs", we do not allow to change that * while attached, yet. * Also special case "size", we need to issue a resize command to change that. * Move both options to the head of the disk_options list, * so we can easily skip them in the opts_equal, later. */ move_opt_to_head(&conf->disk_options, c); move_opt_to_head(&kern->disk_options, k); if (k && is_default(&attach_cmd_ctx, k)) k = NULL; if (!k != !c) conf->adj_resize = 1; if (k && c && !is_equal(&attach_cmd_ctx, c, k)) conf->adj_resize = 1; } void compare_volume(struct d_volume *conf, struct d_volume *kern) { struct d_option *c, *k; conf->adj_add_minor = conf->device_minor != kern->device_minor; conf->adj_del_minor = conf->adj_add_minor; /* do we need to do a full attach, * potentially with a detach first? */ if (!disk_equal(conf, kern) || conf->adj_add_minor) { conf->adj_attach = conf->disk != NULL; conf->adj_detach = kern->disk != NULL; } /* do we need to do a full (detach/)attach, * because max_bio_bvec setting differs? */ compare_max_bio_bvecs(conf, kern); /* do we need to resize? */ if (!conf->adj_attach) compare_size(conf, kern); /* skip these two options (if present) for the opts_equal below. * These have been move_opt_to_head()ed before already. */ k = kern->disk_options; while (k && (!strcmp(k->name, "size") || !strcmp(k->name, "max-bio-bvecs"))) k = k->next; c = conf->disk_options; while (c && (!strcmp(c->name, "size") || !strcmp(c->name, "max-bio-bvecs"))) c = c->next; /* is it sufficient to only adjust the disk options? */ if (!conf->adj_attach) conf->adj_disk_opts = !opts_equal(&disk_options_ctx, c, k); if (conf->adj_attach && kern->disk) conf->adj_detach = 1; } struct d_volume *new_to_be_deleted_minor_from_template(struct d_volume *kern) { /* need to delete it from kernel. * Create a minimal volume, * and flag it as "del_minor". */ struct d_volume *conf = calloc(1, sizeof(*conf)); conf->vnr = kern->vnr; /* conf->device: no need */ conf->device_minor = kern->device_minor; if (kern->disk) { conf->disk = strdup(kern->disk); conf->meta_disk = strdup(kern->meta_disk); conf->meta_index = strdup(kern->meta_index); conf->adj_detach = 1; } conf->adj_del_minor = 1; return conf; } #define ASSERT(x) do { if (!(x)) { \ err("%s:%u:%s: ASSERT(%s) failed.\n", __FILE__, \ __LINE__, __func__, #x); \ abort(); } \ } while (0) /* Both conf and kern are single linked lists * supposed to be ordered by ->vnr; * We may need to conjure dummy volumes to issue "del-minor" on, * and insert these into the conf list. * The resulting new conf list head is returned. */ struct d_volume *compare_volumes(struct d_volume *conf, struct d_volume *kern) { struct d_volume *to_be_deleted = NULL; struct d_volume *conf_head = conf; while (conf || kern) { if (kern && (conf == NULL || kern->vnr < conf->vnr)) { to_be_deleted = INSERT_SORTED(to_be_deleted, new_to_be_deleted_minor_from_template(kern), vnr); kern = kern->next; } else if (conf && (kern == NULL || kern->vnr > conf->vnr)) { conf->adj_add_minor = 1; conf->adj_attach = 1; conf = conf->next; } else { ASSERT(conf); ASSERT(kern); ASSERT(conf->vnr == kern->vnr); compare_volume(conf, kern); conf = conf->next; kern = kern->next; } } for_each_volume(conf, to_be_deleted) conf_head = INSERT_SORTED(conf_head, conf, vnr); return conf_head; } static struct d_volume *matching_volume(struct d_volume *conf_vol, struct d_volume *kern_head) { struct d_volume *vol; for_each_volume(vol, kern_head) { if (vol->vnr == conf_vol->vnr) return vol; } return NULL; } /* * CAUTION this modifies global static char * config_file! */ int adm_adjust(struct cfg_ctx *ctx) { char* argv[20]; int pid, argc; struct d_resource* running; struct d_volume *vol; /* necessary per resource actions */ int do_res_options = 0; /* necessary per connection actions * (currently we still only have one connection per resource */ int do_net_options = 0; int do_disconnect = 0; int do_connect = 0; /* necessary per volume actions are flagged * in the vol->adj_* members. */ int can_do_proxy = 1; char config_file_dummy[250]; char *show_conn; char *resource_name; /* disable check_uniq, so it won't interfere * with parsing of drbdsetup show output */ config_valid = 2; /* setup error reporting context for the parsing routines */ line = 1; sprintf(config_file_dummy,"drbdsetup show %s", ctx->res->name); config_file = config_file_dummy; argc=0; argv[argc++]=drbdsetup; argv[argc++]="show"; ssprintf(argv[argc++], "%s", ctx->res->name); argv[argc++]=0; /* actually parse drbdsetup show output */ yyin = m_popen(&pid,argv); running = parse_resource_for_adjust(ctx); fclose(yyin); waitpid(pid, 0, 0); if (running) { /* Sets "me" and "peer" pointer */ post_parse(running, 0); set_peer_in_resource(running, 0); } /* Parse proxy settings, if this host has a proxy definition. * FIXME what about "zombie" proxy settings, if we remove proxy * settings from the config file without prior proxy-down, this won't * clean them from the proxy. */ if (ctx->res->me->proxy) { line = 1; resource_name = proxy_connection_name(ctx->res); m_asprintf(&show_conn, "show proxy-settings %s", resource_name); sprintf(config_file_dummy,"drbd-proxy-ctl -c '%s'", show_conn); config_file = config_file_dummy; argc=0; argv[argc++]=drbd_proxy_ctl; argv[argc++]="-c"; argv[argc++]=show_conn; argv[argc++]=0; /* actually parse "drbd-proxy-ctl show" output */ yyin = m_popen(&pid,argv); can_do_proxy = !parse_proxy_options_section(running); fclose(yyin); waitpid(pid,0,0); } ctx->res->me->volumes = compare_volumes(ctx->res->me->volumes, running ? running->me->volumes : NULL); if (running) { do_connect = !addr_equal(ctx->res,running); do_net_options = !opts_equal(&net_options_ctx, ctx->res->net_options, running->net_options); do_res_options = !opts_equal(&resource_options_cmd_ctx, ctx->res->res_options, running->res_options); } else { do_res_options = 0; do_connect = 1; schedule_deferred_cmd(adm_new_resource, ctx, "new-resource", CFG_PREREQ); } if (ctx->res->me->proxy && can_do_proxy) do_connect |= proxy_reconf(ctx, running); do_disconnect = do_connect && running && (running->peer || running->net_options); if (do_res_options) schedule_deferred_cmd(adm_set_default_res_options, ctx, "resource-options", CFG_RESOURCE); /* do we need to attach, * do we need to detach first, * or is this just some attribute change? */ for_each_volume(vol, ctx->res->me->volumes) { struct cfg_ctx tmp_ctx = { .res = ctx->res, .vol = vol }; if (vol->adj_detach || vol->adj_del_minor) { struct d_volume *kern_vol = matching_volume(vol, running->me->volumes); struct cfg_ctx k_ctx = tmp_ctx; if (kern_vol != NULL) k_ctx.vol = kern_vol; if (vol->adj_detach) schedule_deferred_cmd(adm_generic_s, &k_ctx, "detach", CFG_PREREQ); if (vol->adj_del_minor) schedule_deferred_cmd(adm_generic_s, &k_ctx, "del-minor", CFG_PREREQ); } if (vol->adj_add_minor) schedule_deferred_cmd(adm_new_minor, &tmp_ctx, "new-minor", CFG_DISK_PREREQ); if (vol->adj_attach) schedule_deferred_cmd(adm_attach, &tmp_ctx, "attach", CFG_DISK); if (vol->adj_disk_opts) schedule_deferred_cmd(adm_set_default_disk_options, &tmp_ctx, "disk-options", CFG_DISK); if (vol->adj_resize) schedule_deferred_cmd(adm_resize, &tmp_ctx, "resize", CFG_DISK); } if (do_connect) { /* "disconnect" specifying the end-point addresses currently in-use, * before "connect"ing with the addresses currently in-config-file. */ if (do_disconnect) { struct cfg_ctx tmp_ctx = { .res = running, .vol = vol, }; schedule_deferred_cmd(adm_disconnect, &tmp_ctx, "disconnect", CFG_NET_PREREQ); } schedule_deferred_cmd(adm_connect, ctx, "connect", CFG_NET); do_net_options = 0; } if (do_net_options) schedule_deferred_cmd(adm_set_default_net_options, ctx, "net-options", CFG_NET); return 0; } drbd-utils-8.9.10/user/v9/0000755000175000017500000000000013027242657015102 5ustar apoikosapoikosdrbd-utils-8.9.10/user/v9/Makefile.in0000644000175000017500000001141513011114651017132 0ustar apoikosapoikos# Makefile for drbd.o # # This file is part of DRBD by Philipp Reisner and Lars Ellenberg. # # drbd is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to # the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. # VPATH = ../../drbd-headers:../shared # variables set by configure DISTRO = @DISTRO@ prefix = @prefix@ exec_prefix = @exec_prefix@ localstatedir = @localstatedir@ datarootdir = @datarootdir@ datadir = @datadir@ sbindir = @sbindir@ sysconfdir = @sysconfdir@ BASH_COMPLETION_SUFFIX = @BASH_COMPLETION_SUFFIX@ UDEV_RULE_SUFFIX = @UDEV_RULE_SUFFIX@ INITDIR = @INITDIR@ LIBDIR = @prefix@/lib/@PACKAGE_TARNAME@ CC = @CC@ CFLAGS = @CFLAGS@ LDFLAGS = @LDFLAGS@ LN_S = @LN_S@ DRBD_LIB_DIR = @DRBD_LIB_DIR@ DRBD_RUN_DIR = @DRBD_RUN_DIR@ DRBD_LOCK_DIR = @DRBD_LOCK_DIR@ DRBD_CONFIG_DIR = @DRBD_CONFIG_DIR@ # features enabled or disabled by configure WITH_83_SUPPORT = @WITH_83_SUPPORT@ WITH_84_SUPPORT = @WITH_84_SUPPORT@ WITH_UDEV = @WITH_UDEV@ WITH_XEN = @WITH_XEN@ WITH_PACEMAKER = @WITH_PACEMAKER@ WITH_RGMANAGER = @WITH_RGMANAGER@ WITH_BASHCOMPLETION = @WITH_BASHCOMPLETION@ # for some reason some of the commands below only work correctly in bash, # and not in e.g. dash. I'm too lazy to fix it to be compatible. SHELL=/bin/bash # variables meant to be overridden from the make command line DESTDIR ?= / CFLAGS += -Wall -I../../drbd-headers -I.. -I. -I../shared CFLAGS += $(EXTRA_CFLAGS) GENETLINK_H := /usr/include/linux/genetlink.h libgenl.o: CFLAGS += $(shell for w in CTRL_ATTR_VERSION CTRL_ATTR_HDRSIZE CTRL_ATTR_MCAST_GROUPS; do grep -qw $$w $(GENETLINK_H) && echo -DHAVE_$$w; done) drbdadm-obj = drbdadm_scanner.o drbdadm_parser.o drbdadm_postparse.o \ drbdadm_main.o drbdadm_adjust.o drbdadm_dump.o drbdtool_common.o \ drbdadm_usage_cnt.o drbd_buildtag.o registry.o config_flags.o \ libgenl.o drbd_nla.o shared_tool.o shared_main.o shared_parser.o drbdsetup-obj = libgenl.o registry.o drbdsetup.o drbdtool_common.o \ drbd_buildtag.o drbd_strings.o config_flags.o drbd_nla.o \ wrap_printf.o drbdsetup_colors.o shared_tool.o drbdmeta-obj = drbdmeta.o drbdmeta_scanner.o drbdtool_common.o drbd_buildtag.o \ drbd_strings.o shared_tool.o all-obj := $(drbdadm-obj) $(drbdsetup-obj) $(drbdmeta-obj) all: tools ../shared_prereqs.mk: ; include ../shared_prereqs.mk tools: drbdadm drbdmeta drbdsetup drbdadm: $(drbdadm-obj) $(LINK.c) $(LDFLAGS) -o $@ $^ drbdadm_scanner.c: drbdadm_scanner.fl drbdadm_parser.h flex -s -odrbdadm_scanner.c drbdadm_scanner.fl drbdsetup: $(drbdsetup-obj) $(LINK.c) $(LDFLAGS) -o $@ $^ drbdmeta: $(drbdmeta-obj) $(LINK.c) $(LDFLAGS) -o $@ $^ clean: rm -f drbdadm_scanner.c rm -f drbdsetup drbdadm drbdmeta $(all-obj) rm -f drbd_strings.c drbd_strings.h rm -f *~ distclean: clean rm -f $(all-dep) install: install -d $(DESTDIR)$(sbindir) install -d $(DESTDIR)$(localstatedir)/lib/drbd install -d $(DESTDIR)$(localstatedir)/lock if getent group haclient > /dev/null 2> /dev/null ; then \ install -g haclient -m 4750 drbdsetup $(DESTDIR)$(sbindir) ; \ install -g haclient -m 4750 drbdmeta $(DESTDIR)$(sbindir) ; \ install -m 755 drbdadm $(DESTDIR)$(sbindir) ; \ else \ install -m 755 drbdsetup $(DESTDIR)$(sbindir) ; \ install -m 755 drbdmeta $(DESTDIR)$(sbindir) ; \ install -m 755 drbdadm $(DESTDIR)$(sbindir) ; \ fi if test -d $(DESTDIR)/sbin && \ ! test $(DESTDIR)/sbin -ef $(DESTDIR)$(sbindir) ; then \ ln -sf $(sbindir)/drbdsetup $(DESTDIR)/sbin ; \ ln -sf $(sbindir)/drbdmeta $(DESTDIR)/sbin ; \ ln -sf $(sbindir)/drbdadm $(DESTDIR)/sbin ; \ fi install -d $(DESTDIR)$(DRBD_LIB_DIR) uninstall: rm -f $(DESTDIR)$(sbindir)/{drbdsetup,drbdadm,drbdmeta} rm -f $(DESTDIR)/sbin/{drbdsetup,drbdadm,drbdmeta} spell: for f in drbdadm_adjust.c drbdadm_main.c drbdadm_parser.c drbdadm_usage_cnt.c drbdmeta.c drbdsetup.c drbdtool_common.c; do \ aspell --save-repl --dont-backup --personal=./../documentation/aspell.en.per check $$f; \ done .PHONY: install uninstall clean distclean spell ../../configure: @echo "please (re-)run ./autogen.sh with appropriate arguments"; exit 1 ../../config.status: ../../configure @echo "please (re-)run ./configure with appropriate arguments"; exit 1 Makefile.in: ; Makefile: Makefile.in ../../config.status cd ../.. && ./config.status user/v9/Makefile drbd-utils-8.9.10/user/v9/config_flags.c0000644000175000017500000006357313011116066017670 0ustar apoikosapoikos#include #include #include #include #include #include #include "libgenl.h" #include #include #include #include #include "drbd_nla.h" #include "drbdtool_common.h" #include #include "config_flags.h" #ifndef ARRAY_SIZE #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #endif #define NLA_POLICY(p) \ .nla_policy = p ## _nl_policy, \ .nla_policy_size = ARRAY_SIZE(p ## _nl_policy) /* ============================================================================================== */ static int enum_string_to_int(const char **map, int size, const char *value, int (*strcmp_fn)(const char *, const char *)) { int n; if (!value) return -1; for (n = 0; n < size; n++) { if (map[n] && !strcmp_fn(value, map[n])) return n; } return -1; } static bool enum_is_default(struct field_def *field, const char *value) { int n; n = enum_string_to_int(field->u.e.map, field->u.e.size, value, strcmp); return n == field->u.e.def; } static bool enum_is_equal(struct field_def *field, const char *a, const char *b) { return !strcmp(a, b); } static int type_of_field(struct context_def *ctx, struct field_def *field) { return ctx->nla_policy[__nla_type(field->nla_type)].type; } static int len_of_field(struct context_def *ctx, struct field_def *field) { return ctx->nla_policy[__nla_type(field->nla_type)].len; } static const char *get_enum(struct context_def *ctx, struct field_def *field, struct nlattr *nla) { int i; assert(type_of_field(ctx, field) == NLA_U32); i = nla_get_u32(nla); if (i < 0 || i >= field->u.e.size) return NULL; return field->u.e.map[i]; } static bool put_enum(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { int n; n = enum_string_to_int(field->u.e.map, field->u.e.size, value, strcmp); if (n == -1) return false; assert(type_of_field(ctx, field) == NLA_U32); nla_put_u32(msg, field->nla_type, n); return true; } static int enum_usage(struct field_def *field, char *str, int size) { const char** map = field->u.e.map; char sep = '{'; int n, len = 0, l; l = snprintf(str, size, "[--%s=", field->name); len += l; size -= l; for (n = 0; n < field->u.e.size; n++) { if (!map[n]) continue; l = snprintf(str + len, size, "%c%s", sep, map[n]); len += l; size -= l; sep = '|'; } assert (sep != '{'); l = snprintf(str+len, size, "}]"); len += l; /* size -= l; */ return len; } static bool enum_is_default_nocase(struct field_def *field, const char *value) { int n; n = enum_string_to_int(field->u.e.map, field->u.e.size, value, strcasecmp); return n == field->u.e.def; } static bool enum_is_equal_nocase(struct field_def *field, const char *a, const char *b) { return !strcasecmp(a, b); } static bool put_enum_nocase(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { int n; n = enum_string_to_int(field->u.e.map, field->u.e.size, value, strcasecmp); if (n == -1) return false; assert(type_of_field(ctx, field) == NLA_U32); nla_put_u32(msg, field->nla_type, n); return true; } static void enum_describe_xml(struct field_def *field) { const char **map = field->u.e.map; int n; printf("\t\n"); } static enum check_codes enum_check(struct field_def *field, const char *value) { int n = enum_string_to_int(field->u.e.map, field->u.e.size, value, strcmp); return n == -1 ? CC_NOT_AN_ENUM : CC_OK; } static enum check_codes enum_check_nocase(struct field_def *field, const char *value) { int n = enum_string_to_int(field->u.e.map, field->u.e.size, value, strcasecmp); return n == -1 ? CC_NOT_AN_ENUM : CC_OK; } struct field_class fc_enum = { .is_default = enum_is_default, .is_equal = enum_is_equal, .get = get_enum, .put = put_enum, .usage = enum_usage, .describe_xml = enum_describe_xml, .check = enum_check, }; struct field_class fc_enum_nocase = { .is_default = enum_is_default_nocase, .is_equal = enum_is_equal_nocase, .get = get_enum, .put = put_enum_nocase, .usage = enum_usage, .describe_xml = enum_describe_xml, .check = enum_check_nocase, }; /* ---------------------------------------------------------------------------------------------- */ static bool numeric_is_default(struct field_def *field, const char *value) { long long l; /* FIXME: unsigned long long values are broken. */ l = m_strtoll(value, field->u.n.scale); return l == field->u.n.def; } static bool numeric_is_equal(struct field_def *field, const char *a, const char *b) { long long la, lb; /* FIXME: unsigned long long values are broken. */ la = m_strtoll(a, field->u.n.scale); lb = m_strtoll(b, field->u.n.scale); return la == lb; } static const char *get_numeric(struct context_def *ctx, struct field_def *field, struct nlattr *nla) { static char buffer[1 + 20 + 2]; char scale = field->u.n.scale; unsigned long long l; int n; switch(type_of_field(ctx, field)) { case NLA_U8: l = nla_get_u8(nla); break; case NLA_U16: l = nla_get_u16(nla); break; case NLA_U32: l = nla_get_u32(nla); break; case NLA_U64: l = nla_get_u64(nla); break; default: return NULL; } if (field->u.n.is_signed) { /* Sign extend. */ switch(type_of_field(ctx, field)) { case NLA_U8: l = (int8_t)l; break; case NLA_U16: l = (int16_t)l; break; case NLA_U32: l = (int32_t)l; break; case NLA_U64: l = (int64_t)l; break; } n = snprintf(buffer, sizeof(buffer), "%lld%c", l, scale == '1' ? 0 : scale); } else n = snprintf(buffer, sizeof(buffer), "%llu%c", l, scale == '1' ? 0 : scale); assert(n < sizeof(buffer)); return buffer; } static bool put_numeric(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { long long l; /* FIXME: unsigned long long values are broken. */ l = m_strtoll(value, field->u.n.scale); switch(type_of_field(ctx, field)) { case NLA_U8: nla_put_u8(msg, field->nla_type, l); break; case NLA_U16: nla_put_u16(msg, field->nla_type, l); break; case NLA_U32: nla_put_u32(msg, field->nla_type, l); break; case NLA_U64: nla_put_u64(msg, field->nla_type, l); break; default: return false; } return true; } static int numeric_usage(struct field_def *field, char *str, int size) { return snprintf(str, size,"[--%s=(%lld ... %lld)]", field->name, field->u.n.min, field->u.n.max); } static void numeric_describe_xml(struct field_def *field) { printf("\t\n"); } static enum check_codes numeric_check(struct field_def *field, const char *value) { enum new_strtoll_errs e; unsigned long long l; e = new_strtoll(value, field->u.n.scale, &l); if (e != MSE_OK) return CC_NOT_A_NUMBER; if (l < field->u.n.min) { if (field->implicit_clamp) l = field->u.n.min; else return CC_TOO_SMALL; } if (l > field->u.n.max) { if (field->implicit_clamp) l = field->u.n.max; else return CC_TOO_BIG; } return CC_OK; } struct field_class fc_numeric = { .is_default = numeric_is_default, .is_equal = numeric_is_equal, .get = get_numeric, .put = put_numeric, .usage = numeric_usage, .describe_xml = numeric_describe_xml, .check = numeric_check, }; /* ---------------------------------------------------------------------------------------------- */ static int boolean_string_to_int(const char *value) { if (!value || !strcmp(value, "yes")) return 1; else if (!strcmp(value, "no")) return 0; else return -1; } static bool boolean_is_default(struct field_def *field, const char *value) { int yesno; yesno = boolean_string_to_int(value); return yesno == field->u.b.def; } static bool boolean_is_equal(struct field_def *field, const char *a, const char *b) { return boolean_string_to_int(a) == boolean_string_to_int(b); } static const char *get_boolean(struct context_def *ctx, struct field_def *field, struct nlattr *nla) { int i; assert(type_of_field(ctx, field) == NLA_U8); i = nla_get_u8(nla); return i ? "yes" : "no"; } static bool put_boolean(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { int yesno; yesno = boolean_string_to_int(value); if (yesno == -1) return false; assert(type_of_field(ctx, field) == NLA_U8); nla_put_u8(msg, field->nla_type, yesno); return true; } static bool put_flag(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { int yesno; yesno = boolean_string_to_int(value); if (yesno == -1) return false; assert(type_of_field(ctx, field) == NLA_U8); if (yesno) nla_put_u8(msg, field->nla_type, yesno); return true; } static int boolean_usage(struct field_def *field, char *str, int size) { return snprintf(str, size,"[--%s={yes|no}]", field->name); } static void boolean_describe_xml(struct field_def *field) { printf("\t\n", field->name, field->u.b.def ? "yes" : "no"); } static enum check_codes boolean_check(struct field_def *field, const char *value) { int yesno = boolean_string_to_int(value); return yesno == -1 ? CC_NOT_A_BOOL : CC_OK; } struct field_class fc_boolean = { .is_default = boolean_is_default, .is_equal = boolean_is_equal, .get = get_boolean, .put = put_boolean, .usage = boolean_usage, .describe_xml = boolean_describe_xml, .check = boolean_check, }; struct field_class fc_flag = { .is_default = boolean_is_default, .is_equal = boolean_is_equal, .get = get_boolean, .put = put_flag, .usage = boolean_usage, .describe_xml = boolean_describe_xml, .check = boolean_check, }; /* ---------------------------------------------------------------------------------------------- */ static bool string_is_default(struct field_def *field, const char *value) { return value && !strcmp(value, ""); } static bool string_is_equal(struct field_def *field, const char *a, const char *b) { return !strcmp(a, b); } static const char *get_string(struct context_def *ctx, struct field_def *field, struct nlattr *nla) { char *str; int len; assert(type_of_field(ctx, field) == NLA_NUL_STRING); str = (char *)nla_data(nla); len = len_of_field(ctx, field); assert(memchr(str, 0, len + 1) != NULL); return str; } static bool put_string(struct context_def *ctx, struct field_def *field, struct msg_buff *msg, const char *value) { assert(type_of_field(ctx, field) == NLA_NUL_STRING); nla_put_string(msg, field->nla_type, value); return true; } static int string_usage(struct field_def *field, char *str, int size) { return snprintf(str, size,"[--%s=]", field->name); } static void string_describe_xml(struct field_def *field) { printf("\t\n", field->name); } static enum check_codes string_check(struct field_def *field, const char *value) { if (field->u.s.max_len) { if (strlen(value) >= field->u.s.max_len) return CC_STR_TOO_LONG; } return CC_OK; } const char *double_quote_string(const char *str) { static char *buffer; const char *s; char *b; int len = 0; for (s = str; *s; s++) { if (*s == '\\' || *s == '"') len++; len++; } b = realloc(buffer, len + 3); if (!b) return NULL; buffer = b; *b++ = '"'; for (s = str; *s; s++) { if (*s == '\\' || *s == '"') *b++ = '\\'; *b++ = *s; } *b++ = '"'; *b++ = 0; return buffer; } struct field_class fc_string = { .is_default = string_is_default, .is_equal = string_is_equal, .get = get_string, .put = put_string, .usage = string_usage, .describe_xml = string_describe_xml, .check = string_check, }; /* ============================================================================================== */ #define ENUM(f, d) \ .nla_type = T_ ## f, \ .ops = &fc_enum, \ .u = { .e = { \ .map = f ## _map, \ .size = ARRAY_SIZE(f ## _map), \ .def = DRBD_ ## d ## _DEF } } #define ENUM_NOCASE(f, d) \ .nla_type = T_ ## f, \ .ops = &fc_enum_nocase, \ .u = { .e = { \ .map = f ## _map, \ .size = ARRAY_SIZE(f ## _map), \ .def = DRBD_ ## d ## _DEF } } #define NUMERIC(f, d) \ .nla_type = T_ ## f, \ .ops = &fc_numeric, \ .u = { .n = { \ .min = DRBD_ ## d ## _MIN, \ .max = DRBD_ ## d ## _MAX, \ .def = DRBD_ ## d ## _DEF, \ .is_signed = F_ ## f ## _IS_SIGNED, \ .scale = DRBD_ ## d ## _SCALE } } #define BOOLEAN(f, d) \ .nla_type = T_ ## f, \ .ops = &fc_boolean, \ .u = { .b = { \ .def = DRBD_ ## d ## _DEF } }, \ .argument_is_optional = true #define FLAG(f) \ .nla_type = T_ ## f, \ .ops = &fc_flag, \ .u = { .b = { \ .def = false } }, \ .argument_is_optional = true #define STRING(f) \ .nla_type = T_ ## f, \ .ops = &fc_string, \ .needs_double_quoting = true #define STRING_MAX_LEN(f, l) \ STRING(f), \ .u = { .s = { .max_len = l } } } /* ============================================================================================== */ const char *wire_protocol_map[] = { [DRBD_PROT_A] = "A", [DRBD_PROT_B] = "B", [DRBD_PROT_C] = "C", }; const char *on_io_error_map[] = { [EP_PASS_ON] = "pass_on", [EP_CALL_HELPER] = "call-local-io-error", [EP_DETACH] = "detach", }; const char *fencing_policy_map[] = { [FP_DONT_CARE] = "dont-care", [FP_RESOURCE] = "resource-only", [FP_STONITH] = "resource-and-stonith", }; const char *after_sb_0p_map[] = { [ASB_DISCONNECT] = "disconnect", [ASB_DISCARD_YOUNGER_PRI] = "discard-younger-primary", [ASB_DISCARD_OLDER_PRI] = "discard-older-primary", [ASB_DISCARD_ZERO_CHG] = "discard-zero-changes", [ASB_DISCARD_LEAST_CHG] = "discard-least-changes", [ASB_DISCARD_LOCAL] = "discard-local", [ASB_DISCARD_REMOTE] = "discard-remote", }; const char *after_sb_1p_map[] = { [ASB_DISCONNECT] = "disconnect", [ASB_CONSENSUS] = "consensus", [ASB_VIOLENTLY] = "violently-as0p", [ASB_DISCARD_SECONDARY] = "discard-secondary", [ASB_CALL_HELPER] = "call-pri-lost-after-sb", }; const char *after_sb_2p_map[] = { [ASB_DISCONNECT] = "disconnect", [ASB_VIOLENTLY] = "violently-as0p", [ASB_CALL_HELPER] = "call-pri-lost-after-sb", }; const char *rr_conflict_map[] = { [ASB_DISCONNECT] = "disconnect", [ASB_VIOLENTLY] = "violently", [ASB_CALL_HELPER] = "call-pri-lost", }; const char *on_no_data_map[] = { [OND_IO_ERROR] = "io-error", [OND_SUSPEND_IO] = "suspend-io", }; const char *on_congestion_map[] = { [OC_BLOCK] = "block", [OC_PULL_AHEAD] = "pull-ahead", [OC_DISCONNECT] = "disconnect", }; const char *read_balancing_map[] = { [RB_PREFER_LOCAL] = "prefer-local", [RB_PREFER_REMOTE] = "prefer-remote", [RB_ROUND_ROBIN] = "round-robin", [RB_LEAST_PENDING] = "least-pending", [RB_CONGESTED_REMOTE] = "when-congested-remote", [RB_32K_STRIPING] = "32K-striping", [RB_64K_STRIPING] = "64K-striping", [RB_128K_STRIPING] = "128K-striping", [RB_256K_STRIPING] = "256K-striping", [RB_512K_STRIPING] = "512K-striping", [RB_1M_STRIPING] = "1M-striping" }; #define CHANGEABLE_DISK_OPTIONS \ { "on-io-error", ENUM(on_io_error, ON_IO_ERROR) }, \ /*{ "fencing", ENUM(fencing_policy, FENCING) },*/ \ { "disk-barrier", BOOLEAN(disk_barrier, DISK_BARRIER) }, \ { "disk-flushes", BOOLEAN(disk_flushes, DISK_FLUSHES) }, \ { "disk-drain", BOOLEAN(disk_drain, DISK_DRAIN) }, \ { "md-flushes", BOOLEAN(md_flushes, MD_FLUSHES) }, \ { "resync-after", NUMERIC(resync_after, MINOR_NUMBER), .checked_in_postparse = true}, \ { "al-extents", NUMERIC(al_extents, AL_EXTENTS), .implicit_clamp = true, }, \ { "al-updates", BOOLEAN(al_updates, AL_UPDATES) }, \ { "discard-zeroes-if-aligned", \ BOOLEAN(discard_zeroes_if_aligned, DISCARD_ZEROES_IF_ALIGNED) }, \ { "disk-timeout", NUMERIC(disk_timeout, DISK_TIMEOUT), \ .unit = "1/10 seconds" }, \ { "read-balancing", ENUM(read_balancing, READ_BALANCING) }, \ { "rs-discard-granularity", \ NUMERIC(rs_discard_granularity, RS_DISCARD_GRANULARITY), \ .unit = "bytes" } #define CHANGEABLE_NET_OPTIONS \ { "protocol", ENUM_NOCASE(wire_protocol, PROTOCOL) }, \ { "timeout", NUMERIC(timeout, TIMEOUT), \ .unit = "1/10 seconds" }, \ { "max-epoch-size", NUMERIC(max_epoch_size, MAX_EPOCH_SIZE) }, \ { "connect-int", NUMERIC(connect_int, CONNECT_INT), \ .unit = "seconds" }, \ { "ping-int", NUMERIC(ping_int, PING_INT), \ .unit = "seconds" }, \ { "sndbuf-size", NUMERIC(sndbuf_size, SNDBUF_SIZE), \ .unit = "bytes" }, \ { "rcvbuf-size", NUMERIC(rcvbuf_size, RCVBUF_SIZE), \ .unit = "bytes" }, \ { "ko-count", NUMERIC(ko_count, KO_COUNT) }, \ { "allow-two-primaries", BOOLEAN(two_primaries, ALLOW_TWO_PRIMARIES) }, \ { "cram-hmac-alg", STRING(cram_hmac_alg) }, \ { "shared-secret", STRING_MAX_LEN(shared_secret, SHARED_SECRET_MAX), \ { "after-sb-0pri", ENUM(after_sb_0p, AFTER_SB_0P) }, \ { "after-sb-1pri", ENUM(after_sb_1p, AFTER_SB_1P) }, \ { "after-sb-2pri", ENUM(after_sb_2p, AFTER_SB_2P) }, \ { "always-asbp", BOOLEAN(always_asbp, ALWAYS_ASBP) }, \ { "rr-conflict", ENUM(rr_conflict, RR_CONFLICT) }, \ { "ping-timeout", NUMERIC(ping_timeo, PING_TIMEO), \ .unit = "1/10 seconds" }, \ { "data-integrity-alg", STRING(integrity_alg) }, \ { "tcp-cork", BOOLEAN(tcp_cork, TCP_CORK) }, \ { "on-congestion", ENUM(on_congestion, ON_CONGESTION) }, \ { "congestion-fill", NUMERIC(cong_fill, CONG_FILL), \ .unit = "bytes" }, \ { "congestion-extents", NUMERIC(cong_extents, CONG_EXTENTS) }, \ { "csums-alg", STRING(csums_alg) }, \ { "csums-after-crash-only", BOOLEAN(csums_after_crash_only, \ CSUMS_AFTER_CRASH_ONLY) }, \ { "verify-alg", STRING(verify_alg) }, \ { "use-rle", BOOLEAN(use_rle, USE_RLE) }, \ { "socket-check-timeout", NUMERIC(sock_check_timeo, SOCKET_CHECK_TIMEO) }, \ { "fencing", ENUM(fencing_policy, FENCING) }, \ { "max-buffers", NUMERIC(max_buffers, MAX_BUFFERS) }, \ { "_name", STRING(name) } struct context_def disk_options_ctx = { NLA_POLICY(disk_conf), .nla_type = DRBD_NLA_DISK_CONF, .fields = { CHANGEABLE_DISK_OPTIONS, { } }, }; struct context_def net_options_ctx = { NLA_POLICY(net_conf), .nla_type = DRBD_NLA_NET_CONF, .fields = { CHANGEABLE_NET_OPTIONS, { } }, }; struct context_def primary_cmd_ctx = { NLA_POLICY(set_role_parms), .nla_type = DRBD_NLA_SET_ROLE_PARMS, .fields = { { "force", FLAG(assume_uptodate) }, { } }, }; struct context_def attach_cmd_ctx = { NLA_POLICY(disk_conf), .nla_type = DRBD_NLA_DISK_CONF, .fields = { { "size", NUMERIC(disk_size, DISK_SIZE), .unit = "bytes" }, CHANGEABLE_DISK_OPTIONS, /* { "*", STRING(backing_dev) }, */ /* { "*", STRING(meta_dev) }, */ /* { "*", NUMERIC(meta_dev_idx, MINOR_NUMBER) }, */ { } }, }; struct context_def detach_cmd_ctx = { NLA_POLICY(detach_parms), .nla_type = DRBD_NLA_DETACH_PARMS, .fields = { { "force", FLAG(force_detach) }, { } }, }; struct context_def new_peer_cmd_ctx = { NLA_POLICY(net_conf), .nla_type = DRBD_NLA_NET_CONF, .fields = { { "transport", STRING(transport_name) }, CHANGEABLE_NET_OPTIONS, { } }, }; struct context_def path_cmd_ctx = { NLA_POLICY(path_parms), .nla_type = DRBD_NLA_PATH_PARMS, .fields = { { } }, }; #define CONNECT_CMD_OPTIONS \ { "tentative", FLAG(tentative) }, \ { "discard-my-data", FLAG(discard_my_data) } struct context_def connect_cmd_ctx = { NLA_POLICY(connect_parms), .nla_type = DRBD_NLA_CONNECT_PARMS, .fields = { CONNECT_CMD_OPTIONS, { } }, }; struct context_def show_net_options_ctx = { NLA_POLICY(net_conf), .nla_type = DRBD_NLA_NET_CONF, .fields = { { "transport", STRING(transport_name) }, CHANGEABLE_NET_OPTIONS, { } }, }; struct context_def disconnect_cmd_ctx = { NLA_POLICY(disconnect_parms), .nla_type = DRBD_NLA_DISCONNECT_PARMS, .fields = { { "force", FLAG(force_disconnect) }, { } }, }; struct context_def resize_cmd_ctx = { NLA_POLICY(resize_parms), .nla_type = DRBD_NLA_RESIZE_PARMS, .fields = { { "size", NUMERIC(resize_size, DISK_SIZE), .unit = "bytes" }, { "assume-peer-has-space", FLAG(resize_force) }, { "assume-clean", FLAG(no_resync) }, { "al-stripes", NUMERIC(al_stripes, AL_STRIPES) }, { "al-stripe-size-kB", NUMERIC(al_stripe_size, AL_STRIPE_SIZE) }, { } }, }; struct context_def resource_options_ctx = { NLA_POLICY(res_opts), .nla_type = DRBD_NLA_RESOURCE_OPTS, .fields = { { "cpu-mask", STRING(cpu_mask) }, { "on-no-data-accessible", ENUM(on_no_data, ON_NO_DATA) }, { "auto-promote", BOOLEAN(auto_promote, AUTO_PROMOTE) }, { "peer-ack-window", NUMERIC(peer_ack_window, PEER_ACK_WINDOW), .unit = "bytes" }, { "peer-ack-delay", NUMERIC(peer_ack_delay, PEER_ACK_DELAY), .unit = "milliseconds" }, { "twopc-timeout", NUMERIC(twopc_timeout, TWOPC_TIMEOUT), .unit = "1/10 seconds" }, { "twopc-retry-timeout", NUMERIC(twopc_retry_timeout, TWOPC_RETRY_TIMEOUT), .unit = "1/10 seconds" }, { "auto-promote-timeout", NUMERIC(auto_promote_timeout, AUTO_PROMOTE_TIMEOUT), .unit = "1/10 seconds"}, { "max-io-depth", NUMERIC(nr_requests, NR_REQUESTS) }, { } }, }; struct context_def new_current_uuid_cmd_ctx = { NLA_POLICY(new_c_uuid_parms), .nla_type = DRBD_NLA_NEW_C_UUID_PARMS, .fields = { { "clear-bitmap", FLAG(clear_bm) }, { } }, }; struct context_def verify_cmd_ctx = { NLA_POLICY(start_ov_parms), .nla_type = DRBD_NLA_START_OV_PARMS, .fields = { { "start", NUMERIC(ov_start_sector, DISK_SIZE), .unit = "bytes" }, { "stop", NUMERIC(ov_stop_sector, DISK_SIZE), .unit = "bytes" }, { } }, }; struct context_def device_options_ctx = { NLA_POLICY(device_conf), .nla_type = DRBD_NLA_DEVICE_CONF, .fields = { { "max-bio-size", NUMERIC(max_bio_size, MAX_BIO_SIZE) }, { } }, }; struct context_def invalidate_ctx = { NLA_POLICY(invalidate_parms), .nla_type = DRBD_NLA_INVALIDATE_PARMS, .fields = { { "sync-from-peer-node-id", NUMERIC(sync_from_peer_node_id, SYNC_FROM_NID) }, { } }, }; struct context_def peer_device_options_ctx = { NLA_POLICY(peer_device_conf), .nla_type = DRBD_NLA_PEER_DEVICE_OPTS, .fields = { { "resync-rate", NUMERIC(resync_rate, RESYNC_RATE), .unit = "bytes/second" }, { "c-plan-ahead", NUMERIC(c_plan_ahead, C_PLAN_AHEAD), .unit = "1/10 seconds" }, { "c-delay-target", NUMERIC(c_delay_target, C_DELAY_TARGET), .unit = "1/10 seconds" }, { "c-fill-target", NUMERIC(c_fill_target, C_FILL_TARGET), .unit = "bytes" }, { "c-max-rate", NUMERIC(c_max_rate, C_MAX_RATE), .unit = "bytes/second" }, { "c-min-rate", NUMERIC(c_min_rate, C_MIN_RATE), .unit = "bytes/second" }, { "bitmap", BOOLEAN(bitmap, BITMAP) }, { } }, }; // only used in drbdadm: struct context_def create_md_ctx = { .fields = { { .name = "max-peers", .argument_is_optional = false }, { .name = "peer-max-bio-size", .argument_is_optional = false }, { .name = "al-stripes", .argument_is_optional = false }, { .name = "al-stripe-size-kB", .argument_is_optional = false }, { .name = "force", .argument_is_optional = true }, { } }, }; struct context_def adjust_ctx = { .fields = { { "skip-disk", .argument_is_optional = true }, { "skip-net", .argument_is_optional = true }, CONNECT_CMD_OPTIONS, { } }, }; // only used by drbdadm's config file parser: struct context_def handlers_ctx = { .fields = { { "pri-on-incon-degr", .ops = &fc_string, .needs_double_quoting = true}, { "pri-lost-after-sb", .ops = &fc_string, .needs_double_quoting = true}, { "pri-lost", .ops = &fc_string, .needs_double_quoting = true}, { "initial-split-brain", .ops = &fc_string, .needs_double_quoting = true}, { "split-brain", .ops = &fc_string, .needs_double_quoting = true}, { "outdate-peer", .ops = &fc_string, .needs_double_quoting = true}, { "fence-peer", .ops = &fc_string, .needs_double_quoting = true}, { "unfence-peer", .ops = &fc_string, .needs_double_quoting = true}, { "local-io-error", .ops = &fc_string, .needs_double_quoting = true}, { "before-resync-target", .ops = &fc_string, .needs_double_quoting = true}, { "after-resync-target", .ops = &fc_string, .needs_double_quoting = true}, { "before-resync-source", .ops = &fc_string, .needs_double_quoting = true}, { "out-of-sync", .ops = &fc_string, .needs_double_quoting = true}, { } }, }; struct context_def proxy_options_ctx = { .fields = { { "memlimit", .ops = &fc_numeric, .u={.n={.min = 0, .max=-1}}}, { "read-loops", .ops = &fc_numeric, .u={.n={.min = 0, .max=-1}}}, { "compression", .ops = &fc_numeric, .u={.n={.min = 0, .max=-1}}}, { "bwlimit", .ops = &fc_numeric, .u={.n={.min = 0, .max=-1}}}, { "sndbuf-size", NUMERIC(sndbuf_size, SNDBUF_SIZE), .unit = "bytes" }, { "rcvbuf-size", NUMERIC(rcvbuf_size, RCVBUF_SIZE), .unit = "bytes" }, { "ping-timeout", NUMERIC(ping_timeo, PING_TIMEO), .unit = "1/10 seconds" }, { } }, }; #define ADM_NUMERIC(d) \ .ops = &fc_numeric, \ .u = { .n = { \ .min = DRBD_ ## d ## _MIN, \ .max = DRBD_ ## d ## _MAX, \ .def = DRBD_ ## d ## _DEF, \ .is_signed = false, \ .scale = DRBD_ ## d ## _SCALE } } struct context_def startup_options_ctx = { .fields = { { "wfc-timeout", ADM_NUMERIC(WFC_TIMEOUT) }, { "degr-wfc-timeout", ADM_NUMERIC(DEGR_WFC_TIMEOUT) }, { "outdated-wfc-timeout", ADM_NUMERIC(OUTDATED_WFC_TIMEOUT) }, { "wait-after-sb", .ops = &fc_boolean }, { } }, }; struct context_def wildcard_ctx = { }; drbd-utils-8.9.10/user/v9/drbdadm_main.c0000644000175000017500000025626613026714234017662 0ustar apoikosapoikos/* drbdadm_main.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2002-2008, LINBIT Information Technologies GmbH. Copyright (C) 2002-2008, Philipp Reisner . Copyright (C) 2002-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #define _XOPEN_SOURCE 600 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linux/drbd_limits.h" #include "drbdtool_common.h" #include "drbdadm.h" #include "registry.h" #include "config_flags.h" #include "drbdadm_dump.h" #include "shared_main.h" #include "drbdadm_parser.h" #define MAX_ARGS 40 char *progname; struct deferred_cmd { struct cfg_ctx ctx; STAILQ_ENTRY(deferred_cmd) link; }; struct option general_admopt[] = { {"stacked", no_argument, 0, 'S'}, {"dry-run", no_argument, 0, 'd'}, {"verbose", no_argument, 0, 'v'}, {"config-file", required_argument, 0, 'c'}, {"config-to-test", required_argument, 0, 't'}, {"config-to-exclude", required_argument, 0, 'E'}, {"drbdsetup", required_argument, 0, 's'}, {"drbdmeta", required_argument, 0, 'm'}, {"drbd-proxy-ctl", required_argument, 0, 'p'}, {"sh-varname", required_argument, 0, 'n'}, {"peer", required_argument, 0, 'P'}, {"version", no_argument, 0, 'V'}, {"setup-option", required_argument, 0, 'W'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; struct option *admopt = general_admopt; extern int yydebug; extern FILE *yyin; static int adm_adjust(const struct cfg_ctx *ctx); static int adm_new_minor(const struct cfg_ctx *ctx); static int adm_resource(const struct cfg_ctx *); static int adm_attach(const struct cfg_ctx *); static int adm_connect(const struct cfg_ctx *); static int adm_new_peer(const struct cfg_ctx *); static int adm_path(const struct cfg_ctx *); static int adm_resize(const struct cfg_ctx *); static int adm_up(const struct cfg_ctx *); static int adm_wait_c(const struct cfg_ctx *); static int adm_wait_ci(const struct cfg_ctx *); static int adm_proxy_up(const struct cfg_ctx *); static int adm_proxy_down(const struct cfg_ctx *); static int sh_nop(const struct cfg_ctx *); static int sh_resources(const struct cfg_ctx *); static int sh_resource(const struct cfg_ctx *); static int sh_mod_parms(const struct cfg_ctx *); static int sh_dev(const struct cfg_ctx *); static int sh_udev(const struct cfg_ctx *); static int sh_minor(const struct cfg_ctx *); static int sh_ip(const struct cfg_ctx *); static int sh_lres(const struct cfg_ctx *); static int sh_ll_dev(const struct cfg_ctx *); static int sh_md_dev(const struct cfg_ctx *); static int sh_md_idx(const struct cfg_ctx *); static int adm_drbdmeta(const struct cfg_ctx *); static int adm_khelper(const struct cfg_ctx *); static int adm_setup_and_meta(const struct cfg_ctx *); static int hidden_cmds(const struct cfg_ctx *); static int adm_outdate(const struct cfg_ctx *); static int adm_chk_resize(const struct cfg_ctx *); static int adm_drbdsetup(const struct cfg_ctx *); static int adm_invalidate(const struct cfg_ctx *); static int __adm_drbdsetup_silent(const struct cfg_ctx *ctx); static int adm_forget_peer(const struct cfg_ctx *); static int adm_peer_device(const struct cfg_ctx *); int ctx_by_name(struct cfg_ctx *ctx, const char *id, checks check); int was_file_already_seen(char *fn); static char *get_opt_val(struct options *, const char *, char *); char ss_buffer[1024]; const char *hostname; int line = 1; int fline; char *config_file = NULL; char *config_save = NULL; char *config_test = NULL; struct resources config = STAILQ_HEAD_INITIALIZER(config); struct d_resource *common = NULL; struct ifreq *ifreq_list = NULL; int is_drbd_top; enum { NORMAL, STACKED, IGNORED, __N_RESOURCE_TYPES }; int nr_resources[__N_RESOURCE_TYPES]; int nr_volumes[__N_RESOURCE_TYPES]; int number_of_minors = 0; int config_from_stdin = 0; int config_valid = 1; int no_tty; int dry_run = 0; int verbose = 0; int adjust_with_progress = 0; bool help; int do_verify_ips = 0; int do_register = 1; /* whether drbdadm was called with "all" instead of resource name(s) */ int all_resources = 0; char *drbdsetup = NULL; char *drbdmeta = NULL; char *drbdadm_83 = NULL; char *drbdadm_84 = NULL; char *drbd_proxy_ctl; char *sh_varname = NULL; struct names backend_options = STAILQ_HEAD_INITIALIZER(backend_options); char *connect_to_host = NULL; STAILQ_HEAD(deferred_cmds, deferred_cmd) deferred_cmds[__CFG_LAST]; int adm_adjust_wp(const struct cfg_ctx *ctx) { if (!verbose && !dry_run) adjust_with_progress = 1; return adm_adjust(ctx); } /* DRBD adm_cmd flags shortcuts, * to avoid merge conflicts and unreadable diffs * when we add the next flag */ #define ACF1_DEFAULT \ .show_in_usage = 1, \ .res_name_required = 1, \ .backend_res_name = 1, \ .iterate_volumes = 1, \ .verify_ips = 0, \ .uc_dialog = 1, \ #define ACF1_MINOR_ONLY \ .show_in_usage = 1, \ .res_name_required = 1, \ .backend_res_name = 0, \ .iterate_volumes = 1, \ .verify_ips = 0, \ .uc_dialog = 1, \ .disk_required = 1, \ #define ACF1_RESNAME \ .show_in_usage = 1, \ .res_name_required = 1, \ .backend_res_name = 1, \ .uc_dialog = 1, \ #define ACF1_CONNECT \ .show_in_usage = 1, \ .res_name_required = 1, \ .backend_res_name = 1, \ .iterate_volumes = 0, \ .verify_ips = 1, \ .need_peer = 1, \ .uc_dialog = 1, \ #define ACF1_DISCONNECT \ .show_in_usage = 1, \ .res_name_required = 1, \ .backend_res_name = 1, \ .need_peer = 1, \ .uc_dialog = 1, \ #define ACF1_DEFNET \ .show_in_usage = 1, \ .res_name_required = 1, \ .backend_res_name = 1, \ .iterate_volumes = 1, \ .verify_ips = 1, \ .need_peer = 1, \ .uc_dialog = 1, \ #define ACF1_WAIT \ .show_in_usage = 1, \ .res_name_required = 1, \ .vol_id_optional = 1, \ .verify_ips = 1, \ .uc_dialog = 1, \ #define ACF1_PEER_DEVICE \ .show_in_usage = 1, \ .res_name_required = 1, \ .backend_res_name = 1, \ .iterate_volumes = 1, \ .need_peer = 1, \ .verify_ips = 0, \ .uc_dialog = 1, \ #define ACF3_RES_HANDLER \ .show_in_usage = 3, \ .res_name_required = 1, \ .backend_res_name = 1, \ .iterate_volumes = 0, \ .vol_id_required = 0, \ .verify_ips = 0, \ .use_cached_config_file = 1, \ #define ACF4_ADVANCED \ .show_in_usage = 4, \ .res_name_required = 1, \ .backend_res_name = 1, \ .iterate_volumes = 1, \ .verify_ips = 0, \ .uc_dialog = 1, \ #define ACF4_ADVANCED_NEED_VOL \ .show_in_usage = 4, \ .res_name_required = 1, \ .backend_res_name = 1, \ .iterate_volumes = 1, \ .vol_id_required = 1, \ .verify_ips = 0, \ .uc_dialog = 1, \ #define ACF1_DUMP \ .show_in_usage = 1, \ .res_name_required = 1, \ .backend_res_name = 1, \ .verify_ips = 1, \ .uc_dialog = 1, \ .test_config = 1, \ #define ACF2_SHELL \ .show_in_usage = 2, \ .iterate_volumes = 1, \ .res_name_required = 1, \ .backend_res_name = 1, \ .verify_ips = 0, \ #define ACF2_SH_RESNAME \ .show_in_usage = 2, \ .iterate_volumes = 0, \ .res_name_required = 1, \ .backend_res_name = 1, \ .verify_ips = 0, \ #define ACF2_PROXY \ .show_in_usage = 2, \ .res_name_required = 1, \ .backend_res_name = 1, \ .verify_ips = 0, \ .need_peer = 1, \ .is_proxy_cmd = 1, \ #define ACF2_HOOK \ .show_in_usage = 2, \ .res_name_required = 1, \ .backend_res_name = 1, \ .verify_ips = 0, \ .use_cached_config_file = 1, \ #define ACF2_GEN_SHELL \ .show_in_usage = 2, \ .res_name_required = 0, \ .verify_ips = 0, \ /* */ struct adm_cmd attach_cmd = {"attach", adm_attach, &attach_cmd_ctx, ACF1_MINOR_ONLY }; /* */ struct adm_cmd disk_options_cmd = {"disk-options", adm_attach, &attach_cmd_ctx, ACF1_MINOR_ONLY }; /* */ struct adm_cmd detach_cmd = {"detach", adm_drbdsetup, &detach_cmd_ctx, .takes_long = 1, ACF1_MINOR_ONLY }; /* */ struct adm_cmd new_peer_cmd = {"new-peer", adm_new_peer, &new_peer_cmd_ctx, ACF1_CONNECT}; /* */ struct adm_cmd del_peer_cmd = {"del-peer", adm_drbdsetup, &disconnect_cmd_ctx, ACF1_CONNECT}; /* */ struct adm_cmd new_path_cmd = {"new-path", adm_path, &path_cmd_ctx, ACF1_CONNECT .iterate_paths = 1}; /* */ struct adm_cmd del_path_cmd = {"del-path", adm_path, &path_cmd_ctx, ACF1_CONNECT .iterate_paths = 1}; /* */ struct adm_cmd connect_cmd = {"connect", adm_connect, &connect_cmd_ctx, ACF1_CONNECT}; /* */ struct adm_cmd net_options_cmd = {"net-options", adm_new_peer, &net_options_ctx, ACF1_CONNECT}; /* */ struct adm_cmd disconnect_cmd = {"disconnect", adm_drbdsetup, &disconnect_cmd_ctx, ACF1_DISCONNECT}; static struct adm_cmd up_cmd = {"up", adm_up, ACF1_RESNAME }; /* */ struct adm_cmd res_options_cmd = {"resource-options", adm_resource, &resource_options_ctx, ACF1_RESNAME}; static struct adm_cmd down_cmd = {"down", adm_drbdsetup, ACF1_RESNAME .takes_long = 1}; static struct adm_cmd primary_cmd = {"primary", adm_drbdsetup, &primary_cmd_ctx, ACF1_RESNAME .takes_long = 1}; static struct adm_cmd secondary_cmd = {"secondary", adm_drbdsetup, ACF1_RESNAME .takes_long = 1}; static struct adm_cmd invalidate_cmd = {"invalidate", adm_invalidate, ACF1_MINOR_ONLY }; static struct adm_cmd invalidate_remote_cmd = {"invalidate-remote", adm_drbdsetup, ACF1_PEER_DEVICE .takes_long = 1}; static struct adm_cmd outdate_cmd = {"outdate", adm_outdate, ACF1_DEFAULT}; /* */ struct adm_cmd resize_cmd = {"resize", adm_resize, &resize_cmd_ctx, ACF1_DEFAULT .disk_required = 1}; static struct adm_cmd verify_cmd = {"verify", adm_drbdsetup, &verify_cmd_ctx, ACF1_PEER_DEVICE}; static struct adm_cmd pause_sync_cmd = {"pause-sync", adm_drbdsetup, ACF1_PEER_DEVICE}; static struct adm_cmd resume_sync_cmd = {"resume-sync", adm_drbdsetup, ACF1_PEER_DEVICE}; static struct adm_cmd adjust_cmd = {"adjust", adm_adjust, &adjust_ctx, ACF1_RESNAME .vol_id_optional = 1}; static struct adm_cmd adjust_wp_cmd = {"adjust-with-progress", adm_adjust_wp, ACF1_RESNAME}; static struct adm_cmd wait_c_cmd = {"wait-connect", adm_wait_c, ACF1_WAIT}; static struct adm_cmd wait_sync_cmd = {"wait-sync", adm_wait_c, ACF1_WAIT}; static struct adm_cmd wait_ci_cmd = {"wait-con-int", adm_wait_ci, .show_in_usage = 1,.verify_ips = 1,}; static struct adm_cmd role_cmd = {"role", adm_drbdsetup, ACF1_RESNAME}; static struct adm_cmd cstate_cmd = {"cstate", adm_drbdsetup, ACF1_DISCONNECT}; static struct adm_cmd dstate_cmd = {"dstate", adm_setup_and_meta, ACF1_MINOR_ONLY }; static struct adm_cmd status_cmd = {"status", adm_drbdsetup, .show_in_usage = 1, .uc_dialog = 1, .backend_res_name=1}; static struct adm_cmd peer_device_options_cmd = {"peer-device-options", adm_peer_device, &peer_device_options_ctx, ACF1_PEER_DEVICE}; static struct adm_cmd dump_cmd = {"dump", adm_dump, ACF1_DUMP}; static struct adm_cmd dump_xml_cmd = {"dump-xml", adm_dump_xml, ACF1_DUMP}; static struct adm_cmd create_md_cmd = {"create-md", adm_create_md, &create_md_ctx, ACF1_MINOR_ONLY }; static struct adm_cmd show_gi_cmd = {"show-gi", adm_setup_and_meta, ACF1_PEER_DEVICE .disk_required = 1}; static struct adm_cmd get_gi_cmd = {"get-gi", adm_setup_and_meta, ACF1_PEER_DEVICE .disk_required = 1}; static struct adm_cmd dump_md_cmd = {"dump-md", adm_drbdmeta, ACF1_MINOR_ONLY }; static struct adm_cmd wipe_md_cmd = {"wipe-md", adm_drbdmeta, ACF1_MINOR_ONLY }; static struct adm_cmd apply_al_cmd = {"apply-al", adm_drbdmeta, ACF1_MINOR_ONLY }; static struct adm_cmd forget_peer_cmd = {"forget-peer", adm_forget_peer, ACF1_DISCONNECT }; static struct adm_cmd hidden_cmd = {"hidden-commands", hidden_cmds,.show_in_usage = 1,}; static struct adm_cmd sh_nop_cmd = {"sh-nop", sh_nop, ACF2_GEN_SHELL .uc_dialog = 1, .test_config = 1}; static struct adm_cmd sh_resources_cmd = {"sh-resources", sh_resources, ACF2_GEN_SHELL}; static struct adm_cmd sh_resource_cmd = {"sh-resource", sh_resource, ACF2_SH_RESNAME}; static struct adm_cmd sh_mod_parms_cmd = {"sh-mod-parms", sh_mod_parms, ACF2_GEN_SHELL}; static struct adm_cmd sh_dev_cmd = {"sh-dev", sh_dev, ACF2_SHELL}; static struct adm_cmd sh_udev_cmd = {"sh-udev", sh_udev, .vol_id_required = 1, ACF2_HOOK}; static struct adm_cmd sh_minor_cmd = {"sh-minor", sh_minor, ACF2_SHELL}; static struct adm_cmd sh_ll_dev_cmd = {"sh-ll-dev", sh_ll_dev, ACF2_SHELL .disk_required = 1}; static struct adm_cmd sh_md_dev_cmd = {"sh-md-dev", sh_md_dev, ACF2_SHELL .disk_required = 1}; static struct adm_cmd sh_md_idx_cmd = {"sh-md-idx", sh_md_idx, ACF2_SHELL .disk_required = 1}; static struct adm_cmd sh_ip_cmd = {"sh-ip", sh_ip, ACF2_SHELL}; static struct adm_cmd sh_lr_of_cmd = {"sh-lr-of", sh_lres, ACF2_SHELL}; static struct adm_cmd proxy_up_cmd = {"proxy-up", adm_proxy_up, ACF2_PROXY}; static struct adm_cmd proxy_down_cmd = {"proxy-down", adm_proxy_down, ACF2_PROXY}; /* */ struct adm_cmd new_resource_cmd = {"new-resource", adm_resource, &resource_options_ctx, ACF2_SH_RESNAME}; /* */ struct adm_cmd new_minor_cmd = {"new-minor", adm_new_minor, &device_options_ctx, ACF4_ADVANCED}; /* */ struct adm_cmd del_minor_cmd = {"del-minor", adm_drbdsetup, ACF1_MINOR_ONLY .show_in_usage = 4, .disk_required = 0, }; static struct adm_cmd khelper01_cmd = {"before-resync-target", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd khelper02_cmd = {"after-resync-target", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd khelper03_cmd = {"before-resync-source", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd khelper04_cmd = {"pri-on-incon-degr", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd khelper05_cmd = {"pri-lost-after-sb", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd khelper06_cmd = {"fence-peer", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd khelper07_cmd = {"local-io-error", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd khelper08_cmd = {"pri-lost", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd khelper09_cmd = {"initial-split-brain", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd khelper10_cmd = {"split-brain", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd khelper11_cmd = {"out-of-sync", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd khelper12_cmd = {"unfence-peer", adm_khelper, ACF3_RES_HANDLER}; static struct adm_cmd suspend_io_cmd = {"suspend-io", adm_drbdsetup, ACF4_ADVANCED .backend_res_name = 0 }; static struct adm_cmd resume_io_cmd = {"resume-io", adm_drbdsetup, ACF4_ADVANCED .backend_res_name = 0 }; static struct adm_cmd set_gi_cmd = {"set-gi", adm_drbdmeta, &wildcard_ctx, .disk_required = 1, .need_peer = 1, ACF4_ADVANCED_NEED_VOL}; static struct adm_cmd new_current_uuid_cmd = {"new-current-uuid", adm_drbdsetup, &new_current_uuid_cmd_ctx, ACF4_ADVANCED_NEED_VOL .backend_res_name = 0}; static struct adm_cmd check_resize_cmd = {"check-resize", adm_chk_resize, ACF4_ADVANCED}; struct adm_cmd *cmds[] = { /* name, function, flags * sort order: * - normal config commands, * - normal meta data manipulation * - sh-* * - handler * - advanced ***/ &attach_cmd, &disk_options_cmd, &detach_cmd, &new_peer_cmd, &del_peer_cmd, &new_path_cmd, &del_path_cmd, &connect_cmd, &net_options_cmd, &disconnect_cmd, &up_cmd, &res_options_cmd, &peer_device_options_cmd, &down_cmd, &primary_cmd, &secondary_cmd, &invalidate_cmd, &invalidate_remote_cmd, &outdate_cmd, &resize_cmd, &verify_cmd, &pause_sync_cmd, &resume_sync_cmd, &adjust_cmd, &adjust_wp_cmd, &wait_c_cmd, &wait_sync_cmd, &wait_ci_cmd, &role_cmd, &cstate_cmd, &dstate_cmd, &status_cmd, &dump_cmd, &dump_xml_cmd, &create_md_cmd, &show_gi_cmd, &get_gi_cmd, &dump_md_cmd, &wipe_md_cmd, &apply_al_cmd, &forget_peer_cmd, &hidden_cmd, &sh_nop_cmd, &sh_resources_cmd, &sh_resource_cmd, &sh_mod_parms_cmd, &sh_dev_cmd, &sh_udev_cmd, &sh_minor_cmd, &sh_ll_dev_cmd, &sh_md_dev_cmd, &sh_md_idx_cmd, &sh_ip_cmd, &sh_lr_of_cmd, &proxy_up_cmd, &proxy_down_cmd, &new_resource_cmd, &new_minor_cmd, &del_minor_cmd, &khelper01_cmd, &khelper02_cmd, &khelper03_cmd, &khelper04_cmd, &khelper05_cmd, &khelper06_cmd, &khelper07_cmd, &khelper08_cmd, &khelper09_cmd, &khelper10_cmd, &khelper11_cmd, &khelper12_cmd, &suspend_io_cmd, &resume_io_cmd, &set_gi_cmd, &new_current_uuid_cmd, &check_resize_cmd, }; /* internal commands: */ /* */ struct adm_cmd res_options_defaults_cmd = { "resource-options", adm_resource, &resource_options_ctx, ACF1_RESNAME }; /* */ struct adm_cmd disk_options_defaults_cmd = { "disk-options", adm_attach, &attach_cmd_ctx, ACF1_MINOR_ONLY }; /* */ struct adm_cmd net_options_defaults_cmd = { "net-options", adm_new_peer, &net_options_ctx, ACF1_CONNECT }; /* */ struct adm_cmd peer_device_options_defaults_cmd = { "peer-device-options", adm_peer_device, &peer_device_options_ctx, ACF1_CONNECT }; /* */ struct adm_cmd proxy_conn_down_cmd = { "", do_proxy_conn_down, ACF1_DEFAULT}; /* */ struct adm_cmd proxy_conn_up_cmd = { "", do_proxy_conn_up, ACF1_DEFAULT}; /* */ struct adm_cmd proxy_conn_plugins_cmd = { "", do_proxy_conn_plugins, ACF1_DEFAULT}; static const struct adm_cmd invalidate_setup_cmd = { "invalidate", __adm_drbdsetup_silent, ACF1_MINOR_ONLY }; static const struct adm_cmd forget_peer_setup_cmd = { "forget-peer", __adm_drbdsetup_silent, ACF1_DISCONNECT }; static void initialize_deferred_cmds() { enum drbd_cfg_stage stage; for (stage = CFG_PREREQ; stage < __CFG_LAST; stage++) STAILQ_INIT(&deferred_cmds[stage]); } void schedule_deferred_cmd(struct adm_cmd *cmd, const struct cfg_ctx *ctx, enum drbd_cfg_stage stage) { struct deferred_cmd *d; if (stage & SCHEDULE_ONCE) { stage &= ~SCHEDULE_ONCE; STAILQ_FOREACH(d, &deferred_cmds[stage], link) { if (d->ctx.cmd == cmd && d->ctx.res == ctx->res && d->ctx.conn == ctx->conn && d->ctx.vol == ctx->vol) return; } } d = calloc(1, sizeof(struct deferred_cmd)); if (d == NULL) { perror("calloc"); exit(E_EXEC_ERROR); } d->ctx = *ctx; d->ctx.cmd = cmd; STAILQ_INSERT_TAIL(&deferred_cmds[stage], d, link); } enum on_error { KEEP_RUNNING, EXIT_ON_FAIL }; static int __call_cmd_fn(const struct cfg_ctx *ctx, enum on_error on_error) { struct d_volume *vol = ctx->vol; bool iterate_paths; int rv = 0; iterate_paths = ctx->path ? 0 : ctx->cmd->iterate_paths; if (ctx->cmd->disk_required && (!vol->disk || !vol->meta_disk || !vol->meta_index)) { rv = 10; err("The %s command requires a local disk, but the configuration gives none.\n", ctx->cmd->name); if (on_error == EXIT_ON_FAIL) exit(rv); return rv; } if (iterate_paths) { struct cfg_ctx tmp_ctx = *ctx; struct path *path; for_each_path(path, &tmp_ctx.conn->paths) { tmp_ctx.path = path; rv = tmp_ctx.cmd->function(&tmp_ctx); if (rv >= 20) { if (on_error == EXIT_ON_FAIL) exit(rv); } } } else { rv = ctx->cmd->function(ctx); if (rv >= 20) { if (on_error == EXIT_ON_FAIL) exit(rv); } } return rv; } static int call_cmd_fn(struct adm_cmd *cmd, const struct cfg_ctx *ctx, enum on_error on_error) { struct cfg_ctx tmp_ctx = *ctx; tmp_ctx.cmd = cmd; return __call_cmd_fn(&tmp_ctx, on_error); } /* If ctx->vol is NULL, and cmd->iterate_volumes is set, * iterate over all volumes in ctx->res. * Else, just pass it on. * */ int call_cmd(const struct adm_cmd *cmd, const struct cfg_ctx *ctx, enum on_error on_error) { struct cfg_ctx tmp_ctx = *ctx; struct d_resource *res = ctx->res; struct d_volume *vol; struct connection *conn; bool iterate_vols, iterate_conns; int ret = 0; if (!res->peers_addrs_set && cmd->need_peer) set_peer_in_resource(res, cmd->need_peer); iterate_vols = ctx->vol ? 0 : cmd->iterate_volumes; iterate_conns = ctx->conn ? 0 : cmd->need_peer; tmp_ctx.cmd = cmd; if (iterate_vols && iterate_conns) { for_each_volume(vol, &res->me->volumes) { tmp_ctx.vol = vol; for_each_connection(conn, &res->connections) { if (conn->ignore) continue; tmp_ctx.conn = conn; ret = __call_cmd_fn(&tmp_ctx, on_error); if (ret) goto out; } } } else if (iterate_vols) { for_each_volume(vol, &res->me->volumes) { tmp_ctx.vol = vol; ret = __call_cmd_fn(&tmp_ctx, on_error); if (ret) break; } } else if (iterate_conns) { for_each_connection(conn, &res->connections) { if (conn->ignore) continue; tmp_ctx.conn = conn; ret = __call_cmd_fn(&tmp_ctx, on_error); if (ret) break; } } else { ret = __call_cmd_fn(&tmp_ctx, on_error); } out: return ret; } static char *drbd_cfg_stage_string[] = { [CFG_PREREQ] = "create res", [CFG_RESOURCE] = "adjust res", [CFG_DISK_PREP_DOWN] = "prepare disk", [CFG_DISK_PREP_UP] = "prepare disk", [CFG_DISK] = "adjust disk", [CFG_NET_DISCONNECT] = "prepare net", [CFG_NET_PREP_DOWN] = "prepare net", [CFG_NET_PREP_UP] = "prepare net", [CFG_NET] = "adjust net", [CFG_PEER_DEVICE] = "adjust peer_devices", [CFG_NET_CONNECT] = "attempt to connect", }; int _run_deferred_cmds(enum drbd_cfg_stage stage) { struct d_resource *last_res = NULL; struct deferred_cmd *d = STAILQ_FIRST(&deferred_cmds[stage]); struct deferred_cmd *t; int r; int rv = 0; if (d && adjust_with_progress) { printf("\n%15s:", drbd_cfg_stage_string[stage]); fflush(stdout); } while (d) { if (d->ctx.res->skip_further_deferred_command) { if (adjust_with_progress) { if (d->ctx.res != last_res) printf(" [skipped:%s]", d->ctx.res->name); } else err("%s: %s %s: skipped due to earlier error\n", progname, d->ctx.cmd->name, d->ctx.res->name); r = 0; } else { if (adjust_with_progress) { if (d->ctx.res != last_res) printf(" %s", d->ctx.res->name); } r = __call_cmd_fn(&d->ctx, KEEP_RUNNING); if (r) { /* If something in the "prerequisite" stages failed, * there is no point in trying to continue. * However if we just failed to adjust some * options, or failed to attach, we still want * to adjust other options, or try to connect. */ if (stage == CFG_PREREQ || stage == CFG_DISK_PREP_DOWN || stage == CFG_DISK_PREP_UP || stage == CFG_NET_PREP_DOWN || stage == CFG_NET_PREP_UP) d->ctx.res->skip_further_deferred_command = 1; if (adjust_with_progress) printf(":failed(%s:%u)", d->ctx.cmd->name, r); } } last_res = d->ctx.res; t = STAILQ_NEXT(d, link); free(d); d = t; if (r > rv) rv = r; } return rv; } int run_deferred_cmds(void) { enum drbd_cfg_stage stage; int r; int ret = 0; if (adjust_with_progress) printf("["); for (stage = CFG_PREREQ; stage < __CFG_LAST; stage++) { r = _run_deferred_cmds(stage); if (r) { if (!adjust_with_progress) return 1; /* FIXME r? */ ret = 1; } } if (adjust_with_progress) printf("\n]\n"); return ret; } static int adm_adjust(const struct cfg_ctx *ctx) { static int adjust_flags = 0; struct d_name *opt; if (!adjust_flags) { opt = find_backend_option("--skip-disk"); if (opt) STAILQ_REMOVE(&backend_options, opt, d_name, link); else adjust_flags |= ADJUST_DISK; opt = find_backend_option("--skip-net"); if (opt) STAILQ_REMOVE(&backend_options, opt, d_name, link); else adjust_flags |= ADJUST_NET; adjust_flags |= ADJUST_SKIP_CHECKED; } return _adm_adjust(ctx, adjust_flags); } static int sh_nop(const struct cfg_ctx *ctx) { if (!config_valid) return 10; return 0; } static int sh_resources(const struct cfg_ctx *ctx) { struct d_resource *res; for_each_resource(res, &config) { if (res->ignore) continue; if (is_drbd_top != res->stacked) continue; printf("%s\n", res->name); } return 0; } static int sh_resource(const struct cfg_ctx *ctx) { printf("%s\n", ctx->res->name); return 0; } static int sh_dev(const struct cfg_ctx *ctx) { printf("%s\n", ctx->vol->device); return 0; } static int sh_udev(const struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; struct d_volume *vol = ctx->vol; /* No shell escape necessary. Udev does not handle it anyways... */ if (!vol) { err("volume not specified\n"); return 1; } if (!strncmp(vol->device, "/dev/drbd", 9)) printf("DEVICE=%s\n", vol->device + 5); else printf("DEVICE=drbd%u\n", vol->device_minor); printf("SYMLINK="); if (vol->implicit) printf("drbd/by-res/%s", res->name); else printf("drbd/by-res/%s/%u", res->name, vol->vnr); if (vol->disk) { if (!strncmp(vol->disk, "/dev/", 5)) printf(" drbd/by-disk/%s", vol->disk + 5); else printf(" drbd/by-disk/%s", vol->disk); } printf("\n"); return 0; } static int sh_minor(const struct cfg_ctx *ctx) { printf("%d\n", ctx->vol->device_minor); return 0; } static int sh_ip(const struct cfg_ctx *ctx) { printf("%s\n", ctx->res->me->address.addr); return 0; } static int sh_lres(const struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; if (!is_drbd_top) { err("sh-lower-resource only available in stacked mode\n"); exit(E_USAGE); } if (!res->stacked) { err("'%s' is not stacked on this host (%s)\n", res->name, hostname); exit(E_USAGE); } printf("%s\n", res->me->lower->name); return 0; } static int sh_ll_dev(const struct cfg_ctx *ctx) { printf("%s\n", ctx->vol->disk); return 0; } static int sh_md_dev(const struct cfg_ctx *ctx) { struct d_volume *vol = ctx->vol; char *r; if (strcmp("internal", vol->meta_disk) == 0) r = vol->disk; else r = vol->meta_disk; printf("%s\n", r); return 0; } static int sh_md_idx(const struct cfg_ctx *ctx) { printf("%s\n", ctx->vol->meta_index); return 0; } /* FIXME this module parameter will go */ static int sh_mod_parms(const struct cfg_ctx *ctx) { int mc = global_options.minor_count; if (mc == 0) { mc = number_of_minors + 3; if (mc > DRBD_MINOR_COUNT_MAX) mc = DRBD_MINOR_COUNT_MAX; if (mc < DRBD_MINOR_COUNT_DEF) mc = DRBD_MINOR_COUNT_DEF; } printf("minor_count=%d\n", mc); return 0; } static void free_volume(struct d_volume *vol) { if (!vol) return; free(vol->device); free(vol->disk); free(vol->meta_disk); free(vol->meta_index); free(vol); } static void free_host_info(struct d_host_info *hi) { struct d_volume *vol, *n; if (!hi) return; free_names(&hi->on_hosts); vol = STAILQ_FIRST(&hi->volumes); while (vol) { n = STAILQ_NEXT(vol, link); free_volume(vol); vol = n; } free(hi->address.addr); free(hi->address.af); free(hi->address.port); } static void free_options(struct options *options) { struct d_option *f, *option = STAILQ_FIRST(options); while (option) { free(option->value); f = option; option = STAILQ_NEXT(option, link); free(f); } } static void free_config() { struct d_resource *f, *t; struct d_host_info *host, *th; f = STAILQ_FIRST(&config); while (f) { free(f->name); host = STAILQ_FIRST(&f->all_hosts); while (host) { th = STAILQ_NEXT(host, link); free_host_info(host); host = th; } free_options(&f->net_options); free_options(&f->disk_options); free_options(&f->startup_options); free_options(&f->proxy_options); free_options(&f->handlers); t = STAILQ_NEXT(f, link); free(f); f = t; } if (common) { free_options(&common->net_options); free_options(&common->disk_options); free_options(&common->startup_options); free_options(&common->proxy_options); free_options(&common->handlers); free(common); } free(ifreq_list); } static void find_drbdcmd(char **cmd, char **pathes) { char **path; path = pathes; while (*path) { if (access(*path, X_OK) == 0) { *cmd = *path; return; } path++; } err("Can not find command (drbdsetup/drbdmeta)\n"); exit(E_EXEC_ERROR); } #define NA(ARGC) \ ({ if((ARGC) >= MAX_ARGS) { err("MAX_ARGS too small\n"); \ exit(E_THINKO); \ } \ (ARGC)++; \ }) static bool is_valid_backend_option(const char* name, const struct context_def *context_def) { const struct field_def *field; int len_to_equal_sign_or_nul; if (context_def == &wildcard_ctx) return true; if (!context_def || strlen(name) <= 2) return false; /* options have a leading "--", while field names do not have that */ name += 2; /* compare only until first equal sign, if any */ len_to_equal_sign_or_nul = strcspn(name, "="); for (field = context_def->fields; field->name; field++) { if (!strncmp(name, field->name, len_to_equal_sign_or_nul)) return true; } return false; } static void add_setup_options(char **argv, int *argcp, const struct context_def *context_def) { struct d_name *b_opt; int argc = *argcp; STAILQ_FOREACH(b_opt, &backend_options, link) { if (is_valid_backend_option(b_opt->name, context_def)) argv[NA(argc)] = b_opt->name; } *argcp = argc; } #define make_option(ARG, OPT) do { \ struct d_name *b_opt; \ bool found = false; \ STAILQ_FOREACH(b_opt, &backend_options, link) { \ if (!strncmp(OPT->name, b_opt->name+2, strlen(OPT->name))) { \ found = true; \ break; \ } \ } \ if (!found) { \ if(OPT->value) \ ARG = ssprintf("--%s=%s", OPT->name, OPT->value); \ else \ ARG = ssprintf("--%s", OPT->name); \ } \ } while (0) #define make_options(ARG, OPTIONS) do { \ struct d_option *option; \ STAILQ_FOREACH(option, OPTIONS, link) \ make_option(ARG, option); \ } while (0) #define ssprintf_addr(A) \ ssprintf(strcmp((A)->af, "ipv6") ? "%s:%s:%s" : "%s:[%s]:%s", \ (A)->af, (A)->addr, (A)->port); static int adm_attach(const struct cfg_ctx *ctx) { struct d_volume *vol = ctx->vol; char *argv[MAX_ARGS]; int argc = 0; bool do_attach = (ctx->cmd == &attach_cmd); bool reset = (ctx->cmd == &disk_options_defaults_cmd); if (do_attach) { int rv = call_cmd_fn(&apply_al_cmd, ctx, KEEP_RUNNING); if (rv) return rv; } argv[NA(argc)] = drbdsetup; argv[NA(argc)] = (char *)ctx->cmd->name; /* "attach" : "disk-options"; */ argv[NA(argc)] = ssprintf("%d", vol->device_minor); if (do_attach) { assert(vol->disk != NULL); assert(vol->disk[0] != '\0'); argv[NA(argc)] = vol->disk; if (!strcmp(vol->meta_disk, "internal")) { argv[NA(argc)] = vol->disk; } else { argv[NA(argc)] = vol->meta_disk; } argv[NA(argc)] = vol->meta_index; } if (reset) argv[NA(argc)] = "--set-defaults"; if (reset || do_attach) { if (!do_attach) { struct d_option *option; STAILQ_FOREACH(option, &ctx->vol->disk_options, link) if (!option->adj_skip) make_option(argv[NA(argc)], option); } else { make_options(argv[NA(argc)], &ctx->vol->disk_options); } } add_setup_options(argv, &argc, ctx->cmd->drbdsetup_ctx); argv[NA(argc)] = 0; return m_system_ex(argv, SLEEPS_LONG, ctx->res->name); } struct d_option *find_opt(struct options *base, const char *name) { struct d_option *option; STAILQ_FOREACH(option, base, link) if (!strcmp(option->name, name)) return option; return NULL; } bool del_opt(struct options *base, const char * const name) { struct d_option *opt; if ((opt = find_opt(base, name))) { STAILQ_REMOVE(base, opt, d_option, link); free_opt(opt); return true; } return false; } int adm_new_minor(const struct cfg_ctx *ctx) { char *argv[MAX_ARGS]; int argc = 0, ex; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = "new-minor"; argv[NA(argc)] = ssprintf("%s", ctx->res->name); argv[NA(argc)] = ssprintf("%u", ctx->vol->device_minor); argv[NA(argc)] = ssprintf("%u", ctx->vol->vnr); argv[NA(argc)] = NULL; ex = m_system_ex(argv, SLEEPS_SHORT, ctx->res->name); if (!ex && do_register) register_minor(ctx->vol->device_minor, config_save); return ex; } static int adm_resource(const struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; char *argv[MAX_ARGS]; int argc = 0, ex; bool do_new_resource = (ctx->cmd == &new_resource_cmd); bool reset = (ctx->cmd == &res_options_defaults_cmd); argv[NA(argc)] = drbdsetup; argv[NA(argc)] = (char *)ctx->cmd->name; /* "new-resource" or "resource-options" */ argv[NA(argc)] = ssprintf("%s", res->name); if (do_new_resource) argv[NA(argc)] = ctx->res->me->node_id; if (reset) argv[NA(argc)] = "--set-defaults"; if (reset || do_new_resource) make_options(argv[NA(argc)], &res->res_options); add_setup_options(argv, &argc, ctx->cmd->drbdsetup_ctx); argv[NA(argc)] = NULL; ex = m_system_ex(argv, SLEEPS_SHORT, res->name); if (!ex && do_new_resource && do_register) register_resource(res->name, config_save); return ex; } static off64_t read_drbd_dev_size(int minor) { char *path; FILE *file; off64_t val; int r; m_asprintf(&path, "/sys/block/drbd%d/size", minor); file = fopen(path, "r"); if (file) { r = fscanf(file, "%" SCNd64, &val); fclose(file); if (r != 1) val = -1; } else val = -1; return val; } int adm_resize(const struct cfg_ctx *ctx) { char *argv[MAX_ARGS]; struct d_option *opt; bool is_resize = !strcmp(ctx->cmd->name, "resize"); off64_t old_size = -1; off64_t target_size = 0; off64_t new_size; int argc = 0; int silent; int ex; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = "resize"; argv[NA(argc)] = ssprintf("%d", ctx->vol->device_minor); opt = find_opt(&ctx->vol->disk_options, "size"); if (!opt) opt = find_opt(&ctx->res->disk_options, "size"); if (opt) { argv[NA(argc)] = ssprintf("--%s=%s", opt->name, opt->value); target_size = m_strtoll(opt->value, 's'); /* FIXME: what if "add_setup_options" below overrides target_size * with an explicit, on-command-line target_size? */ } add_setup_options(argv, &argc, ctx->cmd->drbdsetup_ctx); argv[NA(argc)] = 0; if (is_resize && !dry_run) old_size = read_drbd_dev_size(ctx->vol->device_minor); /* if this is not "resize", but "check-resize", be silent! */ silent = !strcmp(ctx->cmd->name, "check-resize") ? SUPRESS_STDERR : 0; ex = m_system_ex(argv, SLEEPS_SHORT | silent, ctx->res->name); if (ex && target_size) { new_size = read_drbd_dev_size(ctx->vol->device_minor); if (new_size == target_size) { fprintf(stderr, "Current size of drbd%u equals target size (%llu byte), exit code %d ignored.\n", ctx->vol->device_minor, (unsigned long long)new_size, ex); ex = 0; } } if (ex) return ex; /* Record last-known bdev info. * Unfortunately drbdsetup did not have enough information * when doing the "resize", and in theory, _our_ information * about the backing device may even be wrong. * Call drbdsetup again, tell it to ask the kernel for * current config, and update the last known bdev info * according to that. */ /* argv[0] = drbdsetup; */ argv[1] = "check-resize"; /* argv[2] = minor; */ argv[3] = NULL; /* ignore exit code */ m_system_ex(argv, SLEEPS_SHORT | silent, ctx->res->name); /* Here comes a really uggly hack. Wait until the device size actually changed, but only up to 10 seconds if know the target size, up to 3 seconds waiting for some change. */ if (old_size > 0) { int timeo = target_size ? 100 : 30; do { new_size = read_drbd_dev_size(ctx->vol->device_minor); if (new_size >= target_size) /* should be == , but driver ignores usize right now */ return 0; if (new_size != old_size) { if (target_size == 0) return 0; err("Size changed from %"PRId64" to %"PRId64", waiting for %"PRId64".\n", old_size, new_size, target_size); old_size = new_size; /* I want to see it only once.*/ } usleep(100000); } while (timeo-- > 0); return 1; } return 0; } int _adm_drbdmeta(const struct cfg_ctx *ctx, int flags, char *argument) { struct d_volume *vol = ctx->vol; char *argv[MAX_ARGS]; int argc = 0; argv[NA(argc)] = drbdmeta; argv[NA(argc)] = ssprintf("%d", vol->device_minor); argv[NA(argc)] = "v09"; if (!strcmp(vol->meta_disk, "internal")) { assert(vol->disk != NULL); assert(vol->disk[0] != '\0'); argv[NA(argc)] = vol->disk; } else { argv[NA(argc)] = vol->meta_disk; } if (!strcmp(vol->meta_index, "flexible")) { if (!strcmp(vol->meta_disk, "internal")) { argv[NA(argc)] = "flex-internal"; } else { argv[NA(argc)] = "flex-external"; } } else { argv[NA(argc)] = vol->meta_index; } if (ctx->cmd->need_peer) argv[NA(argc)] = ssprintf("--node-id=%s", ctx->conn->peer->node_id); argv[NA(argc)] = (char *)ctx->cmd->name; if (argument) argv[NA(argc)] = argument; add_setup_options(argv, &argc, ctx->cmd->drbdsetup_ctx); argv[NA(argc)] = 0; return m_system_ex(argv, flags, ctx->res->name); } static int adm_drbdmeta(const struct cfg_ctx *ctx) { return _adm_drbdmeta(ctx, SLEEPS_VERY_LONG, NULL); } static void __adm_drbdsetup(const struct cfg_ctx *ctx, int flags, pid_t *pid, int *fd, int *ex) { char *argv[MAX_ARGS]; int argc = 0; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = (char *)ctx->cmd->name; if (ctx->cmd->backend_res_name && ctx->res) argv[NA(argc)] = ssprintf("%s", ctx->res->name); if (ctx->cmd->need_peer) argv[NA(argc)] = ssprintf("%s", ctx->conn->peer->node_id); if (ctx->vol) { if (ctx->cmd->need_peer && ctx->cmd->iterate_volumes) argv[NA(argc)] = ssprintf("%d", ctx->vol->vnr); else argv[NA(argc)] = ssprintf("%d", ctx->vol->device_minor); } add_setup_options(argv, &argc, ctx->cmd->drbdsetup_ctx); if (ctx->cmd == &invalidate_setup_cmd && ctx->conn) argv[NA(argc)] = ssprintf("--sync-from-peer-node-id=%s", ctx->conn->peer->node_id); argv[NA(argc)] = 0; if (ctx->res) setenv("DRBD_RESOURCE", ctx->res->name, 1); m__system(argv, flags, ctx->res ? ctx->res->name : NULL, pid, fd, ex); } static int _adm_drbdsetup(const struct cfg_ctx *ctx, int flags) { int ex; __adm_drbdsetup(ctx, flags, NULL, NULL, &ex); return ex; } static int adm_drbdsetup(const struct cfg_ctx *ctx) { return _adm_drbdsetup(ctx, ctx->cmd->takes_long ? SLEEPS_LONG : SLEEPS_SHORT); } static int __adm_drbdsetup_silent(const struct cfg_ctx *ctx) { char buffer[4096]; int fd, status, rv = 0; pid_t pid; ssize_t rr; ssize_t rw __attribute((unused)); size_t s = 0; __adm_drbdsetup(ctx, SLEEPS_SHORT | RETURN_STDERR_FD, &pid, &fd, NULL); if (!dry_run) { if (fd < 0) { err("Strange: got negative fd.\n"); exit(E_THINKO); } while (1) { rr = read(fd, buffer + s, 4096 - s); if (rr <= 0) break; s += rr; } close(fd); (void) waitpid(pid, &status, 0); alarm(0); if (WIFEXITED(status)) rv = WEXITSTATUS(status); if (alarm_raised) { rv = 0x100; } } /* see drbdsetup.c, print_config_error(): * 11: some unspecific state change error. (ignore for invalidate) * 17: SS_NO_UP_TO_DATE_DISK */ if ((strcmp(ctx->cmd->name, "invalidate") && rv == 11) || rv == 17) rw = write(fileno(stderr), buffer, s); return rv; } static int adm_outdate(const struct cfg_ctx *ctx) { int rv; rv = _adm_drbdsetup(ctx, SLEEPS_SHORT | SUPRESS_STDERR); /* special cases for outdate: * 17: drbdsetup outdate, but is primary and thus cannot be outdated. * 5: drbdsetup outdate, and is inconsistent or worse anyways. */ if (rv == 17) return rv; if (rv == 5) { /* That might mean it is diskless. */ rv = adm_drbdmeta(ctx); if (rv) rv = 5; return rv; } if (rv || dry_run) { rv = adm_drbdmeta(ctx); } return rv; } /* shell equivalent: * ( drbdsetup resize && drbdsetup check-resize ) || drbdmeta check-resize */ static int adm_chk_resize(const struct cfg_ctx *ctx) { /* drbdsetup resize && drbdsetup check-resize */ int ex = adm_resize(ctx); if (ex == 0) return 0; /* try drbdmeta check-resize */ return adm_drbdmeta(ctx); } static int adm_setup_and_meta(const struct cfg_ctx *ctx) { int rv; rv = __adm_drbdsetup_silent(ctx); if (rv == 11 || rv == 17) { /* see drbdsetup.c, print_config_error(): * 11: some unspecific state change error. (ignore for invalidate) * 17: SS_NO_UP_TO_DATE_DISK */ return rv; } if (rv || dry_run) rv = adm_drbdmeta(ctx); return rv; } static int adm_invalidate(const struct cfg_ctx *ctx) { static const struct adm_cmd invalidate_meta_cmd = { "invalidate", adm_drbdmeta, ACF1_MINOR_ONLY }; int rv; rv = call_cmd(&invalidate_setup_cmd, ctx, KEEP_RUNNING); if (rv == 11 || rv == 17) { /* see drbdsetup.c, print_config_error(): * 11: some unspecific state change error. * Means that there are multiple peers * 17: SS_NO_UP_TO_DATE_DISK */ return rv; } if (rv || dry_run == 1) rv = call_cmd(&invalidate_meta_cmd, ctx, KEEP_RUNNING); return rv; } static int adm_forget_peer(const struct cfg_ctx *ctx) { static const struct adm_cmd forget_peer_meta_cmd = { "forget-peer", adm_drbdmeta, ACF1_PEER_DEVICE .disk_required = 1 }; int rv; rv = call_cmd(&forget_peer_setup_cmd, ctx, KEEP_RUNNING); if (rv == 11 || rv == 17) return rv; if (rv || dry_run == 1) rv = call_cmd(&forget_peer_meta_cmd, ctx, KEEP_RUNNING); return rv; } static int adm_khelper(const struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; struct d_volume *vol = ctx->vol; int rv = 0; char *sh_cmd; char minor_string[8]; char volume_string[8]; char *argv[] = { "/bin/sh", "-c", NULL, NULL }; setenv("DRBD_CONF", config_save, 1); setenv("DRBD_RESOURCE", res->name, 1); if (vol) { snprintf(minor_string, sizeof(minor_string), "%u", vol->device_minor); snprintf(volume_string, sizeof(volume_string), "%u", vol->vnr); setenv("DRBD_MINOR", minor_string, 1); setenv("DRBD_VOLUME", volume_string, 1); setenv("DRBD_LL_DISK", shell_escape(vol->disk ?: "none"), 1); } else { char *minor_list; char *volume_list; char *ll_list; char *separator = ""; char *pos_minor; char *pos_volume; char *pos_ll; int volumes = 0; int minor_len, volume_len, ll_len = 0; int n; for_each_volume(vol, &res->me->volumes) { volumes++; ll_len += strlen(shell_escape(vol->disk ?: "none")) + 1; } /* max minor number is 2**20 - 1, which is 7 decimal digits. * plus separator respective trailing zero. */ minor_len = volumes * 8 + 1; volume_len = minor_len; minor_list = alloca(minor_len); volume_list = alloca(volume_len); ll_list = alloca(ll_len); pos_minor = minor_list; pos_volume = volume_list; pos_ll = ll_list; for_each_volume(vol, &res->me->volumes) { #define append(name, fmt, v) do { \ n = snprintf(pos_ ## name, name ## _len, "%s" fmt, \ separator, v); \ if (n >= name ## _len) { \ /* "can not happen" */ \ err("buffer too small when generating the " \ #name " list\n"); \ abort(); \ break; \ } \ name ## _len -= n; \ pos_ ## name += n; \ } while (0) append(minor, "%d", vol->device_minor); append(volume, "%d", vol->vnr); append(ll, "%s", shell_escape(vol->disk ?: "none")); #undef append separator = " "; } setenv("DRBD_MINOR", minor_list, 1); setenv("DRBD_VOLUME", volume_list, 1); setenv("DRBD_LL_DISK", ll_list, 1); } if ((sh_cmd = get_opt_val(&res->handlers, ctx->cmd->name, NULL))) { argv[2] = sh_cmd; rv = m_system_ex(argv, SLEEPS_VERY_LONG, res->name); } return rv; } int adm_peer_device(const struct cfg_ctx *ctx) { bool reset = (ctx->cmd == &peer_device_options_defaults_cmd); struct d_resource *res = ctx->res; struct connection *conn = ctx->conn; struct d_volume *vol = ctx->vol; struct peer_device *peer_device; char *argv[MAX_ARGS]; int argc = 0; peer_device = find_peer_device(res->me, conn, vol->vnr); if (!peer_device) { err("Could not find peer_device object!\n"); exit(E_THINKO); } argv[NA(argc)] = drbdsetup; argv[NA(argc)] = (char *)ctx->cmd->name; /* peer-device-options */ argv[NA(argc)] = ssprintf("%s", res->name); argv[NA(argc)] = ssprintf("%s", conn->peer->node_id); argv[NA(argc)] = ssprintf("%d", vol->vnr); if (reset) argv[NA(argc)] = "--set-defaults"; make_options(argv[NA(argc)], &peer_device->pd_options); add_setup_options(argv, &argc, ctx->cmd->drbdsetup_ctx); argv[NA(argc)] = 0; return m_system_ex(argv, SLEEPS_SHORT, res->name); } static int adm_connect(const struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; struct connection *conn = ctx->conn; char *argv[MAX_ARGS]; int argc = 0; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = (char *)ctx->cmd->name; /* "connect" */ argv[NA(argc)] = ssprintf("%s", res->name); argv[NA(argc)] = ssprintf("%s", conn->peer->node_id); add_setup_options(argv, &argc, ctx->cmd->drbdsetup_ctx); argv[NA(argc)] = 0; return m_system_ex(argv, SLEEPS_SHORT, res->name); } static int adm_new_peer(const struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; struct connection *conn = ctx->conn; char *argv[MAX_ARGS]; int argc = 0; bool reset = (ctx->cmd == &net_options_defaults_cmd); argv[NA(argc)] = drbdsetup; argv[NA(argc)] = (char *)ctx->cmd->name; /* "new-peer", "net-options" */ argv[NA(argc)] = ssprintf("%s", res->name); argv[NA(argc)] = ssprintf("%s", conn->peer->node_id); if (reset) argv[NA(argc)] = "--set-defaults"; if (!strncmp(ctx->cmd->name, "net-options", 11)) del_opt(&conn->net_options, "transport"); make_options(argv[NA(argc)], &conn->net_options); add_setup_options(argv, &argc, ctx->cmd->drbdsetup_ctx); argv[NA(argc)] = 0; return m_system_ex(argv, SLEEPS_SHORT, res->name); } static int adm_path(const struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; struct connection *conn = ctx->conn; struct path *path = ctx->path; char *argv[MAX_ARGS]; int argc = 0; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = (char *)ctx->cmd->name; /* add-path, del-path */ argv[NA(argc)] = ssprintf("%s", res->name); argv[NA(argc)] = ssprintf("%s", conn->peer->node_id); argv[NA(argc)] = ssprintf_addr(path->my_address); argv[NA(argc)] = ssprintf_addr(path->connect_to); add_setup_options(argv, &argc, ctx->cmd->drbdsetup_ctx); argv[NA(argc)] = 0; return m_system_ex(argv, SLEEPS_SHORT, res->name); } void free_opt(struct d_option *item) { free(item->value); free(item); } int _proxy_connect_name_len(const struct d_resource *res, const struct connection *conn) { struct path *path = STAILQ_FIRST(&conn->paths); /* multiple paths via proxy, later! */ return (conn->name ? strlen(conn->name) : strlen(res->name)) + strlen(names_to_str_c(&path->peer_proxy->on_hosts, '_')) + strlen(names_to_str_c(&path->my_proxy->on_hosts, '_')) + 3 /* for the two dashes and the trailing 0 character */; } char *_proxy_connection_name(char *conn_name, const struct d_resource *res, const struct connection *conn) { struct path *path = STAILQ_FIRST(&conn->paths); /* multiple paths via proxy, later! */ sprintf(conn_name, "%s-%s-%s", conn->name ?: res->name, names_to_str_c(&path->peer_proxy->on_hosts, '_'), names_to_str_c(&path->my_proxy->on_hosts, '_')); return conn_name; } int do_proxy_conn_up(const struct cfg_ctx *ctx) { char *argv[4] = { drbd_proxy_ctl, "-c", NULL, NULL }; struct connection *conn; char *conn_name; int rv; rv = 0; for_each_connection(conn, &ctx->res->connections) { struct path *path = STAILQ_FIRST(&conn->paths); /* multiple paths via proxy, later! */ conn_name = proxy_connection_name(ctx->res, conn); argv[2] = ssprintf( "add connection %s %s:%s %s:%s %s:%s %s:%s", conn_name, path->my_proxy->inside.addr, path->my_proxy->inside.port, path->peer_proxy->outside.addr, path->peer_proxy->outside.port, path->my_proxy->outside.addr, path->my_proxy->outside.port, path->my_address->addr, path->my_address->port); rv = m_system_ex(argv, SLEEPS_SHORT, ctx->res->name); if (rv) break; } return rv; } int do_proxy_conn_plugins(const struct cfg_ctx *ctx) { struct connection *conn; char *argv[MAX_ARGS]; char *conn_name; int argc = 0; struct d_option *opt; int counter; int rv; rv = 0; for_each_connection(conn, &ctx->res->connections) { struct path *path = STAILQ_FIRST(&conn->paths); /* multiple paths via proxy, later! */ conn_name = proxy_connection_name(ctx->res, conn); argc = 0; argv[NA(argc)] = drbd_proxy_ctl; STAILQ_FOREACH(opt, &path->my_proxy->options, link) { argv[NA(argc)] = "-c"; argv[NA(argc)] = ssprintf("set %s %s %s", opt->name, conn_name, opt->value); } counter = 0; /* Don't send the "set plugin ... END" line if no plugins are defined * - that's incompatible with the drbd proxy version 1. */ if (!STAILQ_EMPTY(&path->my_proxy->plugins)) { STAILQ_FOREACH(opt, &path->my_proxy->plugins, link) { argv[NA(argc)] = "-c"; argv[NA(argc)] = ssprintf("set plugin %s %d %s", conn_name, counter, opt->name); counter++; } argv[NA(argc)] = ssprintf("set plugin %s %d END", conn_name, counter); } argv[NA(argc)] = 0; if (argc > 2) rv = m_system_ex(argv, SLEEPS_SHORT, ctx->res->name); if (rv) break; } return rv; } int do_proxy_conn_down(const struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; struct connection *conn; char *conn_name; char *argv[4] = { drbd_proxy_ctl, "-c", NULL, NULL}; int rv; rv = 0; for_each_connection(conn, &res->connections) { conn_name = proxy_connection_name(ctx->res, conn); argv[2] = ssprintf("del connection %s", conn_name); rv = m_system_ex(argv, SLEEPS_SHORT, res->name); if (rv) break; } return rv; } static int check_proxy(const struct cfg_ctx *ctx, int do_up) { struct connection *conn = ctx->conn; struct path *path = STAILQ_FIRST(&conn->paths); /* multiple paths via proxy, later! */ int rv; if (!path->my_proxy) { if (all_resources) return 0; err("%s:%d: In resource '%s',no proxy config for connection %sfrom '%s' to '%s'%s.\n", ctx->res->config_file, conn->config_line, ctx->res->name, conn->name ? ssprintf("'%s' (", conn->name) : "", hostname, names_to_str(&conn->peer->on_hosts), conn->name ? ")" : ""); exit(E_CONFIG_INVALID); } if (!hostname_in_list(hostname, &path->my_proxy->on_hosts)) { if (all_resources) return 0; err("The proxy config in resource %s is not for %s.\n", ctx->res->name, hostname); exit(E_CONFIG_INVALID); } if (!path->peer_proxy) { err("There is no proxy config for the peer in resource %s.\n", ctx->res->name); if (all_resources) return 0; exit(E_CONFIG_INVALID); } if (do_up) { rv = do_proxy_conn_up(ctx); if (!rv) rv = do_proxy_conn_plugins(ctx); } else rv = do_proxy_conn_down(ctx); return rv; } static int adm_proxy_up(const struct cfg_ctx *ctx) { return check_proxy(ctx, 1); } static int adm_proxy_down(const struct cfg_ctx *ctx) { return check_proxy(ctx, 0); } /* The "main" loop iterates over resources. * This "sorts" the drbdsetup commands to bring those up * so we will later first create all objects, * then attach all local disks, * adjust various settings, * and then configure the network part */ static int adm_up(const struct cfg_ctx *ctx) { struct cfg_ctx tmp_ctx = *ctx; struct connection *conn; struct d_volume *vol; schedule_deferred_cmd(&new_resource_cmd, ctx, CFG_PREREQ); set_peer_in_resource(ctx->res, true); for_each_connection(conn, &ctx->res->connections) { struct peer_device *peer_device; if (conn->ignore) continue; tmp_ctx.conn = conn; schedule_deferred_cmd(&new_peer_cmd, &tmp_ctx, CFG_NET_PREP_UP); schedule_deferred_cmd(&new_path_cmd, &tmp_ctx, CFG_NET_PATH); schedule_deferred_cmd(&connect_cmd, &tmp_ctx, CFG_NET_CONNECT); STAILQ_FOREACH(peer_device, &conn->peer_devices, connection_link) { struct cfg_ctx tmp2_ctx; if (STAILQ_EMPTY(&peer_device->pd_options)) continue; tmp2_ctx = tmp_ctx; tmp2_ctx.vol = peer_device->volume; schedule_deferred_cmd(&peer_device_options_cmd, &tmp2_ctx, CFG_PEER_DEVICE); } } tmp_ctx.conn = NULL; for_each_volume(vol, &ctx->res->me->volumes) { tmp_ctx.vol = vol; schedule_deferred_cmd(&new_minor_cmd, &tmp_ctx, CFG_DISK_PREP_UP); if (vol->disk) schedule_deferred_cmd(&attach_cmd, &tmp_ctx, CFG_DISK); } return 0; } /* The stacked-timeouts switch in the startup sections allows us to enforce the use of the specified timeouts instead the use of a sane value. Should only be used if the third node should never become primary. */ static int adm_wait_c(const struct cfg_ctx *ctx) { struct d_resource *res = ctx->res; struct d_volume *vol = ctx->vol; char *argv[MAX_ARGS]; int argc = 0, rv; argv[NA(argc)] = drbdsetup; if (ctx->vol && ctx->conn) { argv[NA(argc)] = ssprintf("%s-%s", ctx->cmd->name, "volume"); argv[NA(argc)] = res->name; argv[NA(argc)] = ssprintf("%s", ctx->conn->peer->node_id); argv[NA(argc)] = ssprintf("%d", vol->vnr); } else if (ctx->conn) { argv[NA(argc)] = ssprintf("%s-%s", ctx->cmd->name, "connection"); argv[NA(argc)] = res->name; argv[NA(argc)] = ssprintf("%s", ctx->conn->peer->node_id); } else { argv[NA(argc)] = ssprintf("%s-%s", ctx->cmd->name, "resource"); argv[NA(argc)] = res->name; } if (is_drbd_top && !res->stacked_timeouts) { struct d_option *opt; unsigned long timeout = 20; if ((opt = find_opt(&res->net_options, "connect-int"))) { timeout = strtoul(opt->value, NULL, 10); // one connect-interval? two? timeout *= 2; } argv[argc++] = "--wfc-timeout"; argv[argc] = ssprintf("%lu", timeout); argc++; } else make_options(argv[NA(argc)], &res->startup_options); argv[NA(argc)] = 0; rv = m_system_ex(argv, SLEEPS_FOREVER, res->name); return rv; } int ctx_by_minor(struct cfg_ctx *ctx, const char *id) { struct d_resource *res; struct d_volume *vol; unsigned int mm; mm = minor_by_id(id); if (mm == -1U) return -ENOENT; for_each_resource(res, &config) { if (res->ignore) continue; for_each_volume(vol, &res->me->volumes) { if (mm == vol->device_minor) { is_drbd_top = res->stacked; ctx->res = res; ctx->vol = vol; return 0; } } } return -ENOENT; } struct d_volume *volume_by_vnr(struct volumes *volumes, int vnr) { struct d_volume *vol; for_each_volume(vol, volumes) if (vnr == vol->vnr) return vol; return NULL; } /* if there is something to check: * return true if check succeeds, otherwise false */ static bool set_ignore_flag(struct connection * const conn, checks check, bool ignore) { if (ignore == false) { if (check == WOULD_ENABLE_DISABLED && conn->ignore_tmp) return false; else if (check == WOULD_ENABLE_MULTI_TIMES && !conn->ignore_tmp) return false; if (check == WOULD_ENABLE_MULTI_TIMES) conn->ignore_tmp = ignore; } if (check == SETUP_MULTI) conn->ignore = ignore; return true; } int ctx_by_name(struct cfg_ctx *ctx, const char *id, checks check) { struct d_resource *res; struct d_volume *vol; struct connection *conn; char *input = strdupa(id); char *vol_id; char *res_name, *conn_or_hostname; unsigned vol_nr = ~0U; res_name = input; vol_id = strrchr(input, '/'); if (vol_id) { *vol_id++ = '\0'; vol_nr = m_strtoll(vol_id, 0); } conn_or_hostname = strchr(input, ':'); if (conn_or_hostname) *conn_or_hostname++ = '\0'; res = res_by_name(res_name); if (!res || res->ignore) return -ENOENT; ctx->res = res; set_peer_in_resource(res, 1); /* resource name only (e.g., r0) and in check state * this would enable all connectionst that are not ignored */ if (!conn_or_hostname && check == WOULD_ENABLE_MULTI_TIMES) for_each_connection(conn, &res->connections) if (!conn->ignore) { if (!conn->ignore_tmp) return 1; else conn->ignore_tmp = false; } if (conn_or_hostname) { /* per se we do not know if the part after ':' is a host or a connection name */ struct d_host_info *hi; bool valid_conns = false; ctx->conn = NULL; hi = find_host_info_by_name(res, conn_or_hostname); for_each_connection(conn, &res->connections) { if (hi) { /* it was host name */ if (res->me == hi) { err("Host name '%s' (given with --peer option) is not a " "peer, but the local node\n", conn_or_hostname); return -ENOENT; } if (conn->peer == hi && check == CTX_FIRST) goto found; if (conn->peer && !strcmp(conn->peer->node_id, hi->node_id)) { if (!set_ignore_flag(conn, check, false)) return 1; } else /* a connection that should be ignored */ set_ignore_flag(conn, check, true); } else { /* it was a connection name */ struct d_option *opt; opt = find_opt(&conn->net_options, "_name"); if (opt && !strcmp(opt->value, conn_or_hostname)) { if (check == CTX_FIRST) goto found; if (!set_ignore_flag(conn, check, false)) return 1; } else { /* a connection that should be ignored */ set_ignore_flag(conn, check, true); } } if (!conn->ignore) valid_conns = true; } if (check == SETUP_MULTI && !valid_conns) { err("Not a valid connection (%s) for this host\n", id); return -ENOENT; } } if (check != SETUP_MULTI) return 0; if (0) { found: printf("found it\n"); if (conn->ignore) { err("Connection '%s' has the ignore flag set\n", conn_or_hostname); return -ENOENT; } ctx->conn = conn; } if (!vol_id) { /* We could assign implicit volumes here. * But that broke "drbdadm up specific-resource". */ ctx->vol = NULL; return 0; } vol = volume_by_vnr(&res->me->volumes, vol_nr); if (vol_nr != ~0U) { if (vol) { ctx->vol = vol; return 0; } else { err("Resource '%s' has no volume %d\n", res_name, vol_nr); return -ENOENT; } } return -ENOENT; } /* In case a child exited, or exits, its return code is stored as negative number in the pids[i] array */ static int childs_running(pid_t * pids, int opts) { int i = 0, wr, rv = 0, status; int N = nr_volumes[is_drbd_top ? STACKED : NORMAL]; for (i = 0; i < N; i++) { if (pids[i] <= 0) continue; wr = waitpid(pids[i], &status, opts); if (wr == -1) { // Wait error. if (errno == ECHILD) { printf("No exit code for %d\n", pids[i]); pids[i] = 0; // Child exited before ? continue; } perror("waitpid"); exit(E_EXEC_ERROR); } if (wr == 0) rv = 1; // Child still running. if (wr > 0) { pids[i] = 0; if (WIFEXITED(status)) pids[i] = -WEXITSTATUS(status); if (WIFSIGNALED(status)) pids[i] = -1000; } } return rv; } static void kill_childs(pid_t * pids) { int i; int N = nr_volumes[is_drbd_top ? STACKED : NORMAL]; for (i = 0; i < N; i++) { if (pids[i] <= 0) continue; kill(pids[i], SIGINT); } } /* returns: -1 ... all childs terminated 0 ... timeout expired 1 ... a string was read */ int gets_timeout(pid_t * pids, char *s, int size, int timeout) { int pr, rr, n = 0; struct pollfd pfd; if (s) { pfd.fd = fileno(stdin); pfd.events = POLLIN | POLLHUP | POLLERR | POLLNVAL; n = 1; } redo_without_fd: if (!childs_running(pids, WNOHANG)) { pr = -1; goto out; } do { pr = poll(&pfd, n, timeout); if (pr == -1) { // Poll error. if (errno == EINTR) { if (childs_running(pids, WNOHANG)) continue; goto out; // pr = -1 here. } perror("poll"); exit(E_EXEC_ERROR); } } while (pr == -1); if (pr == 1 && s) { // Input available and s not NULL. /* TODO: what should happen if s == NULL? is this correct? * at least we check here and do not nullptr deref */ rr = read(fileno(stdin), s, size - 1); if (rr == -1) { perror("read"); exit(E_EXEC_ERROR); } else if (size > 1 && rr == 0) { /* WTF. End-of-file... avoid busy loop. */ s[0] = 0; n = 0; goto redo_without_fd; } s[rr] = 0; } out: return pr; } static char *get_opt_val(struct options *base, const char *name, char *def) { struct d_option *option; option = find_opt(base, name); return option ? option->value : def; } static int check_exit_codes(pid_t * pids) { struct d_resource *res; int i = 0, rv = 0; for_each_resource(res, &config) { if (res->ignore) continue; if (is_drbd_top != res->stacked) continue; if (pids[i] == -5 || pids[i] == -1000) { pids[i] = 0; } if (pids[i] == -20) rv = 20; i++; } return rv; } static int adm_wait_ci(const struct cfg_ctx *ctx) { struct d_resource *res; char *argv[20], answer[40]; pid_t *pids; int rr, wtime, argc, i = 0; time_t start; int saved_stdin, saved_stdout, fd; int N; struct sigaction so, sa; int have_tty = 1; saved_stdin = -1; saved_stdout = -1; if (no_tty) { err("WARN: stdin/stdout is not a TTY; using /dev/console"); fprintf(stdout, "WARN: stdin/stdout is not a TTY; using /dev/console"); saved_stdin = dup(fileno(stdin)); if (saved_stdin == -1) perror("dup(stdin)"); saved_stdout = dup(fileno(stdout)); if (saved_stdin == -1) perror("dup(stdout)"); fd = open("/dev/console", O_RDONLY); if (fd == -1) { perror("open('/dev/console, O_RDONLY)"); have_tty = 0; } else { dup2(fd, fileno(stdin)); fd = open("/dev/console", O_WRONLY); if (fd == -1) perror("open('/dev/console, O_WRONLY)"); dup2(fd, fileno(stdout)); } } sa.sa_handler = chld_sig_hand; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDSTOP; sigaction(SIGCHLD, &sa, &so); N = nr_volumes[is_drbd_top ? STACKED : NORMAL]; pids = alloca(N * sizeof(pid_t)); /* alloca can not fail, it can "only" overflow the stack :) * but it needs to be initialized anyways! */ memset(pids, 0, N * sizeof(pid_t)); for_each_resource(res, &config) { if (res->ignore) continue; if (is_drbd_top != res->stacked) continue; /* ctx is not used */ argc = 0; argv[NA(argc)] = drbdsetup; argv[NA(argc)] = "wait-connect-resource"; argv[NA(argc)] = res->name; make_options(argv[NA(argc)], &res->startup_options); argv[NA(argc)] = 0; m__system(argv, RETURN_PID, res->name, &pids[i++], NULL, NULL); } wtime = global_options.dialog_refresh ? : -1; start = time(0); for (i = 0; i < 10; i++) { // no string, but timeout rr = gets_timeout(pids, 0, 0, 1 * 1000); if (rr < 0) break; putchar('.'); fflush(stdout); check_exit_codes(pids); } if (rr == 0) { /* track a "yes", as well as ctrl-d and ctrl-c, * in case our tty is stuck in "raw" mode, and * we get it one character a time (-icanon) */ char yes_string[] = "yes\n"; char *yes_expect = yes_string; int ctrl_c_count = 0; int ctrl_d_count = 0; /* Just in case, if plymouth or usplash is running, * tell them to step aside. * Also try to force canonical tty mode. */ printf ("\n***************************************************************\n" " DRBD's startup script waits for the peer node(s) to appear.\n" " - If this node was already a degraded cluster before the\n" " reboot, the timeout is %s seconds. [degr-wfc-timeout]\n" " - If the peer was available before the reboot, the timeout\n" " is %s seconds. [wfc-timeout]\n" " (These values are for resource '%s'; 0 sec -> wait forever)\n", get_opt_val(&STAILQ_FIRST(&config)->startup_options, "degr-wfc-timeout", "0"), get_opt_val(&STAILQ_FIRST(&config)->startup_options, "wfc-timeout", "0"), STAILQ_FIRST(&config)->name); if (!have_tty) { printf(" To abort waiting for DRBD connections, kill this process: kill %u\n", getpid()); fflush(stdout); /* wait untill killed, or all drbdsetup children have finished. */ do { rr = poll(NULL, 0, -1); if (rr == -1) { if (errno == EINTR) { if (childs_running(pids, WNOHANG)) continue; break; } perror("poll"); exit(E_EXEC_ERROR); } } while (rr != -1); kill_childs(pids); childs_running(pids, 0); check_exit_codes(pids); return 0; } if (system("exec > /dev/null 2>&1; plymouth quit ; usplash_write QUIT ; " "stty echo icanon icrnl")) /* Ignore return value. Cannot do anything about it anyways. */; printf(" To abort waiting enter 'yes' [ -- ]: "); do { printf("\e[s\e[31G[%4d]:\e[u", (int)(time(0) - start)); // Redraw sec. fflush(stdout); rr = gets_timeout(pids, answer, 40, wtime * 1000); check_exit_codes(pids); if (rr != 1) continue; /* If our tty is in "sane" or "canonical" mode, * we get whole lines. * If it still is in "raw" mode, even though we * tried to set ICANON above, possibly some other * "boot splash thingy" is in operation. * We may be lucky to get single characters. * If a sysadmin sees things stuck during boot, * I expect that ctrl-c or ctrl-d will be one * of the first things that are tried. * In raw mode, we get these characters directly. * But I want them to try that three times ;) */ if (answer[0] && answer[1] == 0) { if (answer[0] == '\3') ++ctrl_c_count; if (answer[0] == '\4') ++ctrl_d_count; if (yes_expect && answer[0] == *yes_expect) ++yes_expect; else if (answer[0] == '\n') yes_expect = yes_string; else yes_expect = NULL; } if (!strcmp(answer, "yes\n") || (yes_expect && *yes_expect == '\0') || ctrl_c_count >= 3 || ctrl_d_count >= 3) { kill_childs(pids); childs_running(pids, 0); check_exit_codes(pids); break; } printf(" To abort waiting enter 'yes' [ -- ]:"); } while (rr != -1); printf("\n"); } if (saved_stdin != -1) { dup2(saved_stdin, fileno(stdin)); dup2(saved_stdout, fileno(stdout)); } return 0; } static int adm_cmd_cmp(const void *a, const void *b) { return strcmp((*(struct adm_cmd **)a)->name, (*(struct adm_cmd **)b)->name); } static void print_cmds(int level) { const struct adm_cmd **cmds2; int i, j, half; cmds2 = alloca(ARRAY_SIZE(cmds) * sizeof(struct adm_cmd)); for (i = 0, j = 0; i < ARRAY_SIZE(cmds); i++) { if (cmds[i]->show_in_usage != level) continue; cmds2[j++] = cmds[i]; } qsort(cmds2, j, sizeof(struct adm_cmd *), adm_cmd_cmp); half = (j + 1) / 2; for (i = 0; i < half; i++) { if (i + half < j) printf(" %-35s %-35s\n", cmds2[i]->name, cmds2[i + half]->name); else printf(" %-35s\n", cmds2[i]->name); } } static int hidden_cmds(const struct cfg_ctx *ignored __attribute((unused))) { printf("\nThese additional commands might be useful for writing\n" "nifty shell scripts around drbdadm:\n\n"); print_cmds(2); printf("\nThese commands are used by the kernel part of DRBD to\n" "invoke user mode helper programs:\n\n"); print_cmds(3); printf ("\nThese commands ought to be used by experts and developers:\n\n"); print_cmds(4); printf("\n"); exit(0); } static void field_to_option(const struct field_def *field, struct option *option) { option->name = field->name; option->has_arg = field->argument_is_optional ? optional_argument : required_argument; option->flag = NULL; option->val = 257; } static void print_option(struct option *opt) { if (opt->has_arg == required_argument) { printf(" --%s=...", opt->name); if (opt->val > 1 && opt->val < 256) printf(", -%c ...", opt->val); printf("\n"); } else if (opt->has_arg == optional_argument) { printf(" --%s[=...]", opt->name); if (opt->val > 1 && opt->val < 256) printf(", -%c...", opt->val); printf("\n"); } else { printf(" --%s", opt->name); if (opt->val > 1 && opt->val < 256) printf(", -%c", opt->val); printf("\n"); } } void print_usage_and_exit(struct adm_cmd *cmd, const char *addinfo, int status) { struct option *opt; printf("\nUSAGE: %s %s [OPTION...] {all|RESOURCE...}\n\n" "GENERAL OPTIONS:\n", progname, cmd ? cmd->name : "COMMAND"); for (opt = general_admopt; opt->name; opt++) print_option(opt); if (cmd && cmd->drbdsetup_ctx) { const struct field_def *field; printf("\nOPTIONS FOR %s:\n", cmd->name); for (field = cmd->drbdsetup_ctx->fields; field->name; field++) { struct option opt; field_to_option(field, &opt); print_option(&opt); } } if (!cmd) { printf("\nCOMMANDS:\n"); print_cmds(1); } printf("\nVersion: " PACKAGE_VERSION " (api:%d)\n%s\n", API_VERSION, drbd_buildtag()); if (addinfo) printf("\n%s\n", addinfo); exit(status); } void verify_ips(struct d_resource *res) { if (global_options.disable_ip_verification) return; if (dry_run == 1 || do_verify_ips == 0) return; if (res->ignore) return; if (res->stacked && !is_drbd_top) return; if (!res->me->address.addr) return; if (!have_ip(res->me->address.af, res->me->address.addr)) { ENTRY *e, *ep, *f; e = calloc(1, sizeof *e); if (!e) { err("calloc: %m\n"); exit(E_EXEC_ERROR); } m_asprintf(&e->key, "%s:%s", res->me->address.addr, res->me->address.port); f = tfind(e, &global_btree, btree_key_cmp); free(e); if (f) ep = *(ENTRY **)f; err("%s: in resource %s, on %s:\n\t""IP %s not found on this host.\n", f ? (char *)ep->data : res->config_file, res->name, names_to_str(&res->me->on_hosts), res->me->address.addr); if (INVALID_IP_IS_INVALID_CONF) config_valid = 0; } } static char *conf_file[] = { DRBD_CONFIG_DIR "/drbd-90.conf", DRBD_CONFIG_DIR "/drbd-84.conf", DRBD_CONFIG_DIR "/drbd-83.conf", DRBD_CONFIG_DIR "/drbd-82.conf", DRBD_CONFIG_DIR "/drbd-08.conf", DRBD_CONFIG_DIR "/drbd.conf", 0 }; int pushd(const char *path) { int cwd_fd = -1; cwd_fd = open(".", O_RDONLY | O_CLOEXEC); if (cwd_fd < 0) { err("open(\".\") failed: %m\n"); exit(E_USAGE); } if (path && path[0] && chdir(path)) { err("chdir(\"%s\") failed: %m\n", path); exit(E_USAGE); } return cwd_fd; } void popd(int fd) { if (fd >= 0) { if (fchdir(fd) < 0) { err("fchdir() failed: %m\n"); exit(E_USAGE); } close(fd); } } /* * returns a pointer to an malloced area that contains * an absolute, canonical, version of path. * aborts if any allocation or syscall fails. * return value should be free()d, once no longer needed. */ char *canonify_path(char *path) { int cwd_fd = -1; char *last_slash; char *tmp; char *that_wd; char *abs_path; if (!path || !path[0]) { err("cannot canonify an empty path\n"); exit(E_USAGE); } tmp = strdupa(path); last_slash = strrchr(tmp, '/'); /* Maybe this already is in the top level directory. */ if (last_slash == tmp) return strdup(path); if (last_slash) { *last_slash++ = '\0'; cwd_fd = pushd(tmp); } else { last_slash = tmp; } that_wd = getcwd(NULL, 0); if (!that_wd) { err("getcwd() failed: %m\n"); exit(E_USAGE); } /* could have been a symlink to / */ if (!strcmp("/", that_wd)) m_asprintf(&abs_path, "/%s", last_slash); else m_asprintf(&abs_path, "%s/%s", that_wd, last_slash); free(that_wd); popd(cwd_fd); return abs_path; } void assign_command_names_from_argv0(char **argv) { struct cmd_helper { char *name; char **var; }; static struct cmd_helper helpers[] = { {"drbdsetup", &drbdsetup}, {"drbdmeta", &drbdmeta}, {"drbd-proxy-ctl", &drbd_proxy_ctl}, {"drbdadm-83", &drbdadm_83}, {"drbdadm-84", &drbdadm_84}, {NULL, NULL} }; struct cmd_helper *c; /* in case drbdadm is called with an absolute or relative pathname * look for the drbdsetup binary in the same location, * otherwise, just let execvp sort it out... */ if ((progname = strrchr(argv[0], '/')) == NULL) { progname = argv[0]; for (c = helpers; c->name; ++c) *(c->var) = strdup(c->name); } else { size_t len_dir, l; ++progname; len_dir = progname - argv[0]; for (c = helpers; c->name; ++c) { l = len_dir + strlen(c->name) + 1; *(c->var) = malloc(l); if (*(c->var)) { strncpy(*(c->var), argv[0], len_dir); strcpy(*(c->var) + len_dir, c->name); if (access(*(c->var), X_OK)) strcpy(*(c->var), c->name); /* see add_lib_drbd_to_path() */ } } /* for pretty printing, truncate to basename */ argv[0] = progname; } } static void recognize_all_drbdsetup_options(void) { int i; for (i = 0; i < ARRAY_SIZE(cmds); i++) { const struct adm_cmd *cmd = cmds[i]; const struct field_def *field; if (!cmd->drbdsetup_ctx) continue; for (field = cmd->drbdsetup_ctx->fields; field->name; field++) { struct option opt; int n; field_to_option(field, &opt); for (n = 0; admopt[n].name; n++) { if (!strcmp(admopt[n].name, field->name)) { if (admopt[n].val == 257) assert (admopt[n].has_arg == opt.has_arg); else { err("Warning: drbdsetup %s option --%s " "can only be passed as -W--%s\n", cmd->name, admopt[n].name, admopt[n].name); goto skip; } } } if (admopt == general_admopt) { admopt = malloc((n + 2) * sizeof(*admopt)); memcpy(admopt, general_admopt, (n + 1) * sizeof(*admopt)); } else admopt = realloc(admopt, (n + 2) * sizeof(*admopt)); memcpy(&admopt[n+1], &admopt[n], sizeof(*admopt)); admopt[n] = opt; skip: /* dummy statement required because of label */ ; } } } struct adm_cmd *find_cmd(char *cmdname); int parse_options(int argc, char **argv, struct adm_cmd **cmd, char ***resource_names) { const char *optstring = make_optstring(admopt); struct names backend_options_check; struct d_name *b_opt; int longindex, first_arg_index; STAILQ_INIT(&backend_options_check); *cmd = NULL; *resource_names = calloc(argc + 1, sizeof(char *)); opterr = 1; optind = 0; while (1) { int c; c = getopt_long(argc, argv, optstring, admopt, &longindex); if (c == -1) break; switch (c) { case 257: /* drbdsetup option */ { struct option *option = &admopt[longindex]; char *opt; if (optarg) opt = ssprintf("--%s=%s", option->name, optarg); else opt = ssprintf("--%s", option->name); insert_tail(&backend_options_check, names_from_str(opt)); } break; case 'S': is_drbd_top = 1; break; case 'v': verbose++; break; case 'd': dry_run = 1; break; case 'c': if (!strcmp(optarg, "-")) { yyin = stdin; if (asprintf(&config_file, "STDIN") < 0) { err("asprintf(config_file): %m\n"); return 20; } config_from_stdin = 1; } else { yyin = fopen(optarg, "r"); if (!yyin) { err("Can not open '%s'.\n.", optarg); exit(E_EXEC_ERROR); } if (asprintf(&config_file, "%s", optarg) < 0) { err("asprintf(config_file): %m\n"); return 20; } } break; case 't': config_test = optarg; break; case 'E': /* Remember as absolute name */ was_file_already_seen(optarg); break; case 's': { char *pathes[2]; pathes[0] = optarg; pathes[1] = 0; find_drbdcmd(&drbdsetup, pathes); } break; case 'm': { char *pathes[2]; pathes[0] = optarg; pathes[1] = 0; find_drbdcmd(&drbdmeta, pathes); } break; case 'p': { char *pathes[2]; pathes[0] = optarg; pathes[1] = 0; find_drbdcmd(&drbd_proxy_ctl, pathes); } break; case 'n': { char *c; int shell_var_name_ok = 1; for (c = optarg; *c && shell_var_name_ok; c++) { switch (*c) { case 'a'...'z': case 'A'...'Z': case '0'...'9': case '_': break; default: shell_var_name_ok = 0; } } if (shell_var_name_ok) sh_varname = optarg; else err("ignored --sh-varname=%s: " "contains suspect characters, allowed set is [a-zA-Z0-9_]\n", optarg); } break; case 'V': printf("DRBDADM_BUILDTAG=%s\n", shell_escape(drbd_buildtag())); printf("DRBDADM_API_VERSION=%u\n", API_VERSION); printf("DRBD_KERNEL_VERSION_CODE=0x%06x\n", version_code_kernel()); printf("DRBDADM_VERSION_CODE=0x%06x\n", version_code_userland()); printf("DRBDADM_VERSION=%s\n", shell_escape(PACKAGE_VERSION)); exit(0); break; case 'P': connect_to_host = optarg; break; case 'W': insert_tail(&backend_options, names_from_str(optarg)); break; case 'h': help = true; break; case '?': goto help; } } first_arg_index = optind; for (; optind < argc; optind++) { optarg = argv[optind]; if (*cmd) { static int last_idx = 0; ensure_sanity_of_res_name(optarg); (*resource_names)[last_idx++] = optarg; } else if (!strcmp(optarg, "help")) help = true; else { *cmd = find_cmd(optarg); if (!*cmd) { /* Passing drbdsetup options like this is discouraged! */ insert_tail(&backend_options, names_from_str(optarg)); } } } if (help) print_usage_and_exit(*cmd, NULL, 0); if (*cmd == NULL) { if (first_arg_index < argc) { err("%s: Unknown command '%s'\n", progname, argv[first_arg_index]); return E_USAGE; } print_usage_and_exit(*cmd, "No command specified", E_USAGE); } /* * The backend (drbdsetup) options are command specific. Make sure that only * setup options that this command recognizes are used. */ STAILQ_FOREACH(b_opt, &backend_options_check, link) { const struct field_def *field; const char *option; int len; option = b_opt->name; for (len = 0; option[len]; len++) if (option[len] == '=') break; field = NULL; if (option[0] == '-' && option[1] == '-' && (*cmd)->drbdsetup_ctx && (*cmd)->drbdsetup_ctx != &wildcard_ctx) { for (field = (*cmd)->drbdsetup_ctx->fields; field->name; field++) { if (strlen(field->name) == len - 2 && !strncmp(option + 2, field->name, len - 2)) break; } if (!field->name) field = NULL; } if (!field && (*cmd)->drbdsetup_ctx != &wildcard_ctx) { err("%s: unrecognized option '%.*s'\n", progname, len, option); goto help; } } STAILQ_CONCAT(&backend_options, &backend_options_check); return 0; help: if (*cmd) err("try '%s help %s'\n", progname, (*cmd)->name); else err("try '%s help'\n", progname); return E_USAGE; } struct adm_cmd *find_cmd(char *cmdname) { struct adm_cmd *cmd = NULL; unsigned int i; if (!strcmp("hidden-commands", cmdname)) { // before parsing the configuration file... hidden_cmds(NULL); exit(0); } /* R_PRIMARY / R_SECONDARY is not a state, but a role. Whatever that * means, actually. But anyways, we decided to start using _role_ as * the terminus of choice, and deprecate "state". */ substitute_deprecated_cmd(&cmdname, "state", "role"); /* "outdate-peer" got renamed to fence-peer, * it is not required to actually outdate the peer, * depending on situation it may be sufficient to power-reset it * or do some other fencing action, or even call out to "meatware". * The name of the handler should not imply something that is not done. */ substitute_deprecated_cmd(&cmdname, "outdate-peer", "fence-peer"); for (i = 0; i < ARRAY_SIZE(cmds); i++) { if (!strcmp(cmds[i]->name, cmdname)) { cmd = cmds[i]; break; } } return cmd; } char *config_file_from_arg(char *arg) { char *f; int minor = minor_by_id(arg); if (minor >= 0) { f = lookup_minor(minor); if (!f) { err("Don't know which config file belongs to minor %d, trying default ones...\n", minor); return NULL; } } else { f = lookup_resource(arg); if (!f) { err("Don't know which config file belongs to resource %s, trying default ones...\n", arg); return NULL; } } yyin = fopen(f, "r"); if (yyin == NULL) { err("Couldn't open file %s for reading, reason: %m\ntrying default config file...\n", config_file); return NULL; } return f; } void assign_default_config_file(void) { int i; for (i = 0; conf_file[i]; i++) { yyin = fopen(conf_file[i], "r"); if (yyin) { config_file = conf_file[i]; break; } } if (!config_file) { err("Can not open '%s': %m\n", conf_file[i - 1]); exit(E_CONFIG_INVALID); } } void count_resources(void) { struct d_resource *res; struct d_volume *vol; number_of_minors = 0; for_each_resource(res, &config) { if (res->ignore) { nr_resources[IGNORED]++; /* How can we count ignored volumes? * Do we want to? */ continue; } else if (res->stacked) nr_resources[STACKED]++; else nr_resources[NORMAL]++; for_each_volume(vol, &res->me->volumes) { number_of_minors++; if (res->stacked) nr_volumes[STACKED]++; /* res->ignored won't come here */ else nr_volumes[NORMAL]++; } } } void die_if_no_resources(void) { if (!is_drbd_top && nr_resources[IGNORED] > 0 && nr_resources[NORMAL] == 0) { err("WARN: no normal resources defined for this host (%s)!?\n" "Misspelled name of the local machine with the 'on' keyword ?\n", hostname); exit(E_USAGE); } if (!is_drbd_top && nr_resources[NORMAL] == 0) { err("WARN: no normal resources defined for this host (%s)!?\n", hostname); exit(E_USAGE); } if (is_drbd_top && nr_resources[STACKED] == 0) { err("WARN: nothing stacked for this host (%s), " "nothing to do in stacked mode!\n", hostname); exit(E_USAGE); } } int main(int argc, char **argv) { size_t i; int rv = 0, r; struct adm_cmd *cmd = NULL; char **resource_names = NULL; struct d_resource *res; char *env_drbd_nodename = NULL; int is_dump_xml; int is_dump; struct cfg_ctx ctx = { }; initialize_err(); initialize_deferred_cmds(); yyin = NULL; hostname = get_hostname(); no_tty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout))); env_drbd_nodename = getenv("__DRBD_NODE__"); if (env_drbd_nodename && *env_drbd_nodename) { hostname = strdup(env_drbd_nodename); err("\n" " found __DRBD_NODE__ in environment\n" " PRETENDING that I am >>%s<<\n\n", hostname); } assign_command_names_from_argv0(argv); if (drbdsetup == NULL || drbdmeta == NULL || drbd_proxy_ctl == NULL) { err("could not strdup argv[0].\n"); exit(E_EXEC_ERROR); } maybe_exec_legacy_drbdadm(argv); recognize_all_drbdsetup_options(); rv = parse_options(argc, argv, &cmd, &resource_names); if (rv) return rv; if (config_test && !cmd->test_config) { err("The --config-to-test (-t) option is only allowed " "with the dump and sh-nop commands\n"); exit(E_USAGE); } do_verify_ips = cmd->verify_ips; is_dump_xml = (cmd == &dump_xml_cmd); is_dump = (is_dump_xml || cmd == &dump_cmd); if (!resource_names[0]) { if (is_dump) all_resources = 1; else if (cmd->res_name_required) print_usage_and_exit(cmd, "No resource names specified", E_USAGE); } else if (resource_names[0]) { if (cmd->backend_res_name) /* Okay */ ; else if (!cmd->res_name_required) err("This command will ignore resource names!\n"); else if (resource_names[1] && cmd->use_cached_config_file) err("You should not use this command with multiple resources!\n"); } if (!config_file && cmd->use_cached_config_file) config_file = config_file_from_arg(resource_names[0]); if (!config_file) /* may exit if no config file can be used! */ assign_default_config_file(); /* for error-reporting reasons config_file may be re-assigned by adm_adjust, * we need the current value for register_minor, though. * save that. */ if (config_from_stdin) config_save = config_file; else config_save = canonify_path(config_file); my_parse(); if (config_test) { char *saved_config_file = config_file; char *saved_config_save = config_save; config_file = config_test; config_save = canonify_path(config_test); fclose(yyin); yyin = fopen(config_test, "r"); if (!yyin) { err("Can not open '%s'.\n.", config_test); exit(E_EXEC_ERROR); } my_parse(); config_file = saved_config_file; config_save = saved_config_save; } if (!config_valid) exit(E_CONFIG_INVALID); post_parse(&config, cmd->is_proxy_cmd ? MATCH_ON_PROXY : 0); if (!is_dump || dry_run || verbose) expand_common(); if (dry_run || config_from_stdin) do_register = 0; count_resources(); if (cmd->uc_dialog) uc_node(global_options.usage_count); ctx.cmd = cmd; if (cmd->res_name_required || resource_names[0]) { if (STAILQ_EMPTY(&config) && !is_dump) { err("no resources defined!\n"); exit(E_USAGE); } global_validate_maybe_expand_die_if_invalid(!is_dump, cmd->is_proxy_cmd ? MATCH_ON_PROXY : 0); if (!resource_names[0] || !strcmp(resource_names[0], "all")) { /* either no resource arguments at all, * but command is dump / dump-xml, so implicit "all", * or an explicit "all" argument is given */ all_resources = 1; if (!is_dump) die_if_no_resources(); /* verify ips first, for all of them */ for_each_resource(res, &config) { verify_ips(res); } if (!config_valid) exit(E_CONFIG_INVALID); if (is_dump_xml) print_dump_xml_header(); else if (is_dump) print_dump_header(); for_each_resource(res, &config) { if (!is_dump && res->ignore) continue; if (!is_dump && is_drbd_top != res->stacked) continue; ctx.res = res; ctx.vol = NULL; r = call_cmd(cmd, &ctx, EXIT_ON_FAIL); /* does exit for r >= 20! */ /* this super positioning of return values is soo ugly * anyone any better idea? */ if (r > rv) rv = r; } if (is_dump_xml) printf("\n"); } else { /* explicit list of resources to work on */ struct connection *conn; /* first we execute some sanity checks, * the checks use ignore_tmp */ for_each_resource(res, &config) for_each_connection(conn, &res->connections) conn->ignore_tmp = conn->ignore; /* check if we would enable a connection that should be ignored */ for (i = 0; resource_names[i]; i++) if (ctx_by_name(&ctx, resource_names[i], WOULD_ENABLE_DISABLED) > 0) { err("USAGE_BUG: Tried to enable disabled connections %s\n", resource_names[i]); exit(E_USAGE); } /* check if we would enable a connection that was already enabled. * set all connections to ignore and then check if we would enable a * connection twice */ for_each_resource(res, &config) for_each_connection(conn, &res->connections) conn->ignore_tmp = true; for (i = 0; resource_names[i]; i++) if (ctx_by_name(&ctx, resource_names[i], WOULD_ENABLE_MULTI_TIMES) > 0) { err("USAGE_BUG: %s would enable an already enabled connection\n", resource_names[i]); exit(E_USAGE); } for (i = 0; resource_names[i]; i++) { ctx.res = NULL; ctx.vol = NULL; r = ctx_by_name(&ctx, resource_names[i], SETUP_MULTI); if (!ctx.res) { ctx_by_minor(&ctx, resource_names[i]); r = 0; } if (!ctx.res) { err("'%s' not defined in your config (for this host).\n", resource_names[i]); exit(E_USAGE); } if (r) exit(E_USAGE); if (!cmd->vol_id_required && !cmd->iterate_volumes && ctx.vol != NULL && !cmd->vol_id_optional) { if (ctx.vol->implicit) ctx.vol = NULL; else { err("%s operates on whole resources, but you specified a specific volume!\n", cmd->name); exit(E_USAGE); } } if (cmd->vol_id_required && !ctx.vol && STAILQ_FIRST(&ctx.res->me->volumes)->implicit) ctx.vol = STAILQ_FIRST(&ctx.res->me->volumes); if (cmd->vol_id_required && !ctx.vol) { err("%s requires a specific volume id, but none is specified.\n" "Try '%s minor-' or '%s %s/'\n", cmd->name, cmd->name, cmd->name, resource_names[i]); exit(E_USAGE); } if (ctx.res->ignore && !is_dump) { err("'%s' ignored, since this host (%s) is not mentioned with an 'on' keyword.\n", ctx.res->name, hostname); if (rv < E_USAGE) rv = E_USAGE; continue; } if (is_drbd_top != ctx.res->stacked && !is_dump) { err("'%s' is a %s resource, and not available in %s mode.\n", ctx.res->name, ctx.res->stacked ? "stacked" : "normal", is_drbd_top ? "stacked" : "normal"); if (rv < E_USAGE) rv = E_USAGE; continue; } verify_ips(ctx.res); if (!is_dump && !config_valid) exit(E_CONFIG_INVALID); r = call_cmd(cmd, &ctx, EXIT_ON_FAIL); /* does exit for r >= 20! */ if (r > rv) rv = r; } } } else { // Commands which do not need a resource name /* no call_cmd, as that implies register_minor, * which does not make sense for resource independent commands. * It does also not need to iterate over volumes: it does not even know the resource. */ ctx.cmd = cmd; rv = __call_cmd_fn(&ctx, KEEP_RUNNING); if (rv >= 10) { /* why do we special case the "generic sh-*" commands? */ err("command %s exited with code %d\n", cmd->name, rv); exit(rv); } } r = run_deferred_cmds(); if (r > rv) rv = r; free_config(); free(resource_names); if (admopt != general_admopt) free(admopt); free_btrees(); return rv; } void yyerror(char *text) { err("%s:%d: %s\n", config_file, line, text); exit(E_SYNTAX); } drbd-utils-8.9.10/user/v9/drbdsetup.c0000644000175000017500000035663113011114651017241 0ustar apoikosapoikos/* * DRBD setup via genetlink * * This file is part of DRBD by Philipp Reisner and Lars Ellenberg. * * Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. * Copyright (C) 1999-2008, Philipp Reisner . * Copyright (C) 2002-2008, Lars Ellenberg . * * drbd is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define _GNU_SOURCE #define _XOPEN_SOURCE 600 #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define EXIT_NOMEM 20 #define EXIT_NO_FAMILY 20 #define EXIT_SEND_ERR 20 #define EXIT_RECV_ERR 20 #define EXIT_TIMED_OUT 20 #define EXIT_NOSOCK 30 #define EXIT_THINKO 42 /* * We are not using libnl, * using its API for the few things we want to do * ends up being almost as much lines of code as * coding the necessary bits right here. */ #include "libgenl.h" #include "drbd_nla.h" #include #include #include #include "drbdtool_common.h" #include #include "drbd_strings.h" #include "registry.h" #include "config.h" #include "config_flags.h" #include "wrap_printf.h" #include "drbdsetup_colors.h" char *progname; /* for parsing of messages */ static struct nlattr *global_attrs[128]; /* there is an other table, nested_attr_tb, defined in genl_magic_func.h, * which can be used after _from_attrs, * to check for presence of struct fields. */ #define ntb(t) nested_attr_tb[__nla_type(t)] #ifdef PRINT_NLMSG_LEN /* I'm to lazy to check the maximum possible nlmsg length by hand */ int main(void) { static __u16 nla_attr_minlen[NLA_TYPE_MAX+1] __read_mostly = { [NLA_U8] = sizeof(__u8), [NLA_U16] = sizeof(__u16), [NLA_U32] = sizeof(__u32), [NLA_U64] = sizeof(__u64), [NLA_NESTED] = NLA_HDRLEN, }; int i; int sum_total = 0; #define LEN__(policy) do { \ int sum = 0; \ for (i = 0; i < ARRAY_SIZE(policy); i++) { \ sum += nla_total_size(policy[i].len ?: \ nla_attr_minlen[policy[i].type]); \ \ } \ sum += 4; \ sum_total += sum; \ printf("%-30s %4u [%4u]\n", \ #policy ":", sum, sum_total); \ } while (0) #define LEN_(p) LEN__(p ## _nl_policy) LEN_(disk_conf); LEN_(syncer_conf); LEN_(net_conf); LEN_(set_role_parms); LEN_(resize_parms); LEN_(state_info); LEN_(start_ov_parms); LEN_(new_c_uuid_parms); sum_total += sizeof(struct nlmsghdr) + sizeof(struct genlmsghdr) + sizeof(struct drbd_genlmsghdr); printf("sum total inclusive hdr overhead: %4u\n", sum_total); return 0; } #else #ifndef AF_INET_SDP #define AF_INET_SDP 27 #define PF_INET_SDP AF_INET_SDP #endif #define MULTIPLE_TIMEOUTS (-2) /* pretty print helpers */ static int indent = 0; #define INDENT_WIDTH 4 #define printI(fmt, args... ) printf("%*s" fmt,INDENT_WIDTH * indent,"" , ## args ) enum usage_type { BRIEF, FULL, XML, }; struct drbd_argument { const char* name; __u16 nla_type; int (*convert_function)(struct drbd_argument *, struct msg_buff *, struct drbd_genlmsghdr *dhdr, char *); }; /* Configuration requests typically need a context to operate on. * Possible keys are device minor/volume id (both fit in the drbd_genlmsghdr), * the replication link (aka connection) name, * and/or the replication group (aka resource) name */ enum cfg_ctx_key { /* Only one of these can be present in a command: */ CTX_RESOURCE = 1, CTX_PEER_NODE_ID = 2, CTX_MINOR = 4, CTX_VOLUME = 8, CTX_MY_ADDR = 16, CTX_PEER_ADDR = 32, CTX_ALL = 64, CTX_MULTIPLE_ARGUMENTS = 128, /* To identify a connection, we use (resource_name, peer_node_id) */ CTX_PEER_NODE = CTX_RESOURCE | CTX_PEER_NODE_ID | CTX_MULTIPLE_ARGUMENTS, CTX_PEER_DEVICE = CTX_PEER_NODE | CTX_VOLUME, }; enum cfg_ctx_key ctx_next_arg(enum cfg_ctx_key *key) { enum cfg_ctx_key next_arg; if (*key & CTX_MULTIPLE_ARGUMENTS) { next_arg = *key & ~(*key - 1); /* the lowest set bit */ next_arg &= ~CTX_MULTIPLE_ARGUMENTS; } else next_arg = *key; *key &= ~next_arg; return next_arg; } const char *ctx_arg_string(enum cfg_ctx_key key, enum usage_type ut) { bool xml = (ut == XML); switch(key) { case CTX_RESOURCE: return xml ? "resource" : "{resource}"; case CTX_PEER_NODE_ID: return xml ? "peer_node_id" : "{peer_node_id}"; case CTX_MINOR: return xml ? "minor" : "{minor}"; case CTX_VOLUME: return xml ? "volume" : "{volume}"; case CTX_MY_ADDR: return xml ? "local_addr" : "[local:][{af}:]{local_addr}[:{port}]"; case CTX_PEER_ADDR: return xml ? "remote_addr" : "[peer:][{af}:]{remote_addr}[:{port}]"; case CTX_ALL: return "all"; default: assert(0); } return "unknown argument"; } struct drbd_cmd { const char* cmd; enum cfg_ctx_key ctx_key; int cmd_id; int tla_id; /* top level attribute id */ int (*function)(struct drbd_cmd *, int, char **); struct drbd_argument *drbd_args; int (*show_function)(struct drbd_cmd*, struct genl_info *, void *u_ptr); struct option *options; bool missing_ok; bool warn_on_missing; bool continuous_poll; bool set_defaults; bool lockless; struct context_def *ctx; const char *summary; }; // other functions static int get_af_ssocks(int warn); static void print_command_usage(struct drbd_cmd *cm, enum usage_type); static void print_usage_and_exit(const char *addinfo) __attribute__ ((noreturn)); static const char *resync_susp_str(struct peer_device_info *info); // command functions static int generic_config_cmd(struct drbd_cmd *cm, int argc, char **argv); static int down_cmd(struct drbd_cmd *cm, int argc, char **argv); static int generic_get_cmd(struct drbd_cmd *cm, int argc, char **argv); static int del_minor_cmd(struct drbd_cmd *cm, int argc, char **argv); static int del_resource_cmd(struct drbd_cmd *cm, int argc, char **argv); static int show_cmd(struct drbd_cmd *cm, int argc, char **argv); static int status_cmd(struct drbd_cmd *cm, int argc, char **argv); static int role_cmd(struct drbd_cmd *cm, int argc, char **argv); static int cstate_cmd(struct drbd_cmd *cm, int argc, char **argv); static int dstate_cmd(struct drbd_cmd *cm, int argc, char **argv); static int check_resize_cmd(struct drbd_cmd *cm, int argc, char **argv); static int show_or_get_gi_cmd(struct drbd_cmd *cm, int argc, char **argv); // sub commands for generic_get_cmd static int print_notifications(struct drbd_cmd *, struct genl_info *, void *); static int wait_for_family(struct drbd_cmd *, struct genl_info *, void *); static int remember_resource(struct drbd_cmd *, struct genl_info *, void *); static int remember_device(struct drbd_cmd *, struct genl_info *, void *); static int remember_connection(struct drbd_cmd *, struct genl_info *, void *); static int remember_peer_device(struct drbd_cmd *, struct genl_info *, void *); #define ADDRESS_STR_MAX 256 static char *address_str(char *buffer, void* address, int addr_len); // convert functions for arguments static int conv_block_dev(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg); static int conv_md_idx(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg); static int conv_u32(struct drbd_argument *, struct msg_buff *, struct drbd_genlmsghdr *, char *); static int conv_addr(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg); struct resources_list { struct resources_list *next; char *name; struct nlattr *res_opts; struct resource_info info; struct resource_statistics statistics; }; static struct resources_list *list_resources(void); static struct resources_list *sort_resources(struct resources_list *); static void free_resources(struct resources_list *); struct devices_list { struct devices_list *next; unsigned minor; struct drbd_cfg_context ctx; struct nlattr *disk_conf_nl; struct disk_conf disk_conf; struct device_info info; struct device_statistics statistics; }; static struct devices_list *list_devices(char *); static void free_devices(struct devices_list *); struct connections_list { struct connections_list *next; struct drbd_cfg_context ctx; struct nlattr *path_list; struct nlattr *net_conf; struct connection_info info; struct connection_statistics statistics; }; static struct connections_list *sort_connections(struct connections_list *); static struct connections_list *list_connections(char *); static void free_connections(struct connections_list *); struct peer_devices_list { struct peer_devices_list *next; struct drbd_cfg_context ctx; struct nlattr *peer_device_conf; struct peer_device_info info; struct peer_device_statistics statistics; struct devices_list *device; int timeout_ms; /* used only by wait_for_family() */ }; static struct peer_devices_list *list_peer_devices(char *); static void free_peer_devices(struct peer_devices_list *); struct option wait_cmds_options[] = { { "wfc-timeout", required_argument, 0, 't' }, { "degr-wfc-timeout", required_argument, 0, 'd'}, { "outdated-wfc-timeout", required_argument, 0, 'o'}, { "wait-after-sb", optional_argument, 0, 'w'}, { } }; struct option events_cmd_options[] = { { "timestamps", no_argument, 0, 'T' }, { "statistics", no_argument, 0, 's' }, { "now", no_argument, 0, 'n' }, { "color", optional_argument, 0, 'c' }, { } }; struct option show_cmd_options[] = { { "show-defaults", no_argument, 0, 'D' }, { } }; static struct option status_cmd_options[] = { { "verbose", no_argument, 0, 'v' }, { "statistics", no_argument, 0, 's' }, { "color", optional_argument, 0, 'c' }, { "json", no_argument, 0, 'j' }, { } }; #define F_CONFIG_CMD generic_config_cmd #define NO_PAYLOAD 0 #define F_NEW_EVENTS_CMD(scmd) DRBD_ADM_GET_INITIAL_STATE, NO_PAYLOAD, generic_get_cmd, \ .show_function = scmd struct drbd_cmd commands[] = { {"primary", CTX_RESOURCE, DRBD_ADM_PRIMARY, DRBD_NLA_SET_ROLE_PARMS, F_CONFIG_CMD, .ctx = &primary_cmd_ctx, .summary = "Change the role of a node in a resource to primary." }, {"secondary", CTX_RESOURCE, DRBD_ADM_SECONDARY, NO_PAYLOAD, F_CONFIG_CMD, .summary = "Change the role of a node in a resource to secondary." }, {"attach", CTX_MINOR, DRBD_ADM_ATTACH, DRBD_NLA_DISK_CONF, F_CONFIG_CMD, .drbd_args = (struct drbd_argument[]) { { "lower_dev", T_backing_dev, conv_block_dev }, { "meta_data_dev", T_meta_dev, conv_block_dev }, { "meta_data_index", T_meta_dev_idx, conv_md_idx }, { } }, .ctx = &attach_cmd_ctx, .summary = "Attach a lower-level device to an existing replicated device." }, {"disk-options", CTX_MINOR, DRBD_ADM_CHG_DISK_OPTS, DRBD_NLA_DISK_CONF, F_CONFIG_CMD, .set_defaults = true, .ctx = &disk_options_ctx, .summary = "Change the disk options of an attached lower-level device." }, {"detach", CTX_MINOR, DRBD_ADM_DETACH, DRBD_NLA_DETACH_PARMS, F_CONFIG_CMD, .ctx = &detach_cmd_ctx, .summary = "Detach the lower-level device of a replicated device." }, {"connect", CTX_PEER_NODE, DRBD_ADM_CONNECT, DRBD_NLA_CONNECT_PARMS, F_CONFIG_CMD, .ctx = &connect_cmd_ctx, .summary = "Attempt to (re)establish a replication link to a peer host." }, {"new-peer", CTX_PEER_NODE, DRBD_ADM_NEW_PEER, DRBD_NLA_NET_CONF, F_CONFIG_CMD, .ctx = &new_peer_cmd_ctx, .summary = "Make a peer host known to a resource." }, {"del-peer", CTX_PEER_NODE, DRBD_ADM_DEL_PEER, DRBD_NLA_DISCONNECT_PARMS, F_CONFIG_CMD, .ctx = &disconnect_cmd_ctx, .summary = "Remove a connection to a peer host." }, {"new-path", CTX_PEER_NODE, DRBD_ADM_NEW_PATH, DRBD_NLA_PATH_PARMS, F_CONFIG_CMD, .drbd_args = (struct drbd_argument[]) { { "local-addr", T_my_addr, conv_addr }, { "remote-addr", T_peer_addr, conv_addr }, { } }, .ctx = &path_cmd_ctx, .summary = "Add a path (endpoint address pair) where a peer host should be reachable." }, {"del-path", CTX_PEER_NODE, DRBD_ADM_DEL_PATH, DRBD_NLA_PATH_PARMS, F_CONFIG_CMD, .drbd_args = (struct drbd_argument[]) { { "local-addr", T_my_addr, conv_addr }, { "remote-addr", T_peer_addr, conv_addr }, { } }, .ctx = &path_cmd_ctx, .summary = "Remove a path (endpoint address pair) from a connection to a peer host." }, {"net-options", CTX_PEER_NODE, DRBD_ADM_CHG_NET_OPTS, DRBD_NLA_NET_CONF, F_CONFIG_CMD, .set_defaults = true, .ctx = &net_options_ctx, .summary = "Change the network options of a connection." }, {"disconnect", CTX_PEER_NODE, DRBD_ADM_DISCONNECT, DRBD_NLA_DISCONNECT_PARMS, F_CONFIG_CMD, .ctx = &disconnect_cmd_ctx, .summary = "Unconnect from a peer host." }, {"resize", CTX_MINOR, DRBD_ADM_RESIZE, DRBD_NLA_RESIZE_PARMS, F_CONFIG_CMD, .ctx = &resize_cmd_ctx, .summary = "Reexamine the lower-level device sizes to resize a replicated device." }, {"resource-options", CTX_RESOURCE, DRBD_ADM_RESOURCE_OPTS, DRBD_NLA_RESOURCE_OPTS, F_CONFIG_CMD, .set_defaults = true, .ctx = &resource_options_ctx, .summary = "Change the resource options of an existing resource." }, {"peer-device-options", CTX_PEER_DEVICE, DRBD_ADM_CHG_PEER_DEVICE_OPTS, DRBD_NLA_PEER_DEVICE_OPTS, F_CONFIG_CMD, .set_defaults = true, .ctx = &peer_device_options_ctx, .summary = "Change peer-device options." }, {"new-current-uuid", CTX_MINOR, DRBD_ADM_NEW_C_UUID, DRBD_NLA_NEW_C_UUID_PARMS, F_CONFIG_CMD, .ctx = &new_current_uuid_cmd_ctx, .summary = "Generate a new current UUID." }, {"invalidate", CTX_MINOR, DRBD_ADM_INVALIDATE, DRBD_NLA_INVALIDATE_PARMS, F_CONFIG_CMD, .ctx = &invalidate_ctx, .summary = "Replace the local data of a volume with that of a peer." }, {"invalidate-remote", CTX_PEER_DEVICE, DRBD_ADM_INVAL_PEER, NO_PAYLOAD, F_CONFIG_CMD, .summary = "Replace a peer's data of a volume with the local data." }, {"pause-sync", CTX_PEER_DEVICE, DRBD_ADM_PAUSE_SYNC, NO_PAYLOAD, F_CONFIG_CMD, .summary = "Stop resynchronizing between a local and a peer device." }, {"resume-sync", CTX_PEER_DEVICE, DRBD_ADM_RESUME_SYNC, NO_PAYLOAD, F_CONFIG_CMD, .summary = "Allow resynchronization to resume on a replicated device." }, {"suspend-io", CTX_MINOR, DRBD_ADM_SUSPEND_IO, NO_PAYLOAD, F_CONFIG_CMD, .summary = "Suspend I/O on a replicated device." }, {"resume-io", CTX_MINOR, DRBD_ADM_RESUME_IO, NO_PAYLOAD, F_CONFIG_CMD, .summary = "Resume I/O on a replicated device." }, {"outdate", CTX_MINOR, DRBD_ADM_OUTDATE, NO_PAYLOAD, F_CONFIG_CMD, .summary = "Mark the data on a lower-level device as outdated." }, {"verify", CTX_PEER_DEVICE, DRBD_ADM_START_OV, DRBD_NLA_START_OV_PARMS, F_CONFIG_CMD, .ctx = &verify_cmd_ctx, .summary = "Verify the data on a lower-level device against a peer device." }, {"down", CTX_RESOURCE | CTX_ALL, DRBD_ADM_DOWN, NO_PAYLOAD, down_cmd, .missing_ok = true, .warn_on_missing = true, .summary = "Take a resource down." }, {"role", CTX_RESOURCE, 0, NO_PAYLOAD, role_cmd, .lockless = true, .summary = "Show the current role of a resource." }, {"cstate", CTX_PEER_NODE, 0, NO_PAYLOAD, cstate_cmd, .lockless = true, .summary = "Show the current state of a connection." }, {"dstate", CTX_MINOR, 0, NO_PAYLOAD, dstate_cmd, .lockless = true, .summary = "Show the current disk state of a lower-level device." }, {"show-gi", CTX_PEER_DEVICE, 0, NO_PAYLOAD, show_or_get_gi_cmd, .lockless = true, .summary = "Show the data generation identifiers for a device on a particular connection, with explanations." }, {"get-gi", CTX_PEER_DEVICE, 0, NO_PAYLOAD, show_or_get_gi_cmd, .lockless = true, .summary = "Show the data generation identifiers for a device on a particular connection." }, {"show", CTX_RESOURCE | CTX_ALL, 0, 0, show_cmd, .options = show_cmd_options, .lockless = true, .summary = "Show the current configuration of a resource, or of all resources." }, {"status", CTX_RESOURCE | CTX_ALL, 0, 0, status_cmd, .options = status_cmd_options, .lockless = true, .summary = "Show the state of a resource, or of all resources." }, {"check-resize", CTX_MINOR, 0, NO_PAYLOAD, check_resize_cmd, .lockless = true, .summary = "Remember the current size of a lower-level device." }, {"events2", CTX_RESOURCE | CTX_ALL, F_NEW_EVENTS_CMD(print_notifications), .options = events_cmd_options, .missing_ok = true, .continuous_poll = true, .lockless = true, .summary = "Show the current state and all state changes of a resource, or of all resources." }, {"wait-sync-volume", CTX_PEER_DEVICE, F_NEW_EVENTS_CMD(wait_for_family), .options = wait_cmds_options, .continuous_poll = true, .lockless = true, .summary = "Wait until resync finished on a volume." }, {"wait-sync-connection", CTX_PEER_NODE, F_NEW_EVENTS_CMD(wait_for_family), .options = wait_cmds_options, .continuous_poll = true, .lockless = true, .summary = "Wait until resync finished on all volumes of a connection." }, {"wait-sync-resource", CTX_RESOURCE, F_NEW_EVENTS_CMD(wait_for_family), .options = wait_cmds_options, .continuous_poll = true, .lockless = true, .summary = "Wait until resync finished on all volumes." }, {"wait-connect-volume", CTX_PEER_DEVICE, F_NEW_EVENTS_CMD(wait_for_family), .options = wait_cmds_options, .continuous_poll = true, .lockless = true, .summary = "Wait until a device on a peer is visible." }, {"wait-connect-connection", CTX_PEER_NODE, F_NEW_EVENTS_CMD(wait_for_family), .options = wait_cmds_options, .continuous_poll = true, .lockless = true, .summary = "Wait until all peer volumes of connection are visible." }, {"wait-connect-resource", CTX_RESOURCE, F_NEW_EVENTS_CMD(wait_for_family), .options = wait_cmds_options, .continuous_poll = true, .lockless = true, .summary = "Wait until all connections are establised." }, {"new-resource", CTX_RESOURCE, DRBD_ADM_NEW_RESOURCE, DRBD_NLA_RESOURCE_OPTS, F_CONFIG_CMD, .drbd_args = (struct drbd_argument[]) { { "node_id", T_node_id, conv_u32 }, { } }, .ctx = &resource_options_ctx, .summary = "Create a new resource." }, {"new-minor", CTX_RESOURCE | CTX_MINOR | CTX_VOLUME | CTX_MULTIPLE_ARGUMENTS, DRBD_ADM_NEW_MINOR, DRBD_NLA_DEVICE_CONF, F_CONFIG_CMD, .ctx = &device_options_ctx, .summary = "Create a new replicated device within a resource." }, {"del-minor", CTX_MINOR, DRBD_ADM_DEL_MINOR, NO_PAYLOAD, del_minor_cmd, .summary = "Remove a replicated device." }, {"del-resource", CTX_RESOURCE, DRBD_ADM_DEL_RESOURCE, NO_PAYLOAD, del_resource_cmd, .summary = "Remove a resource." }, {"forget-peer", CTX_RESOURCE, DRBD_ADM_FORGET_PEER, DRBD_NLA_FORGET_PEER_PARMS, F_CONFIG_CMD, .drbd_args = (struct drbd_argument[]) { { "peer_node_id", T_forget_peer_node_id, conv_u32 }, { } }, .summary = "Completely remove any reference to a unconnected peer from meta-data." }, }; bool show_defaults; bool wait_after_split_brain; #define OTHER_ERROR 900 #define EM(C) [ C - ERR_CODE_BASE ] /* The EM(123) are used for old error messages. */ static const char *error_messages[] = { EM(NO_ERROR) = "No further Information available.", EM(ERR_LOCAL_ADDR) = "Local address(port) already in use.", EM(ERR_PEER_ADDR) = "Remote address(port) already in use.", EM(ERR_OPEN_DISK) = "Can not open backing device.", EM(ERR_OPEN_MD_DISK) = "Can not open meta device.", EM(106) = "Lower device already in use.", EM(ERR_DISK_NOT_BDEV) = "Lower device is not a block device.", EM(ERR_MD_NOT_BDEV) = "Meta device is not a block device.", EM(109) = "Open of lower device failed.", EM(110) = "Open of meta device failed.", EM(ERR_DISK_TOO_SMALL) = "Low.dev. smaller than requested DRBD-dev. size.", EM(ERR_MD_DISK_TOO_SMALL) = "Meta device too small.", EM(113) = "You have to use the disk command first.", EM(ERR_BDCLAIM_DISK) = "Lower device is already claimed. This usually means it is mounted.", EM(ERR_BDCLAIM_MD_DISK) = "Meta device is already claimed. This usually means it is mounted.", EM(ERR_MD_IDX_INVALID) = "Lower device / meta device / index combination invalid.", EM(117) = "Currently we only support devices up to 3.998TB.\n" "(up to 2TB in case you do not have CONFIG_LBD set)\n" "Contact office@linbit.com, if you need more.", EM(ERR_IO_MD_DISK) = "IO error(s) occurred during initial access to meta-data.\n", EM(ERR_MD_UNCLEAN) = "Unclean meta-data found.\nYou need to 'drbdadm apply-al res'\n", EM(ERR_MD_INVALID) = "No valid meta-data signature found.\n\n" "\t==> Use 'drbdadm create-md res' to initialize meta-data area. <==\n", EM(ERR_AUTH_ALG) = "The 'cram-hmac-alg' you specified is not known in " "the kernel. (Maybe you need to modprobe it, or modprobe hmac?)", EM(ERR_AUTH_ALG_ND) = "The 'cram-hmac-alg' you specified is not a digest.", EM(ERR_NOMEM) = "kmalloc() failed. Out of memory?", EM(ERR_DISCARD_IMPOSSIBLE) = "--discard-my-data not allowed when primary.", EM(ERR_DISK_CONFIGURED) = "Device is attached to a disk (use detach first)", EM(ERR_NET_CONFIGURED) = "Device has a net-config (use disconnect first)", EM(ERR_MANDATORY_TAG) = "UnknownMandatoryTag", EM(ERR_MINOR_INVALID) = "Device minor not allocated", EM(128) = "Resulting device state would be invalid", EM(ERR_INTR) = "Interrupted by Signal", EM(ERR_RESIZE_RESYNC) = "Resize not allowed during resync.", EM(ERR_NO_PRIMARY) = "Need one Primary node to resize.", EM(ERR_RESYNC_AFTER) = "The resync-after minor number is invalid", EM(ERR_RESYNC_AFTER_CYCLE) = "This would cause a resync-after dependency cycle", EM(ERR_PAUSE_IS_SET) = "Sync-pause flag is already set", EM(ERR_PAUSE_IS_CLEAR) = "Sync-pause flag is already cleared", EM(136) = "Disk state is lower than outdated", EM(ERR_PACKET_NR) = "Kernel does not know how to handle your request.\n" "Maybe API_VERSION mismatch?", EM(ERR_NO_DISK) = "Device does not have a disk-config", EM(ERR_NOT_PROTO_C) = "Protocol C required", EM(ERR_NOMEM_BITMAP) = "vmalloc() failed. Out of memory?", EM(ERR_INTEGRITY_ALG) = "The 'data-integrity-alg' you specified is not known in " "the kernel. (Maybe you need to modprobe it, or modprobe hmac?)", EM(ERR_INTEGRITY_ALG_ND) = "The 'data-integrity-alg' you specified is not a digest.", EM(ERR_CPU_MASK_PARSE) = "Invalid cpu-mask.", EM(ERR_VERIFY_ALG) = "VERIFYAlgNotAvail", EM(ERR_VERIFY_ALG_ND) = "VERIFYAlgNotDigest", EM(ERR_VERIFY_RUNNING) = "Can not change verify-alg while online verify runs", EM(ERR_DATA_NOT_CURRENT) = "Can only attach to the data we lost last (see kernel log).", EM(ERR_CONNECTED) = "Need to be StandAlone", EM(ERR_CSUMS_ALG) = "CSUMSAlgNotAvail", EM(ERR_CSUMS_ALG_ND) = "CSUMSAlgNotDigest", EM(ERR_CSUMS_RESYNC_RUNNING) = "Can not change csums-alg while resync is in progress", EM(ERR_PERM) = "Permission denied. CAP_SYS_ADMIN necessary", EM(ERR_NEED_APV_93) = "Protocol version 93 required to use --assume-clean", EM(ERR_STONITH_AND_PROT_A) = "Fencing policy resource-and-stonith only with prot B or C allowed", EM(ERR_CONG_NOT_PROTO_A) = "on-congestion policy pull-ahead only with prot A allowed", EM(ERR_PIC_AFTER_DEP) = "Sync-pause flag is already cleared.\n" "Note: Resync pause caused by a local resync-after dependency.", EM(ERR_PIC_PEER_DEP) = "Sync-pause flag is already cleared.\n" "Note: Resync pause caused by the peer node.", EM(ERR_RES_NOT_KNOWN) = "Unknown resource", EM(ERR_RES_IN_USE) = "Resource still in use (delete all minors first)", EM(ERR_MINOR_CONFIGURED) = "Minor still configured (down it first)", EM(ERR_MINOR_OR_VOLUME_EXISTS) = "Minor or volume exists already (delete it first)", EM(ERR_INVALID_REQUEST) = "Invalid configuration request", EM(ERR_NEED_APV_100) = "Prot version 100 required in order to change\n" "these network options while connected", EM(ERR_NEED_ALLOW_TWO_PRI) = "Can not clear allow_two_primaries as long as\n" "there a primaries on both sides", EM(ERR_MD_LAYOUT_CONNECTED) = "DRBD need to be connected for online MD layout change\n", EM(ERR_MD_LAYOUT_TOO_BIG) = "Resulting AL area too big\n", EM(ERR_MD_LAYOUT_TOO_SMALL) = "Resulting AL are too small\n", EM(ERR_MD_LAYOUT_NO_FIT) = "Resulting AL does not fit into available meta data space\n", EM(ERR_IMPLICIT_SHRINK) = "Implicit device shrinking not allowed. See kernel log.\n", EM(ERR_INVALID_PEER_NODE_ID) = "Invalid peer-node-id\n", EM(ERR_CREATE_TRANSPORT) = "Failed to create transport (drbd_transport_xxx module missing?)\n", EM(ERR_LOCAL_AND_PEER_ADDR) = "Combination of local address(port) and remote address(port) already in use\n", }; #define MAX_ERROR (sizeof(error_messages)/sizeof(*error_messages)) const char * error_to_string(int err_no) { const unsigned int idx = err_no - ERR_CODE_BASE; if (idx >= MAX_ERROR) return "Unknown... maybe API_VERSION mismatch?"; return error_messages[idx]; } #undef MAX_ERROR char *cmdname = NULL; /* "drbdsetup" for reporting in usage etc. */ /* * In CTX_MINOR, CTX_RESOURCE, CTX_ALL, objname and minor refer to the object * the command operates on. */ char *objname; unsigned minor = -1U; struct drbd_cfg_context global_ctx; enum cfg_ctx_key context; int lock_fd; struct genl_sock *drbd_sock = NULL; struct genl_family drbd_genl_family = { .name = "drbd", .version = GENL_MAGIC_VERSION, .hdrsize = GENL_MAGIC_FAMILY_HDRSZ, }; #if 0 /* currently unused. */ static bool endpoints_equal(struct drbd_cfg_context *a, struct drbd_cfg_context *b) { return a->ctx_my_addr_len == b->ctx_my_addr_len && a->ctx_peer_addr_len == b->ctx_peer_addr_len && !memcmp(a->ctx_my_addr, b->ctx_my_addr, a->ctx_my_addr_len) && !memcmp(a->ctx_peer_addr, b->ctx_peer_addr, a->ctx_peer_addr_len); } #endif static int conv_block_dev(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg) { struct stat sb; int device_fd; if ((device_fd = open(arg,O_RDWR))==-1) { PERROR("Can not open device '%s'", arg); return OTHER_ERROR; } if (fstat(device_fd, &sb)) { PERROR("fstat(%s) failed", arg); return OTHER_ERROR; } if(!S_ISBLK(sb.st_mode)) { fprintf(stderr, "%s is not a block device!\n", arg); return OTHER_ERROR; } close(device_fd); nla_put_string(msg, ad->nla_type, arg); return NO_ERROR; } static int conv_md_idx(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg) { int idx; if(!strcmp(arg,"internal")) idx = DRBD_MD_INDEX_FLEX_INT; else if(!strcmp(arg,"flexible")) idx = DRBD_MD_INDEX_FLEX_EXT; else idx = m_strtoll(arg,1); nla_put_u32(msg, ad->nla_type, idx); return NO_ERROR; } static int conv_u32(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg) { unsigned int i = m_strtoll(arg, 1); nla_put_u32(msg, ad->nla_type, i); return NO_ERROR; } static void resolv6(const char *name, struct sockaddr_in6 *addr) { struct addrinfo hints, *res, *tmp; int err; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; err = getaddrinfo(name, 0, &hints, &res); if (err) { fprintf(stderr, "getaddrinfo %s: %s\n", name, gai_strerror(err)); exit(20); } /* Yes, it is a list. We use only the first result. The loop is only * there to document that we know it is a list */ for (tmp = res; tmp; tmp = tmp->ai_next) { memcpy(addr, tmp->ai_addr, sizeof(*addr)); break; } freeaddrinfo(res); if (0) { /* debug output */ char ip[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &addr->sin6_addr, ip, sizeof(ip)); fprintf(stderr, "%s -> %02x %04x %08x %s %08x\n", name, addr->sin6_family, addr->sin6_port, addr->sin6_flowinfo, ip, addr->sin6_scope_id); } } static unsigned long resolv(const char* name) { unsigned long retval; if((retval = inet_addr(name)) == INADDR_NONE ) { struct hostent *he; he = gethostbyname(name); if (!he) { fprintf(stderr, "can not resolve the hostname: gethostbyname(%s): %s\n", name, hstrerror(h_errno)); exit(20); } retval = ((struct in_addr *)(he->h_addr_list[0]))->s_addr; } return retval; } static void split_ipv6_addr(char **address, int *port) { /* ipv6:[fe80::0234:5678:9abc:def1]:8000; */ char *b = strrchr(*address,']'); if (address[0][0] != '[' || b == NULL || (b[1] != ':' && b[1] != '\0')) { fprintf(stderr, "unexpected ipv6 format: %s\n", *address); exit(20); } *b = 0; *address += 1; /* skip '[' */ if (b[1] == ':') *port = m_strtoll(b+2,1); /* b+2: "]:" */ else *port = 7788; /* will we ever get rid of that default port? */ } static void split_address(int *af, char** address, int* port) { static struct { char* text; int af; } afs[] = { { "ipv4:", AF_INET }, { "ipv6:", AF_INET6 }, { "sdp:", AF_INET_SDP }, { "ssocks:", -1 }, }; unsigned int i; char *b; *af=AF_INET; for (i=0; isin6_port = htons(port); /* sin6->sin6_len = sizeof(*sin6); */ return sizeof(*sin6); } else { /* AF_INET, AF_SDP, AF_SSOCKS, * all use the IPv4 addressing scheme */ struct sockaddr_in *sin = (struct sockaddr_in *)storage; memset(sin, 0, sizeof(*sin)); sin->sin_port = htons(port); sin->sin_family = af; sin->sin_addr.s_addr = resolv(address); return sizeof(*sin); } return 0; } static int conv_addr(struct drbd_argument *ad, struct msg_buff *msg, struct drbd_genlmsghdr *dhdr, char* arg) { struct sockaddr_storage x; int addr_len; if (strncmp(arg, "local:", 6) == 0) arg += 6; else if (strncmp(arg, "peer:", 5) == 0) arg += 5; addr_len = sockaddr_from_str(&x, arg); if (addr_len == 0) { fprintf(stderr, "does not look like an endpoint address '%s'", arg); return OTHER_ERROR; } nla_put(msg, ad->nla_type, addr_len, &x); return NO_ERROR; } /* It will only print the WARNING if the warn flag is set with the _first_ call! */ #define PROC_NET_AF_SCI_FAMILY "/proc/net/af_sci/family" #define PROC_NET_AF_SSOCKS_FAMILY "/proc/net/af_ssocks/family" static int get_af_ssocks(int warn_and_use_default) { char buf[16]; int c, fd; static int af = -1; if (af > 0) return af; fd = open(PROC_NET_AF_SSOCKS_FAMILY, O_RDONLY); if (fd < 0) fd = open(PROC_NET_AF_SCI_FAMILY, O_RDONLY); if (fd < 0) { if (warn_and_use_default) { fprintf(stderr, "open(" PROC_NET_AF_SSOCKS_FAMILY ") " "failed: %m\n WARNING: assuming AF_SSOCKS = 27. " "Socket creation may fail.\n"); af = 27; } return af; } c = read(fd, buf, sizeof(buf)-1); if (c > 0) { buf[c] = 0; if (buf[c-1] == '\n') buf[c-1] = 0; af = m_strtoll(buf,1); } else { if (warn_and_use_default) { fprintf(stderr, "read(" PROC_NET_AF_SSOCKS_FAMILY ") " "failed: %m\n WARNING: assuming AF_SSOCKS = 27. " "Socket creation may fail.\n"); af = 27; } } close(fd); return af; } static struct option *make_longoptions(struct drbd_cmd *cm) { static struct option buffer[47]; int i = 0; int primary_force_index = -1; int connect_tentative_index = -1; if (cm->ctx) { struct field_def *field; /* * Make sure to keep cm->ctx->fields first: we use the index * returned by getopt_long() to access cm->ctx->fields. */ for (field = cm->ctx->fields; field->name; field++) { assert(i < ARRAY_SIZE(buffer)); buffer[i].name = field->name; buffer[i].has_arg = field->argument_is_optional ? optional_argument : required_argument; buffer[i].flag = NULL; buffer[i].val = 0; if (!strcmp(cm->cmd, "primary") && !strcmp(field->name, "force")) primary_force_index = i; if (!strcmp(cm->cmd, "connect") && !strcmp(field->name, "tentative")) connect_tentative_index = i; i++; } assert(field - cm->ctx->fields == i); } if (cm->options) { struct option *option; for (option = cm->options; option->name; option++) { assert(i < ARRAY_SIZE(buffer)); buffer[i] = *option; i++; } } if (primary_force_index != -1) { /* * For backward compatibility, add --overwrite-data-of-peer as * an alias to --force. */ assert(i < ARRAY_SIZE(buffer)); buffer[i] = buffer[primary_force_index]; buffer[i].name = "overwrite-data-of-peer"; buffer[i].val = 1000 + primary_force_index; i++; } if (connect_tentative_index != -1) { /* * For backward compatibility, add --dry-run as an alias to * --tentative. */ assert(i < ARRAY_SIZE(buffer)); buffer[i] = buffer[connect_tentative_index]; buffer[i].name = "dry-run"; buffer[i].val = 1000 + connect_tentative_index; i++; } if (cm->set_defaults) { assert(i < ARRAY_SIZE(buffer)); buffer[i].name = "set-defaults"; buffer[i].has_arg = 0; buffer[i].flag = NULL; buffer[i].val = '('; i++; } assert(i < ARRAY_SIZE(buffer)); buffer[i].name = NULL; buffer[i].has_arg = 0; buffer[i].flag = NULL; buffer[i].val = 0; return buffer; } /* prepends global objname to output (if any) */ static int check_error(int err_no, char *desc) { int rv = 0; if (err_no == NO_ERROR || err_no == SS_SUCCESS) return 0; if (err_no == OTHER_ERROR) { if (desc) fprintf(stderr,"%s: %s\n", objname, desc); return 20; } if ( ( err_no >= AFTER_LAST_ERR_CODE || err_no <= ERR_CODE_BASE ) && ( err_no > SS_CW_NO_NEED || err_no <= SS_AFTER_LAST_ERROR) ) { fprintf(stderr,"%s: Error code %d unknown.\n" "You should update the drbd userland tools.\n", objname, err_no); rv = 20; } else { if(err_no > ERR_CODE_BASE ) { fprintf(stderr,"%s: Failure: (%d) %s\n", objname, err_no, desc ?: error_to_string(err_no)); rv = 10; } else if (err_no == SS_UNKNOWN_ERROR) { fprintf(stderr,"%s: State change failed: (%d)" "unknown error.\n", objname, err_no); rv = 11; } else if (err_no > SS_TWO_PRIMARIES) { // Ignore SS_SUCCESS, SS_NOTHING_TO_DO, SS_CW_Success... } else { fprintf(stderr,"%s: State change failed: (%d) %s\n", objname, err_no, drbd_set_st_err_str(err_no)); if (err_no == SS_NO_UP_TO_DATE_DISK) { /* all available disks are inconsistent, * or I am consistent, but cannot outdate the peer. */ rv = 17; } else if (err_no == SS_LOWER_THAN_OUTDATED) { /* was inconsistent anyways */ rv = 5; } else if (err_no == SS_NO_LOCAL_DISK) { /* Can not start resync, no local disks, try with drbdmeta */ rv = 16; } else { rv = 11; } } } if (global_attrs[DRBD_NLA_CFG_REPLY] && global_attrs[DRBD_NLA_CFG_REPLY]->nla_len) { struct nlattr *nla; int rem; fprintf(stderr, "additional info from kernel:\n"); nla_for_each_nested(nla, global_attrs[DRBD_NLA_CFG_REPLY], rem) { if (nla_type(nla) == __nla_type(T_info_text)) fprintf(stderr, "%s\n", (char*)nla_data(nla)); } } return rv; } static void warn_print_excess_args(int argc, char **argv, int i) { fprintf(stderr, "Excess arguments:"); for (; i < argc; i++) fprintf(stderr, " %s", argv[i]); printf("\n"); } int drbd_tla_parse(struct nlmsghdr *nlh) { return nla_parse(global_attrs, ARRAY_SIZE(drbd_tla_nl_policy)-1, nlmsg_attrdata(nlh, GENL_HDRLEN + drbd_genl_family.hdrsize), nlmsg_attrlen(nlh, GENL_HDRLEN + drbd_genl_family.hdrsize), drbd_tla_nl_policy); } #define ASSERT(exp) if (!(exp)) \ fprintf(stderr,"ASSERT( " #exp " ) in %s:%d\n", __FILE__,__LINE__); static int _generic_config_cmd(struct drbd_cmd *cm, int argc, char **argv) { struct drbd_argument *ad; struct nlattr *nla; struct option *options; int c, i; int rv; char *desc = NULL; /* error description from kernel reply message */ struct drbd_genlmsghdr *dhdr; struct msg_buff *smsg; struct iovec iov; struct nlmsghdr *nlh; struct drbd_genlmsghdr *dh; struct timespec retry_timeout = { .tv_nsec = 62500000L, /* 1/16 second */ }; /* pre allocate request message and reply buffer */ iov.iov_len = DEFAULT_MSG_SIZE; iov.iov_base = malloc(iov.iov_len); smsg = msg_new(DEFAULT_MSG_SIZE); if (!smsg || !iov.iov_base) { desc = "could not allocate netlink messages"; rv = OTHER_ERROR; goto error; } nlh = (struct nlmsghdr*)iov.iov_base; dh = genlmsg_data(nlmsg_data(nlh)); dhdr = genlmsg_put(smsg, &drbd_genl_family, 0, cm->cmd_id); dhdr->minor = -1; dhdr->flags = 0; if (context & CTX_MINOR) dhdr->minor = minor; if (context & ~CTX_MINOR) { nla = nla_nest_start(smsg, DRBD_NLA_CFG_CONTEXT); if (context & CTX_RESOURCE) nla_put_string(smsg, T_ctx_resource_name, objname); if (context & CTX_PEER_NODE_ID) nla_put_u32(smsg, T_ctx_peer_node_id, global_ctx.ctx_peer_node_id); if (context & CTX_VOLUME) nla_put_u32(smsg, T_ctx_volume, global_ctx.ctx_volume); nla_nest_end(smsg, nla); } nla = NULL; options = make_longoptions(cm); optind = 0; /* reset getopt_long() */ for (;;) { int idx; c = getopt_long(argc, argv, "(", options, &idx); if (c == -1) break; if (c >= 1000) { /* This is a field alias. */ idx = c - 1000; c = 0; } if (c == 0) { struct field_def *field = &cm->ctx->fields[idx]; assert (field->name == options[idx].name); if (!nla) { assert (cm->tla_id != NO_PAYLOAD); nla = nla_nest_start(smsg, cm->tla_id); } if (!field->ops->put(cm->ctx, field, smsg, optarg)) { fprintf(stderr, "Option --%s: invalid " "argument '%s'\n", field->name, optarg); rv = OTHER_ERROR; goto error; } } else if (c == '(') dhdr->flags |= DRBD_GENL_F_SET_DEFAULTS; else { rv = OTHER_ERROR; goto error; } } for (i = optind, ad = cm->drbd_args; ad && ad->name; i++) { if (argc < i + 1) { fprintf(stderr, "Missing argument '%s'\n", ad->name); print_command_usage(cm, FULL); rv = OTHER_ERROR; goto error; } if (!nla) { assert (cm->tla_id != NO_PAYLOAD); nla = nla_nest_start(smsg, cm->tla_id); } rv = ad->convert_function(ad, smsg, dhdr, argv[i]); if (rv != NO_ERROR) goto error; ad++; } /* dhdr->minor may have been set by one of the convert functions. */ minor = dhdr->minor; if (nla) nla_nest_end(smsg, nla); /* argc should be cmd + n options + n args; * if it is more, we did not understand some */ if (i < argc) { warn_print_excess_args(argc, argv, i); rv = OTHER_ERROR; goto error; } if (strcmp(cm->cmd, "new-minor") == 0) { /* HACK */ /* hack around "sysfs: cannot create duplicate filename '/devices/virtual/bdi/147:0'" * and subsequent NULL deref in kernel below add_disk(). */ struct stat sb; char buf[PATH_MAX]; int i; int c = 0; for (i = 0; i <= 2; i++) { if (i == 0) c = snprintf(buf, PATH_MAX, "/sys/devices/virtual/block/drbd%u", minor); if (i == 1) c = snprintf(buf, PATH_MAX, "/sys/devices/virtual/bdi/147:%u", minor); if (i == 2) c = snprintf(buf, PATH_MAX, "/sys/block/drbd%u", minor); if (c < PATH_MAX) { if (lstat(buf, &sb) == 0) { syslog(LOG_ERR, "new-minor %s %u %u: sysfs node '%s' (already? still?) exists\n", objname, minor, global_ctx.ctx_volume, buf); fprintf(stderr, "new-minor %s %u %u: sysfs node '%s' (already? still?) exists\n", objname, minor, global_ctx.ctx_volume, buf); rv = ERR_MINOR_OR_VOLUME_EXISTS; desc = NULL; goto error; } } } } for(;;) { if (genl_send(drbd_sock, smsg)) { desc = "error sending config command"; rv = OTHER_ERROR; goto error; } do { int received; /* reduce timeout! limit retries */ received = genl_recv_msgs(drbd_sock, &iov, &desc, 120000); if (received <= 0) { if (received == -E_RCV_ERROR_REPLY && !errno) continue; if (!desc) desc = "error receiving config reply"; rv = OTHER_ERROR; goto error; } } while (false); ASSERT(dh->minor == minor); rv = dh->ret_code; if (rv != SS_IN_TRANSIENT_STATE) break; nanosleep(&retry_timeout, NULL); /* Double the timeout, up to 10 seconds. */ if (retry_timeout.tv_sec < 10) { retry_timeout.tv_sec *= 2; retry_timeout.tv_nsec *= 2; if (retry_timeout.tv_nsec > 1000000000L) { retry_timeout.tv_sec++; retry_timeout.tv_nsec -= 1000000000L; } } } if (rv == ERR_RES_NOT_KNOWN) { if (cm->warn_on_missing && isatty(STDERR_FILENO)) fprintf(stderr, "Resource unknown\n"); if (cm->missing_ok) rv = NO_ERROR; } drbd_tla_parse(nlh); error: msg_free(smsg); rv = check_error(rv, desc); free(iov.iov_base); return rv; } static int generic_config_cmd(struct drbd_cmd *cm, int argc, char **argv) { return _generic_config_cmd(cm, argc, argv); } static int del_minor_cmd(struct drbd_cmd *cm, int argc, char **argv) { int rv; rv = generic_config_cmd(cm, argc, argv); if (!rv) unregister_minor(minor); return rv; } static int del_resource_cmd(struct drbd_cmd *cm, int argc, char **argv) { int rv; rv = generic_config_cmd(cm, argc, argv); if (!rv) unregister_resource(objname); return rv; } static struct drbd_cmd *find_cmd_by_name(const char *name) { unsigned int i; for (i = 0; i < ARRAY_SIZE(commands); i++) { if (!strcmp(name, commands[i].cmd)) { return commands + i; } } return NULL; } static void print_options(struct nlattr *attr, struct context_def *ctx, const char *sect_name) { struct field_def *field; int opened = 0; if (!attr) return; if (drbd_nla_parse_nested(nested_attr_tb, ctx->nla_policy_size - 1, attr, ctx->nla_policy)) { fprintf(stderr, "nla_policy violation for %s payload!\n", sect_name); /* still, print those that validated ok */ } for (field = ctx->fields; field->name; field++) { struct nlattr *nlattr; const char *str; bool is_default; nlattr = ntb(field->nla_type); if (!nlattr) continue; str = field->ops->get(ctx, field, nlattr); is_default = field->ops->is_default(field, str); if (is_default && !show_defaults) continue; if (!opened) { opened=1; printI("%s {\n",sect_name); ++indent; } if (field->needs_double_quoting) str = double_quote_string(str); printI("%-16s\t%s;",field->name, str); if (field->unit || is_default) { printf(" # "); if (field->unit) printf("%s", field->unit); if (field->unit && is_default) printf(", "); if (is_default) printf("default"); } printf("\n"); } if(opened) { --indent; printI("}\n"); } } struct choose_timeout_ctx { struct drbd_cfg_context ctx; struct msg_buff *smsg; struct iovec *iov; int timeout; int wfc_timeout; int degr_wfc_timeout; int outdated_wfc_timeout; }; int choose_timeout(struct choose_timeout_ctx *ctx) { char *desc = NULL; struct drbd_genlmsghdr *dhdr; struct nlattr *nla; int rr; if (0 < ctx->wfc_timeout && (ctx->wfc_timeout < ctx->degr_wfc_timeout || ctx->degr_wfc_timeout == 0)) { ctx->degr_wfc_timeout = ctx->wfc_timeout; fprintf(stderr, "degr-wfc-timeout has to be shorter than wfc-timeout\n" "degr-wfc-timeout implicitly set to wfc-timeout (%ds)\n", ctx->degr_wfc_timeout); } if (0 < ctx->degr_wfc_timeout && (ctx->degr_wfc_timeout < ctx->outdated_wfc_timeout || ctx->outdated_wfc_timeout == 0)) { ctx->outdated_wfc_timeout = ctx->wfc_timeout; fprintf(stderr, "outdated-wfc-timeout has to be shorter than degr-wfc-timeout\n" "outdated-wfc-timeout implicitly set to degr-wfc-timeout (%ds)\n", ctx->degr_wfc_timeout); } dhdr = genlmsg_put(ctx->smsg, &drbd_genl_family, 0, DRBD_ADM_GET_TIMEOUT_TYPE); dhdr->minor = -1; dhdr->flags = 0; nla = nla_nest_start(ctx->smsg, DRBD_NLA_CFG_CONTEXT); nla_put_string(ctx->smsg, T_ctx_resource_name, ctx->ctx.ctx_resource_name); nla_put_u32(ctx->smsg, T_ctx_peer_node_id, ctx->ctx.ctx_peer_node_id); nla_put_u32(ctx->smsg, T_ctx_volume, ctx->ctx.ctx_volume); nla_nest_end(ctx->smsg, nla); if (genl_send(drbd_sock, ctx->smsg)) { desc = "error sending config command"; goto error; } rr = genl_recv_msgs(drbd_sock, ctx->iov, &desc, 120000); if (rr > 0) { struct nlmsghdr *nlh = (struct nlmsghdr*)ctx->iov->iov_base; struct genl_info info = { .seq = nlh->nlmsg_seq, .nlhdr = nlh, .genlhdr = nlmsg_data(nlh), .userhdr = genlmsg_data(nlmsg_data(nlh)), .attrs = global_attrs, }; struct drbd_genlmsghdr *dh = info.userhdr; struct timeout_parms parms; rr = dh->ret_code; if (rr == ERR_MINOR_INVALID) { desc = "minor not available"; goto error; } if (rr != NO_ERROR) goto error; if (drbd_tla_parse(nlh) || timeout_parms_from_attrs(&parms, &info)) { desc = "reply did not validate - " "do you need to upgrade your userland tools?"; goto error; } rr = parms.timeout_type; ctx->timeout = (rr == UT_DEGRADED) ? ctx->degr_wfc_timeout : (rr == UT_PEER_OUTDATED) ? ctx->outdated_wfc_timeout : ctx->wfc_timeout; return 0; } error: if (!desc) desc = "error receiving netlink reply"; fprintf(stderr, "error determining which timeout to use: %s\n", desc); return 20; } #include static bool kernel_older_than(int version, int patchlevel, int sublevel) { struct utsname utsname; char *rel; int l; if (uname(&utsname) != 0) return false; rel = utsname.release; l = strtol(rel, &rel, 10); if (l > version) return false; else if (l < version || *rel == 0) return true; l = strtol(rel + 1, &rel, 10); if (l > patchlevel) return false; else if (l < patchlevel || *rel == 0) return true; l = strtol(rel + 1, &rel, 10); if (l >= sublevel) return false; return true; } static int shortest_timeout(struct peer_devices_list *peer_devices) { struct peer_devices_list *peer_device; int timeout = -1; /* There is no point waiting for peers I do not even know about. */ if (peer_devices == NULL) return 1; for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { if (peer_device->timeout_ms > 0 && (peer_device->timeout_ms < timeout || timeout == -1)) timeout = peer_device->timeout_ms; } return timeout; } static bool update_timeouts(struct peer_devices_list *peer_devices, int elapsed) { struct peer_devices_list *peer_device; bool all_expired = true; for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { if (peer_device->timeout_ms != -1) { peer_device->timeout_ms -= elapsed; if (peer_device->timeout_ms < 0) peer_device->timeout_ms = 0; } if (peer_device->timeout_ms != 0) all_expired = false; } return all_expired; } static bool parse_color_argument(void) { if (!optarg || !strcmp(optarg, "always")) opt_color = ALWAYS_COLOR; else if (!strcmp(optarg, "never")) opt_color = NEVER_COLOR; else if (!strcmp(optarg, "auto")) opt_color = AUTO_COLOR; else return 0; return 1; } static bool opt_now; static bool opt_verbose; static bool opt_statistics; static bool opt_timestamps; static int generic_get(struct drbd_cmd *cm, int timeout_arg, void *u_ptr) { char *desc = NULL; struct drbd_genlmsghdr *dhdr; struct msg_buff *smsg; struct iovec iov; int timeout_ms, flags; int rv = NO_ERROR; int err = 0; /* pre allocate request message and reply buffer */ iov.iov_len = DEFAULT_MSG_SIZE; iov.iov_base = malloc(iov.iov_len); smsg = msg_new(DEFAULT_MSG_SIZE); if (!smsg || !iov.iov_base) { desc = "could not allocate netlink messages"; rv = OTHER_ERROR; goto out; } if (cm->continuous_poll) { /* also always (try to) listen to nlctrl notify, * so we have a chance to notice rmmod. */ int id = GENL_ID_CTRL; setsockopt(drbd_sock->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &id, sizeof(id)); if (genl_join_mc_group(drbd_sock, "events") && !kernel_older_than(2, 6, 23)) { desc = "unable to join drbd events multicast group"; rv = OTHER_ERROR; goto out2; } } flags = 0; if (minor == -1U) flags |= NLM_F_DUMP; dhdr = genlmsg_put(smsg, &drbd_genl_family, flags, cm->cmd_id); dhdr->minor = minor; dhdr->flags = 0; if (minor == -1U && strcmp(objname, "all")) { /* Restrict the dump to a single resource. */ struct nlattr *nla; nla = nla_nest_start(smsg, DRBD_NLA_CFG_CONTEXT); nla_put_string(smsg, T_ctx_resource_name, objname); nla_nest_end(smsg, nla); } if (genl_send(drbd_sock, smsg)) { desc = "error sending config command"; rv = OTHER_ERROR; goto out2; } /* disable sequence number check in genl_recv_msgs */ drbd_sock->s_seq_expect = 0; for (;;) { int received, rem, ret; struct nlmsghdr *nlh = (struct nlmsghdr *)iov.iov_base; struct timeval before; struct pollfd pollfds[2] = { [0] = { .fd = 1, .events = POLLHUP, }, [1] = { .fd = drbd_sock->s_fd, .events = POLLIN, }, }; gettimeofday(&before, NULL); timeout_ms = timeout_arg == MULTIPLE_TIMEOUTS ? shortest_timeout(u_ptr) : timeout_arg; ret = poll(pollfds, 2, timeout_ms); if (ret == 0) { err = 5; goto out2; } if (pollfds[0].revents == POLLERR || pollfds[0].revents == POLLHUP) goto out2; received = genl_recv_msgs(drbd_sock, &iov, &desc, -1); if (received < 0) { switch(received) { case E_RCV_TIMEDOUT: err = 5; goto out2; case -E_RCV_FAILED: err = 20; goto out2; case -E_RCV_NO_SOURCE_ADDR: continue; /* ignore invalid message */ case -E_RCV_SEQ_MISMATCH: /* we disabled it, so it should not happen */ err = 20; goto out2; case -E_RCV_MSG_TRUNC: continue; case -E_RCV_UNEXPECTED_TYPE: continue; case -E_RCV_NLMSG_DONE: if (cm->continuous_poll) continue; err = cm->show_function(cm, NULL, u_ptr); if (err) goto out2; err = -*(int*)nlmsg_data(nlh); if (err && (err != ENODEV || !cm->missing_ok)) { fprintf(stderr, "received netlink error reply: %s\n", strerror(err)); err = 20; } goto out2; case -E_RCV_ERROR_REPLY: if (!errno) /* positive ACK message */ continue; if (!desc) desc = strerror(errno); fprintf(stderr, "received netlink error reply: %s\n", desc); err = 20; goto out2; default: if (!desc) desc = "error receiving config reply"; err = 20; goto out2; } } if (timeout_ms != -1) { struct timeval after; int elapsed_ms; bool exit; gettimeofday(&after, NULL); elapsed_ms = (after.tv_sec - before.tv_sec) * 1000 + (after.tv_usec - before.tv_usec) / 1000; if (timeout_arg == MULTIPLE_TIMEOUTS) { exit = update_timeouts(u_ptr, elapsed_ms); } else { timeout_ms -= elapsed_ms; exit = timeout_ms <= 0; } if (exit) { err = 5; goto out2; } } /* There may be multiple messages in one datagram (for dump replies). */ nlmsg_for_each_msg(nlh, nlh, received, rem) { struct drbd_genlmsghdr *dh = genlmsg_data(nlmsg_data(nlh)); struct genl_info info = (struct genl_info){ .seq = nlh->nlmsg_seq, .nlhdr = nlh, .genlhdr = nlmsg_data(nlh), .userhdr = genlmsg_data(nlmsg_data(nlh)), .attrs = global_attrs, }; dbg(3, "received type:%x\n", nlh->nlmsg_type); if (nlh->nlmsg_type < NLMSG_MIN_TYPE) { /* Ignore netlink control messages. */ continue; } if (nlh->nlmsg_type == GENL_ID_CTRL) { #ifdef HAVE_CTRL_CMD_DELMCAST_GRP dbg(3, "received cmd:%x\n", info.genlhdr->cmd); if (info.genlhdr->cmd == CTRL_CMD_DELMCAST_GRP) { struct nlattr *nla = nlmsg_find_attr(nlh, GENL_HDRLEN, CTRL_ATTR_FAMILY_ID); if (nla && nla_get_u16(nla) == drbd_genl_family.id) { /* FIXME: We could wait for the multicast group to be recreated ... */ goto out2; } } #endif /* Ignore other generic netlink control messages. */ continue; } if (nlh->nlmsg_type != drbd_genl_family.id) { /* Ignore messages for all other netlink families. */ continue; } /* parse early, otherwise drbd_cfg_context_from_attrs * can not work */ if (drbd_tla_parse(nlh)) { /* FIXME * should continuous_poll continue? */ desc = "reply did not validate - " "do you need to upgrade your userland tools?"; rv = OTHER_ERROR; goto out2; } if (cm->continuous_poll) { struct drbd_cfg_context ctx; /* * We will receive all events and have to * filter for what we want ourself. */ /* FIXME * Do we want to ignore broadcasts until the * initial get/dump requests is done? */ if (!drbd_cfg_context_from_attrs(&ctx, &info)) { switch ((int)cm->ctx_key) { case CTX_MINOR: /* Assert that, for an unicast reply, * reply minor matches request minor. * "unsolicited" kernel broadcasts are "pid=0" (netlink "port id") * (and expected to be genlmsghdr.cmd == DRBD_EVENT) */ if (minor != dh->minor) { if (info.nlhdr->nlmsg_pid != 0) dbg(1, "received netlink packet for minor %u, while expecting %u\n", dh->minor, minor); continue; } break; case CTX_PEER_DEVICE: if (ctx.ctx_volume != global_ctx.ctx_volume) continue; /* also needs to match the connection, of course */ case CTX_PEER_NODE: if (ctx.ctx_peer_node_id != global_ctx.ctx_peer_node_id) continue; /* also needs to match the resource, of course */ case CTX_RESOURCE: case CTX_RESOURCE | CTX_ALL: if (!strcmp(objname, "all")) break; if (strcmp(objname, ctx.ctx_resource_name)) continue; break; default: fprintf(stderr, "DRECK: %x\n", cm->ctx_key); assert(0); } } } rv = dh->ret_code; if (rv == ERR_MINOR_INVALID && cm->missing_ok) rv = NO_ERROR; if (rv != NO_ERROR) goto out2; err = cm->show_function(cm, &info, u_ptr); if (err) { if (err < 0) err = 0; goto out2; } } if (!cm->continuous_poll && !(flags & NLM_F_DUMP)) { /* There will be no more reply packets. */ err = cm->show_function(cm, NULL, u_ptr); goto out2; } } out2: msg_free(smsg); out: if (!err) err = check_error(rv, desc); free(iov.iov_base); return err; } static int generic_get_cmd(struct drbd_cmd *cm, int argc, char **argv) { static struct option no_options[] = { { } }; struct choose_timeout_ctx timeo_ctx = { .wfc_timeout = DRBD_WFC_TIMEOUT_DEF, .degr_wfc_timeout = DRBD_DEGR_WFC_TIMEOUT_DEF, .outdated_wfc_timeout = DRBD_OUTDATED_WFC_TIMEOUT_DEF, }; int c, timeout_ms, err = NO_ERROR; struct peer_devices_list *peer_devices = NULL; struct option *options = cm->options ? cm->options : no_options; const char *opts = make_optstring(options); optind = 0; /* reset getopt_long() */ for(;;) { c = getopt_long(argc, argv, opts, options, 0); if (c == -1) break; switch(c) { default: case '?': return 20; case 't': timeo_ctx.wfc_timeout = m_strtoll(optarg, 1); if(DRBD_WFC_TIMEOUT_MIN > timeo_ctx.wfc_timeout || timeo_ctx.wfc_timeout > DRBD_WFC_TIMEOUT_MAX) { fprintf(stderr, "wfc_timeout => %d" " out of range [%d..%d]\n", timeo_ctx.wfc_timeout, DRBD_WFC_TIMEOUT_MIN, DRBD_WFC_TIMEOUT_MAX); return 20; } break; case 'd': timeo_ctx.degr_wfc_timeout = m_strtoll(optarg, 1); if(DRBD_DEGR_WFC_TIMEOUT_MIN > timeo_ctx.degr_wfc_timeout || timeo_ctx.degr_wfc_timeout > DRBD_DEGR_WFC_TIMEOUT_MAX) { fprintf(stderr, "degr_wfc_timeout => %d" " out of range [%d..%d]\n", timeo_ctx.degr_wfc_timeout, DRBD_DEGR_WFC_TIMEOUT_MIN, DRBD_DEGR_WFC_TIMEOUT_MAX); return 20; } break; case 'o': timeo_ctx.outdated_wfc_timeout = m_strtoll(optarg, 1); if(DRBD_OUTDATED_WFC_TIMEOUT_MIN > timeo_ctx.outdated_wfc_timeout || timeo_ctx.outdated_wfc_timeout > DRBD_OUTDATED_WFC_TIMEOUT_MAX) { fprintf(stderr, "outdated_wfc_timeout => %d" " out of range [%d..%d]\n", timeo_ctx.outdated_wfc_timeout, DRBD_OUTDATED_WFC_TIMEOUT_MIN, DRBD_OUTDATED_WFC_TIMEOUT_MAX); return 20; } break; case 'n': opt_now = true; break; case 's': opt_verbose = true; opt_statistics = true; break; case 'w': if (!optarg || !strcmp(optarg, "yes")) wait_after_split_brain = true; break; case 'D': show_defaults = true; break; case 'T': opt_timestamps = true; break; case 'c': if (!parse_color_argument()) print_usage_and_exit("unknown --color argument"); break; } } if (optind < argc) { warn_print_excess_args(argc, argv, optind + 1); return 20; } timeout_ms = -1; if (cm->show_function == &wait_for_family) { struct peer_devices_list *peer_device; struct msg_buff *smsg; struct iovec iov; int rr; char *res_name = cm->ctx_key & CTX_RESOURCE ? objname : "all"; peer_devices = list_peer_devices(res_name); /* if there are no peer devices, we don't wait by definition */ if (!peer_devices) return 0; iov.iov_len = DEFAULT_MSG_SIZE; iov.iov_base = malloc(iov.iov_len); smsg = msg_new(DEFAULT_MSG_SIZE); if (!smsg || !iov.iov_base) { msg_free(smsg); free(iov.iov_base); fprintf(stderr, "could not allocate netlink messages\n"); return 20; } timeo_ctx.smsg = smsg; timeo_ctx.iov = &iov; for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { timeo_ctx.ctx = peer_device->ctx; rr = choose_timeout(&timeo_ctx); if (rr) return rr; peer_device->timeout_ms = timeo_ctx.timeout ? timeo_ctx.timeout * 1000 : -1; /* rewind send message buffer */ smsg->tail = smsg->data; } msg_free(smsg); free(iov.iov_base); timeout_ms = MULTIPLE_TIMEOUTS; } if (!cm->continuous_poll) timeout_ms = 120000; /* normal "get" request, or "show" */ err = generic_get(cm, timeout_ms, peer_devices); free_peer_devices(peer_devices); return err; } static bool options_empty(struct nlattr *attr, struct context_def *ctx) { struct field_def *field; if (!attr) return true; if (drbd_nla_parse_nested(nested_attr_tb, ctx->nla_policy_size - 1, attr, ctx->nla_policy)) { fprintf(stderr, "nla_policy violation\n"); } for (field = ctx->fields; field->name; field++) { struct nlattr *nlattr; const char *str; bool is_default; nlattr = ntb(field->nla_type); if (!nlattr) continue; str = field->ops->get(ctx, field, nlattr); is_default = field->ops->is_default(field, str); if (is_default && !show_defaults) continue; return false; } return true; } static void show_peer_device(struct peer_devices_list *peer_device) { if (options_empty(peer_device->peer_device_conf, &peer_device_options_ctx)) return; printI("volume %d {\n", peer_device->ctx.ctx_volume); ++indent; print_options(peer_device->peer_device_conf, &peer_device_options_ctx, "disk"); --indent; printI("}\n"); } static void print_paths(struct connections_list *connection) { char address[ADDRESS_STR_MAX]; char *colon; struct nlattr *nla; int tmp; if (!connection->path_list) return; nla_for_each_nested(nla, connection->path_list, tmp) { int l = nla_len(nla); if (!address_str(address, nla_data(nla), l)) continue; colon = strchr(address, ':'); if (colon) *colon = ' '; if (nla->nla_type == T_my_addr) { printI("path {\n"); ++indent; printI("_this_host %s;\n", address); } if (nla->nla_type == T_peer_addr) { printI("_remote_host %s;\n", address); --indent; printI("}\n"); } } } static void show_connection(struct connections_list *connection, struct peer_devices_list *peer_devices) { struct peer_devices_list *peer_device; printI("connection {\n"); ++indent; printI("_peer_node_id %d;\n", connection->ctx.ctx_peer_node_id); print_paths(connection); if (connection->info.conn_connection_state == C_STANDALONE) printI("_is_standalone;\n"); print_options(connection->net_conf, &show_net_options_ctx, "net"); for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { if (connection->ctx.ctx_peer_node_id == peer_device->ctx.ctx_peer_node_id) show_peer_device(peer_device); } --indent; printI("}\n"); } static void show_volume(struct devices_list *device) { printI("volume %d {\n", device->ctx.ctx_volume); ++indent; printI("device\t\t\tminor %d;\n", device->minor); if (device->disk_conf.backing_dev[0]) { printI("disk\t\t\t\"%s\";\n", device->disk_conf.backing_dev); printI("meta-disk\t\t\t"); switch(device->disk_conf.meta_dev_idx) { case DRBD_MD_INDEX_INTERNAL: case DRBD_MD_INDEX_FLEX_INT: printf("internal;\n"); break; case DRBD_MD_INDEX_FLEX_EXT: printf("%s;\n", double_quote_string(device->disk_conf.meta_dev)); break; default: printf("%s [ %d ];\n", double_quote_string(device->disk_conf.meta_dev), device->disk_conf.meta_dev_idx); } } print_options(device->disk_conf_nl, &attach_cmd_ctx, "disk"); --indent; printI("}\n"); /* close volume */ } static int show_cmd(struct drbd_cmd *cm, int argc, char **argv) { struct resources_list *resources_list, *resource; char *old_objname = objname; int c; optind = 0; /* reset getopt_long() */ for (;;) { c = getopt_long(argc, argv, "D", show_cmd_options, 0); if (c == -1) break; switch(c) { default: case '?': return 20; case 'D': show_defaults = true; break; } } resources_list = sort_resources(list_resources()); if (resources_list == NULL) printf("# No currently configured DRBD found.\n"); for (resource = resources_list; resource; resource = resource->next) { struct devices_list *devices, *device; struct connections_list *connections, *connection; struct peer_devices_list *peer_devices = NULL; struct nlattr *nla; if (strcmp(old_objname, "all") && strcmp(old_objname, resource->name)) continue; devices = list_devices(resource->name); connections = sort_connections(list_connections(resource->name)); if (devices && connections) peer_devices = list_peer_devices(resource->name); objname = resource->name; printI("resource %s {\n", resource->name); ++indent; print_options(resource->res_opts, &resource_options_ctx, "options"); printI("_this_host {\n"); ++indent; nla = nla_find_nested(resource->res_opts, __nla_type(T_node_id)); if (nla) printI("node-id\t\t\t%d;\n", *(uint32_t *)nla_data(nla)); for (device = devices; device; device = device->next) show_volume(device); --indent; printI("}\n"); for (connection = connections; connection; connection = connection->next) show_connection(connection, peer_devices); --indent; printI("}\n\n"); free_connections(connections); free_devices(devices); free_peer_devices(peer_devices); } free(resources_list); objname = old_objname; return 0; } static const char *susp_str(struct resource_info *info) { static char buffer[32]; *buffer = 0; if (info->res_susp) strcat(buffer, ",user" + (*buffer == 0)); if (info->res_susp_nod) strcat(buffer, ",no-data" + (*buffer == 0)); if (info->res_susp_fen) strcat(buffer, ",fencing" + (*buffer == 0)); if (*buffer == 0) strcat(buffer, "no"); return buffer; } int nowrap_printf(int indent, const char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = vprintf(format, ap); va_end(ap); return ret; } void print_resource_statistics(int indent, struct resource_statistics *old, struct resource_statistics *new, int (*wrap_printf)(int, const char *, ...)) { static const char *write_ordering_str[] = { [WO_NONE] = "none", [WO_DRAIN_IO] = "drain", [WO_BDEV_FLUSH] = "flush", [WO_BIO_BARRIER] = "barrier", }; uint32_t wo = new->res_stat_write_ordering; if ((!old || old->res_stat_write_ordering != wo) && wo < ARRAY_SIZE(write_ordering_str) && write_ordering_str[wo]) { wrap_printf(indent, " write-ordering:%s", write_ordering_str[wo]); } } void print_device_statistics(int indent, struct device_statistics *old, struct device_statistics *new, int (*wrap_printf)(int, const char *, ...)) { if (opt_statistics) { if (opt_verbose) wrap_printf(indent, " size:" U64, (uint64_t)new->dev_size / 2); wrap_printf(indent, " read:" U64, (uint64_t)new->dev_read / 2); wrap_printf(indent, " written:" U64, (uint64_t)new->dev_write / 2); if (opt_verbose) { wrap_printf(indent, " al-writes:" U64, (uint64_t)new->dev_al_writes); wrap_printf(indent, " bm-writes:" U64, (uint64_t)new->dev_bm_writes); wrap_printf(indent, " upper-pending:" U32, new->dev_upper_pending); wrap_printf(indent, " lower-pending:" U32, new->dev_lower_pending); if (!old || old->dev_al_suspended != new->dev_al_suspended) wrap_printf(indent, " al-suspended:%s", new->dev_al_suspended ? "yes" : "no"); } } if ((!old || old->dev_upper_blocked != new->dev_upper_blocked || old->dev_lower_blocked != new->dev_lower_blocked) && new->dev_size != -1 && (opt_verbose || new->dev_upper_blocked || new->dev_lower_blocked)) { const char *x1 = "", *x2 = ""; bool first = true; if (new->dev_upper_blocked) { x1 = ",upper" + first; first = false; } if (new->dev_lower_blocked) { x2 = ",lower" + first; first = false; } if (first) x1 = "no"; wrap_printf(indent, " blocked:%s%s", x1, x2); } } void print_connection_statistics(int indent, struct connection_statistics *old, struct connection_statistics *new, int (*wrap_printf)(int, const char *, ...)) { if (!old || old->conn_congested != new->conn_congested) wrap_printf(indent, " congested:%s", new->conn_congested ? "yes" : "no"); } static void peer_device_status_json(struct peer_devices_list *peer_device) { struct peer_device_statistics *s = &peer_device->statistics; printf(" {\n" " \"volume\": %d,\n" " \"replication-state\": \"%s\",\n" " \"peer-disk-state\": \"%s\",\n" " \"resync-suspended\": \"%s\",\n" " \"received\": " U64 ",\n" " \"sent\": " U64 ",\n" " \"out-of-sync\": " U64 ",\n" " \"pending\": " U32 ",\n" " \"unacked\": " U32 "\n", peer_device->ctx.ctx_volume, drbd_repl_str(peer_device->info.peer_repl_state), drbd_disk_str(peer_device->info.peer_disk_state), resync_susp_str(&peer_device->info), (uint64_t)s->peer_dev_received / 2, (uint64_t)s->peer_dev_sent / 2, (uint64_t)s->peer_dev_out_of_sync / 2, s->peer_dev_pending, s->peer_dev_unacked); if (peer_device->info.peer_repl_state >= L_SYNC_SOURCE && peer_device->info.peer_repl_state <= L_PAUSED_SYNC_T) printf(" \"resync-done\": %.2f,\n", 100 * (1 - (double)peer_device->statistics.peer_dev_out_of_sync / (double)peer_device->device->statistics.dev_size)); printf(" }"); } static void connection_status_json(struct connections_list *connection, struct peer_devices_list *peer_devices) { struct peer_devices_list *peer_device; int i = 0; printf(" {\n" " \"peer-node-id\": %d,\n" " \"name\": \"%s\",\n" " \"connection-state\": \"%s\", \n" " \"congested\": %s,\n" " \"peer-role\": \"%s\",\n" " \"peer_devices\": [\n", connection->ctx.ctx_peer_node_id, connection->ctx.ctx_conn_name, drbd_conn_str(connection->info.conn_connection_state), connection->statistics.conn_congested ? "true" : "false", drbd_role_str(connection->info.conn_role)); for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { if (connection->ctx.ctx_peer_node_id != peer_device->ctx.ctx_peer_node_id) continue; if (i) puts(","); peer_device_status_json(peer_device); i++; } printf(" ]\n }"); } static void device_status_json(struct devices_list *device) { printf(" {\n" " \"volume\": %d,\n" " \"minor\": %d,\n" " \"disk-state:\": \"%s\",\n", device->ctx.ctx_volume, device->minor, drbd_disk_str(device->info.dev_disk_state)); if (device->statistics.dev_size != -1) { struct device_statistics *s = &device->statistics; printf(" \"size\": " U64 ",\n" " \"read\": " U64 ",\n" " \"written\": " U64 ",\n" " \"al-writes\": " U64 ",\n" " \"bm-writes\": " U64 ",\n" " \"upper-pending\": " U32 ",\n" " \"lower-pending\": " U32 "\n", (uint64_t)s->dev_size / 2, (uint64_t)s->dev_read / 2, (uint64_t)s->dev_write / 2, (uint64_t)s->dev_al_writes, (uint64_t)s->dev_bm_writes, s->dev_upper_pending, s->dev_lower_pending); } printf(" }"); } static void resource_status_json(struct resources_list *resource) { static const char *write_ordering_str[] = { [WO_NONE] = "none", [WO_DRAIN_IO] = "drain", [WO_BDEV_FLUSH] = "flush", [WO_BIO_BARRIER] = "barrier", }; struct nlattr *nla; int node_id = -1; bool suspended = resource->info.res_susp || resource->info.res_susp_nod || resource->info.res_susp_fen; nla = nla_find_nested(resource->res_opts, __nla_type(T_node_id)); if (nla) node_id = *(uint32_t *)nla_data(nla); printf("{\n" " \"name\": \"%s\",\n" " \"node-id\": %d,\n" " \"role\": \"%s\",\n" " \"suspended\": %s,\n" " \"write-ordering\": \"%s\",\n" " \"devices\": [\n", resource->name, node_id, drbd_role_str(resource->info.res_role), suspended ? "true" : "false", write_ordering_str[resource->statistics.res_stat_write_ordering]); } void print_peer_device_statistics(int indent, struct peer_device_statistics *old, struct peer_device_statistics *new, int (*wrap_printf)(int, const char *, ...)) { wrap_printf(indent, " received:" U64, (uint64_t)new->peer_dev_received / 2); wrap_printf(indent, " sent:" U64, (uint64_t)new->peer_dev_sent / 2); if (opt_verbose || new->peer_dev_out_of_sync) wrap_printf(indent, " out-of-sync:" U64, (uint64_t)new->peer_dev_out_of_sync / 2); if (opt_verbose) { wrap_printf(indent, " pending:" U32, new->peer_dev_pending); wrap_printf(indent, " unacked:" U32, new->peer_dev_unacked); } } void resource_status(struct resources_list *resource) { enum drbd_role role = resource->info.res_role; wrap_printf(0, "%s", resource->name); if (opt_verbose) { struct nlattr *nla; nla = nla_find_nested(resource->res_opts, __nla_type(T_node_id)); if (nla) wrap_printf(4, " node-id:%d", *(uint32_t *)nla_data(nla)); } wrap_printf(4, " role:%s%s%s", role_color_start(role, true), drbd_role_str(role), role_color_stop(role, true)); if (opt_verbose || resource->info.res_susp || resource->info.res_susp_nod || resource->info.res_susp_fen) wrap_printf(4, " suspended:%s", susp_str(&resource->info)); if (opt_statistics && opt_verbose) { wrap_printf(4, "\n"); print_resource_statistics(4, NULL, &resource->statistics, wrap_printf); } wrap_printf(0, "\n"); } static void device_status(struct devices_list *device, bool single_device) { enum drbd_disk_state disk_state = device->info.dev_disk_state; int indent = 2; if (opt_verbose || !(single_device && device->ctx.ctx_volume == 0)) { wrap_printf(indent, "volume:%u", device->ctx.ctx_volume); indent = 6; if (opt_verbose) wrap_printf(indent, " minor:%u", device->minor); } wrap_printf(indent, " disk:%s%s%s", disk_state_color_start(disk_state, true), drbd_disk_str(disk_state), disk_state_color_stop(disk_state, true)); indent = 6; if (device->statistics.dev_size != -1) { if (opt_statistics) wrap_printf(indent, "\n"); print_device_statistics(indent, NULL, &device->statistics, wrap_printf); } wrap_printf(indent, "\n"); } static const char *resync_susp_str(struct peer_device_info *info) { static char buffer[64]; *buffer = 0; if (info->peer_resync_susp_user) strcat(buffer, ",user" + (*buffer == 0)); if (info->peer_resync_susp_peer) strcat(buffer, ",peer" + (*buffer == 0)); if (info->peer_resync_susp_dependency) strcat(buffer, ",dependency" + (*buffer == 0)); if (*buffer == 0) strcat(buffer, "no"); return buffer; } static void peer_device_status(struct peer_devices_list *peer_device, bool single_device) { int indent = 4; if (opt_verbose || !(single_device && peer_device->ctx.ctx_volume == 0)) { wrap_printf(indent, "volume:%d", peer_device->ctx.ctx_volume); indent = 8; } if (opt_verbose || peer_device->info.peer_repl_state > L_ESTABLISHED) { enum drbd_repl_state repl_state = peer_device->info.peer_repl_state; wrap_printf(indent, " replication:%s%s%s", repl_state_color_start(repl_state), drbd_repl_str(repl_state), repl_state_color_stop(repl_state)); indent = 8; } if (opt_verbose || opt_statistics || peer_device->info.peer_repl_state != L_OFF || peer_device->info.peer_disk_state != D_UNKNOWN) { enum drbd_disk_state disk_state = peer_device->info.peer_disk_state; wrap_printf(indent, " peer-disk:%s%s%s", disk_state_color_start(disk_state, false), drbd_disk_str(disk_state), disk_state_color_stop(disk_state, false)); indent = 8; if (peer_device->info.peer_repl_state >= L_SYNC_SOURCE && peer_device->info.peer_repl_state <= L_PAUSED_SYNC_T) { wrap_printf(indent, " done:%.2f", 100 * (1 - (double)peer_device->statistics.peer_dev_out_of_sync / (double)peer_device->device->statistics.dev_size)); } if (opt_verbose || peer_device->info.peer_resync_susp_user || peer_device->info.peer_resync_susp_peer || peer_device->info.peer_resync_susp_dependency) wrap_printf(indent, " resync-suspended:%s", resync_susp_str(&peer_device->info)); if (opt_statistics && peer_device->statistics.peer_dev_received != -1) { wrap_printf(indent, "\n"); print_peer_device_statistics(indent, NULL, &peer_device->statistics, wrap_printf); } } wrap_printf(0, "\n"); } static void peer_devices_status(struct drbd_cfg_context *ctx, struct peer_devices_list *peer_devices, bool single_device) { struct peer_devices_list *peer_device; for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { if (ctx->ctx_peer_node_id != peer_device->ctx.ctx_peer_node_id) continue; peer_device_status(peer_device, single_device); } } static void connection_status(struct connections_list *connection, struct peer_devices_list *peer_devices, bool single_device) { if (connection->ctx.ctx_conn_name_len) wrap_printf(2, "%s", connection->ctx.ctx_conn_name); if (opt_verbose || connection->ctx.ctx_conn_name_len == 0) { int in = connection->ctx.ctx_conn_name_len ? 6 : 2; wrap_printf(in, " node-id:%d", connection->ctx.ctx_peer_node_id); } if (opt_verbose || connection->info.conn_connection_state != C_CONNECTED) { enum drbd_conn_state cstate = connection->info.conn_connection_state; wrap_printf(6, " connection:%s%s%s", cstate_color_start(cstate), drbd_conn_str(cstate), cstate_color_stop(cstate)); } if (opt_verbose || connection->info.conn_connection_state == C_CONNECTED) { enum drbd_role role = connection->info.conn_role; wrap_printf(6, " role:%s%s%s", role_color_start(role, false), drbd_role_str(role), role_color_stop(role, false)); } if (opt_verbose || connection->statistics.conn_congested > 0) print_connection_statistics(6, NULL, &connection->statistics, wrap_printf); wrap_printf(0, "\n"); if (opt_verbose || opt_statistics || connection->info.conn_connection_state == C_CONNECTED) peer_devices_status(&connection->ctx, peer_devices, single_device); } static void stop_colors(int sig) { printf("%s", stop_color_code()); signal(sig, SIG_DFL); raise(sig); } static void link_peer_devices_to_devices(struct peer_devices_list *peer_devices, struct devices_list *devices) { struct peer_devices_list *peer_device; struct devices_list *device; for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { for (device = devices; device; device = device->next) { if (peer_device->ctx.ctx_volume == device->ctx.ctx_volume) { peer_device->device = device; break; } } } } static int status_cmd(struct drbd_cmd *cm, int argc, char **argv) { struct resources_list *resources, *resource; struct sigaction sa = { .sa_handler = stop_colors, .sa_flags = SA_RESETHAND, }; bool found = false; bool json = false; int c; optind = 0; /* reset getopt_long() */ for (;;) { c = getopt_long(argc, argv, make_optstring(cm->options), cm->options, 0); if (c == -1) break; switch(c) { default: case '?': return 20; case 'v': opt_verbose = true; break; case 's': opt_statistics = true; break; case 'c': if (!parse_color_argument()) print_usage_and_exit("unknown --color argument"); break; case 'j': json = true; break; } } resources = sort_resources(list_resources()); if (resources == NULL) printf("# No currently configured DRBD found.\n"); sigaction(SIGHUP, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); sigaction(SIGTERM, &sa, NULL); if (json) puts("["); for (resource = resources; resource; resource = resource->next) { struct devices_list *devices, *device; struct connections_list *connections, *connection; struct peer_devices_list *peer_devices = NULL; bool single_device; if (strcmp(objname, "all") && strcmp(objname, resource->name)) continue; devices = list_devices(resource->name); connections = sort_connections(list_connections(resource->name)); if (devices && connections) peer_devices = list_peer_devices(resource->name); link_peer_devices_to_devices(peer_devices, devices); if (json) { resource_status_json(resource); for (device = devices; device; device = device->next) { device_status_json(device); if (device->next) puts(","); } puts(" ],\n \"connections\": ["); for (connection = connections; connection; connection = connection->next) { connection_status_json(connection, peer_devices); if (connection->next) puts(","); } puts(" ]\n}"); if (resource->next) puts(","); } else { resource_status(resource); single_device = devices && !devices->next; for (device = devices; device; device = device->next) device_status(device, single_device); for (connection = connections; connection; connection = connection->next) connection_status(connection, peer_devices, single_device); wrap_printf(0, "\n"); } free_connections(connections); free_devices(devices); free_peer_devices(peer_devices); found = true; } if (json) puts("]\n"); free_resources(resources); if (!found && strcmp(objname, "all")) { fprintf(stderr, "%s: No such resource\n", objname); return 10; } return 0; } static int role_cmd(struct drbd_cmd *cm, int argc, char **argv) { struct resources_list *resources, *resource; int ret = ERR_RES_NOT_KNOWN; resources = list_resources(); for (resource = resources; resource; resource = resource->next) { if (strcmp(objname, resource->name)) continue; printf("%s\n", drbd_role_str(resource->info.res_role)); ret = NO_ERROR; break; } free_resources(resources); if (ret != NO_ERROR) { fprintf(stderr, "%s: %s\n", objname, error_to_string(ret)); return 10; } return 0; } static int cstate_cmd(struct drbd_cmd *cm, int argc, char **argv) { struct connections_list *connections, *connection; bool found = false; connections = list_connections(NULL); for (connection = connections; connection; connection = connection->next) { if (connection->ctx.ctx_peer_node_id != global_ctx.ctx_peer_node_id) continue; printf("%s\n", drbd_conn_str(connection->info.conn_connection_state)); found = true; break; } free_connections(connections); if (!found) { fprintf(stderr, "%s: No such connection\n", objname); return 10; } return 0; } static int dstate_cmd(struct drbd_cmd *cm, int argc, char **argv) { struct devices_list *devices, *device; bool found = false; struct peer_devices_list *peer_devices, *peer_device; devices = list_devices(NULL); for (device = devices; device; device = device->next) { if (device->minor != minor) continue; printf("%s", drbd_disk_str(device->info.dev_disk_state)); /* printf("%s/%s\n",drbd_disk_str(state.disk),drbd_disk_str(state.pdsk)); */ peer_devices = list_peer_devices(device->ctx.ctx_resource_name); for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { if (device->ctx.ctx_volume == peer_device->ctx.ctx_volume) printf("/%s", drbd_disk_str(peer_device->info.peer_disk_state)); } printf("\n"); found = true; break; } free_devices(devices); if (!found) { fprintf(stderr, "%s: No such device\n", objname); return 10; } return 0; } static char *af_to_str(int af) { if (af == AF_INET) return "ipv4"; else if (af == AF_INET6) return "ipv6"; /* AF_SSOCKS typically is 27, the same as AF_INET_SDP. * But with warn_and_use_default = 0, it will stay at -1 if not available. * Just keep the test on ssocks before the one on SDP (which is hard-coded), * and all should be fine. */ else if (af == get_af_ssocks(0)) return "ssocks"; else if (af == AF_INET_SDP) return "sdp"; else return "unknown"; } static char *address_str(char *buffer, void* address, int addr_len) { union { struct sockaddr addr; struct sockaddr_in addr4; struct sockaddr_in6 addr6; } a; /* avoid alignment issues on certain platforms (e.g. armel) */ memset(&a, 0, sizeof(a)); memcpy(&a.addr, address, addr_len); if (a.addr.sa_family == AF_INET || a.addr.sa_family == get_af_ssocks(0) || a.addr.sa_family == AF_INET_SDP) { snprintf(buffer, ADDRESS_STR_MAX, "%s:%s:%u", af_to_str(a.addr4.sin_family), inet_ntoa(a.addr4.sin_addr), ntohs(a.addr4.sin_port)); return buffer; } else if (a.addr.sa_family == AF_INET6) { char buf2[ADDRESS_STR_MAX]; int n; buf2[0] = 0; /* inet_ntop does not include scope info */ getnameinfo(&a.addr, addr_len, buf2, sizeof(buf2), NULL, 0, NI_NUMERICHOST|NI_NUMERICSERV); n = snprintf(buffer, ADDRESS_STR_MAX, "%s:[%s]:%u", af_to_str(a.addr6.sin6_family), buf2, ntohs(a.addr6.sin6_port)); assert(n > 0); assert(n < ADDRESS_STR_MAX); /* there should be no need to truncate */ return buffer; } else return NULL; } static int remember_resource(struct drbd_cmd *cmd, struct genl_info *info, void *u_ptr) { struct resources_list ***tail = u_ptr; struct drbd_cfg_context cfg = { .ctx_volume = -1U, .ctx_peer_node_id = -1U }; if (!info) return 0; drbd_cfg_context_from_attrs(&cfg, info); if (cfg.ctx_resource_name) { struct resources_list *r = calloc(1, sizeof(*r)); struct nlattr *res_opts = global_attrs[DRBD_NLA_RESOURCE_OPTS]; r->name = strdup(cfg.ctx_resource_name); if (res_opts) { int size = nla_total_size(nla_len(res_opts)); r->res_opts = malloc(size); memcpy(r->res_opts, res_opts, size); } resource_info_from_attrs(&r->info, info); memset(&r->statistics, -1, sizeof(r->statistics)); resource_statistics_from_attrs(&r->statistics, info); **tail = r; *tail = &r->next; } return 0; } static void free_resources(struct resources_list *resources) { while (resources) { struct resources_list *r = resources; resources = resources->next; free(r->name); free(r->res_opts); free(r); } } static int resource_name_cmp(const struct resources_list * const *a, const struct resources_list * const *b) { return strcmp((*a)->name, (*b)->name); } static struct resources_list *sort_resources(struct resources_list *resources) { struct resources_list *r; int n; for (r = resources, n = 0; r; r = r->next) n++; if (n > 1) { struct resources_list **array; array = malloc(sizeof(*array) * n); for (r = resources, n = 0; r; r = r->next) array[n++] = r; qsort(array, n, sizeof(*array), (int (*)(const void *, const void *)) resource_name_cmp); n--; array[n]->next = NULL; for (; n > 0; n--) array[n - 1]->next = array[n]; resources = array[0]; free(array); } return resources; } /* * Expects objname to be set to the resource name or "all". */ static struct resources_list *list_resources(void) { struct drbd_cmd cmd = { .cmd_id = DRBD_ADM_GET_RESOURCES, .show_function = remember_resource, .missing_ok = false, }; struct resources_list *list = NULL, **tail = &list; char *old_objname = objname; unsigned old_minor = minor; int old_my_addr_len = global_ctx.ctx_my_addr_len; int old_peer_addr_len = global_ctx.ctx_peer_addr_len; int err; objname = "all"; minor = -1; global_ctx.ctx_my_addr_len = 0; global_ctx.ctx_peer_addr_len = 0; err = generic_get(&cmd, 120000, &tail); objname = old_objname; minor = old_minor; global_ctx.ctx_my_addr_len = old_my_addr_len; global_ctx.ctx_peer_addr_len = old_peer_addr_len; if (err) { free_resources(list); list = NULL; } return list; } static int remember_device(struct drbd_cmd *cm, struct genl_info *info, void *u_ptr) { struct devices_list ***tail = u_ptr; struct drbd_cfg_context ctx = { .ctx_volume = -1U, .ctx_peer_node_id = -1U }; if (!info) return 0; drbd_cfg_context_from_attrs(&ctx, info); if (ctx.ctx_volume != -1U) { struct devices_list *d = calloc(1, sizeof(*d)); struct nlattr *disk_conf_nl = global_attrs[DRBD_NLA_DISK_CONF]; d->minor = ((struct drbd_genlmsghdr*)(info->userhdr))->minor; d->ctx = ctx; if (disk_conf_nl) { int size = nla_total_size(nla_len(disk_conf_nl)); d->disk_conf_nl = malloc(size); memcpy(d->disk_conf_nl, disk_conf_nl, size); } disk_conf_from_attrs(&d->disk_conf, info); d->info.dev_disk_state = D_DISKLESS; device_info_from_attrs(&d->info, info); memset(&d->statistics, -1, sizeof(d->statistics)); device_statistics_from_attrs(&d->statistics, info); **tail = d; *tail = &d->next; } return 0; } /* * Expects objname to be set to the resource name or "all". */ static struct devices_list *list_devices(char *resource_name) { struct drbd_cmd cmd = { .cmd_id = DRBD_ADM_GET_DEVICES, .show_function = remember_device, .missing_ok = false, }; struct devices_list *list = NULL, **tail = &list; char *old_objname = objname; unsigned old_minor = minor; int old_my_addr_len = global_ctx.ctx_my_addr_len; int old_peer_addr_len = global_ctx.ctx_peer_addr_len; int err; objname = resource_name ? resource_name : "all"; minor = -1; global_ctx.ctx_my_addr_len = 0; global_ctx.ctx_peer_addr_len = 0; err = generic_get(&cmd, 120000, &tail); objname = old_objname; minor = old_minor; global_ctx.ctx_my_addr_len = old_my_addr_len; global_ctx.ctx_peer_addr_len = old_peer_addr_len; if (err) { free_devices(list); list = NULL; } return list; } static void free_devices(struct devices_list *devices) { while (devices) { struct devices_list *d = devices; devices = devices->next; free(d->disk_conf_nl); free(d); } } static int remember_connection(struct drbd_cmd *cmd, struct genl_info *info, void *u_ptr) { struct connections_list ***tail = u_ptr; struct drbd_cfg_context ctx = { .ctx_volume = -1U, .ctx_peer_node_id = -1U }; if (!info) return 0; drbd_cfg_context_from_attrs(&ctx, info); if (ctx.ctx_resource_name) { struct connections_list *c = calloc(1, sizeof(*c)); struct nlattr *net_conf = global_attrs[DRBD_NLA_NET_CONF]; struct nlattr *path_list = global_attrs[DRBD_NLA_PATH_PARMS]; c->ctx = ctx; if (net_conf) { int size = nla_total_size(nla_len(net_conf)); c->net_conf = malloc(size); memcpy(c->net_conf, net_conf, size); } if (path_list) { int size = nla_total_size(nla_len(path_list)); c->path_list = malloc(size); memcpy(c->path_list, path_list, size); } connection_info_from_attrs(&c->info, info); memset(&c->statistics, -1, sizeof(c->statistics)); connection_statistics_from_attrs(&c->statistics, info); **tail = c; *tail = &c->next; } return 0; } static int connection_name_cmp(const struct connections_list * const *a, const struct connections_list * const *b) { if (!(*a)->ctx.ctx_conn_name_len != !(*b)->ctx.ctx_conn_name_len) return !(*b)->ctx.ctx_conn_name_len; return strcmp((*a)->ctx.ctx_conn_name, (*b)->ctx.ctx_conn_name); } static struct connections_list *sort_connections(struct connections_list *connections) { struct connections_list *c; int n; for (c = connections, n = 0; c; c = c->next) n++; if (n > 1) { struct connections_list **array; array = malloc(sizeof(*array) * n); for (c = connections, n = 0; c; c = c->next) array[n++] = c; qsort(array, n, sizeof(*array), (int (*)(const void *, const void *)) connection_name_cmp); n--; array[n]->next = NULL; for (; n > 0; n--) array[n - 1]->next = array[n]; connections = array[0]; free(array); } return connections; } /* * Expects objname to be set to the resource name or "all". */ static struct connections_list *list_connections(char *resource_name) { struct drbd_cmd cmd = { .cmd_id = DRBD_ADM_GET_CONNECTIONS, .show_function = remember_connection, .missing_ok = true, }; struct connections_list *list = NULL, **tail = &list; char *old_objname = objname; unsigned old_minor = minor; int old_my_addr_len = global_ctx.ctx_my_addr_len; int old_peer_addr_len = global_ctx.ctx_peer_addr_len; int err; objname = resource_name ? resource_name : "all"; minor = -1; global_ctx.ctx_my_addr_len = 0; global_ctx.ctx_peer_addr_len = 0; err = generic_get(&cmd, 120000, &tail); objname = old_objname; minor = old_minor; global_ctx.ctx_my_addr_len = old_my_addr_len; global_ctx.ctx_peer_addr_len = old_peer_addr_len; if (err) { free_connections(list); list = NULL; } return list; } static void free_connections(struct connections_list *connections) { while (connections) { struct connections_list *l = connections; connections = connections->next; free(l->net_conf); free(l); } } static int remember_peer_device(struct drbd_cmd *cmd, struct genl_info *info, void *u_ptr) { struct peer_devices_list ***tail = u_ptr; struct drbd_cfg_context ctx = { .ctx_volume = -1U, .ctx_peer_node_id = -1U }; if (!info) return 0; drbd_cfg_context_from_attrs(&ctx, info); if (ctx.ctx_resource_name) { struct peer_devices_list *p = calloc(1, sizeof(*p)); struct nlattr *peer_device_conf = global_attrs[DRBD_NLA_PEER_DEVICE_OPTS]; if (!p) exit(20); p->ctx = ctx; if (peer_device_conf) { int size = nla_total_size(nla_len(peer_device_conf)); p->peer_device_conf = malloc(size); memcpy(p->peer_device_conf, peer_device_conf, size); } peer_device_info_from_attrs(&p->info, info); memset(&p->statistics, -1, sizeof(p->statistics)); peer_device_statistics_from_attrs(&p->statistics, info); **tail = p; *tail = &p->next; } return 0; } /* * Expects objname to be set to the resource name or "all". */ static struct peer_devices_list *list_peer_devices(char *resource_name) { struct drbd_cmd cmd = { .cmd_id = DRBD_ADM_GET_PEER_DEVICES, .show_function = remember_peer_device, .missing_ok = false, }; struct peer_devices_list *list = NULL, **tail = &list; char *old_objname = objname; unsigned old_minor = minor; int old_my_addr_len = global_ctx.ctx_my_addr_len; int old_peer_addr_len = global_ctx.ctx_peer_addr_len; int err; objname = resource_name ? resource_name : "all"; minor = -1; global_ctx.ctx_my_addr_len = 0; global_ctx.ctx_peer_addr_len = 0; err = generic_get(&cmd, 120000, &tail); objname = old_objname; minor = old_minor; global_ctx.ctx_my_addr_len = old_my_addr_len; global_ctx.ctx_peer_addr_len = old_peer_addr_len; if (err) { free_peer_devices(list); list = NULL; } return list; } static void free_peer_devices(struct peer_devices_list *peer_devices) { while (peer_devices) { struct peer_devices_list *p = peer_devices; peer_devices = peer_devices->next; free(p->peer_device_conf); free(p); } } static int check_resize_cmd(struct drbd_cmd *cm, int argc, char **argv) { struct devices_list *devices, *device; bool found = false; bool ret = 0; devices = list_devices(NULL); for (device = devices; device; device = device->next) { struct bdev_info bd = { 0, }; uint64_t bd_size; int fd; if (device->minor != minor) continue; found = true; if (!device->disk_conf.backing_dev) { fprintf(stderr, "Has no disk config, try with drbdmeta.\n"); ret = 1; break; } if (device->disk_conf.meta_dev_idx >= 0 || device->disk_conf.meta_dev_idx == DRBD_MD_INDEX_FLEX_EXT) { lk_bdev_delete(minor); break; } fd = open(device->disk_conf.backing_dev, O_RDONLY); if (fd == -1) { fprintf(stderr, "Could not open %s: %m.\n", device->disk_conf.backing_dev); ret = 1; break; } bd_size = bdev_size(fd); close(fd); if (lk_bdev_load(minor, &bd) == 0 && bd.bd_size == bd_size && bd.bd_name && !strcmp(bd.bd_name, device->disk_conf.backing_dev)) break; /* nothing changed. */ bd.bd_size = bd_size; bd.bd_name = device->disk_conf.backing_dev; lk_bdev_save(minor, &bd); break; } free_devices(devices); if (!found) { fprintf(stderr, "%s: No such device\n", objname); return 10; } return ret; } static bool peer_device_ctx_match(struct drbd_cfg_context *a, struct drbd_cfg_context *b) { return strcmp(a->ctx_resource_name, b->ctx_resource_name) == 0 && a->ctx_peer_node_id == b->ctx_peer_node_id && a->ctx_volume == b->ctx_volume; } static int show_or_get_gi_cmd(struct drbd_cmd *cm, int argc, char **argv) { struct peer_devices_list *peer_devices, *peer_device; struct devices_list *devices = NULL, *device; uint64_t uuids[UI_SIZE]; int ret = 0, i; peer_devices = list_peer_devices(NULL); for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { if (!peer_device_ctx_match(&global_ctx, &peer_device->ctx)) continue; devices = list_devices(peer_device->ctx.ctx_resource_name); for (device = devices; device; device = device->next) { if (device->ctx.ctx_volume == global_ctx.ctx_volume) goto found; } } fprintf(stderr, "%s: No such peer device\n", objname); ret = 10; out: free_devices(devices); free_peer_devices(peer_devices); return ret; found: if (peer_device->info.peer_repl_state == L_OFF && device->info.dev_disk_state == D_DISKLESS) { fprintf(stderr, "Device is unconfigured\n"); ret = 1; goto out; } if (device->info.dev_disk_state == D_DISKLESS) { /* XXX we could print the exposed_data_uuid anyways: */ if (0) printf(X64(016)"\n", (uint64_t)device->statistics.dev_exposed_data_uuid); fprintf(stderr, "Device has no disk\n"); ret = 1; goto out; } memset(uuids, 0, sizeof(uuids)); uuids[UI_CURRENT] = device->statistics.dev_current_uuid; uuids[UI_BITMAP] = peer_device->statistics.peer_dev_bitmap_uuid; i = device->statistics.history_uuids_len / 8; if (i >= HISTORY_UUIDS_V08) i = HISTORY_UUIDS_V08 - 1; for (; i >= 0; i--) uuids[UI_HISTORY_START + i] = ((uint64_t *)device->statistics.history_uuids)[i]; if(!strcmp(cm->cmd, "show-gi")) dt_pretty_print_v9_uuids(uuids, device->statistics.dev_disk_flags, peer_device->statistics.peer_dev_flags); else dt_print_v9_uuids(uuids, device->statistics.dev_disk_flags, peer_device->statistics.peer_dev_flags); goto out; } static int down_cmd(struct drbd_cmd *cm, int argc, char **argv) { struct resources_list *resources, *resource; char *old_objname; int rv = 0; if(argc > 2) { warn_print_excess_args(argc, argv, 2); return OTHER_ERROR; } old_objname = objname; context = CTX_RESOURCE; resources = list_resources(); for (resource = resources; resource; resource = resource->next) { struct devices_list *devices; int rv2; if (strcmp(old_objname, "all") && strcmp(old_objname, resource->name)) continue; objname = resource->name; devices = list_devices(objname); rv2 = _generic_config_cmd(cm, argc, argv); if (!rv2) { struct devices_list *device; for (device = devices; device; device = device->next) unregister_minor(device->minor); unregister_resource(objname); } if (!rv) rv = rv2; free_devices(devices); } free_resources(resources); return rv; } #define _EVPRINT(checksize, fstr, ...) do { \ ret = snprintf(key + pos, size, fstr, __VA_ARGS__); \ if (ret < 0) \ return ret; \ pos += ret; \ if (size && checksize) \ size -= ret; \ } while(0) #define EVPRINT(...) _EVPRINT(1, __VA_ARGS__) /* for llvm static analyzer */ #define EVPRINT_NOSIZE(...) _EVPRINT(0, __VA_ARGS__) static int event_key(char *key, int size, const char *name, unsigned minor, struct drbd_cfg_context *ctx) { char addr[ADDRESS_STR_MAX]; int ret, pos = 0; if (!ctx) return -1; EVPRINT("%s", name); if (ctx->ctx_resource_name) EVPRINT(" name:%s", ctx->ctx_resource_name); if (ctx->ctx_peer_node_id != -1U) EVPRINT(" peer-node-id:%d", ctx->ctx_peer_node_id); if (ctx->ctx_conn_name_len) EVPRINT(" conn-name:%s", ctx->ctx_conn_name); if (ctx->ctx_my_addr_len && address_str(addr, ctx->ctx_my_addr, ctx->ctx_my_addr_len)) EVPRINT(" local:%s", addr); if (ctx->ctx_peer_addr_len && address_str(addr, ctx->ctx_peer_addr, ctx->ctx_peer_addr_len)) EVPRINT(" peer:%s", addr); if (ctx->ctx_volume != -1U) EVPRINT(" volume:%u", ctx->ctx_volume); if (minor != -1U) EVPRINT_NOSIZE(" minor:%u", minor); return pos; } static int known_objects_cmp(const void *a, const void *b) { return strcmp(((const struct entry *)a)->key, ((const struct entry *)b)->key); } static void *update_info(char **key, void *value, size_t size) { static void *known_objects; struct entry entry = { .key = *key }, **found; if (value) { void *old_value = NULL; found = tsearch(&entry, &known_objects, known_objects_cmp); if (*found != &entry) old_value = (*found)->data; else { *found = malloc(sizeof(**found)); if (!*found) goto fail; (*found)->key = *key; *key = NULL; } (*found)->data = malloc(size); if (!(*found)->data) goto fail; memcpy((*found)->data, value, size); return old_value; } else { found = tfind(&entry, &known_objects, known_objects_cmp); if (found) { struct entry *entry = *found; tdelete(entry, &known_objects, known_objects_cmp); free(entry->data); free(entry->key); free(entry); } return NULL; } fail: perror(progname); exit(20); } static int print_notifications(struct drbd_cmd *cm, struct genl_info *info, void *u_ptr) { static const char *action_name[] = { [NOTIFY_EXISTS] = "exists", [NOTIFY_CREATE] = "create", [NOTIFY_CHANGE] = "change", [NOTIFY_DESTROY] = "destroy", [NOTIFY_CALL] = "call", [NOTIFY_RESPONSE] = "response", }; static char *object_name[] = { [DRBD_RESOURCE_STATE] = "resource", [DRBD_DEVICE_STATE] = "device", [DRBD_CONNECTION_STATE] = "connection", [DRBD_PEER_DEVICE_STATE] = "peer-device", [DRBD_HELPER] = "helper", [DRBD_PATH_STATE] = "path", }; static uint32_t last_seq; static bool last_seq_known; static struct timeval tv; static bool keep_tv; struct drbd_cfg_context ctx = { .ctx_volume = -1U, .ctx_peer_node_id = -1U, }; struct drbd_notification_header nh = { .nh_type = -1U }; enum drbd_notification_type action; struct drbd_genlmsghdr *dh; char *key = NULL; if (!info) { keep_tv = false; return 0; } dh = info->userhdr; if (dh->ret_code == ERR_MINOR_INVALID && cm->missing_ok) return 0; if (dh->ret_code != NO_ERROR) return dh->ret_code; if (drbd_notification_header_from_attrs(&nh, info)) return 0; action = nh.nh_type & ~NOTIFY_FLAGS; if (action >= ARRAY_SIZE(action_name) || !action_name[action]) { dbg(1, "unknown notification type\n"); goto out; } if (opt_now && action != NOTIFY_EXISTS) return 0; if (info->genlhdr->cmd != DRBD_INITIAL_STATE_DONE) { if (drbd_cfg_context_from_attrs(&ctx, info)) return 0; if (info->genlhdr->cmd >= ARRAY_SIZE(object_name) || !object_name[info->genlhdr->cmd]) { dbg(1, "unknown notification\n"); goto out; } } if (action != NOTIFY_EXISTS) { if (last_seq_known) { int skipped = info->nlhdr->nlmsg_seq - (last_seq + 1); if (skipped) printf("- skipped %d\n", skipped); } last_seq = info->nlhdr->nlmsg_seq; last_seq_known = true; } if (opt_timestamps) { struct tm *tm; if (!keep_tv) gettimeofday(&tv, NULL); keep_tv = !!(nh.nh_type & NOTIFY_CONTINUES); tm = localtime(&tv.tv_sec); printf("%04u-%02u-%02uT%02u:%02u:%02u.%06u%+03d:%02u ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)tv.tv_usec, (int)(tm->tm_gmtoff / 3600), (int)((abs(tm->tm_gmtoff) / 60) % 60)); } if (info->genlhdr->cmd != DRBD_INITIAL_STATE_DONE) { const char *name = object_name[info->genlhdr->cmd]; int size; size = event_key(NULL, 0, name, dh->minor, &ctx); if (size < 0) goto fail; key = malloc(size + 1); if (!key) goto fail; event_key(key, size + 1, name, dh->minor, &ctx); } printf("%s %s", action_name[action], key ? key : "-"); switch(info->genlhdr->cmd) { case DRBD_RESOURCE_STATE: if (action != NOTIFY_DESTROY) { struct { struct resource_info i; struct resource_statistics s; } *old, new; if (resource_info_from_attrs(&new.i, info)) { dbg(1, "resource info missing\n"); goto nl_out; } old = update_info(&key, &new, sizeof(new)); if (!old || new.i.res_role != old->i.res_role) printf(" role:%s%s%s", ROLE_COLOR_STRING(new.i.res_role, 1)); if (!old || new.i.res_susp != old->i.res_susp || new.i.res_susp_nod != old->i.res_susp_nod || new.i.res_susp_fen != old->i.res_susp_fen) printf(" suspended:%s", susp_str(&new.i)); if (opt_statistics) { if (resource_statistics_from_attrs(&new.s, info)) { dbg(1, "resource statistics missing\n"); if (old) new.s = old->s; } else print_resource_statistics(0, old ? &old->s : NULL, &new.s, nowrap_printf); } free(old); } else update_info(&key, NULL, 0); break; case DRBD_DEVICE_STATE: if (action != NOTIFY_DESTROY) { struct { struct device_info i; struct device_statistics s; } *old, new; if (device_info_from_attrs(&new.i, info)) { dbg(1, "device info missing\n"); goto nl_out; } old = update_info(&key, &new, sizeof(new)); if (!old || new.i.dev_disk_state != old->i.dev_disk_state) printf(" disk:%s%s%s", DISK_COLOR_STRING(new.i.dev_disk_state, 1)); if (opt_statistics) { if (device_statistics_from_attrs(&new.s, info)) { dbg(1, "device statistics missing\n"); if (old) new.s = old->s; } else print_device_statistics(0, old ? &old->s : NULL, &new.s, nowrap_printf); } free(old); } else update_info(&key, NULL, 0); break; case DRBD_CONNECTION_STATE: if (action != NOTIFY_DESTROY) { struct { struct connection_info i; struct connection_statistics s; } *old, new; if (connection_info_from_attrs(&new.i, info)) { dbg(1, "connection info missing\n"); goto nl_out; } old = update_info(&key, &new, sizeof(new)); if (!old || new.i.conn_connection_state != old->i.conn_connection_state) printf(" connection:%s%s%s", CONN_COLOR_STRING(new.i.conn_connection_state)); if (!old || new.i.conn_role != old->i.conn_role) printf(" role:%s%s%s", ROLE_COLOR_STRING(new.i.conn_role, 0)); if (opt_statistics) { if (connection_statistics_from_attrs(&new.s, info)) { dbg(1, "connection statistics missing\n"); if (old) new.s = old->s; } else print_connection_statistics(0, old ? &old->s : NULL, &new.s, nowrap_printf); } free(old); } else update_info(&key, NULL, 0); break; case DRBD_PEER_DEVICE_STATE: if (action != NOTIFY_DESTROY) { struct { struct peer_device_info i; struct peer_device_statistics s; } *old, new; if (peer_device_info_from_attrs(&new.i, info)) { dbg(1, "peer device info missing\n"); goto nl_out; } old = update_info(&key, &new, sizeof(new)); if (!old || new.i.peer_repl_state != old->i.peer_repl_state) printf(" replication:%s%s%s", REPL_COLOR_STRING(new.i.peer_repl_state)); if (!old || new.i.peer_disk_state != old->i.peer_disk_state) printf(" peer-disk:%s%s%s", DISK_COLOR_STRING(new.i.peer_disk_state, 0)); if (!old || new.i.peer_resync_susp_user != old->i.peer_resync_susp_user || new.i.peer_resync_susp_peer != old->i.peer_resync_susp_peer || new.i.peer_resync_susp_dependency != old->i.peer_resync_susp_dependency) printf(" resync-suspended:%s", resync_susp_str(&new.i)); if (opt_statistics) { if (peer_device_statistics_from_attrs(&new.s, info)) { dbg(1, "peer device statistics missing\n"); if (old) new.s = old->s; } else print_peer_device_statistics(0, old ? &old->s : NULL, &new.s, nowrap_printf); } free(old); } else update_info(&key, NULL, 0); break; case DRBD_PATH_STATE: if (action != NOTIFY_DESTROY) { struct drbd_path_info new = {}, *old; if (drbd_path_info_from_attrs(&new, info)) { dbg(1, "path info missing\n"); goto nl_out; } old = update_info(&key, &new, sizeof(new)); if (!old || old->path_established != new.path_established) printf(" established:%s", new.path_established ? "yes" : "no"); free(old); } else update_info(&key, NULL, 0); break; case DRBD_HELPER: { struct drbd_helper_info helper_info; if (!drbd_helper_info_from_attrs(&helper_info, info)) { printf(" helper:%s", helper_info.helper_name); if (action == NOTIFY_RESPONSE) printf(" status:%u", helper_info.helper_status); } else { dbg(1, "helper info missing\n"); goto nl_out; } } break; case DRBD_INITIAL_STATE_DONE: break; } nl_out: printf("\n"); out: free(key); fflush(stdout); if (opt_now && info->genlhdr->cmd == DRBD_INITIAL_STATE_DONE) return -1; return 0; fail: perror(progname); exit(20); } void peer_devices_append(struct peer_devices_list *peer_devices, struct genl_info *info) { struct peer_devices_list *peer_device, **tail; if (!peer_devices) return; for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) tail = &peer_device->next; remember_peer_device(NULL, info, &tail); } /* Actually waits for all volumes of a connection... */ static int wait_for_family(struct drbd_cmd *cm, struct genl_info *info, void *u_ptr) { struct peer_devices_list *peer_devices = u_ptr; struct drbd_cfg_context ctx = { .ctx_volume = -1U, .ctx_peer_node_id = -1U }; struct drbd_notification_header nh = { .nh_type = -1U }; struct drbd_genlmsghdr *dh; if (!info) return 0; if (drbd_cfg_context_from_attrs(&ctx, info) || drbd_notification_header_from_attrs(&nh, info)) return 0; dh = info->userhdr; if (dh->ret_code != NO_ERROR) return dh->ret_code; if ((nh.nh_type & ~NOTIFY_FLAGS) == NOTIFY_DESTROY) return 0; switch(info->genlhdr->cmd) { case DRBD_CONNECTION_STATE: { struct connection_info connection_info; if ((nh.nh_type & ~NOTIFY_FLAGS) == NOTIFY_CREATE) break; /* Ignore C_STANDALONE while creating it */ if (connection_info_from_attrs(&connection_info, info)) { dbg(1, "connection info missing\n"); break; } if (connection_info.conn_connection_state < C_UNCONNECTED) { if (!wait_after_split_brain) return -1; /* done waiting */ fprintf(stderr, "\ndrbd %s connection to peer-id %u ('%s') is %s, " "but I'm configured to wait anways (--wait-after-sb)\n", ctx.ctx_resource_name, ctx.ctx_peer_node_id, ctx.ctx_conn_name, drbd_conn_str(connection_info.conn_connection_state)); } break; } case DRBD_PEER_DEVICE_STATE: { struct peer_device_info peer_device_info; struct peer_devices_list *peer_device; int nr_peer_devices = 0, nr_done = 0; bool wait_connect; if (peer_device_info_from_attrs(&peer_device_info, info)) { dbg(1, "peer device info missing\n"); break; } wait_connect = strstr(cm->cmd, "sync") == NULL; if ((nh.nh_type & ~NOTIFY_FLAGS) == NOTIFY_CREATE) peer_devices_append(peer_devices, info); for (peer_device = peer_devices; peer_device; peer_device = peer_device->next) { enum drbd_repl_state rs; if (peer_device_ctx_match(&ctx, &peer_device->ctx)) peer_device->info = peer_device_info; /* wait-*-volume: filter out all but the specific peer device */ if (cm->ctx_key == CTX_PEER_DEVICE && !peer_device_ctx_match(&global_ctx, &peer_device->ctx)) continue; /* wait-*-connection: filter out other connections */ if (cm->ctx_key == CTX_PEER_NODE && peer_device->ctx.ctx_peer_node_id != global_ctx.ctx_peer_node_id) continue; /* wait-*-resource: no filter */ nr_peer_devices++; rs = peer_device->info.peer_repl_state; if (rs == L_ESTABLISHED || (wait_connect && rs > L_ESTABLISHED) || peer_device->timeout_ms == 0) nr_done++; } if (nr_peer_devices == nr_done) return -1; /* Done with waiting */ break; } } return 0; } /* * Check if an integer is a power of two. */ static bool power_of_two(int i) { return i && !(i & (i - 1)); } static void print_command_usage(struct drbd_cmd *cm, enum usage_type ut) { struct drbd_argument *args; if(ut == XML) { printf("\n", cm->cmd); if (cm->summary) printf("\t%s\n", cm->summary); if (cm->ctx_key && ut != BRIEF) { enum cfg_ctx_key ctx = cm->ctx_key, arg; bool more_than_one_choice = !power_of_two(ctx & ~CTX_MULTIPLE_ARGUMENTS) && !(ctx & CTX_MULTIPLE_ARGUMENTS); const char *indent = "\t\t" + !more_than_one_choice; if (more_than_one_choice) printf("\t\n"); ctx |= CTX_MULTIPLE_ARGUMENTS; for (arg = ctx_next_arg(&ctx); arg; arg = ctx_next_arg(&ctx)) printf("%s%s\n", indent, ctx_arg_string(arg, ut)); if (more_than_one_choice) printf("\t\n"); } if(cm->drbd_args) { for (args = cm->drbd_args; args->name; args++) { printf("\t%s\n", args->name); } } if (cm->options) { struct option *option; for (option = cm->options; option->name; option++) { /* * The "string" options here really are * timeouts, but we can't describe them * in a resonable way here. */ printf("\t\n", option->name, option->has_arg == no_argument ? "flag" : "string"); } } if (cm->set_defaults) printf("\t\n"); if (cm->ctx) { struct field_def *field; for (field = cm->ctx->fields; field->name; field++) field->ops->describe_xml(field); } printf("\n"); return; } if (ut == BRIEF) { wrap_printf(4, "%s - ", cm->cmd); if (cm->summary) wrap_printf_wordwise(8, cm->summary); wrap_printf(4, "\n"); } else { wrap_printf(0, "%s %s", progname, cm->cmd); if (cm->summary) wrap_printf(4, " - %s", cm->summary); wrap_printf(4, "\n\n"); wrap_printf(0, "USAGE: %s %s", progname, cm->cmd); if (cm->ctx_key && ut != BRIEF) { enum cfg_ctx_key ctx = cm->ctx_key, arg; bool more_than_one_choice = !power_of_two(ctx & ~CTX_MULTIPLE_ARGUMENTS) && !(ctx & CTX_MULTIPLE_ARGUMENTS); bool first = true; if (more_than_one_choice) wrap_printf(4, " {"); ctx |= CTX_MULTIPLE_ARGUMENTS; for (arg = ctx_next_arg(&ctx); arg; arg = ctx_next_arg(&ctx)) { if (more_than_one_choice && !first) wrap_printf(4, " |"); first = false; wrap_printf(4, " %s", ctx_arg_string(arg, ut)); } if (more_than_one_choice) wrap_printf(4, " }"); } if (cm->drbd_args) { for (args = cm->drbd_args; args->name; args++) wrap_printf(4, " {%s}", args->name); } if (cm->options || cm->set_defaults || cm->ctx) wrap_printf(4, "\n"); if (cm->options) { struct option *option; for (option = cm->options; option->name; option++) wrap_printf(4, " [--%s%s]", option->name, option->has_arg == no_argument ? "" : "=..."); } if (cm->set_defaults) wrap_printf(4, " [--set-defaults]"); if (cm->ctx) { struct field_def *field; for (field = cm->ctx->fields; field->name; field++) { char buffer[300]; int n; n = field->ops->usage(field, buffer, sizeof(buffer)); assert(n < sizeof(buffer)); wrap_printf(4, " %s", buffer); } } wrap_printf(4, "\n"); } } static void print_usage_and_exit(const char *addinfo) { size_t i; printf("drbdsetup - Configure the DRBD kernel module.\n\n" "USAGE: %s command {arguments} [options]\n" "\nCommands:\n",cmdname); for (i = 0; i < ARRAY_SIZE(commands); i++) print_command_usage(&commands[i], BRIEF); printf("\nUse 'drbdsetup help command' for command-specific help.\n\n"); if (addinfo) /* FIXME: ?! */ printf("\n%s\n", addinfo); exit(20); } static int modprobe_drbd(void) { struct stat sb; int ret, retries = 10; ret = stat("/proc/drbd", &sb); if (ret && errno == ENOENT) { ret = system("/sbin/modprobe drbd"); if (ret != 0) { fprintf(stderr, "Failed to modprobe drbd (%m)\n"); return 0; } for(;;) { struct timespec ts = { .tv_nsec = 1000000, }; ret = stat("/proc/drbd", &sb); if (!ret || retries-- == 0) break; nanosleep(&ts, NULL); } } if (ret) { fprintf(stderr, "Could not stat /proc/drbd: %m\n"); fprintf(stderr, "Make sure that the DRBD kernel module is installed " "and can be loaded!\n"); } return ret == 0; } static void maybe_exec_legacy_drbdsetup(char **argv) { const struct version *driver_version = drbd_driver_version(FALLBACK_TO_UTILS); if (driver_version->version.major == 8 && driver_version->version.minor == 3) { #ifdef DRBD_LEGACY_83 static const char * const drbdsetup_83 = "drbdsetup-83"; add_lib_drbd_to_path(); execvp(drbdsetup_83, argv); fprintf(stderr, "execvp() failed to exec %s: %m\n", drbdsetup_83); #else config_help_legacy("drbdsetup", driver_version); #endif exit(20); } if (driver_version->version.major == 8 && driver_version->version.minor == 4) { #ifdef DRBD_LEGACY_84 static const char * const drbdsetup_84 = "drbdsetup-84"; add_lib_drbd_to_path(); execvp(drbdsetup_84, argv); fprintf(stderr, "execvp() failed to exec %s: %m\n", drbdsetup_84); #else config_help_legacy("drbdsetup", driver_version); #endif exit(20); } } int main(int argc, char **argv) { struct drbd_cmd *cmd; struct option *options; int c, rv = 0; int longindex, first_optind; progname = basename(argv[0]); if (chdir("/")) { /* highly unlikely, but gcc is picky */ perror("cannot chdir /"); return -111; } cmdname = strrchr(argv[0],'/'); if (cmdname) argv[0] = ++cmdname; else cmdname = argv[0]; if (argc > 2 && (!strcmp(argv[2], "--help") || !strcmp(argv[2], "-h"))) { char *swap = argv[1]; argv[1] = argv[2]; argv[2] = swap; } if (argc > 1 && (!strcmp(argv[1], "help") || !strcmp(argv[1], "xml-help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h"))) { enum usage_type usage_type = !strcmp(argv[1], "xml-help") ? XML : FULL; if(argc > 2) { cmd = find_cmd_by_name(argv[2]); if(cmd) { print_command_usage(cmd, usage_type); exit(0); } else print_usage_and_exit("unknown command"); } else print_usage_and_exit(NULL); } /* * drbdsetup previously took the object to operate on as its first argument, * followed by the command. For backwards compatibility, still support his. */ if (argc >= 3 && !find_cmd_by_name(argv[1]) && find_cmd_by_name(argv[2])) { char *swap = argv[1]; argv[1] = argv[2]; argv[2] = swap; } if (argc < 2) print_usage_and_exit(NULL); if (!modprobe_drbd()) { if (!strcmp(argv[1], "down") || !strcmp(argv[1], "secondary") || !strcmp(argv[1], "disconnect") || !strcmp(argv[1], "detach")) return 0; /* "down" succeeds even if drbd is missing */ return 20; } maybe_exec_legacy_drbdsetup(argv); cmd = find_cmd_by_name(argv[1]); if (!cmd) print_usage_and_exit("invalid command"); /* Make argv[0] the command name so that getopt_long() will leave it in * the first position. */ argv++; argc--; options = make_longoptions(cmd); for (;;) { c = getopt_long(argc, argv, "(", options, &longindex); if (c == -1) break; if (c == '?' || c == ':') print_usage_and_exit(NULL); } /* All non-option arguments now are in argv[optind .. argc - 1]. */ first_optind = optind; if (cmd->continuous_poll && kernel_older_than(2, 6, 23)) { /* with newer kernels, we need to use setsockopt NETLINK_ADD_MEMBERSHIP */ /* maybe more specific: (1 << GENL_ID_CTRL)? */ drbd_genl_family.nl_groups = -1; } drbd_sock = genl_connect_to_family(&drbd_genl_family); if (!drbd_sock) { fprintf(stderr, "Could not connect to 'drbd' generic netlink family\n"); return 20; } if (drbd_genl_family.version != GENL_MAGIC_VERSION || drbd_genl_family.hdrsize != sizeof(struct drbd_genlmsghdr)) { fprintf(stderr, "API mismatch!\n\t" "API version drbdsetup: %u kernel: %u\n\t" "header size drbdsetup: %u kernel: %u\n", GENL_MAGIC_VERSION, drbd_genl_family.version, (unsigned)sizeof(struct drbd_genlmsghdr), drbd_genl_family.hdrsize); return 20; } context = 0; enum cfg_ctx_key ctx_key = cmd->ctx_key, next_arg; for (next_arg = ctx_next_arg(&ctx_key); next_arg; next_arg = ctx_next_arg(&ctx_key), optind++) { if (argc == optind && !(ctx_key & CTX_MULTIPLE_ARGUMENTS) && (next_arg & CTX_ALL)) { context |= CTX_ALL; /* assume "all" if no argument is given */ objname = "all"; break; } else if (argc <= optind) { fprintf(stderr, "Missing argument %d to command\n", optind); print_command_usage(cmd, FULL); exit(20); } else if (next_arg & (CTX_RESOURCE | CTX_MINOR | CTX_ALL)) { ensure_sanity_of_res_name(argv[optind]); if (!objname) objname = argv[optind]; if (!strcmp(argv[optind], "all")) { if (!(next_arg & CTX_ALL)) print_usage_and_exit("command does not accept argument 'all'"); context |= CTX_ALL; } else if (next_arg & CTX_MINOR) { minor = dt_minor_of_dev(argv[optind]); if (minor == -1U && next_arg == CTX_MINOR) { fprintf(stderr, "Cannot determine minor device number of " "device '%s'\n", argv[optind]); exit(20); } context |= CTX_MINOR; } else /* not "all", and not a minor number/device name */ { if (!(next_arg & CTX_RESOURCE)) { fprintf(stderr, "command does not accept argument '%s'\n", objname); print_command_usage(cmd, FULL); exit(20); } context |= CTX_RESOURCE; assert(strlen(objname) < sizeof(global_ctx.ctx_resource_name)); memset(global_ctx.ctx_resource_name, 0, sizeof(global_ctx.ctx_resource_name)); global_ctx.ctx_resource_name_len = strlen(objname); strcpy(global_ctx.ctx_resource_name, objname); } } else { if (next_arg == CTX_MY_ADDR) { const char *str = argv[optind]; struct sockaddr_storage *x; if (strncmp(str, "local:", 6) == 0) str += 6; assert(sizeof(global_ctx.ctx_my_addr) >= sizeof(*x)); x = (struct sockaddr_storage *)&global_ctx.ctx_my_addr; global_ctx.ctx_my_addr_len = sockaddr_from_str(x, str); } else if (next_arg == CTX_PEER_ADDR) { const char *str = argv[optind]; struct sockaddr_storage *x; if (strncmp(str, "peer:", 5) == 0) str += 5; assert(sizeof(global_ctx.ctx_peer_addr) >= sizeof(*x)); x = (struct sockaddr_storage *)&global_ctx.ctx_peer_addr; global_ctx.ctx_peer_addr_len = sockaddr_from_str(x, str); } else if (next_arg == CTX_VOLUME) { global_ctx.ctx_volume = m_strtoll(argv[optind], 1); } else if (next_arg == CTX_PEER_NODE_ID) { global_ctx.ctx_peer_node_id = m_strtoll(argv[optind], 1); } else assert(0); context |= next_arg; } } /* Remove the options we have already processed from argv */ if (first_optind != optind) { int n; for (n = 0; n < argc - optind; n++) argv[first_optind + n] = argv[optind + n]; argc -= optind - first_optind; } if (!objname) objname = "??"; if ((context & CTX_MINOR) && !cmd->lockless) lock_fd = dt_lock_drbd(minor); rv = cmd->function(cmd, argc, argv); if ((context & CTX_MINOR) && !cmd->lockless) dt_unlock_drbd(lock_fd); return rv; } #endif drbd-utils-8.9.10/user/v9/sys_queue.h0000644000175000017500000004642012466702074017303 0ustar apoikosapoikos/* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 */ #ifndef _SYS_QUEUE_H_ #define _SYS_QUEUE_H_ /* * This file defines five types of data structures: singly-linked lists, * lists, simple queues, tail queues, and circular queues. * * A singly-linked list is headed by a single forward pointer. The * elements are singly linked for minimum space and pointer manipulation * overhead at the expense of O(n) removal for arbitrary elements. New * elements can be added to the list after an existing element or at the * head of the list. Elements being removed from the head of the list * should use the explicit macro for this purpose for optimum * efficiency. A singly-linked list may only be traversed in the forward * direction. Singly-linked lists are ideal for applications with large * datasets and few or no removals or for implementing a LIFO queue. * * A list is headed by a single forward pointer (or an array of forward * pointers for a hash table header). The elements are doubly linked * so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before * or after an existing element or at the head of the list. A list * may only be traversed in the forward direction. * * A simple queue is headed by a pair of pointers, one the head of the * list and the other to the tail of the list. The elements are singly * linked to save space, so elements can only be removed from the * head of the list. New elements can be added to the list after * an existing element, at the head of the list, or at the end of the * list. A simple queue may only be traversed in the forward direction. * * A tail queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or * after an existing element, at the head of the list, or at the end of * the list. A tail queue may be traversed in either direction. * * A circle queue is headed by a pair of pointers, one to the head of the * list and the other to the tail of the list. The elements are doubly * linked so that an arbitrary element can be removed without a need to * traverse the list. New elements can be added to the list before or after * an existing element, at the head of the list, or at the end of the list. * A circle queue may be traversed in either direction, but has a more * complex end of list detection. * * For details on the use of these macros, see the queue(3) manual page. */ /* * List definitions. */ #define LIST_HEAD(name, type) \ struct name { \ struct type *lh_first; /* first element */ \ } #define LIST_HEAD_INITIALIZER(head) \ { NULL } #define LIST_ENTRY(type) \ struct { \ struct type *le_next; /* next element */ \ struct type **le_prev; /* address of previous next element */ \ } /* * List functions. */ #define LIST_INIT(head) do { \ (head)->lh_first = NULL; \ } while (/*CONSTCOND*/0) #define LIST_INSERT_AFTER(listelm, elm, field) do { \ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ (listelm)->field.le_next->field.le_prev = \ &(elm)->field.le_next; \ (listelm)->field.le_next = (elm); \ (elm)->field.le_prev = &(listelm)->field.le_next; \ } while (/*CONSTCOND*/0) #define LIST_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.le_prev = (listelm)->field.le_prev; \ (elm)->field.le_next = (listelm); \ *(listelm)->field.le_prev = (elm); \ (listelm)->field.le_prev = &(elm)->field.le_next; \ } while (/*CONSTCOND*/0) #define LIST_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ (head)->lh_first = (elm); \ (elm)->field.le_prev = &(head)->lh_first; \ } while (/*CONSTCOND*/0) #define LIST_REMOVE(elm, field) do { \ if ((elm)->field.le_next != NULL) \ (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ *(elm)->field.le_prev = (elm)->field.le_next; \ } while (/*CONSTCOND*/0) #define LIST_FOREACH(var, head, field) \ for ((var) = ((head)->lh_first); \ (var); \ (var) = ((var)->field.le_next)) /* * List access methods. */ #define LIST_EMPTY(head) ((head)->lh_first == NULL) #define LIST_FIRST(head) ((head)->lh_first) #define LIST_NEXT(elm, field) ((elm)->field.le_next) /* * Singly-linked List definitions. */ #define SLIST_HEAD(name, type) \ struct name { \ struct type *slh_first; /* first element */ \ } #define SLIST_HEAD_INITIALIZER(head) \ { NULL } #define SLIST_ENTRY(type) \ struct { \ struct type *sle_next; /* next element */ \ } /* * Singly-linked List functions. */ #define SLIST_INIT(head) do { \ (head)->slh_first = NULL; \ } while (/*CONSTCOND*/0) #define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ (elm)->field.sle_next = (slistelm)->field.sle_next; \ (slistelm)->field.sle_next = (elm); \ } while (/*CONSTCOND*/0) #define SLIST_INSERT_HEAD(head, elm, field) do { \ (elm)->field.sle_next = (head)->slh_first; \ (head)->slh_first = (elm); \ } while (/*CONSTCOND*/0) #define SLIST_REMOVE_HEAD(head, field) do { \ (head)->slh_first = (head)->slh_first->field.sle_next; \ } while (/*CONSTCOND*/0) #define SLIST_REMOVE(head, elm, type, field) do { \ if ((head)->slh_first == (elm)) { \ SLIST_REMOVE_HEAD((head), field); \ } \ else { \ struct type *curelm = (head)->slh_first; \ while(curelm->field.sle_next != (elm)) \ curelm = curelm->field.sle_next; \ curelm->field.sle_next = \ curelm->field.sle_next->field.sle_next; \ } \ } while (/*CONSTCOND*/0) #define SLIST_FOREACH(var, head, field) \ for((var) = (head)->slh_first; (var); (var) = (var)->field.sle_next) /* * Singly-linked List access methods. */ #define SLIST_EMPTY(head) ((head)->slh_first == NULL) #define SLIST_FIRST(head) ((head)->slh_first) #define SLIST_NEXT(elm, field) ((elm)->field.sle_next) /* * Singly-linked Tail queue declarations. */ #define STAILQ_HEAD(name, type) \ struct name { \ struct type *stqh_first; /* first element */ \ struct type **stqh_last; /* addr of last next element */ \ } #define STAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).stqh_first } #define STAILQ_ENTRY(type) \ struct { \ struct type *stqe_next; /* next element */ \ } /* * Singly-linked Tail queue functions. */ #define STAILQ_INIT(head) do { \ (head)->stqh_first = NULL; \ (head)->stqh_last = &(head)->stqh_first; \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \ (head)->stqh_last = &(elm)->field.stqe_next; \ (head)->stqh_first = (elm); \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.stqe_next = NULL; \ *(head)->stqh_last = (elm); \ (head)->stqh_last = &(elm)->field.stqe_next; \ } while (/*CONSTCOND*/0) #define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\ (head)->stqh_last = &(elm)->field.stqe_next; \ (listelm)->field.stqe_next = (elm); \ } while (/*CONSTCOND*/0) #define STAILQ_REMOVE_HEAD(head, field) do { \ if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \ (head)->stqh_last = &(head)->stqh_first; \ } while (/*CONSTCOND*/0) #define STAILQ_REMOVE(head, elm, type, field) do { \ if ((head)->stqh_first == (elm)) { \ STAILQ_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->stqh_first; \ while (curelm->field.stqe_next != (elm)) \ curelm = curelm->field.stqe_next; \ if ((curelm->field.stqe_next = \ curelm->field.stqe_next->field.stqe_next) == NULL) \ (head)->stqh_last = &(curelm)->field.stqe_next; \ } \ } while (/*CONSTCOND*/0) #define STAILQ_FOREACH(var, head, field) \ for ((var) = ((head)->stqh_first); \ (var); \ (var) = ((var)->field.stqe_next)) #define STAILQ_FOREACH_SAFE(var, next, head, field) \ for ((var) = ((head)->stqh_first); \ (((var) != NULL) && (((next) = ((var)->field.stqe_next)) || 1)); \ (var) = (next)) #define STAILQ_CONCAT(head1, head2) do { \ if (!STAILQ_EMPTY((head2))) { \ *(head1)->stqh_last = (head2)->stqh_first; \ (head1)->stqh_last = (head2)->stqh_last; \ STAILQ_INIT((head2)); \ } \ } while (/*CONSTCOND*/0) /* * Singly-linked Tail queue access methods. */ #define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) #define STAILQ_FIRST(head) ((head)->stqh_first) #define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) /* * Simple queue definitions. */ #define SIMPLEQ_HEAD(name, type) \ struct name { \ struct type *sqh_first; /* first element */ \ struct type **sqh_last; /* addr of last next element */ \ } #define SIMPLEQ_HEAD_INITIALIZER(head) \ { NULL, &(head).sqh_first } #define SIMPLEQ_ENTRY(type) \ struct { \ struct type *sqe_next; /* next element */ \ } /* * Simple queue functions. */ #define SIMPLEQ_INIT(head) do { \ (head)->sqh_first = NULL; \ (head)->sqh_last = &(head)->sqh_first; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ (head)->sqh_last = &(elm)->field.sqe_next; \ (head)->sqh_first = (elm); \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.sqe_next = NULL; \ *(head)->sqh_last = (elm); \ (head)->sqh_last = &(elm)->field.sqe_next; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ (head)->sqh_last = &(elm)->field.sqe_next; \ (listelm)->field.sqe_next = (elm); \ } while (/*CONSTCOND*/0) #define SIMPLEQ_REMOVE_HEAD(head, field) do { \ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ (head)->sqh_last = &(head)->sqh_first; \ } while (/*CONSTCOND*/0) #define SIMPLEQ_REMOVE(head, elm, type, field) do { \ if ((head)->sqh_first == (elm)) { \ SIMPLEQ_REMOVE_HEAD((head), field); \ } else { \ struct type *curelm = (head)->sqh_first; \ while (curelm->field.sqe_next != (elm)) \ curelm = curelm->field.sqe_next; \ if ((curelm->field.sqe_next = \ curelm->field.sqe_next->field.sqe_next) == NULL) \ (head)->sqh_last = &(curelm)->field.sqe_next; \ } \ } while (/*CONSTCOND*/0) #define SIMPLEQ_FOREACH(var, head, field) \ for ((var) = ((head)->sqh_first); \ (var); \ (var) = ((var)->field.sqe_next)) /* * Simple queue access methods. */ #define SIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) #define SIMPLEQ_FIRST(head) ((head)->sqh_first) #define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) /* * Tail queue definitions. */ #define _TAILQ_HEAD(name, type, qual) \ struct name { \ qual type *tqh_first; /* first element */ \ qual type *qual *tqh_last; /* addr of last next element */ \ } #define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,) #define TAILQ_HEAD_INITIALIZER(head) \ { NULL, &(head).tqh_first } #define _TAILQ_ENTRY(type, qual) \ struct { \ qual type *tqe_next; /* next element */ \ qual type *qual *tqe_prev; /* address of previous next element */\ } #define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,) /* * Tail queue functions. */ #define TAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ (head)->tqh_last = &(head)->tqh_first; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ (head)->tqh_first->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (head)->tqh_first = (elm); \ (elm)->field.tqe_prev = &(head)->tqh_first; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ (elm)->field.tqe_prev = (head)->tqh_last; \ *(head)->tqh_last = (elm); \ (head)->tqh_last = &(elm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ (elm)->field.tqe_next->field.tqe_prev = \ &(elm)->field.tqe_next; \ else \ (head)->tqh_last = &(elm)->field.tqe_next; \ (listelm)->field.tqe_next = (elm); \ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ (elm)->field.tqe_next = (listelm); \ *(listelm)->field.tqe_prev = (elm); \ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ (elm)->field.tqe_next->field.tqe_prev = \ (elm)->field.tqe_prev; \ else \ (head)->tqh_last = (elm)->field.tqe_prev; \ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ } while (/*CONSTCOND*/0) #define TAILQ_FOREACH(var, head, field) \ for ((var) = ((head)->tqh_first); \ (var); \ (var) = ((var)->field.tqe_next)) #define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ (var); \ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) #define TAILQ_CONCAT(head1, head2, field) do { \ if (!TAILQ_EMPTY(head2)) { \ *(head1)->tqh_last = (head2)->tqh_first; \ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ (head1)->tqh_last = (head2)->tqh_last; \ TAILQ_INIT((head2)); \ } \ } while (/*CONSTCOND*/0) /* * Tail queue access methods. */ #define TAILQ_EMPTY(head) ((head)->tqh_first == NULL) #define TAILQ_FIRST(head) ((head)->tqh_first) #define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) #define TAILQ_LAST(head, headname) \ (*(((struct headname *)((head)->tqh_last))->tqh_last)) #define TAILQ_PREV(elm, headname, field) \ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) /* * Circular queue definitions. */ #define CIRCLEQ_HEAD(name, type) \ struct name { \ struct type *cqh_first; /* first element */ \ struct type *cqh_last; /* last element */ \ } #define CIRCLEQ_HEAD_INITIALIZER(head) \ { (void *)&head, (void *)&head } #define CIRCLEQ_ENTRY(type) \ struct { \ struct type *cqe_next; /* next element */ \ struct type *cqe_prev; /* previous element */ \ } /* * Circular queue functions. */ #define CIRCLEQ_INIT(head) do { \ (head)->cqh_first = (void *)(head); \ (head)->cqh_last = (void *)(head); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ (elm)->field.cqe_prev = (listelm); \ if ((listelm)->field.cqe_next == (void *)(head)) \ (head)->cqh_last = (elm); \ else \ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ (listelm)->field.cqe_next = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ (elm)->field.cqe_next = (listelm); \ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ if ((listelm)->field.cqe_prev == (void *)(head)) \ (head)->cqh_first = (elm); \ else \ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ (listelm)->field.cqe_prev = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ (elm)->field.cqe_next = (head)->cqh_first; \ (elm)->field.cqe_prev = (void *)(head); \ if ((head)->cqh_last == (void *)(head)) \ (head)->cqh_last = (elm); \ else \ (head)->cqh_first->field.cqe_prev = (elm); \ (head)->cqh_first = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.cqe_next = (void *)(head); \ (elm)->field.cqe_prev = (head)->cqh_last; \ if ((head)->cqh_first == (void *)(head)) \ (head)->cqh_first = (elm); \ else \ (head)->cqh_last->field.cqe_next = (elm); \ (head)->cqh_last = (elm); \ } while (/*CONSTCOND*/0) #define CIRCLEQ_REMOVE(head, elm, field) do { \ if ((elm)->field.cqe_next == (void *)(head)) \ (head)->cqh_last = (elm)->field.cqe_prev; \ else \ (elm)->field.cqe_next->field.cqe_prev = \ (elm)->field.cqe_prev; \ if ((elm)->field.cqe_prev == (void *)(head)) \ (head)->cqh_first = (elm)->field.cqe_next; \ else \ (elm)->field.cqe_prev->field.cqe_next = \ (elm)->field.cqe_next; \ } while (/*CONSTCOND*/0) #define CIRCLEQ_FOREACH(var, head, field) \ for ((var) = ((head)->cqh_first); \ (var) != (const void *)(head); \ (var) = ((var)->field.cqe_next)) #define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ for ((var) = ((head)->cqh_last); \ (var) != (const void *)(head); \ (var) = ((var)->field.cqe_prev)) /* * Circular queue access methods. */ #define CIRCLEQ_EMPTY(head) ((head)->cqh_first == (void *)(head)) #define CIRCLEQ_FIRST(head) ((head)->cqh_first) #define CIRCLEQ_LAST(head) ((head)->cqh_last) #define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) #define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) #define CIRCLEQ_LOOP_NEXT(head, elm, field) \ (((elm)->field.cqe_next == (void *)(head)) \ ? ((head)->cqh_first) \ : (elm->field.cqe_next)) #define CIRCLEQ_LOOP_PREV(head, elm, field) \ (((elm)->field.cqe_prev == (void *)(head)) \ ? ((head)->cqh_last) \ : (elm->field.cqe_prev)) #endif /* sys/queue.h */ drbd-utils-8.9.10/user/v9/drbdsetup_colors.h0000644000175000017500000000234312554136660020632 0ustar apoikosapoikos#ifndef DRBDSETUP_COLORS_H #define DRBDSETUP_COLORS_H #include enum when_color { NEVER_COLOR = -1, AUTO_COLOR = 0, ALWAYS_COLOR = 1 }; extern enum when_color opt_color; extern const char *stop_color_code(void); extern const char *role_color_start(enum drbd_role, bool); extern const char *role_color_stop(enum drbd_role, bool); extern const char *cstate_color_start(enum drbd_conn_state); extern const char *cstate_color_stop(enum drbd_conn_state); extern const char *repl_state_color_start(enum drbd_repl_state); extern const char *repl_state_color_stop(enum drbd_repl_state); extern const char *disk_state_color_start(enum drbd_disk_state, bool); extern const char *disk_state_color_stop(enum drbd_disk_state, bool); #define REPL_COLOR_STRING(__r) \ repl_state_color_start(__r), drbd_repl_str(__r), repl_state_color_stop(__r) #define DISK_COLOR_STRING(__d, __local) \ disk_state_color_start(__d, __local), drbd_disk_str(__d), disk_state_color_stop(__d, __local) #define ROLE_COLOR_STRING(__r, __local) \ role_color_start(__r, __local), drbd_role_str(__r), role_color_stop(__r, __local) #define CONN_COLOR_STRING(__c) \ cstate_color_start(__c), drbd_conn_str(__c), cstate_color_stop(__c) #endif /* DRBDSETUP_COLORS_H */ drbd-utils-8.9.10/user/v9/drbdadm_dump.h0000644000175000017500000000037712477305373017707 0ustar apoikosapoikos#ifndef DRBDADM_DUMP_H #define DRBDADM_DUMP_H #include "drbdadm.h" extern void print_dump_xml_header(void); extern void print_dump_header(void); extern int adm_dump(const struct cfg_ctx *ctx); extern int adm_dump_xml(const struct cfg_ctx *ctx); #endif drbd-utils-8.9.10/user/v9/drbdadm_dump.c0000644000175000017500000004160412572043560017671 0ustar apoikosapoikos/* drbdadm_dump.c This file is part of DRBD by Philipp Reisner and Lars Ellenberg. Copyright (C) 2003-2008, LINBIT Information Technologies GmbH. Copyright (C) 2003-2008, Philipp Reisner . Copyright (C) 2003-2008, Lars Ellenberg . drbd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. drbd is distributed in the hope that 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 drbd; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include "drbdadm.h" #include "drbdtool_common.h" static int indent = 0; #define INDENT_WIDTH 4 #define BFMT "%s;\n" #define IPV4FMT "%-16s %s %s:%s%s" #define IPV6FMT "%-16s %s [%s]:%s%s" #define MDISK "%-16s %s;\n" #define MDISKI "%-16s %s [%s];\n" #define printI(fmt, args... ) printf("%*s" fmt,INDENT_WIDTH * indent,"" , ## args ) #define printA(name, val ) \ printf("%*s%*s %3s;\n", \ INDENT_WIDTH * indent,"" , \ -24+INDENT_WIDTH * indent, \ name, val ) static void dump_options(char *name, struct options *options); static void __dump_options(struct options *options) { struct d_option *option; STAILQ_FOREACH(option, options, link) { if (option->value) printA(option->name, option->is_escaped ? option->value : esc(option-> value)); else printI(BFMT, option->name); } } static void dump_options2(char *name, struct options *options, void(*within)(struct options *), struct options *ctx) { if (STAILQ_EMPTY(options) && (!ctx || (ctx && STAILQ_EMPTY(ctx)))) return; printI("%s {\n", name); ++indent; __dump_options(options); if (within) within(ctx); --indent; printI("}\n"); } static void dump_peer_device_options(struct options *options) { if (!STAILQ_EMPTY(options)) { /* printI("# peer device options:\n"); */ __dump_options(options); } } static void dump_options(char *name, struct options *options) { dump_options2(name, options, NULL, NULL); } static void dump_proxy_plugins(struct options *options) { dump_options("plugin", options); } void dump_global_info() { static const char * const yes_no_ask[] = { [UC_YES] = "yes", [UC_NO] = "no", [UC_ASK] = "ask", }; if (!global_options.minor_count && !global_options.disable_ip_verification && global_options.dialog_refresh == 1 && global_options.usage_count == UC_ASK && !verbose) return; printI("global {\n"); ++indent; if (global_options.disable_ip_verification) printI("disable-ip-verification;\n"); if (global_options.minor_count) printI("minor-count %i;\n", global_options.minor_count); if (global_options.dialog_refresh != 1) printI("dialog-refresh %i;\n", global_options.dialog_refresh); if (global_options.usage_count != UC_ASK) printI("usage-count %s;\n", yes_no_ask[global_options.usage_count]); --indent; printI("}\n\n"); } static void fake_startup_options(struct d_resource *res); static void dump_common_info() { if (!common) return; printI("common {\n"); ++indent; fake_startup_options(common); dump_options("options", &common->res_options); dump_options("net", &common->net_options); dump_options2("disk", &common->disk_options, dump_peer_device_options, &common->pd_options); dump_options("startup", &common->startup_options); dump_options2("proxy", &common->proxy_options, dump_proxy_plugins, &common->proxy_plugins); dump_options("handlers", &common->handlers); --indent; printf("}\n\n"); } static void dump_address(char *name, struct d_address *address, char *postfix) { if (!strcmp(address->af, "ipv6")) printI(IPV6FMT, name, address->af, address->addr, address->port, postfix); else printI(IPV4FMT, name, address->af, address->addr, address->port, postfix); } static void dump_proxy_info(const char *prefix, struct d_proxy_info *pi) { printI("%sproxy on %s {\n", prefix, names_to_str(&pi->on_hosts)); ++indent; dump_address("inside", &pi->inside, ";\n"); dump_address("outside", &pi->outside, ";\n"); dump_options2("options", &pi->options, dump_proxy_plugins, &pi->plugins); --indent; printI("}\n"); } static void dump_volume(int has_lower, struct d_volume *vol) { if (!vol->implicit) { printI("volume %d {\n", vol->vnr); ++indent; } /* Handle volume of '_remote_host' */ if (!vol->device && !vol->disk && !vol->meta_disk && !vol->meta_index) goto out; dump_options2("disk", &vol->disk_options, dump_peer_device_options, &vol->pd_options); if (vol->parsed_device || verbose) { printI("device%*s", -19 + INDENT_WIDTH * indent, ""); if (vol->device) printf("%s ", esc(vol->device)); printf("minor %d;\n", vol->device_minor); } if (!has_lower && (vol->parsed_disk || verbose)) printA("disk", esc(vol->disk ? vol->disk : "none")); if (!has_lower && (vol->parsed_meta_disk || verbose) && vol->disk) { if (!strcmp(vol->meta_index, "flexible")) printI(MDISK, "meta-disk", esc(vol->meta_disk)); else if (!strcmp(vol->meta_index, "internal")) printA("meta-disk", "internal"); else printI(MDISKI, "meta-disk", esc(vol->meta_disk), vol->meta_index); } if (!vol->implicit) { out: --indent; printI("}\n"); } } static void dump_host_info(struct d_host_info *hi) { struct d_volume *vol; if (!hi) { printI(" # No host section data available.\n"); return; } if (hi->implicit && !verbose) return; if (hi->lower) { printI("stacked-on-top-of %s {\n", esc(hi->lower->name)); ++indent; printI("# on %s \n", names_to_str(&hi->on_hosts)); } else if (hi->by_address) { dump_address("floating", &hi->address, " {\n"); ++indent; } else { printI("on %s {\n", names_to_str(&hi->on_hosts)); ++indent; } printI("node-id %s;\n", hi->node_id); dump_options("options", &hi->res_options); for_each_volume(vol, &hi->volumes) { if (vol->parsed_device || vol->parsed_disk || vol->parsed_meta_disk || verbose) dump_volume(!!hi->lower, vol); } if (!hi->by_address && hi->address.addr) dump_address("address", &hi->address, ";\n"); if (hi->proxy_compat_only && !verbose) dump_proxy_info("", hi->proxy_compat_only); --indent; printI("}\n"); } static void dump_hname_address_pairs(struct hname_address_pairs *hname_address_pairs) { struct hname_address *ha; STAILQ_FOREACH(ha, hname_address_pairs, link) { if (ha->by_address || ha->faked_hostname) { dump_address("address", &ha->address, ssprintf("%s# on %s\n", ha->proxy ? " " : "; ", ha->name)); } else { printI("host %s", ha->name); if (ha->parsed_address || (verbose && ha->address.addr)) dump_address(" address", &ha->address, ""); else if (ha->parsed_port) printf(" port %s", ha->address.port); } if (ha->proxy) dump_proxy_info(" via ", ha->proxy); else printf(";\n"); } } static void dump_connection(struct connection *conn) { struct peer_device *pd; struct path *path; if (conn->implicit && !verbose) return; printI("connection"); if (conn->name) printf(" %s", esc(conn->name)); printf(" {\n"); ++indent; path = STAILQ_FIRST(&conn->paths); if (path->implicit && !verbose) { dump_hname_address_pairs(&path->hname_address_pairs); } else { for_each_path(path, &conn->paths) { printI("path {\n"); ++indent; dump_hname_address_pairs(&path->hname_address_pairs); --indent; printI("}\n"); } } dump_options("net", &conn->net_options); dump_options("disk", &conn->pd_options); STAILQ_FOREACH(pd, &conn->peer_devices, connection_link) { if (pd->implicit && !verbose) continue; printI("volume %d {\n", pd->vnr); ++indent; dump_options("disk", &pd->pd_options); --indent; printI("}\n"); } --indent; printI("}\n"); } static void __dump_options_xml(struct options *options) { struct d_option *option; STAILQ_FOREACH(option, options, link) { if (option->value) printI("