diff options
author | sjg <sjg@FreeBSD.org> | 2013-10-13 02:35:19 +0000 |
---|---|---|
committer | sjg <sjg@FreeBSD.org> | 2013-10-13 02:35:19 +0000 |
commit | 7fcd33c1faf567506b5c0b4148c7a15a10788a5d (patch) | |
tree | 2c6f4d1ca5d1c643faea64e1f4c90105a1ab406a /usr.sbin | |
parent | 2a59274eda20cc626e28052fff7aa8b7bf6a3683 (diff) | |
parent | 5cca672bb0892f1c5da630c34a1f98e2de4d7064 (diff) | |
download | FreeBSD-src-7fcd33c1faf567506b5c0b4148c7a15a10788a5d.zip FreeBSD-src-7fcd33c1faf567506b5c0b4148c7a15a10788a5d.tar.gz |
Merge head@256284
Diffstat (limited to 'usr.sbin')
153 files changed, 12697 insertions, 1496 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index f76988f..ba44cd3 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -17,6 +17,7 @@ SUBDIR= adduser \ crashinfo \ cron \ ctladm \ + ctld \ daemon \ dconschat \ devinfo \ @@ -35,6 +36,7 @@ SUBDIR= adduser \ ifmcstat \ inetd \ iostat \ + iscsid \ isfctl \ kldxref \ mailwrapper \ @@ -122,29 +124,6 @@ SUBDIR+= praudit SUBDIR+= authpf .endif -.if ${MK_BIND_DNSSEC} != "no" && ${MK_OPENSSL} != "no" -SUBDIR+= dnssec-dsfromkey -SUBDIR+= dnssec-keyfromlabel -SUBDIR+= dnssec-keygen -SUBDIR+= dnssec-revoke -SUBDIR+= dnssec-settime -SUBDIR+= dnssec-signzone -SUBDIR+= dnssec-verify -.endif -.if ${MK_BIND_NAMED} != "no" -SUBDIR+= arpaname -SUBDIR+= ddns-confgen -SUBDIR+= genrandom -SUBDIR+= isc-hmac-fixup -SUBDIR+= named -SUBDIR+= named-checkconf -SUBDIR+= named-checkzone -SUBDIR+= named-journalprint -SUBDIR+= nsec3hash -SUBDIR+= rndc -SUBDIR+= rndc-confgen -.endif - .if ${MK_BLUETOOTH} != "no" SUBDIR+= bluetooth .endif @@ -315,6 +294,10 @@ SUBDIR+= config SUBDIR+= crunch .endif +.if ${MK_UNBOUND} != "no" +SUBDIR+= unbound +.endif + .if ${MK_USB} != "no" SUBDIR+= uathload SUBDIR+= uhsoctl diff --git a/usr.sbin/arp/arp.c b/usr.sbin/arp/arp.c index 8bdd555..25fbb23 100644 --- a/usr.sbin/arp/arp.c +++ b/usr.sbin/arp/arp.c @@ -187,8 +187,11 @@ main(int argc, char *argv[]) if (argc != 0) usage(); search(0, nuke_entry); - } else + } else { + if (argc != 1) + usage(); rtn = delete(argv[0]); + } break; case F_FILESET: if (argc != 1) diff --git a/usr.sbin/arpaname/Makefile b/usr.sbin/arpaname/Makefile deleted file mode 100644 index 145d18a..0000000 --- a/usr.sbin/arpaname/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/tools - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= arpaname - -.PATH: ${SRCDIR} -SRCS+= arpaname.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 3 - -MAN= arpaname.1 - -.include <bsd.prog.mk> diff --git a/usr.sbin/arpaname/Makefile.depend b/usr.sbin/arpaname/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/arpaname/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile index 17355c3..0644ed7 100644 --- a/usr.sbin/bhyve/Makefile +++ b/usr.sbin/bhyve/Makefile @@ -6,8 +6,8 @@ PROG= bhyve DEBUG_FLAGS= -g -O0 -SRCS= acpi.c atpic.c bhyverun.c consport.c dbgport.c elcr.c inout.c -SRCS+= ioapic.c mem.c mevent.c mptbl.c +SRCS= acpi.c atpic.c bhyverun.c block_if.c consport.c dbgport.c elcr.c +SRCS+= inout.c ioapic.c mem.c mevent.c mptbl.c pci_ahci.c SRCS+= pci_emul.c pci_hostbridge.c pci_passthru.c pci_virtio_block.c SRCS+= pci_virtio_net.c pci_uart.c pit_8254.c pmtmr.c post.c rtc.c SRCS+= virtio.c xmsr.c spinup_ap.c @@ -17,8 +17,8 @@ SRCS+= vmm_instruction_emul.c NO_MAN= -DPADD= ${LIBVMMAPI} ${LIBMD} ${LIBPTHREAD} -LDADD= -lvmmapi -lmd -lpthread +DPADD= ${LIBVMMAPI} ${LIBMD} ${LIBUTIL} ${LIBPTHREAD} +LDADD= -lvmmapi -lmd -lutil -lpthread WARNS?= 2 diff --git a/usr.sbin/bhyve/ahci.h b/usr.sbin/bhyve/ahci.h new file mode 100644 index 0000000..7101dc5 --- /dev/null +++ b/usr.sbin/bhyve/ahci.h @@ -0,0 +1,304 @@ +/*- + * Copyright (c) 1998 - 2008 Søren Schmidt <sos@FreeBSD.org> + * Copyright (c) 2009-2012 Alexander Motin <mav@FreeBSD.org> + * 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, + * without modification, immediately at the beginning of the file. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef _AHCI_H_ +#define _AHCI_H_ + +/* ATA register defines */ +#define ATA_DATA 0 /* (RW) data */ + +#define ATA_FEATURE 1 /* (W) feature */ +#define ATA_F_DMA 0x01 /* enable DMA */ +#define ATA_F_OVL 0x02 /* enable overlap */ + +#define ATA_COUNT 2 /* (W) sector count */ + +#define ATA_SECTOR 3 /* (RW) sector # */ +#define ATA_CYL_LSB 4 /* (RW) cylinder# LSB */ +#define ATA_CYL_MSB 5 /* (RW) cylinder# MSB */ +#define ATA_DRIVE 6 /* (W) Sector/Drive/Head */ +#define ATA_D_LBA 0x40 /* use LBA addressing */ +#define ATA_D_IBM 0xa0 /* 512 byte sectors, ECC */ + +#define ATA_COMMAND 7 /* (W) command */ + +#define ATA_ERROR 8 /* (R) error */ +#define ATA_E_ILI 0x01 /* illegal length */ +#define ATA_E_NM 0x02 /* no media */ +#define ATA_E_ABORT 0x04 /* command aborted */ +#define ATA_E_MCR 0x08 /* media change request */ +#define ATA_E_IDNF 0x10 /* ID not found */ +#define ATA_E_MC 0x20 /* media changed */ +#define ATA_E_UNC 0x40 /* uncorrectable data */ +#define ATA_E_ICRC 0x80 /* UDMA crc error */ +#define ATA_E_ATAPI_SENSE_MASK 0xf0 /* ATAPI sense key mask */ + +#define ATA_IREASON 9 /* (R) interrupt reason */ +#define ATA_I_CMD 0x01 /* cmd (1) | data (0) */ +#define ATA_I_IN 0x02 /* read (1) | write (0) */ +#define ATA_I_RELEASE 0x04 /* released bus (1) */ +#define ATA_I_TAGMASK 0xf8 /* tag mask */ + +#define ATA_STATUS 10 /* (R) status */ +#define ATA_ALTSTAT 11 /* (R) alternate status */ +#define ATA_S_ERROR 0x01 /* error */ +#define ATA_S_INDEX 0x02 /* index */ +#define ATA_S_CORR 0x04 /* data corrected */ +#define ATA_S_DRQ 0x08 /* data request */ +#define ATA_S_DSC 0x10 /* drive seek completed */ +#define ATA_S_SERVICE 0x10 /* drive needs service */ +#define ATA_S_DWF 0x20 /* drive write fault */ +#define ATA_S_DMA 0x20 /* DMA ready */ +#define ATA_S_READY 0x40 /* drive ready */ +#define ATA_S_BUSY 0x80 /* busy */ + +#define ATA_CONTROL 12 /* (W) control */ +#define ATA_A_IDS 0x02 /* disable interrupts */ +#define ATA_A_RESET 0x04 /* RESET controller */ +#define ATA_A_4BIT 0x08 /* 4 head bits */ +#define ATA_A_HOB 0x80 /* High Order Byte enable */ + +/* SATA register defines */ +#define ATA_SSTATUS 13 +#define ATA_SS_DET_MASK 0x0000000f +#define ATA_SS_DET_NO_DEVICE 0x00000000 +#define ATA_SS_DET_DEV_PRESENT 0x00000001 +#define ATA_SS_DET_PHY_ONLINE 0x00000003 +#define ATA_SS_DET_PHY_OFFLINE 0x00000004 + +#define ATA_SS_SPD_MASK 0x000000f0 +#define ATA_SS_SPD_NO_SPEED 0x00000000 +#define ATA_SS_SPD_GEN1 0x00000010 +#define ATA_SS_SPD_GEN2 0x00000020 +#define ATA_SS_SPD_GEN3 0x00000040 + +#define ATA_SS_IPM_MASK 0x00000f00 +#define ATA_SS_IPM_NO_DEVICE 0x00000000 +#define ATA_SS_IPM_ACTIVE 0x00000100 +#define ATA_SS_IPM_PARTIAL 0x00000200 +#define ATA_SS_IPM_SLUMBER 0x00000600 + +#define ATA_SERROR 14 +#define ATA_SE_DATA_CORRECTED 0x00000001 +#define ATA_SE_COMM_CORRECTED 0x00000002 +#define ATA_SE_DATA_ERR 0x00000100 +#define ATA_SE_COMM_ERR 0x00000200 +#define ATA_SE_PROT_ERR 0x00000400 +#define ATA_SE_HOST_ERR 0x00000800 +#define ATA_SE_PHY_CHANGED 0x00010000 +#define ATA_SE_PHY_IERROR 0x00020000 +#define ATA_SE_COMM_WAKE 0x00040000 +#define ATA_SE_DECODE_ERR 0x00080000 +#define ATA_SE_PARITY_ERR 0x00100000 +#define ATA_SE_CRC_ERR 0x00200000 +#define ATA_SE_HANDSHAKE_ERR 0x00400000 +#define ATA_SE_LINKSEQ_ERR 0x00800000 +#define ATA_SE_TRANSPORT_ERR 0x01000000 +#define ATA_SE_UNKNOWN_FIS 0x02000000 +#define ATA_SE_EXCHANGED 0x04000000 + +#define ATA_SCONTROL 15 +#define ATA_SC_DET_MASK 0x0000000f +#define ATA_SC_DET_IDLE 0x00000000 +#define ATA_SC_DET_RESET 0x00000001 +#define ATA_SC_DET_DISABLE 0x00000004 + +#define ATA_SC_SPD_MASK 0x000000f0 +#define ATA_SC_SPD_NO_SPEED 0x00000000 +#define ATA_SC_SPD_SPEED_GEN1 0x00000010 +#define ATA_SC_SPD_SPEED_GEN2 0x00000020 +#define ATA_SC_SPD_SPEED_GEN3 0x00000040 + +#define ATA_SC_IPM_MASK 0x00000f00 +#define ATA_SC_IPM_NONE 0x00000000 +#define ATA_SC_IPM_DIS_PARTIAL 0x00000100 +#define ATA_SC_IPM_DIS_SLUMBER 0x00000200 + +#define ATA_SACTIVE 16 + +#define AHCI_MAX_PORTS 32 +#define AHCI_MAX_SLOTS 32 + +/* SATA AHCI v1.0 register defines */ +#define AHCI_CAP 0x00 +#define AHCI_CAP_NPMASK 0x0000001f +#define AHCI_CAP_SXS 0x00000020 +#define AHCI_CAP_EMS 0x00000040 +#define AHCI_CAP_CCCS 0x00000080 +#define AHCI_CAP_NCS 0x00001F00 +#define AHCI_CAP_NCS_SHIFT 8 +#define AHCI_CAP_PSC 0x00002000 +#define AHCI_CAP_SSC 0x00004000 +#define AHCI_CAP_PMD 0x00008000 +#define AHCI_CAP_FBSS 0x00010000 +#define AHCI_CAP_SPM 0x00020000 +#define AHCI_CAP_SAM 0x00080000 +#define AHCI_CAP_ISS 0x00F00000 +#define AHCI_CAP_ISS_SHIFT 20 +#define AHCI_CAP_SCLO 0x01000000 +#define AHCI_CAP_SAL 0x02000000 +#define AHCI_CAP_SALP 0x04000000 +#define AHCI_CAP_SSS 0x08000000 +#define AHCI_CAP_SMPS 0x10000000 +#define AHCI_CAP_SSNTF 0x20000000 +#define AHCI_CAP_SNCQ 0x40000000 +#define AHCI_CAP_64BIT 0x80000000 + +#define AHCI_GHC 0x04 +#define AHCI_GHC_AE 0x80000000 +#define AHCI_GHC_MRSM 0x00000004 +#define AHCI_GHC_IE 0x00000002 +#define AHCI_GHC_HR 0x00000001 + +#define AHCI_IS 0x08 +#define AHCI_PI 0x0c +#define AHCI_VS 0x10 + +#define AHCI_CCCC 0x14 +#define AHCI_CCCC_TV_MASK 0xffff0000 +#define AHCI_CCCC_TV_SHIFT 16 +#define AHCI_CCCC_CC_MASK 0x0000ff00 +#define AHCI_CCCC_CC_SHIFT 8 +#define AHCI_CCCC_INT_MASK 0x000000f8 +#define AHCI_CCCC_INT_SHIFT 3 +#define AHCI_CCCC_EN 0x00000001 +#define AHCI_CCCP 0x18 + +#define AHCI_EM_LOC 0x1C +#define AHCI_EM_CTL 0x20 +#define AHCI_EM_MR 0x00000001 +#define AHCI_EM_TM 0x00000100 +#define AHCI_EM_RST 0x00000200 +#define AHCI_EM_LED 0x00010000 +#define AHCI_EM_SAFTE 0x00020000 +#define AHCI_EM_SES2 0x00040000 +#define AHCI_EM_SGPIO 0x00080000 +#define AHCI_EM_SMB 0x01000000 +#define AHCI_EM_XMT 0x02000000 +#define AHCI_EM_ALHD 0x04000000 +#define AHCI_EM_PM 0x08000000 + +#define AHCI_CAP2 0x24 +#define AHCI_CAP2_BOH 0x00000001 +#define AHCI_CAP2_NVMP 0x00000002 +#define AHCI_CAP2_APST 0x00000004 + +#define AHCI_OFFSET 0x100 +#define AHCI_STEP 0x80 + +#define AHCI_P_CLB 0x00 +#define AHCI_P_CLBU 0x04 +#define AHCI_P_FB 0x08 +#define AHCI_P_FBU 0x0c +#define AHCI_P_IS 0x10 +#define AHCI_P_IE 0x14 +#define AHCI_P_IX_DHR 0x00000001 +#define AHCI_P_IX_PS 0x00000002 +#define AHCI_P_IX_DS 0x00000004 +#define AHCI_P_IX_SDB 0x00000008 +#define AHCI_P_IX_UF 0x00000010 +#define AHCI_P_IX_DP 0x00000020 +#define AHCI_P_IX_PC 0x00000040 +#define AHCI_P_IX_MP 0x00000080 + +#define AHCI_P_IX_PRC 0x00400000 +#define AHCI_P_IX_IPM 0x00800000 +#define AHCI_P_IX_OF 0x01000000 +#define AHCI_P_IX_INF 0x04000000 +#define AHCI_P_IX_IF 0x08000000 +#define AHCI_P_IX_HBD 0x10000000 +#define AHCI_P_IX_HBF 0x20000000 +#define AHCI_P_IX_TFE 0x40000000 +#define AHCI_P_IX_CPD 0x80000000 + +#define AHCI_P_CMD 0x18 +#define AHCI_P_CMD_ST 0x00000001 +#define AHCI_P_CMD_SUD 0x00000002 +#define AHCI_P_CMD_POD 0x00000004 +#define AHCI_P_CMD_CLO 0x00000008 +#define AHCI_P_CMD_FRE 0x00000010 +#define AHCI_P_CMD_CCS_MASK 0x00001f00 +#define AHCI_P_CMD_CCS_SHIFT 8 +#define AHCI_P_CMD_ISS 0x00002000 +#define AHCI_P_CMD_FR 0x00004000 +#define AHCI_P_CMD_CR 0x00008000 +#define AHCI_P_CMD_CPS 0x00010000 +#define AHCI_P_CMD_PMA 0x00020000 +#define AHCI_P_CMD_HPCP 0x00040000 +#define AHCI_P_CMD_MPSP 0x00080000 +#define AHCI_P_CMD_CPD 0x00100000 +#define AHCI_P_CMD_ESP 0x00200000 +#define AHCI_P_CMD_FBSCP 0x00400000 +#define AHCI_P_CMD_APSTE 0x00800000 +#define AHCI_P_CMD_ATAPI 0x01000000 +#define AHCI_P_CMD_DLAE 0x02000000 +#define AHCI_P_CMD_ALPE 0x04000000 +#define AHCI_P_CMD_ASP 0x08000000 +#define AHCI_P_CMD_ICC_MASK 0xf0000000 +#define AHCI_P_CMD_NOOP 0x00000000 +#define AHCI_P_CMD_ACTIVE 0x10000000 +#define AHCI_P_CMD_PARTIAL 0x20000000 +#define AHCI_P_CMD_SLUMBER 0x60000000 + +#define AHCI_P_TFD 0x20 +#define AHCI_P_SIG 0x24 +#define AHCI_P_SSTS 0x28 +#define AHCI_P_SCTL 0x2c +#define AHCI_P_SERR 0x30 +#define AHCI_P_SACT 0x34 +#define AHCI_P_CI 0x38 +#define AHCI_P_SNTF 0x3C +#define AHCI_P_FBS 0x40 +#define AHCI_P_FBS_EN 0x00000001 +#define AHCI_P_FBS_DEC 0x00000002 +#define AHCI_P_FBS_SDE 0x00000004 +#define AHCI_P_FBS_DEV 0x00000f00 +#define AHCI_P_FBS_DEV_SHIFT 8 +#define AHCI_P_FBS_ADO 0x0000f000 +#define AHCI_P_FBS_ADO_SHIFT 12 +#define AHCI_P_FBS_DWE 0x000f0000 +#define AHCI_P_FBS_DWE_SHIFT 16 + +/* Just to be sure, if building as module. */ +#if MAXPHYS < 512 * 1024 +#undef MAXPHYS +#define MAXPHYS 512 * 1024 +#endif +/* Pessimistic prognosis on number of required S/G entries */ +#define AHCI_SG_ENTRIES (roundup(btoc(MAXPHYS) + 1, 8)) +/* Command list. 32 commands. First, 1Kbyte aligned. */ +#define AHCI_CL_OFFSET 0 +#define AHCI_CL_SIZE 32 +/* Command tables. Up to 32 commands, Each, 128byte aligned. */ +#define AHCI_CT_OFFSET (AHCI_CL_OFFSET + AHCI_CL_SIZE * AHCI_MAX_SLOTS) +#define AHCI_CT_SIZE (128 + AHCI_SG_ENTRIES * 16) +/* Total main work area. */ +#define AHCI_WORK_SIZE (AHCI_CT_OFFSET + AHCI_CT_SIZE * ch->numslots) + +#endif /* _AHCI_H_ */ diff --git a/usr.sbin/bhyve/bhyverun.c b/usr.sbin/bhyve/bhyverun.c index 43eea98..5359442 100644 --- a/usr.sbin/bhyve/bhyverun.c +++ b/usr.sbin/bhyve/bhyverun.c @@ -37,13 +37,14 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <stdlib.h> +#include <err.h> #include <libgen.h> #include <unistd.h> #include <assert.h> #include <errno.h> -#include <signal.h> #include <pthread.h> #include <pthread_np.h> +#include <sysexits.h> #include <machine/vmm.h> #include <vmmapi.h> @@ -61,9 +62,6 @@ __FBSDID("$FreeBSD$"); #include "spinup_ap.h" #include "rtc.h" -#define DEFAULT_GUEST_HZ 100 -#define DEFAULT_GUEST_TSLICE 200 - #define GUEST_NIO_PORT 0x488 /* guest upcalls via i/o port */ #define VMEXIT_SWITCH 0 /* force vcpu switch in mux mode */ @@ -77,14 +75,11 @@ __FBSDID("$FreeBSD$"); typedef int (*vmexit_handler_t)(struct vmctx *, struct vm_exit *, int *vcpu); -int guest_tslice = DEFAULT_GUEST_TSLICE; -int guest_hz = DEFAULT_GUEST_HZ; char *vmname; int guest_ncpus; static int pincpu = -1; -static int guest_vcpu_mux; static int guest_vmexit_on_hlt, guest_vmexit_on_pause, disable_x2apic; static int foundcpus; @@ -102,13 +97,13 @@ static void vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip); struct vm_exit vmexit[VM_MAXCPU]; -struct fbsdstats { +struct bhyvestats { uint64_t vmexit_bogus; uint64_t vmexit_bogus_switch; uint64_t vmexit_hlt; uint64_t vmexit_pause; uint64_t vmexit_mtrap; - uint64_t vmexit_paging; + uint64_t vmexit_inst_emul; uint64_t cpu_switch_rotate; uint64_t cpu_switch_direct; int io_reset; @@ -125,28 +120,24 @@ usage(int code) { fprintf(stderr, - "Usage: %s [-aehABHIP][-g <gdb port>][-z <hz>][-s <pci>]" - "[-S <pci>][-p pincpu][-n <pci>][-m lowmem][-M highmem]" + "Usage: %s [-aehAHIP][-g <gdb port>][-s <pci>][-S <pci>]" + "[-c vcpus][-p pincpu][-m mem]" " <vmname>\n" " -a: local apic is in XAPIC mode (default is X2APIC)\n" " -A: create an ACPI table\n" - " -g: gdb port (default is %d and 0 means don't open)\n" + " -g: gdb port\n" " -c: # cpus (default 1)\n" " -p: pin vcpu 'n' to host cpu 'pincpu + n'\n" - " -B: inject breakpoint exception on vm entry\n" " -H: vmexit from the guest on hlt\n" " -I: present an ioapic to the guest\n" " -P: vmexit from the guest on pause\n" " -e: exit on unhandled i/o access\n" " -h: help\n" - " -z: guest hz (default is %d)\n" " -s: <slot,driver,configinfo> PCI slot config\n" " -S: <slot,driver,configinfo> legacy PCI slot config\n" - " -m: memory size in MB\n" - " -x: mux vcpus to 1 hcpu\n" - " -t: mux vcpu timeslice hz (default %d)\n", - progname, DEFAULT_GDB_PORT, DEFAULT_GUEST_HZ, - DEFAULT_GUEST_TSLICE); + " -m: memory size in MB\n", + progname); + exit(code); } @@ -178,13 +169,6 @@ fbsdrun_vmexit_on_hlt(void) return (guest_vmexit_on_hlt); } -int -fbsdrun_muxed(void) -{ - - return (guest_vcpu_mux); -} - static void * fbsdrun_start_thread(void *param) { @@ -226,25 +210,12 @@ fbsdrun_addcpu(struct vmctx *ctx, int vcpu, uint64_t rip) vmexit[vcpu].rip = rip; vmexit[vcpu].inst_length = 0; - if (vcpu == BSP || !guest_vcpu_mux){ - mt_vmm_info[vcpu].mt_ctx = ctx; - mt_vmm_info[vcpu].mt_vcpu = vcpu; - - error = pthread_create(&mt_vmm_info[vcpu].mt_thr, NULL, - fbsdrun_start_thread, &mt_vmm_info[vcpu]); - assert(error == 0); - } -} - -static int -fbsdrun_get_next_cpu(int curcpu) -{ + mt_vmm_info[vcpu].mt_ctx = ctx; + mt_vmm_info[vcpu].mt_vcpu = vcpu; - /* - * Get the next available CPU. Assumes they arrive - * in ascending order with no gaps. - */ - return ((curcpu + 1) % foundcpus); + error = pthread_create(&mt_vmm_info[vcpu].mt_thr, NULL, + fbsdrun_start_thread, &mt_vmm_info[vcpu]); + assert(error == 0); } static int @@ -264,17 +235,10 @@ static int vmexit_handle_notify(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu, uint32_t eax) { -#if PG_DEBUG /* put all types of debug here */ - if (eax == 0) { - pause_noswitch = 1; - } else if (eax == 1) { - pause_noswitch = 0; - } else { - pause_noswitch = 0; - if (eax == 5) { - vm_set_capability(ctx, *pvcpu, VM_CAP_MTRAP_EXIT, 1); - } - } +#if BHYVE_DEBUG + /* + * put guest-driven debug here + */ #endif return (VMEXIT_CONTINUE); } @@ -337,11 +301,6 @@ vmexit_wrmsr(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) newcpu = emulate_wrmsr(ctx, *pvcpu, vme->u.msr.code,vme->u.msr.wval); - if (guest_vcpu_mux && *pvcpu != newcpu) { - retval = VMEXIT_SWITCH; - *pvcpu = newcpu; - } - return (retval); } @@ -354,11 +313,6 @@ vmexit_spinup_ap(struct vmctx *ctx, struct vm_exit *vme, int *pvcpu) newcpu = spinup_ap(ctx, *pvcpu, vme->u.spinup_ap.vcpu, vme->u.spinup_ap.rip); - if (guest_vcpu_mux && *pvcpu != newcpu) { - retval = VMEXIT_SWITCH; - *pvcpu = newcpu; - } - return (retval); } @@ -378,71 +332,55 @@ vmexit_vmx(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) return (VMEXIT_ABORT); } -static int bogus_noswitch = 1; - static int vmexit_bogus(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { + stats.vmexit_bogus++; - if (!guest_vcpu_mux || guest_ncpus == 1 || bogus_noswitch) { - return (VMEXIT_RESTART); - } else { - stats.vmexit_bogus_switch++; - vmexit->inst_length = 0; - *pvcpu = -1; - return (VMEXIT_SWITCH); - } + return (VMEXIT_RESTART); } static int vmexit_hlt(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { + stats.vmexit_hlt++; - if (fbsdrun_muxed()) { - *pvcpu = -1; - return (VMEXIT_SWITCH); - } else { - /* - * Just continue execution with the next instruction. We use - * the HLT VM exit as a way to be friendly with the host - * scheduler. - */ - return (VMEXIT_CONTINUE); - } -} -static int pause_noswitch; + /* + * Just continue execution with the next instruction. We use + * the HLT VM exit as a way to be friendly with the host + * scheduler. + */ + return (VMEXIT_CONTINUE); +} static int vmexit_pause(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { + stats.vmexit_pause++; - if (fbsdrun_muxed() && !pause_noswitch) { - *pvcpu = -1; - return (VMEXIT_SWITCH); - } else { - return (VMEXIT_CONTINUE); - } + return (VMEXIT_CONTINUE); } static int vmexit_mtrap(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { + stats.vmexit_mtrap++; return (VMEXIT_RESTART); } static int -vmexit_paging(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) +vmexit_inst_emul(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) { int err; - stats.vmexit_paging++; + stats.vmexit_inst_emul++; - err = emulate_mem(ctx, *pvcpu, vmexit->u.paging.gpa, - &vmexit->u.paging.vie); + err = emulate_mem(ctx, *pvcpu, vmexit->u.inst_emul.gpa, + &vmexit->u.inst_emul.vie); if (err) { if (err == EINVAL) { @@ -451,7 +389,7 @@ vmexit_paging(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) vmexit->rip); } else if (err == ESRCH) { fprintf(stderr, "Unhandled memory access to 0x%lx\n", - vmexit->u.paging.gpa); + vmexit->u.inst_emul.gpa); } return (VMEXIT_ABORT); @@ -460,39 +398,6 @@ vmexit_paging(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu) return (VMEXIT_CONTINUE); } -static void -sigalrm(int sig) -{ - return; -} - -static void -setup_timeslice(void) -{ - struct sigaction sa; - struct itimerval itv; - int error; - - /* - * Setup a realtime timer to generate a SIGALRM at a - * frequency of 'guest_tslice' ticks per second. - */ - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = sigalrm; - - error = sigaction(SIGALRM, &sa, NULL); - assert(error == 0); - - itv.it_interval.tv_sec = 0; - itv.it_interval.tv_usec = 1000000 / guest_tslice; - itv.it_value.tv_sec = 0; - itv.it_value.tv_usec = 1000000 / guest_tslice; - - error = setitimer(ITIMER_REAL, &itv, NULL); - assert(error == 0); -} - static vmexit_handler_t handler[VM_EXITCODE_MAX] = { [VM_EXITCODE_INOUT] = vmexit_inout, [VM_EXITCODE_VMX] = vmexit_vmx, @@ -500,7 +405,7 @@ static vmexit_handler_t handler[VM_EXITCODE_MAX] = { [VM_EXITCODE_RDMSR] = vmexit_rdmsr, [VM_EXITCODE_WRMSR] = vmexit_wrmsr, [VM_EXITCODE_MTRAP] = vmexit_mtrap, - [VM_EXITCODE_PAGING] = vmexit_paging, + [VM_EXITCODE_INST_EMUL] = vmexit_inst_emul, [VM_EXITCODE_SPINUP_AP] = vmexit_spinup_ap, }; @@ -511,9 +416,6 @@ vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip) int error, rc, prevcpu; enum vm_exitcode exitcode; - if (guest_vcpu_mux) - setup_timeslice(); - if (pincpu >= 0) { CPU_ZERO(&mask); CPU_SET(pincpu + vcpu, &mask); @@ -550,15 +452,6 @@ vm_loop(struct vmctx *ctx, int vcpu, uint64_t rip) rc = (*handler[exitcode])(ctx, &vmexit[vcpu], &vcpu); switch (rc) { - case VMEXIT_SWITCH: - assert(guest_vcpu_mux); - if (vcpu == -1) { - stats.cpu_switch_rotate++; - vcpu = fbsdrun_get_next_cpu(prevcpu); - } else { - stats.cpu_switch_direct++; - } - /* fall through */ case VMEXIT_CONTINUE: rip = vmexit[vcpu].rip + vmexit[vcpu].inst_length; break; @@ -594,21 +487,20 @@ num_vcpus_allowed(struct vmctx *ctx) int main(int argc, char *argv[]) { - int c, error, gdb_port, inject_bkpt, tmp, err, ioapic, bvmcons; + int c, error, gdb_port, tmp, err, ioapic, bvmcons; int max_vcpus; struct vmctx *ctx; uint64_t rip; size_t memsize; bvmcons = 0; - inject_bkpt = 0; progname = basename(argv[0]); - gdb_port = DEFAULT_GDB_PORT; + gdb_port = 0; guest_ncpus = 1; ioapic = 0; memsize = 256 * MB; - while ((c = getopt(argc, argv, "abehABHIPxp:g:c:z:s:S:n:m:")) != -1) { + while ((c = getopt(argc, argv, "abehAHIPp:g:c:s:S:m:")) != -1) { switch (c) { case 'a': disable_x2apic = 1; @@ -619,12 +511,6 @@ main(int argc, char *argv[]) case 'b': bvmcons = 1; break; - case 'B': - inject_bkpt = 1; - break; - case 'x': - guest_vcpu_mux = 1; - break; case 'p': pincpu = atoi(optarg); break; @@ -634,12 +520,6 @@ main(int argc, char *argv[]) case 'g': gdb_port = atoi(optarg); break; - case 'z': - guest_hz = atoi(optarg); - break; - case 't': - guest_tslice = atoi(optarg); - break; case 's': if (pci_parse_slot(optarg, 0) != 0) exit(1); @@ -651,7 +531,9 @@ main(int argc, char *argv[]) else break; case 'm': - memsize = strtoul(optarg, NULL, 0) * MB; + error = vm_parse_memsize(optarg, &memsize); + if (error) + errx(EX_USAGE, "invalid memsize '%s'", optarg); break; case 'H': guest_vmexit_on_hlt = 1; @@ -677,16 +559,6 @@ main(int argc, char *argv[]) if (argc != 1) usage(1); - /* No need to mux if guest is uni-processor */ - if (guest_ncpus <= 1) - guest_vcpu_mux = 0; - - /* vmexit on hlt if guest is muxed */ - if (guest_vcpu_mux) { - guest_vmexit_on_hlt = 1; - guest_vmexit_on_pause = 1; - } - vmname = argv[0]; ctx = vm_open(vmname); @@ -765,11 +637,6 @@ main(int argc, char *argv[]) error = vm_get_register(ctx, BSP, VM_REG_GUEST_RIP, &rip); assert(error == 0); - if (inject_bkpt) { - error = vm_inject_event(ctx, BSP, VM_HW_EXCEPTION, IDT_BP); - assert(error == 0); - } - /* * build the guest tables, MP etc. */ diff --git a/usr.sbin/bhyve/bhyverun.h b/usr.sbin/bhyve/bhyverun.h index ffe3018..28ffb3c 100644 --- a/usr.sbin/bhyve/bhyverun.h +++ b/usr.sbin/bhyve/bhyverun.h @@ -36,8 +36,6 @@ #endif struct vmctx; -extern int guest_hz; -extern int guest_tslice; extern int guest_ncpus; extern char *vmname; diff --git a/usr.sbin/bhyve/block_if.c b/usr.sbin/bhyve/block_if.c new file mode 100644 index 0000000..9228b9a --- /dev/null +++ b/usr.sbin/bhyve/block_if.c @@ -0,0 +1,426 @@ +/*- + * Copyright (c) 2013 Peter Grehan <grehan@freebsd.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/errno.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/disk.h> + +#include <assert.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> +#include <pthread_np.h> +#include <unistd.h> + +#include "bhyverun.h" +#include "block_if.h" + +#define BLOCKIF_SIG 0xb109b109 + +#define BLOCKIF_MAXREQ 16 + +enum blockop { + BOP_READ, + BOP_WRITE, + BOP_FLUSH, + BOP_CANCEL +}; + +enum blockstat { + BST_FREE, + BST_INUSE +}; + +struct blockif_elem { + TAILQ_ENTRY(blockif_elem) be_link; + struct blockif_req *be_req; + enum blockop be_op; + enum blockstat be_status; +}; + +struct blockif_ctxt { + int bc_magic; + int bc_fd; + int bc_rdonly; + off_t bc_size; + int bc_sectsz; + pthread_t bc_btid; + pthread_mutex_t bc_mtx; + pthread_cond_t bc_cond; + int bc_closing; + + /* Request elements and free/inuse queues */ + TAILQ_HEAD(, blockif_elem) bc_freeq; + TAILQ_HEAD(, blockif_elem) bc_inuseq; + u_int bc_req_count; + struct blockif_elem bc_reqs[BLOCKIF_MAXREQ]; +}; + +static int +blockif_enqueue(struct blockif_ctxt *bc, struct blockif_req *breq, + enum blockop op) +{ + struct blockif_elem *be; + + assert(bc->bc_req_count < BLOCKIF_MAXREQ); + + be = TAILQ_FIRST(&bc->bc_freeq); + assert(be != NULL); + assert(be->be_status == BST_FREE); + + TAILQ_REMOVE(&bc->bc_freeq, be, be_link); + be->be_status = BST_INUSE; + be->be_req = breq; + be->be_op = op; + TAILQ_INSERT_TAIL(&bc->bc_inuseq, be, be_link); + + bc->bc_req_count++; + + return (0); +} + +static int +blockif_dequeue(struct blockif_ctxt *bc, struct blockif_elem *el) +{ + struct blockif_elem *be; + + if (bc->bc_req_count == 0) + return (ENOENT); + + be = TAILQ_FIRST(&bc->bc_inuseq); + assert(be != NULL); + assert(be->be_status == BST_INUSE); + *el = *be; + + TAILQ_REMOVE(&bc->bc_inuseq, be, be_link); + be->be_status = BST_FREE; + be->be_req = NULL; + TAILQ_INSERT_TAIL(&bc->bc_freeq, be, be_link); + + bc->bc_req_count--; + + return (0); +} + +static void +blockif_proc(struct blockif_ctxt *bc, struct blockif_elem *be) +{ + struct blockif_req *br; + int err; + + br = be->be_req; + err = 0; + + switch (be->be_op) { + case BOP_READ: + if (preadv(bc->bc_fd, br->br_iov, br->br_iovcnt, + br->br_offset) < 0) + err = errno; + break; + case BOP_WRITE: + if (bc->bc_rdonly) + err = EROFS; + else if (pwritev(bc->bc_fd, br->br_iov, br->br_iovcnt, + br->br_offset) < 0) + err = errno; + break; + case BOP_FLUSH: + break; + case BOP_CANCEL: + err = EINTR; + break; + default: + err = EINVAL; + break; + } + + (*br->br_callback)(br, err); +} + +static void * +blockif_thr(void *arg) +{ + struct blockif_ctxt *bc; + struct blockif_elem req; + + bc = arg; + + for (;;) { + pthread_mutex_lock(&bc->bc_mtx); + while (!blockif_dequeue(bc, &req)) { + pthread_mutex_unlock(&bc->bc_mtx); + blockif_proc(bc, &req); + pthread_mutex_lock(&bc->bc_mtx); + } + pthread_cond_wait(&bc->bc_cond, &bc->bc_mtx); + pthread_mutex_unlock(&bc->bc_mtx); + + /* + * Check ctxt status here to see if exit requested + */ + if (bc->bc_closing) + pthread_exit(NULL); + } + + /* Not reached */ + return (NULL); +} + +struct blockif_ctxt * +blockif_open(const char *optstr, const char *ident) +{ + char tname[MAXCOMLEN + 1]; + char *nopt, *xopts; + struct blockif_ctxt *bc; + struct stat sbuf; + off_t size; + int extra, fd, i, sectsz; + int nocache, sync, ro; + + nocache = 0; + sync = 0; + ro = 0; + + /* + * The first element in the optstring is always a pathname. + * Optional elements follow + */ + nopt = strdup(optstr); + for (xopts = strtok(nopt, ","); + xopts != NULL; + xopts = strtok(NULL, ",")) { + if (!strcmp(xopts, "nocache")) + nocache = 1; + else if (!strcmp(xopts, "sync")) + sync = 1; + else if (!strcmp(xopts, "ro")) + ro = 1; + } + + extra = 0; + if (nocache) + extra |= O_DIRECT; + if (sync) + extra |= O_SYNC; + + fd = open(nopt, (ro ? O_RDONLY : O_RDWR) | extra); + if (fd < 0 && !ro) { + /* Attempt a r/w fail with a r/o open */ + fd = open(nopt, O_RDONLY | extra); + ro = 1; + } + + if (fd < 0) { + perror("Could not open backing file"); + return (NULL); + } + + if (fstat(fd, &sbuf) < 0) { + perror("Could not stat backing file"); + close(fd); + return (NULL); + } + + /* + * Deal with raw devices + */ + size = sbuf.st_size; + sectsz = DEV_BSIZE; + if (S_ISCHR(sbuf.st_mode)) { + if (ioctl(fd, DIOCGMEDIASIZE, &size) < 0 || + ioctl(fd, DIOCGSECTORSIZE, §sz)) { + perror("Could not fetch dev blk/sector size"); + close(fd); + return (NULL); + } + assert(size != 0); + assert(sectsz != 0); + } + + bc = malloc(sizeof(struct blockif_ctxt)); + if (bc == NULL) { + close(fd); + return (NULL); + } + + memset(bc, 0, sizeof(*bc)); + bc->bc_magic = BLOCKIF_SIG; + bc->bc_fd = fd; + bc->bc_size = size; + bc->bc_sectsz = sectsz; + pthread_mutex_init(&bc->bc_mtx, NULL); + pthread_cond_init(&bc->bc_cond, NULL); + TAILQ_INIT(&bc->bc_freeq); + TAILQ_INIT(&bc->bc_inuseq); + bc->bc_req_count = 0; + for (i = 0; i < BLOCKIF_MAXREQ; i++) { + bc->bc_reqs[i].be_status = BST_FREE; + TAILQ_INSERT_HEAD(&bc->bc_freeq, &bc->bc_reqs[i], be_link); + } + + pthread_create(&bc->bc_btid, NULL, blockif_thr, bc); + + snprintf(tname, sizeof(tname), "%s blk-%s", vmname, ident); + pthread_set_name_np(bc->bc_btid, tname); + + return (bc); +} + +static int +blockif_request(struct blockif_ctxt *bc, struct blockif_req *breq, + enum blockop op) +{ + int err; + + err = 0; + + pthread_mutex_lock(&bc->bc_mtx); + if (bc->bc_req_count < BLOCKIF_MAXREQ) { + /* + * Enqueue and inform the block i/o thread + * that there is work available + */ + blockif_enqueue(bc, breq, op); + pthread_cond_signal(&bc->bc_cond); + } else { + /* + * Callers are not allowed to enqueue more than + * the specified blockif queue limit. Return an + * error to indicate that the queue length has been + * exceeded. + */ + err = E2BIG; + } + pthread_mutex_unlock(&bc->bc_mtx); + + return (err); +} + +int +blockif_read(struct blockif_ctxt *bc, struct blockif_req *breq) +{ + + assert(bc->bc_magic == BLOCKIF_SIG); + return (blockif_request(bc, breq, BOP_READ)); +} + +int +blockif_write(struct blockif_ctxt *bc, struct blockif_req *breq) +{ + + assert(bc->bc_magic == BLOCKIF_SIG); + return (blockif_request(bc, breq, BOP_WRITE)); +} + +int +blockif_flush(struct blockif_ctxt *bc, struct blockif_req *breq) +{ + + assert(bc->bc_magic == BLOCKIF_SIG); + return (blockif_request(bc, breq, BOP_FLUSH)); +} + +int +blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq) +{ + + assert(bc->bc_magic == BLOCKIF_SIG); + return (blockif_request(bc, breq, BOP_CANCEL)); +} + +int +blockif_close(struct blockif_ctxt *bc) +{ + void *jval; + int err; + + err = 0; + + assert(bc->bc_magic == BLOCKIF_SIG); + + /* + * Stop the block i/o thread + */ + bc->bc_closing = 1; + pthread_cond_signal(&bc->bc_cond); + pthread_join(bc->bc_btid, &jval); + + /* XXX Cancel queued i/o's ??? */ + + /* + * Release resources + */ + bc->bc_magic = 0; + close(bc->bc_fd); + free(bc); + + return (0); +} + +/* + * Accessors + */ +off_t +blockif_size(struct blockif_ctxt *bc) +{ + + assert(bc->bc_magic == BLOCKIF_SIG); + return (bc->bc_size); +} + +int +blockif_sectsz(struct blockif_ctxt *bc) +{ + + assert(bc->bc_magic == BLOCKIF_SIG); + return (bc->bc_sectsz); +} + +int +blockif_queuesz(struct blockif_ctxt *bc) +{ + + assert(bc->bc_magic == BLOCKIF_SIG); + return (BLOCKIF_MAXREQ); +} + +int +blockif_is_ro(struct blockif_ctxt *bc) +{ + + assert(bc->bc_magic == BLOCKIF_SIG); + return (bc->bc_rdonly); +} diff --git a/usr.sbin/bhyve/block_if.h b/usr.sbin/bhyve/block_if.h new file mode 100644 index 0000000..e0c0bb1 --- /dev/null +++ b/usr.sbin/bhyve/block_if.h @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2013 Peter Grehan <grehan@freebsd.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD$ + */ + +/* + * The block API to be used by bhyve block-device emulations. The routines + * are thread safe, with no assumptions about the context of the completion + * callback - it may occur in the caller's context, or asynchronously in + * another thread. + */ + +#ifndef _BLOCK_IF_H_ +#define _BLOCK_IF_H_ + +#include <sys/uio.h> +#include <sys/unistd.h> + +#define BLOCKIF_IOV_MAX 32 /* not practical to be IOV_MAX */ + +struct blockif_req { + struct iovec br_iov[BLOCKIF_IOV_MAX]; + int br_iovcnt; + off_t br_offset; + void (*br_callback)(struct blockif_req *req, int err); + void *br_param; +}; + +struct blockif_ctxt; +struct blockif_ctxt *blockif_open(const char *optstr, const char *ident); +off_t blockif_size(struct blockif_ctxt *bc); +int blockif_sectsz(struct blockif_ctxt *bc); +int blockif_queuesz(struct blockif_ctxt *bc); +int blockif_is_ro(struct blockif_ctxt *bc); +int blockif_read(struct blockif_ctxt *bc, struct blockif_req *breq); +int blockif_write(struct blockif_ctxt *bc, struct blockif_req *breq); +int blockif_flush(struct blockif_ctxt *bc, struct blockif_req *breq); +int blockif_cancel(struct blockif_ctxt *bc, struct blockif_req *breq); +int blockif_close(struct blockif_ctxt *bc); + +#endif /* _BLOCK_IF_H_ */ diff --git a/usr.sbin/bhyve/dbgport.h b/usr.sbin/bhyve/dbgport.h index 8c7dab7..2ddcbf8 100644 --- a/usr.sbin/bhyve/dbgport.h +++ b/usr.sbin/bhyve/dbgport.h @@ -29,8 +29,6 @@ #ifndef _DBGPORT_H_ #define _DBGPORT_H_ -#define DEFAULT_GDB_PORT 6466 - void init_dbgport(int port); #endif diff --git a/usr.sbin/bhyve/mevent.c b/usr.sbin/bhyve/mevent.c index a6109db..eff0610 100644 --- a/usr.sbin/bhyve/mevent.c +++ b/usr.sbin/bhyve/mevent.c @@ -59,12 +59,15 @@ __FBSDID("$FreeBSD$"); extern char *vmname; static pthread_t mevent_tid; +static int mevent_timid = 43; static int mevent_pipefd[2]; static pthread_mutex_t mevent_lmutex = PTHREAD_MUTEX_INITIALIZER; struct mevent { void (*me_func)(int, enum ev_type, void *); +#define me_msecs me_fd int me_fd; + int me_timid; enum ev_type me_type; void *me_param; int me_cq; @@ -129,6 +132,9 @@ mevent_kq_filter(struct mevent *mevp) if (mevp->me_type == EVF_WRITE) retval = EVFILT_WRITE; + if (mevp->me_type == EVF_TIMER) + retval = EVFILT_TIMER; + return (retval); } @@ -140,6 +146,8 @@ mevent_kq_flags(struct mevent *mevp) switch (mevp->me_state) { case MEV_ENABLE: ret = EV_ADD; + if (mevp->me_type == EVF_TIMER) + ret |= EV_ENABLE; break; case MEV_DISABLE: ret = EV_DISABLE; @@ -177,11 +185,16 @@ mevent_build(int mfd, struct kevent *kev) */ close(mevp->me_fd); } else { - kev[i].ident = mevp->me_fd; + if (mevp->me_type == EVF_TIMER) { + kev[i].ident = mevp->me_timid; + kev[i].data = mevp->me_msecs; + } else { + kev[i].ident = mevp->me_fd; + kev[i].data = 0; + } kev[i].filter = mevent_kq_filter(mevp); kev[i].flags = mevent_kq_flags(mevp); kev[i].fflags = mevent_kq_fflags(mevp); - kev[i].data = 0; kev[i].udata = mevp; i++; } @@ -219,12 +232,12 @@ mevent_handle(struct kevent *kev, int numev) } struct mevent * -mevent_add(int fd, enum ev_type type, +mevent_add(int tfd, enum ev_type type, void (*func)(int, enum ev_type, void *), void *param) { struct mevent *lp, *mevp; - if (fd < 0 || func == NULL) { + if (tfd < 0 || func == NULL) { return (NULL); } @@ -236,13 +249,15 @@ mevent_add(int fd, enum ev_type type, * Verify that the fd/type tuple is not present in any list */ LIST_FOREACH(lp, &global_head, me_list) { - if (lp->me_fd == fd && lp->me_type == type) { + if (type != EVF_TIMER && lp->me_fd == tfd && + lp->me_type == type) { goto exit; } } LIST_FOREACH(lp, &change_head, me_list) { - if (lp->me_fd == fd && lp->me_type == type) { + if (type != EVF_TIMER && lp->me_fd == tfd && + lp->me_type == type) { goto exit; } } @@ -256,7 +271,11 @@ mevent_add(int fd, enum ev_type type, } memset(mevp, 0, sizeof(struct mevent)); - mevp->me_fd = fd; + if (type == EVF_TIMER) { + mevp->me_msecs = tfd; + mevp->me_timid = mevent_timid++; + } else + mevp->me_fd = tfd; mevp->me_type = type; mevp->me_func = func; mevp->me_param = param; diff --git a/usr.sbin/bhyve/mevent.h b/usr.sbin/bhyve/mevent.h index 32a9d74..6c0f656 100644 --- a/usr.sbin/bhyve/mevent.h +++ b/usr.sbin/bhyve/mevent.h @@ -31,7 +31,8 @@ enum ev_type { EVF_READ, - EVF_WRITE + EVF_WRITE, + EVF_TIMER }; struct mevent; diff --git a/usr.sbin/bhyve/mevent_test.c b/usr.sbin/bhyve/mevent_test.c index c72a497..9c68ff7 100644 --- a/usr.sbin/bhyve/mevent_test.c +++ b/usr.sbin/bhyve/mevent_test.c @@ -34,12 +34,16 @@ */ #include <sys/types.h> +#include <sys/stdint.h> +#include <sys/sysctl.h> #include <sys/socket.h> #include <netinet/in.h> +#include <machine/cpufunc.h> #include <stdio.h> #include <stdlib.h> #include <pthread.h> +#include <unistd.h> #include "mevent.h" @@ -48,8 +52,62 @@ static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t accept_condvar = PTHREAD_COND_INITIALIZER; +static struct mevent *tevp; + +char *vmname = "test vm"; + + #define MEVENT_ECHO +/* Number of timer events to capture */ +#define TEVSZ 4096 +uint64_t tevbuf[TEVSZ]; + +static void +timer_print(void) +{ + uint64_t min, max, diff, sum, tsc_freq; + size_t len; + int j; + + min = UINT64_MAX; + max = 0; + sum = 0; + + len = sizeof(tsc_freq); + sysctlbyname("machdep.tsc_freq", &tsc_freq, &len, NULL, 0); + + for (j = 1; j < TEVSZ; j++) { + /* Convert a tsc diff into microseconds */ + diff = (tevbuf[j] - tevbuf[j-1]) * 1000000 / tsc_freq; + sum += diff; + if (min > diff) + min = diff; + if (max < diff) + max = diff; + } + + printf("timers done: usecs, min %ld, max %ld, mean %ld\n", min, max, + sum/(TEVSZ - 1)); +} + +static void +timer_callback(int fd, enum ev_type type, void *param) +{ + static int i; + + if (i >= TEVSZ) + abort(); + + tevbuf[i++] = rdtsc(); + + if (i == TEVSZ) { + mevent_delete(tevp); + timer_print(); + } +} + + #ifdef MEVENT_ECHO struct esync { pthread_mutex_t e_mt; @@ -101,6 +159,8 @@ echoer(void *param) pthread_mutex_unlock(&sync.e_mt); pthread_mutex_destroy(&sync.e_mt); pthread_cond_destroy(&sync.e_cond); + + return (NULL); } #else @@ -115,6 +175,8 @@ echoer(void *param) while ((len = read(fd, buf, sizeof(buf))) > 0) { write(1, buf, len); } + + return (NULL); } #endif /* MEVENT_ECHO */ @@ -133,6 +195,7 @@ acceptor(void *param) pthread_t tid; int news; int s; + static int first; if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); @@ -163,11 +226,24 @@ acceptor(void *param) if (news < 0) { perror("accept error"); } else { + static int first = 1; + + if (first) { + /* + * Start a timer + */ + first = 0; + tevp = mevent_add(1, EVF_TIMER, timer_callback, + NULL); + } + printf("incoming connection, spawning thread\n"); pthread_create(&tid, NULL, echoer, (void *)(uintptr_t)news); } } + + return (NULL); } main() diff --git a/usr.sbin/bhyve/pci_ahci.c b/usr.sbin/bhyve/pci_ahci.c new file mode 100644 index 0000000..f69f127 --- /dev/null +++ b/usr.sbin/bhyve/pci_ahci.c @@ -0,0 +1,1805 @@ +/*- + * Copyright (c) 2013 Zhixiang Yu <zcore@freebsd.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/linker_set.h> +#include <sys/stat.h> +#include <sys/uio.h> +#include <sys/ioctl.h> +#include <sys/disk.h> +#include <sys/ata.h> +#include <sys/endian.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <assert.h> +#include <pthread.h> +#include <inttypes.h> + +#include "bhyverun.h" +#include "pci_emul.h" +#include "ahci.h" +#include "block_if.h" + +#define MAX_PORTS 6 /* Intel ICH8 AHCI supports 6 ports */ + +#define PxSIG_ATA 0x00000101 /* ATA drive */ +#define PxSIG_ATAPI 0xeb140101 /* ATAPI drive */ + +enum sata_fis_type { + FIS_TYPE_REGH2D = 0x27, /* Register FIS - host to device */ + FIS_TYPE_REGD2H = 0x34, /* Register FIS - device to host */ + FIS_TYPE_DMAACT = 0x39, /* DMA activate FIS - device to host */ + FIS_TYPE_DMASETUP = 0x41, /* DMA setup FIS - bidirectional */ + FIS_TYPE_DATA = 0x46, /* Data FIS - bidirectional */ + FIS_TYPE_BIST = 0x58, /* BIST activate FIS - bidirectional */ + FIS_TYPE_PIOSETUP = 0x5F, /* PIO setup FIS - device to host */ + FIS_TYPE_SETDEVBITS = 0xA1, /* Set dev bits FIS - device to host */ +}; + +/* + * SCSI opcodes + */ +#define TEST_UNIT_READY 0x00 +#define REQUEST_SENSE 0x03 +#define INQUIRY 0x12 +#define START_STOP_UNIT 0x1B +#define PREVENT_ALLOW 0x1E +#define READ_CAPACITY 0x25 +#define READ_10 0x28 +#define POSITION_TO_ELEMENT 0x2B +#define READ_TOC 0x43 +#define GET_EVENT_STATUS_NOTIFICATION 0x4A +#define MODE_SENSE_10 0x5A +#define READ_12 0xA8 +#define READ_CD 0xBE + +/* + * SCSI mode page codes + */ +#define MODEPAGE_RW_ERROR_RECOVERY 0x01 +#define MODEPAGE_CD_CAPABILITIES 0x2A + +/* + * Debug printf + */ +#ifdef AHCI_DEBUG +static FILE *dbg; +#define DPRINTF(format, arg...) do{fprintf(dbg, format, ##arg);fflush(dbg);}while(0) +#else +#define DPRINTF(format, arg...) +#endif +#define WPRINTF(format, arg...) printf(format, ##arg) + +struct ahci_ioreq { + struct blockif_req io_req; + struct ahci_port *io_pr; + STAILQ_ENTRY(ahci_ioreq) io_list; + uint8_t *cfis; + uint32_t len; + uint32_t done; + int slot; + int prdtl; +}; + +struct ahci_port { + struct blockif_ctxt *bctx; + struct pci_ahci_softc *pr_sc; + uint8_t *cmd_lst; + uint8_t *rfis; + int atapi; + int reset; + int mult_sectors; + uint8_t xfermode; + uint8_t sense_key; + uint8_t asc; + + uint32_t clb; + uint32_t clbu; + uint32_t fb; + uint32_t fbu; + uint32_t is; + uint32_t ie; + uint32_t cmd; + uint32_t unused0; + uint32_t tfd; + uint32_t sig; + uint32_t ssts; + uint32_t sctl; + uint32_t serr; + uint32_t sact; + uint32_t ci; + uint32_t sntf; + uint32_t fbs; + + /* + * i/o request info + */ + struct ahci_ioreq *ioreq; + int ioqsz; + STAILQ_HEAD(ahci_fhead, ahci_ioreq) iofhd; +}; + +struct ahci_cmd_hdr { + uint16_t flags; + uint16_t prdtl; + uint32_t prdbc; + uint64_t ctba; + uint32_t reserved[4]; +}; + +struct ahci_prdt_entry { + uint64_t dba; + uint32_t reserved; + uint32_t dbc; +}; + +struct pci_ahci_softc { + struct pci_devinst *asc_pi; + pthread_mutex_t mtx; + int ports; + uint32_t cap; + uint32_t ghc; + uint32_t is; + uint32_t pi; + uint32_t vs; + uint32_t ccc_ctl; + uint32_t ccc_pts; + uint32_t em_loc; + uint32_t em_ctl; + uint32_t cap2; + uint32_t bohc; + struct ahci_port port[MAX_PORTS]; +}; +#define ahci_ctx(sc) ((sc)->asc_pi->pi_vmctx) + +static inline void lba_to_msf(uint8_t *buf, int lba) +{ + lba += 150; + buf[0] = (lba / 75) / 60; + buf[1] = (lba / 75) % 60; + buf[2] = lba % 75; +} + +/* + * generate HBA intr depending on whether or not ports within + * the controller have an interrupt pending. + */ +static void +ahci_generate_intr(struct pci_ahci_softc *sc) +{ + int i; + + for (i = 0; i < sc->ports; i++) { + struct ahci_port *pr; + pr = &sc->port[i]; + if (pr->is & pr->ie) + sc->is |= (1 << i); + } + + DPRINTF("%s %x\n", __func__, sc->is); + + if (sc->is && (sc->ghc & AHCI_GHC_IE)) + pci_generate_msi(sc->asc_pi, 0); +} + +static void +ahci_write_fis(struct ahci_port *p, enum sata_fis_type ft, uint8_t *fis) +{ + int offset, len, irq; + + if (p->rfis == NULL || !(p->cmd & AHCI_P_CMD_FRE)) + return; + + switch (ft) { + case FIS_TYPE_REGD2H: + offset = 0x40; + len = 20; + irq = AHCI_P_IX_DHR; + break; + case FIS_TYPE_SETDEVBITS: + offset = 0x58; + len = 8; + irq = AHCI_P_IX_SDB; + break; + case FIS_TYPE_PIOSETUP: + offset = 0x20; + len = 20; + irq = 0; + break; + default: + WPRINTF("unsupported fis type %d\n", ft); + return; + } + memcpy(p->rfis + offset, fis, len); + if (irq) { + p->is |= irq; + ahci_generate_intr(p->pr_sc); + } +} + +static void +ahci_write_fis_sdb(struct ahci_port *p, int slot, uint32_t tfd) +{ + uint8_t fis[8]; + uint8_t error; + + error = (tfd >> 8) & 0xff; + memset(fis, 0, sizeof(fis)); + fis[0] = error; + fis[2] = tfd & 0x77; + *(uint32_t *)(fis + 4) = (1 << slot); + if (fis[2] & ATA_S_ERROR) + p->is |= AHCI_P_IX_TFE; + p->tfd = tfd; + ahci_write_fis(p, FIS_TYPE_SETDEVBITS, fis); +} + +static void +ahci_write_fis_d2h(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd) +{ + uint8_t fis[20]; + uint8_t error; + + error = (tfd >> 8) & 0xff; + memset(fis, 0, sizeof(fis)); + fis[0] = FIS_TYPE_REGD2H; + fis[1] = (1 << 6); + fis[2] = tfd & 0xff; + fis[3] = error; + fis[4] = cfis[4]; + fis[5] = cfis[5]; + fis[6] = cfis[6]; + fis[7] = cfis[7]; + fis[8] = cfis[8]; + fis[9] = cfis[9]; + fis[10] = cfis[10]; + fis[11] = cfis[11]; + fis[12] = cfis[12]; + fis[13] = cfis[13]; + if (fis[2] & ATA_S_ERROR) + p->is |= AHCI_P_IX_TFE; + p->tfd = tfd; + p->ci &= ~(1 << slot); + ahci_write_fis(p, FIS_TYPE_REGD2H, fis); +} + +static void +ahci_write_reset_fis_d2h(struct ahci_port *p) +{ + uint8_t fis[20]; + + memset(fis, 0, sizeof(fis)); + fis[0] = FIS_TYPE_REGD2H; + fis[3] = 1; + fis[4] = 1; + if (p->atapi) { + fis[5] = 0x14; + fis[6] = 0xeb; + } + fis[12] = 1; + ahci_write_fis(p, FIS_TYPE_REGD2H, fis); +} + +static void +ahci_port_reset(struct ahci_port *pr) +{ + pr->sctl = 0; + pr->serr = 0; + pr->sact = 0; + pr->xfermode = ATA_UDMA6; + pr->mult_sectors = 128; + + if (!pr->bctx) { + pr->ssts = ATA_SS_DET_NO_DEVICE; + pr->sig = 0xFFFFFFFF; + pr->tfd = 0x7F; + return; + } + pr->ssts = ATA_SS_DET_PHY_ONLINE | ATA_SS_SPD_GEN2 | + ATA_SS_IPM_ACTIVE; + pr->tfd = (1 << 8) | ATA_S_DSC | ATA_S_DMA; + if (!pr->atapi) { + pr->sig = PxSIG_ATA; + pr->tfd |= ATA_S_READY; + } else + pr->sig = PxSIG_ATAPI; + ahci_write_reset_fis_d2h(pr); +} + +static void +ahci_reset(struct pci_ahci_softc *sc) +{ + int i; + + sc->ghc = AHCI_GHC_AE; + sc->is = 0; + for (i = 0; i < sc->ports; i++) { + sc->port[i].ie = 0; + sc->port[i].is = 0; + ahci_port_reset(&sc->port[i]); + } +} + +static void +ata_string(uint8_t *dest, const char *src, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (*src) + dest[i ^ 1] = *src++; + else + dest[i ^ 1] = ' '; + } +} + +static void +atapi_string(uint8_t *dest, const char *src, int len) +{ + int i; + + for (i = 0; i < len; i++) { + if (*src) + dest[i] = *src++; + else + dest[i] = ' '; + } +} + +static void +ahci_handle_dma(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done, + int seek) +{ + struct ahci_ioreq *aior; + struct blockif_req *breq; + struct pci_ahci_softc *sc; + struct ahci_prdt_entry *prdt; + struct ahci_cmd_hdr *hdr; + uint64_t lba; + uint32_t len; + int i, err, iovcnt, ncq, readop; + + sc = p->pr_sc; + prdt = (struct ahci_prdt_entry *)(cfis + 0x80); + hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE); + ncq = 0; + readop = 1; + + prdt += seek; + if (cfis[2] == ATA_WRITE_DMA || cfis[2] == ATA_WRITE_DMA48 || + cfis[2] == ATA_WRITE_FPDMA_QUEUED) + readop = 0; + + if (cfis[2] == ATA_WRITE_FPDMA_QUEUED || + cfis[2] == ATA_READ_FPDMA_QUEUED) { + lba = ((uint64_t)cfis[10] << 40) | + ((uint64_t)cfis[9] << 32) | + ((uint64_t)cfis[8] << 24) | + ((uint64_t)cfis[6] << 16) | + ((uint64_t)cfis[5] << 8) | + cfis[4]; + len = cfis[11] << 8 | cfis[3]; + if (!len) + len = 65536; + ncq = 1; + } else if (cfis[2] == ATA_READ_DMA48 || cfis[2] == ATA_WRITE_DMA48) { + lba = ((uint64_t)cfis[10] << 40) | + ((uint64_t)cfis[9] << 32) | + ((uint64_t)cfis[8] << 24) | + ((uint64_t)cfis[6] << 16) | + ((uint64_t)cfis[5] << 8) | + cfis[4]; + len = cfis[13] << 8 | cfis[12]; + if (!len) + len = 65536; + } else { + lba = ((cfis[7] & 0xf) << 24) | (cfis[6] << 16) | + (cfis[5] << 8) | cfis[4]; + len = cfis[12]; + if (!len) + len = 256; + } + lba *= blockif_sectsz(p->bctx); + len *= blockif_sectsz(p->bctx); + + /* + * Pull request off free list + */ + aior = STAILQ_FIRST(&p->iofhd); + assert(aior != NULL); + STAILQ_REMOVE_HEAD(&p->iofhd, io_list); + aior->cfis = cfis; + aior->slot = slot; + aior->len = len; + aior->done = done; + breq = &aior->io_req; + breq->br_offset = lba + done; + iovcnt = hdr->prdtl - seek; + if (iovcnt > BLOCKIF_IOV_MAX) { + aior->prdtl = iovcnt - BLOCKIF_IOV_MAX; + iovcnt = BLOCKIF_IOV_MAX; + } else + aior->prdtl = 0; + breq->br_iovcnt = iovcnt; + + /* + * Build up the iovec based on the prdt + */ + for (i = 0; i < iovcnt; i++) { + breq->br_iov[i].iov_base = paddr_guest2host(ahci_ctx(sc), + prdt->dba, prdt->dbc + 1); + breq->br_iov[i].iov_len = prdt->dbc + 1; + aior->done += (prdt->dbc + 1); + prdt++; + } + if (readop) + err = blockif_read(p->bctx, breq); + else + err = blockif_write(p->bctx, breq); + assert(err == 0); + + if (!aior->prdtl && ncq) + p->ci &= ~(1 << slot); +} + +static void +ahci_handle_flush(struct ahci_port *p, int slot, uint8_t *cfis) +{ + struct ahci_ioreq *aior; + struct blockif_req *breq; + int err; + + /* + * Pull request off free list + */ + aior = STAILQ_FIRST(&p->iofhd); + assert(aior != NULL); + STAILQ_REMOVE_HEAD(&p->iofhd, io_list); + aior->cfis = cfis; + aior->slot = slot; + aior->len = 0; + breq = &aior->io_req; + + err = blockif_flush(p->bctx, breq); + assert(err == 0); +} + +static inline void +write_prdt(struct ahci_port *p, int slot, uint8_t *cfis, + void *buf, int size) +{ + struct ahci_cmd_hdr *hdr; + struct ahci_prdt_entry *prdt; + void *from; + int i, len; + + hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE); + len = size; + from = buf; + prdt = (struct ahci_prdt_entry *)(cfis + 0x80); + for (i = 0; i < hdr->prdtl && len; i++) { + uint8_t *ptr = paddr_guest2host(ahci_ctx(p->pr_sc), + prdt->dba, prdt->dbc + 1); + memcpy(ptr, from, prdt->dbc + 1); + len -= (prdt->dbc + 1); + from += (prdt->dbc + 1); + prdt++; + } + hdr->prdbc = size - len; +} + +static void +handle_identify(struct ahci_port *p, int slot, uint8_t *cfis) +{ + struct ahci_cmd_hdr *hdr; + + hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE); + if (p->atapi || hdr->prdtl == 0) { + p->tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR; + p->is |= AHCI_P_IX_TFE; + } else { + uint16_t buf[256]; + uint64_t sectors; + + sectors = blockif_size(p->bctx) / blockif_sectsz(p->bctx); + memset(buf, 0, sizeof(buf)); + buf[0] = 0x0040; + /* TODO emulate different serial? */ + ata_string((uint8_t *)(buf+10), "123456", 20); + ata_string((uint8_t *)(buf+23), "001", 8); + ata_string((uint8_t *)(buf+27), "BHYVE SATA DISK", 40); + buf[47] = (0x8000 | 128); + buf[48] = 0x1; + buf[49] = (1 << 8 | 1 << 9 | 1 << 11); + buf[50] = (1 << 14); + buf[53] = (1 << 1 | 1 << 2); + if (p->mult_sectors) + buf[59] = (0x100 | p->mult_sectors); + buf[60] = sectors; + buf[61] = (sectors >> 16); + buf[63] = 0x7; + if (p->xfermode & ATA_WDMA0) + buf[63] |= (1 << ((p->xfermode & 7) + 8)); + buf[64] = 0x3; + buf[65] = 100; + buf[66] = 100; + buf[67] = 100; + buf[68] = 100; + buf[75] = 31; + buf[76] = (1 << 8 | 1 << 2); + buf[80] = 0x1f0; + buf[81] = 0x28; + buf[82] = (1 << 5 | 1 << 14); + buf[83] = (1 << 10 | 1 << 12 | 1 << 13 | 1 << 14); + buf[84] = (1 << 14); + buf[85] = (1 << 5 | 1 << 14); + buf[86] = (1 << 10 | 1 << 12 | 1 << 13); + buf[87] = (1 << 14); + buf[88] = 0x7f; + if (p->xfermode & ATA_UDMA0) + buf[88] |= (1 << ((p->xfermode & 7) + 8)); + buf[93] = (1 | 1 <<14); + buf[100] = sectors; + buf[101] = (sectors >> 16); + buf[102] = (sectors >> 32); + buf[103] = (sectors >> 48); + write_prdt(p, slot, cfis, (void *)buf, sizeof(buf)); + p->tfd = ATA_S_DSC | ATA_S_READY; + p->is |= AHCI_P_IX_DP; + } + p->ci &= ~(1 << slot); + ahci_generate_intr(p->pr_sc); +} + +static void +handle_atapi_identify(struct ahci_port *p, int slot, uint8_t *cfis) +{ + if (!p->atapi) { + p->tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR; + p->is |= AHCI_P_IX_TFE; + } else { + uint16_t buf[256]; + + memset(buf, 0, sizeof(buf)); + buf[0] = (2 << 14 | 5 << 8 | 1 << 7 | 2 << 5); + /* TODO emulate different serial? */ + ata_string((uint8_t *)(buf+10), "123456", 20); + ata_string((uint8_t *)(buf+23), "001", 8); + ata_string((uint8_t *)(buf+27), "BHYVE SATA DVD ROM", 40); + buf[49] = (1 << 9 | 1 << 8); + buf[50] = (1 << 14 | 1); + buf[53] = (1 << 2 | 1 << 1); + buf[62] = 0x3f; + buf[63] = 7; + buf[64] = 3; + buf[65] = 100; + buf[66] = 100; + buf[67] = 100; + buf[68] = 100; + buf[76] = (1 << 2 | 1 << 1); + buf[78] = (1 << 5); + buf[80] = (0x1f << 4); + buf[82] = (1 << 4); + buf[83] = (1 << 14); + buf[84] = (1 << 14); + buf[85] = (1 << 4); + buf[87] = (1 << 14); + buf[88] = (1 << 14 | 0x7f); + write_prdt(p, slot, cfis, (void *)buf, sizeof(buf)); + p->tfd = ATA_S_DSC | ATA_S_READY; + p->is |= AHCI_P_IX_DHR; + } + p->ci &= ~(1 << slot); + ahci_generate_intr(p->pr_sc); +} + +static void +atapi_inquiry(struct ahci_port *p, int slot, uint8_t *cfis) +{ + uint8_t buf[36]; + uint8_t *acmd; + int len; + + acmd = cfis + 0x40; + + buf[0] = 0x05; + buf[1] = 0x80; + buf[2] = 0x00; + buf[3] = 0x21; + buf[4] = 31; + buf[5] = 0; + buf[6] = 0; + buf[7] = 0; + atapi_string(buf + 8, "BHYVE", 8); + atapi_string(buf + 16, "BHYVE DVD-ROM", 16); + atapi_string(buf + 32, "001", 4); + + len = sizeof(buf); + if (len > acmd[4]) + len = acmd[4]; + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + write_prdt(p, slot, cfis, buf, len); + ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); +} + +static void +atapi_read_capacity(struct ahci_port *p, int slot, uint8_t *cfis) +{ + uint8_t buf[8]; + uint64_t sectors; + + sectors = blockif_size(p->bctx) / blockif_sectsz(p->bctx); + sectors >>= 2; + be32enc(buf, sectors - 1); + be32enc(buf + 4, 2048); + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + write_prdt(p, slot, cfis, buf, sizeof(buf)); + ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); +} + +static void +atapi_read_toc(struct ahci_port *p, int slot, uint8_t *cfis) +{ + uint8_t *acmd; + uint8_t format; + int len; + + acmd = cfis + 0x40; + + len = be16dec(acmd + 7); + format = acmd[9] >> 6; + switch (format) { + case 0: + { + int msf, size; + uint64_t sectors; + uint8_t start_track, buf[20], *bp; + + msf = (acmd[1] >> 1) & 1; + start_track = acmd[6]; + if (start_track > 1 && start_track != 0xaa) { + uint32_t tfd; + p->sense_key = ATA_SENSE_ILLEGAL_REQUEST; + p->asc = 0x24; + tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR; + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, tfd); + return; + } + bp = buf + 2; + *bp++ = 1; + *bp++ = 1; + if (start_track <= 1) { + *bp++ = 0; + *bp++ = 0x14; + *bp++ = 1; + *bp++ = 0; + if (msf) { + *bp++ = 0; + lba_to_msf(bp, 0); + bp += 3; + } else { + *bp++ = 0; + *bp++ = 0; + *bp++ = 0; + *bp++ = 0; + } + } + *bp++ = 0; + *bp++ = 0x14; + *bp++ = 0xaa; + *bp++ = 0; + sectors = blockif_size(p->bctx) / blockif_sectsz(p->bctx); + sectors >>= 2; + if (msf) { + *bp++ = 0; + lba_to_msf(bp, sectors); + bp += 3; + } else { + be32enc(bp, sectors); + bp += 4; + } + size = bp - buf; + be16enc(buf, size - 2); + if (len > size) + len = size; + write_prdt(p, slot, cfis, buf, len); + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); + break; + } + case 1: + { + uint8_t buf[12]; + + memset(buf, 0, sizeof(buf)); + buf[1] = 0xa; + buf[2] = 0x1; + buf[3] = 0x1; + if (len > sizeof(buf)) + len = sizeof(buf); + write_prdt(p, slot, cfis, buf, len); + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); + break; + } + case 2: + { + int msf, size; + uint64_t sectors; + uint8_t start_track, *bp, buf[50]; + + msf = (acmd[1] >> 1) & 1; + start_track = acmd[6]; + bp = buf + 2; + *bp++ = 1; + *bp++ = 1; + + *bp++ = 1; + *bp++ = 0x14; + *bp++ = 0; + *bp++ = 0xa0; + *bp++ = 0; + *bp++ = 0; + *bp++ = 0; + *bp++ = 0; + *bp++ = 1; + *bp++ = 0; + *bp++ = 0; + + *bp++ = 1; + *bp++ = 0x14; + *bp++ = 0; + *bp++ = 0xa1; + *bp++ = 0; + *bp++ = 0; + *bp++ = 0; + *bp++ = 0; + *bp++ = 1; + *bp++ = 0; + *bp++ = 0; + + *bp++ = 1; + *bp++ = 0x14; + *bp++ = 0; + *bp++ = 0xa2; + *bp++ = 0; + *bp++ = 0; + *bp++ = 0; + sectors = blockif_size(p->bctx) / blockif_sectsz(p->bctx); + sectors >>= 2; + if (msf) { + *bp++ = 0; + lba_to_msf(bp, sectors); + bp += 3; + } else { + be32enc(bp, sectors); + bp += 4; + } + + *bp++ = 1; + *bp++ = 0x14; + *bp++ = 0; + *bp++ = 1; + *bp++ = 0; + *bp++ = 0; + *bp++ = 0; + if (msf) { + *bp++ = 0; + lba_to_msf(bp, 0); + bp += 3; + } else { + *bp++ = 0; + *bp++ = 0; + *bp++ = 0; + *bp++ = 0; + } + + size = bp - buf; + be16enc(buf, size - 2); + if (len > size) + len = size; + write_prdt(p, slot, cfis, buf, len); + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); + break; + } + default: + { + uint32_t tfd; + + p->sense_key = ATA_SENSE_ILLEGAL_REQUEST; + p->asc = 0x24; + tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR; + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, tfd); + break; + } + } +} + +static void +atapi_read(struct ahci_port *p, int slot, uint8_t *cfis, + uint32_t done, int seek) +{ + struct ahci_ioreq *aior; + struct ahci_cmd_hdr *hdr; + struct ahci_prdt_entry *prdt; + struct blockif_req *breq; + struct pci_ahci_softc *sc; + uint8_t *acmd; + uint64_t lba; + uint32_t len; + int i, err, iovcnt; + + sc = p->pr_sc; + acmd = cfis + 0x40; + hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE); + prdt = (struct ahci_prdt_entry *)(cfis + 0x80); + + prdt += seek; + lba = be32dec(acmd + 2); + if (acmd[0] == READ_10) + len = be16dec(acmd + 7); + else + len = be32dec(acmd + 6); + if (len == 0) { + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); + } + lba *= 2048; + len *= 2048; + + /* + * Pull request off free list + */ + aior = STAILQ_FIRST(&p->iofhd); + assert(aior != NULL); + STAILQ_REMOVE_HEAD(&p->iofhd, io_list); + aior->cfis = cfis; + aior->slot = slot; + aior->len = len; + aior->done = done; + breq = &aior->io_req; + breq->br_offset = lba + done; + iovcnt = hdr->prdtl - seek; + if (iovcnt > BLOCKIF_IOV_MAX) { + aior->prdtl = iovcnt - BLOCKIF_IOV_MAX; + iovcnt = BLOCKIF_IOV_MAX; + } else + aior->prdtl = 0; + breq->br_iovcnt = iovcnt; + + /* + * Build up the iovec based on the prdt + */ + for (i = 0; i < hdr->prdtl; i++) { + breq->br_iov[i].iov_base = paddr_guest2host(ahci_ctx(sc), + prdt->dba, prdt->dbc + 1); + breq->br_iov[i].iov_len = prdt->dbc + 1; + aior->done += (prdt->dbc + 1); + prdt++; + } + err = blockif_read(p->bctx, breq); + assert(err == 0); +} + +static void +atapi_request_sense(struct ahci_port *p, int slot, uint8_t *cfis) +{ + uint8_t buf[64]; + uint8_t *acmd; + int len; + + acmd = cfis + 0x40; + len = acmd[4]; + if (len > sizeof(buf)) + len = sizeof(buf); + memset(buf, 0, len); + buf[0] = 0x70 | (1 << 7); + buf[2] = p->sense_key; + buf[7] = 10; + buf[12] = p->asc; + write_prdt(p, slot, cfis, buf, len); + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); +} + +static void +atapi_start_stop_unit(struct ahci_port *p, int slot, uint8_t *cfis) +{ + uint8_t *acmd = cfis + 0x40; + uint32_t tfd; + + switch (acmd[4] & 3) { + case 0: + case 1: + case 3: + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + tfd = ATA_S_READY | ATA_S_DSC; + break; + case 2: + /* TODO eject media */ + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + p->sense_key = ATA_SENSE_ILLEGAL_REQUEST; + p->asc = 0x53; + tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR; + break; + } + ahci_write_fis_d2h(p, slot, cfis, tfd); +} + +static void +atapi_mode_sense(struct ahci_port *p, int slot, uint8_t *cfis) +{ + uint8_t *acmd; + uint32_t tfd; + uint8_t pc, code; + int len; + + acmd = cfis + 0x40; + len = be16dec(acmd + 7); + pc = acmd[2] >> 6; + code = acmd[2] & 0x3f; + + switch (pc) { + case 0: + switch (code) { + case MODEPAGE_RW_ERROR_RECOVERY: + { + uint8_t buf[16]; + + if (len > sizeof(buf)) + len = sizeof(buf); + + memset(buf, 0, sizeof(buf)); + be16enc(buf, 16 - 2); + buf[2] = 0x70; + buf[8] = 0x01; + buf[9] = 16 - 10; + buf[11] = 0x05; + write_prdt(p, slot, cfis, buf, len); + tfd = ATA_S_READY | ATA_S_DSC; + break; + } + case MODEPAGE_CD_CAPABILITIES: + { + uint8_t buf[30]; + + if (len > sizeof(buf)) + len = sizeof(buf); + + memset(buf, 0, sizeof(buf)); + be16enc(buf, 30 - 2); + buf[2] = 0x70; + buf[8] = 0x2A; + buf[9] = 30 - 10; + buf[10] = 0x08; + buf[12] = 0x71; + be16enc(&buf[18], 2); + be16enc(&buf[20], 512); + write_prdt(p, slot, cfis, buf, len); + tfd = ATA_S_READY | ATA_S_DSC; + break; + } + default: + goto error; + break; + } + break; + case 3: + p->sense_key = ATA_SENSE_ILLEGAL_REQUEST; + p->asc = 0x39; + tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR; + break; +error: + case 1: + case 2: + p->sense_key = ATA_SENSE_ILLEGAL_REQUEST; + p->asc = 0x24; + tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR; + break; + } + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, tfd); +} + +static void +atapi_get_event_status_notification(struct ahci_port *p, int slot, + uint8_t *cfis) +{ + uint8_t *acmd; + uint32_t tfd; + + acmd = cfis + 0x40; + + /* we don't support asynchronous operation */ + if (!(acmd[1] & 1)) { + p->sense_key = ATA_SENSE_ILLEGAL_REQUEST; + p->asc = 0x24; + tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR; + } else { + uint8_t buf[8]; + int len; + + len = be16dec(acmd + 7); + if (len > sizeof(buf)) + len = sizeof(buf); + + memset(buf, 0, sizeof(buf)); + be16enc(buf, 8 - 2); + buf[2] = 0x04; + buf[3] = 0x10; + buf[5] = 0x02; + write_prdt(p, slot, cfis, buf, len); + tfd = ATA_S_READY | ATA_S_DSC; + } + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, tfd); +} + +static void +handle_packet_cmd(struct ahci_port *p, int slot, uint8_t *cfis) +{ + uint8_t *acmd; + + acmd = cfis + 0x40; + +#ifdef AHCI_DEBUG + { + int i; + DPRINTF("ACMD:"); + for (i = 0; i < 16; i++) + DPRINTF("%02x ", acmd[i]); + DPRINTF("\n"); + } +#endif + + switch (acmd[0]) { + case TEST_UNIT_READY: + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); + break; + case INQUIRY: + atapi_inquiry(p, slot, cfis); + break; + case READ_CAPACITY: + atapi_read_capacity(p, slot, cfis); + break; + case PREVENT_ALLOW: + /* TODO */ + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); + break; + case READ_TOC: + atapi_read_toc(p, slot, cfis); + break; + case READ_10: + case READ_12: + atapi_read(p, slot, cfis, 0, 0); + break; + case REQUEST_SENSE: + atapi_request_sense(p, slot, cfis); + break; + case START_STOP_UNIT: + atapi_start_stop_unit(p, slot, cfis); + break; + case MODE_SENSE_10: + atapi_mode_sense(p, slot, cfis); + break; + case GET_EVENT_STATUS_NOTIFICATION: + atapi_get_event_status_notification(p, slot, cfis); + break; + default: + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + p->sense_key = ATA_SENSE_ILLEGAL_REQUEST; + p->asc = 0x20; + ahci_write_fis_d2h(p, slot, cfis, (p->sense_key << 12) | + ATA_S_READY | ATA_S_ERROR); + break; + } +} + +static void +ahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis) +{ + + switch (cfis[2]) { + case ATA_ATA_IDENTIFY: + handle_identify(p, slot, cfis); + break; + case ATA_SETFEATURES: + { + switch (cfis[3]) { + case ATA_SF_ENAB_WCACHE: + case ATA_SF_DIS_WCACHE: + case ATA_SF_ENAB_RCACHE: + case ATA_SF_DIS_RCACHE: + p->tfd = ATA_S_DSC | ATA_S_READY; + break; + case ATA_SF_SETXFER: + { + switch (cfis[12] & 0xf8) { + case ATA_PIO: + case ATA_PIO0: + break; + case ATA_WDMA0: + case ATA_UDMA0: + p->xfermode = (cfis[12] & 0x7); + break; + } + p->tfd = ATA_S_DSC | ATA_S_READY; + break; + } + default: + p->tfd = ATA_S_ERROR | ATA_S_READY; + p->tfd |= (ATA_ERROR_ABORT << 8); + break; + } + p->is |= AHCI_P_IX_DP; + p->ci &= ~(1 << slot); + ahci_generate_intr(p->pr_sc); + break; + } + case ATA_SET_MULTI: + if (cfis[12] != 0 && + (cfis[12] > 128 || (cfis[12] & (cfis[12] - 1)))) { + p->tfd = ATA_S_ERROR | ATA_S_READY; + p->tfd |= (ATA_ERROR_ABORT << 8); + } else { + p->mult_sectors = cfis[12]; + p->tfd = ATA_S_DSC | ATA_S_READY; + } + p->is |= AHCI_P_IX_DP; + p->ci &= ~(1 << slot); + ahci_generate_intr(p->pr_sc); + break; + case ATA_READ_DMA: + case ATA_WRITE_DMA: + case ATA_READ_DMA48: + case ATA_WRITE_DMA48: + case ATA_READ_FPDMA_QUEUED: + case ATA_WRITE_FPDMA_QUEUED: + ahci_handle_dma(p, slot, cfis, 0, 0); + break; + case ATA_FLUSHCACHE: + case ATA_FLUSHCACHE48: + ahci_handle_flush(p, slot, cfis); + break; + case ATA_STANDBY_CMD: + break; + case ATA_NOP: + case ATA_STANDBY_IMMEDIATE: + case ATA_IDLE_IMMEDIATE: + case ATA_SLEEP: + ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); + break; + case ATA_ATAPI_IDENTIFY: + handle_atapi_identify(p, slot, cfis); + break; + case ATA_PACKET_CMD: + if (!p->atapi) { + p->tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR; + p->is |= AHCI_P_IX_TFE; + p->ci &= ~(1 << slot); + ahci_generate_intr(p->pr_sc); + } else + handle_packet_cmd(p, slot, cfis); + break; + default: + WPRINTF("Unsupported cmd:%02x\n", cfis[2]); + p->tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR; + p->is |= AHCI_P_IX_TFE; + p->ci &= ~(1 << slot); + ahci_generate_intr(p->pr_sc); + break; + } +} + +static void +ahci_handle_slot(struct ahci_port *p, int slot) +{ + struct ahci_cmd_hdr *hdr; + struct ahci_prdt_entry *prdt; + struct pci_ahci_softc *sc; + uint8_t *cfis; + int cfl; + + sc = p->pr_sc; + hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE); + cfl = (hdr->flags & 0x1f) * 4; + cfis = paddr_guest2host(ahci_ctx(sc), hdr->ctba, + 0x80 + hdr->prdtl * sizeof(struct ahci_prdt_entry)); + prdt = (struct ahci_prdt_entry *)(cfis + 0x80); + +#ifdef AHCI_DEBUG + DPRINTF("\ncfis:"); + for (i = 0; i < cfl; i++) { + if (i % 10 == 0) + DPRINTF("\n"); + DPRINTF("%02x ", cfis[i]); + } + DPRINTF("\n"); + + for (i = 0; i < hdr->prdtl; i++) { + DPRINTF("%d@%08"PRIx64"\n", prdt->dbc & 0x3fffff, prdt->dba); + prdt++; + } +#endif + + if (cfis[0] != FIS_TYPE_REGH2D) { + WPRINTF("Not a H2D FIS:%02x\n", cfis[0]); + return; + } + + if (cfis[1] & 0x80) { + ahci_handle_cmd(p, slot, cfis); + } else { + if (cfis[15] & (1 << 2)) + p->reset = 1; + else if (p->reset) { + p->reset = 0; + ahci_port_reset(p); + } + p->ci &= ~(1 << slot); + } +} + +static void +ahci_handle_port(struct ahci_port *p) +{ + int i; + + if (!(p->cmd & AHCI_P_CMD_ST)) + return; + + for (i = 0; (i < 32) && p->ci; i++) { + if (p->ci & (1 << i)) + ahci_handle_slot(p, i); + } +} + +/* + * blockif callback routine - this runs in the context of the blockif + * i/o thread, so the mutex needs to be acquired. + */ +static void +ata_ioreq_cb(struct blockif_req *br, int err) +{ + struct ahci_cmd_hdr *hdr; + struct ahci_ioreq *aior; + struct ahci_port *p; + struct pci_ahci_softc *sc; + uint32_t tfd; + uint8_t *cfis; + int pending, slot, ncq; + + DPRINTF("%s %d\n", __func__, err); + + ncq = 0; + aior = br->br_param; + p = aior->io_pr; + cfis = aior->cfis; + slot = aior->slot; + pending = aior->prdtl; + sc = p->pr_sc; + hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE); + + if (cfis[2] == ATA_WRITE_FPDMA_QUEUED || + cfis[2] == ATA_READ_FPDMA_QUEUED) + ncq = 1; + + pthread_mutex_lock(&sc->mtx); + + /* + * Move the blockif request back to the free list + */ + STAILQ_INSERT_TAIL(&p->iofhd, aior, io_list); + + if (pending && !err) { + ahci_handle_dma(p, slot, cfis, aior->done, + hdr->prdtl - pending); + goto out; + } + + if (!err && aior->done == aior->len) { + tfd = ATA_S_READY | ATA_S_DSC; + if (ncq) + hdr->prdbc = 0; + else + hdr->prdbc = aior->len; + } else { + tfd = (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR; + hdr->prdbc = 0; + if (ncq) + p->serr |= (1 << slot); + } + + if (ncq) { + p->sact &= ~(1 << slot); + ahci_write_fis_sdb(p, slot, tfd); + } else + ahci_write_fis_d2h(p, slot, cfis, tfd); + +out: + pthread_mutex_unlock(&sc->mtx); + DPRINTF("%s exit\n", __func__); +} + +static void +atapi_ioreq_cb(struct blockif_req *br, int err) +{ + struct ahci_cmd_hdr *hdr; + struct ahci_ioreq *aior; + struct ahci_port *p; + struct pci_ahci_softc *sc; + uint8_t *cfis; + uint32_t tfd; + int pending, slot; + + DPRINTF("%s %d\n", __func__, err); + + aior = br->br_param; + p = aior->io_pr; + cfis = aior->cfis; + slot = aior->slot; + pending = aior->prdtl; + sc = p->pr_sc; + hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + aior->slot * AHCI_CL_SIZE); + + pthread_mutex_lock(&sc->mtx); + + /* + * Move the blockif request back to the free list + */ + STAILQ_INSERT_TAIL(&p->iofhd, aior, io_list); + + if (pending && !err) { + atapi_read(p, slot, cfis, aior->done, hdr->prdtl - pending); + goto out; + } + + if (!err && aior->done == aior->len) { + tfd = ATA_S_READY | ATA_S_DSC; + hdr->prdbc = aior->len; + } else { + p->sense_key = ATA_SENSE_ILLEGAL_REQUEST; + p->asc = 0x21; + tfd = (p->sense_key << 12) | ATA_S_READY | ATA_S_ERROR; + hdr->prdbc = 0; + } + + cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; + ahci_write_fis_d2h(p, slot, cfis, tfd); + +out: + pthread_mutex_unlock(&sc->mtx); + DPRINTF("%s exit\n", __func__); +} + +static void +pci_ahci_ioreq_init(struct ahci_port *pr) +{ + struct ahci_ioreq *vr; + int i; + + pr->ioqsz = blockif_queuesz(pr->bctx); + pr->ioreq = calloc(pr->ioqsz, sizeof(struct ahci_ioreq)); + STAILQ_INIT(&pr->iofhd); + + /* + * Add all i/o request entries to the free queue + */ + for (i = 0; i < pr->ioqsz; i++) { + vr = &pr->ioreq[i]; + vr->io_pr = pr; + if (!pr->atapi) + vr->io_req.br_callback = ata_ioreq_cb; + else + vr->io_req.br_callback = atapi_ioreq_cb; + vr->io_req.br_param = vr; + STAILQ_INSERT_TAIL(&pr->iofhd, vr, io_list); + } +} + +static void +pci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value) +{ + int port = (offset - AHCI_OFFSET) / AHCI_STEP; + offset = (offset - AHCI_OFFSET) % AHCI_STEP; + struct ahci_port *p = &sc->port[port]; + + DPRINTF("pci_ahci_port %d: write offset 0x%"PRIx64" value 0x%"PRIx64"\n", + port, offset, value); + + switch (offset) { + case AHCI_P_CLB: + p->clb = value; + break; + case AHCI_P_CLBU: + p->clbu = value; + break; + case AHCI_P_FB: + p->fb = value; + break; + case AHCI_P_FBU: + p->fbu = value; + break; + case AHCI_P_IS: + p->is &= ~value; + break; + case AHCI_P_IE: + p->ie = value & 0xFDC000FF; + ahci_generate_intr(sc); + break; + case AHCI_P_CMD: + { + p->cmd = value; + + if (!(value & AHCI_P_CMD_ST)) { + p->cmd &= ~(AHCI_P_CMD_CR | AHCI_P_CMD_CCS_MASK); + p->ci = 0; + p->sact = 0; + } else { + uint64_t clb; + + p->cmd |= AHCI_P_CMD_CR; + clb = (uint64_t)p->clbu << 32 | p->clb; + p->cmd_lst = paddr_guest2host(ahci_ctx(sc), clb, + AHCI_CL_SIZE * AHCI_MAX_SLOTS); + } + + if (value & AHCI_P_CMD_FRE) { + uint64_t fb; + + p->cmd |= AHCI_P_CMD_FR; + fb = (uint64_t)p->fbu << 32 | p->fb; + /* we don't support FBSCP, so rfis size is 256Bytes */ + p->rfis = paddr_guest2host(ahci_ctx(sc), fb, 256); + } else { + p->cmd &= ~AHCI_P_CMD_FR; + } + + if (value & AHCI_P_CMD_CLO) { + p->tfd = 0; + p->cmd &= ~AHCI_P_CMD_CLO; + } + + ahci_handle_port(p); + break; + } + case AHCI_P_TFD: + case AHCI_P_SIG: + case AHCI_P_SSTS: + WPRINTF("pci_ahci_port: read only registers 0x%"PRIx64"\n", offset); + break; + case AHCI_P_SCTL: + if (!(p->cmd & AHCI_P_CMD_ST)) { + if (value & ATA_SC_DET_RESET) + ahci_port_reset(p); + p->sctl = value; + } + break; + case AHCI_P_SERR: + p->serr &= ~value; + break; + case AHCI_P_SACT: + p->sact |= value; + break; + case AHCI_P_CI: + p->ci |= value; + ahci_handle_port(p); + break; + case AHCI_P_SNTF: + case AHCI_P_FBS: + default: + break; + } +} + +static void +pci_ahci_host_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value) +{ + DPRINTF("pci_ahci_host: write offset 0x%"PRIx64" value 0x%"PRIx64"\n", + offset, value); + + switch (offset) { + case AHCI_CAP: + case AHCI_PI: + case AHCI_VS: + case AHCI_CAP2: + WPRINTF("pci_ahci_host: read only registers 0x%"PRIx64"\n", offset); + break; + case AHCI_GHC: + if (value & AHCI_GHC_HR) + ahci_reset(sc); + else if (value & AHCI_GHC_IE) { + sc->ghc |= AHCI_GHC_IE; + ahci_generate_intr(sc); + } + break; + case AHCI_IS: + sc->is &= ~value; + ahci_generate_intr(sc); + break; + default: + break; + } +} + +static void +pci_ahci_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int baridx, uint64_t offset, int size, uint64_t value) +{ + struct pci_ahci_softc *sc = pi->pi_arg; + + assert(baridx == 5); + assert(size == 4); + + pthread_mutex_lock(&sc->mtx); + + if (offset < AHCI_OFFSET) + pci_ahci_host_write(sc, offset, value); + else if (offset < AHCI_OFFSET + sc->ports * AHCI_STEP) + pci_ahci_port_write(sc, offset, value); + else + WPRINTF("pci_ahci: unknown i/o write offset 0x%"PRIx64"\n", offset); + + pthread_mutex_unlock(&sc->mtx); +} + +static uint64_t +pci_ahci_host_read(struct pci_ahci_softc *sc, uint64_t offset) +{ + uint32_t value; + + switch (offset) { + case AHCI_CAP: + case AHCI_GHC: + case AHCI_IS: + case AHCI_PI: + case AHCI_VS: + case AHCI_CCCC: + case AHCI_CCCP: + case AHCI_EM_LOC: + case AHCI_EM_CTL: + case AHCI_CAP2: + { + uint32_t *p = &sc->cap; + p += (offset - AHCI_CAP) / sizeof(uint32_t); + value = *p; + break; + } + default: + value = 0; + break; + } + DPRINTF("pci_ahci_host: read offset 0x%"PRIx64" value 0x%x\n", + offset, value); + + return (value); +} + +static uint64_t +pci_ahci_port_read(struct pci_ahci_softc *sc, uint64_t offset) +{ + uint32_t value; + int port = (offset - AHCI_OFFSET) / AHCI_STEP; + offset = (offset - AHCI_OFFSET) % AHCI_STEP; + + switch (offset) { + case AHCI_P_CLB: + case AHCI_P_CLBU: + case AHCI_P_FB: + case AHCI_P_FBU: + case AHCI_P_IS: + case AHCI_P_IE: + case AHCI_P_CMD: + case AHCI_P_TFD: + case AHCI_P_SIG: + case AHCI_P_SSTS: + case AHCI_P_SCTL: + case AHCI_P_SERR: + case AHCI_P_SACT: + case AHCI_P_CI: + case AHCI_P_SNTF: + case AHCI_P_FBS: + { + uint32_t *p= &sc->port[port].clb; + p += (offset - AHCI_P_CLB) / sizeof(uint32_t); + value = *p; + break; + } + default: + value = 0; + break; + } + + DPRINTF("pci_ahci_port %d: read offset 0x%"PRIx64" value 0x%x\n", + port, offset, value); + + return value; +} + +static uint64_t +pci_ahci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, + uint64_t offset, int size) +{ + struct pci_ahci_softc *sc = pi->pi_arg; + uint32_t value; + + assert(baridx == 5); + assert(size == 4); + + pthread_mutex_lock(&sc->mtx); + + if (offset < AHCI_OFFSET) + value = pci_ahci_host_read(sc, offset); + else if (offset < AHCI_OFFSET + sc->ports * AHCI_STEP) + value = pci_ahci_port_read(sc, offset); + else { + value = 0; + WPRINTF("pci_ahci: unknown i/o read offset 0x%"PRIx64"\n", offset); + } + + pthread_mutex_unlock(&sc->mtx); + + return (value); +} + +static int +pci_ahci_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts, int atapi) +{ + char bident[sizeof("XX:X:X")]; + struct blockif_ctxt *bctxt; + struct pci_ahci_softc *sc; + int ret, slots; + + ret = 0; + + if (opts == NULL) { + fprintf(stderr, "pci_ahci: backing device required\n"); + return (1); + } + +#ifdef AHCI_DEBUG + dbg = fopen("/tmp/log", "w+"); +#endif + + sc = malloc(sizeof(struct pci_ahci_softc)); + memset(sc, 0, sizeof(struct pci_ahci_softc)); + pi->pi_arg = sc; + sc->asc_pi = pi; + sc->ports = MAX_PORTS; + + /* + * Only use port 0 for a backing device. All other ports will be + * marked as unused + */ + sc->port[0].atapi = atapi; + + /* + * Attempt to open the backing image. Use the PCI + * slot/func/ahci_port for the identifier string + * since that uniquely identifies a storage device. + */ + snprintf(bident, sizeof(bident), "%d:%d:%d", pi->pi_slot, pi->pi_func, + 0); + bctxt = blockif_open(opts, bident); + if (bctxt == NULL) { + ret = 1; + goto open_fail; + } + sc->port[0].bctx = bctxt; + sc->port[0].pr_sc = sc; + + /* + * Allocate blockif request structures and add them + * to the free list + */ + pci_ahci_ioreq_init(&sc->port[0]); + + pthread_mutex_init(&sc->mtx, NULL); + + /* Intel ICH8 AHCI */ + slots = sc->port[0].ioqsz; + if (slots > 32) + slots = 32; + --slots; + sc->cap = AHCI_CAP_64BIT | AHCI_CAP_SNCQ | AHCI_CAP_SSNTF | + AHCI_CAP_SMPS | AHCI_CAP_SSS | AHCI_CAP_SALP | + AHCI_CAP_SAL | AHCI_CAP_SCLO | (0x3 << AHCI_CAP_ISS_SHIFT)| + AHCI_CAP_PMD | AHCI_CAP_SSC | AHCI_CAP_PSC | + (slots << AHCI_CAP_NCS_SHIFT) | AHCI_CAP_SXS | (sc->ports - 1); + + /* Only port 0 implemented */ + sc->pi = 1; + sc->vs = 0x10300; + sc->cap2 = AHCI_CAP2_APST; + ahci_reset(sc); + + pci_set_cfgdata16(pi, PCIR_DEVICE, 0x2821); + pci_set_cfgdata16(pi, PCIR_VENDOR, 0x8086); + pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_STORAGE); + pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_STORAGE_SATA); + pci_set_cfgdata8(pi, PCIR_PROGIF, PCIP_STORAGE_SATA_AHCI_1_0); + pci_emul_add_msicap(pi, 1); + pci_emul_alloc_bar(pi, 5, PCIBAR_MEM32, + AHCI_OFFSET + sc->ports * AHCI_STEP); + +open_fail: + if (ret) { + blockif_close(sc->port[0].bctx); + free(sc); + } + + return (ret); +} + +static int +pci_ahci_hd_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + + return (pci_ahci_init(ctx, pi, opts, 0)); +} + +static int +pci_ahci_atapi_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + + return (pci_ahci_init(ctx, pi, opts, 1)); +} + +/* + * Use separate emulation names to distinguish drive and atapi devices + */ +struct pci_devemu pci_de_ahci_hd = { + .pe_emu = "ahci-hd", + .pe_init = pci_ahci_hd_init, + .pe_barwrite = pci_ahci_write, + .pe_barread = pci_ahci_read +}; +PCI_EMUL_SET(pci_de_ahci_hd); + +struct pci_devemu pci_de_ahci_cd = { + .pe_emu = "ahci-cd", + .pe_init = pci_ahci_atapi_init, + .pe_barwrite = pci_ahci_write, + .pe_barread = pci_ahci_read +}; +PCI_EMUL_SET(pci_de_ahci_cd); diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c index 626dfdf..fe21cee 100644 --- a/usr.sbin/bhyve/pci_emul.c +++ b/usr.sbin/bhyve/pci_emul.c @@ -941,10 +941,19 @@ pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes, uint32_t val) assert(offset >= capoff); /* - * Capability ID and Next Capability Pointer are readonly + * Capability ID and Next Capability Pointer are readonly. + * However, some o/s's do 4-byte writes that include these. + * For this case, trim the write back to 2 bytes and adjust + * the data. */ - if (offset == capoff || offset == capoff + 1) - return; + if (offset == capoff || offset == capoff + 1) { + if (offset == capoff && bytes == 4) { + bytes = 2; + offset += 2; + val >>= 16; + } else + return; + } switch (capid) { case PCIY_MSI: @@ -1048,7 +1057,7 @@ init_pci(struct vmctx *ctx) * Accesses to memory addresses that are not allocated to system * memory or PCI devices return 0xff's. */ - error = vm_get_memory_seg(ctx, 0, &lowmem); + error = vm_get_memory_seg(ctx, 0, &lowmem, NULL); assert(error == 0); memset(&memp, 0, sizeof(struct mem_range)); diff --git a/usr.sbin/bhyve/pci_virtio_block.c b/usr.sbin/bhyve/pci_virtio_block.c index 76f681b..529cd42 100644 --- a/usr.sbin/bhyve/pci_virtio_block.c +++ b/usr.sbin/bhyve/pci_virtio_block.c @@ -156,7 +156,7 @@ pci_vtblk_proc(struct pci_vtblk_softc *sc, struct vqueue_info *vq) * XXX - note - this fails on crash dump, which does a * VIRTIO_BLK_T_FLUSH with a zero transfer length */ - assert (n >= 3 && n <= VTBLK_MAXSEGS + 2); + assert(n >= 2 && n <= VTBLK_MAXSEGS + 2); assert((flags[0] & VRING_DESC_F_WRITE) == 0); assert(iov[0].iov_len == sizeof(struct virtio_blk_hdr)); diff --git a/usr.sbin/bhyve/pit_8254.c b/usr.sbin/bhyve/pit_8254.c index c96596a..3987da3 100644 --- a/usr.sbin/bhyve/pit_8254.c +++ b/usr.sbin/bhyve/pit_8254.c @@ -29,15 +29,23 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include <sys/types.h> #include <sys/time.h> +#include <machine/vmm.h> #include <machine/clock.h> -#include <stdio.h> #include <assert.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <vmmapi.h> #include "bhyverun.h" #include "inout.h" +#include "ioapic.h" +#include "mevent.h" #include "pit_8254.h" #define TIMER_SEL_MASK 0xc0 @@ -51,14 +59,19 @@ __FBSDID("$FreeBSD$"); static const int nsecs_per_tick = 1000000000 / PIT_8254_FREQ; struct counter { + struct vmctx *ctx; + struct mevent *tevp; struct timeval tv; /* uptime when counter was loaded */ + int mode; uint16_t initial; /* initial counter value */ uint8_t cr[2]; uint8_t ol[2]; int crbyte; int olbyte; + int frbyte; }; + static void timevalfix(struct timeval *t1) { @@ -82,16 +95,55 @@ timevalsub(struct timeval *t1, const struct timeval *t2) timevalfix(t1); } +static uint64_t pit_mev_count; + +static void +pit_mevent_cb(int fd, enum ev_type type, void *param) +{ + struct counter *c; + + c = param; + + pit_mev_count++; + + ioapic_assert_pin(c->ctx, 0); + ioapic_deassert_pin(c->ctx, 0); + + /* + * Delete the timer for one-shots + */ + if (c->mode != TIMER_RATEGEN) { + mevent_delete(c->tevp); + c->tevp = NULL; + } +} + static void -latch(struct counter *c) +pit_timer_start(struct vmctx *ctx, struct counter *c) +{ + int msecs; + + if (c->initial != 0) { + msecs = c->initial * nsecs_per_tick / 1000000; + if (msecs == 0) + msecs = 1; + + if (c->tevp == NULL) + c->tevp = mevent_add(msecs, EVF_TIMER, pit_mevent_cb, + c); + } +} + +static uint16_t +pit_update_counter(struct counter *c, int latch) { struct timeval tv2; uint16_t lval; uint64_t delta_nsecs, delta_ticks; /* cannot latch a new value until the old one has been consumed */ - if (c->olbyte != 0) - return; + if (latch && c->olbyte != 0) + return (0); if (c->initial == 0 || c->initial == 1) { /* @@ -101,10 +153,10 @@ latch(struct counter *c) * of the program. * * If the counter's initial value is not programmed we - * assume a value that would be set to generate 'guest_hz' + * assume a value that would be set to generate 100 * interrupts per second. */ - c->initial = TIMER_DIV(PIT_8254_FREQ, guest_hz); + c->initial = TIMER_DIV(PIT_8254_FREQ, 100); gettimeofday(&c->tv, NULL); } @@ -114,9 +166,14 @@ latch(struct counter *c) delta_ticks = delta_nsecs / nsecs_per_tick; lval = c->initial - delta_ticks % c->initial; - c->olbyte = 2; - c->ol[1] = lval; /* LSB */ - c->ol[0] = lval >> 8; /* MSB */ + + if (latch) { + c->olbyte = 2; + c->ol[1] = lval; /* LSB */ + c->ol[0] = lval >> 8; /* MSB */ + } + + return (lval); } static int @@ -150,13 +207,18 @@ pit_8254_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, * Counter mode is not affected when issuing a * latch command. */ - if (mode != TIMER_RATEGEN && mode != TIMER_SQWAVE) + if (mode != TIMER_INTTC && + mode != TIMER_RATEGEN && + mode != TIMER_SQWAVE && + mode != TIMER_SWSTROBE) return (-1); } c = &counter[sel >> 6]; + c->ctx = ctx; + c->mode = mode; if (rw == TIMER_LATCH) - latch(c); + pit_update_counter(c, 1); else c->olbyte = 0; /* reset latch after reprogramming */ @@ -169,20 +231,32 @@ pit_8254_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, if (in) { /* - * XXX * The spec says that once the output latch is completely - * read it should revert to "following" the counter. We don't - * do this because it is hard and any reasonable OS should - * always latch the counter before trying to read it. + * read it should revert to "following" the counter. Use + * the free running counter for this case (i.e. Linux + * TSC calibration). Assuming the access mode is 16-bit, + * toggle the MSB/LSB bit on each read. */ - if (c->olbyte == 0) - c->olbyte = 2; - *eax = c->ol[--c->olbyte]; + if (c->olbyte == 0) { + uint16_t tmp; + + tmp = pit_update_counter(c, 0); + if (c->frbyte) + tmp >>= 8; + tmp &= 0xff; + *eax = tmp; + c->frbyte ^= 1; + } else + *eax = c->ol[--c->olbyte]; } else { c->cr[c->crbyte++] = *eax; if (c->crbyte == 2) { + c->frbyte = 0; c->crbyte = 0; c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8; + /* Start an interval timer for counter 0 */ + if (port == 0x40) + pit_timer_start(ctx, c); if (c->initial == 0) c->initial = 0xffff; gettimeofday(&c->tv, NULL); diff --git a/usr.sbin/bhyve/rtc.c b/usr.sbin/bhyve/rtc.c index a0e2c6a..c1a84d74 100644 --- a/usr.sbin/bhyve/rtc.c +++ b/usr.sbin/bhyve/rtc.c @@ -46,8 +46,11 @@ __FBSDID("$FreeBSD$"); #define IO_RTC 0x70 #define RTC_SEC 0x00 /* seconds */ +#define RTC_SEC_ALARM 0x01 #define RTC_MIN 0x02 +#define RTC_MIN_ALARM 0x03 #define RTC_HRS 0x04 +#define RTC_HRS_ALARM 0x05 #define RTC_WDAY 0x06 #define RTC_DAY 0x07 #define RTC_MONTH 0x08 @@ -94,6 +97,12 @@ static uint8_t rtc_nvram[RTC_NVRAM_SZ]; /* XXX initialize these to default values as they would be from BIOS */ static uint8_t status_a, status_b; +static struct { + uint8_t hours; + uint8_t mins; + uint8_t secs; +} rtc_alarm; + static u_char const bin2bcd_data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, @@ -148,8 +157,11 @@ rtc_addr_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, switch (*eax & 0x7f) { case RTC_SEC: + case RTC_SEC_ALARM: case RTC_MIN: + case RTC_MIN_ALARM: case RTC_HRS: + case RTC_HRS_ALARM: case RTC_WDAY: case RTC_DAY: case RTC_MONTH: @@ -199,6 +211,15 @@ rtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, if (in) { switch (addr) { + case RTC_SEC_ALARM: + *eax = rtc_alarm.secs; + break; + case RTC_MIN_ALARM: + *eax = rtc_alarm.mins; + break; + case RTC_HRS_ALARM: + *eax = rtc_alarm.hours; + break; case RTC_SEC: *eax = rtcout(tm.tm_sec); return (0); @@ -266,6 +287,15 @@ rtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes, case RTC_STATUSD: /* ignore write */ break; + case RTC_SEC_ALARM: + rtc_alarm.secs = *eax; + break; + case RTC_MIN_ALARM: + rtc_alarm.mins = *eax; + break; + case RTC_HRS_ALARM: + rtc_alarm.hours = *eax; + break; case RTC_SEC: case RTC_MIN: case RTC_HRS: @@ -311,18 +341,18 @@ rtc_init(struct vmctx *ctx) * 0x34/0x35 - 64KB chunks above 16MB, below 4GB * 0x5b/0x5c/0x5d - 64KB chunks above 4GB */ - err = vm_get_memory_seg(ctx, 0, &lomem); + err = vm_get_memory_seg(ctx, 0, &lomem, NULL); assert(err == 0); lomem = (lomem - m_16MB) / m_64KB; rtc_nvram[nvoff(RTC_LMEM_LSB)] = lomem; rtc_nvram[nvoff(RTC_LMEM_MSB)] = lomem >> 8; - if (vm_get_memory_seg(ctx, m_4GB, &himem) == 0) { + if (vm_get_memory_seg(ctx, m_4GB, &himem, NULL) == 0) { himem /= m_64KB; rtc_nvram[nvoff(RTC_HMEM_LSB)] = himem; rtc_nvram[nvoff(RTC_HMEM_SB)] = himem >> 8; - rtc_nvram[nvoff(RTC_NVRAM_START)] = himem >> 16; + rtc_nvram[nvoff(RTC_HMEM_MSB)] = himem >> 16; } } diff --git a/usr.sbin/bhyve/virtio.c b/usr.sbin/bhyve/virtio.c index cdc9228..c26272b 100644 --- a/usr.sbin/bhyve/virtio.c +++ b/usr.sbin/bhyve/virtio.c @@ -139,7 +139,8 @@ vi_intr_init(struct virtio_softc *vs, int barnum, int use_msix) return (1); } else { vs->vs_flags &= ~VIRTIO_USE_MSIX; - pci_emul_add_msicap(vs->vs_pi, barnum); + /* Only 1 MSI vector for bhyve */ + pci_emul_add_msicap(vs->vs_pi, 1); } return (0); } diff --git a/usr.sbin/bhyvectl/Makefile b/usr.sbin/bhyvectl/Makefile index 9fde12c..df3f19c 100644 --- a/usr.sbin/bhyvectl/Makefile +++ b/usr.sbin/bhyvectl/Makefile @@ -7,8 +7,8 @@ SRCS= bhyvectl.c NO_MAN= -DPADD= ${LIBVMMAPI} -LDADD= -lvmmapi +DPADD= ${LIBVMMAPI} ${LIBUTIL} +LDADD= -lvmmapi -lutil WARNS?= 3 diff --git a/usr.sbin/bhyvectl/bhyvectl.c b/usr.sbin/bhyvectl/bhyvectl.c index 438d01c..d6b32b8 100644 --- a/usr.sbin/bhyvectl/bhyvectl.c +++ b/usr.sbin/bhyvectl/bhyvectl.c @@ -188,12 +188,13 @@ usage(void) " [--unassign-pptdev=<bus/slot/func>]\n" " [--set-mem=<memory in units of MB>]\n" " [--get-lowmem]\n" - " [--get-highmem]\n", + " [--get-highmem]\n" + " [--get-gpa-pmap]\n", progname); exit(1); } -static int get_stats, getcap, setcap, capval; +static int get_stats, getcap, setcap, capval, get_gpa_pmap; static const char *capname; static int create, destroy, get_lowmem, get_highmem; static uint64_t memsize; @@ -377,18 +378,20 @@ enum { SET_CAP, CAPNAME, UNASSIGN_PPTDEV, + GET_GPA_PMAP, }; int main(int argc, char *argv[]) { char *vmname; - int error, ch, vcpu; - vm_paddr_t gpa; + int error, ch, vcpu, ptenum; + vm_paddr_t gpa, gpa_pmap; size_t len; struct vm_exit vmexit; - uint64_t ctl, eptp, bm, addr, u64; + uint64_t ctl, eptp, bm, addr, u64, pteval[4], *pte; struct vmctx *ctx; + int wired; uint64_t cr0, cr3, cr4, dr7, rsp, rip, rflags, efer, pat; uint64_t rax, rbx, rcx, rdx, rsi, rdi, rbp; @@ -427,6 +430,7 @@ main(int argc, char *argv[]) { "capname", REQ_ARG, 0, CAPNAME }, { "unassign-pptdev", REQ_ARG, 0, UNASSIGN_PPTDEV }, { "setcap", REQ_ARG, 0, SET_CAP }, + { "get-gpa-pmap", REQ_ARG, 0, GET_GPA_PMAP }, { "getcap", NO_ARG, &getcap, 1 }, { "get-stats", NO_ARG, &get_stats, 1 }, { "get-desc-ds",NO_ARG, &get_desc_ds, 1 }, @@ -666,6 +670,10 @@ main(int argc, char *argv[]) capval = strtoul(optarg, NULL, 0); setcap = 1; break; + case GET_GPA_PMAP: + gpa_pmap = strtoul(optarg, NULL, 0); + get_gpa_pmap = 1; + break; case CAPNAME: capname = optarg; break; @@ -819,16 +827,18 @@ main(int argc, char *argv[]) if (!error && (get_lowmem || get_all)) { gpa = 0; - error = vm_get_memory_seg(ctx, gpa, &len); + error = vm_get_memory_seg(ctx, gpa, &len, &wired); if (error == 0) - printf("lowmem\t\t0x%016lx/%ld\n", gpa, len); + printf("lowmem\t\t0x%016lx/%ld%s\n", gpa, len, + wired ? " wired" : ""); } if (!error && (get_highmem || get_all)) { gpa = 4 * GB; - error = vm_get_memory_seg(ctx, gpa, &len); + error = vm_get_memory_seg(ctx, gpa, &len, &wired); if (error == 0) - printf("highmem\t\t0x%016lx/%ld\n", gpa, len); + printf("highmem\t\t0x%016lx/%ld%s\n", gpa, len, + wired ? " wired" : ""); } if (!error && (get_efer || get_all)) { @@ -1457,6 +1467,17 @@ main(int argc, char *argv[]) printf("Capability \"%s\" is not available\n", capname); } + if (!error && get_gpa_pmap) { + error = vm_get_gpa_pmap(ctx, gpa_pmap, pteval, &ptenum); + if (error == 0) { + printf("gpa %#lx:", gpa_pmap); + pte = &pteval[0]; + while (ptenum-- > 0) + printf(" %#lx", *pte++); + printf("\n"); + } + } + if (!error && (getcap || get_all)) { int captype, val, getcaptype; diff --git a/usr.sbin/bhyveload/Makefile b/usr.sbin/bhyveload/Makefile index 7b00818..e7b19bd 100644 --- a/usr.sbin/bhyveload/Makefile +++ b/usr.sbin/bhyveload/Makefile @@ -4,8 +4,8 @@ PROG= bhyveload SRCS= bhyveload.c MAN= bhyveload.8 -DPADD+= ${LIBVMMAPI} -LDADD+= -lvmmapi +DPADD+= ${LIBVMMAPI} ${LIBUTIL} +LDADD+= -lvmmapi -lutil WARNS?= 3 diff --git a/usr.sbin/bhyveload/bhyveload.8 b/usr.sbin/bhyveload/bhyveload.8 index f2e5e35..2efcad0 100644 --- a/usr.sbin/bhyveload/bhyveload.8 +++ b/usr.sbin/bhyveload/bhyveload.8 @@ -60,13 +60,29 @@ and will be created if it does not already exist. .Sh OPTIONS The following options are available: .Bl -tag -width indent -.It Fl m Ar mem-size +.It Fl m Ar mem-size Xo +.Sm off +.Op Cm K | k | M | m | G | g | T | t +.Xc +.Sm on .Ar mem-size -is the amount of memory allocated to the guest in units of megabytes. +is the amount of memory allocated to the guest. +.Pp +The +.Ar mem-size +argument may be suffixed with one of +.Cm K , +.Cm M , +.Cm G +or +.Cm T +(either upper or lower case) to indicate a multiple of +Kilobytes, Megabytes, Gigabytes or Terabytes +respectively. .Pp The default value of .Ar mem-size -is 256. +is 256M. .It Fl d Ar disk-path The .Ar disk-path @@ -83,7 +99,7 @@ that boots off the ISO image .Pa /freebsd/release.iso and has 1GB memory allocated to it: .Pp -.Dl "bhyveload -m 1024 -d /freebsd/release.iso freebsd-vm" +.Dl "bhyveload -m 1G -d /freebsd/release.iso freebsd-vm" .Sh SEE ALSO .Xr bhyve 4 , .Xr bhyve 8 , diff --git a/usr.sbin/bhyveload/bhyveload.c b/usr.sbin/bhyveload/bhyveload.c index 4cd280c..6e541e8 100644 --- a/usr.sbin/bhyveload/bhyveload.c +++ b/usr.sbin/bhyveload/bhyveload.c @@ -67,12 +67,14 @@ __FBSDID("$FreeBSD$"); #include <dirent.h> #include <dlfcn.h> #include <errno.h> +#include <err.h> #include <fcntl.h> #include <getopt.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sysexits.h> #include <termios.h> #include <unistd.h> @@ -492,8 +494,8 @@ static void cb_getmem(void *arg, uint64_t *ret_lowmem, uint64_t *ret_highmem) { - vm_get_memory_seg(ctx, 0, ret_lowmem); - vm_get_memory_seg(ctx, 4 * GB, ret_highmem); + vm_get_memory_seg(ctx, 0, ret_lowmem, NULL); + vm_get_memory_seg(ctx, 4 * GB, ret_highmem, NULL); } static const char * @@ -581,9 +583,10 @@ main(int argc, char** argv) break; case 'm': - mem_size = strtoul(optarg, NULL, 0) * MB; + error = vm_parse_memsize(optarg, &mem_size); + if (error != 0) + errx(EX_USAGE, "Invalid memsize '%s'", optarg); break; - case '?': usage(); } diff --git a/usr.sbin/boot0cfg/boot0cfg.8 b/usr.sbin/boot0cfg/boot0cfg.8 index 4d622a2..27f94e0 100644 --- a/usr.sbin/boot0cfg/boot0cfg.8 +++ b/usr.sbin/boot0cfg/boot0cfg.8 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 4, 2012 +.Dd October 1, 2013 .Dt BOOT0CFG 8 .Os .Sh NAME @@ -173,17 +173,17 @@ Image for serial consoles (COM1,9600,8,N,1,MODEM) .Sh EXAMPLES To boot slice 2 on the next boot: .Pp -.Dl "boot0cfg -s 2 ad0" +.Dl "boot0cfg -s 2 ada0" .Pp To enable just slices 1 and 3 in the menu: .Pp -.Dl "boot0cfg -m 0x5 ad0" +.Dl "boot0cfg -m 0x5 ada0" .Pp To go back to non-interactive booting, use .Xr fdisk 8 to install the default MBR: .Pp -.Dl "fdisk -B ad0" +.Dl "fdisk -B ada0" .Sh SEE ALSO .Xr geom 4 , .Xr boot 8 , diff --git a/usr.sbin/bsdconfig/bsdconfig b/usr.sbin/bsdconfig/bsdconfig index b23e083..2c0d513 100755 --- a/usr.sbin/bsdconfig/bsdconfig +++ b/usr.sbin/bsdconfig/bsdconfig @@ -300,7 +300,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" # Only update default-item on success - [ $retval -eq 0 ] && f_dialog_default_store "$menu_choice" + [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } @@ -396,11 +396,10 @@ while :; do f_dialog_menutag_fetch mtag f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$BSDCONFIG_HELPFILE" continue - elif [ $retval -ne 0 ]; then + elif [ $retval -ne $DIALOG_OK ]; then f_die fi diff --git a/usr.sbin/bsdconfig/console/ttys b/usr.sbin/bsdconfig/console/ttys index c0f3ee8..80cc668 100755 --- a/usr.sbin/bsdconfig/console/ttys +++ b/usr.sbin/bsdconfig/console/ttys @@ -92,7 +92,7 @@ dialog_menu_main() local retval=$? f_dialog_menutag_store -s "$menu_choice" - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then local item item=$( eval f_dialog_menutag2item \ \"\$menu_choice\" $menu_list ) diff --git a/usr.sbin/bsdconfig/examples/Makefile b/usr.sbin/bsdconfig/examples/Makefile index 78711d2..d8813ce 100644 --- a/usr.sbin/bsdconfig/examples/Makefile +++ b/usr.sbin/bsdconfig/examples/Makefile @@ -3,7 +3,7 @@ NO_OBJ= FILESDIR= ${SHAREDIR}/examples/bsdconfig -FILES= browse_packages.sh bsdconfigrc +FILES= browse_packages_ftp.sh browse_packages_http.sh bsdconfigrc beforeinstall: mkdir -p ${DESTDIR}${FILESDIR} diff --git a/usr.sbin/bsdconfig/examples/browse_packages.sh b/usr.sbin/bsdconfig/examples/browse_packages_ftp.sh index 1deb562..1deb562 100755 --- a/usr.sbin/bsdconfig/examples/browse_packages.sh +++ b/usr.sbin/bsdconfig/examples/browse_packages_ftp.sh diff --git a/usr.sbin/bsdconfig/examples/browse_packages_http.sh b/usr.sbin/bsdconfig/examples/browse_packages_http.sh new file mode 100755 index 0000000..e88a576 --- /dev/null +++ b/usr.sbin/bsdconfig/examples/browse_packages_http.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# $FreeBSD$ +# +# This sample downloads the package INDEX file from HTTP to /tmp (if it doesn't +# already exist) and then displays the package configuration/management screen +# using the local INDEX file (results in faster browsing of packages from-start +# since the INDEX can be loaded from local media). +# +# NOTE: Packages cannot be installed unless staged to /tmp/packages/All +# +. /usr/share/bsdconfig/script.subr +nonInteractive=1 +TMPDIR=/tmp +if [ ! -e "$TMPDIR/packages/INDEX" ]; then + [ -d "$TMPDIR/packages" ] || mkdir -p "$TMPDIR/packages" || exit 1 + _httpPath=http://ftp.freebsd.org + # For older releases, use http://ftp-archive.freebsd.org + mediaSetHTTP + mediaOpen + f_show_info "Downloading packages/INDEX from\n %s" "$_httpPath" + f_device_get media packages/INDEX > $TMPDIR/packages/INDEX +fi +_directoryPath=$TMPDIR +mediaSetDirectory +configPackages diff --git a/usr.sbin/bsdconfig/mouse/enable b/usr.sbin/bsdconfig/mouse/enable index 695eac0..106faec 100755 --- a/usr.sbin/bsdconfig/mouse/enable +++ b/usr.sbin/bsdconfig/mouse/enable @@ -102,7 +102,7 @@ f_dialog_title_restore # Stop the mouse daemon # f_quietly vidcontrol -m off -if [ $retval -eq $SUCCESS ]; then +if [ $retval -eq $DIALOG_OK ]; then f_sysrc_set moused_enable "YES" || f_die ln -fs /dev/sysmouse /dev/mouse || f_die # backwards compat else diff --git a/usr.sbin/bsdconfig/networking/devices b/usr.sbin/bsdconfig/networking/devices index 00a50c5..9d65c51 100755 --- a/usr.sbin/bsdconfig/networking/devices +++ b/usr.sbin/bsdconfig/networking/devices @@ -148,7 +148,7 @@ while :; do "$interface" "$_ipaddr" "$_netmask" "$_options" $dhcp # Return to root menu if above returns success - [ $? -eq $SUCCESS ] && break + [ $? -eq $DIALOG_OK ] && break done exit $SUCCESS diff --git a/usr.sbin/bsdconfig/networking/share/device.subr b/usr.sbin/bsdconfig/networking/share/device.subr index a0ca5cc..f4198c1 100644 --- a/usr.sbin/bsdconfig/networking/share/device.subr +++ b/usr.sbin/bsdconfig/networking/share/device.subr @@ -125,7 +125,7 @@ f_dialog_menu_netdev() ) if [ ! "$interfaces" ]; then f_show_msg "$msg_no_network_interfaces" - return $FAILURE + return $DIALOG_CANCEL fi # @@ -242,12 +242,10 @@ f_dialog_menu_netdev_edit() local retval=$? f_dialog_data_sanitize tag - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$TCP_HELPFILE" continue - elif [ $retval -ne $SUCCESS ]; then - # "Cancel" was chosen (-1) or ESC was pressed (255) + elif [ $retval -ne $DIALOG_OK ]; then return $retval else # Only update default-item on success @@ -298,7 +296,7 @@ f_dialog_menu_netdev_edit() ) retval=$? trap 'interrupt' SIGINT - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then dhcp=1 ipaddr=$( f_ifconfig_inet $interface ) netmask=$( f_ifconfig_netmask $interface ) @@ -312,11 +310,11 @@ f_dialog_menu_netdev_edit() fi ;; 3\ *) f_dialog_input_ipaddr "$interface" "$ipaddr" - [ $? -eq $SUCCESS ] && dhcp= ;; + [ $? -eq $DIALOG_OK ] && dhcp= ;; 4\ *) f_dialog_input_netmask "$interface" "$netmask" - [ $? -eq $SUCCESS -a "$_netmask" ] && dhcp= ;; + [ $? -eq $DIALOG_OK -a "$_netmask" ] && dhcp= ;; 5\ *) f_dialog_menu_media_options "$interface" "$options" - [ $? -eq $SUCCESS ] && dhcp= ;; + [ $? -eq $DIALOG_OK ] && dhcp= ;; esac done @@ -383,7 +381,7 @@ f_dialog_menu_netdev_edit() fi fi - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/networking/share/hostname.subr b/usr.sbin/bsdconfig/networking/share/hostname.subr index 05f0315..ab47494 100644 --- a/usr.sbin/bsdconfig/networking/share/hostname.subr +++ b/usr.sbin/bsdconfig/networking/share/hostname.subr @@ -110,7 +110,7 @@ f_dialog_input_hostname() # while :; do f_dialog_input hostname "$msg" "$hostname" \ - "$hline_alnum_punc_tab_enter" || return + "$hline_alnum_punc_tab_enter" || return $? # Taint-check the user's input f_dialog_validate_hostname "$hostname" && break done @@ -150,7 +150,7 @@ f_dialog_input_hostname() fi fi - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/networking/share/ipaddr.subr b/usr.sbin/bsdconfig/networking/share/ipaddr.subr index 9c51f59..f67713a 100644 --- a/usr.sbin/bsdconfig/networking/share/ipaddr.subr +++ b/usr.sbin/bsdconfig/networking/share/ipaddr.subr @@ -147,7 +147,7 @@ f_dialog_input_ipaddr() local setting="$( printf "$msg_current_ipaddr" \ "$interface" "$_ipaddr" )" f_noyes "$msg_nfs_mounts_may_cause_hang" "$setting" || - return $FAILURE + return $DIALOG_CANCEL fi local msg="$( printf "$msg_please_enter_new_ip_addr" "$interface" )" @@ -163,11 +163,11 @@ f_dialog_input_ipaddr() # - User has not made any changes to the given value # f_dialog_input _input "$msg" "$_ipaddr" \ - "$hline_num_punc_tab_enter" || return - [ "$_ipaddr" = "$_input" ] && return $FAILURE + "$hline_num_punc_tab_enter" || return $? + [ "$_ipaddr" = "$_input" ] && return $DIALOG_CANCEL # Return success if NULL value was entered - [ "$_input" ] || return $SUCCESS + [ "$_input" ] || return $DIALOG_OK # Take only the first "word" of the user's input _ipaddr="$_input" @@ -208,7 +208,7 @@ f_dialog_input_ipaddr() ipaddr="$_ipaddr" [ "$_netmask" ] && netmask="$_netmask" - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/networking/share/media.subr b/usr.sbin/bsdconfig/networking/share/media.subr index c6efa57..d4283c1 100644 --- a/usr.sbin/bsdconfig/networking/share/media.subr +++ b/usr.sbin/bsdconfig/networking/share/media.subr @@ -118,7 +118,7 @@ f_dialog_input_options() local setting="$( printf "$msg_current_options" \ "$interface" "$options" )" f_noyes "$msg_nfs_mounts_may_cause_hang" "$setting" || - return $FAILURE + return $DIALOG_CANCEL fi local msg="$( printf "$msg_please_enter_mediaopts" "$interface" )" @@ -138,7 +138,7 @@ f_dialog_input_options() local retval=$? f_dialog_line_sanitize _options - [ $retval -eq $SUCCESS ] && options="$_options" + [ $retval -eq $DIALOG_OK ] && options="$_options" return $retval } @@ -165,7 +165,7 @@ f_dialog_menu_media_options() local setting="$( printf "$msg_current_options" \ "$interface" "$_options" )" f_noyes "$msg_nfs_mounts_may_cause_hang" "$setting" || - return $FAILURE + return $DIALOG_CANCEL fi # @@ -219,7 +219,7 @@ f_dialog_menu_media_options() local retval=$? f_dialog_data_sanitize tag - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then options=$( eval f_dialog_menutag2item \"\$tag\" \ $supported_media ) case "$options" in diff --git a/usr.sbin/bsdconfig/networking/share/netmask.subr b/usr.sbin/bsdconfig/networking/share/netmask.subr index 845bf7e..ebd91fc 100644 --- a/usr.sbin/bsdconfig/networking/share/netmask.subr +++ b/usr.sbin/bsdconfig/networking/share/netmask.subr @@ -97,7 +97,7 @@ f_dialog_input_netmask() local setting="$( printf "$msg_current_subnet" \ "$interface" "$_netmask" )" f_noyes "$msg_nfs_mounts_may_cause_hang" "$setting" || - return $FAILURE + return $DIALOG_CANCEL fi # @@ -111,11 +111,11 @@ f_dialog_input_netmask() # - User has not made any changes to the given value # f_dialog_input _input "$msg" "$_netmask" \ - "$hline_num_punc_tab_enter" || return - [ "$_netmask" = "$_input" ] && return $FAILURE + "$hline_num_punc_tab_enter" || return $? + [ "$_netmask" = "$_input" ] && return $DIALOG_CANCEL # Return success if NULL value was entered - [ "$_input" ] || return $SUCCESS + [ "$_input" ] || return $DIALOG_OK # Take only the first "word" of the user's input _netmask="$_input" diff --git a/usr.sbin/bsdconfig/networking/share/resolv.subr b/usr.sbin/bsdconfig/networking/share/resolv.subr index ac281fc..dfeac34 100644 --- a/usr.sbin/bsdconfig/networking/share/resolv.subr +++ b/usr.sbin/bsdconfig/networking/share/resolv.subr @@ -195,7 +195,7 @@ f_dialog_resolv_conf_update() # update with our new `domain' and `search' directives. # local tmpfile="$( mktemp -t "$pgm" )" - [ "$tmpfile" ] || return $FAILURE + [ "$tmpfile" ] || return $DIALOG_CANCEL # # Fixup permissions and ownership (mktemp(1) creates the @@ -235,7 +235,8 @@ f_dialog_resolv_conf_update() # Write the temporary file contents and move the temporary # file into place. # - echo "$new_contents" | tail -r > "$tmpfile" || return $FAILURE + echo "$new_contents" | tail -r > "$tmpfile" || + return $DIALOG_CANCEL f_quietly mv "$tmpfile" "$RESOLV_CONF" fi @@ -293,8 +294,8 @@ f_dialog_input_nameserver() # # Perform sanity checks # - f_isinteger "$index" || return $FAILURE - [ $index -ge 0 ] || return $FAILURE + f_isinteger "$index" || return $DIALOG_CANCEL + [ $index -ge 0 ] || return $DIALOG_CANCEL local msg if [ $index -gt 0 ]; then @@ -312,7 +313,7 @@ f_dialog_input_nameserver() # while :; do f_dialog_input new_ns "$msg" "$ns" \ - "$hline_num_punc_tab_enter" || return + "$hline_num_punc_tab_enter" || return $? # Take only the first "word" of the user's input new_ns="${new_ns%%[$IFS]*}" @@ -331,7 +332,7 @@ f_dialog_input_nameserver() if [ $index -eq "0" -a "$new_ns" ]; then f_dialog_info "$msg_saving_nameserver" printf "nameserver\t%s\n" "$new_ns" >> "$RESOLV_CONF" - return $SUCCESS + return $DIALOG_OK elif [ $index -gt 0 -a "$old_ns" != "$new_ns" ]; then if [ "$new_ns" ]; then msg="$msg_saving_nameserver_existing" @@ -344,7 +345,7 @@ f_dialog_input_nameserver() # Create a new temporary file to write our new resolv.conf(5) # local tmpfile="$( mktemp -t "$pgm" )" - [ "$tmpfile" ] || return $FAILURE + [ "$tmpfile" ] || return $DIALOG_CANCEL # # Quietly fixup permissions and ownership @@ -381,7 +382,7 @@ f_dialog_input_nameserver() # Write the temporary file contents and move the temporary # file into place. # - echo "$new_contents" > "$tmpfile" || return $FAILURE + echo "$new_contents" > "$tmpfile" || return $DIALOG_CANCEL f_quietly mv "$tmpfile" "$RESOLV_CONF" fi } @@ -452,7 +453,7 @@ f_dialog_menu_nameservers() f_dialog_data_sanitize tag # Return if "Cancel" was chosen (-1) or ESC was pressed (255) - if [ $retval -ne $SUCCESS ]; then + if [ $retval -ne $DIALOG_OK ]; then return $retval else # Only update default-item on success diff --git a/usr.sbin/bsdconfig/networking/share/routing.subr b/usr.sbin/bsdconfig/networking/share/routing.subr index 3af9f36..d0b8927 100644 --- a/usr.sbin/bsdconfig/networking/share/routing.subr +++ b/usr.sbin/bsdconfig/networking/share/routing.subr @@ -75,7 +75,7 @@ f_dialog_input_defaultrouter() local setting="$( printf "$msg_current_default_router" \ "$defaultrouter" )" f_noyes "$msg_nfs_mounts_may_cause_hang" "$setting" || - return $FAILURE + return $DIALOG_CANCEL fi # @@ -87,8 +87,8 @@ f_dialog_input_defaultrouter() "$msg_please_enter_default_router" \ "$defaultrouter" "$hline_num_punc_tab_enter" retval=$? - [ "$defaultrouter" ] || return $SUCCESS - [ $retval -eq $SUCCESS ] || return $retval + [ "$defaultrouter" ] || return $DIALOG_OK + [ $retval -eq $DIALOG_OK ] || return $retval # Taint-check the user's input f_dialog_validate_ipaddr "$defaultrouter" && break @@ -112,7 +112,7 @@ f_dialog_input_defaultrouter() f_dialog_clear f_yesno "$msg_activate_default_router" \ "$( f_route_get_default )" "$defaultrouter" - if [ $? -eq $SUCCESS ]; then + if [ $? -eq $DIALOG_OK ]; then local err # Apply the default router/gateway @@ -120,7 +120,7 @@ f_dialog_input_defaultrouter() err=$( route add default "$defaultrouter" 2>&1 ) if [ $? -ne $SUCCESS ]; then f_dialog_msgbox "$err" - return $FAILURE + return $DIALOG_CANCEL fi fi fi diff --git a/usr.sbin/bsdconfig/password/share/password.subr b/usr.sbin/bsdconfig/password/share/password.subr index 05db7f1..2280339 100644 --- a/usr.sbin/bsdconfig/password/share/password.subr +++ b/usr.sbin/bsdconfig/password/share/password.subr @@ -114,7 +114,7 @@ f_dialog_input_password() break done - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/security/kern_securelevel b/usr.sbin/bsdconfig/security/kern_securelevel index fd1c91c..cce2ef0 100755 --- a/usr.sbin/bsdconfig/security/kern_securelevel +++ b/usr.sbin/bsdconfig/security/kern_securelevel @@ -131,11 +131,10 @@ while :; do retval=$? f_dialog_menutag_fetch mtag - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$SECURELEVEL_HELPFILE" continue - elif [ $retval -ne 0 ]; then + elif [ $retval -ne $DIALOG_OK ]; then f_die fi diff --git a/usr.sbin/bsdconfig/security/security b/usr.sbin/bsdconfig/security/security index ca19a26..7248982 100755 --- a/usr.sbin/bsdconfig/security/security +++ b/usr.sbin/bsdconfig/security/security @@ -123,7 +123,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" # Only update default-item on success - [ $retval -eq 0 ] && f_dialog_default_store "$menu_choice" + [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } diff --git a/usr.sbin/bsdconfig/share/common.subr b/usr.sbin/bsdconfig/share/common.subr index 985bf6b..d087cca 100644 --- a/usr.sbin/bsdconfig/share/common.subr +++ b/usr.sbin/bsdconfig/share/common.subr @@ -560,18 +560,19 @@ f_index_file() if [ "$lang" ]; then awk -v keyword="$keyword" "$f_index_file_awk" \ - $BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX.$lang && return + $BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX.$lang && + return $SUCCESS # No match, fall-thru to non-i18n sources fi awk -v keyword="$keyword" "$f_index_file_awk" \ - $BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX && return + $BSDCFG_LIBE${BSDCFG_LIBE:+/}*/INDEX && return $SUCCESS # No match? Fall-thru to `local' libexec sources (add-on modules) [ "$BSDCFG_LOCAL_LIBE" ] || return $FAILURE if [ "$lang" ]; then awk -v keyword="$keyword" "$f_index_file_awk" \ - $BSDCFG_LOCAL_LIBE/*/INDEX.$lang && return + $BSDCFG_LOCAL_LIBE/*/INDEX.$lang && return $SUCCESS # No match, fall-thru to non-i18n sources fi awk -v keyword="$keyword" "$f_index_file_awk" \ diff --git a/usr.sbin/bsdconfig/share/device.subr b/usr.sbin/bsdconfig/share/device.subr index bbd3a10..b920809 100644 --- a/usr.sbin/bsdconfig/share/device.subr +++ b/usr.sbin/bsdconfig/share/device.subr @@ -552,7 +552,7 @@ f_device_find() f_device_init() { local name="$1" init_func - device_$name get init init_func || return + device_$name get init init_func || return $? ${init_func:-:} $name } @@ -564,7 +564,7 @@ f_device_init() f_device_get() { local name="$1" file="$2" probe="$3" get_func - device_$name get get get_func || return + device_$name get get get_func || return $? ${get_func:-:} $name "$file" ${3+"$probe"} } @@ -575,7 +575,7 @@ f_device_get() f_device_shutdown() { local name="$1" shutdown_func - device_$name get shutdown shutdown_func || return + device_$name get shutdown shutdown_func || return $? ${shutdown_func:-:} $name } @@ -597,7 +597,7 @@ f_device_menu() [ "$devtype" = "$type" ] || continue devs="$devs $dev" done - [ "$devs" ] || return $FAILURE + [ "$devs" ] || return $DIALOG_CANCEL local desc menu_list= for dev in $devs; do @@ -637,7 +637,7 @@ f_device_menu() ) local retval=$? - [ $retval -ne 2 ] && break + [ $retval -ne $DIALOG_HELP ] && break # Otherwise, the Help button was pressed f_show_help "$helpfile" # ...then loop back to menu @@ -646,7 +646,7 @@ f_device_menu() [ "$errexit" ] && set -e - if [ $retval -eq 0 ]; then + if [ $retval -eq $DIALOG_OK ]; then # Clean up the output of [X]dialog(1) and return it f_dialog_data_sanitize mtag echo "$mtag" >&2 diff --git a/usr.sbin/bsdconfig/share/dialog.subr b/usr.sbin/bsdconfig/share/dialog.subr index bde3753..ff7fc86 100644 --- a/usr.sbin/bsdconfig/share/dialog.subr +++ b/usr.sbin/bsdconfig/share/dialog.subr @@ -74,6 +74,18 @@ unset XDIALOG_FORCE_AUTOSIZE unset XDIALOG_INFOBOX_TIMEOUT # +# Exit codes for [X]dialog(1) +# +DIALOG_OK=${SUCCESS:-0} +DIALOG_CANCEL=${FAILURE:-1} +DIALOG_HELP=2 +DIALOG_ITEM_HELP=2 +DIALOG_EXTRA=3 +DIALOG_ITEM_HELP=4 +export DIALOG_ERROR=254 # sh(1) can't handle the default of `-1' +DIALOG_ESC=255 + +# # Default behavior is to call f_dialog_init() automatically when loaded. # : ${DIALOG_SELF_INITIALIZE=1} diff --git a/usr.sbin/bsdconfig/share/media/any.subr b/usr.sbin/bsdconfig/share/media/any.subr index 3675713..516d2d2 100644 --- a/usr.sbin/bsdconfig/share/media/any.subr +++ b/usr.sbin/bsdconfig/share/media/any.subr @@ -113,11 +113,10 @@ f_media_get_type() f_dialog_data_sanitize mtag f_dprintf "retval=%s mtag=[%s]" $retval "$mtag" - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$MEDIA_HELPFILE" continue - elif [ $retval -ne 0 ]; then + elif [ $retval -ne $DIALOG_OK ]; then return $FAILURE fi diff --git a/usr.sbin/bsdconfig/share/media/cdrom.subr b/usr.sbin/bsdconfig/share/media/cdrom.subr index cd68029..c59f88a 100644 --- a/usr.sbin/bsdconfig/share/media/cdrom.subr +++ b/usr.sbin/bsdconfig/share/media/cdrom.subr @@ -170,7 +170,7 @@ f_media_shutdown_cdrom() { local dev="$1" err - [ "$CDROM_MOUNTED" ] || return + [ "$CDROM_MOUNTED" ] || return $FAILURE if [ "$CDROM_PREVIOUSLY_MOUNTED" ]; then CDROM_MOUNTED= diff --git a/usr.sbin/bsdconfig/share/media/common.subr b/usr.sbin/bsdconfig/share/media/common.subr index 495aca7..01d50a1 100644 --- a/usr.sbin/bsdconfig/share/media/common.subr +++ b/usr.sbin/bsdconfig/share/media/common.subr @@ -127,7 +127,7 @@ f_media_generic_get() fi [ "$probe_type" ] && return $SUCCESS cat "$path" - return + return $? fi done diff --git a/usr.sbin/bsdconfig/share/media/dos.subr b/usr.sbin/bsdconfig/share/media/dos.subr index 440c5c9..be4cfc7 100644 --- a/usr.sbin/bsdconfig/share/media/dos.subr +++ b/usr.sbin/bsdconfig/share/media/dos.subr @@ -148,7 +148,7 @@ f_media_shutdown_dos() { local dev="$1" err - [ "$DOS_MOUNTED" ] || return + [ "$DOS_MOUNTED" ] || return $FAILURE if ! err=$( umount -f "$MOUNTPOINT" 2>&1 ); then err="${err#umount: }"; err="${err#*: }" diff --git a/usr.sbin/bsdconfig/share/media/floppy.subr b/usr.sbin/bsdconfig/share/media/floppy.subr index eb6bc5e..62fe64b 100644 --- a/usr.sbin/bsdconfig/share/media/floppy.subr +++ b/usr.sbin/bsdconfig/share/media/floppy.subr @@ -205,7 +205,7 @@ f_media_shutdown_floppy() { local dev="$1" err mp - [ "$FLOPPY_MOUNTED" ] || return + [ "$FLOPPY_MOUNTED" ] || return $FAILURE device_$dev get private mp if ! err=$( umount -f "${mp:=$MOUNTPOINT}" 2>&1 ); then diff --git a/usr.sbin/bsdconfig/share/media/ftp.subr b/usr.sbin/bsdconfig/share/media/ftp.subr index dfdff26..d7f33bb 100644 --- a/usr.sbin/bsdconfig/share/media/ftp.subr +++ b/usr.sbin/bsdconfig/share/media/ftp.subr @@ -125,7 +125,6 @@ f_dialog_menu_media_ftp() ' $msg_germany #8' 'ftp8.de.freebsd.org' '$msg_greece' 'ftp.gr.freebsd.org' ' $msg_greece #2' 'ftp2.gr.freebsd.org' - '$msg_iceland' 'ftp.is.freebsd.org' '$msg_ireland' 'ftp3.ie.freebsd.org' '$msg_israel' 'ftp.il.freebsd.org' '$msg_italy' 'ftp.it.freebsd.org' @@ -165,7 +164,6 @@ f_dialog_menu_media_ftp() ' $msg_sweden #2' 'ftp2.se.freebsd.org' ' $msg_sweden #3' 'ftp3.se.freebsd.org' ' $msg_sweden #4' 'ftp4.se.freebsd.org' - ' $msg_sweden #5' 'ftp5.se.freebsd.org' ' $msg_sweden #6' 'ftp6.se.freebsd.org' '$msg_switzerland' 'ftp.ch.freebsd.org' '$msg_taiwan' 'ftp.tw.freebsd.org' @@ -215,7 +213,7 @@ f_dialog_menu_media_ftp() $height $width $rows \ $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD - ) || return $FAILURE + ) || return $DIALOG_CANCEL f_dialog_data_sanitize mtag case "$mtag" in @@ -226,7 +224,7 @@ f_dialog_menu_media_ftp() setvar $VAR_FTP_PATH "ftp://$value" esac - return $SUCCESS + return $DIALOG_OK } # f_media_set_ftp @@ -742,7 +740,7 @@ f_media_init_ftp() else f_yesno "$msg_cant_find_distribution" \ "$rel" "$ftp_host" - if [ $? -eq $SUCCESS ]; then + if [ $? -eq $DIALOG_OK ]; then unset $VAR_FTP_PATH f_media_set_ftp && continue fi diff --git a/usr.sbin/bsdconfig/share/media/http.subr b/usr.sbin/bsdconfig/share/media/http.subr index 1236c99..97c4ed0 100644 --- a/usr.sbin/bsdconfig/share/media/http.subr +++ b/usr.sbin/bsdconfig/share/media/http.subr @@ -77,7 +77,8 @@ f_dialog_menu_media_http() f_dialog_title_restore local prompt="$msg_please_select_the_site_closest_to_you_or_other" local menu_list=" - 'URL' '$msg_specify_some_other_http_site' + '$msg_main_site' 'ftp.freebsd.org' + 'URL' '$msg_specify_some_other_http_site' " # END-QUOTE local hline="$msg_select_a_site_thats_close" @@ -100,7 +101,7 @@ f_dialog_menu_media_http() $height $width $rows \ $menu_list \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD - ) || return $FAILURE + ) || return $DIALOG_CANCEL f_dialog_data_sanitize mtag case "$mtag" in @@ -111,7 +112,7 @@ f_dialog_menu_media_http() setvar $VAR_HTTP_PATH "http://$value" esac - return $SUCCESS + return $DIALOG_OK } # f_media_set_http @@ -390,8 +391,12 @@ f_http_check_access() f_show_info "$msg_checking_access_to" "$http_path" local rx + case "$http_path" in + http://*|/*) : valid request ;; + *) http_path="/$http_path" # full URI requests only + esac if ! rx=$( - printf "GET /%s/ HTTP/1.0\r\n\r\n" "${http_path%/}" | + printf "GET %s/ HTTP/1.0\r\n\r\n" "${http_path%/}" | nc -n "$host" "$http_port" ); then f_show_msg "$msg_couldnt_connect_to_server http://%s:%s/" \ @@ -583,8 +588,12 @@ f_media_get_http() local http_path f_getvar $VAR_HTTP_PATH%/ http_path - local url="/$http_path/$file" rx + case "$http_path" in + http://*|/*) : valid request ;; + *) http_path="/$http_path" # full URI requests only + esac + local url="$http_path/$file" rx f_dprintf "sending http request for: %s" "$url" printf "GET %s HTTP/1.0\r\n\r\n" "$url" | nc -n "$host" "$http_port" | ( diff --git a/usr.sbin/bsdconfig/share/media/nfs.subr b/usr.sbin/bsdconfig/share/media/nfs.subr index 9ce0467..8db5a06 100644 --- a/usr.sbin/bsdconfig/share/media/nfs.subr +++ b/usr.sbin/bsdconfig/share/media/nfs.subr @@ -233,7 +233,7 @@ f_media_shutdown_nfs() { local dev="$1" err - [ "$NFS_MOUNTED" ] || return + [ "$NFS_MOUNTED" ] || return $FAILURE f_dprintf "Unmounting NFS partition on %s" "$MOUNTPOINT" if ! err=$( umount -f "$MOUNTPOINT" 2>&1 ); then diff --git a/usr.sbin/bsdconfig/share/media/options.subr b/usr.sbin/bsdconfig/share/media/options.subr index 3f71a96..bc9568e 100644 --- a/usr.sbin/bsdconfig/share/media/options.subr +++ b/usr.sbin/bsdconfig/share/media/options.subr @@ -224,11 +224,10 @@ f_media_options_menu() defaultitem="$mtag" f_dprintf "retval=%s mtag=[%s]" $retval "$mtag" - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$OPTIONS_HELPFILE" continue - elif [ $retval -ne 0 ]; then + elif [ $retval -ne $DIALOG_OK ]; then break # to success fi diff --git a/usr.sbin/bsdconfig/share/media/tcpip.subr b/usr.sbin/bsdconfig/share/media/tcpip.subr index 6b4791e..5957b90 100644 --- a/usr.sbin/bsdconfig/share/media/tcpip.subr +++ b/usr.sbin/bsdconfig/share/media/tcpip.subr @@ -471,10 +471,10 @@ f_dialog_validate_tcpip() ! f_validate_gateway "$gateway" "$ipaddr" "$netmask"; then f_show_msg "$msg_invalid_gateway_ipv4_address_specified" else - return $SUCCESS + return $DIALOG_OK fi - return $FAILURE + return $DIALOG_CANCEL } # f_ifconfig_inet $interface [$var_to_set] @@ -1102,7 +1102,7 @@ f_device_dialog_tcp() local use_dhcp="" use_rtsol="" local _ipaddr _netmask _extras - [ "$dev" ] || return $FAILURE + [ "$dev" ] || return $DIALOG_CANCEL # Initialize vars from previous device values local private @@ -1125,7 +1125,6 @@ f_device_dialog_tcp() unset $VAR_NONINTERACTIVE fi - # # Try a RTSOL scan if such behavior is desired. # If the variable was configured and is YES, do it. @@ -1321,7 +1320,7 @@ f_device_dialog_tcp() if [ ! "$cp" ]; then # User either chose "Cancel", pressed # ESC, or blanked every form field - return $FAILURE + return $DIALOG_CANCEL else n=$( echo "$cp" | f_number_of_lines ) [ $n -eq 1 ] && case "$cp" in HELP*) @@ -1404,14 +1403,12 @@ f_device_dialog_tcp() f_dialog_data_sanitize cp f_dprintf "retval=%u mtag=[%s]" $retval "$cp" - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$TCP_HELPFILE" continue - elif [ $retval -ne 0 ]; then - # User chose "Cancel" or pressed ESC + elif [ $retval -ne $DIALOG_OK ]; then f_dialog_title_restore - return $FAILURE + return $DIALOG_CANCEL fi case "$cp" in @@ -1490,7 +1487,7 @@ f_device_dialog_tcp() [ "$use_dhcp" ] || f_config_resolv # XXX this will do it on the MFS copy - return $SUCCESS + return $DIALOG_OK } # f_device_scan_tcp [$var_to_set] @@ -1586,15 +1583,15 @@ f_device_select_tcp() if [ ${cnt:=0} -gt 0 ]; then dev="${devs%%[$IFS]*}" f_device_dialog_tcp $dev - if [ $? -eq $SUCCESS ]; then + if [ $? -eq $DIALOG_OK ]; then setvar $VAR_NETWORK_DEVICE $dev - return $SUCCESS + return $DIALOG_OK fi fi done f_interactive && f_show_msg "$msg_no_network_devices" - return $FAILURE + return $DIALOG_CANCEL fi # $network_dev @@ -1610,18 +1607,18 @@ f_device_select_tcp() if f_dialog_yesno "$msg_assume_network_is_already_configured" then setvar $VAR_NETWORK_DEVICE $dev - return $SUCCESS + return $DIALOG_OK fi fi local retval=$SUCCESS if [ ${cnt:=0} -eq 0 ]; then f_show_msg "$msg_no_network_devices" - retval=$FAILURE + retval=$DIALOG_CANCEL elif [ $cnt -eq 1 ]; then f_device_dialog_tcp $dev retval=$? - [ $retval -eq $SUCCESS ] && setvar $VAR_NETWORK_DEVICE $dev + [ $retval -eq $DIALOG_OK ] && setvar $VAR_NETWORK_DEVICE $dev else local title="$msg_network_interface_information_required" local prompt="$msg_please_select_ethernet_device_to_configure" @@ -1632,15 +1629,15 @@ f_device_select_tcp() "$NETWORK_DEVICE_HELPFILE" \ 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ) retval=$? - [ "$dev" ] || return $FAILURE + [ "$dev" ] || return $DIALOG_CANCEL f_device_find "$dev" $DEVICE_TYPE_NETWORK devs - [ "$devs" ] || return $FAILURE + [ "$devs" ] || return $DIALOG_CANCEL dev="${devs%%[$IFS]*}" f_device_dialog_tcp $dev retval=$? - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then f_struct_copy device_$dev device_network setvar $VAR_NETWORK_DEVICE network else @@ -1675,7 +1672,7 @@ f_dialog_menu_select_tcp() "$name" fi fi - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/share/media/ufs.subr b/usr.sbin/bsdconfig/share/media/ufs.subr index cc63475..2de29b3 100644 --- a/usr.sbin/bsdconfig/share/media/ufs.subr +++ b/usr.sbin/bsdconfig/share/media/ufs.subr @@ -177,7 +177,7 @@ f_media_shutdown_ufs() { local dev="$1" err - [ "$UFS_MOUNTED" ] || return + [ "$UFS_MOUNTED" ] || return $FAILURE if ! err=$( umount -f "$MOUNTPOINT" 2>&1 ); then err="${err#umount: }"; err="${err#*: }" diff --git a/usr.sbin/bsdconfig/share/media/usb.subr b/usr.sbin/bsdconfig/share/media/usb.subr index e345ae0..f7afc29 100644 --- a/usr.sbin/bsdconfig/share/media/usb.subr +++ b/usr.sbin/bsdconfig/share/media/usb.subr @@ -158,7 +158,7 @@ f_media_shutdown_usb() { local dev="$1" err - [ "$USB_MOUNTED" ] || return + [ "$USB_MOUNTED" ] || return $FAILURE if ! err=$( umount -f "$MOUNTPOINT" 2>&1 ); then err="${err#umount: }"; err="${err#*: }" diff --git a/usr.sbin/bsdconfig/share/mustberoot.subr b/usr.sbin/bsdconfig/share/mustberoot.subr index 765487d..67b4c3e 100644 --- a/usr.sbin/bsdconfig/share/mustberoot.subr +++ b/usr.sbin/bsdconfig/share/mustberoot.subr @@ -176,9 +176,9 @@ f_become_root_via_sudo() retval=$? # Catch X11-related errors - if [ $retval -eq 255 ]; then + if [ $retval -eq $DIALOG_ESC ]; then f_die $retval "$password" - elif [ $retval -ne 0 ]; then + elif [ $retval -ne $DIALOG_OK ]; then # User cancelled exit $retval fi @@ -316,10 +316,10 @@ f_authenticate_some_user() retval=$? # Catch X11-related errors - [ $retval -eq 255 ] && f_die $retval "$user_pass" + [ $retval -eq $DIALOG_ESC ] && f_die $retval "$user_pass" # Exit if the user cancelled. - [ $retval -eq $SUCCESS ] || exit $retval + [ $retval -eq $DIALOG_OK ] || exit $retval # # Make sure the user exists and is non-root diff --git a/usr.sbin/bsdconfig/share/packages/packages.subr b/usr.sbin/bsdconfig/share/packages/packages.subr index e447b2e..85aebc8 100755 --- a/usr.sbin/bsdconfig/share/packages/packages.subr +++ b/usr.sbin/bsdconfig/share/packages/packages.subr @@ -132,7 +132,7 @@ f_package_select() package="$1" shift 1 # package for pkgsel in $SELECTED_PACKAGES; do - [ "$package" = "$pkgsel" ] && return + [ "$package" = "$pkgsel" ] && return $SUCCESS done SELECTED_PACKAGES="$SELECTED_PACKAGES $package" f_dprintf "Added %s to selection list" "$package" @@ -312,7 +312,7 @@ f_package_menu_categories() # creates _{varcat}_ninstalled and _{varcat}_nselected local category_list - debug= f_getvar "$var_to_get" category_list || return $FAILURE + debug= f_getvar "$var_to_get" category_list || return $DIALOG_CANCEL # Accent the category menu list with ninstalled/nselected eval f_package_accent_category_menu category_list $category_list @@ -395,7 +395,7 @@ f_package_menu_select() local defaultitem="$3" local hline="$hline_arrows_tab_punc_enter" - f_isinteger "$page" || return $FAILURE + f_isinteger "$page" || return $DIALOG_CANCEL local varcat f_str2varname "$category" varcat @@ -518,7 +518,7 @@ f_package_menu_select() f_dialog_data_sanitize menu_choice f_dialog_menutag_store "$menu_choice" - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then local item item=$( eval f_dialog_menutag2item${SHOW_DESC:+_with_help} \ \"\$menu_choice\" $menu_list ) @@ -603,7 +603,7 @@ f_package_review() done if [ ! "$menu_list" ]; then f_show_msg "$msg_no_packages_were_selected_for_extraction" - return $FAILURE # They might have selected this by accident + return $DIALOG_CANCEL # Might have selected this by accident fi menu_list=$( echo "$menu_list" | sort ) @@ -660,7 +660,7 @@ f_package_review() f_package_deselect "$package" done - return $SUCCESS + return $DIALOG_OK } # f_package_config @@ -693,7 +693,7 @@ f_package_config() f_dprintf "retval=%u mtag=[%s]" $retval "$category" category_defaultitem="$category" - [ $retval -eq $SUCCESS ] || break + [ $retval -eq $DIALOG_OK ] || break # Maybe the user chose an action (like `Review') case "$category" in @@ -729,14 +729,14 @@ f_package_config() # the Cancel button because stdout will be NULL. # Alternatively, Xdialog(1) will terminate with 1 # if/when Cancel is chosen on any widget. - if [ $retval -eq 255 -o ! "$menu_choice" ]; then - # User pressed ESC or chose Cancel + if [ $retval -eq $DIALOG_ESC -o ! "$menu_choice" ] + then break - elif [ $retval -eq 1 ]; then + elif [ $retval -eq $DIALOG_CANCEL ]; then # Using X11, Xdialog(1) returned 1 for Cancel f_show_msg "%s" "$menu_choice" break - elif [ $retval -ne $SUCCESS ]; then + elif [ $retval -ne $DIALOG_OK ]; then # X11-related error occurred using Xdialog(1) f_show_msg "%s" "$menu_choice" break diff --git a/usr.sbin/bsdconfig/startup/misc b/usr.sbin/bsdconfig/startup/misc index edfc1ac..738f730 100755 --- a/usr.sbin/bsdconfig/startup/misc +++ b/usr.sbin/bsdconfig/startup/misc @@ -309,10 +309,10 @@ dialog_input_value() f_dialog_title_restore # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval value="$_input" - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN @@ -429,7 +429,7 @@ while :; do ;; esac - [ $? -eq $SUCCESS ] || f_dialog_msgbox "$err\n" + [ $? -eq $DIALOG_OK ] || f_dialog_msgbox "$err\n" done exit $SUCCESS diff --git a/usr.sbin/bsdconfig/startup/rcconf b/usr.sbin/bsdconfig/startup/rcconf index d064224..6154d27 100755 --- a/usr.sbin/bsdconfig/startup/rcconf +++ b/usr.sbin/bsdconfig/startup/rcconf @@ -195,7 +195,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" # Only update default-item on success - [ $retval -eq 0 ] && f_dialog_default_store "$menu_choice" + [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } @@ -236,13 +236,13 @@ while :; do f_dialog_input_view_details continue esac - elif [ $retval -eq 2 ]; then + elif [ $retval -eq $DIALOG_HELP ]; then # The ``Help'' button (labeled "Details") was pressed f_dialog_input_view_details continue fi - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die case "$mtag" in "X $msg_exit") break ;; diff --git a/usr.sbin/bsdconfig/startup/rcdelete b/usr.sbin/bsdconfig/startup/rcdelete index b6eeba4..5769be9 100755 --- a/usr.sbin/bsdconfig/startup/rcdelete +++ b/usr.sbin/bsdconfig/startup/rcdelete @@ -232,7 +232,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" # Only update default-item on success - [ $retval -eq 0 ] && f_dialog_default_store "$menu_choice" + [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } @@ -249,7 +249,7 @@ dialog_menu_confirm_delete() local menu_list # Calculated below local hline="$hline_arrows_tab_enter" - [ $# -ge 1 ] || return $FAILURE + [ $# -ge 1 ] || return $DIALOG_CANCEL # If asked to delete only one variable, simply ask and return if [ $# -eq 1 ]; then @@ -351,13 +351,13 @@ while :; do f_dialog_input_view_details && dialog_create_main continue esac - elif [ $retval -eq 2 ]; then + elif [ $retval -eq $DIALOG_HELP ]; then # The ``Help'' button (labeled "Details") was pressed f_dialog_input_view_details && dialog_create_main continue fi - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die case "$mtag" in "X $msg_exit_cancel") break ;; diff --git a/usr.sbin/bsdconfig/startup/rcvar b/usr.sbin/bsdconfig/startup/rcvar index 7d29bfc..e206ed4 100755 --- a/usr.sbin/bsdconfig/startup/rcvar +++ b/usr.sbin/bsdconfig/startup/rcvar @@ -158,7 +158,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" f_dialog_default_store "$menu_choice" - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then local item item=$( eval f_dialog_menutag2item${SHOW_DESC:+_with_help} \ \"\$menu_choice\" $menu_list ) diff --git a/usr.sbin/bsdconfig/startup/share/rcconf.subr b/usr.sbin/bsdconfig/startup/share/rcconf.subr index 4c3e0d6..f8f9a66 100644 --- a/usr.sbin/bsdconfig/startup/share/rcconf.subr +++ b/usr.sbin/bsdconfig/startup/share/rcconf.subr @@ -336,7 +336,7 @@ f_dialog_input_view_details() f_dialog_title_restore - [ $retval -eq 0 ] || return $FAILURE + [ $retval -eq $DIALOG_OK ] || return $DIALOG_CANCEL case "$mtag" in "R $msg_reset") @@ -465,7 +465,7 @@ f_dialog_input_rcvar() # Return if user either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_please_enter_rcvar_name" \ - "$_input" "$hline_alnum_tab_enter" || return + "$_input" "$hline_alnum_tab_enter" || return $? # Check for invalid entry (1of2) if ! echo "$_input" | grep -q "^[[:alpha:]_]"; then @@ -486,7 +486,7 @@ f_dialog_input_rcvar() f_dprintf "f_dialog_input_rcvar: rcvar->[%s]" "$rcvar" - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/startup/share/rcedit.subr b/usr.sbin/bsdconfig/startup/share/rcedit.subr index 64ee1cc..fe0989d 100644 --- a/usr.sbin/bsdconfig/startup/share/rcedit.subr +++ b/usr.sbin/bsdconfig/startup/share/rcedit.subr @@ -69,11 +69,11 @@ f_dialog_rcedit() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg" "$_input" \ - "$hline_alnum_punc_tab_enter" || return + "$hline_alnum_punc_tab_enter" || return $? # Return if the value has not changed from current local cur_val="$( f_sysrc_get "$var" )" - [ "$_input" = "$cur_val" ] && return $SUCCESS + [ "$_input" = "$cur_val" ] && return $DIALOG_OK f_dprintf "%s: [%s]->[%s]" "$var" "$cur_val" "$_input" diff --git a/usr.sbin/bsdconfig/timezone/timezone b/usr.sbin/bsdconfig/timezone/timezone index 709c8a8..d4b7ab5 100755 --- a/usr.sbin/bsdconfig/timezone/timezone +++ b/usr.sbin/bsdconfig/timezone/timezone @@ -226,7 +226,7 @@ if [ "$_PATH_WALL_CMOS_CLOCK" -a ! "$SKIPUTC" ]; then result=$? fi - if [ $result -eq 0 ]; then + if [ $result -eq $DIALOG_OK ]; then # User chose YES [ "$REALLYDOIT" ] && f_quietly rm -f "$_PATH_WALL_CMOS_CLOCK" @@ -250,7 +250,7 @@ if [ $# -ge 1 ]; then result=$? f_dialog_title_restore - if [ $result -eq 0 ]; then + if [ $result -eq $DIALOG_OK ]; then # User chose YES f_install_zoneinfo_file "$default" result=$? @@ -287,7 +287,7 @@ while :; do retval=$? f_dialog_menutag_fetch mtag - if [ $retval -ne 0 ]; then + if [ $retval -ne $DIALOG_OK ]; then [ "$TZ_OR_FAIL" ] && f_die exit $SUCCESS fi @@ -368,7 +368,7 @@ while :; do f_dialog_data_sanitize tag defaultctry="$tag" - if [ $retval -ne 0 ]; then + if [ $retval -ne $DIALOG_OK ]; then NEED_CONTINENT=1 continue # back to main menu fi @@ -427,7 +427,7 @@ while :; do f_dialog_data_sanitize n defaultzone="$n" - if [ $retval -ne 0 ]; then + if [ $retval -ne $DIALOG_OK ]; then [ $nitems -eq 1 ] && NEED_CONTINENT=1 NEED_COUNTRY=1 continue @@ -441,7 +441,7 @@ while :; do f_confirm_zone "$real_continent/$filename" || continue fi - [ $retval -eq 0 ] || continue # back to main menu + [ $retval -eq $DIALOG_OK ] || continue # back to main menu if ! f_install_zoneinfo "$real_continent/$filename"; then [ $nzones -lt 0 ] && NEED_COUNTRY=1 diff --git a/usr.sbin/bsdconfig/usermgmt/groupdel b/usr.sbin/bsdconfig/usermgmt/groupdel index 3076216..2f41164 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupdel +++ b/usr.sbin/bsdconfig/usermgmt/groupdel @@ -75,7 +75,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" defaultitem="$mtag" - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die [ "$mtag" = "X $msg_exit" ] && break diff --git a/usr.sbin/bsdconfig/usermgmt/groupedit b/usr.sbin/bsdconfig/usermgmt/groupedit index 1f1c744..3eacaff 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupedit +++ b/usr.sbin/bsdconfig/usermgmt/groupedit @@ -75,7 +75,7 @@ while :; do f_dprintf "retval=%s mtag=[%s]" $retval "$mtag" defaultitem="$mtag" - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die [ "$mtag" = "X $msg_exit" ] && break diff --git a/usr.sbin/bsdconfig/usermgmt/groupinput b/usr.sbin/bsdconfig/usermgmt/groupinput index 6fff40f..5b60bb2 100755 --- a/usr.sbin/bsdconfig/usermgmt/groupinput +++ b/usr.sbin/bsdconfig/usermgmt/groupinput @@ -162,9 +162,9 @@ if [ "$mode" = "Add" ]; then f_dialog_noyes "$msg_use_default_values_for_all_account_details" retval=$? - if [ $retval -eq 255 ]; then # User pressed ESC + if [ $retval -eq $DIALOG_ESC ]; then exit $SUCCESS - elif [ $retval -ne $SUCCESS ]; then + elif [ $retval -ne $DIALOG_OK ]; then # # Ask a series of questions to pre-fill the editor screen. # @@ -174,9 +174,9 @@ if [ "$mode" = "Add" ]; then # [ "$passwdtype" = "yes" ] && - { f_dialog_input_group_password || exit 0; } - f_dialog_input_group_gid || exit 0 - f_dialog_input_group_members || exit 0 + { f_dialog_input_group_password || exit $SUCCESS; } + f_dialog_input_group_gid || exit $SUCCESS + f_dialog_input_group_members || exit $SUCCESS fi fi @@ -255,13 +255,11 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" # Exit if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die case "$mtag" in X) # Exit - if [ "$save_flag" ]; then - save_changes || continue - fi + [ "$save_flag" ] && { save_changes || continue; } break ;; 1) # Group Name @@ -274,7 +272,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" # Loop if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue [ "$mtag" = "X $msg_exit" ] && continue diff --git a/usr.sbin/bsdconfig/usermgmt/share/group_input.subr b/usr.sbin/bsdconfig/usermgmt/share/group_input.subr index 93d981a..2ca141e 100644 --- a/usr.sbin/bsdconfig/usermgmt/share/group_input.subr +++ b/usr.sbin/bsdconfig/usermgmt/share/group_input.subr @@ -125,15 +125,15 @@ f_dialog_input_group_name() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_group" "$_input" \ - "$hline_alnum_tab_enter" || return + "$hline_alnum_tab_enter" || return $? # Check for no-change - [ "$_input" = "$_name" ] && return $SUCCESS + [ "$_input" = "$_name" ] && return $DIALOG_OK # Check for reversion if [ "$_input" = "$cur_group_name" ]; then group_name="$cur_group_name" - return $SUCCESS + return $DIALOG_OK fi # Check for NULL entry @@ -161,7 +161,7 @@ f_dialog_input_group_name() f_dprintf "group_name: [%s]->[%s]" "$cur_group_name" "$group_name" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_group_password @@ -210,7 +210,7 @@ f_dialog_input_group_password() debug= f_dialog_line_sanitize _password1 # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval _password2=$( $DIALOG \ --title "$DIALOG_TITLE" \ @@ -227,7 +227,7 @@ f_dialog_input_group_password() debug= f_dialog_line_sanitize _password2 # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval # Check for password mismatch if [ "$_password1" != "$_password2" ]; then @@ -239,9 +239,9 @@ f_dialog_input_group_password() if [ ! "$_password1" ]; then f_dialog_yesno "$msg_disable_password_auth_for_group" local retval=$? - if [ $retval -eq 255 ]; then # ESC was pressed + if [ $retval -eq $DIALOG_ESC ]; then return $retval - elif [ $retval -eq $SUCCESS ]; then + elif [ $retval -eq $DIALOG_OK ]; then pw_group_password_disable=1 else continue # back to password prompt @@ -258,7 +258,7 @@ f_dialog_input_group_password() f_dprintf "group_password: [%s]->[%s]" \ "$cur_group_password" "$group_password" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_group_gid [$group_gid] @@ -273,14 +273,14 @@ f_dialog_input_group_gid() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_group_id_leave_empty_for_default" \ - "$_input" "$hline_num_tab_enter" || return + "$_input" "$hline_num_tab_enter" || return $? group_gid="$_input" save_flag=1 f_dprintf "group_gid: [%s]->[%s]" "$cur_group_gid" "$group_gid" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_group_members [$group_members] @@ -329,7 +329,7 @@ f_dialog_input_group_members() f_dprintf "retval=%u menu_choice=[%s]" $retval "$menu_choice" # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval local _group_members case "$menu_choice" in @@ -399,7 +399,7 @@ f_dialog_input_group_members() f_dprintf "group_members: [%s]->[%s]" \ "$cur_group_members" "$group_members" - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr index 21bfbd3..bd2fc9b 100644 --- a/usr.sbin/bsdconfig/usermgmt/share/user_input.subr +++ b/usr.sbin/bsdconfig/usermgmt/share/user_input.subr @@ -206,7 +206,7 @@ f_dialog_input_member_groups() f_dprintf "pw_member_groups: [%s]->[%s]" \ "$cur_pw_member_groups" "$pw_member_groups" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_name [$name] @@ -229,15 +229,15 @@ f_dialog_input_name() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_login" "$_input" \ - "$hline_alnum_tab_enter" || return + "$hline_alnum_tab_enter" || return $? # Check for no-change - [ "$_input" = "$_name" ] && return $SUCCESS + [ "$_input" = "$_name" ] && return $DIALOG_OK # Check for reversion if [ "$_input" = "$cur_pw_name" ]; then pw_name="$cur_pw_name" - return $SUCCESS + return $DIALOG_OK fi # Check for NULL entry @@ -265,7 +265,7 @@ f_dialog_input_name() f_dprintf "pw_name: [%s]->[%s]" "$cur_pw_name" "$pw_name" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_password @@ -336,9 +336,9 @@ f_dialog_input_password() if [ ! "$_password1" ]; then f_dialog_yesno "$msg_disable_password_auth_for_account" local retval=$? - if [ $retval -eq 255 ]; then # ESC was pressed + if [ $retval -eq $DIALOG_ESC ]; then return $retval - elif [ $retval -eq $SUCCESS ]; then + elif [ $retval -eq $DIALOG_OK ]; then pw_password_disable=1 else continue # back to password prompt @@ -354,7 +354,7 @@ f_dialog_input_password() f_dprintf "pw_password: [%s]->[%s]" "$cur_pw_password" "$pw_password" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_gecos [$gecos] @@ -370,14 +370,14 @@ f_dialog_input_gecos() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_full_name" "$_input" \ - "$hline_alnum_punc_tab_enter" || return + "$hline_alnum_punc_tab_enter" || return $? pw_gecos="$_input" save_flag=1 f_dprintf "pw_gecos: [%s]->[%s]" "$cur_pw_gecos" "$pw_gecos" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_uid [$uid] @@ -392,14 +392,14 @@ f_dialog_input_uid() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_user_id_leave_empty_for_default" \ - "$_input" "$hline_num_tab_enter" || return + "$_input" "$hline_num_tab_enter" || return $? pw_uid="$_input" save_flag=1 f_dprintf "pw_uid: [%s]->[%s]" "$cur_pw_uid" "$pw_uid" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_gid [$gid] @@ -414,14 +414,14 @@ f_dialog_input_gid() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_group_id_leave_empty_for_default" \ - "$_input" "$hline_num_tab_enter" || return + "$_input" "$hline_num_tab_enter" || return $? pw_gid="$_input" save_flag=1 f_dprintf "pw_gid: [%s]->[%s]" "$cur_pw_gid" "$pw_gid" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_class [$class] @@ -436,14 +436,14 @@ f_dialog_input_class() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_login_class" "$_input" \ - "$hline_alnum_tab_enter" || return + "$hline_alnum_tab_enter" || return $? pw_class="$_input" save_flag=1 f_dprintf "pw_class: [%s]->[%s]" "$cur_pw_class" "$pw_class" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_expire_password [$seconds] @@ -508,7 +508,7 @@ f_dialog_input_expire_password() f_dprintf "retval=%u date_type=[%s]" $retval "$date_type" # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval case "$date_type" in 1) # Password does not expire @@ -538,7 +538,7 @@ f_dialog_input_expire_password() f_dprintf "retval=%u ret_date=[%s]" $retval "$ret_date" # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue _input_time= [ "$secs" ] && _input_time=$( date -j \ @@ -559,7 +559,7 @@ f_dialog_input_expire_password() f_dprintf "retval=%u ret_time=[%s]" $retval "$ret_time" # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue _input=$( date \ -j -f "%d/%m/%Y %T" \ @@ -627,7 +627,7 @@ f_dialog_input_expire_password() f_dprintf "pw_password_expire: [%s]->[%s]" \ "$cur_pw_password_expire" "$pw_password_expire" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_expire_account [$seconds] @@ -692,7 +692,7 @@ f_dialog_input_expire_account() f_dprintf "retval=%u date_type=[%s]" $retval "$date_type" # Return if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || return $retval + [ $retval -eq $DIALOG_OK ] || return $retval case "$date_type" in 1) # Account does not expire @@ -722,7 +722,7 @@ f_dialog_input_expire_account() f_dprintf "retval=%u ret_date=[%s]" $retval "$ret_date" # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue _input_time= [ "$secs" ] && _input_time=$( date -j \ @@ -743,7 +743,7 @@ f_dialog_input_expire_account() f_dprintf "retval=%u ret_time=[%s]" $retval "$ret_time" # Return to menu if either ESC or Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue _input=$( date \ -j -f "%d/%m/%Y %T" \ @@ -811,7 +811,7 @@ f_dialog_input_expire_account() f_dprintf "pw_account_expire: [%s]->[%s]" \ "$cur_pw_account_expire" "$pw_account_expire" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_home_dir [$home_dir] @@ -826,14 +826,14 @@ f_dialog_input_home_dir() # Return if user has either pressed ESC or chosen Cancel/No f_dialog_input _input "$msg_home_directory" "$_input" \ - "$hline_alnum_punc_tab_enter" || return + "$hline_alnum_punc_tab_enter" || return $? pw_home_dir="$_input" save_flag=1 f_dprintf "pw_home_dir: [%s]->[%s]" "$cur_pw_home_dir" "$pw_home_dir" - return $SUCCESS + return $DIALOG_OK } # f_dialog_input_home_create @@ -850,7 +850,7 @@ f_dialog_input_home_create() f_dialog_yesno "$msg_create_home_directory" retval=$? - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then pw_home_create="$msg_yes" else pw_home_create="$msg_no" @@ -860,7 +860,7 @@ f_dialog_input_home_create() f_dprintf "pw_home_create: [%s]->[%s]" \ "$cur_pw_home_create" "$pw_home_create" - [ $retval -ne 255 ] # return failure if user pressed ESC + [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } # f_dialog_input_group_delete @@ -893,7 +893,7 @@ f_dialog_input_group_delete() fi retval=$? - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then pw_group_delete="$msg_yes" else pw_group_delete="$msg_no" @@ -903,7 +903,7 @@ f_dialog_input_group_delete() f_dprintf "pw_group_delete: [%s]->[%s]" \ "$cur_pw_group_delete" "$pw_group_delete" - [ $retval -ne 255 ] # return failure if user pressed ESC + [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } # f_dialog_input_home_delete @@ -920,7 +920,7 @@ f_dialog_input_home_delete() f_dialog_yesno "$msg_delete_home_directory" retval=$? - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then pw_home_delete="$msg_yes" else pw_home_delete="$msg_no" @@ -930,7 +930,7 @@ f_dialog_input_home_delete() f_dprintf "pw_home_delete: [%s]->[%s]" \ "$cur_pw_home_delete" "$pw_home_delete" - [ $retval -ne 255 ] # return failure if user pressed ESC + [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } # f_dialog_input_dotfiles_create @@ -948,7 +948,7 @@ f_dialog_input_dotfiles_create() f_dialog_yesno "$msg_create_dotfiles" retval=$? - if [ $retval -eq $SUCCESS ]; then + if [ $retval -eq $DIALOG_OK ]; then pw_dotfiles_create="$msg_yes" else pw_dotfiles_create="$msg_no" @@ -958,7 +958,7 @@ f_dialog_input_dotfiles_create() f_dprintf "pw_dotfiles_create: [%s]->[%s]" \ "$cur_pw_dotfiles_create" "$pw_dotfiles_create" - [ $retval -ne 255 ] # return failure if user pressed ESC + [ $retval -ne $DIALOG_ESC ] # return failure if user pressed ESC } # f_dialog_input_shell [$shell] @@ -1015,7 +1015,7 @@ f_dialog_input_shell() f_dprintf "pw_shell: [%s]->[%s]" "$cur_pw_shell" "$pw_shell" - return $SUCCESS + return $DIALOG_OK } ############################################################ MAIN diff --git a/usr.sbin/bsdconfig/usermgmt/userdel b/usr.sbin/bsdconfig/usermgmt/userdel index f266497..299e25b 100755 --- a/usr.sbin/bsdconfig/usermgmt/userdel +++ b/usr.sbin/bsdconfig/usermgmt/userdel @@ -75,7 +75,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" defaultitem="$mtag" - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die [ "$mtag" = "X $msg_exit" ] && break diff --git a/usr.sbin/bsdconfig/usermgmt/useredit b/usr.sbin/bsdconfig/usermgmt/useredit index 64b7b8e..a4bee68 100755 --- a/usr.sbin/bsdconfig/usermgmt/useredit +++ b/usr.sbin/bsdconfig/usermgmt/useredit @@ -75,7 +75,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" defaultitem="$mtag" - [ $retval -eq 0 ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die [ "$mtag" = "X $msg_exit" ] && break diff --git a/usr.sbin/bsdconfig/usermgmt/userinput b/usr.sbin/bsdconfig/usermgmt/userinput index 196262b..aad6ef3 100755 --- a/usr.sbin/bsdconfig/usermgmt/userinput +++ b/usr.sbin/bsdconfig/usermgmt/userinput @@ -257,9 +257,9 @@ if [ "$mode" = "Add" ]; then f_dialog_noyes "$msg_use_default_values_for_all_account_details" retval=$? - if [ $retval -eq 255 ]; then # User pressed ESC + if [ $retval -eq $DIALOG_ESC ]; then exit $SUCCESS - elif [ $retval -ne $SUCCESS ]; then + elif [ $retval -ne $DIALOG_OK ]; then # # Ask a series of questions to pre-fill the editor screen. # @@ -431,7 +431,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" # Exit if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || f_die + [ $retval -eq $DIALOG_OK ] || f_die case "$mtag" in X) # Exit @@ -450,7 +450,7 @@ while :; do f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" # Loop if user has either pressed ESC or chosen Cancel/No - [ $retval -eq $SUCCESS ] || continue + [ $retval -eq $DIALOG_OK ] || continue [ "$mtag" = "X $msg_exit" ] && continue diff --git a/usr.sbin/bsdconfig/usermgmt/usermgmt b/usr.sbin/bsdconfig/usermgmt/usermgmt index 0a4c160..5335bd6 100755 --- a/usr.sbin/bsdconfig/usermgmt/usermgmt +++ b/usr.sbin/bsdconfig/usermgmt/usermgmt @@ -100,7 +100,7 @@ dialog_menu_main() f_dialog_menutag_store "$menu_choice" # Only update default-item on success - [ $retval -eq 0 ] && f_dialog_default_store "$menu_choice" + [ $retval -eq $DIALOG_OK ] && f_dialog_default_store "$menu_choice" return $retval } @@ -136,11 +136,10 @@ while :; do f_dialog_menutag_fetch mtag f_dprintf "retval=%u mtag=[%s]" $retval "$mtag" - if [ $retval -eq 2 ]; then - # The Help button was pressed + if [ $retval -eq $DIALOG_HELP ]; then f_show_help "$USERMGMT_HELPFILE" continue - elif [ $retval -ne $SUCCESS ]; then + elif [ $retval -ne $DIALOG_OK ]; then f_die fi diff --git a/usr.sbin/bsdinstall/partedit/gpart_ops.c b/usr.sbin/bsdinstall/partedit/gpart_ops.c index 479365a..289ac98 100644 --- a/usr.sbin/bsdinstall/partedit/gpart_ops.c +++ b/usr.sbin/bsdinstall/partedit/gpart_ops.c @@ -954,7 +954,8 @@ addpartform: } /* If there isn't one, and we need one, ask */ - if (strcmp(items[2].text, "/") == 0 && bootpart_size(scheme) > 0 && + if ((strcmp(items[0].text, "freebsd") == 0 || + strcmp(items[2].text, "/") == 0) && bootpart_size(scheme) > 0 && pp == NULL) { if (interactive) choice = dialog_yesno("Boot Partition", diff --git a/usr.sbin/bsdinstall/partedit/partedit_powerpc.c b/usr.sbin/bsdinstall/partedit/partedit_powerpc.c index ef23eb6..06129c7 100644 --- a/usr.sbin/bsdinstall/partedit/partedit_powerpc.c +++ b/usr.sbin/bsdinstall/partedit/partedit_powerpc.c @@ -59,7 +59,8 @@ is_scheme_bootable(const char *part_type) { return (1); if (strcmp(platform, "ps3") == 0 && strcmp(part_type, "GPT") == 0) return (1); - if (strcmp(platform, "chrp") == 0 && strcmp(part_type, "MBR") == 0) + if (strcmp(platform, "chrp") == 0 && + (strcmp(part_type, "MBR") == 0 || strcmp(part_type, "BSD") == 0)) return (1); return (0); diff --git a/usr.sbin/bsdinstall/scripts/mirrorselect b/usr.sbin/bsdinstall/scripts/mirrorselect index 0776a4d..ef17cfe 100755 --- a/usr.sbin/bsdinstall/scripts/mirrorselect +++ b/usr.sbin/bsdinstall/scripts/mirrorselect @@ -87,7 +87,6 @@ MIRROR=`dialog --backtitle "FreeBSD Installer" \ ftp://ftp8.de.freebsd.org "Germany #8"\ ftp://ftp.gr.freebsd.org "Greece"\ ftp://ftp2.gr.freebsd.org "Greece #2"\ - ftp://ftp.is.freebsd.org "Iceland"\ ftp://ftp3.ie.freebsd.org "Ireland #3"\ ftp://ftp.il.freebsd.org "Israel"\ ftp://ftp.it.freebsd.org "Italy"\ @@ -127,7 +126,6 @@ MIRROR=`dialog --backtitle "FreeBSD Installer" \ ftp://ftp2.se.freebsd.org "Sweden #2"\ ftp://ftp3.se.freebsd.org "Sweden #3"\ ftp://ftp4.se.freebsd.org "Sweden #4"\ - ftp://ftp5.se.freebsd.org "Sweden #5"\ ftp://ftp6.se.freebsd.org "Sweden #6"\ ftp://ftp.ch.freebsd.org "Switzerland"\ ftp://ftp.tw.freebsd.org "Taiwan"\ diff --git a/usr.sbin/ctladm/ctladm.8 b/usr.sbin/ctladm/ctladm.8 index 68e79ff..b9d4e61 100644 --- a/usr.sbin/ctladm/ctladm.8 +++ b/usr.sbin/ctladm/ctladm.8 @@ -197,6 +197,16 @@ .Nm .Ic dumpstructs .Nm +.Ic islist +.Op Fl v +.Op Fl x +.Nm +.Ic islogout +.Aq Fl a | Fl h Ar host | Fl c Ar connection-id | Fl i Ar name +.Nm +.Ic isterminate +.Aq Fl a | Fl h Ar host | Fl c Ar connection-id | Fl i Ar name +.Nm .Ic help .Sh DESCRIPTION The @@ -883,6 +893,41 @@ If you specify .Fl x , the entire LUN database is displayed in XML format. .El +.It Ic islist +Get a list of currently running iSCSI connections. +This includes initiator and target names and the unique connection IDs. +.Bl -tag -width 11n +.It Fl v +Verbose mode. +.It Fl x +Dump the raw XML. +The connections list information from the kernel comes in XML format, and this +option allows the display of the raw XML data. +.El +.It Ic islogout +Ask the initiator to log out iSCSI connections matching criteria. +.Bl -tag -width 11n +.It Fl a +Log out all connections. +.It Fl h +Specify initiator IP address. +.It Fl c +Specify connection ID. +.It Fl i +Specify initiator name. +.El +.It Ic isterminate +Forcibly terminate iSCSI connections matching criteria. +.Bl -tag -width 11n +.It Fl a +Terminate all connections. +.It Fl h +Specify initiator IP address. +.It Fl c +Specify connection ID. +.It Fl i +Specify initiator name. +.El .It Ic help Display .Nm @@ -977,7 +1022,8 @@ This will result in a sense key of NOT READY (0x02), and an ASC/ASCQ of .Xr cam 4 , .Xr ctl 4 , .Xr xpt 4 , -.Xr camcontrol 8 +.Xr camcontrol 8 , +.Xr ctld 8 .Sh HISTORY The .Nm diff --git a/usr.sbin/ctladm/ctladm.c b/usr.sbin/ctladm/ctladm.c index 17c1148..6f03de0 100644 --- a/usr.sbin/ctladm/ctladm.c +++ b/usr.sbin/ctladm/ctladm.c @@ -117,7 +117,10 @@ typedef enum { CTLADM_CMD_PRES_OUT, CTLADM_CMD_INQ_VPD_DEVID, CTLADM_CMD_RTPG, - CTLADM_CMD_MODIFY + CTLADM_CMD_MODIFY, + CTLADM_CMD_ISLIST, + CTLADM_CMD_ISLOGOUT, + CTLADM_CMD_ISTERMINATE } ctladm_cmdfunction; typedef enum { @@ -180,6 +183,9 @@ static struct ctladm_opts option_table[] = { {"help", CTLADM_CMD_HELP, CTLADM_ARG_NONE, NULL}, {"inject", CTLADM_CMD_ERR_INJECT, CTLADM_ARG_NEED_TL, "cd:i:p:r:s:"}, {"inquiry", CTLADM_CMD_INQUIRY, CTLADM_ARG_NEED_TL, NULL}, + {"islist", CTLADM_CMD_ISLIST, CTLADM_ARG_NONE, "vx"}, + {"islogout", CTLADM_CMD_ISLOGOUT, CTLADM_ARG_NONE, "ah:c:i:"}, + {"isterminate", CTLADM_CMD_ISTERMINATE, CTLADM_ARG_NONE, "ah:c:i:"}, {"lunlist", CTLADM_CMD_LUNLIST, CTLADM_ARG_NONE, NULL}, {"modesense", CTLADM_CMD_MODESENSE, CTLADM_ARG_NEED_TL, "P:S:dlm:c:"}, {"modify", CTLADM_CMD_MODIFY, CTLADM_ARG_NONE, "b:l:s:"}, @@ -489,6 +495,9 @@ retry: case CTL_PORT_ISC: type = "ISC"; break; + case CTL_PORT_ISCSI: + type = "ISCSI"; + break; default: type = "UNKNOWN"; break; @@ -578,6 +587,7 @@ static struct ctladm_opts cctl_fe_table[] = { {"fc", CTL_PORT_FC, CTLADM_ARG_NONE, NULL}, {"scsi", CTL_PORT_SCSI, CTLADM_ARG_NONE, NULL}, {"internal", CTL_PORT_INTERNAL, CTLADM_ARG_NONE, NULL}, + {"iscsi", CTL_PORT_ISCSI, CTLADM_ARG_NONE, NULL}, {"all", CTL_PORT_ALL, CTLADM_ARG_NONE, NULL}, {NULL, 0, 0, NULL} }; @@ -690,7 +700,7 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt) } else if ((targ_port == -1) && (port_type == CTL_PORT_NONE)) port_type = CTL_PORT_ALL; - bzero(&entry, sizeof(&entry)); + bzero(&entry, sizeof(entry)); /* * These are needed for all but list/dump mode. @@ -3399,6 +3409,403 @@ bailout: return (retval); } +struct cctl_islist_conn { + int connection_id; + char *initiator; + char *initiator_addr; + char *initiator_alias; + char *target; + char *target_alias; + char *header_digest; + char *data_digest; + char *max_data_segment_length;; + int immediate_data; + int iser; + STAILQ_ENTRY(cctl_islist_conn) links; +}; + +struct cctl_islist_data { + int num_conns; + STAILQ_HEAD(,cctl_islist_conn) conn_list; + struct cctl_islist_conn *cur_conn; + int level; + struct sbuf *cur_sb[32]; +}; + +static void +cctl_islist_start_element(void *user_data, const char *name, const char **attr) +{ + int i; + struct cctl_islist_data *islist; + struct cctl_islist_conn *cur_conn; + + islist = (struct cctl_islist_data *)user_data; + cur_conn = islist->cur_conn; + islist->level++; + if ((u_int)islist->level >= (sizeof(islist->cur_sb) / + sizeof(islist->cur_sb[0]))) + errx(1, "%s: too many nesting levels, %zd max", __func__, + sizeof(islist->cur_sb) / sizeof(islist->cur_sb[0])); + + islist->cur_sb[islist->level] = sbuf_new_auto(); + if (islist->cur_sb[islist->level] == NULL) + err(1, "%s: Unable to allocate sbuf", __func__); + + if (strcmp(name, "connection") == 0) { + if (cur_conn != NULL) + errx(1, "%s: improper connection element nesting", + __func__); + + cur_conn = calloc(1, sizeof(*cur_conn)); + if (cur_conn == NULL) + err(1, "%s: cannot allocate %zd bytes", __func__, + sizeof(*cur_conn)); + + islist->num_conns++; + islist->cur_conn = cur_conn; + + STAILQ_INSERT_TAIL(&islist->conn_list, cur_conn, links); + + for (i = 0; attr[i] != NULL; i += 2) { + if (strcmp(attr[i], "id") == 0) { + cur_conn->connection_id = + strtoull(attr[i+1], NULL, 0); + } else { + errx(1, + "%s: invalid connection attribute %s = %s", + __func__, attr[i], attr[i+1]); + } + } + } +} + +static void +cctl_islist_end_element(void *user_data, const char *name) +{ + struct cctl_islist_data *islist; + struct cctl_islist_conn *cur_conn; + char *str; + + islist = (struct cctl_islist_data *)user_data; + cur_conn = islist->cur_conn; + + if ((cur_conn == NULL) + && (strcmp(name, "ctlislist") != 0)) + errx(1, "%s: cur_conn == NULL! (name = %s)", __func__, name); + + if (islist->cur_sb[islist->level] == NULL) + errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, + islist->level, name); + + sbuf_finish(islist->cur_sb[islist->level]); + str = strdup(sbuf_data(islist->cur_sb[islist->level])); + if (str == NULL) + err(1, "%s can't allocate %zd bytes for string", __func__, + sbuf_len(islist->cur_sb[islist->level])); + + sbuf_delete(islist->cur_sb[islist->level]); + islist->cur_sb[islist->level] = NULL; + islist->level--; + + if (strcmp(name, "initiator") == 0) { + cur_conn->initiator = str; + str = NULL; + } else if (strcmp(name, "initiator_addr") == 0) { + cur_conn->initiator_addr = str; + str = NULL; + } else if (strcmp(name, "initiator_alias") == 0) { + cur_conn->initiator_alias = str; + str = NULL; + } else if (strcmp(name, "target") == 0) { + cur_conn->target = str; + str = NULL; + } else if (strcmp(name, "target_alias") == 0) { + cur_conn->target_alias = str; + str = NULL; + } else if (strcmp(name, "header_digest") == 0) { + cur_conn->header_digest = str; + str = NULL; + } else if (strcmp(name, "data_digest") == 0) { + cur_conn->data_digest = str; + str = NULL; + } else if (strcmp(name, "max_data_segment_length") == 0) { + cur_conn->max_data_segment_length = str; + str = NULL; + } else if (strcmp(name, "immediate_data") == 0) { + cur_conn->immediate_data = atoi(str); + } else if (strcmp(name, "iser") == 0) { + cur_conn->iser = atoi(str); + } else if (strcmp(name, "connection") == 0) { + islist->cur_conn = NULL; + } else if (strcmp(name, "ctlislist") == 0) { + } else + errx(1, "unknown element %s", name); + + free(str); +} + +static void +cctl_islist_char_handler(void *user_data, const XML_Char *str, int len) +{ + struct cctl_islist_data *islist; + + islist = (struct cctl_islist_data *)user_data; + + sbuf_bcat(islist->cur_sb[islist->level], str, len); +} + +static int +cctl_islist(int fd, int argc, char **argv, char *combinedopt) +{ + struct ctl_iscsi req; + struct cctl_islist_data islist; + struct cctl_islist_conn *conn; + XML_Parser parser; + char *conn_str; + int conn_len; + int dump_xml = 0; + int c, retval, verbose = 0; + + retval = 0; + conn_len = 4096; + + bzero(&islist, sizeof(islist)); + STAILQ_INIT(&islist.conn_list); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'v': + verbose = 1; + break; + case 'x': + dump_xml = 1; + break; + default: + break; + } + } + +retry: + conn_str = malloc(conn_len); + + bzero(&req, sizeof(req)); + req.type = CTL_ISCSI_LIST; + req.data.list.alloc_len = conn_len; + req.data.list.conn_xml = conn_str; + + if (ioctl(fd, CTL_ISCSI, &req) == -1) { + warn("%s: error issuing CTL_ISCSI ioctl", __func__); + retval = 1; + goto bailout; + } + + if (req.status == CTL_ISCSI_ERROR) { + warnx("%s: error returned from CTL_ISCSI ioctl:\n%s", + __func__, req.error_str); + } else if (req.status == CTL_ISCSI_LIST_NEED_MORE_SPACE) { + conn_len = conn_len << 1; + goto retry; + } + + if (dump_xml != 0) { + printf("%s", conn_str); + goto bailout; + } + + parser = XML_ParserCreate(NULL); + if (parser == NULL) { + warn("%s: Unable to create XML parser", __func__); + retval = 1; + goto bailout; + } + + XML_SetUserData(parser, &islist); + XML_SetElementHandler(parser, cctl_islist_start_element, + cctl_islist_end_element); + XML_SetCharacterDataHandler(parser, cctl_islist_char_handler); + + retval = XML_Parse(parser, conn_str, strlen(conn_str), 1); + XML_ParserFree(parser); + if (retval != 1) { + retval = 1; + goto bailout; + } + + if (verbose != 0) { + STAILQ_FOREACH(conn, &islist.conn_list, links) { + printf("Session ID: %d\n", conn->connection_id); + printf("Initiator name: %s\n", conn->initiator); + printf("Initiator addr: %s\n", conn->initiator_addr); + printf("Initiator alias: %s\n", conn->initiator_alias); + printf("Target name: %s\n", conn->target); + printf("Target alias: %s\n", conn->target_alias); + printf("Header digest: %s\n", conn->header_digest); + printf("Data digest: %s\n", conn->data_digest); + printf("DataSegmentLen: %s\n", conn->max_data_segment_length); + printf("ImmediateData: %s\n", conn->immediate_data ? "Yes" : "No"); + printf("iSER (RDMA): %s\n", conn->iser ? "Yes" : "No"); + printf("\n"); + } + } else { + printf("%4s %-16s %-36s %-36s\n", "ID", "Address", "Initiator name", + "Target name"); + STAILQ_FOREACH(conn, &islist.conn_list, links) { + printf("%4u %-16s %-36s %-36s\n", + conn->connection_id, conn->initiator_addr, conn->initiator, + conn->target); + } + } +bailout: + free(conn_str); + + return (retval); +} + +static int +cctl_islogout(int fd, int argc, char **argv, char *combinedopt) +{ + struct ctl_iscsi req; + int retval = 0, c; + int all = 0, connection_id = -1, nargs = 0; + char *initiator_name = NULL, *initiator_addr = NULL; + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'a': + all = 1; + nargs++; + break; + case 'h': + initiator_addr = strdup(optarg); + if (initiator_addr == NULL) + err(1, "%s: strdup", __func__); + nargs++; + break; + case 'c': + connection_id = strtoul(optarg, NULL, 0); + nargs++; + break; + case 'i': + initiator_name = strdup(optarg); + if (initiator_name == NULL) + err(1, "%s: strdup", __func__); + nargs++; + break; + default: + break; + } + } + + if (nargs == 0) + errx(1, "%s: either -a, -h, -c, or -i must be specified", + __func__); + if (nargs > 1) + errx(1, "%s: only one of -a, -h, -c, or -i may be specified", + __func__); + + bzero(&req, sizeof(req)); + req.type = CTL_ISCSI_LOGOUT; + req.data.logout.connection_id = connection_id; + if (initiator_addr != NULL) + strlcpy(req.data.logout.initiator_addr, + initiator_addr, sizeof(req.data.logout.initiator_addr)); + if (initiator_name != NULL) + strlcpy(req.data.logout.initiator_name, + initiator_name, sizeof(req.data.logout.initiator_name)); + if (all != 0) + req.data.logout.all = 1; + + if (ioctl(fd, CTL_ISCSI, &req) == -1) { + warn("%s: error issuing CTL_ISCSI ioctl", __func__); + retval = 1; + goto bailout; + } + + if (req.status != CTL_ISCSI_OK) { + warnx("%s: error returned from CTL iSCSI logout request:\n%s", + __func__, req.error_str); + retval = 1; + goto bailout; + } + + printf("iSCSI logout requests submitted\n"); + +bailout: + return (retval); +} + +static int +cctl_isterminate(int fd, int argc, char **argv, char *combinedopt) +{ + struct ctl_iscsi req; + int retval = 0, c; + int all = 0, connection_id = -1, nargs = 0; + char *initiator_name = NULL, *initiator_addr = NULL; + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'a': + all = 1; + nargs++; + break; + case 'h': + initiator_addr = strdup(optarg); + if (initiator_addr == NULL) + err(1, "%s: strdup", __func__); + nargs++; + break; + case 'c': + connection_id = strtoul(optarg, NULL, 0); + nargs++; + break; + case 'i': + initiator_name = strdup(optarg); + if (initiator_name == NULL) + err(1, "%s: strdup", __func__); + nargs++; + break; + default: + break; + } + } + + if (nargs == 0) + errx(1, "%s: either -a, -h, -c, or -i must be specified", + __func__); + if (nargs > 1) + errx(1, "%s: only one of -a, -h, -c, or -i may be specified", + __func__); + + bzero(&req, sizeof(req)); + req.type = CTL_ISCSI_TERMINATE; + req.data.terminate.connection_id = connection_id; + if (initiator_addr != NULL) + strlcpy(req.data.terminate.initiator_addr, + initiator_addr, sizeof(req.data.terminate.initiator_addr)); + if (initiator_name != NULL) + strlcpy(req.data.terminate.initiator_name, + initiator_name, sizeof(req.data.terminate.initiator_name)); + if (all != 0) + req.data.terminate.all = 1; + + if (ioctl(fd, CTL_ISCSI, &req) == -1) { + warn("%s: error issuing CTL_ISCSI ioctl", __func__); + retval = 1; + goto bailout; + } + + if (req.status != CTL_ISCSI_OK) { + warnx("%s: error returned from CTL iSCSI connection " + "termination request:\n%s", __func__, req.error_str); + retval = 1; + goto bailout; + } + + printf("iSCSI connections terminated\n"); + +bailout: + return (retval); +} /* * Name/value pair used for per-LUN attributes. @@ -3441,7 +3848,7 @@ cctl_start_element(void *user_data, const char *name, const char **attr) devlist = (struct cctl_devlist_data *)user_data; cur_lun = devlist->cur_lun; devlist->level++; - if ((u_int)devlist->level > (sizeof(devlist->cur_sb) / + if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0]))) errx(1, "%s: too many nesting levels, %zd max", __func__, sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0])); @@ -3713,6 +4120,9 @@ usage(int error) " [-s len fmt [args]] [-c] [-d delete_id]\n" " ctladm port <-l | -o <on|off> | [-w wwnn][-W wwpn]>\n" " [-p targ_port] [-t port_type] [-q] [-x]\n" +" ctladm islist [-v | -x]\n" +" ctladm islogout <-A | -a addr | -c connection-id | -n name>\n" +" ctladm isterminate <-A | -a addr | -c connection-id | -n name>\n" " ctladm dumpooa\n" " ctladm dumpstructs\n" " ctladm help\n" @@ -4093,6 +4503,15 @@ main(int argc, char **argv) case CTLADM_CMD_MODIFY: retval = cctl_modify_lun(fd, argc, argv, combinedopt); break; + case CTLADM_CMD_ISLIST: + retval = cctl_islist(fd, argc, argv, combinedopt); + break; + case CTLADM_CMD_ISLOGOUT: + retval = cctl_islogout(fd, argc, argv, combinedopt); + break; + case CTLADM_CMD_ISTERMINATE: + retval = cctl_isterminate(fd, argc, argv, combinedopt); + break; case CTLADM_CMD_HELP: default: usage(retval); diff --git a/usr.sbin/ctld/Makefile b/usr.sbin/ctld/Makefile new file mode 100644 index 0000000..e6c292d --- /dev/null +++ b/usr.sbin/ctld/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +PROG= ctld +SRCS= ctld.c discovery.c kernel.c keys.c log.c login.c parse.y pdu.c token.l y.tab.h +CFLAGS+= -I${.CURDIR} +CFLAGS+= -I${.CURDIR}/../../sys +CFLAGS+= -I${.CURDIR}/../../sys/cam/ctl +CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi +#CFLAGS+= -DICL_KERNEL_PROXY +MAN= ctld.8 ctl.conf.5 + +DPADD= ${LIBCAM} ${LIBSBUF} ${LIBBSDXML} ${LIBUTIL} +LDADD= -lbsdxml -lcam -lcrypto -lfl -lsbuf -lssl -lutil + +YFLAGS+= -v +CLEANFILES= y.tab.c y.tab.h y.output + +WARNS= 6 +NO_WMISSING_VARIABLE_DECLARATIONS= + +.include <bsd.prog.mk> diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5 new file mode 100644 index 0000000..fb8bc2a --- /dev/null +++ b/usr.sbin/ctld/ctl.conf.5 @@ -0,0 +1,244 @@ +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This software was developed by Edward Tomasz Napierala under sponsorship +.\" from the FreeBSD Foundation. +.\" +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd April 24, 2013 +.Dt CTL.CONF 5 +.Os +.Sh NAME +.Nm ctl.conf +.Nd CAM Target Layer / iSCSI target daemon configuration file +.Sh DESCRIPTION +The +.Nm +configuration file is used by the +.Xr ctld 8 +daemon. +Lines starting with +.Ql # +and empty lines are interpreted as comments. +The general syntax of the +.Nm +file is: +.Bd -literal -offset indent +pidfile <path> + +auth-group <name> { + chap <user> <secret> + ... +} + +portal-group <name> { + listen <address> + listen-iser <address> + discovery-auth-group <name> + ... +} + +target <name> { + auth-group <name> + portal-group <name> + lun <number> { + path <path> + } + ... +} +.Ed +.Ss global level +The following statements are available at the global level: +.Bl -tag -width indent +.It Ic auth-group Aq Ar name +Opens an auth-group section, defining an authentication group, +which can then be assigned to any number of targets. +.It Ic debug Aq Ar level +Specifies debug level. +The default is 0. +.It Ic maxproc Aq Ar number +Specifies limit for concurrently running child processes handling +incoming connections. +The default is 30. +Setting it to 0 disables the limit. +.It Ic pidfile Aq Ar path +Specifies path to pidfile. +The default is +.Pa /var/run/ctld.pid . +.It Ic portal-group Aq Ar name +Opens a portal-group section, defining a portal group, +which can then be assigned to any number of targets. +.It Ic target Aq Ar name +Opens a target configuration section. +.It Ic timeout Aq Ar seconds +Specifies timeout for login session, after which the connection +will be forcibly terminated. +The default is 60. +Setting it to 0 disables the timeout. +.El +.Ss auth-grup level +The following statements are available at the auth-group level: +.Bl -tag -width indent +.It Ic chap Ao Ar user Ac Aq Ar secret +Specifies CHAP authentication credentials. +.It Ic chap-mutual Ao Ar user Ac Ao Ar secret Ac Ao Ar mutualuser Ac Aq Ar mutualsecret +Specifies mutual CHAP authentication credentials. +Note that for any auth-group, configuration may contain either chap, +or chap-mutual entries; it's an error to mix them. +.El +.Ss portal-group level +The following statements are available at the portal-group level: +.Bl -tag -width indent +.It Ic discovery-auth-group Aq Ar name +Assigns previously defined authentication group to that portal group, +to be used for target discovery. +By default, the discovery will be denied. +A special auth-group, "no-authentication", may be used to allow for discovery +without authentication. +.It Ic listen Aq Ar address +Specifies IPv4 or IPv6 address and port to listen on for incoming connections. +.It Ic listen-iser Aq Ar address +Specifies IPv4 or IPv6 address and port to listen on for incoming connections +using iSER (iSCSI over RDMA) protocol. +.El +.Ss target level: +The following statements are available at the target level: +.Bl -tag -width indent +.It Ic alias Aq Ar text +Assigns human-readable description to that target. +There is no default. +.It Ic auth-group Aq Ar name +Assigns previously defined authentication group to that target. +There is no default; every target must use either auth-group, +or chap, or chap-mutual statements. +A special auth-group, "no-authentication", may be used to permit access +without authentication. +.It Ic chap Ao Ar user Ac Aq Ar secret +Specifies CHAP authentication credentials. +Note that targets must use either auth-group, or chap, +or chap-mutual clauses; it's a configuration error to mix them in one target. +.It Ic chap-mutual Ao Ar user Ac Ao Ar secret Ac Ao Ar mutualuser Ac Aq Ar mutualsecret +Specifies mutual CHAP authentication credentials. +Note that targets must use either auth-group, chap, +chap-mutual clauses; it's a configuration error to mix them in one target. +.It Ic portal-group Aq Ar name +Assigns previously defined portal group to that target. +Default portal group is "default", which makes the target available +on TCP port 3260 on all configured IPv4 and IPv6 addresses. +.It Ic lun Aq Ar number +Opens a lun configuration section, defining LUN exported by a target. +.El +.Ss lun level +The following statements are available at the lun level: +.Bl -tag -width indent +.It Ic backend Ao Ar block | Ar ramdisk Ac +Specifies the CTL backend to use for a given LUN. +Valid choices are +.Dq block +and +.Dq ramdisk ; +block is used for LUNs backed +by files in the filesystem; ramdisk is a bitsink device, used mostly for +testing. +The default backend is block. +.It Ic blocksize Aq Ar size +Specifies blocksize visible to the initiator. +The default blocksize is 512. +.It Ic device-id Aq Ar string +Specifies SCSI Device Identification string presented to the initiator. +.It Ic option Ao Ar name Ac Aq Ar value +Specifies CTL-specific options passed to the kernel. +.It Ic path Aq Ar path +Specifies path to file used to back the LUN. +.It Ic serial Aq Ar string +Specifies SCSI serial number presented to the initiator. +.It Ic size Aq Ar size +Specifies LUN size, in bytes. +.El +.Sh FILES +.Bl -tag -width ".Pa /etc/ctl.conf" -compact +.It Pa /etc/ctl.conf +The default location of the +.Xr ctld 8 +configuration file. +.El +.Sh EXAMPLES +.Bd -literal +pidfile /var/run/ctld.pid + +auth-group example2 { + chap-mutual "user" "secret" "mutualuser" "mutualsecret" + chap-mutual "user2" "secret2" "mutualuser" "mutualsecret" +} + +portal-group example2 { + discovery-auth-group no-authentication + listen 127.0.0.1 + listen 0.0.0.0:3261 + listen [::]:3261 + listen [fe80::be:ef] +} + +target iqn.2012-06.com.example:target0 { + alias "Testing target" + auth-group no-authentication + lun 0 { + path /dev/zvol/example_0 + blocksize 4096 + size 4G + } +} + +target iqn.2012-06.com.example:target3 { + chap chapuser chapsecret + lun 0 { + path /dev/zvol/example_3 + } +} + +target iqn.2012-06.com.example:target2 { + auth-group example2 + portal-group example2 + lun 0 { + path /dev/zvol/example2_0 + } + lun 1 { + path /dev/zvol/example2_1 + option foo bar + } +} +.Ed +.Sh SEE ALSO +.Xr ctl 4 , +.Xr ctladm 8 , +.Xr ctld 8 +.Sh AUTHORS +The +.Nm +configuration file functionality for +.Xr ctld 8 +was developed by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org +under sponsorship from the FreeBSD Foundation. diff --git a/usr.sbin/ctld/ctld.8 b/usr.sbin/ctld/ctld.8 new file mode 100644 index 0000000..60ffbfc --- /dev/null +++ b/usr.sbin/ctld/ctld.8 @@ -0,0 +1,113 @@ +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This software was developed by Edward Tomasz Napierala under sponsorship +.\" from the FreeBSD Foundation. +.\" +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 20, 2012 +.Dt CTLD 8 +.Os +.Sh NAME +.Nm ctld +.Nd CAM Target Layer / iSCSI target daemon +.Sh SYNOPSIS +.Nm +.Op Fl d +.Op Fl f Ar config-file +.Sh DESCRIPTION +The +.Nm +daemon is responsible for managing the CAM Target Layer configuration, +accepting incoming iSCSI connections, performing authentication and +passing connections to the kernel part of the native iSCSI target. +.Pp +Upon startup, the +.Nm +daemon parses the configuration file and exits, if it encounters any errors. +Then it compares the configuration with the kernel list of LUNs managed +by previously running +.Nm +instances, removes LUNs no longer existing in the configuration file, +and creates new LUNs as neccessary. +After that it listens for the incoming iSCSI connections, performs +authentication, and, if successful, passes the connections to the kernel part +of CTL iSCSI target, which handles it from that point. +.Pp +When it receives a SIGHUP signal, the +.Nm +reloads its configuration and applies the changes to the kernel. +Changes are applied in a way that avoids unneccessary disruptions; +for example removing one LUN does not affect other LUNs. +.Pp +When exiting gracefully, the +.Nm +daemon removes LUNs it managed and forcibly disconnects all the clients. +Otherwise - e.g. when killed with SIGKILL - LUNs stay configured +and clients remain connected. +.Pp +To perform administrative actions that apply to already connected +sessions, such as forcing termination, use +.Xr ctladm 8 . +.Pp +The following options are available: +.Bl -tag -width ".Fl P Ar pidfile" +.It Fl f Ar config-file +Specifies the name of the configuration file. +The default is +.Pa /etc/ctl.conf . +.It Fl d +Debug mode. +The server sends verbose debug output to standard error, and does not +put itself in the background. +The server will also not fork and will exit after processing one connection. +This option is only intended for debugging the target. +.El +.Sh FILES +.Bl -tag -width ".Pa /var/run/ctld.pid" -compact +.It Pa /etc/ctl.conf +The configuration file for +.Nm . +The file format and configuration options are described in +.Xr ctl.conf 5 . +.It Pa /var/run/ctld.pid +The default location of the +.Nm +PID file. +.El +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr ctl 4 , +.Xr ctl.conf 5 , +.Xr ctladm 8 +.Sh AUTHORS +The +.Nm +was developed by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org +under sponsorship from the FreeBSD Foundation. diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c new file mode 100644 index 0000000..a5472ab --- /dev/null +++ b/usr.sbin/ctld/ctld.c @@ -0,0 +1,1713 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <netinet/in.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <netdb.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "ctld.h" + +static volatile bool sighup_received = false; +static volatile bool sigterm_received = false; +static volatile bool sigalrm_received = false; + +static int nchildren = 0; + +static void +usage(void) +{ + + fprintf(stderr, "usage: ctld [-d][-f config-file]\n"); + exit(1); +} + +char * +checked_strdup(const char *s) +{ + char *c; + + c = strdup(s); + if (c == NULL) + log_err(1, "strdup"); + return (c); +} + +struct conf * +conf_new(void) +{ + struct conf *conf; + + conf = calloc(1, sizeof(*conf)); + if (conf == NULL) + log_err(1, "calloc"); + TAILQ_INIT(&conf->conf_targets); + TAILQ_INIT(&conf->conf_auth_groups); + TAILQ_INIT(&conf->conf_portal_groups); + + conf->conf_debug = 0; + conf->conf_timeout = 60; + conf->conf_maxproc = 30; + + return (conf); +} + +void +conf_delete(struct conf *conf) +{ + struct target *targ, *tmp; + struct auth_group *ag, *cagtmp; + struct portal_group *pg, *cpgtmp; + + assert(conf->conf_pidfh == NULL); + + TAILQ_FOREACH_SAFE(targ, &conf->conf_targets, t_next, tmp) + target_delete(targ); + TAILQ_FOREACH_SAFE(ag, &conf->conf_auth_groups, ag_next, cagtmp) + auth_group_delete(ag); + TAILQ_FOREACH_SAFE(pg, &conf->conf_portal_groups, pg_next, cpgtmp) + portal_group_delete(pg); + free(conf->conf_pidfile_path); + free(conf); +} + +static struct auth * +auth_new(struct auth_group *ag) +{ + struct auth *auth; + + auth = calloc(1, sizeof(*auth)); + if (auth == NULL) + log_err(1, "calloc"); + auth->a_auth_group = ag; + TAILQ_INSERT_TAIL(&ag->ag_auths, auth, a_next); + return (auth); +} + +static void +auth_delete(struct auth *auth) +{ + TAILQ_REMOVE(&auth->a_auth_group->ag_auths, auth, a_next); + + free(auth->a_user); + free(auth->a_secret); + free(auth->a_mutual_user); + free(auth->a_mutual_secret); + free(auth); +} + +const struct auth * +auth_find(struct auth_group *ag, const char *user) +{ + const struct auth *auth; + + TAILQ_FOREACH(auth, &ag->ag_auths, a_next) { + if (strcmp(auth->a_user, user) == 0) + return (auth); + } + + return (NULL); +} + +struct auth_group * +auth_group_new(struct conf *conf, const char *name) +{ + struct auth_group *ag; + + if (name != NULL) { + ag = auth_group_find(conf, name); + if (ag != NULL) { + log_warnx("duplicated auth-group \"%s\"", name); + return (NULL); + } + } + + ag = calloc(1, sizeof(*ag)); + if (ag == NULL) + log_err(1, "calloc"); + if (name != NULL) + ag->ag_name = checked_strdup(name); + TAILQ_INIT(&ag->ag_auths); + ag->ag_conf = conf; + TAILQ_INSERT_TAIL(&conf->conf_auth_groups, ag, ag_next); + + return (ag); +} + +void +auth_group_delete(struct auth_group *ag) +{ + struct auth *auth, *tmp; + + TAILQ_REMOVE(&ag->ag_conf->conf_auth_groups, ag, ag_next); + + TAILQ_FOREACH_SAFE(auth, &ag->ag_auths, a_next, tmp) + auth_delete(auth); + free(ag->ag_name); + free(ag); +} + +struct auth_group * +auth_group_find(struct conf *conf, const char *name) +{ + struct auth_group *ag; + + TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { + if (ag->ag_name != NULL && strcmp(ag->ag_name, name) == 0) + return (ag); + } + + return (NULL); +} + +static void +auth_check_secret_length(struct auth *auth) +{ + size_t len; + + len = strlen(auth->a_secret); + if (len > 16) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("secret for user \"%s\", auth-group \"%s\", " + "is too long; it should be at most 16 characters " + "long", auth->a_user, auth->a_auth_group->ag_name); + else + log_warnx("secret for user \"%s\", target \"%s\", " + "is too long; it should be at most 16 characters " + "long", auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + if (len < 12) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("secret for user \"%s\", auth-group \"%s\", " + "is too short; it should be at least 12 characters " + "long", auth->a_user, + auth->a_auth_group->ag_name); + else + log_warnx("secret for user \"%s\", target \"%s\", " + "is too short; it should be at least 16 characters " + "long", auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + + if (auth->a_mutual_secret != NULL) { + len = strlen(auth->a_secret); + if (len > 16) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("mutual secret for user \"%s\", " + "auth-group \"%s\", is too long; it should " + "be at most 16 characters long", + auth->a_user, auth->a_auth_group->ag_name); + else + log_warnx("mutual secret for user \"%s\", " + "target \"%s\", is too long; it should " + "be at most 16 characters long", + auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + if (len < 12) { + if (auth->a_auth_group->ag_name != NULL) + log_warnx("mutual secret for user \"%s\", " + "auth-group \"%s\", is too short; it " + "should be at least 12 characters long", + auth->a_user, auth->a_auth_group->ag_name); + else + log_warnx("mutual secret for user \"%s\", " + "target \"%s\", is too short; it should be " + "at least 16 characters long", + auth->a_user, + auth->a_auth_group->ag_target->t_iqn); + } + } +} + +const struct auth * +auth_new_chap(struct auth_group *ag, const char *user, + const char *secret) +{ + struct auth *auth; + + if (ag->ag_type == AG_TYPE_UNKNOWN) + ag->ag_type = AG_TYPE_CHAP; + if (ag->ag_type != AG_TYPE_CHAP) { + if (ag->ag_name != NULL) + log_warnx("cannot mix \"chap\" authentication with " + "other types for auth-group \"%s\"", ag->ag_name); + else + log_warnx("cannot mix \"chap\" authentication with " + "other types for target \"%s\"", + ag->ag_target->t_iqn); + return (NULL); + } + + auth = auth_new(ag); + auth->a_user = checked_strdup(user); + auth->a_secret = checked_strdup(secret); + + auth_check_secret_length(auth); + + return (auth); +} + +const struct auth * +auth_new_chap_mutual(struct auth_group *ag, const char *user, + const char *secret, const char *user2, const char *secret2) +{ + struct auth *auth; + + if (ag->ag_type == AG_TYPE_UNKNOWN) + ag->ag_type = AG_TYPE_CHAP_MUTUAL; + if (ag->ag_type != AG_TYPE_CHAP_MUTUAL) { + if (ag->ag_name != NULL) + log_warnx("cannot mix \"chap-mutual\" authentication " + "with other types for auth-group \"%s\"", + ag->ag_name); + else + log_warnx("cannot mix \"chap-mutual\" authentication " + "with other types for target \"%s\"", + ag->ag_target->t_iqn); + return (NULL); + } + + auth = auth_new(ag); + auth->a_user = checked_strdup(user); + auth->a_secret = checked_strdup(secret); + auth->a_mutual_user = checked_strdup(user2); + auth->a_mutual_secret = checked_strdup(secret2); + + auth_check_secret_length(auth); + + return (auth); +} + +static struct portal * +portal_new(struct portal_group *pg) +{ + struct portal *portal; + + portal = calloc(1, sizeof(*portal)); + if (portal == NULL) + log_err(1, "calloc"); + TAILQ_INIT(&portal->p_targets); + portal->p_portal_group = pg; + TAILQ_INSERT_TAIL(&pg->pg_portals, portal, p_next); + return (portal); +} + +static void +portal_delete(struct portal *portal) +{ + TAILQ_REMOVE(&portal->p_portal_group->pg_portals, portal, p_next); + freeaddrinfo(portal->p_ai); + free(portal->p_listen); + free(portal); +} + +struct portal_group * +portal_group_new(struct conf *conf, const char *name) +{ + struct portal_group *pg; + + pg = portal_group_find(conf, name); + if (pg != NULL) { + log_warnx("duplicated portal-group \"%s\"", name); + return (NULL); + } + + pg = calloc(1, sizeof(*pg)); + if (pg == NULL) + log_err(1, "calloc"); + pg->pg_name = checked_strdup(name); + TAILQ_INIT(&pg->pg_portals); + pg->pg_conf = conf; + conf->conf_last_portal_group_tag++; + pg->pg_tag = conf->conf_last_portal_group_tag; + TAILQ_INSERT_TAIL(&conf->conf_portal_groups, pg, pg_next); + + return (pg); +} + +void +portal_group_delete(struct portal_group *pg) +{ + struct portal *portal, *tmp; + + TAILQ_REMOVE(&pg->pg_conf->conf_portal_groups, pg, pg_next); + + TAILQ_FOREACH_SAFE(portal, &pg->pg_portals, p_next, tmp) + portal_delete(portal); + free(pg->pg_name); + free(pg); +} + +struct portal_group * +portal_group_find(struct conf *conf, const char *name) +{ + struct portal_group *pg; + + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + if (strcmp(pg->pg_name, name) == 0) + return (pg); + } + + return (NULL); +} + +int +portal_group_add_listen(struct portal_group *pg, const char *value, bool iser) +{ + struct addrinfo hints; + struct portal *portal; + char *addr, *ch, *arg; + const char *port; + int error, colons = 0; + +#ifndef ICL_KERNEL_PROXY + if (iser) { + log_warnx("ctld(8) compiled without ICL_KERNEL_PROXY " + "does not support iSER protocol"); + return (-1); + } +#endif + + portal = portal_new(pg); + portal->p_listen = checked_strdup(value); + portal->p_iser = iser; + + arg = portal->p_listen; + if (arg[0] == '\0') { + log_warnx("empty listen address"); + free(portal->p_listen); + free(portal); + return (1); + } + if (arg[0] == '[') { + /* + * IPv6 address in square brackets, perhaps with port. + */ + arg++; + addr = strsep(&arg, "]"); + if (arg == NULL) { + log_warnx("invalid listen address %s", + portal->p_listen); + free(portal->p_listen); + free(portal); + return (1); + } + if (arg[0] == '\0') { + port = "3260"; + } else if (arg[0] == ':') { + port = arg + 1; + } else { + log_warnx("invalid listen address %s", + portal->p_listen); + free(portal->p_listen); + free(portal); + return (1); + } + } else { + /* + * Either IPv6 address without brackets - and without + * a port - or IPv4 address. Just count the colons. + */ + for (ch = arg; *ch != '\0'; ch++) { + if (*ch == ':') + colons++; + } + if (colons > 1) { + addr = arg; + port = "3260"; + } else { + addr = strsep(&arg, ":"); + if (arg == NULL) + port = "3260"; + else + port = arg; + } + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + error = getaddrinfo(addr, port, &hints, &portal->p_ai); + if (error != 0) { + log_warnx("getaddrinfo for %s failed: %s", + portal->p_listen, gai_strerror(error)); + free(portal->p_listen); + free(portal); + return (1); + } + + /* + * XXX: getaddrinfo(3) may return multiple addresses; we should turn + * those into multiple portals. + */ + + return (0); +} + +static bool +valid_hex(const char ch) +{ + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'A': + case 'b': + case 'B': + case 'c': + case 'C': + case 'd': + case 'D': + case 'e': + case 'E': + case 'f': + case 'F': + return (true); + default: + return (false); + } +} + +bool +valid_iscsi_name(const char *name) +{ + int i; + + if (strlen(name) >= MAX_NAME_LEN) { + log_warnx("overlong name for target \"%s\"; max length allowed " + "by iSCSI specification is %d characters", + name, MAX_NAME_LEN); + return (false); + } + + /* + * In the cases below, we don't return an error, just in case the admin + * was right, and we're wrong. + */ + if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) { + for (i = strlen("iqn."); name[i] != '\0'; i++) { + /* + * XXX: We should verify UTF-8 normalisation, as defined + * by 3.2.6.2: iSCSI Name Encoding. + */ + if (isalnum(name[i])) + continue; + if (name[i] == '-' || name[i] == '.' || name[i] == ':') + continue; + log_warnx("invalid character \"%c\" in target name " + "\"%s\"; allowed characters are letters, digits, " + "'-', '.', and ':'", name[i], name); + break; + } + /* + * XXX: Check more stuff: valid date and a valid reversed domain. + */ + } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) { + if (strlen(name) != strlen("eui.") + 16) + log_warnx("invalid target name \"%s\"; the \"eui.\" " + "should be followed by exactly 16 hexadecimal " + "digits", name); + for (i = strlen("eui."); name[i] != '\0'; i++) { + if (!valid_hex(name[i])) { + log_warnx("invalid character \"%c\" in target " + "name \"%s\"; allowed characters are 1-9 " + "and A-F", name[i], name); + break; + } + } + } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) { + if (strlen(name) > strlen("naa.") + 32) + log_warnx("invalid target name \"%s\"; the \"naa.\" " + "should be followed by at most 32 hexadecimal " + "digits", name); + for (i = strlen("naa."); name[i] != '\0'; i++) { + if (!valid_hex(name[i])) { + log_warnx("invalid character \"%c\" in target " + "name \"%s\"; allowed characters are 1-9 " + "and A-F", name[i], name); + break; + } + } + } else { + log_warnx("invalid target name \"%s\"; should start with " + "either \".iqn\", \"eui.\", or \"naa.\"", + name); + } + return (true); +} + +struct target * +target_new(struct conf *conf, const char *iqn) +{ + struct target *targ; + int i, len; + + targ = target_find(conf, iqn); + if (targ != NULL) { + log_warnx("duplicated target \"%s\"", iqn); + return (NULL); + } + if (valid_iscsi_name(iqn) == false) { + log_warnx("target name \"%s\" is invalid", iqn); + return (NULL); + } + targ = calloc(1, sizeof(*targ)); + if (targ == NULL) + log_err(1, "calloc"); + targ->t_iqn = checked_strdup(iqn); + + /* + * RFC 3722 requires us to normalize the name to lowercase. + */ + len = strlen(iqn); + for (i = 0; i < len; i++) + targ->t_iqn[i] = tolower(targ->t_iqn[i]); + + TAILQ_INIT(&targ->t_luns); + targ->t_conf = conf; + TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); + + return (targ); +} + +void +target_delete(struct target *targ) +{ + struct lun *lun, *tmp; + + TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next); + + TAILQ_FOREACH_SAFE(lun, &targ->t_luns, l_next, tmp) + lun_delete(lun); + free(targ->t_iqn); + free(targ); +} + +struct target * +target_find(struct conf *conf, const char *iqn) +{ + struct target *targ; + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (strcasecmp(targ->t_iqn, iqn) == 0) + return (targ); + } + + return (NULL); +} + +struct lun * +lun_new(struct target *targ, int lun_id) +{ + struct lun *lun; + + lun = lun_find(targ, lun_id); + if (lun != NULL) { + log_warnx("duplicated lun %d for target \"%s\"", + lun_id, targ->t_iqn); + return (NULL); + } + + lun = calloc(1, sizeof(*lun)); + if (lun == NULL) + log_err(1, "calloc"); + lun->l_lun = lun_id; + TAILQ_INIT(&lun->l_options); + lun->l_target = targ; + TAILQ_INSERT_TAIL(&targ->t_luns, lun, l_next); + + return (lun); +} + +void +lun_delete(struct lun *lun) +{ + struct lun_option *lo, *tmp; + + TAILQ_REMOVE(&lun->l_target->t_luns, lun, l_next); + + TAILQ_FOREACH_SAFE(lo, &lun->l_options, lo_next, tmp) + lun_option_delete(lo); + free(lun->l_backend); + free(lun->l_device_id); + free(lun->l_path); + free(lun->l_serial); + free(lun); +} + +struct lun * +lun_find(struct target *targ, int lun_id) +{ + struct lun *lun; + + TAILQ_FOREACH(lun, &targ->t_luns, l_next) { + if (lun->l_lun == lun_id) + return (lun); + } + + return (NULL); +} + +void +lun_set_backend(struct lun *lun, const char *value) +{ + free(lun->l_backend); + lun->l_backend = checked_strdup(value); +} + +void +lun_set_blocksize(struct lun *lun, size_t value) +{ + + lun->l_blocksize = value; +} + +void +lun_set_device_id(struct lun *lun, const char *value) +{ + free(lun->l_device_id); + lun->l_device_id = checked_strdup(value); +} + +void +lun_set_path(struct lun *lun, const char *value) +{ + free(lun->l_path); + lun->l_path = checked_strdup(value); +} + +void +lun_set_serial(struct lun *lun, const char *value) +{ + free(lun->l_serial); + lun->l_serial = checked_strdup(value); +} + +void +lun_set_size(struct lun *lun, size_t value) +{ + + lun->l_size = value; +} + +void +lun_set_ctl_lun(struct lun *lun, uint32_t value) +{ + + lun->l_ctl_lun = value; +} + +struct lun_option * +lun_option_new(struct lun *lun, const char *name, const char *value) +{ + struct lun_option *lo; + + lo = lun_option_find(lun, name); + if (lo != NULL) { + log_warnx("duplicated lun option %s for lun %d, target \"%s\"", + name, lun->l_lun, lun->l_target->t_iqn); + return (NULL); + } + + lo = calloc(1, sizeof(*lo)); + if (lo == NULL) + log_err(1, "calloc"); + lo->lo_name = checked_strdup(name); + lo->lo_value = checked_strdup(value); + lo->lo_lun = lun; + TAILQ_INSERT_TAIL(&lun->l_options, lo, lo_next); + + return (lo); +} + +void +lun_option_delete(struct lun_option *lo) +{ + + TAILQ_REMOVE(&lo->lo_lun->l_options, lo, lo_next); + + free(lo->lo_name); + free(lo->lo_value); + free(lo); +} + +struct lun_option * +lun_option_find(struct lun *lun, const char *name) +{ + struct lun_option *lo; + + TAILQ_FOREACH(lo, &lun->l_options, lo_next) { + if (strcmp(lo->lo_name, name) == 0) + return (lo); + } + + return (NULL); +} + +void +lun_option_set(struct lun_option *lo, const char *value) +{ + + free(lo->lo_value); + lo->lo_value = checked_strdup(value); +} + +static struct connection * +connection_new(struct portal *portal, int fd, const char *host) +{ + struct connection *conn; + + conn = calloc(1, sizeof(*conn)); + if (conn == NULL) + log_err(1, "calloc"); + conn->conn_portal = portal; + conn->conn_socket = fd; + conn->conn_initiator_addr = checked_strdup(host); + + /* + * Default values, from RFC 3720, section 12. + */ + conn->conn_max_data_segment_length = 8192; + conn->conn_max_burst_length = 262144; + conn->conn_immediate_data = true; + + return (conn); +} + +#if 0 +static void +conf_print(struct conf *conf) +{ + struct auth_group *ag; + struct auth *auth; + struct portal_group *pg; + struct portal *portal; + struct target *targ; + struct lun *lun; + struct lun_option *lo; + + TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { + fprintf(stderr, "auth-group %s {\n", ag->ag_name); + TAILQ_FOREACH(auth, &ag->ag_auths, a_next) + fprintf(stderr, "\t chap-mutual %s %s %s %s\n", + auth->a_user, auth->a_secret, + auth->a_mutual_user, auth->a_mutual_secret); + fprintf(stderr, "}\n"); + } + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + fprintf(stderr, "portal-group %s {\n", pg->pg_name); + TAILQ_FOREACH(portal, &pg->pg_portals, p_next) + fprintf(stderr, "\t listen %s\n", portal->p_listen); + fprintf(stderr, "}\n"); + } + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + fprintf(stderr, "target %s {\n", targ->t_iqn); + if (targ->t_alias != NULL) + fprintf(stderr, "\t alias %s\n", targ->t_alias); + TAILQ_FOREACH(lun, &targ->t_luns, l_next) { + fprintf(stderr, "\tlun %d {\n", lun->l_lun); + fprintf(stderr, "\t\tpath %s\n", lun->l_path); + TAILQ_FOREACH(lo, &lun->l_options, lo_next) + fprintf(stderr, "\t\toption %s %s\n", + lo->lo_name, lo->lo_value); + fprintf(stderr, "\t}\n"); + } + fprintf(stderr, "}\n"); + } +} +#endif + +int +conf_verify(struct conf *conf) +{ + struct auth_group *ag; + struct portal_group *pg; + struct target *targ; + struct lun *lun, *lun2; + bool found_lun0; + + if (conf->conf_pidfile_path == NULL) + conf->conf_pidfile_path = checked_strdup(DEFAULT_PIDFILE); + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (targ->t_auth_group == NULL) { + log_warnx("missing authentication for target \"%s\"; " + "must specify either \"auth-group\", \"chap\", " + "or \"chap-mutual\"", targ->t_iqn); + return (1); + } + if (targ->t_portal_group == NULL) { + targ->t_portal_group = portal_group_find(conf, + "default"); + assert(targ->t_portal_group != NULL); + } + found_lun0 = false; + TAILQ_FOREACH(lun, &targ->t_luns, l_next) { + if (lun->l_lun == 0) + found_lun0 = true; + if (lun->l_backend == NULL) + lun_set_backend(lun, "block"); + if (strcmp(lun->l_backend, "block") == 0 && + lun->l_path == NULL) { + log_warnx("missing path for lun %d, " + "target \"%s\"", lun->l_lun, targ->t_iqn); + return (1); + } + if (strcmp(lun->l_backend, "ramdisk") == 0) { + if (lun->l_size == 0) { + log_warnx("missing size for " + "ramdisk-backed lun %d, " + "target \"%s\"", + lun->l_lun, targ->t_iqn); + return (1); + } + if (lun->l_path != NULL) { + log_warnx("path must not be specified " + "for ramdisk-backed lun %d, " + "target \"%s\"", + lun->l_lun, targ->t_iqn); + return (1); + } + } + if (lun->l_lun < 0 || lun->l_lun > 255) { + log_warnx("invalid lun number for lun %d, " + "target \"%s\"; must be between 0 and 255", + lun->l_lun, targ->t_iqn); + return (1); + } +#if 1 /* Should we? */ + TAILQ_FOREACH(lun2, &targ->t_luns, l_next) { + if (lun == lun2) + continue; + if (lun->l_path != NULL && + lun2->l_path != NULL && + strcmp(lun->l_path, lun2->l_path) == 0) + log_debugx("WARNING: duplicate path " + "for lun %d, target \"%s\"", + lun->l_lun, targ->t_iqn); + } +#endif + if (lun->l_blocksize == 0) { + lun_set_blocksize(lun, DEFAULT_BLOCKSIZE); + } else if (lun->l_blocksize <= 0) { + log_warnx("invalid blocksize for lun %d, " + "target \"%s\"; must be larger than 0", + lun->l_lun, targ->t_iqn); + return (1); + } + if (lun->l_size != 0 && + lun->l_size % lun->l_blocksize != 0) { + log_warnx("invalid size for lun %d, target " + "\"%s\"; must be multiple of blocksize", + lun->l_lun, targ->t_iqn); + return (1); + } + } + if (!found_lun0) { + log_warnx("mandatory LUN 0 not configured " + "for target \"%s\"", targ->t_iqn); + return (1); + } + } + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + assert(pg->pg_name != NULL); + if (pg->pg_discovery_auth_group == NULL) { + pg->pg_discovery_auth_group = + auth_group_find(conf, "no-access"); + assert(pg->pg_discovery_auth_group != NULL); + } + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (targ->t_portal_group == pg) + break; + } + if (targ == NULL) { + if (strcmp(pg->pg_name, "default") != 0) + log_warnx("portal-group \"%s\" not assigned " + "to any target", pg->pg_name); + pg->pg_unassigned = true; + } else + pg->pg_unassigned = false; + } + TAILQ_FOREACH(ag, &conf->conf_auth_groups, ag_next) { + if (ag->ag_name == NULL) + assert(ag->ag_target != NULL); + else + assert(ag->ag_target == NULL); + + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { + if (targ->t_auth_group == ag) + break; + } + if (targ == NULL && ag->ag_name != NULL && + strcmp(ag->ag_name, "no-authentication") != 0 && + strcmp(ag->ag_name, "no-access") != 0) { + log_warnx("auth-group \"%s\" not assigned " + "to any target", ag->ag_name); + } + } + + return (0); +} + +static int +conf_apply(struct conf *oldconf, struct conf *newconf) +{ + struct target *oldtarg, *newtarg, *tmptarg; + struct lun *oldlun, *newlun, *tmplun; + struct portal_group *oldpg, *newpg; + struct portal *oldp, *newp; + pid_t otherpid; + int changed, cumulated_error = 0, error; +#ifndef ICL_KERNEL_PROXY + int one = 1; +#endif + + if (oldconf->conf_debug != newconf->conf_debug) { + log_debugx("changing debug level to %d", newconf->conf_debug); + log_init(newconf->conf_debug); + } + + if (oldconf->conf_pidfh != NULL) { + assert(oldconf->conf_pidfile_path != NULL); + if (newconf->conf_pidfile_path != NULL && + strcmp(oldconf->conf_pidfile_path, + newconf->conf_pidfile_path) == 0) { + newconf->conf_pidfh = oldconf->conf_pidfh; + oldconf->conf_pidfh = NULL; + } else { + log_debugx("removing pidfile %s", + oldconf->conf_pidfile_path); + pidfile_remove(oldconf->conf_pidfh); + oldconf->conf_pidfh = NULL; + } + } + + if (newconf->conf_pidfh == NULL && newconf->conf_pidfile_path != NULL) { + log_debugx("opening pidfile %s", newconf->conf_pidfile_path); + newconf->conf_pidfh = + pidfile_open(newconf->conf_pidfile_path, 0600, &otherpid); + if (newconf->conf_pidfh == NULL) { + if (errno == EEXIST) + log_errx(1, "daemon already running, pid: %jd.", + (intmax_t)otherpid); + log_err(1, "cannot open or create pidfile \"%s\"", + newconf->conf_pidfile_path); + } + } + + TAILQ_FOREACH_SAFE(oldtarg, &oldconf->conf_targets, t_next, tmptarg) { + /* + * First, remove any targets present in the old configuration + * and missing in the new one. + */ + newtarg = target_find(newconf, oldtarg->t_iqn); + if (newtarg == NULL) { + TAILQ_FOREACH_SAFE(oldlun, &oldtarg->t_luns, l_next, + tmplun) { + log_debugx("target %s not found in the " + "configuration file; removing its lun %d, " + "backed by CTL lun %d", + oldtarg->t_iqn, oldlun->l_lun, + oldlun->l_ctl_lun); + error = kernel_lun_remove(oldlun); + if (error != 0) { + log_warnx("failed to remove lun %d, " + "target %s, CTL lun %d", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + cumulated_error++; + } + lun_delete(oldlun); + } + target_delete(oldtarg); + continue; + } + + /* + * Second, remove any LUNs present in the old target + * and missing in the new one. + */ + TAILQ_FOREACH_SAFE(oldlun, &oldtarg->t_luns, l_next, tmplun) { + newlun = lun_find(newtarg, oldlun->l_lun); + if (newlun == NULL) { + log_debugx("lun %d, target %s, CTL lun %d " + "not found in the configuration file; " + "removing", oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + error = kernel_lun_remove(oldlun); + if (error != 0) { + log_warnx("failed to remove lun %d, " + "target %s, CTL lun %d", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + cumulated_error++; + } + lun_delete(oldlun); + continue; + } + + /* + * Also remove the LUNs changed by more than size. + */ + changed = 0; + assert(oldlun->l_backend != NULL); + assert(newlun->l_backend != NULL); + if (strcmp(newlun->l_backend, oldlun->l_backend) != 0) { + log_debugx("backend for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (oldlun->l_blocksize != newlun->l_blocksize) { + log_debugx("blocksize for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (newlun->l_device_id != NULL && + (oldlun->l_device_id == NULL || + strcmp(oldlun->l_device_id, newlun->l_device_id) != + 0)) { + log_debugx("device-id for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (newlun->l_path != NULL && + (oldlun->l_path == NULL || + strcmp(oldlun->l_path, newlun->l_path) != 0)) { + log_debugx("path for lun %d, target %s, " + "CTL lun %d, changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (newlun->l_serial != NULL && + (oldlun->l_serial == NULL || + strcmp(oldlun->l_serial, newlun->l_serial) != 0)) { + log_debugx("serial for lun %d, target %s, " + "CTL lun %d changed; removing", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + changed = 1; + } + if (changed) { + error = kernel_lun_remove(oldlun); + if (error != 0) { + log_warnx("failed to remove lun %d, " + "target %s, CTL lun %d", + oldlun->l_lun, oldtarg->t_iqn, + oldlun->l_ctl_lun); + cumulated_error++; + } + lun_delete(oldlun); + continue; + } + + lun_set_ctl_lun(newlun, oldlun->l_ctl_lun); + } + } + + /* + * Now add new targets or modify existing ones. + */ + TAILQ_FOREACH(newtarg, &newconf->conf_targets, t_next) { + oldtarg = target_find(oldconf, newtarg->t_iqn); + + TAILQ_FOREACH(newlun, &newtarg->t_luns, l_next) { + if (oldtarg != NULL) { + oldlun = lun_find(oldtarg, newlun->l_lun); + if (oldlun != NULL) { + if (newlun->l_size != oldlun->l_size) { + log_debugx("resizing lun %d, " + "target %s, CTL lun %d", + newlun->l_lun, + newtarg->t_iqn, + newlun->l_ctl_lun); + error = + kernel_lun_resize(newlun); + if (error != 0) { + log_warnx("failed to " + "resize lun %d, " + "target %s, " + "CTL lun %d", + newlun->l_lun, + newtarg->t_iqn, + newlun->l_lun); + cumulated_error++; + } + } + continue; + } + } + log_debugx("adding lun %d, target %s", + newlun->l_lun, newtarg->t_iqn); + error = kernel_lun_add(newlun); + if (error != 0) { + log_warnx("failed to add lun %d, target %s", + newlun->l_lun, newtarg->t_iqn); + cumulated_error++; + } + } + } + + /* + * Go through the new portals, opening the sockets as neccessary. + */ + TAILQ_FOREACH(newpg, &newconf->conf_portal_groups, pg_next) { + if (newpg->pg_unassigned) { + log_debugx("not listening on portal-group \"%s\", " + "not assigned to any target", + newpg->pg_name); + continue; + } + TAILQ_FOREACH(newp, &newpg->pg_portals, p_next) { + /* + * Try to find already open portal and reuse + * the listening socket. We don't care about + * what portal or portal group that was, what + * matters is the listening address. + */ + TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, + pg_next) { + TAILQ_FOREACH(oldp, &oldpg->pg_portals, + p_next) { + if (strcmp(newp->p_listen, + oldp->p_listen) == 0 && + oldp->p_socket > 0) { + newp->p_socket = + oldp->p_socket; + oldp->p_socket = 0; + break; + } + } + } + if (newp->p_socket > 0) { + /* + * We're done with this portal. + */ + continue; + } + +#ifdef ICL_KERNEL_PROXY + log_debugx("listening on %s, portal-group \"%s\" using ICL proxy", + newp->p_listen, newpg->pg_name); + kernel_listen(newp->p_ai, newp->p_iser); +#else + assert(newp->p_iser == false); + + log_debugx("listening on %s, portal-group \"%s\"", + newp->p_listen, newpg->pg_name); + newp->p_socket = socket(newp->p_ai->ai_family, + newp->p_ai->ai_socktype, + newp->p_ai->ai_protocol); + if (newp->p_socket < 0) { + log_warn("socket(2) failed for %s", + newp->p_listen); + cumulated_error++; + continue; + } + error = setsockopt(newp->p_socket, SOL_SOCKET, + SO_REUSEADDR, &one, sizeof(one)); + if (error != 0) { + log_warn("setsockopt(SO_REUSEADDR) failed " + "for %s", newp->p_listen); + close(newp->p_socket); + newp->p_socket = 0; + cumulated_error++; + continue; + } + error = bind(newp->p_socket, newp->p_ai->ai_addr, + newp->p_ai->ai_addrlen); + if (error != 0) { + log_warn("bind(2) failed for %s", + newp->p_listen); + close(newp->p_socket); + newp->p_socket = 0; + cumulated_error++; + continue; + } + error = listen(newp->p_socket, -1); + if (error != 0) { + log_warn("listen(2) failed for %s", + newp->p_listen); + close(newp->p_socket); + newp->p_socket = 0; + cumulated_error++; + continue; + } +#endif /* !ICL_KERNEL_PROXY */ + } + } + + /* + * Go through the no longer used sockets, closing them. + */ + TAILQ_FOREACH(oldpg, &oldconf->conf_portal_groups, pg_next) { + TAILQ_FOREACH(oldp, &oldpg->pg_portals, p_next) { + if (oldp->p_socket <= 0) + continue; + log_debugx("closing socket for %s, portal-group \"%s\"", + oldp->p_listen, oldpg->pg_name); + close(oldp->p_socket); + oldp->p_socket = 0; + } + } + + return (cumulated_error); +} + +bool +timed_out(void) +{ + + return (sigalrm_received); +} + +static void +sigalrm_handler(int dummy __unused) +{ + /* + * It would be easiest to just log an error and exit. We can't + * do this, though, because log_errx() is not signal safe, since + * it calls syslog(3). Instead, set a flag checked by pdu_send() + * and pdu_receive(), to call log_errx() there. Should they fail + * to notice, we'll exit here one second later. + */ + if (sigalrm_received) { + /* + * Oh well. Just give up and quit. + */ + _exit(2); + } + + sigalrm_received = true; +} + +static void +set_timeout(const struct conf *conf) +{ + struct sigaction sa; + struct itimerval itv; + int error; + + if (conf->conf_timeout <= 0) { + log_debugx("session timeout disabled"); + return; + } + + bzero(&sa, sizeof(sa)); + sa.sa_handler = sigalrm_handler; + sigfillset(&sa.sa_mask); + error = sigaction(SIGALRM, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); + + /* + * First SIGALRM will arive after conf_timeout seconds. + * If we do nothing, another one will arrive a second later. + */ + bzero(&itv, sizeof(itv)); + itv.it_interval.tv_sec = 1; + itv.it_value.tv_sec = conf->conf_timeout; + + log_debugx("setting session timeout to %d seconds", + conf->conf_timeout); + error = setitimer(ITIMER_REAL, &itv, NULL); + if (error != 0) + log_err(1, "setitimer"); +} + +static int +wait_for_children(bool block) +{ + pid_t pid; + int status; + int num = 0; + + for (;;) { + /* + * If "block" is true, wait for at least one process. + */ + if (block && num == 0) + pid = wait4(-1, &status, 0, NULL); + else + pid = wait4(-1, &status, WNOHANG, NULL); + if (pid <= 0) + break; + if (WIFSIGNALED(status)) { + log_warnx("child process %d terminated with signal %d", + pid, WTERMSIG(status)); + } else if (WEXITSTATUS(status) != 0) { + log_warnx("child process %d terminated with exit status %d", + pid, WEXITSTATUS(status)); + } else { + log_debugx("child process %d terminated gracefully", pid); + } + num++; + } + + return (num); +} + +static void +handle_connection(struct portal *portal, int fd, bool dont_fork) +{ + struct connection *conn; +#ifndef ICL_KERNEL_PROXY + struct sockaddr_storage ss; + socklen_t sslen = sizeof(ss); + int error; +#endif + pid_t pid; + char host[NI_MAXHOST + 1]; + struct conf *conf; + + conf = portal->p_portal_group->pg_conf; + + if (dont_fork) { + log_debugx("incoming connection; not forking due to -d flag"); + } else { + nchildren -= wait_for_children(false); + assert(nchildren >= 0); + + while (conf->conf_maxproc > 0 && nchildren >= conf->conf_maxproc) { + log_debugx("maxproc limit of %d child processes hit; " + "waiting for child process to exit", conf->conf_maxproc); + nchildren -= wait_for_children(true); + assert(nchildren >= 0); + } + log_debugx("incoming connection; forking child process #%d", + nchildren); + nchildren++; + pid = fork(); + if (pid < 0) + log_err(1, "fork"); + if (pid > 0) { + close(fd); + return; + } + } + pidfile_close(conf->conf_pidfh); + +#ifdef ICL_KERNEL_PROXY + /* + * XXX + */ + log_set_peer_addr("XXX"); +#else + error = getpeername(fd, (struct sockaddr *)&ss, &sslen); + if (error != 0) + log_err(1, "getpeername"); + error = getnameinfo((struct sockaddr *)&ss, sslen, + host, sizeof(host), NULL, 0, NI_NUMERICHOST); + if (error != 0) + log_errx(1, "getaddrinfo: %s", gai_strerror(error)); + + log_debugx("accepted connection from %s; portal group \"%s\"", + host, portal->p_portal_group->pg_name); + log_set_peer_addr(host); + setproctitle("%s", host); +#endif + + conn = connection_new(portal, fd, host); + set_timeout(conf); + kernel_capsicate(); + login(conn); + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + kernel_handoff(conn); + log_debugx("connection handed off to the kernel"); + } else { + assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); + discovery(conn); + } + log_debugx("nothing more to do; exiting"); + exit(0); +} + +#ifndef ICL_KERNEL_PROXY +static int +fd_add(int fd, fd_set *fdset, int nfds) +{ + + /* + * Skip sockets which we failed to bind. + */ + if (fd <= 0) + return (nfds); + + FD_SET(fd, fdset); + if (fd > nfds) + nfds = fd; + return (nfds); +} +#endif + +static void +main_loop(struct conf *conf, bool dont_fork) +{ + struct portal_group *pg; + struct portal *portal; +#ifdef ICL_KERNEL_PROXY + int connection_id; +#else + fd_set fdset; + int error, nfds, client_fd; +#endif + + pidfile_write(conf->conf_pidfh); + + for (;;) { + if (sighup_received || sigterm_received) + return; + +#ifdef ICL_KERNEL_PROXY + connection_id = kernel_accept(); + if (connection_id == 0) + continue; + + /* + * XXX: This is obviously temporary. + */ + pg = TAILQ_FIRST(&conf->conf_portal_groups); + portal = TAILQ_FIRST(&pg->pg_portals); + + handle_connection(portal, connection_id, dont_fork); +#else + FD_ZERO(&fdset); + nfds = 0; + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + TAILQ_FOREACH(portal, &pg->pg_portals, p_next) + nfds = fd_add(portal->p_socket, &fdset, nfds); + } + error = select(nfds + 1, &fdset, NULL, NULL, NULL); + if (error <= 0) { + if (errno == EINTR) + return; + log_err(1, "select"); + } + TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) { + TAILQ_FOREACH(portal, &pg->pg_portals, p_next) { + if (!FD_ISSET(portal->p_socket, &fdset)) + continue; + client_fd = accept(portal->p_socket, NULL, 0); + if (client_fd < 0) + log_err(1, "accept"); + handle_connection(portal, client_fd, dont_fork); + break; + } + } +#endif /* !ICL_KERNEL_PROXY */ + } +} + +static void +sighup_handler(int dummy __unused) +{ + + sighup_received = true; +} + +static void +sigterm_handler(int dummy __unused) +{ + + sigterm_received = true; +} + +static void +register_signals(void) +{ + struct sigaction sa; + int error; + + bzero(&sa, sizeof(sa)); + sa.sa_handler = sighup_handler; + sigfillset(&sa.sa_mask); + error = sigaction(SIGHUP, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); + + sa.sa_handler = sigterm_handler; + error = sigaction(SIGTERM, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); + + sa.sa_handler = sigterm_handler; + error = sigaction(SIGINT, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); +} + +int +main(int argc, char **argv) +{ + struct conf *oldconf, *newconf, *tmpconf; + const char *config_path = DEFAULT_CONFIG_PATH; + int debug = 0, ch, error; + bool dont_daemonize = false; + + while ((ch = getopt(argc, argv, "df:")) != -1) { + switch (ch) { + case 'd': + dont_daemonize = true; + debug++; + break; + case 'f': + config_path = optarg; + break; + case '?': + default: + usage(); + } + } + argc -= optind; + if (argc != 0) + usage(); + + log_init(debug); + kernel_init(); + + oldconf = conf_new_from_kernel(); + newconf = conf_new_from_file(config_path); + if (newconf == NULL) + log_errx(1, "configuration error, exiting"); + if (debug > 0) { + oldconf->conf_debug = debug; + newconf->conf_debug = debug; + } + + if (dont_daemonize == false) { + if (daemon(0, 0) == -1) { + log_warn("cannot daemonize"); + pidfile_remove(newconf->conf_pidfh); + exit(1); + } + } + +#ifdef ICL_KERNEL_PROXY + log_debugx("enabling CTL iSCSI port"); + error = kernel_port_on(); + if (error != 0) + log_errx(1, "failed to enable CTL iSCSI port, exiting"); +#endif + + error = conf_apply(oldconf, newconf); + if (error != 0) + log_errx(1, "failed to apply configuration, exiting"); + conf_delete(oldconf); + oldconf = NULL; + + register_signals(); + +#ifndef ICL_KERNEL_PROXY + log_debugx("enabling CTL iSCSI port"); + error = kernel_port_on(); + if (error != 0) + log_errx(1, "failed to enable CTL iSCSI port, exiting"); +#endif + + for (;;) { + main_loop(newconf, dont_daemonize); + if (sighup_received) { + sighup_received = false; + log_debugx("received SIGHUP, reloading configuration"); + tmpconf = conf_new_from_file(config_path); + if (tmpconf == NULL) { + log_warnx("configuration error, " + "continuing with old configuration"); + } else { + if (debug > 0) + tmpconf->conf_debug = debug; + oldconf = newconf; + newconf = tmpconf; + error = conf_apply(oldconf, newconf); + if (error != 0) + log_warnx("failed to reload " + "configuration"); + conf_delete(oldconf); + oldconf = NULL; + } + } else if (sigterm_received) { + log_debugx("exiting on signal; " + "reloading empty configuration"); + + log_debugx("disabling CTL iSCSI port " + "and terminating all connections"); + error = kernel_port_off(); + if (error != 0) + log_warnx("failed to disable CTL iSCSI port"); + + oldconf = newconf; + newconf = conf_new(); + if (debug > 0) + newconf->conf_debug = debug; + error = conf_apply(oldconf, newconf); + if (error != 0) + log_warnx("failed to apply configuration"); + + log_warnx("exiting on signal"); + exit(0); + } else { + nchildren -= wait_for_children(false); + assert(nchildren >= 0); + } + } + /* NOTREACHED */ +} diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h new file mode 100644 index 0000000..3557978 --- /dev/null +++ b/usr.sbin/ctld/ctld.h @@ -0,0 +1,277 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef CTLD_H +#define CTLD_H + +#include <sys/queue.h> +#include <stdbool.h> +#include <libutil.h> + +#define DEFAULT_CONFIG_PATH "/etc/ctl.conf" +#define DEFAULT_PIDFILE "/var/run/ctld.pid" +#define DEFAULT_BLOCKSIZE 512 + +#define MAX_NAME_LEN 223 +#define MAX_DATA_SEGMENT_LENGTH (128 * 1024) +#define MAX_BURST_LENGTH 16776192 + +struct auth { + TAILQ_ENTRY(auth) a_next; + struct auth_group *a_auth_group; + char *a_user; + char *a_secret; + char *a_mutual_user; + char *a_mutual_secret; +}; + +#define AG_TYPE_UNKNOWN 0 +#define AG_TYPE_NO_AUTHENTICATION 1 +#define AG_TYPE_CHAP 2 +#define AG_TYPE_CHAP_MUTUAL 3 + +struct auth_group { + TAILQ_ENTRY(auth_group) ag_next; + struct conf *ag_conf; + char *ag_name; + struct target *ag_target; + int ag_type; + TAILQ_HEAD(, auth) ag_auths; +}; + +struct portal { + TAILQ_ENTRY(portal) p_next; + struct portal_group *p_portal_group; + bool p_iser; + char *p_listen; + struct addrinfo *p_ai; + + TAILQ_HEAD(, target) p_targets; + int p_socket; +}; + +struct portal_group { + TAILQ_ENTRY(portal_group) pg_next; + struct conf *pg_conf; + char *pg_name; + struct auth_group *pg_discovery_auth_group; + bool pg_unassigned; + TAILQ_HEAD(, portal) pg_portals; + + uint16_t pg_tag; +}; + +struct lun_option { + TAILQ_ENTRY(lun_option) lo_next; + struct lun *lo_lun; + char *lo_name; + char *lo_value; +}; + +struct lun { + TAILQ_ENTRY(lun) l_next; + TAILQ_HEAD(, lun_option) l_options; + struct target *l_target; + int l_lun; + char *l_backend; + int l_blocksize; + char *l_device_id; + char *l_path; + char *l_serial; + int64_t l_size; + + int l_ctl_lun; +}; + +struct target { + TAILQ_ENTRY(target) t_next; + TAILQ_HEAD(, lun) t_luns; + struct conf *t_conf; + struct auth_group *t_auth_group; + struct portal_group *t_portal_group; + char *t_iqn; + char *t_alias; +}; + +struct conf { + char *conf_pidfile_path; + TAILQ_HEAD(, target) conf_targets; + TAILQ_HEAD(, auth_group) conf_auth_groups; + TAILQ_HEAD(, portal_group) conf_portal_groups; + int conf_debug; + int conf_timeout; + int conf_maxproc; + + uint16_t conf_last_portal_group_tag; + struct pidfh *conf_pidfh; +}; + +#define CONN_SESSION_TYPE_NONE 0 +#define CONN_SESSION_TYPE_DISCOVERY 1 +#define CONN_SESSION_TYPE_NORMAL 2 + +#define CONN_DIGEST_NONE 0 +#define CONN_DIGEST_CRC32C 1 + +struct connection { + struct portal *conn_portal; + struct target *conn_target; + int conn_socket; + int conn_session_type; + char *conn_initiator_name; + char *conn_initiator_addr; + char *conn_initiator_alias; + uint32_t conn_cmdsn; + uint32_t conn_statsn; + size_t conn_max_data_segment_length; + size_t conn_max_burst_length; + int conn_immediate_data; + int conn_header_digest; + int conn_data_digest; +}; + +struct pdu { + struct connection *pdu_connection; + struct iscsi_bhs *pdu_bhs; + char *pdu_data; + size_t pdu_data_len; +}; + +#define KEYS_MAX 1024 + +struct keys { + char *keys_names[KEYS_MAX]; + char *keys_values[KEYS_MAX]; + char *keys_data; + size_t keys_data_len; +}; + +struct conf *conf_new(void); +struct conf *conf_new_from_file(const char *path); +struct conf *conf_new_from_kernel(void); +void conf_delete(struct conf *conf); +int conf_verify(struct conf *conf); + +struct auth_group *auth_group_new(struct conf *conf, const char *name); +void auth_group_delete(struct auth_group *ag); +struct auth_group *auth_group_find(struct conf *conf, const char *name); + +const struct auth *auth_new_chap(struct auth_group *ag, + const char *user, const char *secret); +const struct auth *auth_new_chap_mutual(struct auth_group *ag, + const char *user, const char *secret, + const char *user2, const char *secret2); +const struct auth *auth_find(struct auth_group *ag, + const char *user); + +struct portal_group *portal_group_new(struct conf *conf, const char *name); +void portal_group_delete(struct portal_group *pg); +struct portal_group *portal_group_find(struct conf *conf, const char *name); +int portal_group_add_listen(struct portal_group *pg, + const char *listen, bool iser); + +struct target *target_new(struct conf *conf, const char *iqn); +void target_delete(struct target *target); +struct target *target_find(struct conf *conf, + const char *iqn); + +struct lun *lun_new(struct target *target, int lun_id); +void lun_delete(struct lun *lun); +struct lun *lun_find(struct target *target, int lun_id); +void lun_set_backend(struct lun *lun, const char *value); +void lun_set_blocksize(struct lun *lun, size_t value); +void lun_set_device_id(struct lun *lun, const char *value); +void lun_set_path(struct lun *lun, const char *value); +void lun_set_serial(struct lun *lun, const char *value); +void lun_set_size(struct lun *lun, size_t value); +void lun_set_ctl_lun(struct lun *lun, uint32_t value); + +struct lun_option *lun_option_new(struct lun *lun, + const char *name, const char *value); +void lun_option_delete(struct lun_option *clo); +struct lun_option *lun_option_find(struct lun *lun, const char *name); +void lun_option_set(struct lun_option *clo, + const char *value); + +void kernel_init(void); +int kernel_lun_add(struct lun *lun); +int kernel_lun_resize(struct lun *lun); +int kernel_lun_remove(struct lun *lun); +void kernel_handoff(struct connection *conn); +int kernel_port_on(void); +int kernel_port_off(void); +void kernel_capsicate(void); + +/* + * ICL_KERNEL_PROXY + */ +void kernel_listen(struct addrinfo *ai, bool iser); +int kernel_accept(void); +void kernel_send(struct pdu *pdu); +void kernel_receive(struct pdu *pdu); + +struct keys *keys_new(void); +void keys_delete(struct keys *keys); +void keys_load(struct keys *keys, const struct pdu *pdu); +void keys_save(struct keys *keys, struct pdu *pdu); +const char *keys_find(struct keys *keys, const char *name); +int keys_find_int(struct keys *keys, const char *name); +void keys_add(struct keys *keys, + const char *name, const char *value); +void keys_add_int(struct keys *keys, + const char *name, int value); + +struct pdu *pdu_new(struct connection *conn); +struct pdu *pdu_new_response(struct pdu *request); +void pdu_delete(struct pdu *pdu); +void pdu_receive(struct pdu *request); +void pdu_send(struct pdu *response); + +void login(struct connection *conn); + +void discovery(struct connection *conn); + +void log_init(int level); +void log_set_peer_name(const char *name); +void log_set_peer_addr(const char *addr); +void log_err(int, const char *, ...) + __dead2 __printf0like(2, 3); +void log_errx(int, const char *, ...) + __dead2 __printf0like(2, 3); +void log_warn(const char *, ...) __printf0like(1, 2); +void log_warnx(const char *, ...) __printflike(1, 2); +void log_debugx(const char *, ...) __printf0like(1, 2); + +char *checked_strdup(const char *); +bool valid_iscsi_name(const char *name); +bool timed_out(void); + +#endif /* !CTLD_H */ diff --git a/usr.sbin/ctld/discovery.c b/usr.sbin/ctld/discovery.c new file mode 100644 index 0000000..bef7da6 --- /dev/null +++ b/usr.sbin/ctld/discovery.c @@ -0,0 +1,221 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> + +#include "ctld.h" +#include "iscsi_proto.h" + +static struct pdu * +text_receive(struct connection *conn) +{ + struct pdu *request; + struct iscsi_bhs_text_request *bhstr; + + request = pdu_new(conn); + pdu_receive(request); + if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != + ISCSI_BHS_OPCODE_TEXT_REQUEST) + log_errx(1, "protocol error: received invalid opcode 0x%x", + request->pdu_bhs->bhs_opcode); + bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; +#if 0 + if ((bhstr->bhstr_flags & ISCSI_BHSTR_FLAGS_FINAL) == 0) + log_errx(1, "received Text PDU without the \"F\" flag"); +#endif + /* + * XXX: Implement the C flag some day. + */ + if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) + log_errx(1, "received Text PDU with unsupported \"C\" flag"); + if (request->pdu_data_len == 0) + log_errx(1, "received Text PDU with empty data segment"); + + if (ntohl(bhstr->bhstr_cmdsn) < conn->conn_cmdsn) { + log_errx(1, "received Text PDU with decreasing CmdSN: " + "was %d, is %d", conn->conn_cmdsn, ntohl(bhstr->bhstr_cmdsn)); + } + if (ntohl(bhstr->bhstr_expstatsn) != conn->conn_statsn) { + log_errx(1, "received Text PDU with wrong StatSN: " + "is %d, should be %d", ntohl(bhstr->bhstr_expstatsn), + conn->conn_statsn); + } + conn->conn_cmdsn = ntohl(bhstr->bhstr_cmdsn); + + return (request); +} + +static struct pdu * +text_new_response(struct pdu *request) +{ + struct pdu *response; + struct connection *conn; + struct iscsi_bhs_text_request *bhstr; + struct iscsi_bhs_text_response *bhstr2; + + bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; + conn = request->pdu_connection; + + response = pdu_new_response(request); + bhstr2 = (struct iscsi_bhs_text_response *)response->pdu_bhs; + bhstr2->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_RESPONSE; + bhstr2->bhstr_flags = BHSTR_FLAGS_FINAL; + bhstr2->bhstr_lun = bhstr->bhstr_lun; + bhstr2->bhstr_initiator_task_tag = bhstr->bhstr_initiator_task_tag; + bhstr2->bhstr_target_transfer_tag = bhstr->bhstr_target_transfer_tag; + bhstr2->bhstr_statsn = htonl(conn->conn_statsn++); + bhstr2->bhstr_expcmdsn = htonl(conn->conn_cmdsn); + bhstr2->bhstr_maxcmdsn = htonl(conn->conn_cmdsn); + + return (response); +} + +static struct pdu * +logout_receive(struct connection *conn) +{ + struct pdu *request; + struct iscsi_bhs_logout_request *bhslr; + + request = pdu_new(conn); + pdu_receive(request); + if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != + ISCSI_BHS_OPCODE_LOGOUT_REQUEST) + log_errx(1, "protocol error: received invalid opcode 0x%x", + request->pdu_bhs->bhs_opcode); + bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; + if ((bhslr->bhslr_reason & 0x7f) != BHSLR_REASON_CLOSE_SESSION) + log_debugx("received Logout PDU with invalid reason 0x%x; " + "continuing anyway", bhslr->bhslr_reason & 0x7f); + if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { + log_errx(1, "received Logout PDU with decreasing CmdSN: " + "was %d, is %d", conn->conn_cmdsn, + ntohl(bhslr->bhslr_cmdsn)); + } + if (ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { + log_errx(1, "received Logout PDU with wrong StatSN: " + "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), + conn->conn_statsn); + } + conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); + + return (request); +} + +static struct pdu * +logout_new_response(struct pdu *request) +{ + struct pdu *response; + struct connection *conn; + struct iscsi_bhs_logout_request *bhslr; + struct iscsi_bhs_logout_response *bhslr2; + + bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; + conn = request->pdu_connection; + + response = pdu_new_response(request); + bhslr2 = (struct iscsi_bhs_logout_response *)response->pdu_bhs; + bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_RESPONSE; + bhslr2->bhslr_flags = 0x80; + bhslr2->bhslr_response = BHSLR_RESPONSE_CLOSED_SUCCESSFULLY; + bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; + bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); + bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); + bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); + + return (response); +} + +void +discovery(struct connection *conn) +{ + struct pdu *request, *response; + struct keys *request_keys, *response_keys; + struct target *targ; + const char *send_targets; + + log_debugx("beginning discovery session; waiting for Text PDU"); + request = text_receive(conn); + request_keys = keys_new(); + keys_load(request_keys, request); + + send_targets = keys_find(request_keys, "SendTargets"); + if (send_targets == NULL) + log_errx(1, "received Text PDU without SendTargets"); + + response = text_new_response(request); + response_keys = keys_new(); + + if (strcmp(send_targets, "All") == 0) { + TAILQ_FOREACH(targ, + &conn->conn_portal->p_portal_group->pg_conf->conf_targets, + t_next) { + if (targ->t_portal_group != + conn->conn_portal->p_portal_group) { + log_debugx("not returning target \"%s\"; " + "belongs to a different portal group", + targ->t_iqn); + continue; + } + keys_add(response_keys, "TargetName", targ->t_iqn); + } + } else { + targ = target_find(conn->conn_portal->p_portal_group->pg_conf, + send_targets); + if (targ == NULL) { + log_debugx("initiator requested information on unknown " + "target \"%s\"; returning nothing", send_targets); + } else { + keys_add(response_keys, "TargetName", targ->t_iqn); + } + } + keys_save(response_keys, response); + + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); + pdu_delete(request); + keys_delete(request_keys); + + log_debugx("done sending targets; waiting for Logout PDU"); + request = logout_receive(conn); + response = logout_new_response(request); + + pdu_send(response); + pdu_delete(response); + pdu_delete(request); + + log_debugx("discovery session done"); +} diff --git a/usr.sbin/ctld/kernel.c b/usr.sbin/ctld/kernel.c new file mode 100644 index 0000000..dc55594 --- /dev/null +++ b/usr.sbin/ctld/kernel.c @@ -0,0 +1,782 @@ +/*- + * Copyright (c) 2003, 2004 Silicon Graphics International Corp. + * Copyright (c) 1997-2007 Kenneth D. Merry + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * Portions of this software were developed by Edward Tomasz Napierala + * under sponsorship from the FreeBSD Foundation. + * + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + * + * $FreeBSD$ + */ + +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/linker.h> +#include <sys/queue.h> +#include <sys/callout.h> +#include <sys/sbuf.h> +#include <sys/capability.h> +#include <assert.h> +#include <bsdxml.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <cam/scsi/scsi_all.h> +#include <cam/scsi/scsi_message.h> +#include <cam/ctl/ctl.h> +#include <cam/ctl/ctl_io.h> +#include <cam/ctl/ctl_frontend_internal.h> +#include <cam/ctl/ctl_backend.h> +#include <cam/ctl/ctl_ioctl.h> +#include <cam/ctl/ctl_backend_block.h> +#include <cam/ctl/ctl_util.h> +#include <cam/ctl/ctl_scsi_all.h> + +#ifdef ICL_KERNEL_PROXY +#include <netdb.h> +#endif + +#include "ctld.h" + +static int ctl_fd = 0; + +void +kernel_init(void) +{ + int retval, saved_errno; + + ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); + if (ctl_fd < 0 && errno == ENOENT) { + saved_errno = errno; + retval = kldload("ctl"); + if (retval != -1) + ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); + else + errno = saved_errno; + } + if (ctl_fd < 0) + log_err(1, "failed to open %s", CTL_DEFAULT_DEV); +} + +/* + * Name/value pair used for per-LUN attributes. + */ +struct cctl_lun_nv { + char *name; + char *value; + STAILQ_ENTRY(cctl_lun_nv) links; +}; + +/* + * Backend LUN information. + */ +struct cctl_lun { + uint64_t lun_id; + char *backend_type; + uint64_t size_blocks; + uint32_t blocksize; + char *serial_number; + char *device_id; + char *cfiscsi_target; + char *cfiscsi_target_alias; + int cfiscsi_lun; + STAILQ_HEAD(,cctl_lun_nv) attr_list; + STAILQ_ENTRY(cctl_lun) links; +}; + +struct cctl_devlist_data { + int num_luns; + STAILQ_HEAD(,cctl_lun) lun_list; + struct cctl_lun *cur_lun; + int level; + struct sbuf *cur_sb[32]; +}; + +static void +cctl_start_element(void *user_data, const char *name, const char **attr) +{ + int i; + struct cctl_devlist_data *devlist; + struct cctl_lun *cur_lun; + + devlist = (struct cctl_devlist_data *)user_data; + cur_lun = devlist->cur_lun; + devlist->level++; + if ((u_int)devlist->level >= (sizeof(devlist->cur_sb) / + sizeof(devlist->cur_sb[0]))) + log_errx(1, "%s: too many nesting levels, %zd max", __func__, + sizeof(devlist->cur_sb) / sizeof(devlist->cur_sb[0])); + + devlist->cur_sb[devlist->level] = sbuf_new_auto(); + if (devlist->cur_sb[devlist->level] == NULL) + log_err(1, "%s: unable to allocate sbuf", __func__); + + if (strcmp(name, "lun") == 0) { + if (cur_lun != NULL) + log_errx(1, "%s: improper lun element nesting", + __func__); + + cur_lun = calloc(1, sizeof(*cur_lun)); + if (cur_lun == NULL) + log_err(1, "%s: cannot allocate %zd bytes", __func__, + sizeof(*cur_lun)); + + devlist->num_luns++; + devlist->cur_lun = cur_lun; + + STAILQ_INIT(&cur_lun->attr_list); + STAILQ_INSERT_TAIL(&devlist->lun_list, cur_lun, links); + + for (i = 0; attr[i] != NULL; i += 2) { + if (strcmp(attr[i], "id") == 0) { + cur_lun->lun_id = strtoull(attr[i+1], NULL, 0); + } else { + log_errx(1, "%s: invalid LUN attribute %s = %s", + __func__, attr[i], attr[i+1]); + } + } + } +} + +static void +cctl_end_element(void *user_data, const char *name) +{ + struct cctl_devlist_data *devlist; + struct cctl_lun *cur_lun; + char *str; + + devlist = (struct cctl_devlist_data *)user_data; + cur_lun = devlist->cur_lun; + + if ((cur_lun == NULL) + && (strcmp(name, "ctllunlist") != 0)) + log_errx(1, "%s: cur_lun == NULL! (name = %s)", __func__, name); + + if (devlist->cur_sb[devlist->level] == NULL) + log_errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, + devlist->level, name); + + sbuf_finish(devlist->cur_sb[devlist->level]); + str = checked_strdup(sbuf_data(devlist->cur_sb[devlist->level])); + + if (strlen(str) == 0) { + free(str); + str = NULL; + } + + sbuf_delete(devlist->cur_sb[devlist->level]); + devlist->cur_sb[devlist->level] = NULL; + devlist->level--; + + if (strcmp(name, "backend_type") == 0) { + cur_lun->backend_type = str; + str = NULL; + } else if (strcmp(name, "size") == 0) { + cur_lun->size_blocks = strtoull(str, NULL, 0); + } else if (strcmp(name, "blocksize") == 0) { + cur_lun->blocksize = strtoul(str, NULL, 0); + } else if (strcmp(name, "serial_number") == 0) { + cur_lun->serial_number = str; + str = NULL; + } else if (strcmp(name, "device_id") == 0) { + cur_lun->device_id = str; + str = NULL; + } else if (strcmp(name, "cfiscsi_target") == 0) { + cur_lun->cfiscsi_target = str; + str = NULL; + } else if (strcmp(name, "cfiscsi_target_alias") == 0) { + cur_lun->cfiscsi_target_alias = str; + str = NULL; + } else if (strcmp(name, "cfiscsi_lun") == 0) { + cur_lun->cfiscsi_lun = strtoul(str, NULL, 0); + } else if (strcmp(name, "lun") == 0) { + devlist->cur_lun = NULL; + } else if (strcmp(name, "ctllunlist") == 0) { + + } else { + struct cctl_lun_nv *nv; + + nv = calloc(1, sizeof(*nv)); + if (nv == NULL) + log_err(1, "%s: can't allocate %zd bytes for nv pair", + __func__, sizeof(*nv)); + + nv->name = checked_strdup(name); + + nv->value = str; + str = NULL; + STAILQ_INSERT_TAIL(&cur_lun->attr_list, nv, links); + } + + free(str); +} + +static void +cctl_char_handler(void *user_data, const XML_Char *str, int len) +{ + struct cctl_devlist_data *devlist; + + devlist = (struct cctl_devlist_data *)user_data; + + sbuf_bcat(devlist->cur_sb[devlist->level], str, len); +} + +struct conf * +conf_new_from_kernel(void) +{ + struct conf *conf = NULL; + struct target *targ; + struct lun *cl; + struct lun_option *lo; + struct ctl_lun_list list; + struct cctl_devlist_data devlist; + struct cctl_lun *lun; + XML_Parser parser; + char *lun_str = NULL; + int lun_len; + int retval; + + lun_len = 4096; + + bzero(&devlist, sizeof(devlist)); + STAILQ_INIT(&devlist.lun_list); + + log_debugx("obtaining previously configured CTL luns from the kernel"); + +retry: + lun_str = realloc(lun_str, lun_len); + if (lun_str == NULL) + log_err(1, "realloc"); + + bzero(&list, sizeof(list)); + list.alloc_len = lun_len; + list.status = CTL_LUN_LIST_NONE; + list.lun_xml = lun_str; + + if (ioctl(ctl_fd, CTL_LUN_LIST, &list) == -1) { + log_warn("error issuing CTL_LUN_LIST ioctl"); + free(lun_str); + return (NULL); + } + + if (list.status == CTL_LUN_LIST_ERROR) { + log_warnx("error returned from CTL_LUN_LIST ioctl: %s", + list.error_str); + free(lun_str); + return (NULL); + } + + if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { + lun_len = lun_len << 1; + goto retry; + } + + parser = XML_ParserCreate(NULL); + if (parser == NULL) { + log_warnx("unable to create XML parser"); + free(lun_str); + return (NULL); + } + + XML_SetUserData(parser, &devlist); + XML_SetElementHandler(parser, cctl_start_element, cctl_end_element); + XML_SetCharacterDataHandler(parser, cctl_char_handler); + + retval = XML_Parse(parser, lun_str, strlen(lun_str), 1); + XML_ParserFree(parser); + free(lun_str); + if (retval != 1) { + log_warnx("XML_Parse failed"); + return (NULL); + } + + conf = conf_new(); + + STAILQ_FOREACH(lun, &devlist.lun_list, links) { + struct cctl_lun_nv *nv; + + if (lun->cfiscsi_target == NULL) { + log_debugx("CTL lun %ju wasn't managed by ctld; " + "ignoring", (uintmax_t)lun->lun_id); + continue; + } + + targ = target_find(conf, lun->cfiscsi_target); + if (targ == NULL) { +#if 0 + log_debugx("found new kernel target %s for CTL lun %ld", + lun->cfiscsi_target, lun->lun_id); +#endif + targ = target_new(conf, lun->cfiscsi_target); + if (targ == NULL) { + log_warnx("target_new failed"); + continue; + } + } + + cl = lun_find(targ, lun->cfiscsi_lun); + if (cl != NULL) { + log_warnx("found CTL lun %ju, backing lun %d, target " + "%s, also backed by CTL lun %d; ignoring", + (uintmax_t) lun->lun_id, cl->l_lun, + cl->l_target->t_iqn, cl->l_ctl_lun); + continue; + } + + log_debugx("found CTL lun %ju, backing lun %d, target %s", + (uintmax_t)lun->lun_id, lun->cfiscsi_lun, lun->cfiscsi_target); + + cl = lun_new(targ, lun->cfiscsi_lun); + if (cl == NULL) { + log_warnx("lun_new failed"); + continue; + } + lun_set_backend(cl, lun->backend_type); + lun_set_blocksize(cl, lun->blocksize); + lun_set_device_id(cl, lun->device_id); + lun_set_serial(cl, lun->serial_number); + lun_set_size(cl, lun->size_blocks * cl->l_blocksize); + lun_set_ctl_lun(cl, lun->lun_id); + + STAILQ_FOREACH(nv, &lun->attr_list, links) { + if (strcmp(nv->name, "file") == 0 || + strcmp(nv->name, "dev") == 0) { + lun_set_path(cl, nv->value); + continue; + } + lo = lun_option_new(cl, nv->name, nv->value); + if (lo == NULL) + log_warnx("unable to add CTL lun option %s " + "for CTL lun %ju for lun %d, target %s", + nv->name, (uintmax_t) lun->lun_id, + cl->l_lun, cl->l_target->t_iqn); + } + } + + return (conf); +} + +int +kernel_lun_add(struct lun *lun) +{ + struct lun_option *lo; + struct ctl_lun_req req; + char *tmp; + int error, i, num_options; + + bzero(&req, sizeof(req)); + + strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); + req.reqtype = CTL_LUNREQ_CREATE; + + req.reqdata.create.blocksize_bytes = lun->l_blocksize; + + if (lun->l_size != 0) + req.reqdata.create.lun_size_bytes = lun->l_size; + + req.reqdata.create.flags |= CTL_LUN_FLAG_DEV_TYPE; + req.reqdata.create.device_type = T_DIRECT; + + if (lun->l_serial != NULL) { + strlcpy(req.reqdata.create.serial_num, lun->l_serial, + sizeof(req.reqdata.create.serial_num)); + req.reqdata.create.flags |= CTL_LUN_FLAG_SERIAL_NUM; + } + + if (lun->l_device_id != NULL) { + strlcpy(req.reqdata.create.device_id, lun->l_device_id, + sizeof(req.reqdata.create.device_id)); + req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID; + } + + if (lun->l_path != NULL) { + lo = lun_option_find(lun, "file"); + if (lo != NULL) { + lun_option_set(lo, lun->l_path); + } else { + lo = lun_option_new(lun, "file", lun->l_path); + assert(lo != NULL); + } + } + + lo = lun_option_find(lun, "cfiscsi_target"); + if (lo != NULL) { + lun_option_set(lo, lun->l_target->t_iqn); + } else { + lo = lun_option_new(lun, "cfiscsi_target", + lun->l_target->t_iqn); + assert(lo != NULL); + } + + if (lun->l_target->t_alias != NULL) { + lo = lun_option_find(lun, "cfiscsi_target_alias"); + if (lo != NULL) { + lun_option_set(lo, lun->l_target->t_alias); + } else { + lo = lun_option_new(lun, "cfiscsi_target_alias", + lun->l_target->t_alias); + assert(lo != NULL); + } + } + + asprintf(&tmp, "%d", lun->l_lun); + if (tmp == NULL) + log_errx(1, "asprintf"); + lo = lun_option_find(lun, "cfiscsi_lun"); + if (lo != NULL) { + lun_option_set(lo, tmp); + free(tmp); + } else { + lo = lun_option_new(lun, "cfiscsi_lun", tmp); + free(tmp); + assert(lo != NULL); + } + + num_options = 0; + TAILQ_FOREACH(lo, &lun->l_options, lo_next) + num_options++; + + req.num_be_args = num_options; + if (num_options > 0) { + req.be_args = malloc(num_options * sizeof(*req.be_args)); + if (req.be_args == NULL) { + log_warn("error allocating %zd bytes", + num_options * sizeof(*req.be_args)); + return (1); + } + + i = 0; + TAILQ_FOREACH(lo, &lun->l_options, lo_next) { + /* + * +1 for the terminating '\0' + */ + req.be_args[i].namelen = strlen(lo->lo_name) + 1; + req.be_args[i].name = lo->lo_name; + req.be_args[i].vallen = strlen(lo->lo_value) + 1; + req.be_args[i].value = lo->lo_value; + req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD; + i++; + } + assert(i == num_options); + } + + error = ioctl(ctl_fd, CTL_LUN_REQ, &req); + free(req.be_args); + if (error != 0) { + log_warn("error issuing CTL_LUN_REQ ioctl"); + return (1); + } + + if (req.status == CTL_LUN_ERROR) { + log_warnx("error returned from LUN creation request: %s", + req.error_str); + return (1); + } + + if (req.status != CTL_LUN_OK) { + log_warnx("unknown LUN creation request status %d", + req.status); + return (1); + } + + lun_set_ctl_lun(lun, req.reqdata.create.req_lun_id); + + return (0); +} + +int +kernel_lun_resize(struct lun *lun) +{ + struct ctl_lun_req req; + + bzero(&req, sizeof(req)); + + strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); + req.reqtype = CTL_LUNREQ_MODIFY; + + req.reqdata.modify.lun_id = lun->l_ctl_lun; + req.reqdata.modify.lun_size_bytes = lun->l_size; + + if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) { + log_warn("error issuing CTL_LUN_REQ ioctl"); + return (1); + } + + if (req.status == CTL_LUN_ERROR) { + log_warnx("error returned from LUN modification request: %s", + req.error_str); + return (1); + } + + if (req.status != CTL_LUN_OK) { + log_warnx("unknown LUN modification request status %d", + req.status); + return (1); + } + + return (0); +} + +int +kernel_lun_remove(struct lun *lun) +{ + struct ctl_lun_req req; + + bzero(&req, sizeof(req)); + + strlcpy(req.backend, lun->l_backend, sizeof(req.backend)); + req.reqtype = CTL_LUNREQ_RM; + + req.reqdata.rm.lun_id = lun->l_ctl_lun; + + if (ioctl(ctl_fd, CTL_LUN_REQ, &req) == -1) { + log_warn("error issuing CTL_LUN_REQ ioctl"); + return (1); + } + + if (req.status == CTL_LUN_ERROR) { + log_warnx("error returned from LUN removal request: %s", + req.error_str); + return (1); + } + + if (req.status != CTL_LUN_OK) { + log_warnx("unknown LUN removal request status %d", req.status); + return (1); + } + + return (0); +} + +void +kernel_handoff(struct connection *conn) +{ + struct ctl_iscsi req; + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_HANDOFF; + strlcpy(req.data.handoff.initiator_name, + conn->conn_initiator_name, sizeof(req.data.handoff.initiator_name)); + strlcpy(req.data.handoff.initiator_addr, + conn->conn_initiator_addr, sizeof(req.data.handoff.initiator_addr)); + if (conn->conn_initiator_alias != NULL) { + strlcpy(req.data.handoff.initiator_alias, + conn->conn_initiator_alias, sizeof(req.data.handoff.initiator_alias)); + } + strlcpy(req.data.handoff.target_name, + conn->conn_target->t_iqn, sizeof(req.data.handoff.target_name)); + req.data.handoff.socket = conn->conn_socket; + req.data.handoff.portal_group_tag = + conn->conn_portal->p_portal_group->pg_tag; + if (conn->conn_header_digest == CONN_DIGEST_CRC32C) + req.data.handoff.header_digest = CTL_ISCSI_DIGEST_CRC32C; + if (conn->conn_data_digest == CONN_DIGEST_CRC32C) + req.data.handoff.data_digest = CTL_ISCSI_DIGEST_CRC32C; + req.data.handoff.cmdsn = conn->conn_cmdsn; + req.data.handoff.statsn = conn->conn_statsn; + req.data.handoff.max_recv_data_segment_length = + conn->conn_max_data_segment_length; + req.data.handoff.max_burst_length = conn->conn_max_burst_length; + req.data.handoff.immediate_data = conn->conn_immediate_data; + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) + log_err(1, "error issuing CTL_ISCSI ioctl; " + "dropping connection"); + + if (req.status != CTL_ISCSI_OK) + log_errx(1, "error returned from CTL iSCSI handoff request: " + "%s; dropping connection", req.error_str); +} + +int +kernel_port_on(void) +{ + struct ctl_port_entry entry; + int error; + + bzero(&entry, sizeof(entry)); + + entry.port_type = CTL_PORT_ISCSI; + entry.targ_port = -1; + + error = ioctl(ctl_fd, CTL_ENABLE_PORT, &entry); + if (error != 0) { + log_warn("CTL_ENABLE_PORT ioctl failed"); + return (-1); + } + + return (0); +} + +int +kernel_port_off(void) +{ + struct ctl_port_entry entry; + int error; + + bzero(&entry, sizeof(entry)); + + entry.port_type = CTL_PORT_ISCSI; + entry.targ_port = -1; + + error = ioctl(ctl_fd, CTL_DISABLE_PORT, &entry); + if (error != 0) { + log_warn("CTL_DISABLE_PORT ioctl failed"); + return (-1); + } + + return (0); +} + +#ifdef ICL_KERNEL_PROXY +void +kernel_listen(struct addrinfo *ai, bool iser) +{ + struct ctl_iscsi req; + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_LISTEN; + req.data.listen.iser = iser; + req.data.listen.domain = ai->ai_family; + req.data.listen.socktype = ai->ai_socktype; + req.data.listen.protocol = ai->ai_protocol; + req.data.listen.addr = ai->ai_addr; + req.data.listen.addrlen = ai->ai_addrlen; + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) + log_warn("error issuing CTL_ISCSI_LISTEN ioctl"); +} + +int +kernel_accept(void) +{ + struct ctl_iscsi req; + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_ACCEPT; + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { + log_warn("error issuing CTL_ISCSI_LISTEN ioctl"); + return (0); + } + + return (req.data.accept.connection_id); +} + +void +kernel_send(struct pdu *pdu) +{ + struct ctl_iscsi req; + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_SEND; + req.data.send.connection_id = pdu->pdu_connection->conn_socket; + req.data.send.bhs = pdu->pdu_bhs; + req.data.send.data_segment_len = pdu->pdu_data_len; + req.data.send.data_segment = pdu->pdu_data; + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) + log_err(1, "error issuing CTL_ISCSI ioctl; " + "dropping connection"); + + if (req.status != CTL_ISCSI_OK) + log_errx(1, "error returned from CTL iSCSI send: " + "%s; dropping connection", req.error_str); +} + +void +kernel_receive(struct pdu *pdu) +{ + struct ctl_iscsi req; + + pdu->pdu_data = malloc(MAX_DATA_SEGMENT_LENGTH); + if (pdu->pdu_data == NULL) + log_err(1, "malloc"); + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_RECEIVE; + req.data.receive.connection_id = pdu->pdu_connection->conn_socket; + req.data.receive.bhs = pdu->pdu_bhs; + req.data.receive.data_segment_len = MAX_DATA_SEGMENT_LENGTH; + req.data.receive.data_segment = pdu->pdu_data; + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) + log_err(1, "error issuing CTL_ISCSI ioctl; " + "dropping connection"); + + if (req.status != CTL_ISCSI_OK) + log_errx(1, "error returned from CTL iSCSI receive: " + "%s; dropping connection", req.error_str); + +} + +#endif /* ICL_KERNEL_PROXY */ + +/* + * XXX: I CANT INTO LATIN + */ +void +kernel_capsicate(void) +{ + int error; + cap_rights_t rights; + const unsigned long cmds[] = { CTL_ISCSI }; + + cap_rights_init(&rights, CAP_IOCTL); + error = cap_rights_limit(ctl_fd, &rights); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_rights_limit"); + + error = cap_ioctls_limit(ctl_fd, cmds, + sizeof(cmds) / sizeof(cmds[0])); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_ioctls_limit"); + + error = cap_enter(); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_enter"); + + if (cap_sandboxed()) + log_debugx("Capsicum capability mode enabled"); + else + log_warnx("Capsicum capability mode not supported"); +} + diff --git a/usr.sbin/ctld/keys.c b/usr.sbin/ctld/keys.c new file mode 100644 index 0000000..0ea4aa0 --- /dev/null +++ b/usr.sbin/ctld/keys.c @@ -0,0 +1,216 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "ctld.h" + +struct keys * +keys_new(void) +{ + struct keys *keys; + + keys = calloc(sizeof(*keys), 1); + if (keys == NULL) + log_err(1, "calloc"); + + return (keys); +} + +void +keys_delete(struct keys *keys) +{ + + free(keys->keys_data); + free(keys); +} + +void +keys_load(struct keys *keys, const struct pdu *pdu) +{ + int i; + char *pair; + size_t pair_len; + + if (pdu->pdu_data_len == 0) + log_errx(1, "protocol error: empty data segment"); + + if (pdu->pdu_data[pdu->pdu_data_len - 1] != '\0') + log_errx(1, "protocol error: key not NULL-terminated\n"); + + assert(keys->keys_data == NULL); + keys->keys_data_len = pdu->pdu_data_len; + keys->keys_data = malloc(keys->keys_data_len); + if (keys->keys_data == NULL) + log_err(1, "malloc"); + memcpy(keys->keys_data, pdu->pdu_data, keys->keys_data_len); + + /* + * XXX: Review this carefully. + */ + pair = keys->keys_data; + for (i = 0;; i++) { + if (i >= KEYS_MAX) + log_errx(1, "too many keys received"); + + pair_len = strlen(pair); + + keys->keys_values[i] = pair; + keys->keys_names[i] = strsep(&keys->keys_values[i], "="); + if (keys->keys_names[i] == NULL || keys->keys_values[i] == NULL) + log_errx(1, "malformed keys"); + log_debugx("key received: \"%s=%s\"", + keys->keys_names[i], keys->keys_values[i]); + + pair += pair_len + 1; /* +1 to skip the terminating '\0'. */ + if (pair == keys->keys_data + keys->keys_data_len) + break; + assert(pair < keys->keys_data + keys->keys_data_len); + } +} + +void +keys_save(struct keys *keys, struct pdu *pdu) +{ + char *data; + size_t len; + int i; + + /* + * XXX: Not particularly efficient. + */ + len = 0; + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + break; + /* + * +1 for '=', +1 for '\0'. + */ + len += strlen(keys->keys_names[i]) + + strlen(keys->keys_values[i]) + 2; + } + + if (len == 0) + return; + + data = malloc(len); + if (data == NULL) + log_err(1, "malloc"); + + pdu->pdu_data = data; + pdu->pdu_data_len = len; + + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + break; + data += sprintf(data, "%s=%s", + keys->keys_names[i], keys->keys_values[i]); + data += 1; /* for '\0'. */ + } +} + +const char * +keys_find(struct keys *keys, const char *name) +{ + int i; + + /* + * Note that we don't handle duplicated key names here, + * as they are not supposed to happen in requests, and if they do, + * it's an initiator error. + */ + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + return (NULL); + if (strcmp(keys->keys_names[i], name) == 0) + return (keys->keys_values[i]); + } + return (NULL); +} + +int +keys_find_int(struct keys *keys, const char *name) +{ + const char *str; + char *endptr; + int num; + + str = keys_find(keys, name); + if (str == NULL) + return (-1); + + num = strtoul(str, &endptr, 10); + if (*endptr != '\0') { + log_debugx("invalid numeric value \"%s\"", str); + return (-1); + } + + return (num); +} + +void +keys_add(struct keys *keys, const char *name, const char *value) +{ + int i; + + log_debugx("key to send: \"%s=%s\"", name, value); + + /* + * Note that we don't check for duplicates here, as they are perfectly + * fine in responses, e.g. the "TargetName" keys in discovery sesion + * response. + */ + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) { + keys->keys_names[i] = checked_strdup(name); + keys->keys_values[i] = checked_strdup(value); + return; + } + } + log_errx(1, "too many keys"); +} + +void +keys_add_int(struct keys *keys, const char *name, int value) +{ + char *str; + int ret; + + ret = asprintf(&str, "%d", value); + if (ret <= 0) + log_err(1, "asprintf"); + + keys_add(keys, name, str); + free(str); +} diff --git a/usr.sbin/ctld/log.c b/usr.sbin/ctld/log.c new file mode 100644 index 0000000..7b8ba71 --- /dev/null +++ b/usr.sbin/ctld/log.c @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <vis.h> + +#include "ctld.h" + +static int log_level = 0; +static char *peer_name = NULL; +static char *peer_addr = NULL; + +#define MSGBUF_LEN 1024 + +void +log_init(int level) +{ + + log_level = level; + openlog(getprogname(), LOG_NDELAY | LOG_PID, LOG_DAEMON); +} + +void +log_set_peer_name(const char *name) +{ + + /* + * XXX: Turn it into assertion? + */ + if (peer_name != NULL) + log_errx(1, "%s called twice", __func__); + if (peer_addr == NULL) + log_errx(1, "%s called before log_set_peer_addr", __func__); + + peer_name = checked_strdup(name); +} + +void +log_set_peer_addr(const char *addr) +{ + + /* + * XXX: Turn it into assertion? + */ + if (peer_addr != NULL) + log_errx(1, "%s called twice", __func__); + + peer_addr = checked_strdup(addr); +} + +static void +log_common(int priority, int log_errno, const char *fmt, va_list ap) +{ + static char msgbuf[MSGBUF_LEN]; + static char msgbuf_strvised[MSGBUF_LEN * 4 + 1]; + int ret; + + ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); + if (ret < 0) { + fprintf(stderr, "%s: snprintf failed", getprogname()); + syslog(LOG_CRIT, "snprintf failed"); + exit(1); + } + + ret = strnvis(msgbuf_strvised, sizeof(msgbuf_strvised), msgbuf, VIS_NL); + if (ret < 0) { + fprintf(stderr, "%s: strnvis failed", getprogname()); + syslog(LOG_CRIT, "strnvis failed"); + exit(1); + } + + if (log_errno == -1) { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s\n", getprogname(), + peer_addr, peer_name, msgbuf_strvised); + syslog(priority, "%s (%s): %s", + peer_addr, peer_name, msgbuf_strvised); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s\n", getprogname(), + peer_addr, msgbuf_strvised); + syslog(priority, "%s: %s", + peer_addr, msgbuf_strvised); + } else { + fprintf(stderr, "%s: %s\n", getprogname(), msgbuf_strvised); + syslog(priority, "%s", msgbuf_strvised); + } + + } else { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), + peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + syslog(priority, "%s (%s): %s: %s", + peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), + peer_addr, msgbuf_strvised, strerror(errno)); + syslog(priority, "%s: %s: %s", + peer_addr, msgbuf_strvised, strerror(errno)); + } else { + fprintf(stderr, "%s: %s: %s\n", getprogname(), + msgbuf_strvised, strerror(errno)); + syslog(priority, "%s: %s", + msgbuf_strvised, strerror(errno)); + } + } +} + +void +log_err(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_CRIT, errno, fmt, ap); + va_end(ap); + + exit(eval); +} + +void +log_errx(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_CRIT, -1, fmt, ap); + va_end(ap); + + exit(eval); +} + +void +log_warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_WARNING, errno, fmt, ap); + va_end(ap); +} + +void +log_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_WARNING, -1, fmt, ap); + va_end(ap); +} + +void +log_debugx(const char *fmt, ...) +{ + va_list ap; + + if (log_level == 0) + return; + + va_start(ap, fmt); + log_common(LOG_DEBUG, -1, fmt, ap); + va_end(ap); +} diff --git a/usr.sbin/ctld/login.c b/usr.sbin/ctld/login.c new file mode 100644 index 0000000..042cf2a --- /dev/null +++ b/usr.sbin/ctld/login.c @@ -0,0 +1,1053 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <netinet/in.h> +#include <openssl/err.h> +#include <openssl/md5.h> +#include <openssl/rand.h> + +#include "ctld.h" +#include "iscsi_proto.h" + +static void login_send_error(struct pdu *request, + char class, char detail); + +static void +login_set_nsg(struct pdu *response, int nsg) +{ + struct iscsi_bhs_login_response *bhslr; + + assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION || + nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || + nsg == BHSLR_STAGE_FULL_FEATURE_PHASE); + + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + + bhslr->bhslr_flags &= 0xFC; + bhslr->bhslr_flags |= nsg; +} + +static int +login_csg(const struct pdu *request) +{ + struct iscsi_bhs_login_request *bhslr; + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + + return ((bhslr->bhslr_flags & 0x0C) >> 2); +} + +static void +login_set_csg(struct pdu *response, int csg) +{ + struct iscsi_bhs_login_response *bhslr; + + assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION || + csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || + csg == BHSLR_STAGE_FULL_FEATURE_PHASE); + + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + + bhslr->bhslr_flags &= 0xF3; + bhslr->bhslr_flags |= csg << 2; +} + +static struct pdu * +login_receive(struct connection *conn, bool initial) +{ + struct pdu *request; + struct iscsi_bhs_login_request *bhslr; + + request = pdu_new(conn); + pdu_receive(request); + if ((request->pdu_bhs->bhs_opcode & ~ISCSI_BHS_OPCODE_IMMEDIATE) != + ISCSI_BHS_OPCODE_LOGIN_REQUEST) { + /* + * The first PDU in session is special - if we receive any PDU + * different than login request, we have to drop the connection + * without sending response ("A target receiving any PDU + * except a Login request before the Login Phase is started MUST + * immediately terminate the connection on which the PDU + * was received.") + */ + if (initial == false) + login_send_error(request, 0x02, 0x0b); + log_errx(1, "protocol error: received invalid opcode 0x%x", + request->pdu_bhs->bhs_opcode); + } + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + /* + * XXX: Implement the C flag some day. + */ + if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0) { + login_send_error(request, 0x03, 0x00); + log_errx(1, "received Login PDU with unsupported \"C\" flag"); + } + if (bhslr->bhslr_version_max != 0x00) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with unsupported " + "Version-max 0x%x", bhslr->bhslr_version_max); + } + if (bhslr->bhslr_version_min != 0x00) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with unsupported " + "Version-min 0x%x", bhslr->bhslr_version_min); + } + if (request->pdu_data_len == 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received Login PDU with empty data segment"); + } + if (ntohl(bhslr->bhslr_cmdsn) < conn->conn_cmdsn) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with decreasing CmdSN: " + "was %d, is %d", conn->conn_cmdsn, + ntohl(bhslr->bhslr_cmdsn)); + } + if (initial == false && + ntohl(bhslr->bhslr_expstatsn) != conn->conn_statsn) { + login_send_error(request, 0x02, 0x05); + log_errx(1, "received Login PDU with wrong ExpStatSN: " + "is %d, should be %d", ntohl(bhslr->bhslr_expstatsn), + conn->conn_statsn); + } + conn->conn_cmdsn = ntohl(bhslr->bhslr_cmdsn); + + return (request); +} + +static struct pdu * +login_new_response(struct pdu *request) +{ + struct pdu *response; + struct connection *conn; + struct iscsi_bhs_login_request *bhslr; + struct iscsi_bhs_login_response *bhslr2; + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + conn = request->pdu_connection; + + response = pdu_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_RESPONSE; + login_set_csg(response, BHSLR_STAGE_SECURITY_NEGOTIATION); + memcpy(bhslr2->bhslr_isid, + bhslr->bhslr_isid, sizeof(bhslr2->bhslr_isid)); + bhslr2->bhslr_initiator_task_tag = bhslr->bhslr_initiator_task_tag; + bhslr2->bhslr_statsn = htonl(conn->conn_statsn++); + bhslr2->bhslr_expcmdsn = htonl(conn->conn_cmdsn); + bhslr2->bhslr_maxcmdsn = htonl(conn->conn_cmdsn); + + return (response); +} + +static void +login_send_error(struct pdu *request, char class, char detail) +{ + struct pdu *response; + struct iscsi_bhs_login_response *bhslr2; + + log_debugx("sending Login Response PDU with failure class 0x%x/0x%x; " + "see next line for reason", class, detail); + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_status_class = class; + bhslr2->bhslr_status_detail = detail; + + pdu_send(response); + pdu_delete(response); +} + +static int +login_list_contains(const char *list, const char *what) +{ + char *tofree, *str, *token; + + tofree = str = checked_strdup(list); + + while ((token = strsep(&str, ",")) != NULL) { + if (strcmp(token, what) == 0) { + free(tofree); + return (1); + } + } + free(tofree); + return (0); +} + +static int +login_list_prefers(const char *list, + const char *choice1, const char *choice2) +{ + char *tofree, *str, *token; + + tofree = str = checked_strdup(list); + + while ((token = strsep(&str, ",")) != NULL) { + if (strcmp(token, choice1) == 0) { + free(tofree); + return (1); + } + if (strcmp(token, choice2) == 0) { + free(tofree); + return (2); + } + } + free(tofree); + return (-1); +} + +static int +login_hex2int(const char hex) +{ + switch (hex) { + case '0': + return (0x00); + case '1': + return (0x01); + case '2': + return (0x02); + case '3': + return (0x03); + case '4': + return (0x04); + case '5': + return (0x05); + case '6': + return (0x06); + case '7': + return (0x07); + case '8': + return (0x08); + case '9': + return (0x09); + case 'a': + case 'A': + return (0x0a); + case 'b': + case 'B': + return (0x0b); + case 'c': + case 'C': + return (0x0c); + case 'd': + case 'D': + return (0x0d); + case 'e': + case 'E': + return (0x0e); + case 'f': + case 'F': + return (0x0f); + default: + return (-1); + } +} + +/* + * XXX: Review this _carefully_. + */ +static int +login_hex2bin(const char *hex, char **binp, size_t *bin_lenp) +{ + int i, hex_len, nibble; + bool lo = true; /* As opposed to 'hi'. */ + char *bin; + size_t bin_off, bin_len; + + if (strncasecmp(hex, "0x", strlen("0x")) != 0) { + log_warnx("malformed variable, should start with \"0x\""); + return (-1); + } + + hex += strlen("0x"); + hex_len = strlen(hex); + if (hex_len < 1) { + log_warnx("malformed variable; doesn't contain anything " + "but \"0x\""); + return (-1); + } + + bin_len = hex_len / 2 + hex_len % 2; + bin = calloc(bin_len, 1); + if (bin == NULL) + log_err(1, "calloc"); + + bin_off = bin_len - 1; + for (i = hex_len - 1; i >= 0; i--) { + nibble = login_hex2int(hex[i]); + if (nibble < 0) { + log_warnx("malformed variable, invalid char \"%c\"", + hex[i]); + return (-1); + } + + assert(bin_off < bin_len); + if (lo) { + bin[bin_off] = nibble; + lo = false; + } else { + bin[bin_off] |= nibble << 4; + bin_off--; + lo = true; + } + } + + *binp = bin; + *bin_lenp = bin_len; + return (0); +} + +static char * +login_bin2hex(const char *bin, size_t bin_len) +{ + unsigned char *hex, *tmp, ch; + size_t hex_len; + size_t i; + + hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ + hex = malloc(hex_len); + if (hex == NULL) + log_err(1, "malloc"); + + tmp = hex; + tmp += sprintf(tmp, "0x"); + for (i = 0; i < bin_len; i++) { + ch = bin[i]; + tmp += sprintf(tmp, "%02x", ch); + } + + return (hex); +} + +static void +login_compute_md5(const char id, const char *secret, + const void *challenge, size_t challenge_len, void *response, + size_t response_len) +{ + MD5_CTX ctx; + int rv; + + assert(response_len == MD5_DIGEST_LENGTH); + + MD5_Init(&ctx); + MD5_Update(&ctx, &id, sizeof(id)); + MD5_Update(&ctx, secret, strlen(secret)); + MD5_Update(&ctx, challenge, challenge_len); + rv = MD5_Final(response, &ctx); + if (rv != 1) + log_errx(1, "MD5_Final"); +} + +#define LOGIN_CHALLENGE_LEN 1024 + +static struct pdu * +login_receive_chap_a(struct connection *conn) +{ + struct pdu *request; + struct keys *request_keys; + const char *chap_a; + + request = login_receive(conn, false); + request_keys = keys_new(); + keys_load(request_keys, request); + + chap_a = keys_find(request_keys, "CHAP_A"); + if (chap_a == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU without CHAP_A"); + } + if (login_list_contains(chap_a, "5") == 0) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "received CHAP Login PDU with unsupported CHAP_A " + "\"%s\"", chap_a); + } + keys_delete(request_keys); + + return (request); +} + +static void +login_send_chap_c(struct pdu *request, const unsigned char id, + const void *challenge, const size_t challenge_len) +{ + struct pdu *response; + struct keys *response_keys; + char *chap_c, chap_i[4]; + + chap_c = login_bin2hex(challenge, challenge_len); + snprintf(chap_i, sizeof(chap_i), "%d", id); + + response = login_new_response(request); + response_keys = keys_new(); + keys_add(response_keys, "CHAP_A", "5"); + keys_add(response_keys, "CHAP_I", chap_i); + keys_add(response_keys, "CHAP_C", chap_c); + free(chap_c); + keys_save(response_keys, response); + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); +} + +static struct pdu * +login_receive_chap_r(struct connection *conn, + struct auth_group *ag, const unsigned char id, const void *challenge, + const size_t challenge_len, const struct auth **cap) +{ + struct pdu *request; + struct keys *request_keys; + const char *chap_n, *chap_r; + char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH]; + size_t response_bin_len; + const struct auth *auth; + int error; + + request = login_receive(conn, false); + request_keys = keys_new(); + keys_load(request_keys, request); + + chap_n = keys_find(request_keys, "CHAP_N"); + if (chap_n == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU without CHAP_N"); + } + chap_r = keys_find(request_keys, "CHAP_R"); + if (chap_r == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU without CHAP_R"); + } + error = login_hex2bin(chap_r, &response_bin, &response_bin_len); + if (error != 0) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU with malformed CHAP_R"); + } + + /* + * Verify the response. + */ + assert(ag->ag_type == AG_TYPE_CHAP || + ag->ag_type == AG_TYPE_CHAP_MUTUAL); + auth = auth_find(ag, chap_n); + if (auth == NULL) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "received CHAP Login with invalid user \"%s\"", + chap_n); + } + + assert(auth->a_secret != NULL); + assert(strlen(auth->a_secret) > 0); + login_compute_md5(id, auth->a_secret, challenge, + challenge_len, expected_response_bin, + sizeof(expected_response_bin)); + + if (memcmp(response_bin, expected_response_bin, + sizeof(expected_response_bin)) != 0) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "CHAP authentication failed for user \"%s\"", + auth->a_user); + } + + keys_delete(request_keys); + free(response_bin); + + *cap = auth; + return (request); +} + +static void +login_send_chap_success(struct pdu *request, + const struct auth *auth) +{ + struct pdu *response; + struct keys *request_keys, *response_keys; + struct iscsi_bhs_login_response *bhslr2; + const char *chap_i, *chap_c; + char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH]; + size_t challenge_len; + unsigned char id; + int error; + + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; + login_set_nsg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + + /* + * Actually, one more thing: mutual authentication. + */ + request_keys = keys_new(); + keys_load(request_keys, request); + chap_i = keys_find(request_keys, "CHAP_I"); + chap_c = keys_find(request_keys, "CHAP_C"); + if (chap_i != NULL || chap_c != NULL) { + if (chap_i == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "initiator requested target " + "authentication, but didn't send CHAP_I"); + } + if (chap_c == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "initiator requested target " + "authentication, but didn't send CHAP_C"); + } + if (auth->a_auth_group->ag_type != AG_TYPE_CHAP_MUTUAL) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "initiator requests target authentication " + "for user \"%s\", but mutual user/secret " + "is not set", auth->a_user); + } + + id = strtoul(chap_i, NULL, 10); + error = login_hex2bin(chap_c, &challenge, &challenge_len); + if (error != 0) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received CHAP Login PDU with malformed " + "CHAP_C"); + } + + log_debugx("performing mutual authentication as user \"%s\"", + auth->a_mutual_user); + login_compute_md5(id, auth->a_mutual_secret, challenge, + challenge_len, response_bin, sizeof(response_bin)); + + chap_r = login_bin2hex(response_bin, + sizeof(response_bin)); + response_keys = keys_new(); + keys_add(response_keys, "CHAP_N", auth->a_mutual_user); + keys_add(response_keys, "CHAP_R", chap_r); + free(chap_r); + keys_save(response_keys, response); + keys_delete(response_keys); + } else { + log_debugx("initiator did not request target authentication"); + } + + keys_delete(request_keys); + pdu_send(response); + pdu_delete(response); +} + +static void +login_chap(struct connection *conn, struct auth_group *ag) +{ + const struct auth *auth; + struct pdu *request; + char challenge_bin[LOGIN_CHALLENGE_LEN]; + unsigned char id; + int rv; + + /* + * Receive CHAP_A PDU. + */ + log_debugx("beginning CHAP authentication; waiting for CHAP_A"); + request = login_receive_chap_a(conn); + + /* + * Generate the challenge. + */ + rv = RAND_bytes(challenge_bin, sizeof(challenge_bin)); + if (rv != 1) { + login_send_error(request, 0x03, 0x02); + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + rv = RAND_bytes(&id, sizeof(id)); + if (rv != 1) { + login_send_error(request, 0x03, 0x02); + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + + /* + * Send the challenge. + */ + log_debugx("sending CHAP_C, binary challenge size is %zd bytes", + sizeof(challenge_bin)); + login_send_chap_c(request, id, challenge_bin, + sizeof(challenge_bin)); + pdu_delete(request); + + /* + * Receive CHAP_N/CHAP_R PDU and authenticate. + */ + log_debugx("waiting for CHAP_N/CHAP_R"); + request = login_receive_chap_r(conn, ag, id, challenge_bin, + sizeof(challenge_bin), &auth); + + /* + * Yay, authentication succeeded! + */ + log_debugx("authentication succeeded for user \"%s\"; " + "transitioning to Negotiation Phase", auth->a_user); + login_send_chap_success(request, auth); + pdu_delete(request); +} + +static void +login_negotiate_key(struct pdu *request, const char *name, + const char *value, bool skipped_security, struct keys *response_keys) +{ + int which, tmp; + struct connection *conn; + + conn = request->pdu_connection; + + if (strcmp(name, "InitiatorName") == 0) { + if (!skipped_security) + log_errx(1, "initiator resent InitiatorName"); + } else if (strcmp(name, "SessionType") == 0) { + if (!skipped_security) + log_errx(1, "initiator resent SessionType"); + } else if (strcmp(name, "TargetName") == 0) { + if (!skipped_security) + log_errx(1, "initiator resent TargetName"); + } else if (strcmp(name, "InitiatorAlias") == 0) { + if (conn->conn_initiator_alias != NULL) + free(conn->conn_initiator_alias); + conn->conn_initiator_alias = checked_strdup(value); + } else if (strcmp(value, "Irrelevant") == 0) { + /* Ignore. */ + } else if (strcmp(name, "HeaderDigest") == 0) { + /* + * We don't handle digests for discovery sessions. + */ + if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { + log_debugx("discovery session; digests disabled"); + keys_add(response_keys, name, "None"); + return; + } + + which = login_list_prefers(value, "CRC32C", "None"); + switch (which) { + case 1: + log_debugx("initiator prefers CRC32C " + "for header digest; we'll use it"); + conn->conn_header_digest = CONN_DIGEST_CRC32C; + keys_add(response_keys, name, "CRC32C"); + break; + case 2: + log_debugx("initiator prefers not to do " + "header digest; we'll comply"); + keys_add(response_keys, name, "None"); + break; + default: + log_warnx("initiator sent unrecognized " + "HeaderDigest value \"%s\"; will use None", value); + keys_add(response_keys, name, "None"); + break; + } + } else if (strcmp(name, "DataDigest") == 0) { + if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { + log_debugx("discovery session; digests disabled"); + keys_add(response_keys, name, "None"); + return; + } + + which = login_list_prefers(value, "CRC32C", "None"); + switch (which) { + case 1: + log_debugx("initiator prefers CRC32C " + "for data digest; we'll use it"); + conn->conn_data_digest = CONN_DIGEST_CRC32C; + keys_add(response_keys, name, "CRC32C"); + break; + case 2: + log_debugx("initiator prefers not to do " + "data digest; we'll comply"); + keys_add(response_keys, name, "None"); + break; + default: + log_warnx("initiator sent unrecognized " + "DataDigest value \"%s\"; will use None", value); + keys_add(response_keys, name, "None"); + break; + } + } else if (strcmp(name, "MaxConnections") == 0) { + keys_add(response_keys, name, "1"); + } else if (strcmp(name, "InitialR2T") == 0) { + keys_add(response_keys, name, "Yes"); + } else if (strcmp(name, "ImmediateData") == 0) { + if (conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY) { + log_debugx("discovery session; ImmediateData irrelevant"); + keys_add(response_keys, name, "Irrelevant"); + } else { + if (strcmp(value, "Yes") == 0) { + conn->conn_immediate_data = true; + keys_add(response_keys, name, "Yes"); + } else { + conn->conn_immediate_data = false; + keys_add(response_keys, name, "No"); + } + } + } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received invalid " + "MaxRecvDataSegmentLength"); + } + if (tmp > MAX_DATA_SEGMENT_LENGTH) { + log_debugx("capping MaxDataSegmentLength from %d to %d", + tmp, MAX_DATA_SEGMENT_LENGTH); + tmp = MAX_DATA_SEGMENT_LENGTH; + } + conn->conn_max_data_segment_length = tmp; + keys_add_int(response_keys, name, tmp); + } else if (strcmp(name, "MaxBurstLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received invalid MaxBurstLength"); + } + if (tmp > MAX_BURST_LENGTH) { + log_debugx("capping MaxBurstLength from %d to %d", + tmp, MAX_BURST_LENGTH); + tmp = MAX_BURST_LENGTH; + } + conn->conn_max_burst_length = tmp; + keys_add(response_keys, name, value); + } else if (strcmp(name, "FirstBurstLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received invalid " + "FirstBurstLength"); + } + if (tmp > MAX_DATA_SEGMENT_LENGTH) { + log_debugx("capping FirstBurstLength from %d to %d", + tmp, MAX_DATA_SEGMENT_LENGTH); + tmp = MAX_DATA_SEGMENT_LENGTH; + } + /* + * We don't pass the value to the kernel; it only enforces + * hardcoded limit anyway. + */ + keys_add_int(response_keys, name, tmp); + } else if (strcmp(name, "DefaultTime2Wait") == 0) { + keys_add(response_keys, name, value); + } else if (strcmp(name, "DefaultTime2Retain") == 0) { + keys_add(response_keys, name, "0"); + } else if (strcmp(name, "MaxOutstandingR2T") == 0) { + keys_add(response_keys, name, "1"); + } else if (strcmp(name, "DataPDUInOrder") == 0) { + keys_add(response_keys, name, "Yes"); + } else if (strcmp(name, "DataSequenceInOrder") == 0) { + keys_add(response_keys, name, "Yes"); + } else if (strcmp(name, "ErrorRecoveryLevel") == 0) { + keys_add(response_keys, name, "0"); + } else if (strcmp(name, "OFMarker") == 0) { + keys_add(response_keys, name, "No"); + } else if (strcmp(name, "IFMarker") == 0) { + keys_add(response_keys, name, "No"); + } else { + log_debugx("unknown key \"%s\"; responding " + "with NotUnderstood", name); + keys_add(response_keys, name, "NotUnderstood"); + } +} + +static void +login_negotiate(struct connection *conn, struct pdu *request) +{ + struct pdu *response; + struct iscsi_bhs_login_response *bhslr2; + struct keys *request_keys, *response_keys; + int i; + bool skipped_security; + + if (request == NULL) { + log_debugx("beginning parameter negotiation; " + "waiting for Login PDU"); + request = login_receive(conn, false); + skipped_security = false; + } else + skipped_security = true; + + request_keys = keys_new(); + keys_load(request_keys, request); + + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; + bhslr2->bhslr_tsih = htons(0xbadd); + login_set_csg(response, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + login_set_nsg(response, BHSLR_STAGE_FULL_FEATURE_PHASE); + response_keys = keys_new(); + for (i = 0; i < KEYS_MAX; i++) { + if (request_keys->keys_names[i] == NULL) + break; + + login_negotiate_key(request, request_keys->keys_names[i], + request_keys->keys_values[i], skipped_security, + response_keys); + } + + log_debugx("parameter negotiation done; " + "transitioning to Full Feature Phase"); + + keys_save(response_keys, response); + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); + pdu_delete(request); + keys_delete(request_keys); +} + +void +login(struct connection *conn) +{ + struct pdu *request, *response; + struct iscsi_bhs_login_request *bhslr; + struct iscsi_bhs_login_response *bhslr2; + struct keys *request_keys, *response_keys; + struct auth_group *ag; + const char *initiator_name, *initiator_alias, *session_type, + *target_name, *auth_method; + char *portal_group_tag; + int rv; + + /* + * Handle the initial Login Request - figure out required authentication + * method and either transition to the next phase, if no authentication + * is required, or call appropriate authentication code. + */ + log_debugx("beginning Login Phase; waiting for Login PDU"); + request = login_receive(conn, true); + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + if (bhslr->bhslr_tsih != 0) { + login_send_error(request, 0x02, 0x0a); + log_errx(1, "received Login PDU with non-zero TSIH"); + } + + /* + * XXX: Implement the C flag some day. + */ + request_keys = keys_new(); + keys_load(request_keys, request); + + assert(conn->conn_initiator_name == NULL); + initiator_name = keys_find(request_keys, "InitiatorName"); + if (initiator_name == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received Login PDU without InitiatorName"); + } + if (valid_iscsi_name(initiator_name) == false) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received Login PDU with invalid InitiatorName"); + } + conn->conn_initiator_name = checked_strdup(initiator_name); + log_set_peer_name(conn->conn_initiator_name); + /* + * XXX: This doesn't work (does nothing) because of Capsicum. + */ + setproctitle("%s (%s)", conn->conn_initiator_addr, conn->conn_initiator_name); + + initiator_alias = keys_find(request_keys, "InitiatorAlias"); + if (initiator_alias != NULL) + conn->conn_initiator_alias = checked_strdup(initiator_alias); + + assert(conn->conn_session_type == CONN_SESSION_TYPE_NONE); + session_type = keys_find(request_keys, "SessionType"); + if (session_type != NULL) { + if (strcmp(session_type, "Normal") == 0) { + conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; + } else if (strcmp(session_type, "Discovery") == 0) { + conn->conn_session_type = CONN_SESSION_TYPE_DISCOVERY; + } else { + login_send_error(request, 0x02, 0x00); + log_errx(1, "received Login PDU with invalid " + "SessionType \"%s\"", session_type); + } + } else + conn->conn_session_type = CONN_SESSION_TYPE_NORMAL; + + assert(conn->conn_target == NULL); + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + target_name = keys_find(request_keys, "TargetName"); + if (target_name == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received Login PDU without TargetName"); + } + + conn->conn_target = + target_find(conn->conn_portal->p_portal_group->pg_conf, + target_name); + if (conn->conn_target == NULL) { + login_send_error(request, 0x02, 0x03); + log_errx(1, "requested target \"%s\" not found", + target_name); + } + } + + /* + * At this point we know what kind of authentication we need. + */ + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + ag = conn->conn_target->t_auth_group; + if (ag->ag_name != NULL) { + log_debugx("initiator requests to connect " + "to target \"%s\"; auth-group \"%s\"", + conn->conn_target->t_iqn, + conn->conn_target->t_auth_group->ag_name); + } else { + log_debugx("initiator requests to connect " + "to target \"%s\"", conn->conn_target->t_iqn); + } + } else { + assert(conn->conn_session_type == CONN_SESSION_TYPE_DISCOVERY); + ag = conn->conn_portal->p_portal_group->pg_discovery_auth_group; + if (ag->ag_name != NULL) { + log_debugx("initiator requests " + "discovery session; auth-group \"%s\"", ag->ag_name); + } else { + log_debugx("initiator requests discovery session"); + } + } + + /* + * Let's see if the initiator intends to do any kind of authentication + * at all. + */ + if (login_csg(request) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) { + if (ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "initiator skipped the authentication, " + "but authentication is required"); + } + + keys_delete(request_keys); + + log_debugx("initiator skipped the authentication, " + "and we don't need it; proceeding with negotiation"); + login_negotiate(conn, request); + return; + } + + if (ag->ag_type == AG_TYPE_NO_AUTHENTICATION) { + /* + * Initiator might want to to authenticate, + * but we don't need it. + */ + log_debugx("authentication not required; " + "transitioning to parameter negotiation"); + + if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) + log_warnx("initiator did not set the \"T\" flag; " + "transitioning anyway"); + + response = login_new_response(request); + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + bhslr2->bhslr_flags |= BHSLR_FLAGS_TRANSIT; + login_set_nsg(response, + BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + response_keys = keys_new(); + /* + * Required by Linux initiator. + */ + auth_method = keys_find(request_keys, "AuthMethod"); + if (auth_method != NULL && + login_list_contains(auth_method, "None")) + keys_add(response_keys, "AuthMethod", "None"); + + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + if (conn->conn_target->t_alias != NULL) + keys_add(response_keys, + "TargetAlias", conn->conn_target->t_alias); + rv = asprintf(&portal_group_tag, "%d", + conn->conn_portal->p_portal_group->pg_tag); + if (rv <= 0) + log_err(1, "asprintf"); + keys_add(response_keys, + "TargetPortalGroupTag", portal_group_tag); + free(portal_group_tag); + } + keys_save(response_keys, response); + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); + pdu_delete(request); + keys_delete(request_keys); + + login_negotiate(conn, NULL); + return; + } + + log_debugx("CHAP authentication required"); + + auth_method = keys_find(request_keys, "AuthMethod"); + if (auth_method == NULL) { + login_send_error(request, 0x02, 0x07); + log_errx(1, "received Login PDU without AuthMethod"); + } + /* + * XXX: This should be Reject, not just a login failure (5.3.2). + */ + if (login_list_contains(auth_method, "CHAP") == 0) { + login_send_error(request, 0x02, 0x01); + log_errx(1, "initiator requests unsupported AuthMethod \"%s\" " + "instead of \"CHAP\"", auth_method); + } + + response = login_new_response(request); + + response_keys = keys_new(); + keys_add(response_keys, "AuthMethod", "CHAP"); + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { + rv = asprintf(&portal_group_tag, "%d", + conn->conn_portal->p_portal_group->pg_tag); + if (rv <= 0) + log_err(1, "asprintf"); + keys_add(response_keys, + "TargetPortalGroupTag", portal_group_tag); + free(portal_group_tag); + if (conn->conn_target->t_alias != NULL) + keys_add(response_keys, + "TargetAlias", conn->conn_target->t_alias); + } + keys_save(response_keys, response); + + pdu_send(response); + pdu_delete(response); + keys_delete(response_keys); + pdu_delete(request); + keys_delete(request_keys); + + login_chap(conn, ag); + + login_negotiate(conn, NULL); +} diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y new file mode 100644 index 0000000..333e964 --- /dev/null +++ b/usr.sbin/ctld/parse.y @@ -0,0 +1,621 @@ +%{ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/queue.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <assert.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "ctld.h" + +extern FILE *yyin; +extern char *yytext; +extern int lineno; + +static struct conf *conf = NULL; +static struct auth_group *auth_group = NULL; +static struct portal_group *portal_group = NULL; +static struct target *target = NULL; +static struct lun *lun = NULL; + +extern void yyerror(const char *); +extern int yylex(void); +extern void yyrestart(FILE *); + +%} + +%token ALIAS AUTH_GROUP BACKEND BLOCKSIZE CHAP CHAP_MUTUAL CLOSING_BRACKET +%token DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP LISTEN LISTEN_ISER LUN MAXPROC NUM +%token OPENING_BRACKET OPTION PATH PIDFILE PORTAL_GROUP SERIAL SIZE STR TARGET +%token TIMEOUT + +%union +{ + uint64_t num; + char *str; +} + +%token <num> NUM +%token <str> STR + +%% + +statements: + | + statements statement + ; + +statement: + debug_statement + | + timeout_statement + | + maxproc_statement + | + pidfile_statement + | + auth_group_definition + | + portal_group_definition + | + target_statement + ; + +debug_statement: DEBUG NUM + { + conf->conf_debug = $2; + } + ; + +timeout_statement: TIMEOUT NUM + { + conf->conf_timeout = $2; + } + ; + +maxproc_statement: MAXPROC NUM + { + conf->conf_maxproc = $2; + } + ; + +pidfile_statement: PIDFILE STR + { + if (conf->conf_pidfile_path != NULL) { + log_warnx("pidfile specified more than once"); + free($2); + return (1); + } + conf->conf_pidfile_path = $2; + } + ; + +auth_group_definition: AUTH_GROUP auth_group_name + OPENING_BRACKET auth_group_entries CLOSING_BRACKET + { + auth_group = NULL; + } + ; + +auth_group_name: STR + { + auth_group = auth_group_new(conf, $1); + free($1); + if (auth_group == NULL) + return (1); + } + ; + +auth_group_entries: + | + auth_group_entries auth_group_entry + ; + +auth_group_entry: + auth_group_chap + | + auth_group_chap_mutual + ; + +auth_group_chap: CHAP STR STR + { + const struct auth *ca; + + ca = auth_new_chap(auth_group, $2, $3); + free($2); + free($3); + if (ca == NULL) + return (1); + } + ; + +auth_group_chap_mutual: CHAP_MUTUAL STR STR STR STR + { + const struct auth *ca; + + ca = auth_new_chap_mutual(auth_group, $2, $3, $4, $5); + free($2); + free($3); + free($4); + free($5); + if (ca == NULL) + return (1); + } + ; + +portal_group_definition: PORTAL_GROUP portal_group_name + OPENING_BRACKET portal_group_entries CLOSING_BRACKET + { + portal_group = NULL; + } + ; + +portal_group_name: STR + { + portal_group = portal_group_new(conf, $1); + free($1); + if (portal_group == NULL) + return (1); + } + ; + +portal_group_entries: + | + portal_group_entries portal_group_entry + ; + +portal_group_entry: + portal_group_discovery_auth_group + | + portal_group_listen + | + portal_group_listen_iser + ; + +portal_group_discovery_auth_group: DISCOVERY_AUTH_GROUP STR + { + if (portal_group->pg_discovery_auth_group != NULL) { + log_warnx("discovery-auth-group for portal-group " + "\"%s\" specified more than once", + portal_group->pg_name); + return (1); + } + portal_group->pg_discovery_auth_group = + auth_group_find(conf, $2); + if (portal_group->pg_discovery_auth_group == NULL) { + log_warnx("unknown discovery-auth-group \"%s\" " + "for portal-group \"%s\"", + $2, portal_group->pg_name); + return (1); + } + free($2); + } + ; + +portal_group_listen: LISTEN STR + { + int error; + + error = portal_group_add_listen(portal_group, $2, false); + free($2); + if (error != 0) + return (1); + } + ; + +portal_group_listen_iser: LISTEN_ISER STR + { + int error; + + error = portal_group_add_listen(portal_group, $2, true); + free($2); + if (error != 0) + return (1); + } + ; + +target_statement: TARGET target_iqn + OPENING_BRACKET target_entries CLOSING_BRACKET + { + target = NULL; + } + ; + +target_iqn: STR + { + target = target_new(conf, $1); + free($1); + if (target == NULL) + return (1); + } + ; + +target_entries: + | + target_entries target_entry + ; + +target_entry: + alias_statement + | + auth_group_statement + | + chap_statement + | + chap_mutual_statement + | + portal_group_statement + | + lun_statement + ; + +alias_statement: ALIAS STR + { + if (target->t_alias != NULL) { + log_warnx("alias for target \"%s\" " + "specified more than once", target->t_iqn); + return (1); + } + target->t_alias = $2; + } + ; + +auth_group_statement: AUTH_GROUP STR + { + if (target->t_auth_group != NULL) { + if (target->t_auth_group->ag_name != NULL) + log_warnx("auth-group for target \"%s\" " + "specified more than once", target->t_iqn); + else + log_warnx("cannot mix auth-grup with explicit " + "authorisations for target \"%s\"", + target->t_iqn); + return (1); + } + target->t_auth_group = auth_group_find(conf, $2); + if (target->t_auth_group == NULL) { + log_warnx("unknown auth-group \"%s\" for target " + "\"%s\"", $2, target->t_iqn); + return (1); + } + free($2); + } + ; + +chap_statement: CHAP STR STR + { + const struct auth *ca; + + if (target->t_auth_group != NULL) { + if (target->t_auth_group->ag_name != NULL) { + log_warnx("cannot mix auth-grup with explicit " + "authorisations for target \"%s\"", + target->t_iqn); + free($2); + free($3); + return (1); + } + } else { + target->t_auth_group = auth_group_new(conf, NULL); + if (target->t_auth_group == NULL) { + free($2); + free($3); + return (1); + } + target->t_auth_group->ag_target = target; + } + ca = auth_new_chap(target->t_auth_group, $2, $3); + free($2); + free($3); + if (ca == NULL) + return (1); + } + ; + +chap_mutual_statement: CHAP_MUTUAL STR STR STR STR + { + const struct auth *ca; + + if (target->t_auth_group != NULL) { + if (target->t_auth_group->ag_name != NULL) { + log_warnx("cannot mix auth-grup with explicit " + "authorisations for target \"%s\"", + target->t_iqn); + free($2); + free($3); + free($4); + free($5); + return (1); + } + } else { + target->t_auth_group = auth_group_new(conf, NULL); + if (target->t_auth_group == NULL) { + free($2); + free($3); + free($4); + free($5); + return (1); + } + target->t_auth_group->ag_target = target; + } + ca = auth_new_chap_mutual(target->t_auth_group, + $2, $3, $4, $5); + free($2); + free($3); + free($4); + free($5); + if (ca == NULL) + return (1); + } + ; + +portal_group_statement: PORTAL_GROUP STR + { + if (target->t_portal_group != NULL) { + log_warnx("portal-group for target \"%s\" " + "specified more than once", target->t_iqn); + free($2); + return (1); + } + target->t_portal_group = portal_group_find(conf, $2); + if (target->t_portal_group == NULL) { + log_warnx("unknown portal-group \"%s\" for target " + "\"%s\"", $2, target->t_iqn); + free($2); + return (1); + } + free($2); + } + ; + +lun_statement: LUN lun_number + OPENING_BRACKET lun_statement_entries CLOSING_BRACKET + { + lun = NULL; + } + ; + +lun_number: NUM + { + lun = lun_new(target, $1); + if (lun == NULL) + return (1); + } + ; + +lun_statement_entries: + | + lun_statement_entries lun_statement_entry + ; + +lun_statement_entry: + backend_statement + | + blocksize_statement + | + device_id_statement + | + option_statement + | + path_statement + | + serial_statement + | + size_statement + ; + +backend_statement: BACKEND STR + { + if (lun->l_backend != NULL) { + log_warnx("backend for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + free($2); + return (1); + } + lun_set_backend(lun, $2); + free($2); + } + ; + +blocksize_statement: BLOCKSIZE NUM + { + if (lun->l_blocksize != 0) { + log_warnx("blocksize for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + return (1); + } + lun_set_blocksize(lun, $2); + } + ; + +device_id_statement: DEVICE_ID STR + { + if (lun->l_device_id != NULL) { + log_warnx("device_id for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + free($2); + return (1); + } + lun_set_device_id(lun, $2); + free($2); + } + ; + +option_statement: OPTION STR STR + { + struct lun_option *clo; + + clo = lun_option_new(lun, $2, $3); + free($2); + free($3); + if (clo == NULL) + return (1); + } + ; + +path_statement: PATH STR + { + if (lun->l_path != NULL) { + log_warnx("path for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + free($2); + return (1); + } + lun_set_path(lun, $2); + free($2); + } + ; + +serial_statement: SERIAL STR + { + if (lun->l_serial != NULL) { + log_warnx("serial for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + free($2); + return (1); + } + lun_set_serial(lun, $2); + free($2); + } + ; + +size_statement: SIZE NUM + { + if (lun->l_size != 0) { + log_warnx("size for lun %d, target \"%s\" " + "specified more than once", + lun->l_lun, target->t_iqn); + return (1); + } + lun_set_size(lun, $2); + } + ; +%% + +void +yyerror(const char *str) +{ + + log_warnx("error in configuration file at line %d near '%s': %s", + lineno, yytext, str); +} + +static void +check_perms(const char *path) +{ + struct stat sb; + int error; + + error = stat(path, &sb); + if (error != 0) { + log_warn("stat"); + return; + } + if (sb.st_mode & S_IWOTH) { + log_warnx("%s is world-writable", path); + } else if (sb.st_mode & S_IROTH) { + log_warnx("%s is world-readable", path); + } else if (sb.st_mode & S_IXOTH) { + /* + * Ok, this one doesn't matter, but still do it, + * just for consistency. + */ + log_warnx("%s is world-executable", path); + } + + /* + * XXX: Should we also check for owner != 0? + */ +} + +struct conf * +conf_new_from_file(const char *path) +{ + struct auth_group *ag; + struct portal_group *pg; + int error; + + log_debugx("obtaining configuration from %s", path); + + conf = conf_new(); + + ag = auth_group_new(conf, "no-authentication"); + ag->ag_type = AG_TYPE_NO_AUTHENTICATION; + + /* + * Here, the type doesn't really matter, as the group doesn't contain + * any entries and thus will always deny access. + */ + ag = auth_group_new(conf, "no-access"); + ag->ag_type = AG_TYPE_CHAP; + + pg = portal_group_new(conf, "default"); + portal_group_add_listen(pg, "0.0.0.0:3260", false); + portal_group_add_listen(pg, "[::]:3260", false); + + yyin = fopen(path, "r"); + if (yyin == NULL) { + log_warn("unable to open configuration file %s", path); + conf_delete(conf); + return (NULL); + } + check_perms(path); + lineno = 0; + yyrestart(yyin); + error = yyparse(); + auth_group = NULL; + portal_group = NULL; + target = NULL; + lun = NULL; + fclose(yyin); + if (error != 0) { + conf_delete(conf); + return (NULL); + } + + error = conf_verify(conf); + if (error != 0) { + conf_delete(conf); + return (NULL); + } + + return (conf); +} diff --git a/usr.sbin/ctld/pdu.c b/usr.sbin/ctld/pdu.c new file mode 100644 index 0000000..eb8921e --- /dev/null +++ b/usr.sbin/ctld/pdu.c @@ -0,0 +1,246 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/uio.h> +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "ctld.h" +#include "iscsi_proto.h" + +#ifdef ICL_KERNEL_PROXY +#include <sys/ioctl.h> +#endif + +static int +pdu_ahs_length(const struct pdu *pdu) +{ + + return (pdu->pdu_bhs->bhs_total_ahs_len * 4); +} + +static int +pdu_data_segment_length(const struct pdu *pdu) +{ + uint32_t len = 0; + + len += pdu->pdu_bhs->bhs_data_segment_len[0]; + len <<= 8; + len += pdu->pdu_bhs->bhs_data_segment_len[1]; + len <<= 8; + len += pdu->pdu_bhs->bhs_data_segment_len[2]; + + return (len); +} + +static void +pdu_set_data_segment_length(struct pdu *pdu, uint32_t len) +{ + + pdu->pdu_bhs->bhs_data_segment_len[2] = len; + pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8; + pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16; +} + +struct pdu * +pdu_new(struct connection *conn) +{ + struct pdu *pdu; + + pdu = calloc(sizeof(*pdu), 1); + if (pdu == NULL) + log_err(1, "calloc"); + + pdu->pdu_bhs = calloc(sizeof(*pdu->pdu_bhs), 1); + if (pdu->pdu_bhs == NULL) + log_err(1, "calloc"); + + pdu->pdu_connection = conn; + + return (pdu); +} + +struct pdu * +pdu_new_response(struct pdu *request) +{ + + return (pdu_new(request->pdu_connection)); +} + +#ifdef ICL_KERNEL_PROXY + +void +pdu_receive(struct pdu *pdu) +{ + size_t len; + + kernel_receive(pdu); + + len = pdu_ahs_length(pdu); + if (len > 0) + log_errx(1, "protocol error: non-empty AHS"); + + len = pdu_data_segment_length(pdu); + assert(len <= MAX_DATA_SEGMENT_LENGTH); + pdu->pdu_data_len = len; +} + +void +pdu_send(struct pdu *pdu) +{ + + pdu_set_data_segment_length(pdu, pdu->pdu_data_len); + kernel_send(pdu); +} + +#else /* !ICL_KERNEL_PROXY */ + +static size_t +pdu_padding(const struct pdu *pdu) +{ + + if ((pdu->pdu_data_len % 4) != 0) + return (4 - (pdu->pdu_data_len % 4)); + + return (0); +} + +static void +pdu_read(int fd, char *data, size_t len) +{ + ssize_t ret; + + while (len > 0) { + ret = read(fd, data, len); + if (ret < 0) { + if (timed_out()) + log_errx(1, "exiting due to timeout"); + log_err(1, "read"); + } else if (ret == 0) + log_errx(1, "read: connection lost"); + len -= ret; + data += ret; + } +} + +void +pdu_receive(struct pdu *pdu) +{ + size_t len, padding; + char dummy[4]; + + pdu_read(pdu->pdu_connection->conn_socket, + (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); + + len = pdu_ahs_length(pdu); + if (len > 0) + log_errx(1, "protocol error: non-empty AHS"); + + len = pdu_data_segment_length(pdu); + if (len > 0) { + if (len > MAX_DATA_SEGMENT_LENGTH) { + log_errx(1, "protocol error: received PDU " + "with DataSegmentLength exceeding %d", + MAX_DATA_SEGMENT_LENGTH); + } + + pdu->pdu_data_len = len; + pdu->pdu_data = malloc(len); + if (pdu->pdu_data == NULL) + log_err(1, "malloc"); + + pdu_read(pdu->pdu_connection->conn_socket, + (char *)pdu->pdu_data, pdu->pdu_data_len); + + padding = pdu_padding(pdu); + if (padding != 0) { + assert(padding < sizeof(dummy)); + pdu_read(pdu->pdu_connection->conn_socket, + (char *)dummy, padding); + } + } +} + +void +pdu_send(struct pdu *pdu) +{ + ssize_t ret, total_len; + size_t padding; + uint32_t zero = 0; + struct iovec iov[3]; + int iovcnt; + + pdu_set_data_segment_length(pdu, pdu->pdu_data_len); + iov[0].iov_base = pdu->pdu_bhs; + iov[0].iov_len = sizeof(*pdu->pdu_bhs); + total_len = iov[0].iov_len; + iovcnt = 1; + + if (pdu->pdu_data_len > 0) { + iov[1].iov_base = pdu->pdu_data; + iov[1].iov_len = pdu->pdu_data_len; + total_len += iov[1].iov_len; + iovcnt = 2; + + padding = pdu_padding(pdu); + if (padding > 0) { + assert(padding < sizeof(zero)); + iov[2].iov_base = &zero; + iov[2].iov_len = padding; + total_len += iov[2].iov_len; + iovcnt = 3; + } + } + + ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt); + if (ret < 0) { + if (timed_out()) + log_errx(1, "exiting due to timeout"); + log_err(1, "writev"); + } + if (ret != total_len) + log_errx(1, "short write"); +} + +#endif /* !ICL_KERNEL_PROXY */ + +void +pdu_delete(struct pdu *pdu) +{ + + free(pdu->pdu_data); + free(pdu->pdu_bhs); + free(pdu); +} diff --git a/usr.sbin/ctld/token.l b/usr.sbin/ctld/token.l new file mode 100644 index 0000000..f968d97 --- /dev/null +++ b/usr.sbin/ctld/token.l @@ -0,0 +1,85 @@ +%{ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> + +#include "ctld.h" +#include "y.tab.h" + +int lineno; + +#define YY_DECL int yylex(void) +extern int yylex(void); + +%} + +%option noinput +%option nounput + +%% +alias { return ALIAS; } +auth-group { return AUTH_GROUP; } +backend { return BACKEND; } +blocksize { return BLOCKSIZE; } +chap { return CHAP; } +chap-mutual { return CHAP_MUTUAL; } +debug { return DEBUG; } +device-id { return DEVICE_ID; } +discovery-auth-group { return DISCOVERY_AUTH_GROUP; } +listen { return LISTEN; } +listen-iser { return LISTEN_ISER; } +lun { return LUN; } +maxproc { return MAXPROC; } +option { return OPTION; } +path { return PATH; } +pidfile { return PIDFILE; } +portal-group { return PORTAL_GROUP; } +serial { return SERIAL; } +size { return SIZE; } +target { return TARGET; } +timeout { return TIMEOUT; } +[0-9]+[kKmMgGtTpPeE]? { if (expand_number(yytext, &yylval.num) == 0) + return NUM; + else + return STR; + } +\"[^"]+\" { yylval.str = strndup(yytext + 1, + strlen(yytext) - 2); return STR; } +[a-zA-Z0-9\.\-_/\:\[\]]+ { yylval.str = strdup(yytext); return STR; } +\{ { return OPENING_BRACKET; } +\} { return CLOSING_BRACKET; } +#.*$ /* ignore comments */; +\n { lineno++; } +[ \t]+ /* ignore whitespace */; +%% diff --git a/usr.sbin/daemon/daemon.8 b/usr.sbin/daemon/daemon.8 index 14b134a..87cfd98 100644 --- a/usr.sbin/daemon/daemon.8 +++ b/usr.sbin/daemon/daemon.8 @@ -26,7 +26,7 @@ .\" .\" $FreeBSD$ .\" -.Dd June 4, 2012 +.Dd September 13, 2013 .Dt DAEMON 8 .Os .Sh NAME @@ -35,7 +35,8 @@ .Sh SYNOPSIS .Nm .Op Fl cfr -.Op Fl p Ar pidfile +.Op Fl p Ar child_pidfile +.Op Fl P Ar supervisor_pidfile .Op Fl u Ar user .Ar command arguments ... .Sh DESCRIPTION @@ -53,19 +54,39 @@ Change the current working directory to the root .It Fl f Redirect standard input, standard output and standard error to .Pa /dev/null . -.It Fl p Ar file +.It Fl p Ar child_pidfile Write the ID of the created process into the -.Ar file +.Ar child_pidfile using the .Xr pidfile 3 functionality. The program is executed in a spawned child process while the .Nm waits until it terminates to keep the -.Ar file +.Ar child_pidfile locked and removes it after the process exits. The -.Ar file +.Ar child_pidfile +owner is the user who runs the +.Nm +regardless of whether the +.Fl u +option is used or not. +.It Fl P Ar supervisor_pidfile +Write the ID of the +.Nm +process into the +.Ar supervisor_pidfile +using the +.Xr pidfile 3 +functionality. +The program is executed in a spawned child process while the +.Nm +waits until it terminates to keep the +.Ar supervisor_pidfile +locked and removes it after the process exits. +The +.Ar supervisor_pidfile owner is the user who runs the .Nm regardless of whether the @@ -79,27 +100,46 @@ Requires adequate superuser privileges. .El .Pp If the -.Fl p +.Fl p , +.Fl P or .Fl r option is specified the program is executed in a spawned child process. The .Nm -waits until it terminates to keep the pid file locked and removes it +waits until it terminates to keep the pid file(s) locked and removes them after the process exits or restarts the program. In this case if the monitoring .Nm receives software termination signal (SIGTERM) it forwards it to the spawned process. -Normally it will cause the child to exit followed by the termination -of the supervising process after removing the pidfile. +Normally it will cause the child to exit, remove the pidfile(s) +and then terminate. +.Pp +The +.Fl P +option is useful combined with the +.Fl r +option as +.Ar supervisor_pidfile +contains the ID of the supervisor +not the child. This is especially important if you use +.Fl r +in an rc script as the +.Fl p +option will give you the child's ID to signal when you attempt to +stop the service, causing +.Nm +to restart the child. .Sh EXIT STATUS The .Nm utility exits 1 if an error is returned by the .Xr daemon 3 -library routine, 2 if the -.Ar pidfile +library routine, 2 if +.Ar child_pidfile +or +.Ar supervisor_pidfile is requested, but cannot be opened, 3 if process is already running (pidfile exists and is locked), otherwise 0. diff --git a/usr.sbin/daemon/daemon.c b/usr.sbin/daemon/daemon.c index b012c88..322c0d4 100644 --- a/usr.sbin/daemon/daemon.c +++ b/usr.sbin/daemon/daemon.c @@ -53,16 +53,16 @@ static void usage(void); int main(int argc, char *argv[]) { - struct pidfh *pfh = NULL; + struct pidfh *ppfh, *pfh; sigset_t mask, oldmask; - int ch, nochdir, noclose, restart; - const char *pidfile, *user; + int ch, nochdir, noclose, restart, serrno; + const char *pidfile, *ppidfile, *user; pid_t otherpid, pid; nochdir = noclose = 1; restart = 0; - pidfile = user = NULL; - while ((ch = getopt(argc, argv, "cfp:ru:")) != -1) { + ppidfile = pidfile = user = NULL; + while ((ch = getopt(argc, argv, "cfp:P:ru:")) != -1) { switch (ch) { case 'c': nochdir = 0; @@ -73,6 +73,9 @@ main(int argc, char *argv[]) case 'p': pidfile = optarg; break; + case 'P': + ppidfile = optarg; + break; case 'r': restart = 1; break; @@ -89,7 +92,7 @@ main(int argc, char *argv[]) if (argc == 0) usage(); - pfh = NULL; + ppfh = pfh = NULL; /* * Try to open the pidfile before calling daemon(3), * to be able to report the error intelligently @@ -104,9 +107,27 @@ main(int argc, char *argv[]) err(2, "pidfile ``%s''", pidfile); } } + /* Do the same for actual daemon process. */ + if (ppidfile != NULL) { + ppfh = pidfile_open(ppidfile, 0600, &otherpid); + if (ppfh == NULL) { + serrno = errno; + pidfile_remove(pfh); + errno = serrno; + if (errno == EEXIST) { + errx(3, "process already running, pid: %d", + otherpid); + } + err(2, "ppidfile ``%s''", ppidfile); + } + } - if (daemon(nochdir, noclose) == -1) - err(1, NULL); + if (daemon(nochdir, noclose) == -1) { + warn("daemon"); + goto exit; + } + /* Write out parent pidfile if needed. */ + pidfile_write(ppfh); /* * If the pidfile or restart option is specified the daemon @@ -123,22 +144,28 @@ main(int argc, char *argv[]) * Restore default action for SIGTERM in case the * parent process decided to ignore it. */ - if (signal(SIGTERM, SIG_DFL) == SIG_ERR) - err(1, "signal"); + if (signal(SIGTERM, SIG_DFL) == SIG_ERR) { + warn("signal"); + goto exit; + } /* * Because SIGCHLD is ignored by default, setup dummy handler * for it, so we can mask it. */ - if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR) - err(1, "signal"); + if (signal(SIGCHLD, dummy_sighandler) == SIG_ERR) { + warn("signal"); + goto exit; + } /* * Block interesting signals. */ sigemptyset(&mask); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGCHLD); - if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1) - err(1, "sigprocmask"); + if (sigprocmask(SIG_SETMASK, &mask, &oldmask) == -1) { + warn("sigprocmask"); + goto exit; + } /* * Try to protect against pageout kill. Ignore the * error, madvise(2) will fail only if a process does @@ -152,8 +179,8 @@ restart: */ pid = fork(); if (pid == -1) { - pidfile_remove(pfh); - err(1, "fork"); + warn("fork"); + goto exit; } } if (pid <= 0) { @@ -176,13 +203,16 @@ restart: */ err(1, "%s", argv[0]); } + setproctitle("%s[%d]", argv[0], pid); if (wait_child(pid, &mask) == 0 && restart) { sleep(1); goto restart; } +exit: pidfile_remove(pfh); - exit(0); /* Exit status does not matter. */ + pidfile_remove(ppfh); + exit(1); /* If daemon(3) succeeded exit status does not matter. */ } static void @@ -240,7 +270,7 @@ static void usage(void) { (void)fprintf(stderr, - "usage: daemon [-cfr] [-p pidfile] [-u user] command " - "arguments ...\n"); + "usage: daemon [-cfr] [-p child_pidfile] [-P supervisor_pidfile] " + "[-u user]\n command arguments ...\n"); exit(1); } diff --git a/usr.sbin/ddns-confgen/Makefile b/usr.sbin/ddns-confgen/Makefile deleted file mode 100644 index a33bea3..0000000 --- a/usr.sbin/ddns-confgen/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/confgen - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= ddns-confgen - -.PATH: ${SRCDIR}/unix -SRCS+= os.c - -.PATH: ${SRCDIR} -SRCS+= ddns-confgen.c keygen.c util.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include -I${LIB_BIND_DIR} - -WARNS?= 3 - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -MAN= ddns-confgen.8 - -MANFILTER= sed -e 's@fI/etc\\fR.*@fI/etc/namedb\\fR@' \ - -e '/^sysconfdir$$/d' \ - -e '/was specified as when BIND was built)/d' - -.include <bsd.prog.mk> diff --git a/usr.sbin/ddns-confgen/Makefile.depend b/usr.sbin/ddns-confgen/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/ddns-confgen/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/dnssec-dsfromkey/Makefile b/usr.sbin/dnssec-dsfromkey/Makefile deleted file mode 100644 index 0b57eb5..0000000 --- a/usr.sbin/dnssec-dsfromkey/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/dnssec - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= dnssec-dsfromkey - -.PATH: ${SRCDIR} -SRCS+= dnssec-dsfromkey.c dnssectool.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 3 - -MAN= dnssec-dsfromkey.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/dnssec-dsfromkey/Makefile.depend b/usr.sbin/dnssec-dsfromkey/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/dnssec-dsfromkey/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/dnssec-keyfromlabel/Makefile b/usr.sbin/dnssec-keyfromlabel/Makefile deleted file mode 100644 index fd6a341..0000000 --- a/usr.sbin/dnssec-keyfromlabel/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/dnssec - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= dnssec-keyfromlabel - -.PATH: ${SRCDIR} -SRCS+= dnssec-keyfromlabel.c dnssectool.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 3 - -MAN= dnssec-keyfromlabel.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/dnssec-keyfromlabel/Makefile.depend b/usr.sbin/dnssec-keyfromlabel/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/dnssec-keyfromlabel/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/dnssec-keygen/Makefile b/usr.sbin/dnssec-keygen/Makefile deleted file mode 100644 index 1bdf0c0..0000000 --- a/usr.sbin/dnssec-keygen/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/dnssec - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= dnssec-keygen - -.PATH: ${SRCDIR} -SRCS+= dnssec-keygen.c dnssectool.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 3 - -MAN= dnssec-keygen.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/dnssec-keygen/Makefile.depend b/usr.sbin/dnssec-keygen/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/dnssec-keygen/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/dnssec-revoke/Makefile b/usr.sbin/dnssec-revoke/Makefile deleted file mode 100644 index 7889d96..0000000 --- a/usr.sbin/dnssec-revoke/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/dnssec - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= dnssec-revoke - -.PATH: ${SRCDIR} -SRCS+= dnssec-revoke.c dnssectool.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 3 - -MAN= dnssec-revoke.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/dnssec-revoke/Makefile.depend b/usr.sbin/dnssec-revoke/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/dnssec-revoke/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/dnssec-settime/Makefile b/usr.sbin/dnssec-settime/Makefile deleted file mode 100644 index 4ab7fb5..0000000 --- a/usr.sbin/dnssec-settime/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/dnssec - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= dnssec-settime - -.PATH: ${SRCDIR} -SRCS+= dnssec-settime.c dnssectool.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 3 - -MAN= dnssec-settime.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/dnssec-settime/Makefile.depend b/usr.sbin/dnssec-settime/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/dnssec-settime/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/dnssec-signzone/Makefile b/usr.sbin/dnssec-signzone/Makefile deleted file mode 100644 index 7f56b8d..0000000 --- a/usr.sbin/dnssec-signzone/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/dnssec - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= dnssec-signzone - -.PATH: ${SRCDIR} -SRCS+= dnssec-signzone.c dnssectool.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 2 - -MAN= dnssec-signzone.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/dnssec-signzone/Makefile.depend b/usr.sbin/dnssec-signzone/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/dnssec-signzone/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/dnssec-verify/Makefile b/usr.sbin/dnssec-verify/Makefile deleted file mode 100644 index 1d1dc9d..0000000 --- a/usr.sbin/dnssec-verify/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/dnssec - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= dnssec-verify - -.PATH: ${SRCDIR} -SRCS+= dnssec-verify.c dnssectool.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include -CFLAGS+= -fsigned-char - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 3 - -MAN= dnssec-verify.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/genrandom/Makefile b/usr.sbin/genrandom/Makefile deleted file mode 100644 index 975d84b..0000000 --- a/usr.sbin/genrandom/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/tools - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= genrandom - -.PATH: ${SRCDIR} -SRCS+= genrandom.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 3 - -MAN= genrandom.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/genrandom/Makefile.depend b/usr.sbin/genrandom/Makefile.depend deleted file mode 100644 index a1ed6b6..0000000 --- a/usr.sbin/genrandom/Makefile.depend +++ /dev/null @@ -1,26 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/gpioctl/gpioctl.8 b/usr.sbin/gpioctl/gpioctl.8 index 32ae409..c1abb5e 100644 --- a/usr.sbin/gpioctl/gpioctl.8 +++ b/usr.sbin/gpioctl/gpioctl.8 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 25, 2011 +.Dd August 13, 2013 .Dt GPIOCTL 1 .Os .Sh NAME @@ -36,20 +36,20 @@ .Sh SYNOPSIS .Nm .Cm -l -.Fl f Ar ctldev +.Op Fl f Ar ctldev .Op Fl v .Nm .Cm -t -.Fl f Ar ctldev +.Op Fl f Ar ctldev .Ar pin .Nm .Cm -c -.Fl f Ar ctldev +.Op Fl f Ar ctldev .Ar pin .Ar flag .Op flag ... .Nm -.Cm -f Ar ctldev +.Op Cm -f Ar ctldev .Ar pin .Ar [0|1] .Sh DESCRIPTION @@ -83,6 +83,8 @@ Inverted output pin .El .It Fl f Ar ctldev GPIO controller device to use +If not specified, defaults to +.Pa /dev/gpioc0 .It Fl l list available pins .It Fl t Ar pin @@ -105,6 +107,8 @@ Configure pin 12 to be input pin .Pp gpioctl -f /dev/gpioc0 -c 12 IN .El +.Sh SEE ALSO +.Xr gpio 4 .Sh HISTORY The .Nm diff --git a/usr.sbin/gpioctl/gpioctl.c b/usr.sbin/gpioctl/gpioctl.c index 9d76e1d..e4708a4 100644 --- a/usr.sbin/gpioctl/gpioctl.c +++ b/usr.sbin/gpioctl/gpioctl.c @@ -30,6 +30,7 @@ __FBSDID("$FreeBSD$"); #include <fcntl.h> #include <getopt.h> +#include <paths.h> #include <stdio.h> #include <stdarg.h> #include <stdlib.h> @@ -63,10 +64,10 @@ static void usage(void) { fprintf(stderr, "Usage:\n"); - fprintf(stderr, "\tgpioctl -f ctldev -l [-v]\n"); - fprintf(stderr, "\tgpioctl -f ctldev -t pin\n"); - fprintf(stderr, "\tgpioctl -f ctldev -c pin flag ...\n"); - fprintf(stderr, "\tgpioctl -f ctldev pin [0|1]\n"); + fprintf(stderr, "\tgpioctl [-f ctldev] -l [-v]\n"); + fprintf(stderr, "\tgpioctl [-f ctldev] -t pin\n"); + fprintf(stderr, "\tgpioctl [-f ctldev] -c pin flag ...\n"); + fprintf(stderr, "\tgpioctl [-f ctldev] pin [0|1]\n"); exit(1); } @@ -185,6 +186,7 @@ main(int argc, char **argv) int i; struct gpio_pin pin; struct gpio_req req; + char defctlfile[] = _PATH_DEVGPIOC "0"; char *ctlfile = NULL; int pinn, pinv, fd, ch; int flags, flag, ok; @@ -226,7 +228,7 @@ main(int argc, char **argv) printf("%d/%s\n", i, argv[i]); if (ctlfile == NULL) - fail("No gpioctl device provided\n"); + ctlfile = defctlfile; fd = open(ctlfile, O_RDONLY); if (fd < 0) { diff --git a/usr.sbin/isc-hmac-fixup/Makefile b/usr.sbin/isc-hmac-fixup/Makefile deleted file mode 100644 index 1313855..0000000 --- a/usr.sbin/isc-hmac-fixup/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/tools - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= isc-hmac-fixup - -.PATH: ${SRCDIR} -SRCS+= isc-hmac-fixup.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 3 - -MAN= isc-hmac-fixup.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/isc-hmac-fixup/Makefile.depend b/usr.sbin/isc-hmac-fixup/Makefile.depend deleted file mode 100644 index a1ed6b6..0000000 --- a/usr.sbin/isc-hmac-fixup/Makefile.depend +++ /dev/null @@ -1,26 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/iscsid/Makefile b/usr.sbin/iscsid/Makefile new file mode 100644 index 0000000..468b284 --- /dev/null +++ b/usr.sbin/iscsid/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD$ + +PROG= iscsid +SRCS= discovery.c iscsid.c keys.c log.c login.c pdu.c +CFLAGS+= -I${.CURDIR} +CFLAGS+= -I${.CURDIR}/../../sys/cam +CFLAGS+= -I${.CURDIR}/../../sys/dev/iscsi +#CFLAGS+= -DICL_KERNEL_PROXY +MAN= iscsid.8 + +DPADD= ${LIBUTIL} +LDADD= -lcrypto -lssl -lutil + +WARNS= 6 + +.include <bsd.prog.mk> diff --git a/usr.sbin/iscsid/discovery.c b/usr.sbin/iscsid/discovery.c new file mode 100644 index 0000000..b5731de --- /dev/null +++ b/usr.sbin/iscsid/discovery.c @@ -0,0 +1,223 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> + +#include "iscsid.h" +#include "iscsi_proto.h" + +static struct pdu * +text_receive(struct connection *conn) +{ + struct pdu *response; + struct iscsi_bhs_text_response *bhstr; + + response = pdu_new(conn); + pdu_receive(response); + if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_TEXT_RESPONSE) + log_errx(1, "protocol error: received invalid opcode 0x%x", + response->pdu_bhs->bhs_opcode); + bhstr = (struct iscsi_bhs_text_response *)response->pdu_bhs; +#if 0 + if ((bhstr->bhstr_flags & BHSTR_FLAGS_FINAL) == 0) + log_errx(1, "received Text PDU without the \"F\" flag"); +#endif + /* + * XXX: Implement the C flag some day. + */ + if ((bhstr->bhstr_flags & BHSTR_FLAGS_CONTINUE) != 0) + log_errx(1, "received Text PDU with unsupported \"C\" flag"); + if (response->pdu_data_len == 0) + log_errx(1, "received Text PDU with empty data segment"); + if (ntohl(bhstr->bhstr_statsn) != conn->conn_statsn + 1) { + log_errx(1, "received Text PDU with wrong StatSN: " + "is %d, should be %d", ntohl(bhstr->bhstr_statsn), + conn->conn_statsn + 1); + } + conn->conn_statsn = ntohl(bhstr->bhstr_statsn); + + return (response); +} + +static struct pdu * +text_new_request(struct connection *conn) +{ + struct pdu *request; + struct iscsi_bhs_text_request *bhstr; + + request = pdu_new(conn); + bhstr = (struct iscsi_bhs_text_request *)request->pdu_bhs; + bhstr->bhstr_opcode = ISCSI_BHS_OPCODE_TEXT_REQUEST | + ISCSI_BHS_OPCODE_IMMEDIATE; + bhstr->bhstr_flags = BHSTR_FLAGS_FINAL; + bhstr->bhstr_initiator_task_tag = 0; + bhstr->bhstr_target_transfer_tag = 0xffffffff; + + bhstr->bhstr_initiator_task_tag = 0; /* XXX */ + bhstr->bhstr_cmdsn = 0; /* XXX */ + bhstr->bhstr_expstatsn = htonl(conn->conn_statsn + 1); + + return (request); +} + +static struct pdu * +logout_receive(struct connection *conn) +{ + struct pdu *response; + struct iscsi_bhs_logout_response *bhslr; + + response = pdu_new(conn); + pdu_receive(response); + if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_LOGOUT_RESPONSE) + log_errx(1, "protocol error: received invalid opcode 0x%x", + response->pdu_bhs->bhs_opcode); + bhslr = (struct iscsi_bhs_logout_response *)response->pdu_bhs; + if (ntohs(bhslr->bhslr_response) != BHSLR_RESPONSE_CLOSED_SUCCESSFULLY) + log_warnx("received Logout Response with reason %d", + ntohs(bhslr->bhslr_response)); + if (ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) { + log_errx(1, "received Logout PDU with wrong StatSN: " + "is %d, should be %d", ntohl(bhslr->bhslr_statsn), + conn->conn_statsn + 1); + } + conn->conn_statsn = ntohl(bhslr->bhslr_statsn); + + return (response); +} + +static struct pdu * +logout_new_request(struct connection *conn) +{ + struct pdu *request; + struct iscsi_bhs_logout_request *bhslr; + + request = pdu_new(conn); + bhslr = (struct iscsi_bhs_logout_request *)request->pdu_bhs; + bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGOUT_REQUEST | + ISCSI_BHS_OPCODE_IMMEDIATE; + bhslr->bhslr_reason = BHSLR_REASON_CLOSE_SESSION; + bhslr->bhslr_reason |= 0x80; + bhslr->bhslr_initiator_task_tag = 0; /* XXX */ + bhslr->bhslr_cmdsn = 0; /* XXX */ + bhslr->bhslr_expstatsn = htonl(conn->conn_statsn + 1); + + return (request); +} + +static void +kernel_add(const struct connection *conn, const char *target) +{ + struct iscsi_session_add isa; + int error; + + memset(&isa, 0, sizeof(isa)); + memcpy(&isa.isa_conf, &conn->conn_conf, sizeof(isa.isa_conf)); + strlcpy(isa.isa_conf.isc_target, target, + sizeof(isa.isa_conf.isc_target)); + isa.isa_conf.isc_discovery = 0; + error = ioctl(conn->conn_iscsi_fd, ISCSISADD, &isa); + if (error != 0) + log_warn("failed to add %s: ISCSISADD", target); +} + +static void +kernel_remove(const struct connection *conn) +{ + struct iscsi_session_remove isr; + int error; + + memset(&isr, 0, sizeof(isr)); + isr.isr_session_id = conn->conn_session_id; + error = ioctl(conn->conn_iscsi_fd, ISCSISREMOVE, &isr); + if (error != 0) + log_warn("ISCSISREMOVE"); +} + +void +discovery(struct connection *conn) +{ + struct pdu *request, *response; + struct keys *request_keys, *response_keys; + int i; + + log_debugx("beginning discovery session"); + request = text_new_request(conn); + request_keys = keys_new(); + keys_add(request_keys, "SendTargets", "All"); + keys_save(request_keys, request); + keys_delete(request_keys); + request_keys = NULL; + pdu_send(request); + pdu_delete(request); + request = NULL; + + log_debugx("waiting for Text Response"); + response = text_receive(conn); + response_keys = keys_new(); + keys_load(response_keys, response); + for (i = 0; i < KEYS_MAX; i++) { + if (response_keys->keys_names[i] == NULL) + break; + + if (strcmp(response_keys->keys_names[i], "TargetName") != 0) + continue; + + log_debugx("adding target %s", response_keys->keys_values[i]); + /* + * XXX: Validate the target name? + */ + kernel_add(conn, response_keys->keys_values[i]); + } + keys_delete(response_keys); + pdu_delete(response); + + log_debugx("removing temporary discovery session"); + kernel_remove(conn); + + log_debugx("discovery done; logging out"); + request = logout_new_request(conn); + pdu_send(request); + pdu_delete(request); + request = NULL; + + log_debugx("waiting for Logout Response"); + response = logout_receive(conn); + pdu_delete(response); + + log_debugx("discovery session done"); +} diff --git a/usr.sbin/iscsid/iscsid.8 b/usr.sbin/iscsid/iscsid.8 new file mode 100644 index 0000000..89c7c25 --- /dev/null +++ b/usr.sbin/iscsid/iscsid.8 @@ -0,0 +1,113 @@ +.\" Copyright (c) 2012 The FreeBSD Foundation +.\" All rights reserved. +.\" +.\" This software was developed by Edward Tomasz Napierala under sponsorship +.\" from the FreeBSD Foundation. +.\" +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS 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 AUTHORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd September 20, 2012 +.Dt ISCSID 8 +.Os +.Sh NAME +.Nm iscsid +.Nd iSCSI initiator daemon +.Sh SYNOPSIS +.Nm +.Op Fl P Ar pidfile +.Op Fl d +.Op Fl l Ar loglevel +.Op Fl m Ar maxproc +.Op Fl t Ar seconds +.Sh DESCRIPTION +The +.Nm +daemon is responsible for performing the Login Phase of iSCSI connections, +as well as performing SendTargets discovery. +.Pp +Upon startup, the +.Nm +daemon opens the iSCSI initiator device file and waits for kernel requests. +It doesn't use any configuration file; all the information it needs it gets +from the kernel. +.Pp +When the +.Nm +damon is not running, already established iSCSI connections continue +to work. +However, establishing new connections, or recovering existing ones in case +of connection error, is not possible. +.Pp +The following options are available: +.Bl -tag -width ".Fl P Ar pidfile" +.It Fl P Ar pidfile +Specify alternative location of a file where main process PID will be stored. +The default location is +.Pa /var/run/iscsid.pid . +.It Fl d +Debug mode. +The server sends verbose debug output to standard error, and does not +put itself in the background. +The server will also not fork and will exit after processing one connection. +This option is only intended for debugging the initiator. +.It Fl l Ar loglevel +Specifies debug level. +The default is 0. +.It Fl m Ar maxproc +Specifies limit for concurrently running child processes handling +connections. +The default is 30. +Setting it to 0 disables the limit. +.It Fl t Ar seconds +Specifies timeout for login session, after which the connection +will be forcibly terminated. +The default is 60. +Setting it to 0 disables the timeout. +.El +.Sh FILES +.Bl -tag -width ".Pa /var/run/iscsid.pid" -compact +.It Pa /dev/iscsi +The iSCSI initiator device file. +.It Pa /var/run/iscsid.pid +The default location of the +.Nm +PID file. +.El +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr iscsictl 8 +.Sh HISTORY +The +.Nm +command appeared in +.Fx 10.0 . +.Sh AUTHORS +The +.Nm +was developed by +.An Edward Tomasz Napierala Aq trasz@FreeBSD.org +under sponsorship from the FreeBSD Foundation. diff --git a/usr.sbin/iscsid/iscsid.c b/usr.sbin/iscsid/iscsid.c new file mode 100644 index 0000000..c22d7fd --- /dev/null +++ b/usr.sbin/iscsid/iscsid.c @@ -0,0 +1,569 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/linker.h> +#include <sys/socket.h> +#include <sys/capability.h> +#include <sys/wait.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <netdb.h> +#include <signal.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <libutil.h> + +#include "iscsid.h" + +static volatile bool sigalrm_received = false; + +static int nchildren = 0; + +static void +usage(void) +{ + + fprintf(stderr, "usage: iscsid [-P pidfile][-d][-m maxproc][-t timeout]\n"); + exit(1); +} + +char * +checked_strdup(const char *s) +{ + char *c; + + c = strdup(s); + if (c == NULL) + log_err(1, "strdup"); + return (c); +} + +static void +resolve_addr(const struct connection *conn, const char *address, + struct addrinfo **ai, bool initiator_side) +{ + struct addrinfo hints; + char *arg, *addr, *ch; + const char *port; + int error, colons = 0; + + arg = checked_strdup(address); + + if (arg[0] == '\0') { + fail(conn, "empty address"); + log_errx(1, "empty address"); + } + if (arg[0] == '[') { + /* + * IPv6 address in square brackets, perhaps with port. + */ + arg++; + addr = strsep(&arg, "]"); + if (arg == NULL) { + fail(conn, "malformed address"); + log_errx(1, "malformed address %s", address); + } + if (arg[0] == '\0') { + port = NULL; + } else if (arg[0] == ':') { + port = arg + 1; + } else { + fail(conn, "malformed address"); + log_errx(1, "malformed address %s", address); + } + } else { + /* + * Either IPv6 address without brackets - and without + * a port - or IPv4 address. Just count the colons. + */ + for (ch = arg; *ch != '\0'; ch++) { + if (*ch == ':') + colons++; + } + if (colons > 1) { + addr = arg; + port = NULL; + } else { + addr = strsep(&arg, ":"); + if (arg == NULL) + port = NULL; + else + port = arg; + } + } + + if (port == NULL && !initiator_side) + port = "3260"; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV; + if (initiator_side) + hints.ai_flags |= AI_PASSIVE; + + error = getaddrinfo(addr, port, &hints, ai); + if (error != 0) { + fail(conn, gai_strerror(error)); + log_errx(1, "getaddrinfo for %s failed: %s", + address, gai_strerror(error)); + } +} + +static struct connection * +connection_new(unsigned int session_id, const struct iscsi_session_conf *conf, + int iscsi_fd) +{ + struct connection *conn; + struct addrinfo *from_ai, *to_ai; + const char *from_addr, *to_addr; +#ifdef ICL_KERNEL_PROXY + struct iscsi_daemon_connect idc; +#endif + int error; + + conn = calloc(1, sizeof(*conn)); + if (conn == NULL) + log_err(1, "calloc"); + + /* + * Default values, from RFC 3720, section 12. + */ + conn->conn_header_digest = CONN_DIGEST_NONE; + conn->conn_data_digest = CONN_DIGEST_NONE; + conn->conn_initial_r2t = true; + conn->conn_immediate_data = true; + conn->conn_max_data_segment_length = 8192; + conn->conn_max_burst_length = 262144; + conn->conn_first_burst_length = 65536; + + conn->conn_session_id = session_id; + conn->conn_iscsi_fd = iscsi_fd; + + /* + * XXX: Should we sanitize this somehow? + */ + memcpy(&conn->conn_conf, conf, sizeof(conn->conn_conf)); + + from_addr = conn->conn_conf.isc_initiator_addr; + to_addr = conn->conn_conf.isc_target_addr; + + if (from_addr[0] != '\0') + resolve_addr(conn, from_addr, &from_ai, true); + else + from_ai = NULL; + + resolve_addr(conn, to_addr, &to_ai, false); + +#ifdef ICL_KERNEL_PROXY + + memset(&idc, 0, sizeof(idc)); + idc.idc_session_id = conn->conn_session_id; + if (conn->conn_conf.isc_iser) + idc.idc_iser = 1; + idc.idc_domain = to_ai->ai_family; + idc.idc_socktype = to_ai->ai_socktype; + idc.idc_protocol = to_ai->ai_protocol; + if (from_ai != NULL) { + idc.idc_from_addr = from_ai->ai_addr; + idc.idc_from_addrlen = from_ai->ai_addrlen; + } + idc.idc_to_addr = to_ai->ai_addr; + idc.idc_to_addrlen = to_ai->ai_addrlen; + + log_debugx("connecting to %s using ICL kernel proxy", to_addr); + error = ioctl(iscsi_fd, ISCSIDCONNECT, &idc); + if (error != 0) { + fail(conn, strerror(errno)); + log_err(1, "failed to connect to %s using ICL kernel proxy", + to_addr); + } + +#else /* !ICL_KERNEL_PROXY */ + + if (conn->conn_conf.isc_iser) { + fail(conn, "iSER not supported"); + log_errx(1, "iscsid(8) compiled without ICL_KERNEL_PROXY " + "does not support iSER"); + } + + conn->conn_socket = socket(to_ai->ai_family, to_ai->ai_socktype, + to_ai->ai_protocol); + if (conn->conn_socket < 0) { + fail(conn, strerror(errno)); + log_err(1, "failed to create socket for %s", from_addr); + } + if (from_ai != NULL) { + error = bind(conn->conn_socket, from_ai->ai_addr, + from_ai->ai_addrlen); + if (error != 0) { + fail(conn, strerror(errno)); + log_err(1, "failed to bind to %s", from_addr); + } + } + log_debugx("connecting to %s", to_addr); + error = connect(conn->conn_socket, to_ai->ai_addr, to_ai->ai_addrlen); + if (error != 0) { + fail(conn, strerror(errno)); + log_err(1, "failed to connect to %s", to_addr); + } + +#endif /* !ICL_KERNEL_PROXY */ + + return (conn); +} + +static void +handoff(struct connection *conn) +{ + struct iscsi_daemon_handoff idh; + int error; + + log_debugx("handing off connection to the kernel"); + + memset(&idh, 0, sizeof(idh)); + idh.idh_session_id = conn->conn_session_id; +#ifndef ICL_KERNEL_PROXY + idh.idh_socket = conn->conn_socket; +#endif + strlcpy(idh.idh_target_alias, conn->conn_target_alias, + sizeof(idh.idh_target_alias)); + memcpy(idh.idh_isid, conn->conn_isid, sizeof(idh.idh_isid)); + idh.idh_statsn = conn->conn_statsn; + idh.idh_header_digest = conn->conn_header_digest; + idh.idh_data_digest = conn->conn_data_digest; + idh.idh_initial_r2t = conn->conn_initial_r2t; + idh.idh_immediate_data = conn->conn_immediate_data; + idh.idh_max_data_segment_length = conn->conn_max_data_segment_length; + idh.idh_max_burst_length = conn->conn_max_burst_length; + idh.idh_first_burst_length = conn->conn_first_burst_length; + + error = ioctl(conn->conn_iscsi_fd, ISCSIDHANDOFF, &idh); + if (error != 0) + log_err(1, "ISCSIDHANDOFF"); +} + +void +fail(const struct connection *conn, const char *reason) +{ + struct iscsi_daemon_fail idf; + int error; + + memset(&idf, 0, sizeof(idf)); + idf.idf_session_id = conn->conn_session_id; + strlcpy(idf.idf_reason, reason, sizeof(idf.idf_reason)); + + error = ioctl(conn->conn_iscsi_fd, ISCSIDFAIL, &idf); + if (error != 0) + log_err(1, "ISCSIDFAIL"); +} + +/* + * XXX: I CANT INTO LATIN + */ +static void +capsicate(struct connection *conn) +{ + int error; + cap_rights_t rights; +#ifdef ICL_KERNEL_PROXY + const unsigned long cmds[] = { ISCSIDCONNECT, ISCSIDSEND, ISCSIDRECEIVE, + ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, ISCSISREMOVE }; +#else + const unsigned long cmds[] = { ISCSIDHANDOFF, ISCSIDFAIL, ISCSISADD, + ISCSISREMOVE }; +#endif + + cap_rights_init(&rights, CAP_IOCTL); + error = cap_rights_limit(conn->conn_iscsi_fd, &rights); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_rights_limit"); + + error = cap_ioctls_limit(conn->conn_iscsi_fd, cmds, + sizeof(cmds) / sizeof(cmds[0])); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_ioctls_limit"); + + error = cap_enter(); + if (error != 0 && errno != ENOSYS) + log_err(1, "cap_enter"); + + if (cap_sandboxed()) + log_debugx("Capsicum capability mode enabled"); + else + log_warnx("Capsicum capability mode not supported"); +} + +bool +timed_out(void) +{ + + return (sigalrm_received); +} + +static void +sigalrm_handler(int dummy __unused) +{ + /* + * It would be easiest to just log an error and exit. We can't + * do this, though, because log_errx() is not signal safe, since + * it calls syslog(3). Instead, set a flag checked by pdu_send() + * and pdu_receive(), to call log_errx() there. Should they fail + * to notice, we'll exit here one second later. + */ + if (sigalrm_received) { + /* + * Oh well. Just give up and quit. + */ + _exit(2); + } + + sigalrm_received = true; +} + +static void +set_timeout(int timeout) +{ + struct sigaction sa; + struct itimerval itv; + int error; + + if (timeout <= 0) { + log_debugx("session timeout disabled"); + return; + } + + bzero(&sa, sizeof(sa)); + sa.sa_handler = sigalrm_handler; + sigfillset(&sa.sa_mask); + error = sigaction(SIGALRM, &sa, NULL); + if (error != 0) + log_err(1, "sigaction"); + + /* + * First SIGALRM will arive after conf_timeout seconds. + * If we do nothing, another one will arrive a second later. + */ + bzero(&itv, sizeof(itv)); + itv.it_interval.tv_sec = 1; + itv.it_value.tv_sec = timeout; + + log_debugx("setting session timeout to %d seconds", + timeout); + error = setitimer(ITIMER_REAL, &itv, NULL); + if (error != 0) + log_err(1, "setitimer"); +} + +static void +handle_request(int iscsi_fd, const struct iscsi_daemon_request *request, int timeout) +{ + struct connection *conn; + + log_set_peer_addr(request->idr_conf.isc_target_addr); + if (request->idr_conf.isc_target[0] != '\0') { + log_set_peer_name(request->idr_conf.isc_target); + setproctitle("%s (%s)", request->idr_conf.isc_target_addr, request->idr_conf.isc_target); + } else { + setproctitle("%s", request->idr_conf.isc_target_addr); + } + + conn = connection_new(request->idr_session_id, &request->idr_conf, iscsi_fd); + set_timeout(timeout); + capsicate(conn); + login(conn); + if (conn->conn_conf.isc_discovery != 0) + discovery(conn); + else + handoff(conn); + + log_debugx("nothing more to do; exiting"); + exit (0); +} + +static int +wait_for_children(bool block) +{ + pid_t pid; + int status; + int num = 0; + + for (;;) { + /* + * If "block" is true, wait for at least one process. + */ + if (block && num == 0) + pid = wait4(-1, &status, 0, NULL); + else + pid = wait4(-1, &status, WNOHANG, NULL); + if (pid <= 0) + break; + if (WIFSIGNALED(status)) { + log_warnx("child process %d terminated with signal %d", + pid, WTERMSIG(status)); + } else if (WEXITSTATUS(status) != 0) { + log_warnx("child process %d terminated with exit status %d", + pid, WEXITSTATUS(status)); + } else { + log_debugx("child process %d terminated gracefully", pid); + } + num++; + } + + return (num); +} + +int +main(int argc, char **argv) +{ + int ch, debug = 0, error, iscsi_fd, maxproc = 30, retval, saved_errno, + timeout = 60; + bool dont_daemonize = false; + struct pidfh *pidfh; + pid_t pid, otherpid; + const char *pidfile_path = DEFAULT_PIDFILE; + struct iscsi_daemon_request request; + + while ((ch = getopt(argc, argv, "P:dl:m:t:")) != -1) { + switch (ch) { + case 'P': + pidfile_path = optarg; + break; + case 'd': + dont_daemonize = true; + debug++; + break; + case 'l': + debug = atoi(optarg); + break; + case 'm': + maxproc = atoi(optarg); + break; + case 't': + timeout = atoi(optarg); + break; + case '?': + default: + usage(); + } + } + argc -= optind; + if (argc != 0) + usage(); + + log_init(debug); + + pidfh = pidfile_open(pidfile_path, 0600, &otherpid); + if (pidfh == NULL) { + if (errno == EEXIST) + log_errx(1, "daemon already running, pid: %jd.", + (intmax_t)otherpid); + log_err(1, "cannot open or create pidfile \"%s\"", + pidfile_path); + } + + iscsi_fd = open(ISCSI_PATH, O_RDWR); + if (iscsi_fd < 0 && errno == ENOENT) { + saved_errno = errno; + retval = kldload("iscsi"); + if (retval != -1) + iscsi_fd = open(ISCSI_PATH, O_RDWR); + else + errno = saved_errno; + } + if (iscsi_fd < 0) + log_err(1, "failed to open %s", ISCSI_PATH); + + if (dont_daemonize == false) { + if (daemon(0, 0) == -1) { + log_warn("cannot daemonize"); + pidfile_remove(pidfh); + exit(1); + } + } + + pidfile_write(pidfh); + + for (;;) { + log_debugx("waiting for request from the kernel"); + + memset(&request, 0, sizeof(request)); + error = ioctl(iscsi_fd, ISCSIDWAIT, &request); + if (error != 0) { + if (errno == EINTR) { + nchildren -= wait_for_children(false); + assert(nchildren >= 0); + continue; + } + + log_err(1, "ISCSIDWAIT"); + } + + if (dont_daemonize) { + log_debugx("not forking due to -d flag; " + "will exit after servicing a single request"); + } else { + nchildren -= wait_for_children(false); + assert(nchildren >= 0); + + while (maxproc > 0 && nchildren >= maxproc) { + log_debugx("maxproc limit of %d child processes hit; " + "waiting for child process to exit", maxproc); + nchildren -= wait_for_children(true); + assert(nchildren >= 0); + } + log_debugx("incoming connection; forking child process #%d", + nchildren); + nchildren++; + + pid = fork(); + if (pid < 0) + log_err(1, "fork"); + if (pid > 0) + continue; + } + + pidfile_close(pidfh); + handle_request(iscsi_fd, &request, timeout); + } + + return (0); +} diff --git a/usr.sbin/iscsid/iscsid.h b/usr.sbin/iscsid/iscsid.h new file mode 100644 index 0000000..5af994a --- /dev/null +++ b/usr.sbin/iscsid/iscsid.h @@ -0,0 +1,120 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#ifndef ISCSID_H +#define ISCSID_H + +#include <stdbool.h> +#include <stdint.h> + +#include <iscsi_ioctl.h> + +#define DEFAULT_PIDFILE "/var/run/iscsid.pid" + +#define CONN_DIGEST_NONE 0 +#define CONN_DIGEST_CRC32C 1 + +#define CONN_MUTUAL_CHALLENGE_LEN 1024 + +struct connection { + int conn_iscsi_fd; +#ifndef ICL_KERNEL_PROXY + int conn_socket; +#endif + unsigned int conn_session_id; + struct iscsi_session_conf conn_conf; + char conn_target_alias[ISCSI_ADDR_LEN]; + uint8_t conn_isid[6]; + uint32_t conn_statsn; + int conn_header_digest; + int conn_data_digest; + bool conn_initial_r2t; + bool conn_immediate_data; + size_t conn_max_data_segment_length; + size_t conn_max_burst_length; + size_t conn_first_burst_length; + char conn_mutual_challenge[CONN_MUTUAL_CHALLENGE_LEN]; + unsigned char conn_mutual_id; +}; + +struct pdu { + struct connection *pdu_connection; + struct iscsi_bhs *pdu_bhs; + char *pdu_data; + size_t pdu_data_len; +}; + +#define KEYS_MAX 1024 + +struct keys { + char *keys_names[KEYS_MAX]; + char *keys_values[KEYS_MAX]; + char *keys_data; + size_t keys_data_len; +}; + +struct keys *keys_new(void); +void keys_delete(struct keys *key); +void keys_load(struct keys *keys, const struct pdu *pdu); +void keys_save(struct keys *keys, struct pdu *pdu); +const char *keys_find(struct keys *keys, const char *name); +int keys_find_int(struct keys *keys, const char *name); +void keys_add(struct keys *keys, + const char *name, const char *value); +void keys_add_int(struct keys *keys, + const char *name, int value); + +struct pdu *pdu_new(struct connection *ic); +struct pdu *pdu_new_response(struct pdu *request); +void pdu_receive(struct pdu *request); +void pdu_send(struct pdu *response); +void pdu_delete(struct pdu *ip); + +void login(struct connection *ic); + +void discovery(struct connection *ic); + +void log_init(int level); +void log_set_peer_name(const char *name); +void log_set_peer_addr(const char *addr); +void log_err(int, const char *, ...) + __dead2 __printf0like(2, 3); +void log_errx(int, const char *, ...) + __dead2 __printf0like(2, 3); +void log_warn(const char *, ...) __printf0like(1, 2); +void log_warnx(const char *, ...) __printflike(1, 2); +void log_debugx(const char *, ...) __printf0like(1, 2); + +char *checked_strdup(const char *); +bool timed_out(void); +void fail(const struct connection *, const char *); + +#endif /* !ISCSID_H */ diff --git a/usr.sbin/iscsid/keys.c b/usr.sbin/iscsid/keys.c new file mode 100644 index 0000000..d9055a2 --- /dev/null +++ b/usr.sbin/iscsid/keys.c @@ -0,0 +1,217 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "iscsid.h" + +struct keys * +keys_new(void) +{ + struct keys *keys; + + keys = calloc(sizeof(*keys), 1); + if (keys == NULL) + log_err(1, "calloc"); + + return (keys); +} + +void +keys_delete(struct keys *keys) +{ + + free(keys->keys_data); + free(keys); +} + +void +keys_load(struct keys *keys, const struct pdu *pdu) +{ + int i; + char *pair; + size_t pair_len; + + if (pdu->pdu_data_len == 0) + log_errx(1, "protocol error: empty data segment"); + + if (pdu->pdu_data[pdu->pdu_data_len - 1] != '\0') + log_errx(1, "protocol error: key not NULL-terminated\n"); + + assert(keys->keys_data == NULL); + keys->keys_data_len = pdu->pdu_data_len; + keys->keys_data = malloc(keys->keys_data_len); + if (keys->keys_data == NULL) + log_err(1, "malloc"); + memcpy(keys->keys_data, pdu->pdu_data, keys->keys_data_len); + + /* + * XXX: Review this carefully. + */ + pair = keys->keys_data; + for (i = 0;; i++) { + if (i >= KEYS_MAX) + log_errx(1, "too many keys received"); + + pair_len = strlen(pair); + + keys->keys_values[i] = pair; + keys->keys_names[i] = strsep(&keys->keys_values[i], "="); + if (keys->keys_names[i] == NULL || keys->keys_values[i] == NULL) + log_errx(1, "malformed keys"); + log_debugx("key received: \"%s=%s\"", + keys->keys_names[i], keys->keys_values[i]); + + pair += pair_len + 1; /* +1 to skip the terminating '\0'. */ + if (pair == keys->keys_data + keys->keys_data_len) + break; + assert(pair < keys->keys_data + keys->keys_data_len); + } +} + +void +keys_save(struct keys *keys, struct pdu *pdu) +{ + char *data; + size_t len; + int i; + + /* + * XXX: Not particularly efficient. + */ + len = 0; + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + break; + /* + * +1 for '=', +1 for '\0'. + */ + len += strlen(keys->keys_names[i]) + + strlen(keys->keys_values[i]) + 2; + } + + if (len == 0) + return; + + data = malloc(len); + if (data == NULL) + log_err(1, "malloc"); + + pdu->pdu_data = data; + pdu->pdu_data_len = len; + + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + break; + data += sprintf(data, "%s=%s", + keys->keys_names[i], keys->keys_values[i]); + data += 1; /* for '\0'. */ + } +} + +const char * +keys_find(struct keys *keys, const char *name) +{ + int i; + + /* + * Note that we don't handle duplicated key names here, + * as they are not supposed to happen in requests, and if they do, + * it's an initiator error. + */ + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) + return (NULL); + if (strcmp(keys->keys_names[i], name) == 0) + return (keys->keys_values[i]); + } + return (NULL); +} + +int +keys_find_int(struct keys *keys, const char *name) +{ + const char *str; + char *endptr; + int num; + + str = keys_find(keys, name); + if (str == NULL) + return (-1); + + num = strtoul(str, &endptr, 10); + if (*endptr != '\0') { + log_debugx("invalid numeric value \"%s\"", str); + return (-1); + } + + return (num); +} + +void +keys_add(struct keys *keys, const char *name, const char *value) +{ + int i; + + log_debugx("key to send: \"%s=%s\"", name, value); + + /* + * Note that we don't check for duplicates here, as they are perfectly + * fine in responses, e.g. the "TargetName" keys in discovery sesion + * response. + */ + for (i = 0; i < KEYS_MAX; i++) { + if (keys->keys_names[i] == NULL) { + keys->keys_names[i] = checked_strdup(name); + keys->keys_values[i] = checked_strdup(value); + return; + } + } + log_errx(1, "too many keys"); +} + +void +keys_add_int(struct keys *keys, const char *name, int value) +{ + char *str; + int ret; + + ret = asprintf(&str, "%d", value); + if (ret <= 0) + log_err(1, "asprintf"); + + keys_add(keys, name, str); + free(str); +} diff --git a/usr.sbin/iscsid/log.c b/usr.sbin/iscsid/log.c new file mode 100644 index 0000000..be3aaed --- /dev/null +++ b/usr.sbin/iscsid/log.c @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <vis.h> + +#include "iscsid.h" + +static int log_level = 0; +static char *peer_name = NULL; +static char *peer_addr = NULL; + +#define MSGBUF_LEN 1024 + +void +log_init(int level) +{ + + log_level = level; + openlog(getprogname(), LOG_NDELAY | LOG_PID, LOG_DAEMON); +} + +void +log_set_peer_name(const char *name) +{ + + /* + * XXX: Turn it into assertion? + */ + if (peer_name != NULL) + log_errx(1, "%s called twice", __func__); + if (peer_addr == NULL) + log_errx(1, "%s called before log_set_peer_addr", __func__); + + peer_name = checked_strdup(name); +} + +void +log_set_peer_addr(const char *addr) +{ + + /* + * XXX: Turn it into assertion? + */ + if (peer_addr != NULL) + log_errx(1, "%s called twice", __func__); + + peer_addr = checked_strdup(addr); +} + +static void +log_common(int priority, int log_errno, const char *fmt, va_list ap) +{ + static char msgbuf[MSGBUF_LEN]; + static char msgbuf_strvised[MSGBUF_LEN * 4 + 1]; + int ret; + + ret = vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap); + if (ret < 0) { + fprintf(stderr, "%s: snprintf failed", getprogname()); + syslog(LOG_CRIT, "snprintf failed"); + exit(1); + } + + ret = strnvis(msgbuf_strvised, sizeof(msgbuf_strvised), msgbuf, VIS_NL); + if (ret < 0) { + fprintf(stderr, "%s: strnvis failed", getprogname()); + syslog(LOG_CRIT, "strnvis failed"); + exit(1); + } + + if (log_errno == -1) { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s\n", getprogname(), + peer_addr, peer_name, msgbuf_strvised); + syslog(priority, "%s (%s): %s", + peer_addr, peer_name, msgbuf_strvised); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s\n", getprogname(), + peer_addr, msgbuf_strvised); + syslog(priority, "%s: %s", + peer_addr, msgbuf_strvised); + } else { + fprintf(stderr, "%s: %s\n", getprogname(), msgbuf_strvised); + syslog(priority, "%s", msgbuf_strvised); + } + + } else { + if (peer_name != NULL) { + fprintf(stderr, "%s: %s (%s): %s: %s\n", getprogname(), + peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + syslog(priority, "%s (%s): %s: %s", + peer_addr, peer_name, msgbuf_strvised, strerror(errno)); + } else if (peer_addr != NULL) { + fprintf(stderr, "%s: %s: %s: %s\n", getprogname(), + peer_addr, msgbuf_strvised, strerror(errno)); + syslog(priority, "%s: %s: %s", + peer_addr, msgbuf_strvised, strerror(errno)); + } else { + fprintf(stderr, "%s: %s: %s\n", getprogname(), + msgbuf_strvised, strerror(errno)); + syslog(priority, "%s: %s", + msgbuf_strvised, strerror(errno)); + } + } +} + +void +log_err(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_CRIT, errno, fmt, ap); + va_end(ap); + + exit(eval); +} + +void +log_errx(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_CRIT, -1, fmt, ap); + va_end(ap); + + exit(eval); +} + +void +log_warn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_WARNING, errno, fmt, ap); + va_end(ap); +} + +void +log_warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + log_common(LOG_WARNING, -1, fmt, ap); + va_end(ap); +} + +void +log_debugx(const char *fmt, ...) +{ + va_list ap; + + if (log_level == 0) + return; + + va_start(ap, fmt); + log_common(LOG_DEBUG, -1, fmt, ap); + va_end(ap); +} diff --git a/usr.sbin/iscsid/login.c b/usr.sbin/iscsid/login.c new file mode 100644 index 0000000..2ae36e8 --- /dev/null +++ b/usr.sbin/iscsid/login.c @@ -0,0 +1,908 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <assert.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <netinet/in.h> +#include <openssl/err.h> +#include <openssl/md5.h> +#include <openssl/rand.h> + +#include "iscsid.h" +#include "iscsi_proto.h" + +static int +login_nsg(const struct pdu *response) +{ + struct iscsi_bhs_login_response *bhslr; + + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + + return (bhslr->bhslr_flags & 0x03); +} + +static void +login_set_nsg(struct pdu *request, int nsg) +{ + struct iscsi_bhs_login_request *bhslr; + + assert(nsg == BHSLR_STAGE_SECURITY_NEGOTIATION || + nsg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || + nsg == BHSLR_STAGE_FULL_FEATURE_PHASE); + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + + bhslr->bhslr_flags &= 0xFC; + bhslr->bhslr_flags |= nsg; +} + +static void +login_set_csg(struct pdu *request, int csg) +{ + struct iscsi_bhs_login_request *bhslr; + + assert(csg == BHSLR_STAGE_SECURITY_NEGOTIATION || + csg == BHSLR_STAGE_OPERATIONAL_NEGOTIATION || + csg == BHSLR_STAGE_FULL_FEATURE_PHASE); + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + + bhslr->bhslr_flags &= 0xF3; + bhslr->bhslr_flags |= csg << 2; +} + +static const char * +login_target_error_str(int class, int detail) +{ + static char msg[128]; + + /* + * RFC 3270, 10.13.5. Status-Class and Status-Detail + */ + switch (class) { + case 0x01: + switch (detail) { + case 0x01: + return ("Target moved temporarily"); + case 0x02: + return ("Target moved permanently"); + default: + snprintf(msg, sizeof(msg), "unknown redirection; " + "Status-Class 0x%x, Status-Detail 0x%x", + class, detail); + return (msg); + } + case 0x02: + switch (detail) { + case 0x00: + return ("Initiator error"); + case 0x01: + return ("Authentication failure"); + case 0x02: + return ("Authorization failure"); + case 0x03: + return ("Not found"); + case 0x04: + return ("Target removed"); + case 0x05: + return ("Unsupported version"); + case 0x06: + return ("Too many connections"); + case 0x07: + return ("Missing parameter"); + case 0x08: + return ("Can't include in session"); + case 0x09: + return ("Session type not supported"); + case 0x0a: + return ("Session does not exist"); + case 0x0b: + return ("Invalid during login"); + default: + snprintf(msg, sizeof(msg), "unknown initiator error; " + "Status-Class 0x%x, Status-Detail 0x%x", + class, detail); + return (msg); + } + case 0x03: + switch (detail) { + case 0x00: + return ("Target error"); + case 0x01: + return ("Service unavailable"); + case 0x02: + return ("Out of resources"); + default: + snprintf(msg, sizeof(msg), "unknown target error; " + "Status-Class 0x%x, Status-Detail 0x%x", + class, detail); + return (msg); + } + default: + snprintf(msg, sizeof(msg), "unknown error; " + "Status-Class 0x%x, Status-Detail 0x%x", + class, detail); + return (msg); + } +} + +static struct pdu * +login_receive(struct connection *conn, bool initial) +{ + struct pdu *response; + struct iscsi_bhs_login_response *bhslr; + const char *errorstr; + + response = pdu_new(conn); + pdu_receive(response); + if (response->pdu_bhs->bhs_opcode != ISCSI_BHS_OPCODE_LOGIN_RESPONSE) { + log_errx(1, "protocol error: received invalid opcode 0x%x", + response->pdu_bhs->bhs_opcode); + } + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + /* + * XXX: Implement the C flag some day. + */ + if ((bhslr->bhslr_flags & BHSLR_FLAGS_CONTINUE) != 0) + log_errx(1, "received Login PDU with unsupported \"C\" flag"); + if (bhslr->bhslr_version_max != 0x00) + log_errx(1, "received Login PDU with unsupported " + "Version-max 0x%x", bhslr->bhslr_version_max); + if (bhslr->bhslr_version_active != 0x00) + log_errx(1, "received Login PDU with unsupported " + "Version-active 0x%x", bhslr->bhslr_version_active); + if (bhslr->bhslr_status_class != 0) { + errorstr = login_target_error_str(bhslr->bhslr_status_class, + bhslr->bhslr_status_detail); + fail(conn, errorstr); + log_errx(1, "target returned error: %s", errorstr); + } +#if 0 + if (response->pdu_data_len == 0) + log_errx(1, "received Login PDU with empty data segment"); +#endif + if (initial == false && + ntohl(bhslr->bhslr_statsn) != conn->conn_statsn + 1) { + /* + * It's a warning, not an error, to work around what seems + * to be bug in NetBSD iSCSI target. + */ + log_warnx("received Login PDU with wrong StatSN: " + "is %d, should be %d", ntohl(bhslr->bhslr_statsn), + conn->conn_statsn + 1); + } + conn->conn_statsn = ntohl(bhslr->bhslr_statsn); + + return (response); +} + +static struct pdu * +login_new_request(struct connection *conn) +{ + struct pdu *request; + struct iscsi_bhs_login_request *bhslr; + + request = pdu_new(conn); + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + bhslr->bhslr_opcode = ISCSI_BHS_OPCODE_LOGIN_REQUEST | + ISCSI_BHS_OPCODE_IMMEDIATE; + bhslr->bhslr_flags = BHSLR_FLAGS_TRANSIT; + login_set_csg(request, BHSLR_STAGE_SECURITY_NEGOTIATION); + login_set_nsg(request, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + memcpy(bhslr->bhslr_isid, &conn->conn_isid, sizeof(bhslr->bhslr_isid)); + bhslr->bhslr_initiator_task_tag = 0; + bhslr->bhslr_cmdsn = 0; + bhslr->bhslr_expstatsn = htonl(conn->conn_statsn + 1); + + return (request); +} + +static int +login_list_prefers(const char *list, + const char *choice1, const char *choice2) +{ + char *tofree, *str, *token; + + tofree = str = checked_strdup(list); + + while ((token = strsep(&str, ",")) != NULL) { + if (strcmp(token, choice1) == 0) { + free(tofree); + return (1); + } + if (strcmp(token, choice2) == 0) { + free(tofree); + return (2); + } + } + free(tofree); + return (-1); +} + +static int +login_hex2int(const char hex) +{ + switch (hex) { + case '0': + return (0x00); + case '1': + return (0x01); + case '2': + return (0x02); + case '3': + return (0x03); + case '4': + return (0x04); + case '5': + return (0x05); + case '6': + return (0x06); + case '7': + return (0x07); + case '8': + return (0x08); + case '9': + return (0x09); + case 'a': + case 'A': + return (0x0a); + case 'b': + case 'B': + return (0x0b); + case 'c': + case 'C': + return (0x0c); + case 'd': + case 'D': + return (0x0d); + case 'e': + case 'E': + return (0x0e); + case 'f': + case 'F': + return (0x0f); + default: + return (-1); + } +} + +/* + * XXX: Review this _carefully_. + */ +static int +login_hex2bin(const char *hex, char **binp, size_t *bin_lenp) +{ + int i, hex_len, nibble; + bool lo = true; /* As opposed to 'hi'. */ + char *bin; + size_t bin_off, bin_len; + + if (strncasecmp(hex, "0x", strlen("0x")) != 0) { + log_warnx("malformed variable, should start with \"0x\""); + return (-1); + } + + hex += strlen("0x"); + hex_len = strlen(hex); + if (hex_len < 1) { + log_warnx("malformed variable; doesn't contain anything " + "but \"0x\""); + return (-1); + } + + bin_len = hex_len / 2 + hex_len % 2; + bin = calloc(bin_len, 1); + if (bin == NULL) + log_err(1, "calloc"); + + bin_off = bin_len - 1; + for (i = hex_len - 1; i >= 0; i--) { + nibble = login_hex2int(hex[i]); + if (nibble < 0) { + log_warnx("malformed variable, invalid char \"%c\"", + hex[i]); + return (-1); + } + + assert(bin_off < bin_len); + if (lo) { + bin[bin_off] = nibble; + lo = false; + } else { + bin[bin_off] |= nibble << 4; + bin_off--; + lo = true; + } + } + + *binp = bin; + *bin_lenp = bin_len; + return (0); +} + +static char * +login_bin2hex(const char *bin, size_t bin_len) +{ + unsigned char *hex, *tmp, ch; + size_t hex_len; + size_t i; + + hex_len = bin_len * 2 + 3; /* +2 for "0x", +1 for '\0'. */ + hex = malloc(hex_len); + if (hex == NULL) + log_err(1, "malloc"); + + tmp = hex; + tmp += sprintf(tmp, "0x"); + for (i = 0; i < bin_len; i++) { + ch = bin[i]; + tmp += sprintf(tmp, "%02x", ch); + } + + return (hex); +} + +static void +login_compute_md5(const char id, const char *secret, + const void *challenge, size_t challenge_len, void *response, + size_t response_len) +{ + MD5_CTX ctx; + int rv; + + assert(response_len == MD5_DIGEST_LENGTH); + + MD5_Init(&ctx); + MD5_Update(&ctx, &id, sizeof(id)); + MD5_Update(&ctx, secret, strlen(secret)); + MD5_Update(&ctx, challenge, challenge_len); + rv = MD5_Final(response, &ctx); + if (rv != 1) + log_errx(1, "MD5_Final"); +} + +static void +login_negotiate_key(struct connection *conn, const char *name, + const char *value) +{ + int which, tmp; + + if (strcmp(name, "TargetAlias") == 0) { + strlcpy(conn->conn_target_alias, value, + sizeof(conn->conn_target_alias)); + } else if (strcmp(value, "Irrelevant") == 0) { + /* Ignore. */ + } else if (strcmp(name, "HeaderDigest") == 0) { + which = login_list_prefers(value, "CRC32C", "None"); + switch (which) { + case 1: + log_debugx("target prefers CRC32C " + "for header digest; we'll use it"); + conn->conn_header_digest = CONN_DIGEST_CRC32C; + break; + case 2: + log_debugx("target prefers not to do " + "header digest; we'll comply"); + break; + default: + log_warnx("target sent unrecognized " + "HeaderDigest value \"%s\"; will use None", value); + break; + } + } else if (strcmp(name, "DataDigest") == 0) { + which = login_list_prefers(value, "CRC32C", "None"); + switch (which) { + case 1: + log_debugx("target prefers CRC32C " + "for data digest; we'll use it"); + conn->conn_data_digest = CONN_DIGEST_CRC32C; + break; + case 2: + log_debugx("target prefers not to do " + "data digest; we'll comply"); + break; + default: + log_warnx("target sent unrecognized " + "DataDigest value \"%s\"; will use None", value); + break; + } + } else if (strcmp(name, "MaxConnections") == 0) { + /* Ignore. */ + } else if (strcmp(name, "InitialR2T") == 0) { + if (strcmp(value, "Yes") == 0) + conn->conn_initial_r2t = true; + else + conn->conn_initial_r2t = false; + } else if (strcmp(name, "ImmediateData") == 0) { + if (strcmp(value, "Yes") == 0) + conn->conn_immediate_data = true; + else + conn->conn_immediate_data = false; + } else if (strcmp(name, "MaxRecvDataSegmentLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) + log_errx(1, "received invalid " + "MaxRecvDataSegmentLength"); + conn->conn_max_data_segment_length = tmp; + } else if (strcmp(name, "MaxBurstLength") == 0) { + if (conn->conn_immediate_data) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) + log_errx(1, "received invalid MaxBurstLength"); + conn->conn_max_burst_length = tmp; + } + } else if (strcmp(name, "FirstBurstLength") == 0) { + tmp = strtoul(value, NULL, 10); + if (tmp <= 0) + log_errx(1, "received invalid FirstBurstLength"); + conn->conn_first_burst_length = tmp; + } else if (strcmp(name, "DefaultTime2Wait") == 0) { + /* Ignore */ + } else if (strcmp(name, "DefaultTime2Retain") == 0) { + /* Ignore */ + } else if (strcmp(name, "MaxOutstandingR2T") == 0) { + /* Ignore */ + } else if (strcmp(name, "DataPDUInOrder") == 0) { + /* Ignore */ + } else if (strcmp(name, "DataSequenceInOrder") == 0) { + /* Ignore */ + } else if (strcmp(name, "ErrorRecoveryLevel") == 0) { + /* Ignore */ + } else if (strcmp(name, "OFMarker") == 0) { + /* Ignore */ + } else if (strcmp(name, "IFMarker") == 0) { + /* Ignore */ + } else if (strcmp(name, "TargetPortalGroupTag") == 0) { + /* Ignore */ + } else { + log_debugx("unknown key \"%s\"; ignoring", name); + } +} + +static void +login_negotiate(struct connection *conn) +{ + struct pdu *request, *response; + struct keys *request_keys, *response_keys; + struct iscsi_bhs_login_response *bhslr; + int i; + + log_debugx("beginning parameter negotiation"); + request = login_new_request(conn); + login_set_csg(request, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); + login_set_nsg(request, BHSLR_STAGE_FULL_FEATURE_PHASE); + request_keys = keys_new(); + + /* + * The following keys are irrelevant for discovery sessions. + */ + if (conn->conn_conf.isc_discovery == 0) { + if (conn->conn_conf.isc_header_digest != 0) + keys_add(request_keys, "HeaderDigest", "CRC32C"); + else + keys_add(request_keys, "HeaderDigest", "None"); + if (conn->conn_conf.isc_data_digest != 0) + keys_add(request_keys, "DataDigest", "CRC32C"); + else + keys_add(request_keys, "DataDigest", "None"); + + keys_add(request_keys, "ImmediateData", "Yes"); + keys_add_int(request_keys, "MaxBurstLength", + ISCSI_MAX_DATA_SEGMENT_LENGTH); + keys_add_int(request_keys, "FirstBurstLength", + ISCSI_MAX_DATA_SEGMENT_LENGTH); + keys_add(request_keys, "InitialR2T", "Yes"); + } else { + keys_add(request_keys, "HeaderDigest", "None"); + keys_add(request_keys, "DataDigest", "None"); + } + + keys_add_int(request_keys, "MaxRecvDataSegmentLength", + ISCSI_MAX_DATA_SEGMENT_LENGTH); + keys_add(request_keys, "DefaultTime2Wait", "0"); + keys_add(request_keys, "DefaultTime2Retain", "0"); + keys_add(request_keys, "ErrorRecoveryLevel", "0"); + keys_add(request_keys, "MaxOutstandingR2T", "1"); + keys_save(request_keys, request); + keys_delete(request_keys); + request_keys = NULL; + pdu_send(request); + pdu_delete(request); + request = NULL; + + response = login_receive(conn, false); + response_keys = keys_new(); + keys_load(response_keys, response); + for (i = 0; i < KEYS_MAX; i++) { + if (response_keys->keys_names[i] == NULL) + break; + + login_negotiate_key(conn, + response_keys->keys_names[i], response_keys->keys_values[i]); + } + + bhslr = (struct iscsi_bhs_login_response *)response->pdu_bhs; + if ((bhslr->bhslr_flags & BHSLR_FLAGS_TRANSIT) == 0) + log_warnx("received final login response " + "without the \"T\" flag"); + else if (login_nsg(response) != BHSLR_STAGE_FULL_FEATURE_PHASE) + log_warnx("received final login response with wrong NSG 0x%x", + login_nsg(response)); + + log_debugx("parameter negotiation done; " + "transitioning to Full Feature phase"); + + keys_delete(response_keys); + pdu_delete(response); +} + +static void +login_send_chap_a(struct connection *conn) +{ + struct pdu *request; + struct keys *request_keys; + + request = login_new_request(conn); + request_keys = keys_new(); + keys_add(request_keys, "CHAP_A", "5"); + keys_save(request_keys, request); + keys_delete(request_keys); + pdu_send(request); + pdu_delete(request); +} + +static void +login_send_chap_r(struct pdu *response) +{ + struct connection *conn; + struct pdu *request; + struct keys *request_keys, *response_keys; + const char *chap_a, *chap_c, *chap_i; + char *chap_r, *challenge, response_bin[MD5_DIGEST_LENGTH]; + size_t challenge_len; + int error, rv; + unsigned char id; + char *mutual_chap_c, mutual_chap_i[4]; + + /* + * As in the rest of the initiator, 'request' means + * 'initiator -> target', and 'response' means 'target -> initiator', + * + * So, here the 'response' from the target is the packet that contains + * CHAP challenge; our CHAP response goes into 'request'. + */ + + conn = response->pdu_connection; + + response_keys = keys_new(); + keys_load(response_keys, response); + + /* + * First, compute the response. + */ + chap_a = keys_find(response_keys, "CHAP_A"); + if (chap_a == NULL) + log_errx(1, "received CHAP packet without CHAP_A"); + chap_c = keys_find(response_keys, "CHAP_C"); + if (chap_c == NULL) + log_errx(1, "received CHAP packet without CHAP_C"); + chap_i = keys_find(response_keys, "CHAP_I"); + if (chap_i == NULL) + log_errx(1, "received CHAP packet without CHAP_I"); + + if (strcmp(chap_a, "5") != 0) + log_errx(1, "received CHAP packet " + "with unsupported CHAP_A \"%s\"", chap_a); + id = strtoul(chap_i, NULL, 10); + error = login_hex2bin(chap_c, &challenge, &challenge_len); + if (error != 0) + log_errx(1, "received CHAP packet with malformed CHAP_C"); + login_compute_md5(id, conn->conn_conf.isc_secret, + challenge, challenge_len, response_bin, sizeof(response_bin)); + free(challenge); + chap_r = login_bin2hex(response_bin, sizeof(response_bin)); + + keys_delete(response_keys); + + request = login_new_request(conn); + request_keys = keys_new(); + keys_add(request_keys, "CHAP_N", conn->conn_conf.isc_user); + keys_add(request_keys, "CHAP_R", chap_r); + free(chap_r); + + /* + * If we want mutual authentication, we're expected to send + * our CHAP_I/CHAP_C now. + */ + if (conn->conn_conf.isc_mutual_user[0] != '\0') { + log_debugx("requesting mutual authentication; " + "binary challenge size is %zd bytes", + sizeof(conn->conn_mutual_challenge)); + + rv = RAND_bytes(conn->conn_mutual_challenge, + sizeof(conn->conn_mutual_challenge)); + if (rv != 1) { + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + rv = RAND_bytes(&conn->conn_mutual_id, + sizeof(conn->conn_mutual_id)); + if (rv != 1) { + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } + mutual_chap_c = login_bin2hex(conn->conn_mutual_challenge, + sizeof(conn->conn_mutual_challenge)); + snprintf(mutual_chap_i, sizeof(mutual_chap_i), + "%d", conn->conn_mutual_id); + keys_add(request_keys, "CHAP_I", mutual_chap_i); + keys_add(request_keys, "CHAP_C", mutual_chap_c); + free(mutual_chap_c); + } + + keys_save(request_keys, request); + keys_delete(request_keys); + pdu_send(request); + pdu_delete(request); +} + +static void +login_verify_mutual(const struct pdu *response) +{ + struct connection *conn; + struct keys *response_keys; + const char *chap_n, *chap_r; + char *response_bin, expected_response_bin[MD5_DIGEST_LENGTH]; + size_t response_bin_len; + int error; + + conn = response->pdu_connection; + + response_keys = keys_new(); + keys_load(response_keys, response); + + chap_n = keys_find(response_keys, "CHAP_N"); + if (chap_n == NULL) + log_errx(1, "received CHAP Response PDU without CHAP_N"); + chap_r = keys_find(response_keys, "CHAP_R"); + if (chap_r == NULL) + log_errx(1, "received CHAP Response PDU without CHAP_R"); + error = login_hex2bin(chap_r, &response_bin, &response_bin_len); + if (error != 0) + log_errx(1, "received CHAP Response PDU with malformed CHAP_R"); + + if (strcmp(chap_n, conn->conn_conf.isc_mutual_user) != 0) { + fail(conn, "Mutual CHAP failed"); + log_errx(1, "mutual CHAP authentication failed: wrong user"); + } + + login_compute_md5(conn->conn_mutual_id, + conn->conn_conf.isc_mutual_secret, conn->conn_mutual_challenge, + sizeof(conn->conn_mutual_challenge), expected_response_bin, + sizeof(expected_response_bin)); + + if (memcmp(response_bin, expected_response_bin, + sizeof(expected_response_bin)) != 0) { + fail(conn, "Mutual CHAP failed"); + log_errx(1, "mutual CHAP authentication failed: wrong secret"); + } + + keys_delete(response_keys); + free(response_bin); + + log_debugx("mutual CHAP authentication succeeded"); +} + +static void +login_chap(struct connection *conn) +{ + struct pdu *response; + + log_debugx("beginning CHAP authentication; sending CHAP_A"); + login_send_chap_a(conn); + + log_debugx("waiting for CHAP_A/CHAP_C/CHAP_I"); + response = login_receive(conn, false); + + log_debugx("sending CHAP_N/CHAP_R"); + login_send_chap_r(response); + pdu_delete(response); + + /* + * XXX: Make sure this is not susceptible to MITM. + */ + + log_debugx("waiting for CHAP result"); + response = login_receive(conn, false); + if (conn->conn_conf.isc_mutual_user[0] != '\0') + login_verify_mutual(response); + pdu_delete(response); + + log_debugx("CHAP authentication done"); +} + +static void +login_create_isid(struct connection *conn) +{ + int rv; + + /* + * RFC 3720, 10.12.5: 10b, "Random" ISID. + * + */ + conn->conn_isid[0] = 0x80; + + rv = RAND_bytes(&conn->conn_isid[1], 3); + if (rv != 1) { + log_errx(1, "RAND_bytes failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + } +} + +void +login(struct connection *conn) +{ + struct pdu *request, *response; + struct keys *request_keys, *response_keys; + struct iscsi_bhs_login_request *bhslr; + struct iscsi_bhs_login_response *bhslr2; + const char *auth_method; + int i; + + login_create_isid(conn); + + log_debugx("beginning Login phase; sending Login PDU"); + request = login_new_request(conn); + + bhslr = (struct iscsi_bhs_login_request *)request->pdu_bhs; + bhslr->bhslr_flags |= BHSLR_FLAGS_TRANSIT; + + request_keys = keys_new(); + if (conn->conn_conf.isc_mutual_user[0] != '\0') { + keys_add(request_keys, "AuthMethod", "CHAP"); + } else if (conn->conn_conf.isc_user[0] != '\0') { + /* + * Give target a chance to skip authentication if it + * doesn't feel like it. + * + * None is first, CHAP second; this is to work around + * what seems to be LIO (Linux target) bug: otherwise, + * if target is configured with no authentication, + * and we are configured to authenticate, the target + * will erroneously respond with AuthMethod=CHAP + * instead of AuthMethod=None, and will subsequently + * fail the connection. This usually happens with + * Discovery sessions, which default to no authentication. + */ + keys_add(request_keys, "AuthMethod", "None,CHAP"); + } else { + keys_add(request_keys, "AuthMethod", "None"); + } + keys_add(request_keys, "InitiatorName", + conn->conn_conf.isc_initiator); + if (conn->conn_conf.isc_initiator_alias[0] != '\0') { + keys_add(request_keys, "InitiatorAlias", + conn->conn_conf.isc_initiator_alias); + } + if (conn->conn_conf.isc_discovery == 0) { + keys_add(request_keys, "SessionType", "Normal"); + keys_add(request_keys, + "TargetName", conn->conn_conf.isc_target); + } else { + keys_add(request_keys, "SessionType", "Discovery"); + } + keys_save(request_keys, request); + keys_delete(request_keys); + pdu_send(request); + pdu_delete(request); + + response = login_receive(conn, true); + + response_keys = keys_new(); + keys_load(response_keys, response); + + for (i = 0; i < KEYS_MAX; i++) { + if (response_keys->keys_names[i] == NULL) + break; + + /* + * Not interested in AuthMethod at this point; we only need + * to parse things such as TargetAlias. + * + * XXX: This is somewhat ugly. We should have a way to apply + * all the keys to the session and use that by default + * instead of discarding them. + */ + if (strcmp(response_keys->keys_names[i], "AuthMethod") == 0) + continue; + + login_negotiate_key(conn, + response_keys->keys_names[i], response_keys->keys_values[i]); + } + + bhslr2 = (struct iscsi_bhs_login_response *)response->pdu_bhs; + if ((bhslr2->bhslr_flags & BHSLR_FLAGS_TRANSIT) != 0 && + login_nsg(response) == BHSLR_STAGE_OPERATIONAL_NEGOTIATION) { + if (conn->conn_conf.isc_mutual_user[0] != '\0') { + log_errx(1, "target requested transition " + "to operational negotiation, but we require " + "mutual CHAP"); + } + + log_debugx("target requested transition " + "to operational negotiation"); + keys_delete(response_keys); + pdu_delete(response); + login_negotiate(conn); + return; + } + + auth_method = keys_find(response_keys, "AuthMethod"); + if (auth_method == NULL) + log_errx(1, "received response without AuthMethod"); + if (strcmp(auth_method, "None") == 0) { + if (conn->conn_conf.isc_mutual_user[0] != '\0') { + log_errx(1, "target does not require authantication, " + "but we require mutual CHAP"); + } + + log_debugx("target does not require authentication"); + keys_delete(response_keys); + pdu_delete(response); + login_negotiate(conn); + return; + } + + if (strcmp(auth_method, "CHAP") != 0) { + fail(conn, "Unsupported AuthMethod"); + log_errx(1, "received response " + "with unsupported AuthMethod \"%s\"", auth_method); + } + + if (conn->conn_conf.isc_user[0] == '\0' || + conn->conn_conf.isc_secret[0] == '\0') { + fail(conn, "Authentication required"); + log_errx(1, "target requests CHAP authentication, but we don't " + "have user and secret"); + } + + keys_delete(response_keys); + response_keys = NULL; + pdu_delete(response); + response = NULL; + + login_chap(conn); + login_negotiate(conn); +} diff --git a/usr.sbin/iscsid/pdu.c b/usr.sbin/iscsid/pdu.c new file mode 100644 index 0000000..24b63d7 --- /dev/null +++ b/usr.sbin/iscsid/pdu.c @@ -0,0 +1,281 @@ +/*- + * Copyright (c) 2012 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Edward Tomasz Napierala under sponsorship + * from the FreeBSD Foundation. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/uio.h> +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "iscsid.h" +#include "iscsi_proto.h" + +#ifdef ICL_KERNEL_PROXY +#include <sys/ioctl.h> +#endif + +static int +pdu_ahs_length(const struct pdu *pdu) +{ + + return (pdu->pdu_bhs->bhs_total_ahs_len * 4); +} + +static int +pdu_data_segment_length(const struct pdu *pdu) +{ + uint32_t len = 0; + + len += pdu->pdu_bhs->bhs_data_segment_len[0]; + len <<= 8; + len += pdu->pdu_bhs->bhs_data_segment_len[1]; + len <<= 8; + len += pdu->pdu_bhs->bhs_data_segment_len[2]; + + return (len); +} + +static void +pdu_set_data_segment_length(struct pdu *pdu, uint32_t len) +{ + + pdu->pdu_bhs->bhs_data_segment_len[2] = len; + pdu->pdu_bhs->bhs_data_segment_len[1] = len >> 8; + pdu->pdu_bhs->bhs_data_segment_len[0] = len >> 16; +} + +struct pdu * +pdu_new(struct connection *conn) +{ + struct pdu *pdu; + + pdu = calloc(sizeof(*pdu), 1); + if (pdu == NULL) + log_err(1, "calloc"); + + pdu->pdu_bhs = calloc(sizeof(*pdu->pdu_bhs), 1); + if (pdu->pdu_bhs == NULL) + log_err(1, "calloc"); + + pdu->pdu_connection = conn; + + return (pdu); +} + +struct pdu * +pdu_new_response(struct pdu *request) +{ + + return (pdu_new(request->pdu_connection)); +} + +#ifdef ICL_KERNEL_PROXY + +void +pdu_receive(struct pdu *pdu) +{ + struct iscsi_daemon_receive *idr; + size_t len; + int error; + + pdu->pdu_data = malloc(ISCSI_MAX_DATA_SEGMENT_LENGTH); + if (pdu->pdu_data == NULL) + log_err(1, "malloc"); + + idr = calloc(1, sizeof(*idr)); + if (idr == NULL) + log_err(1, "calloc"); + + idr->idr_session_id = pdu->pdu_connection->conn_session_id; + idr->idr_bhs = pdu->pdu_bhs; + idr->idr_data_segment_len = ISCSI_MAX_DATA_SEGMENT_LENGTH; + idr->idr_data_segment = pdu->pdu_data; + + error = ioctl(pdu->pdu_connection->conn_iscsi_fd, ISCSIDRECEIVE, idr); + if (error != 0) + log_err(1, "ISCSIDRECEIVE"); + + len = pdu_ahs_length(pdu); + if (len > 0) + log_errx(1, "protocol error: non-empty AHS"); + + len = pdu_data_segment_length(pdu); + assert(len <= ISCSI_MAX_DATA_SEGMENT_LENGTH); + pdu->pdu_data_len = len; + + free(idr); +} + +void +pdu_send(struct pdu *pdu) +{ + struct iscsi_daemon_send *ids; + int error; + + pdu_set_data_segment_length(pdu, pdu->pdu_data_len); + + ids = calloc(1, sizeof(*ids)); + if (ids == NULL) + log_err(1, "calloc"); + + ids->ids_session_id = pdu->pdu_connection->conn_session_id; + ids->ids_bhs = pdu->pdu_bhs; + ids->ids_data_segment_len = pdu->pdu_data_len; + ids->ids_data_segment = pdu->pdu_data; + + error = ioctl(pdu->pdu_connection->conn_iscsi_fd, ISCSIDSEND, ids); + if (error != 0) + log_err(1, "ISCSIDSEND"); + + free(ids); +} + +#else /* !ICL_KERNEL_PROXY */ + +static size_t +pdu_padding(const struct pdu *pdu) +{ + + if ((pdu->pdu_data_len % 4) != 0) + return (4 - (pdu->pdu_data_len % 4)); + + return (0); +} + +static void +pdu_read(int fd, char *data, size_t len) +{ + ssize_t ret; + + while (len > 0) { + ret = read(fd, data, len); + if (ret < 0) { + if (timed_out()) + log_errx(1, "exiting due to timeout"); + log_err(1, "read"); + } else if (ret == 0) + log_errx(1, "read: connection lost"); + len -= ret; + data += ret; + } +} + +void +pdu_receive(struct pdu *pdu) +{ + size_t len, padding; + char dummy[4]; + + pdu_read(pdu->pdu_connection->conn_socket, + (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); + + len = pdu_ahs_length(pdu); + if (len > 0) + log_errx(1, "protocol error: non-empty AHS"); + + len = pdu_data_segment_length(pdu); + if (len > 0) { + if (len > ISCSI_MAX_DATA_SEGMENT_LENGTH) { + log_errx(1, "protocol error: received PDU " + "with DataSegmentLength exceeding %d", + ISCSI_MAX_DATA_SEGMENT_LENGTH); + } + + pdu->pdu_data_len = len; + pdu->pdu_data = malloc(len); + if (pdu->pdu_data == NULL) + log_err(1, "malloc"); + + pdu_read(pdu->pdu_connection->conn_socket, + (char *)pdu->pdu_data, pdu->pdu_data_len); + + padding = pdu_padding(pdu); + if (padding != 0) { + assert(padding < sizeof(dummy)); + pdu_read(pdu->pdu_connection->conn_socket, + (char *)dummy, padding); + } + } +} + +void +pdu_send(struct pdu *pdu) +{ + ssize_t ret, total_len; + size_t padding; + uint32_t zero = 0; + struct iovec iov[3]; + int iovcnt; + + pdu_set_data_segment_length(pdu, pdu->pdu_data_len); + iov[0].iov_base = pdu->pdu_bhs; + iov[0].iov_len = sizeof(*pdu->pdu_bhs); + total_len = iov[0].iov_len; + iovcnt = 1; + + if (pdu->pdu_data_len > 0) { + iov[1].iov_base = pdu->pdu_data; + iov[1].iov_len = pdu->pdu_data_len; + total_len += iov[1].iov_len; + iovcnt = 2; + + padding = pdu_padding(pdu); + if (padding > 0) { + assert(padding < sizeof(zero)); + iov[2].iov_base = &zero; + iov[2].iov_len = padding; + total_len += iov[2].iov_len; + iovcnt = 3; + } + } + + ret = writev(pdu->pdu_connection->conn_socket, iov, iovcnt); + if (ret < 0) { + if (timed_out()) + log_errx(1, "exiting due to timeout"); + log_err(1, "writev"); + } + if (ret != total_len) + log_errx(1, "short write"); +} + +#endif /* !ICL_KERNEL_PROXY */ + +void +pdu_delete(struct pdu *pdu) +{ + + free(pdu->pdu_data); + free(pdu->pdu_bhs); + free(pdu); +} diff --git a/usr.sbin/jail/jail.c b/usr.sbin/jail/jail.c index 6970ae1..9930dbb 100644 --- a/usr.sbin/jail/jail.c +++ b/usr.sbin/jail/jail.c @@ -470,10 +470,12 @@ main(int argc, char **argv) if (dep_check(j)) continue; if (j->jid < 0) { - if (!(j->flags & (JF_DEPEND | JF_WILD)) - && verbose >= 0) - jail_quoted_warnx(j, - "not found", NULL); + if (!(j->flags & (JF_DEPEND|JF_WILD))) { + if (verbose >= 0) + jail_quoted_warnx(j, + "not found", NULL); + failed(j); + } goto jail_remove_done; } j->comparam = stopcommands; diff --git a/usr.sbin/kldxref/kldxref.c b/usr.sbin/kldxref/kldxref.c index 16b9058..e74be96 100644 --- a/usr.sbin/kldxref/kldxref.c +++ b/usr.sbin/kldxref/kldxref.c @@ -274,6 +274,16 @@ usage(void) exit(1); } +int +compare(const FTSENT *const *a, const FTSENT *const *b) +{ + if ((*a)->fts_info == FTS_D && (*b)->fts_info != FTS_D) + return 1; + if ((*a)->fts_info != FTS_D && (*b)->fts_info == FTS_D) + return -1; + return strcmp((*a)->fts_name, (*b)->fts_name); +} + int main(int argc, char *argv[]) { @@ -315,7 +325,7 @@ main(int argc, char *argv[]) err(1, "%s", argv[0]); } - ftsp = fts_open(argv, fts_options, 0); + ftsp = fts_open(argv, fts_options, compare); if (ftsp == NULL) exit(1); diff --git a/usr.sbin/mtree/create.c b/usr.sbin/mtree/create.c index da72fc6..8be9b02 100644 --- a/usr.sbin/mtree/create.c +++ b/usr.sbin/mtree/create.c @@ -208,7 +208,7 @@ statf(int indent, FTSENT *p) output(indent, &offset, "mode=%#o", p->fts_statp->st_mode & MBITS); if (keys & F_NLINK && p->fts_statp->st_nlink != 1) output(indent, &offset, "nlink=%u", p->fts_statp->st_nlink); - if (keys & F_SIZE) + if (keys & F_SIZE && S_ISREG(p->fts_statp->st_mode)) output(indent, &offset, "size=%jd", (intmax_t)p->fts_statp->st_size); if (keys & F_TIME) diff --git a/usr.sbin/named-checkconf/Makefile b/usr.sbin/named-checkconf/Makefile deleted file mode 100644 index abee068..0000000 --- a/usr.sbin/named-checkconf/Makefile +++ /dev/null @@ -1,27 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/check - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= named-checkconf - -.PATH: ${SRCDIR} -SRCS+= named-checkconf.c check-tool.c - -CFLAGS+= -I${LIB_BIND_DIR} -CFLAGS+= -I${BIND_DIR}/lib/isc/${ISC_ATOMIC_ARCH}/include - -WARNS?= 3 - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -MAN= named-checkconf.8 - -MANFILTER= sed -e "s@/etc/named\.conf@/etc/namedb/named.conf@g" - -.include <bsd.prog.mk> diff --git a/usr.sbin/named-checkconf/Makefile.depend b/usr.sbin/named-checkconf/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/named-checkconf/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/named-checkzone/Makefile b/usr.sbin/named-checkzone/Makefile deleted file mode 100644 index 05cc9f2..0000000 --- a/usr.sbin/named-checkzone/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/check - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= named-checkzone - -.PATH: ${SRCDIR} -SRCS+= named-checkzone.c check-tool.c - -CFLAGS+= -I${LIB_BIND_DIR} - -WARNS?= 3 - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -LINKS= ${BINDIR}/named-checkzone ${BINDIR}/named-compilezone - -MAN= named-checkzone.8 - -MLINKS= named-checkzone.8 named-compilezone.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/named-checkzone/Makefile.depend b/usr.sbin/named-checkzone/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/named-checkzone/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/named-journalprint/Makefile b/usr.sbin/named-journalprint/Makefile deleted file mode 100644 index cdf4016..0000000 --- a/usr.sbin/named-journalprint/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/tools - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= named-journalprint - -.PATH: ${SRCDIR} -SRCS+= named-journalprint.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 3 - -MAN= named-journalprint.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/named-journalprint/Makefile.depend b/usr.sbin/named-journalprint/Makefile.depend deleted file mode 100644 index a1ed6b6..0000000 --- a/usr.sbin/named-journalprint/Makefile.depend +++ /dev/null @@ -1,26 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/named/Makefile b/usr.sbin/named/Makefile deleted file mode 100644 index f092880..0000000 --- a/usr.sbin/named/Makefile +++ /dev/null @@ -1,68 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/named - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= named - -CONFIGARGS='--prefix=/usr' '--infodir=/usr/share/info' '--mandir=/usr/share/man' '--enable-threads' '--enable-getifaddrs' '--disable-linux-caps' '--with-openssl=/usr' '--with-randomdev=/dev/random' - -# Optional features -.if ${MK_BIND_LARGE_FILE} == "yes" -CONFIGARGS+='--enable-largefile' -.endif -.if ${MK_BIND_SIGCHASE} == "yes" -CONFIGARGS+='STD_CDEFINES=-DDIG_SIGCHASE=1' -.endif -.if ${MK_BIND_IDN} == "yes" -CONFIGARGS+='--with-idn=/usr/local' -.else -CONFIGARGS+='--without-idn' -.endif -.if ${MK_BIND_XML} == "yes" -CONFIGARGS+='--with-libxml2=/usr/local' -.else -CONFIGARGS+='--without-libxml2' -.endif - -.PATH: ${SRCDIR}/unix -SRCS+= os.c dlz_dlopen_driver.c - -.PATH: ${SRCDIR} -SRCS+= builtin.c client.c config.c control.c \ - controlconf.c interfacemgr.c \ - listenlist.c log.c logconf.c main.c notify.c \ - query.c server.c sortlist.c statschannel.c \ - tkeyconf.c tsigconf.c update.c xfrout.c \ - zoneconf.c \ - lwaddr.c lwresd.c lwdclient.c lwderror.c lwdgabn.c \ - lwdgnba.c lwdgrbn.c lwdnoop.c lwsearch.c - -CFLAGS+= -DCONFIGARGS="\"${CONFIGARGS}\"" \ - -DPRODUCT=\"${PRODUCT}\" \ - -DDESCRIPTION=\"${DESCRIPTION}\" \ - -DSRCID=\"${SRCID}\" - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include -I${LIB_BIND_DIR} -CFLAGS+= -I${BIND_DIR}/lib/isc/${ISC_ATOMIC_ARCH}/include - -# Remove the date stamp to make it more obvious when real changes happen -CFLAGS+= -DNO_VERSION_DATE - -WARNS?= 0 - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -MAN= named.8 lwresd.8 named.conf.5 - -MANFILTER= sed -e "s@/etc/named\.conf@/etc/namedb/named.conf@g" \ - -e "s@/var\/run\/named\/named.pid@/var/run/named/pid@" - -LINKS= ${BINDIR}/named ${BINDIR}/lwresd - -.include <bsd.prog.mk> diff --git a/usr.sbin/named/Makefile.depend b/usr.sbin/named/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/named/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/nsec3hash/Makefile b/usr.sbin/nsec3hash/Makefile deleted file mode 100644 index 6dbea09..0000000 --- a/usr.sbin/nsec3hash/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/tools - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= nsec3hash - -.PATH: ${SRCDIR} -SRCS+= nsec3hash.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -WARNS?= 2 - -MAN= nsec3hash.8 - -.include <bsd.prog.mk> diff --git a/usr.sbin/nsec3hash/Makefile.depend b/usr.sbin/nsec3hash/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/nsec3hash/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/ntp/config.h b/usr.sbin/ntp/config.h index efa42d2..41622f1 100644 --- a/usr.sbin/ntp/config.h +++ b/usr.sbin/ntp/config.h @@ -84,7 +84,7 @@ #define CLOCK_LOCAL 1 /* Meinberg clocks */ -/* #undef CLOCK_MEINBERG */ +#define CLOCK_MEINBERG 1 /* Magnavox MX4200 GPS receiver */ /* #undef CLOCK_MX4200 */ diff --git a/usr.sbin/ntp/libparse/Makefile b/usr.sbin/ntp/libparse/Makefile index f86988e..9ccc4b3 100644 --- a/usr.sbin/ntp/libparse/Makefile +++ b/usr.sbin/ntp/libparse/Makefile @@ -9,7 +9,7 @@ SRCS= clk_computime.c clk_dcf7000.c clk_hopf6021.c clk_meinberg.c \ clk_rawdcf.c clk_rcc8000.c clk_schmid.c clk_trimtaip.c \ clk_trimtsip.c clk_varitext.c clk_wharton.c data_mbg.c \ info_trimble.c parse.c parse_conf.c trim_info.c \ - binio.c gpstolfp.c + binio.c gpstolfp.c ieee754io.c mfp_mul.c CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../ diff --git a/usr.sbin/rndc-confgen/Makefile b/usr.sbin/rndc-confgen/Makefile deleted file mode 100644 index 2474920..0000000 --- a/usr.sbin/rndc-confgen/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/confgen - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= rndc-confgen - -.PATH: ${SRCDIR}/unix -SRCS+= os.c - -.PATH: ${SRCDIR} -SRCS+= rndc-confgen.c keygen.c util.c - -CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include -I${LIB_BIND_DIR} - -WARNS?= 3 - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -MAN= rndc-confgen.8 - -MANFILTER= sed -e 's@fI/etc\\fR.*@fI/etc/namedb\\fR@' \ - -e '/^sysconfdir$$/d' \ - -e '/was specified as when BIND was built)/d' - -.include <bsd.prog.mk> diff --git a/usr.sbin/rndc-confgen/Makefile.depend b/usr.sbin/rndc-confgen/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/rndc-confgen/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/rndc/Makefile b/usr.sbin/rndc/Makefile deleted file mode 100644 index 66d8452..0000000 --- a/usr.sbin/rndc/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# $FreeBSD$ - -BIND_DIR= ${.CURDIR}/../../contrib/bind9 -LIB_BIND_REL= ../../lib/bind -LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL} -SRCDIR= ${BIND_DIR}/bin/rndc - -.include "${LIB_BIND_DIR}/config.mk" - -PROG= rndc - -.PATH: ${SRCDIR} -SRCS+= rndc.c util.c - -CFLAGS+= -I${SRCDIR}/include -I${LIB_BIND_DIR} -CFLAGS+= -I${BIND_DIR}/lib/isc/${ISC_ATOMIC_ARCH}/include - -WARNS?= 3 - -DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} -LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} - -MAN= rndc.8 rndc.conf.5 - -MANFILTER= sed -e "s@/etc/rndc\.conf@/etc/namedb/rndc.conf@g" \ - -e "s@/etc/rndc\.key@/etc/namedb/rndc.key@g" - -.include <bsd.prog.mk> diff --git a/usr.sbin/rndc/Makefile.depend b/usr.sbin/rndc/Makefile.depend deleted file mode 100644 index 3740d82..0000000 --- a/usr.sbin/rndc/Makefile.depend +++ /dev/null @@ -1,27 +0,0 @@ -# Autogenerated - do NOT edit! - -DEP_RELDIR := ${_PARSEDIR:S,${SRCTOP}/,,} - -DIRDEPS = \ - gnu/lib/libgcc \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/bind/bind9 \ - lib/bind/dns \ - lib/bind/isc \ - lib/bind/isccc \ - lib/bind/isccfg \ - lib/bind/lwres \ - lib/libc \ - lib/libcompiler_rt \ - lib/libthr \ - secure/lib/libcrypto \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/unbound/Makefile b/usr.sbin/unbound/Makefile new file mode 100644 index 0000000..3eb12fe --- /dev/null +++ b/usr.sbin/unbound/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +SUBDIR= daemon anchor checkconf control +SUBDIR+= local-setup + +.include <bsd.subdir.mk> diff --git a/usr.sbin/unbound/Makefile.inc b/usr.sbin/unbound/Makefile.inc new file mode 100644 index 0000000..425d719 --- /dev/null +++ b/usr.sbin/unbound/Makefile.inc @@ -0,0 +1,5 @@ +# $FreeBSD$ + +NO_WERROR= true + +.include "../Makefile.inc" diff --git a/usr.sbin/unbound/anchor/Makefile b/usr.sbin/unbound/anchor/Makefile new file mode 100644 index 0000000..1404766 --- /dev/null +++ b/usr.sbin/unbound/anchor/Makefile @@ -0,0 +1,18 @@ +# $FreeBSD$ + +# Vendor sources and generated files +LDNSDIR= ${.CURDIR}/../../../contrib/ldns +UNBOUNDDIR= ${.CURDIR}/../../../contrib/unbound +EXPATDIR= ${.CURDIR}/../../../contrib/expat + +.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/smallapp ${UNBOUNDDIR}/doc + +PROG= unbound-anchor +SRCS= unbound-anchor.c +CFLAGS= -I${UNBOUNDDIR} -I${LDNSDIR} -I${EXPATDIR}/lib +DPADD= ${LIBUNBOUND} ${LIBLDNS} ${LIBUTIL} ${LIBBSDXML} ${LIBSSL} ${LIBCRYPTO} ${LIBPTHREAD} +LDADD= -lunbound -lldns -lutil -lbsdxml -lssl -lcrypto -lpthread +USEPRIVATELIB= ldns +MAN= unbound-anchor.8 + +.include <bsd.prog.mk> diff --git a/usr.sbin/unbound/checkconf/Makefile b/usr.sbin/unbound/checkconf/Makefile new file mode 100644 index 0000000..6a58806 --- /dev/null +++ b/usr.sbin/unbound/checkconf/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +# Vendor sources and generated files +LDNSDIR= ${.CURDIR}/../../../contrib/ldns +UNBOUNDDIR= ${.CURDIR}/../../../contrib/unbound + +.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/smallapp ${UNBOUNDDIR}/doc + +PROG= unbound-checkconf +SRCS= unbound-checkconf.c worker_cb.c +CFLAGS= -I${UNBOUNDDIR} -I${LDNSDIR} +DPADD= ${LIBUNBOUND} ${LIBLDNS} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBPTHREAD} +LDADD= -lunbound -lldns -lutil -lssl -lcrypto -lpthread +USEPRIVATELIB= ldns +MAN= unbound-checkconf.8 + +.include <bsd.prog.mk> diff --git a/usr.sbin/unbound/control/Makefile b/usr.sbin/unbound/control/Makefile new file mode 100644 index 0000000..7056a9e --- /dev/null +++ b/usr.sbin/unbound/control/Makefile @@ -0,0 +1,18 @@ +# $FreeBSD$ + +# Vendor sources and generated files +LDNSDIR= ${.CURDIR}/../../../contrib/ldns +UNBOUNDDIR= ${.CURDIR}/../../../contrib/unbound + +.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/smallapp ${UNBOUNDDIR}/doc + +PROG= unbound-control +SCRIPTS= unbound-control-setup.sh +SRCS= unbound-control.c worker_cb.c +CFLAGS= -I${UNBOUNDDIR} -I${LDNSDIR} +DPADD= ${LIBUNBOUND} ${LIBLDNS} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBPTHREAD} +LDADD= -lunbound -lldns -lutil -lssl -lcrypto -lpthread +USEPRIVATELIB= ldns +MAN= unbound-control.8 + +.include <bsd.prog.mk> diff --git a/usr.sbin/unbound/daemon/Makefile b/usr.sbin/unbound/daemon/Makefile new file mode 100644 index 0000000..0f9044f --- /dev/null +++ b/usr.sbin/unbound/daemon/Makefile @@ -0,0 +1,17 @@ +# $FreeBSD$ + +# Vendor sources and generated files +LDNSDIR= ${.CURDIR}/../../../contrib/ldns +UNBOUNDDIR= ${.CURDIR}/../../../contrib/unbound + +.PATH: ${UNBOUNDDIR} ${UNBOUNDDIR}/daemon ${UNBOUNDDIR}/doc + +PROG= unbound +SRCS= acl_list.c cachedump.c daemon.c remote.c stats.c unbound.c worker.c +CFLAGS= -I${UNBOUNDDIR} -I${LDNSDIR} +DPADD= ${LIBUNBOUND} ${LIBLDNS} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} ${LIBPTHREAD} +LDADD= -lunbound -lldns -lutil -lssl -lcrypto -lpthread +USEPRIVATELIB= ldns +MAN= unbound.8 unbound.conf.5 + +.include <bsd.prog.mk> diff --git a/usr.sbin/unbound/local-setup/Makefile b/usr.sbin/unbound/local-setup/Makefile new file mode 100644 index 0000000..7a1bc0441 --- /dev/null +++ b/usr.sbin/unbound/local-setup/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +SCRIPTS= local-unbound-setup.sh +MAN= # + +.include <bsd.prog.mk> diff --git a/usr.sbin/unbound/local-setup/local-unbound-setup.sh b/usr.sbin/unbound/local-setup/local-unbound-setup.sh new file mode 100755 index 0000000..99c9324 --- /dev/null +++ b/usr.sbin/unbound/local-setup/local-unbound-setup.sh @@ -0,0 +1,356 @@ +#!/bin/sh +#- +# Copyright (c) 2013 Dag-Erling Smørgrav +# 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. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +# +# $FreeBSD$ +# + +# +# Configuration variables +# +user="" +unbound_conf="" +forward_conf="" +workdir="" +chrootdir="" +anchor="" +pidfile="" +resolv_conf="" +resolvconf_conf="" +service="" +start_unbound="" +forwarders="" + +# +# Global variables +# +self=$(basename $(realpath "$0")) +bkext=$(date "+%Y%m%d.%H%M%S") + +# +# Set default values for unset configuration variables. +# +set_defaults() { + : ${user:=unbound} + : ${workdir:=/var/unbound} + : ${unbound_conf:=${workdir}/unbound.conf} + : ${forward_conf:=${workdir}/forward.conf} + : ${anchor:=${workdir}/root.key} + : ${pidfile:=/var/run/local_unbound.pid} + : ${resolv_conf:=/etc/resolv.conf} + : ${resolvconf_conf:=/etc/resolvconf.conf} + : ${service:=local_unbound} + : ${start_unbound:=yes} +} + +# +# Verify that the configuration files are inside the working +# directory, and if so, set the chroot directory accordingly. +# +set_chrootdir() { + chrootdir="${workdir}" + for file in "${unbound_conf}" "${forward_conf}" "${anchor}" ; do + if [ "${file#${workdir%/}/}" = "${file}" ] ; then + echo "warning: ${file} is outside ${workdir}" >&2 + chrootdir="" + fi + done + if [ -z "${chrootdir}" ] ; then + echo "warning: disabling chroot" >&2 + fi +} + +# +# Scan through /etc/resolv.conf looking for uncommented nameserver +# lines that don't point to localhost and return their values. +# +get_nameservers() { + while read line ; do + local bareline=${line%%\#*} + local key=${bareline%% *} + local value=${bareline#* } + case ${key} in + nameserver) + case ${value} in + 127.0.0.1|::1|localhost|localhost.*) + ;; + *) + echo "${value}" + ;; + esac + ;; + esac + done +} + +# +# Scan through /etc/resolv.conf looking for uncommented nameserver +# lines. Comment out any that don't point to localhost. Finally, +# append a nameserver line that points to localhost, if there wasn't +# one already, and enable the edns0 option. +# +gen_resolv_conf() { + local localhost=no + local edns0=no + while read line ; do + local bareline=${line%%\#*} + local key=${bareline%% *} + local value=${bareline#* } + case ${key} in + nameserver) + case ${value} in + 127.0.0.1|::1|localhost|localhost.*) + localhost=yes + ;; + *) + echo -n "# " + ;; + esac + ;; + options) + case ${value} in + *edns0*) + edns0=yes + ;; + esac + ;; + esac + echo "${line}" + done + if [ "${localhost}" = "no" ] ; then + echo "nameserver 127.0.0.1" + fi + if [ "${edns0}" = "no" ] ; then + echo "options edns0" + fi +} + +# +# Generate resolvconf.conf so it updates forward.conf in addition to +# resolv.conf. Note "in addition to" rather than "instead of", +# because we still want it to update the domain name and search path +# if they change. Setting name_servers to "127.0.0.1" ensures that +# the libc resolver will try unbound first. +# +gen_resolvconf_conf() { + echo "# Generated by $self" + echo "resolv_conf=\"/dev/null\" # prevent updating ${resolv_conf}" + echo "unbound_conf=\"${forward_conf}\"" + echo "unbound_pid=\"${pidfile}\"" + echo "unbound_service=\"${service}\"" + # resolvconf(8) likes to restart rather than reload + echo "unbound_restart=\"service ${service} reload\"" +} + +# +# Generate forward.conf +# +gen_forward_conf() { + echo "# Generated by $self" + echo "forward-zone:" + echo " name: ." + for forwarder ; do + if expr "${forwarder}" : "^[0-9:.]\{1,\}$" >/dev/null ; then + echo " forward-addr: ${forwarder}" + else + echo " forward-host: ${forwarder}" + fi + done +} + +# +# Generate unbound.conf +# +gen_unbound_conf() { + echo "# Generated by $self" + echo "server:" + echo " username: ${user}" + echo " directory: ${workdir}" + echo " chroot: ${chrootdir}" + echo " pidfile: ${pidfile}" + echo " auto-trust-anchor-file: ${anchor}" + echo "" + if [ -f "${forward_conf}" ] ; then + echo "include: ${forward_conf}" + fi +} + +# +# Replace one file with another, making a backup copy of the first, +# but only if the new file is different from the old. +# +replace() { + local file="$1" + local newfile="$2" + if [ ! -f "${file}" ] ; then + echo "${file} created" + mv "${newfile}" "${file}" + elif ! cmp -s "${file}" "${newfile}" ; then + local oldfile="${file}.${bkext}" + echo "original ${file} saved as ${oldfile}" + mv "${file}" "${oldfile}" + mv "${newfile}" "${file}" + else + echo "${file} not modified" + rm "${newfile}" + fi +} + +# +# Print usage message and exit +# +usage() { + exec >&2 + echo "usage: $self [options] [forwarder ...]" + echo "options:" + echo " -n do not start unbound" + echo " -a path full path to trust anchor file" + echo " -c path full path to unbound configuration" + echo " -f path full path to forwarding configuration" + echo " -p path full path to pid file" + echo " -R path full path to resolvconf.conf" + echo " -r path full path to resolv.conf" + echo " -s service name of unbound service" + echo " -u user user to run unbound as" + echo " -w path full path to working directory" + exit 1 +} + +# +# Main +# +main() { + umask 022 + + # + # Parse and validate command-line options + # + while getopts "a:c:f:np:R:r:s:u:w:" option ; do + case $option in + a) + anchor="$OPTARG" + ;; + c) + unbound_conf="$OPTARG" + ;; + f) + forward_conf="$OPTARG" + ;; + n) + start_unbound="no" + ;; + p) + pidfile="$OPTARG" + ;; + R) + resolvconf_conf="$OPTARG" + ;; + r) + resolv_conf="$OPTARG" + ;; + s) + service="$OPTARG" + ;; + u) + user="$OPTARG" + ;; + w) + workdir="$OPTARG" + ;; + *) + usage + ;; + esac + done + shift $((OPTIND-1)) + set_defaults + + # + # Get the list of forwarders, either from the command line or + # from resolv.conf. + # + forwarders="$@" + if [ -z "$forwarders" ] ; then + echo "Extracting forwarders from ${resolv_conf}." + forwarders=$(get_nameservers <"${resolv_conf}") + fi + + # + # Generate forward.conf. + # + if [ -z "${forwarders}" ] ; then + echo -n "No forwarders found in ${resolv_conf##*/}, " + if [ -f "${forward_conf}" ] ; then + echo "using existing ${forward_conf##*/}." + else + echo "unbound will recurse." + fi + else + local tmp_forward_conf=$(mktemp -u "${forward_conf}.XXXXX") + gen_forward_conf ${forwarders} >"${tmp_forward_conf}" + replace "${forward_conf}" "${tmp_forward_conf}" + fi + + # + # Generate unbound.conf. + # + local tmp_unbound_conf=$(mktemp -u "${unbound_conf}.XXXXX") + set_chrootdir + gen_unbound_conf >"${tmp_unbound_conf}" + replace "${unbound_conf}" "${tmp_unbound_conf}" + + # + # Start unbound, unless requested not to. Stop immediately if + # it is not enabled so we don't end up with a resolv.conf that + # points into nothingness. We could "onestart" it, but it + # wouldn't stick. + # + if [ "${start_unbound}" = "no" ] ; then + # skip + elif ! service "${service}" enabled ; then + echo "Please enable $service in rc.conf(5) and try again." + return 1 + elif ! service "${service}" restart ; then + echo "Failed to start $service." + return 1 + fi + + # + # Rewrite resolvconf.conf so resolvconf updates forward.conf + # instead of resolv.conf. + # + local tmp_resolvconf_conf=$(mktemp -u "${resolvconf_conf}.XXXXX") + gen_resolvconf_conf >"${tmp_resolvconf_conf}" + replace "${resolvconf_conf}" "${tmp_resolvconf_conf}" + + # + # Finally, rewrite resolv.conf. + # + local tmp_resolv_conf=$(mktemp -u "${resolv_conf}.XXXXX") + gen_resolv_conf <"${resolv_conf}" >"${tmp_resolv_conf}" + replace "${resolv_conf}" "${tmp_resolv_conf}" +} + +main "$@" |