diff options
Diffstat (limited to 'usr/src/cmd')
412 files changed, 34607 insertions, 4415 deletions
diff --git a/usr/src/cmd/Adm/sun/Makefile b/usr/src/cmd/Adm/sun/Makefile index 9c280f54ec..636dc875e6 100644 --- a/usr/src/cmd/Adm/sun/Makefile +++ b/usr/src/cmd/Adm/sun/Makefile @@ -21,9 +21,11 @@ # # Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2010 Nexenta Systems, Inc. All rights reserved. +# Copyright 2019 Joyent, Inc. # -ETCFILES= ioctl.syscon passwd shadow motd +ETCGENFILES=motd issue +ETCFILES= ioctl.syscon passwd shadow $(ETCGENFILES) FTPDFILES= ftpusers KVMFILES= README SMBFILES= smbpasswd @@ -45,35 +47,28 @@ $(ROOTVARSMB)/smbpasswd := FILEMODE = 0400 .KEEP_STATE: -$(ROOTETCFTPUSERSLINK): $(ROOTETCFTPDFILES) - $(RM) $@; $(SYMLINK) ftpd/ftpusers $@ - all: $(ETCFILES) $(KVMFILES) $(SMBFILES) $(FTPDFILES) install: all $(ROOTETCFILES) $(ROOTETCFTPDFILES) $(ROOTUSRKVMFILES) $(ROOTVARSMBFILES) $(ROOTETCFTPUSERSLINK) clean: - $(RM) $(ROOTETCFTPUSERSLINK) + $(RM) $(ROOTETCFTPUSERSLINK) $(ETCGENFILES) $(SMBFILES) -lint: +clobber: clean -clobber: +$(ROOTETCFTPUSERSLINK): $(ROOTETCFTPDFILES) + $(RM) $@; $(SYMLINK) ftpd/ftpusers $@ -motd: FRC +motd: $(ROOT)/buildstamp @-$(ECHO) "rebuilding motd" - @$(RELEASE_BUILD)-$(ECHO) "The illumos Project\t$(VERSION)\t$(RELEASE_DATE)" > motd - @$(NOT_RELEASE_BUILD)-$(ECHO) "The illumos Project\t$(VERSION)\t`date +'%h. %d, %Y'`" > motd + @$(ECHO) "SmartOS (build: $$(cat $(ROOT)/buildstamp))" >$@ + +issue: issue.in $(ROOT)/buildstamp + sed "s+build: 00000000T000000Z+$$(cat $(ROOT)/buildstamp)+" issue.in >$@ @$(NOT_RELEASE_BUILD)-$(ECHO) $(DEV_CM) | sed -e "s/@(#)//" >> motd @-$(CAT) release_info >> motd smbpasswd: $(TOUCH) smbpasswd -clean: - -lint: - -clobber: - $(RM) motd smbpasswd - FRC: diff --git a/usr/src/cmd/Adm/sun/issue.in b/usr/src/cmd/Adm/sun/issue.in new file mode 100644 index 0000000000..d9957b7a0d --- /dev/null +++ b/usr/src/cmd/Adm/sun/issue.in @@ -0,0 +1,14 @@ + + + [37m[1m*[31m--[0m[31m+--[37m[1m*[0m[33m--[37m[1m*[0m + [31m|[1m\[0m [31m|[0m[31m\ |[33m\ [1m|\[0m J O Y E N T + [31m| [1m\[0m[31m|[0m [31m\| [33m\[1m| \[0m [37;47m#####[0m [37;47m####[0m [37;47m#[0m [37;47m#####[0m [37;47m###[0m [37;47m#[0m [37;47m#[0m TM + [31m+--[1m[37m*[31m--+[0m[33m--[1m[37m*[33m--[37m*[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m##[0m [37;47m#[0m + [37m|[31m\ |\ |\ [33m|[1m\ |[0m [37;47m#[0m [37;47m####[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m + [37m| [31m\| \| \[33m| [1m\|[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m##[0m + [37m[1m*[0m[37m--+[31m--+[33m--+[1m--+[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m#[0m [37;47m###[0m [37;47m#[0m [37;47m#[0m + [37m[1m\ |[0m[34m\ |\ |[1m\ |[0m + [37m[1m\| [0m[34m\| \| [1m\|[0m [1;37mSmartOS[0m + [37m[1m*--+[0m[34m--[37m[1m*[34m--[37m*[0m build: 00000000T000000Z + + diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile index fd13f07290..6d6276c70d 100644 --- a/usr/src/cmd/Makefile +++ b/usr/src/cmd/Makefile @@ -34,8 +34,8 @@ include ../Makefile.master # -# Note that the commands 'lp', and 'perl' are first in -# the list, violating alphabetical order. This is because they are very +# Note that if the 'lp' command were built, it would be first in +# the list, violating alphabetical order. This is because it is very # long-running and should be given the most wall-clock time for a # parallel build. # @@ -56,7 +56,6 @@ FIRST_SUBDIRS= \ COMMON_SUBDIRS= \ allocate \ availdevs \ - lp \ perl \ Adm \ abi \ @@ -98,6 +97,7 @@ COMMON_SUBDIRS= \ cmd-crypto \ cmd-inet \ col \ + column \ compress \ connstat \ consadm \ @@ -198,7 +198,6 @@ COMMON_SUBDIRS= \ growfs \ grpck \ gss \ - hal \ halt \ head \ hostid \ @@ -235,7 +234,6 @@ COMMON_SUBDIRS= \ kvmstat \ last \ lastcomm \ - latencytop \ ldap \ ldapcachemgr \ lgrpinfo \ @@ -258,6 +256,7 @@ COMMON_SUBDIRS= \ ls \ luxadm \ mach \ + machid \ mail \ mailwrapper \ mailx \ @@ -292,6 +291,7 @@ COMMON_SUBDIRS= \ news \ newtask \ nice \ + nicstat \ nl \ nlsadmin \ nohup \ @@ -317,7 +317,6 @@ COMMON_SUBDIRS= \ picl \ plimit \ policykit \ - pools \ power \ powertop \ ppgsz \ @@ -325,7 +324,6 @@ COMMON_SUBDIRS= \ plockstat \ pr \ prctl \ - print \ printf \ priocntl \ profiles \ @@ -343,7 +341,6 @@ COMMON_SUBDIRS= \ pwck \ pwconv \ pwd \ - pyzfs \ raidctl \ raidz_test \ ramdiskadm \ @@ -395,7 +392,6 @@ COMMON_SUBDIRS= \ srchtxt \ srptadm \ srptsvc \ - ssh \ stat \ stmfadm \ stmfproxy \ @@ -447,8 +443,11 @@ COMMON_SUBDIRS= \ utmpd \ uuidgen \ valtools \ + varpd \ vgrind \ vi \ + vndadm \ + vndstat \ volcheck \ volrmmount \ vrrpadm \ @@ -518,11 +517,14 @@ sparc_SUBDIRS= \ vntsd # -# Commands that are messaged. Note that 'lp' comes first -# (see previous comment about 'lp'.) +# Commands that are messaged. Note that 'lp' comes first (see previous comment +# about 'lp'.) +# +# We purposefully leave out auditrecord's messages from illumos-joyent: it +# attempts to use Perl modules constructed as part of the build alongside the +# native Perl. # MSGSUBDIRS= \ - lp \ abi \ acctadm \ allocate \ @@ -531,7 +533,6 @@ MSGSUBDIRS= \ audit \ auditconfig \ auditd \ - auditrecord \ auditset \ auths \ autopush \ @@ -659,11 +660,9 @@ MSGSUBDIRS= \ pg \ pgrep \ picl \ - pools \ power \ pr \ praudit \ - print \ profiles \ projadd \ projects \ @@ -674,7 +673,6 @@ MSGSUBDIRS= \ ptools \ pwconv \ pwd \ - pyzfs \ raidctl \ ramdiskadm \ rcap \ diff --git a/usr/src/cmd/Makefile.check b/usr/src/cmd/Makefile.check index 91507c9ad9..ac1ff29c88 100644 --- a/usr/src/cmd/Makefile.check +++ b/usr/src/cmd/Makefile.check @@ -118,16 +118,12 @@ MANIFEST_SUBDIRS= \ krb5/krb5kdc \ krb5/kwarn \ krb5/slave \ - lp/cmd/lpsched \ picl/picld \ pools/poold \ - print/bsd-sysv-commands \ - print/ppdmgr \ rcap/rcapd \ rpcsvc/rpc.bootparamd \ sendmail/lib \ smbsrv/smbd \ - ssh/etc \ svc/milestone \ tsol/labeld \ tsol/tnctl \ diff --git a/usr/src/cmd/adbgen/common/adbsub.c b/usr/src/cmd/adbgen/common/adbsub.c index d9b7a37273..773a215ae8 100644 --- a/usr/src/cmd/adbgen/common/adbsub.c +++ b/usr/src/cmd/adbgen/common/adbsub.c @@ -19,7 +19,6 @@ * * CDDL HEADER END */ -#pragma ident "%Z%%M% %I% %E% SMI" /* * Copyright (c) 1983-1998 by Sun Microsystems, Inc. @@ -27,6 +26,10 @@ */ /* + * Copyright 2019 Joyent, Inc. + */ + +/* * Subroutines to be called by adbgen2.c, the C program generated * by adbgen1.c. */ @@ -155,9 +158,8 @@ do_fmt(char *acp) case 'S': case 'i': if (warnings) { - fprintf(stderr, - "Unknown format size \"%s\", assuming zero\n", - acp); + fprintf(stderr, "Unknown format size \"%s\", " + "assuming zero\n", acp); warns++; } width = 0; @@ -167,7 +169,7 @@ do_fmt(char *acp) exit(1); } for (i = 0; i < rcount; i++) { - putchar(*cp); + (void) putchar(*cp); } cp++; sum += width * rcount; @@ -186,8 +188,8 @@ format(char *name, size_t size, char *fmt) fs = do_fmt(fmt); if (fs != size && warnings) { fprintf(stderr, - "warning: \"%s\" size is %ld, \"%s\" width is %d\n", - name, size, fmt, fs); + "warning: \"%s\" size is %ld, \"%s\" width is %d\n", + name, size, fmt, fs); warns++; } last_off += fs; diff --git a/usr/src/cmd/auditd/auditd.xml b/usr/src/cmd/auditd/auditd.xml index 88632647f5..ac0600c9e1 100644 --- a/usr/src/cmd/auditd/auditd.xml +++ b/usr/src/cmd/auditd/auditd.xml @@ -142,7 +142,7 @@ --> <property_group name='preselection' type='application'> <propval name='flags' type='astring' - value='lo' /> + value='lo,ex' /> <propval name='naflags' type='astring' value='lo' /> <propval name='read_authorization' type='astring' @@ -188,7 +188,7 @@ <propval name='arge' type='boolean' value='false' /> <propval name='argv' type='boolean' - value='false' /> + value='true' /> <propval name='cnt' type='boolean' value='true' /> <propval name='group' type='boolean' @@ -196,7 +196,7 @@ <propval name='path' type='boolean' value='false' /> <propval name='perzone' type='boolean' - value='false' /> + value='true' /> <propval name='public' type='boolean' value='false' /> <propval name='seq' type='boolean' diff --git a/usr/src/cmd/auditrecord/Makefile b/usr/src/cmd/auditrecord/Makefile index 4f4670dfc9..fdf5213b3f 100644 --- a/usr/src/cmd/auditrecord/Makefile +++ b/usr/src/cmd/auditrecord/Makefile @@ -22,13 +22,15 @@ # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2018, Joyent, Inc. +# include $(SRC)/cmd/Makefile.cmd ATTR = audit_record_attr ROOTAUDITDIR = $(ROOT)/usr/lib/audit -SECURITYFILES = $(ATTR:%=$(ROOTAUDITDIR)/%) +SECURITYFILES = $(ATTR:%=$(ROOTAUDITDIR)/%) $(SECURITYFILES) := FILEMODE = $(LIBFILEMODE) PROG = auditrecord @@ -41,7 +43,7 @@ ADTXMLFILE = $(LIBBSMDIR)/common/adt.xml .KEEP_STATE: -all: $(PROG) $(ATTR) +all: $(PROG) $(ATTR) install: all $(ROOTUSRSBINPROG) install_data @@ -59,7 +61,7 @@ clean: $(RM) $(ATTR) $(STRIPTEXT) $(AGETTEXT) $(ATTR): $(STRIPTEXT) $(ATTRPROC) $(ADTXMLFILE) $(ATTR).txt - ./$(STRIPTEXT) < $(ATTR).txt > $(ATTR) + $(PERL) ./$(STRIPTEXT) < $(ATTR).txt > $(ATTR) $(PERL) -I $(LIBBSMDIR) ./$(ATTRPROC) $(ADTXMLFILE) >> $(ATTR) $(ROOTUSRSBINPROG): $(PROG) diff --git a/usr/src/cmd/bhyve/Makefile b/usr/src/cmd/bhyve/Makefile index 7126fdda17..4dc737c768 100644 --- a/usr/src/cmd/bhyve/Makefile +++ b/usr/src/cmd/bhyve/Makefile @@ -11,7 +11,7 @@ # # Copyright 2014 Pluribus Networks Inc. -# Copyright 2019 Joyent, Inc. +# Copyright 2020 Joyent, Inc. # Copyright 2020 Oxide Computer Company # @@ -103,13 +103,17 @@ SRCS = acpi.c \ OBJS = $(SRCS:.c=.o) -CLOBBERFILES = $(ROOTUSRSBINPROG) +CLOBBERFILES = $(ROOTUSRSBINPROG) $(ZHYVE) + +ZHYVE_DIR = $(ROOT)/usr/lib/brand/bhyve +ZHYVE_PROG = zhyve +ZHYVE = $(ZHYVE_DIR)/$(ZHYVE_PROG) MEVENT_TEST_PROG = mevent_test MEVENT_TEST_SRCS = mevent.c mevent_test.c MEVENT_TEST_OBJS = $(MEVENT_TEST_SRCS:.c=.o) -CLEANFILES = $(PROG) $(MEVENT_TEST_PROG) $(MEVENT_TEST_OBJS) +CLEANFILES = $(PROG) $(ZHYVE_PROG) $(MEVENT_TEST_PROG) $(MEVENT_TEST_OBJS) CFLAGS += $(CCVERBOSE) -_gcc=-Wimplicit-function-declaration -_gcc=-Wno-parentheses CPPFLAGS = -I$(COMPAT)/bhyve -I$(CONTRIB)/bhyve \ @@ -118,14 +122,13 @@ CPPFLAGS = -I$(COMPAT)/bhyve -I$(CONTRIB)/bhyve \ -I$(CONTRIB)/bhyve/dev/mii \ -I$(SRC)/uts/common/io/e1000api \ $(CPPFLAGS.master) \ + -I$(ROOT)/usr/platform/i86pc/include \ -I$(SRC)/uts/i86pc/io/vmm \ -I$(SRC)/uts/common \ -I$(SRC)/uts/i86pc \ + -I$(SRC)/lib/libdladm/common \ -DWITHOUT_CAPSICUM -# Disable the crypto code until it is wired up -CPPFLAGS += -DNO_OPENSSL - pci_nvme.o := CERRWARN += -_gcc=-Wno-pointer-sign pci_nvme.o := SMOFF += kmalloc_wrong_size @@ -135,12 +138,14 @@ SMOFF += all_func_returns,leaks,no_if_block CSTD= $(CSTD_GNU99) C99MODE= -xc99=%all -$(PROG) := LDLIBS += -lsocket -lnsl -ldlpi -ldladm -lmd -luuid -lvmmapi -lz +$(PROG) := LDLIBS += -lsocket -lnsl -ldlpi -ldladm -lmd -lsunw_crypto -luuid \ + -lvmmapi -lz +$(ZHYVE_PROG) := LDLIBS += -lnvpair $(MEVENT_TEST_PROG) := LDLIBS += -lsocket .KEEP_STATE: -all: $(PROG) $(MEVENT_TEST_PROG) $(SUBDIRS) +all: $(PROG) $(MEVENT_TEST_PROG) $(ZHYVE_PROG) $(SUBDIRS) $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS) @@ -149,7 +154,7 @@ $(PROG): $(OBJS) $(MEVENT_TEST_PROG): $(MEVENT_TEST_OBJS) $(LINK.c) -o $@ $(MEVENT_TEST_OBJS) $(LDFLAGS) $(LDLIBS) -install: all $(ROOTUSRSBINPROG) $(SUBDIRS) +install: all $(ZHYVE) $(ROOTUSRSBINPROG) $(SUBDIRS) clean: $(SUBDIRS) $(RM) $(OBJS) $(CLEANFILES) @@ -161,3 +166,12 @@ $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: + +include ../Makefile.targ + +$(ZHYVE_DIR)/%: % + $(INS.file) + +%.o: $(SRC)/uts/i86pc/io/vmm/%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) diff --git a/usr/src/cmd/bhyve/acpi.c b/usr/src/cmd/bhyve/acpi.c index 76ddf5f5f6..580bb900dd 100644 --- a/usr/src/cmd/bhyve/acpi.c +++ b/usr/src/cmd/bhyve/acpi.c @@ -272,15 +272,7 @@ basl_fwrite_madt(FILE *fp) EFPRINTF(fp, "[0001]\t\tLocal Apic ID : %02x\n", i); EFPRINTF(fp, "[0004]\t\tFlags (decoded below) : 00000001\n"); EFPRINTF(fp, "\t\t\tProcessor Enabled : 1\n"); -#ifdef __FreeBSD__ EFPRINTF(fp, "\t\t\tRuntime Online Capable : 0\n"); -#else - /* - * Until iasl is updated to support the "Runtime Online - * Capable" entry, it must be omitted. This should be - * re-checked when illumos receives an acpica update. - */ -#endif /* __FreeBSD__ */ EFPRINTF(fp, "\n"); } diff --git a/usr/src/cmd/bhyve/bhyverun.c b/usr/src/cmd/bhyve/bhyverun.c index 8b7166ce5e..c4ebef1b31 100644 --- a/usr/src/cmd/bhyve/bhyverun.c +++ b/usr/src/cmd/bhyve/bhyverun.c @@ -85,6 +85,10 @@ __FBSDID("$FreeBSD$"); #endif #include <vmmapi.h> +#ifndef __FreeBSD__ +#include <sys/stat.h> +#endif + #include "bhyverun.h" #include "acpi.h" #include "atkbdc.h" @@ -1222,6 +1226,28 @@ do_open(const char *vmname) return (ctx); } +#ifndef __FreeBSD__ + +#define FILE_PROVISIONING "/var/svc/provisioning" +#define FILE_PROVISION_SUCCESS "/var/svc/provision_success" + +static void +mark_provisioned(void) +{ + struct stat stbuf; + + if (lstat(FILE_PROVISIONING, &stbuf) != 0) + return; + + if (rename(FILE_PROVISIONING, FILE_PROVISION_SUCCESS) != 0) { + (void) fprintf(stderr, "Cannot rename %s to %s: %s\n", + FILE_PROVISIONING, FILE_PROVISION_SUCCESS, + strerror(errno)); + } +} + +#endif + int main(int argc, char *argv[]) { @@ -1525,7 +1551,10 @@ main(int argc, char *argv[]) fbsdrun_addcpu(ctx, BSP, BSP, rip); #else fbsdrun_addcpu(ctx, BSP, BSP, rip, suspend); + + mark_provisioned(); #endif + /* * Head off to the main event dispatch loop */ diff --git a/usr/src/cmd/bhyve/rfb.c b/usr/src/cmd/bhyve/rfb.c index a3d80197a8..624c7dc665 100644 --- a/usr/src/cmd/bhyve/rfb.c +++ b/usr/src/cmd/bhyve/rfb.c @@ -3,7 +3,7 @@ * * Copyright (c) 2015 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com> * Copyright (c) 2015 Leon Dang - * Copyright 2018 Joyent, Inc. + * Copyright 2020 Joyent, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -807,7 +807,7 @@ rfb_handle(struct rfb_softc *rc, int cfd) * The client then sends the resulting 16-bytes response. */ #ifndef NO_OPENSSL - strncpy(keystr, rc->password, PASSWD_LENGTH); + strncpy((char *)keystr, rc->password, PASSWD_LENGTH); /* VNC clients encrypts the challenge with all the bit fields * in each byte of the password mirrored. @@ -842,7 +842,8 @@ rfb_handle(struct rfb_softc *rc, int cfd) &ks, DES_ENCRYPT); if (memcmp(crypt_expected, buf, AUTH_LENGTH) != 0) { - message = "Auth Failed: Invalid Password."; + message = + (unsigned char *)"Auth Failed: Invalid Password."; sres = htonl(1); } else sres = 0; diff --git a/usr/src/cmd/bhyve/test/Makefile.com b/usr/src/cmd/bhyve/test/Makefile.com index a2e5bce08f..aeba956e12 100644 --- a/usr/src/cmd/bhyve/test/Makefile.com +++ b/usr/src/cmd/bhyve/test/Makefile.com @@ -32,6 +32,7 @@ CPPFLAGS = -I$(SRC)/cmd/bhyve \ -I$(CONTRIB)/bhyve/dev/usb/controller \ -I$(CONTRIB)/bhyve/dev/mii \ $(CPPFLAGS.master) \ + -I$(ROOT)/usr/platform/i86pc/include \ -I$(SRC)/uts/i86pc/io/vmm \ -I$(SRC)/uts/common \ -I$(SRC)/uts/i86pc \ diff --git a/usr/src/cmd/bhyve/test/tst/mevent/lists.delete.c b/usr/src/cmd/bhyve/test/tst/mevent/lists.delete.c index c5ed91a790..d09ac133a3 100644 --- a/usr/src/cmd/bhyve/test/tst/mevent/lists.delete.c +++ b/usr/src/cmd/bhyve/test/tst/mevent/lists.delete.c @@ -163,7 +163,7 @@ main(int argc, const char *argv[]) */ flush_and_wait(flush_pipe[1]); count2 = get_count(); - if (count1 - 1 != count2) { + if (count1 - 1 != count2 ) { FAIL(("mevent_delete() did not decrease count by 1: " "was %d, now %d", count1, count2)); } diff --git a/usr/src/cmd/bhyve/test/tst/mevent/testlib.c b/usr/src/cmd/bhyve/test/tst/mevent/testlib.c index 67261b9a31..af756d1509 100644 --- a/usr/src/cmd/bhyve/test/tst/mevent/testlib.c +++ b/usr/src/cmd/bhyve/test/tst/mevent/testlib.c @@ -25,8 +25,7 @@ const char *testlib_prog; boolean_t testlib_verbose; static void -timed_out(int signo) -{ +timed_out(int signo) { ASSERT_INT_EQ(("timeout signal"), signo, SIGALRM); FAIL(("Timed out")); diff --git a/usr/src/cmd/bhyve/test/tst/mevent/testlib.h b/usr/src/cmd/bhyve/test/tst/mevent/testlib.h index 7e5ca2e9c9..80949f3cc7 100644 --- a/usr/src/cmd/bhyve/test/tst/mevent/testlib.h +++ b/usr/src/cmd/bhyve/test/tst/mevent/testlib.h @@ -13,9 +13,6 @@ * Copyright 2018 Joyent, Inc. */ -#ifndef _TESTLIB_H_ -#define _TESTLIB_H_ - #include <assert.h> #include <errno.h> #include <signal.h> @@ -31,7 +28,7 @@ #define EXIT_PASS 0 #define EXIT_FAIL 1 -#define VERBOSE(msg) \ +#define VERBOSE(msg) \ if (testlib_verbose) { \ (void) printf("VERBOSE %s: %s:%d %s: ", testlib_prog, \ __FILE__, __LINE__, __func__); \ @@ -39,7 +36,7 @@ (void) printf("\n"); \ } -#define FAIL_PROLOGUE() \ +#define FAIL_PROLOGUE() \ (void) printf("FAIL %s: %s:%d: ", testlib_prog, __FILE__, __LINE__) #define FAIL(msg) \ @@ -50,7 +47,7 @@ exit(EXIT_FAIL); \ } -#define FAIL_ERRNO(msg) FAIL((msg ": %s", strerror(errno))) +#define FAIL_ERRNO(msg) FAIL((msg ": %s", strerror(errno))) #define PASS() \ { \ @@ -89,5 +86,3 @@ extern boolean_t testlib_verbose; extern void start_test(const char *, uint32_t); extern void start_event_thread(void); extern void test_mevent_count_lists(int *, int *, int *); - -#endif /* _TESTLIB_H_ */ diff --git a/usr/src/cmd/bhyve/zhyve.c b/usr/src/cmd/bhyve/zhyve.c new file mode 100644 index 0000000000..d3e764b14d --- /dev/null +++ b/usr/src/cmd/bhyve/zhyve.c @@ -0,0 +1,167 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2018, Joyent, Inc. + */ + +/* + * This small 'zhyve' stub is init for the zone: we therefore need to pick up + * our command-line arguments placed in ZHYVE_CMD_FILE by the boot stub, do a + * little administration, and exec the real bhyve binary. + */ + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <libnvpair.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <sys/corectl.h> + +#define ZHYVE_CMD_FILE "/var/run/bhyve/zhyve.cmd" + +/* + * Do a read of the specified size or return an error. Returns 0 on success + * and -1 on error. Sets errno to EINVAL if EOF is encountered. For other + * errors, see read(2). + */ +static int +full_read(int fd, char *buf, size_t len) +{ + ssize_t nread = 0; + size_t totread = 0; + + while (totread < len) { + nread = read(fd, buf + totread, len - totread); + if (nread == 0) { + errno = EINVAL; + return (-1); + } + if (nread < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + return (-1); + } + totread += nread; + } + assert(totread == len); + + return (0); +} + +/* + * Reads the command line options from the packed nvlist in the file referenced + * by path. On success, 0 is returned and the members of *argv reference memory + * allocated from an nvlist. On failure, -1 is returned. + */ + +static int +parse_options_file(const char *path, uint_t *argcp, char ***argvp) +{ + int fd = -1; + struct stat stbuf; + char *buf = NULL; + nvlist_t *nvl = NULL; + int ret; + + if ((fd = open(path, O_RDONLY)) < 0 || + fstat(fd, &stbuf) != 0 || + (buf = malloc(stbuf.st_size)) == NULL || + full_read(fd, buf, stbuf.st_size) != 0 || + nvlist_unpack(buf, stbuf.st_size, &nvl, 0) != 0 || + nvlist_lookup_string_array(nvl, "bhyve_args", argvp, argcp) != 0) { + nvlist_free(nvl); + ret = -1; + } else { + ret = 0; + } + + free(buf); + (void) close(fd); + + (void) printf("Configuration from %s:\n", path); + nvlist_print(stdout, nvl); + + return (ret); +} + +/* + * Setup to suppress core dumps within the zone. + */ +static void +config_core_dumps() +{ + (void) core_set_options(0x0); +} + +int +main(int argc, char **argv) +{ + char **tmpargs; + uint_t zargc; + char **zargv; + int fd; + + config_core_dumps(); + + fd = open("/dev/null", O_WRONLY); + assert(fd >= 0); + if (fd != STDIN_FILENO) { + (void) dup2(fd, STDIN_FILENO); + (void) close(fd); + } + + fd = open("/dev/zfd/1", O_WRONLY); + assert(fd >= 0); + if (fd != STDOUT_FILENO) { + (void) dup2(fd, STDOUT_FILENO); + (void) close(fd); + } + setvbuf(stdout, NULL, _IONBF, 0); + + fd = open("/dev/zfd/2", O_WRONLY); + assert(fd >= 0); + if (fd != STDERR_FILENO) { + (void) dup2(fd, STDERR_FILENO); + (void) close(fd); + } + setvbuf(stderr, NULL, _IONBF, 0); + + if (parse_options_file(ZHYVE_CMD_FILE, &zargc, &zargv) != 0) { + (void) fprintf(stderr, "%s: failed to parse %s: %s\n", + argv[0], ZHYVE_CMD_FILE, strerror(errno)); + return (EXIT_FAILURE); + } + + /* + * Annoyingly, we need a NULL at the end. + */ + + if ((tmpargs = malloc(sizeof (*zargv) * (zargc + 1))) == NULL) { + perror("malloc failed"); + return (EXIT_FAILURE); + } + + memcpy(tmpargs, zargv, sizeof (*zargv) * zargc); + tmpargs[zargc] = NULL; + + (void) execv("/usr/sbin/bhyve", tmpargs); + + perror("execv failed"); + return (EXIT_FAILURE); +} diff --git a/usr/src/cmd/cmd-crypto/etc/pkcs11.conf b/usr/src/cmd/cmd-crypto/etc/pkcs11.conf index 2b8971a85e..835e61f777 100644 --- a/usr/src/cmd/cmd-crypto/etc/pkcs11.conf +++ b/usr/src/cmd/cmd-crypto/etc/pkcs11.conf @@ -20,6 +20,7 @@ # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2011 Joyent, Inc. All rights reserved. # # /etc/crypto/pkcs11.conf # @@ -39,5 +40,4 @@ metaslot:metaslot_status=enabled;metaslot_auto_key_migrate=enabled;metaslot_token=Sun Software PKCS#11 softtoken;metaslot_slot=Sun Crypto Softtoken /usr/lib/security/$ISA/pkcs11_kernel.so /usr/lib/security/$ISA/pkcs11_softtoken.so -/usr/lib/security/$ISA/pkcs11_tpm.so # End SUNWcsr diff --git a/usr/src/cmd/cmd-inet/etc/services b/usr/src/cmd/cmd-inet/etc/services index 37514ac0a7..1029745480 100644 --- a/usr/src/cmd/cmd-inet/etc/services +++ b/usr/src/cmd/cmd-inet/etc/services @@ -1,6 +1,6 @@ -# # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2019 Joyent, Inc. # # CDDL HEADER START # @@ -22,9 +22,18 @@ # CDDL HEADER END # # Network services, Internet style -# Look at http://www.iana.org/assignments/port-numbers for more # -tcpmux 1/tcp +# Note that it is presently the policy of IANA to assign a single well-known +# port number for both TCP and UDP; hence, officially ports have two entries +# even if the protocol doesn't support UDP operations. +# +# Updated from http://www.iana.org/assignments/port-numbers and other +# sources like http://www.freebsd.org/cgi/cvsweb.cgi/src/etc/services . +# New ports will be added on request if they have been officially assigned +# by IANA and used in the real-world or are needed by a debian package. +# If you need a huge list of used numbers please install the nmap package. + +tcpmux 1/tcp # TCP port service multiplexer echo 7/tcp echo 7/udp discard 9/tcp sink null @@ -33,207 +42,614 @@ systat 11/tcp users daytime 13/tcp daytime 13/udp netstat 15/tcp -qotd 17/tcp # Quote of the Day +qotd 17/tcp quote +msp 18/tcp # message send protocol +msp 18/udp chargen 19/tcp ttytst source chargen 19/udp ttytst source ftp-data 20/tcp ftp 21/tcp ftp 21/sctp -ssh 22/tcp # Secure Shell +fsp 21/udp fspd +ssh 22/tcp # SSH Remote Login Protocol ssh 22/sctp +ssh 22/udp telnet 23/tcp smtp 25/tcp mail time 37/tcp timserver time 37/udp timserver rlp 39/tcp # Resource Location Protocol -rlp 39/udp # Resource Location Protocol -name 42/udp nameserver -whois 43/tcp nicname # usually to sri-nic -tacacs 49/tcp +rlp 39/udp resource # resource location +nameserver 42/tcp name # IEN 116 +nameserver 42/udp name +whois 43/tcp nicname +tacacs 49/tcp # Login Host Protocol (TACACS) tacacs 49/udp +re-mail-ck 50/tcp # Remote Mail Checking Protocol +re-mail-ck 50/udp +domain 53/tcp # name-domain server domain 53/udp -domain 53/tcp -tacacs-ds 65/tcp +mtp 57/tcp # deprecated +tacacs-ds 65/tcp # TACACS-Database Service tacacs-ds 65/udp -bootps 67/udp # BOOTP/DHCP server -bootpc 68/udp # BOOTP/DHCP client -http 80/tcp www www-http -http 80/udp www www-http -http 80/sctp -kerberos 88/udp kdc # Kerberos V5 KDC -kerberos 88/tcp kdc # Kerberos V5 KDC -hostnames 101/tcp hostname # usually to sri-nic -pop2 109/tcp pop-2 # Post Office Protocol - V2 -pop3 110/tcp # Post Office Protocol - Version 3 -sunrpc 111/udp rpcbind -sunrpc 111/tcp rpcbind +bootps 67/tcp # BOOTP server +bootps 67/udp +bootpc 68/tcp # BOOTP client +bootpc 68/udp +tftp 69/udp +gopher 70/tcp # Internet Gopher +gopher 70/udp +rje 77/tcp netrjs +finger 79/tcp +www 80/tcp http # WorldWideWeb HTTP +www 80/sctp http +www 80/udp # HyperText Transfer Protocol +link 87/tcp ttylink +kerberos 88/tcp kerberos5 krb5 kerberos-sec # Kerberos v5 +kerberos 88/udp kerberos5 krb5 kerberos-sec # Kerberos v5 +supdup 95/tcp +hostnames 101/tcp hostname # usually from sri-nic +iso-tsap 102/tcp tsap # part of ISODE +x400 103/tcp # ISO Mail +acr-nema 104/tcp dicom # Digital Imag. & Comm. 300 +acr-nema 104/udp dicom # Digital Imag. & Comm. 300 +csnet-ns 105/tcp cso-ns # also used by CSO name server +csnet-ns 105/udp cso-ns +rtelnet 107/tcp # Remote Telnet +rtelnet 107/udp +pop2 109/tcp postoffice pop-2 # POP version 2 +pop2 109/udp pop-2 +pop3 110/tcp pop-3 # POP version 3 +pop3 110/udp pop-3 +sunrpc 111/tcp portmapper # RPC 4.0 portmapper +sunrpc 111/udp portmapper +auth 113/tcp authentication tap ident sftp 115/tcp -imap 143/tcp imap2 # Internet Mail Access Protocol v2 +uucp-path 117/tcp +nntp 119/tcp readnews untp # USENET News Transfer Protocol +ntp 123/tcp +ntp 123/udp # Network Time Protocol +pwdgen 129/tcp # PWDGEN service +pwdgen 129/udp # PWDGEN service +loc-srv 135/tcp epmap # Location Service +loc-srv 135/udp epmap +netbios-ns 137/tcp # NETBIOS Name Service +netbios-ns 137/udp +netbios-dgm 138/tcp # NETBIOS Datagram Service +netbios-dgm 138/udp +netbios-ssn 139/tcp # NETBIOS session service +netbios-ssn 139/udp +imap2 143/tcp imap # Interim Mail Access P 2 and 4 +imap2 143/udp imap +snmp 161/tcp # Simple Net Mgmt Protocol +snmp 161/udp # Simple Net Mgmt Protocol +snmp-trap 162/tcp snmptrap # Traps for SNMP +snmp-trap 162/udp snmptrap # Traps for SNMP +cmip-man 163/tcp # ISO mgmt over IP (CMOT) +cmip-man 163/udp +cmip-agent 164/tcp +cmip-agent 164/udp +mailq 174/tcp # Mailer transport queue for Zmailer +mailq 174/udp # Mailer transport queue for Zmailer +xdmcp 177/tcp # X Display Mgr. Control Proto +xdmcp 177/udp +nextstep 178/tcp NeXTStep NextStep # NeXTStep window +nextstep 178/udp NeXTStep NextStep # server bgp 179/tcp # Border Gateway Protocol -bgp 179/udp bgp 179/sctp -irc 194/tcp +bgp 179/udp +prospero 191/tcp # Cliff Neuman's Prospero +prospero 191/udp +irc 194/tcp # Internet Relay Chat irc 194/udp -smux 199/tcp +smux 199/tcp # SNMP Unix Multiplexer smux 199/udp -imap3 220/tcp -imap3 220/udp -clearcase 371/tcp -clearcase 371/udp -ldap 389/tcp # Lightweight Directory Access Protocol -ldap 389/udp # Lightweight Directory Access Protocol -https 443/tcp +at-rtmp 201/tcp # AppleTalk routing +at-rtmp 201/udp +at-nbp 202/tcp # AppleTalk name binding +at-nbp 202/udp +at-echo 204/tcp # AppleTalk echo +at-echo 204/udp +at-zis 206/tcp # AppleTalk zone information +at-zis 206/udp +qmtp 209/tcp # Quick Mail Transfer Protocol +qmtp 209/udp # Quick Mail Transfer Protocol +z3950 210/tcp wais # NISO Z39.50 database +z3950 210/udp wais +ipx 213/tcp # IPX +ipx 213/udp +imap3 220/tcp # Interactive Mail Access +imap3 220/udp # Protocol v3 +pawserv 345/tcp # Perf Analysis Workbench +pawserv 345/udp +zserv 346/tcp # Zebra server +zserv 346/udp +fatserv 347/tcp # Fatmen Server +fatserv 347/udp +rpc2portmap 369/tcp +rpc2portmap 369/udp # Coda portmapper +codaauth2 370/tcp +codaauth2 370/udp # Coda authentication server +clearcase 371/tcp Clearcase +clearcase 371/udp Clearcase +ulistserv 372/tcp # UNIX Listserv +ulistserv 372/udp +ldap 389/tcp # Lightweight Directory Access Protocol +ldap 389/udp +imsp 406/tcp # Interactive Mail Support Protocol +imsp 406/udp +slp 427/tcp slp # Service Location Protocol, V2 +slp 427/udp slp # Service Location Protocol, V2 +mobile-ip 434/udp mobile-ip # Mobile-IP +cvc_hostd 442/tcp # Network Console +https 443/tcp # http protocol over TLS/SSL https 443/udp https 443/sctp +snpp 444/tcp # Simple Network Paging Protocol +snpp 444/udp +microsoft-ds 445/tcp # Microsoft Naked CIFS +microsoft-ds 445/udp kpasswd 464/tcp kpasswd 464/udp +saft 487/tcp # Simple Asynchronous File Transfer +saft 487/udp +isakmp 500/tcp # IPsec - Internet Security Association +isakmp 500/udp # and Key Management Protocol dhcpv6-client 546/udp dhcpv6c # DHCPv6 Client (RFC 3315) dhcpv6-client 546/tcp dhcpv6-server 547/udp dhcpv6s # DHCPv6 Server (RFC 3315) dhcpv6-server 547/tcp -rtsp 554/tcp -rtsp 554/udp -nntps 563/tcp snntp -nntps 563/udp snntp -submission 587/tcp # Mail Message Submission -submission 587/udp # see RFC 2476 -ipp 631/tcp +rtsp 554/tcp # Real Time Stream Control Protocol +rtsp 554/udp # Real Time Stream Control Protocol +nqs 607/tcp # Network Queuing system +nqs 607/udp +npmp-local 610/tcp dqs313_qmaster # npmp-local / DQS +npmp-local 610/udp dqs313_qmaster +npmp-gui 611/tcp dqs313_execd # npmp-gui / DQS +npmp-gui 611/udp dqs313_execd +hmmp-ind 612/tcp dqs313_intercell # HMMP Indication / DQS +hmmp-ind 612/udp dqs313_intercell +qmqp 628/tcp +qmqp 628/udp +ipp 631/tcp # Internet Printing Protocol ipp 631/udp -ldaps 636/tcp # LDAP protocol over TLS/SSL (was sldap) -ldaps 636/udp # LDAP protocol over TLS/SSL (was sldap) -silc 706/tcp -silc 706/udp -iscsi 860/tcp -iscsi 860/udp -rsync 873/tcp -rsync 873/udp -ftps-data 989/tcp -ftps-data 989/udp -ftps 990/tcp -ftps 990/udp -imaps 993/tcp -imaps 993/udp -pop3s 995/tcp -pop3s 995/udp -socks 1080/tcp -socks 1080/udp -openvpn 1194/tcp -openvpn 1194/udp -icap 1344/tcp # Internet Content Adaptation Protocol -wins 1512/tcp -wins 1512/udp -radius 1812/tcp -radius 1812/udp -radius-acct 1813/tcp -radius-acct 1813/udp -cvspserver 2401/tcp -icpv2 3130/tcp -icpv2 3130/udp -iscsi-target 3260/tcp -iscsi-target 3260/udp -mysql 3306/tcp -mysql 3306/udp -nut 3493/tcp # Network UPS Tools -svn 3690/tcp -svn 3690/udp -epmd 4369/tcp # Erlang Port Mapper Daemon -epmd 4369/udp -sip 5060/tcp -sip 5060/udp -sip-tls 5061/tcp -sip-tls 5061/udp -xmpp-client 5222/tcp -xmpp-server 5269/tcp -postgresql 5432/tcp postgres -postgresql 5432/udp postgres -http-alt 8080/tcp webcache # HTTP Alternate, webcache -http-alt 8080/udp -memcache 11211/tcp -memcache 11211/udp -# -# Host specific functions -# -tftp 69/udp -rje 77/tcp -finger 79/tcp -link 87/tcp ttylink -supdup 95/tcp -iso-tsap 102/tcp -x400 103/tcp # ISO Mail -x400-snd 104/tcp -csnet-ns 105/tcp -uucp-path 117/tcp -nntp 119/tcp usenet # Network News Transfer -ntp 123/tcp # Network Time Protocol -ntp 123/udp # Network Time Protocol -netbios-ns 137/tcp # NETBIOS Name Service -netbios-ns 137/udp # NETBIOS Name Service -netbios-dgm 138/tcp # NETBIOS Datagram Service -netbios-dgm 138/udp # NETBIOS Datagram Service -netbios-ssn 139/tcp # NETBIOS Session Service -netbios-ssn 139/udp # NETBIOS Session Service -NeWS 144/tcp news # Window System -snmpd 161/udp snmp # Net-SNMP snmp daemon -slp 427/tcp slp # Service Location Protocol, V2 -slp 427/udp slp # Service Location Protocol, V2 -mobile-ip 434/udp mobile-ip # Mobile-IP -cvc_hostd 442/tcp # Network Console -microsoft-ds 445/tcp # Microsoft Directory Services -microsoft-ds 445/udp # Microsoft Directory Services -ike 500/udp ike # Internet Key Exchange uuidgen 697/tcp # UUID Generator uuidgen 697/udp # UUID Generator # # UNIX specific services # -# these are NOT officially assigned -# rdc 121/tcp # SNDR server daemon exec 512/tcp -login 513/tcp -shell 514/tcp cmd # no passwords used -printer 515/tcp spooler # line printer spooler -courier 530/tcp rpc # experimental -uucp 540/tcp uucpd # uucp daemon biff 512/udp comsat +login 513/tcp who 513/udp whod +shell 514/tcp cmd # no passwords used syslog 514/udp +printer 515/tcp spooler # line printer spooler talk 517/udp -route 520/udp router routed +ntalk 518/udp +route 520/udp router routed # RIP ripng 521/udp -klogin 543/tcp # Kerberos authenticated rlogin -kshell 544/tcp cmd # Kerberos authenticated remote shell +timed 525/udp timeserver +tempo 526/tcp newdate +courier 530/tcp rpc +conference 531/tcp chat +netnews 532/tcp readnews +netwall 533/udp # for emergency broadcasts +gdomap 538/tcp # GNUstep distributed objects +gdomap 538/udp +uucp 540/tcp uucpd # uucp daemon +klogin 543/tcp # Kerberized `rlogin' (v5) +kshell 544/tcp krcmd # Kerberized `rsh' (v5) +afpovertcp 548/tcp # AFP over TCP +afpovertcp 548/udp new-rwho 550/udp new-who # experimental +remotefs 556/tcp rfs_server rfs # Brunhoff remote filesystem rmonitor 560/udp rmonitord # experimental monitor 561/udp # experimental +nntps 563/tcp snntp # NNTP over SSL +nntps 563/udp snntp +submission 587/tcp # Submission [RFC4409] +submission 587/udp pcserver 600/tcp # ECD Integrated PC board srvr +ldaps 636/tcp # LDAP over SSL +ldaps 636/udp +tinc 655/tcp # tinc control port +tinc 655/udp sun-dr 665/tcp # Remote Dynamic Reconfiguration -kerberos-adm 749/tcp # Kerberos V5 Administration +silc 706/tcp +silc 706/udp +kerberos-adm 749/tcp # Kerberos `kadmin' (v5) kerberos-adm 749/udp # Kerberos V5 Administration kerberos-iv 750/udp # Kerberos V4 key server krb5_prop 754/tcp # Kerberos V5 KDC propogation -swat 901/tcp # Samba Web Adm.Tool +# +webster 765/tcp # Network dictionary +webster 765/udp +iscsi 860/tcp +iscsi 860/udp +rsync 873/tcp +rsync 873/udp +ftps-data 989/tcp # FTP over SSL (data) +ftps 990/tcp +telnets 992/tcp # Telnet over SSL +telnets 992/udp +imaps 993/tcp # IMAP over SSL +imaps 993/udp +ircs 994/tcp # IRC over SSL +ircs 994/udp +pop3s 995/tcp # POP-3 over SSL +pop3s 995/udp ufsd 1008/tcp ufsd # UFS-aware server ufsd 1008/udp ufsd +portolan 1296/tcp # Portolan +svp-underlay 1339/tcp # SDC VXLAN underlay invalidation cvc 1495/tcp # Network Console +# +# From ``Assigned Numbers'': +# +#> The Registered Ports are not controlled by the IANA and on most systems +#> can be used by ordinary user processes or programs executed by ordinary +#> users. +# +#> Ports are used in the TCP [45,106] to name the ends of logical +#> connections which carry long term conversations. For the purpose of +#> providing services to unknown callers, a service contact port is +#> defined. This list specifies the port used by the server process as its +#> contact port. While the IANA can not control uses of these ports it +#> does register or list uses of these ports as a convienence to the +#> community. +# +socks 1080/tcp # socks proxy server +socks 1080/udp +proofd 1093/tcp +proofd 1093/udp +rootd 1094/tcp +rootd 1094/udp +openvpn 1194/tcp +openvpn 1194/udp +rmiregistry 1099/tcp # Java RMI Registry +rmiregistry 1099/udp +kazaa 1214/tcp +kazaa 1214/udp +nessus 1241/tcp # Nessus vulnerability +nessus 1241/udp # assessment scanner +icap 1344/tcp # Internet Content Adaptation Protocol +lotusnote 1352/tcp lotusnotes # Lotus Note +lotusnote 1352/udp lotusnotes +ms-sql-s 1433/tcp # Microsoft SQL Server +ms-sql-s 1433/udp +ms-sql-m 1434/tcp # Microsoft SQL Monitor +ms-sql-m 1434/udp +wins 1512/tcp +wins 1512/udp ingreslock 1524/tcp +ingreslock 1524/udp +prospero-np 1525/tcp # Prospero non-privileged +prospero-np 1525/udp +datametrics 1645/tcp old-radius +datametrics 1645/udp old-radius +sa-msg-port 1646/tcp old-radacct +sa-msg-port 1646/udp old-radacct +kermit 1649/tcp +kermit 1649/udp +l2f 1701/tcp l2tp +l2f 1701/udp l2tp www-ldap-gw 1760/tcp # HTTP to LDAP gateway www-ldap-gw 1760/udp # HTTP to LDAP gateway -listen 2766/tcp # System V listener port -nfsd 2049/udp nfs # NFS server daemon (clts) -nfsd 2049/tcp nfs # NFS server daemon (cots) +radius 1812/tcp +radius 1812/udp +radius-acct 1813/tcp radacct # Radius Accounting +radius-acct 1813/udp radacct +msnp 1863/tcp # MSN Messenger +msnp 1863/udp +unix-status 1957/tcp # remstats unix-status server +log-server 1958/tcp # remstats log server +remoteping 1959/tcp # remstats remoteping server +cisco-sccp 2000/tcp sieve # Cisco SCCP +cisco-sccp 2000/udp +search 2010/tcp ndtp +pipe_server 2010/tcp +nfs 2049/tcp # Network File System +nfs 2049/udp # Network File System nfsd 2049/sctp nfs -eklogin 2105/tcp # Kerberos encrypted rlogin +gnunet 2086/tcp +gnunet 2086/udp +rtcm-sc104 2101/tcp # RTCM SC-104 IANA 1/29/99 +rtcm-sc104 2101/udp +gsigatekeeper 2119/tcp +gsigatekeeper 2119/udp +gris 2135/tcp # Grid Resource Information Server +gris 2135/udp # Grid Resource Information Server +cvspserver 2401/tcp # CVS client/server operations +cvspserver 2401/udp +venus 2430/tcp # codacon port +venus 2430/udp # Venus callback/wbc interface +venus-se 2431/tcp # tcp side effects +venus-se 2431/udp # udp sftp side effect +codasrv 2432/tcp # not used +codasrv 2432/udp # server port +codasrv-se 2433/tcp # tcp side effects +codasrv-se 2433/udp # udp sftp side effect +mon 2583/tcp # MON traps +mon 2583/udp +dict 2628/tcp # Dictionary server +dict 2628/udp +listen 2766/tcp # System V listener port +gsiftp 2811/tcp +gsiftp 2811/udp +gpsd 2947/tcp +gpsd 2947/udp +gds_db 3050/tcp # InterBase server +gds_db 3050/udp +icpv2 3130/tcp icp # Internet Cache Protocol +icpv2 3130/udp icp +iscsi-target 3260/tcp +iscsi-target 3260/udp +mysql 3306/tcp +mysql 3306/udp +nut 3493/tcp # Network UPS Tools +nut 3493/udp +distcc 3632/tcp # distributed compiler +distcc 3632/udp +daap 3689/tcp # Digital Audio Access Protocol +daap 3689/udp +svn 3690/tcp subversion # Subversion protocol +svn 3690/udp subversion +suucp 4031/tcp # UUCP over SSL +suucp 4031/udp # UUCP over SSL lockd 4045/udp # NFS lock daemon/manager lockd 4045/tcp +sysrqd 4094/tcp # sysrq daemon +sysrqd 4094/udp # sysrq daemon +remctl 4373/tcp # Remote Authenticated Command Service +remctl 4373/udp # Remote Authenticated Command Service +epmd 4369/tcp # Erlang Port Mapper Daemon +epmd 4369/udp ipsec-nat-t 4500/udp # IPsec NAT-Traversal +iax 4569/tcp # Inter-Asterisk eXchange +iax 4569/udp +radmin-port 4899/tcp # RAdmin Port +radmin-port 4899/udp +rfe 5002/udp # Radio Free Ethernet +rfe 5002/tcp +mmcc 5050/tcp # multimedia conference control tool (Yahoo IM) +mmcc 5050/udp +sip 5060/tcp # Session Initiation Protocol +sip 5060/udp +sip-tls 5061/tcp +sip-tls 5061/udp +aol 5190/tcp # AIM +aol 5190/udp +xmpp-client 5222/tcp jabber-client # Jabber Client Connection +xmpp-client 5222/udp jabber-client +xmpp-server 5269/tcp jabber-server # Jabber Server Connection +xmpp-server 5269/udp jabber-server +cfengine 5308/tcp +cfengine 5308/udp +mdns 5353/tcp # Multicast DNS mdns 5353/udp # Multicast DNS -mdns 5353/tcp +postgresql 5432/tcp postgres # PostgreSQL Database +postgresql 5432/udp postgres +freeciv 5556/tcp rptp # Freeciv gameplay +freeciv 5556/udp +amqp 5672/tcp +amqp 5672/udp +amqp 5672/sctp +ggz 5688/tcp # GGZ Gaming Zone +ggz 5688/udp # GGZ Gaming Zone vnc-server 5900/tcp # VNC Server +x11 6000/tcp x11-0 # X Window System +x11 6000/udp x11-0 +x11-1 6001/tcp +x11-1 6001/udp +x11-2 6002/tcp +x11-2 6002/udp +x11-3 6003/tcp +x11-3 6003/udp +x11-4 6004/tcp +x11-4 6004/udp +x11-5 6005/tcp +x11-5 6005/udp +x11-6 6006/tcp +x11-6 6006/udp +x11-7 6007/tcp +x11-7 6007/udp dtspc 6112/tcp # CDE subprocess control +gnutella-svc 6346/tcp # gnutella +gnutella-svc 6346/udp +gnutella-rtr 6347/tcp # gnutella +gnutella-rtr 6347/udp +sge_qmaster 6444/tcp # Grid Engine Qmaster Service +sge_qmaster 6444/udp # Grid Engine Qmaster Service +sge_execd 6445/tcp # Grid Engine Execution Service +sge_execd 6445/udp # Grid Engine Execution Service servicetag 6481/udp servicetag 6481/tcp -fs 7100/tcp # Font server +afs3-fileserver 7000/tcp bbs # file server itself +afs3-fileserver 7000/udp bbs +afs3-callback 7001/tcp # callbacks to cache managers +afs3-callback 7001/udp +afs3-prserver 7002/tcp # users & groups database +afs3-prserver 7002/udp +afs3-vlserver 7003/tcp # volume location database +afs3-vlserver 7003/udp +afs3-kaserver 7004/tcp # AFS/Kerberos authentication +afs3-kaserver 7004/udp +afs3-volser 7005/tcp # volume managment server +afs3-volser 7005/udp +afs3-errors 7006/tcp # error interpretation service +afs3-errors 7006/udp +afs3-bos 7007/tcp # basic overseer process +afs3-bos 7007/udp +afs3-update 7008/tcp # server-to-server updater +afs3-update 7008/udp +afs3-rmtsys 7009/tcp # remote cache manager service +afs3-rmtsys 7009/udp +font-service 7100/tcp xfs # X Font Service +font-service 7100/udp xfs +http-alt 8080/tcp webcache # WWW caching service +http-alt 8080/udp # WWW caching service +bacula-dir 9101/tcp # Bacula Director +bacula-dir 9101/udp +bacula-fd 9102/tcp # Bacula File Daemon +bacula-fd 9102/udp +bacula-sd 9103/tcp # Bacula Storage Daemon +bacula-sd 9103/udp +xmms2 9667/tcp # Cross-platform Music Multiplexing System +xmms2 9667/udp # Cross-platform Music Multiplexing System +amanda 10080/tcp # amanda backup services +amanda 10080/udp +memcache 11211/tcp +memcache 11211/udp +hkp 11371/tcp # OpenPGP HTTP Keyserver +hkp 11371/udp # OpenPGP HTTP Keyserver +bprd 13720/tcp # VERITAS NetBackup +bprd 13720/udp +bpdbm 13721/tcp # VERITAS NetBackup +bpdbm 13721/udp +bpjava-msvc 13722/tcp # BP Java MSVC Protocol +bpjava-msvc 13722/udp +vnetd 13724/tcp # Veritas Network Utility +vnetd 13724/udp +bpcd 13782/tcp # VERITAS NetBackup +bpcd 13782/udp +vopied 13783/tcp # VERITAS NetBackup +vopied 13783/udp solaris-audit 16162/tcp # Secure remote audit logging -wnn6 22273/tcp # Wnn6 jserver -wnn6 22273/udp # Wnn6 jserver +wnn6 22273/tcp # wnn6 +wnn6 22273/udp + +# +# Datagram Delivery Protocol services +# +rtmp 1/ddp # Routing Table Maintenance Protocol +nbp 2/ddp # Name Binding Protocol +echo 4/ddp # AppleTalk Echo Protocol +zip 6/ddp # Zone Information Protocol + +#========================================================================= +# The remaining port numbers are not as allocated by IANA. +#========================================================================= + +# Kerberos (Project Athena/MIT) services +# Note that these are for Kerberos v4, and are unofficial. Sites running +# v4 should uncomment these and comment out the v5 entries above. +# +kerberos4 750/udp kerberos-iv kdc # Kerberos (server) +kerberos4 750/tcp kerberos-iv kdc +kerberos_master 751/udp # Kerberos authentication +kerberos_master 751/tcp +passwd_server 752/udp # Kerberos passwd server +krb_prop 754/tcp krb5_prop hprop # Kerberos slave propagation +krbupdate 760/tcp kreg # Kerberos registration +swat 901/tcp # swat +kpop 1109/tcp # Pop with Kerberos +knetd 2053/tcp # Kerberos de-multiplexor +zephyr-srv 2102/udp # Zephyr server +zephyr-clt 2103/udp # Zephyr serv-hm connection +zephyr-hm 2104/udp # Zephyr hostmanager +eklogin 2105/tcp # Kerberos encrypted rlogin +# Hmmm. Are we using Kv4 or Kv5 now? Worrying. +# The following is probably Kerberos v5 --- ajt@debian.org (11/02/2000) +kx 2111/tcp # X over Kerberos +iprop 2121/tcp # incremental propagation +# +# Unofficial but necessary (for NetBSD) services +# +supfilesrv 871/tcp # SUP server +supfiledbg 1127/tcp # SUP debugging + +# +# Services added for the Debian GNU/Linux distribution +# +linuxconf 98/tcp # LinuxConf +poppassd 106/tcp # Eudora +poppassd 106/udp +ssmtp 465/tcp smtps # SMTP over SSL +moira_db 775/tcp # Moira database +moira_update 777/tcp # Moira update protocol +moira_ureg 779/udp # Moira user registration +spamd 783/tcp # spamassassin daemon +omirr 808/tcp omirrd # online mirror +omirr 808/udp omirrd +customs 1001/tcp # pmake customs server +customs 1001/udp +skkserv 1178/tcp # skk jisho server port +predict 1210/udp # predict -- satellite tracking +rmtcfg 1236/tcp # Gracilis Packeten remote config server +wipld 1300/tcp # Wipl network monitor +xtel 1313/tcp # french minitel +xtelw 1314/tcp # french minitel +support 1529/tcp # GNATS +cfinger 2003/tcp # GNU Finger +frox 2121/tcp # frox: caching ftp proxy +ninstall 2150/tcp # ninstall service +ninstall 2150/udp +zebrasrv 2600/tcp # zebra service +zebra 2601/tcp # zebra vty +ripd 2602/tcp # ripd vty (zebra) +ripngd 2603/tcp # ripngd vty (zebra) +ospfd 2604/tcp # ospfd vty (zebra) +bgpd 2605/tcp # bgpd vty (zebra) +ospf6d 2606/tcp # ospf6d vty (zebra) +ospfapi 2607/tcp # OSPF-API +isisd 2608/tcp # ISISd vty (zebra) +afbackup 2988/tcp # Afbackup system +afbackup 2988/udp +afmbackup 2989/tcp # Afmbackup system +afmbackup 2989/udp +xtell 4224/tcp # xtell server +fax 4557/tcp # FAX transmission service (old) +hylafax 4559/tcp # HylaFAX client-server protocol (new) +distmp3 4600/tcp # distmp3host daemon +munin 4949/tcp lrrd # Munin +enbd-cstatd 5051/tcp # ENBD client statd +enbd-sstatd 5052/tcp # ENBD server statd +pcrd 5151/tcp # PCR-1000 Daemon +noclog 5354/tcp # noclogd with TCP (nocol) +noclog 5354/udp # noclogd with UDP (nocol) +hostmon 5355/tcp # hostmon uses TCP (nocol) +hostmon 5355/udp # hostmon uses UDP (nocol) +rplay 5555/udp # RPlay audio service +nsca 5667/tcp # Nagios Agent - NSCA +mrtd 5674/tcp # MRT Routing Daemon +bgpsim 5675/tcp # MRT Routing Simulator +canna 5680/tcp # cannaserver +sane-port 6566/tcp sane saned # SANE network scanner daemon +ircd 6667/tcp # Internet Relay Chat +zope-ftp 8021/tcp # zope management by ftp +tproxy 8081/tcp # Transparent Proxy +omniorb 8088/tcp # OmniORB +omniorb 8088/udp +clc-build-daemon 8990/tcp # Common lisp build daemon +xinetd 9098/tcp +mandelspawn 9359/udp mandelbrot # network mandelbrot +git 9418/tcp # Git Version Control System +zope 9673/tcp # zope server +webmin 10000/tcp +kamanda 10081/tcp # amanda backup services (Kerberos) +kamanda 10081/udp +amandaidx 10082/tcp # amanda backup services +amidxtape 10083/tcp # amanda backup services +smsqp 11201/tcp # Alamin SMS gateway +smsqp 11201/udp +xpilot 15345/tcp # XPilot Contact Port +xpilot 15345/udp +sgi-cmsd 17001/udp # Cluster membership services daemon +sgi-crsd 17002/udp +sgi-gcd 17003/udp # SGI Group membership daemon +sgi-cad 17004/tcp # Cluster Admin daemon +isdnlog 20011/tcp # isdn logging system +isdnlog 20011/udp +vboxd 20012/tcp # voice box system +vboxd 20012/udp +binkp 24554/tcp # binkp fidonet protocol +asp 27374/tcp # Address Search Protocol +asp 27374/udp +csync2 30865/tcp # cluster synchronization tool +dircproxy 57000/tcp # Detachable IRC Proxy +tfido 60177/tcp # fidonet EMSI over telnet +fido 60179/tcp # fidonet EMSI over TCP + +# Local services diff --git a/usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel b/usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel index c62e339953..49151907eb 100644 --- a/usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel +++ b/usr/src/cmd/cmd-inet/etc/sock2path.d/system%2Fkernel @@ -18,6 +18,7 @@ # CDDL HEADER END # # Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, Joyent, Inc. All rights reserved. # # socket configuration information # @@ -52,3 +53,6 @@ 29 4 1 /dev/spdsock 31 1 0 trill + + 33 1 0 lx_netlink + 33 4 0 lx_netlink diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile b/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile index f9ae4eacdc..499d0b89b0 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/Makefile @@ -19,13 +19,15 @@ # CDDL HEADER END # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2015 Joyent, Inc. # # Needed for ROOTFS_LIBDIR definition include ../../../../lib/Makefile.lib PROG= ipmgmtd -OBJS= ipmgmt_main.o ipmgmt_door.o ipmgmt_persist.o ipmgmt_util.o +OBJS= ipmgmt_main.o ipmgmt_door.o ipmgmt_persist.o ipmgmt_util.o \ + ipmgmt_path.o SRCS= $(OBJS:.o=.c) SVCMETHOD= net-ipmgmt MANIFEST= network-ipmgmt.xml diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c index c83c7627ad..bb4ffcbf0e 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_door.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2014, Joyent, Inc. All rights reserved. * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>. */ @@ -113,7 +114,9 @@ ipmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, goto fail; } - /* check for solaris.network.interface.config authorization */ + /* + * if not root, check for solaris.network.interface.config authorization + */ if (infop->idi_set) { uid_t uid; struct passwd pwd; @@ -125,24 +128,32 @@ ipmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp, goto fail; } uid = ucred_getruid(cred); + ucred_free(cred); if ((int)uid < 0) { err = errno; ipmgmt_log(LOG_ERR, "Could not get user id."); goto fail; } - if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == - NULL) { - err = errno; - ipmgmt_log(LOG_ERR, "Could not get password entry."); - goto fail; - } - if (chkauthattr(NETWORK_INTERFACE_CONFIG_AUTH, - pwd.pw_name) != 1) { - err = EPERM; - ipmgmt_log(LOG_ERR, "Not authorized for operation."); - goto fail; + + /* + * Branded zones may have different auth, but root always + * allowed. + */ + if (uid != 0) { + if (getpwuid_r(uid, &pwd, buf, sizeof (buf)) == NULL) { + err = errno; + ipmgmt_log(LOG_ERR, + "Could not get password entry."); + goto fail; + } + if (chkauthattr(NETWORK_INTERFACE_CONFIG_AUTH, + pwd.pw_name) != 1) { + err = EPERM; + ipmgmt_log(LOG_ERR, + "Not authorized for operation."); + goto fail; + } } - ucred_free(cred); } /* individual handlers take care of calling door_return */ diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h index e95e5c7e00..f4d6d30645 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_impl.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Joyent, Inc. * Copyright (c) 2016, Chris Fraire <cfraire@me.com>. */ @@ -144,8 +145,6 @@ extern ipmgmt_aobjmap_list_t aobjmap; #define ADDROBJ_LOOKUPADD 0x00000004 #define ADDROBJ_SETLIFNUM 0x00000008 -/* Permanent data store for ipadm */ -#define IPADM_DB_FILE "/etc/ipadm/ipadm.conf" #define IPADM_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) /* @@ -155,20 +154,12 @@ extern ipmgmt_aobjmap_list_t aobjmap; */ #define IPADM_DB_VERSION 1 -/* - * A temporary file created in SMF volatile filesystem. This file captures the - * in-memory copy of list `aobjmap' on disk. This is done to recover from - * daemon reboot (using svcadm) or crashes. - */ -#define IPADM_TMPFS_DIR "/etc/svc/volatile/ipadm" -#define ADDROBJ_MAPPING_DB_FILE IPADM_TMPFS_DIR"/aobjmap.conf" - -/* - * A temporary copy of the ipadm configuration file might need - * to be created if write requests are encountered during boottime - * and the root filesystem is mounted read-only. - */ -#define IPADM_VOL_DB_FILE IPADM_TMPFS_DIR"/ipadm.conf" +typedef enum ipadm_path { + IPADM_PATH_TMPFS_DIR = 1, + IPADM_PATH_ADDROBJ_MAP_DB, + IPADM_PATH_DB, + IPADM_PATH_VOL_DB +} ipadm_path_t; /* SCF resources required to interact with svc.configd */ typedef struct scf_resources { @@ -198,6 +189,8 @@ extern void ipmgmt_release_scf_resources(scf_resources_t *); extern boolean_t ipmgmt_needs_upgrade(scf_resources_t *); extern void ipmgmt_update_dbver(scf_resources_t *); +extern void ipmgmt_path(ipadm_path_t, char *, size_t); + #ifdef __cplusplus } #endif diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c index 5cdc0f5697..994d1b0125 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_main.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015 Joyent, Inc. */ /* @@ -105,6 +106,7 @@ ipmgmt_db_init() int fd, err, scferr; scf_resources_t res; boolean_t upgrade = B_TRUE; + char aobjpath[MAXPATHLEN]; /* * Check to see if we need to upgrade the data-store. We need to @@ -134,11 +136,11 @@ ipmgmt_db_init() ipmgmt_release_scf_resources(&res); /* creates the address object data store, if it doesn't exist */ - if ((fd = open(ADDROBJ_MAPPING_DB_FILE, O_CREAT|O_RDONLY, - IPADM_FILE_MODE)) == -1) { + ipmgmt_path(IPADM_PATH_ADDROBJ_MAP_DB, aobjpath, sizeof (aobjpath)); + if ((fd = open(aobjpath, O_CREAT|O_RDONLY, IPADM_FILE_MODE)) == -1) { err = errno; - ipmgmt_log(LOG_ERR, "could not open %s: %s", - ADDROBJ_MAPPING_DB_FILE, strerror(err)); + ipmgmt_log(LOG_ERR, "could not open %s: %s", aobjpath, + strerror(err)); return (err); } (void) close(fd); @@ -152,8 +154,8 @@ ipmgmt_db_init() * representation of the mapping. That is, build `aobjmap' structure * from address object data store. */ - if ((err = ipadm_rw_db(ipmgmt_aobjmap_init, NULL, - ADDROBJ_MAPPING_DB_FILE, 0, IPADM_DB_READ)) != 0) { + if ((err = ipadm_rw_db(ipmgmt_aobjmap_init, NULL, aobjpath, 0, + IPADM_DB_READ)) != 0) { /* if there was nothing to initialize, it's fine */ if (err != ENOENT) return (err); @@ -165,17 +167,42 @@ ipmgmt_db_init() return (err); } +static const char * +ipmgmt_door_path() +{ + static char door[MAXPATHLEN]; + static boolean_t init_done = B_FALSE; + + if (!init_done) { + const char *zroot = zone_get_nroot(); + + /* + * If this is a branded zone, make sure we use the "/native" + * prefix for the door path: + */ + (void) snprintf(door, sizeof (door), "%s%s", zroot != NULL ? + zroot : "", IPMGMT_DOOR); + + init_done = B_TRUE; + } + + return (door); +} + static int ipmgmt_door_init() { int fd; int err; + const char *door = ipmgmt_door_path(); - /* create the door file for ipmgmtd */ - if ((fd = open(IPMGMT_DOOR, O_CREAT|O_RDONLY, IPADM_FILE_MODE)) == -1) { + /* + * Create the door file for ipmgmtd. + */ + if ((fd = open(door, O_CREAT | O_RDONLY, IPADM_FILE_MODE)) == -1) { err = errno; - ipmgmt_log(LOG_ERR, "could not open %s: %s", - IPMGMT_DOOR, strerror(err)); + ipmgmt_log(LOG_ERR, "could not open %s: %s", door, + strerror(err)); return (err); } (void) close(fd); @@ -186,15 +213,16 @@ ipmgmt_door_init() ipmgmt_log(LOG_ERR, "failed to create door: %s", strerror(err)); return (err); } + /* * fdetach first in case a previous daemon instance exited * ungracefully. */ - (void) fdetach(IPMGMT_DOOR); - if (fattach(ipmgmt_door_fd, IPMGMT_DOOR) != 0) { + (void) fdetach(door); + if (fattach(ipmgmt_door_fd, door) != 0) { err = errno; - ipmgmt_log(LOG_ERR, "failed to attach door to %s: %s", - IPMGMT_DOOR, strerror(err)); + ipmgmt_log(LOG_ERR, "failed to attach door to %s: %s", door, + strerror(err)); goto fail; } return (0); @@ -207,13 +235,15 @@ fail: static void ipmgmt_door_fini() { + const char *door = ipmgmt_door_path(); + if (ipmgmt_door_fd == -1) return; - (void) fdetach(IPMGMT_DOOR); + (void) fdetach(door); if (door_revoke(ipmgmt_door_fd) == -1) { ipmgmt_log(LOG_ERR, "failed to revoke access to door %s: %s", - IPMGMT_DOOR, strerror(errno)); + door, strerror(errno)); } } @@ -350,10 +380,14 @@ ipmgmt_init_privileges() { struct stat statbuf; int err; + char tmpfsdir[MAXPATHLEN]; - /* create the IPADM_TMPFS_DIR directory */ - if (stat(IPADM_TMPFS_DIR, &statbuf) < 0) { - if (mkdir(IPADM_TMPFS_DIR, (mode_t)0755) < 0) { + /* + * Create the volatile storage directory: + */ + ipmgmt_path(IPADM_PATH_TMPFS_DIR, tmpfsdir, sizeof (tmpfsdir)); + if (stat(tmpfsdir, &statbuf) < 0) { + if (mkdir(tmpfsdir, (mode_t)0755) < 0) { err = errno; goto fail; } @@ -364,8 +398,8 @@ ipmgmt_init_privileges() } } - if ((chmod(IPADM_TMPFS_DIR, 0755) < 0) || - (chown(IPADM_TMPFS_DIR, UID_NETADM, GID_NETADM) < 0)) { + if ((chmod(tmpfsdir, 0755) < 0) || + (chown(tmpfsdir, UID_NETADM, GID_NETADM) < 0)) { err = errno; goto fail; } diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_path.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_path.c new file mode 100644 index 0000000000..0219ac1522 --- /dev/null +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_path.c @@ -0,0 +1,84 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * Lookup functions for various file paths used by ipmgmtd. This mechanism + * primarily exists to account for a native root prefix when run within a + * branded zone (e.g. "/native"). + */ + +#include <stdio.h> +#include <zone.h> +#include "ipmgmt_impl.h" + +#define IPADM_PERM_DIR "/etc/ipadm" +#define IPADM_TMPFS_DIR "/etc/svc/volatile/ipadm" + +typedef struct ipadm_path_ent { + ipadm_path_t ipe_id; + const char *ipe_path; +} ipadm_path_ent_t; + +static ipadm_path_ent_t ipadm_paths[] = { + /* + * A temporary directory created in the SMF volatile filesystem. + */ + { IPADM_PATH_TMPFS_DIR, IPADM_TMPFS_DIR }, + + /* + * This file captures the in-memory copy of list `aobjmap' on disk. + * This allows the system to recover in the event that the daemon + * crashes or is restarted. + */ + { IPADM_PATH_ADDROBJ_MAP_DB, IPADM_TMPFS_DIR "/aobjmap.conf" }, + + /* + * The permanent data store for ipadm. + */ + { IPADM_PATH_DB, IPADM_PERM_DIR "/ipadm.conf" }, + + /* + * A temporary copy of the ipadm configuration created, if needed, to + * service write requests early in boot. This file is merged with the + * permanent data store once it is available for writes. + */ + { IPADM_PATH_VOL_DB, IPADM_TMPFS_DIR "/ipadm.conf" }, + + { 0, NULL } +}; + +/* + * Load one of the paths used by ipadm into the provided string buffer. + * Prepends the native system prefix (e.g. "/native") if one is in effect, + * such as when running within a branded zone. + */ +void +ipmgmt_path(ipadm_path_t ip, char *buf, size_t bufsz) +{ + int i; + + for (i = 0; ipadm_paths[i].ipe_path != NULL; i++) { + if (ipadm_paths[i].ipe_id == ip) { + const char *zroot = zone_get_nroot(); + + (void) snprintf(buf, bufsz, "%s%s", zroot != NULL ? + zroot : "", ipadm_paths[i].ipe_path); + + return; + } + } + + abort(); +} diff --git a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c index c1995611e9..a185068005 100644 --- a/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c +++ b/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c @@ -386,13 +386,18 @@ static void * ipmgmt_db_restore_thread(void *arg) { int err; + char confpath[MAXPATHLEN]; + char tmpconfpath[MAXPATHLEN]; + + ipmgmt_path(IPADM_PATH_DB, confpath, sizeof (confpath)); + ipmgmt_path(IPADM_PATH_VOL_DB, tmpconfpath, sizeof (tmpconfpath)); for (;;) { (void) sleep(5); (void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock); if (!ipmgmt_rdonly_root) break; - err = ipmgmt_cpfile(IPADM_VOL_DB_FILE, IPADM_DB_FILE, B_FALSE); + err = ipmgmt_cpfile(tmpconfpath, confpath, B_FALSE); if (err == 0) { ipmgmt_rdonly_root = B_FALSE; break; @@ -424,6 +429,11 @@ ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op) mode_t mode; pthread_t tid; pthread_attr_t attr; + char confpath[MAXPATHLEN]; + char tmpconfpath[MAXPATHLEN]; + + ipmgmt_path(IPADM_PATH_DB, confpath, sizeof (confpath)); + ipmgmt_path(IPADM_PATH_VOL_DB, tmpconfpath, sizeof (tmpconfpath)); writeop = (db_op != IPADM_DB_READ); if (writeop) { @@ -436,11 +446,10 @@ ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op) /* * Did a previous write attempt fail? If so, don't even try to - * read/write to IPADM_DB_FILE. + * read/write to the permanent configuration file. */ if (!ipmgmt_rdonly_root) { - err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE, - mode, db_op); + err = ipadm_rw_db(db_walk_func, db_warg, confpath, mode, db_op); if (err != EROFS) goto done; } @@ -448,11 +457,11 @@ ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op) /* * If we haven't already copied the file to the volatile * file system, do so. This should only happen on a failed - * writeop(i.e., we have acquired the write lock above). + * writeop (i.e., we have acquired the write lock above). */ - if (access(IPADM_VOL_DB_FILE, F_OK) != 0) { + if (access(tmpconfpath, F_OK) != 0) { assert(writeop); - err = ipmgmt_cpfile(IPADM_DB_FILE, IPADM_VOL_DB_FILE, B_TRUE); + err = ipmgmt_cpfile(confpath, tmpconfpath, B_TRUE); if (err != 0) goto done; (void) pthread_attr_init(&attr); @@ -463,7 +472,7 @@ ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op) NULL); (void) pthread_attr_destroy(&attr); if (err != 0) { - (void) unlink(IPADM_VOL_DB_FILE); + (void) unlink(tmpconfpath); goto done; } ipmgmt_rdonly_root = B_TRUE; @@ -472,7 +481,7 @@ ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op) /* * Read/write from the volatile copy. */ - err = ipadm_rw_db(db_walk_func, db_warg, IPADM_VOL_DB_FILE, + err = ipadm_rw_db(db_walk_func, db_warg, tmpconfpath, mode, db_op); done: (void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock); @@ -1254,6 +1263,9 @@ ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op) int err; ipadm_dbwrite_cbarg_t cb; nvlist_t *nvl = NULL; + char aobjpath[MAXPATHLEN]; + + ipmgmt_path(IPADM_PATH_ADDROBJ_MAP_DB, aobjpath, sizeof (aobjpath)); if (op == IPADM_DB_WRITE) { if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0) @@ -1264,14 +1276,14 @@ ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op) else cb.dbw_flags = 0; - err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb, - ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE); + err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb, aobjpath, + IPADM_FILE_MODE, IPADM_DB_WRITE); nvlist_free(nvl); } else { assert(op == IPADM_DB_DELETE); - err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep, - ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE); + err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep, aobjpath, + IPADM_FILE_MODE, IPADM_DB_DELETE); } return (err); } diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c index 133254be4a..e6a88304a7 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/defaults.c @@ -32,6 +32,7 @@ #include <stdio.h> #include <sys/stat.h> #include <libnvpair.h> +#include <zone.h> #include "common.h" #include "defaults.h" @@ -67,6 +68,32 @@ static struct dhcp_default defaults[] = { { "ADOPT_DOMAINNAME", "0", 0, 0 }, }; + +/* + * df_find_defaults(): builds the path to the default configuration file + * + * input: void + * output: void + */ + +static const char * +df_find_defaults(void) +{ + static char agent_defaults_path[MAXPATHLEN] = { 0 }; + const char *zroot = NULL; + + if (agent_defaults_path[0] != '\0') { + return agent_defaults_path; + } + + zroot = zone_get_nroot(); + + (void) snprintf(agent_defaults_path, MAXPATHLEN, "%s%s", + zroot != NULL ? zroot : "", DHCP_AGENT_DEFAULTS); + + return agent_defaults_path; +} + /* * df_build_cache(): builds the defaults nvlist cache * @@ -77,6 +104,7 @@ static struct dhcp_default defaults[] = { static nvlist_t * df_build_cache(void) { + const char *agent_defaults_path = df_find_defaults(); char entry[1024]; int i; char *param, *pastv6, *value, *end; @@ -84,7 +112,7 @@ df_build_cache(void) nvlist_t *nvlist; struct dhcp_default *defp; - if ((fp = fopen(DHCP_AGENT_DEFAULTS, "r")) == NULL) + if ((fp = fopen(agent_defaults_path, "r")) == NULL) return (NULL); if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) { @@ -164,6 +192,7 @@ df_build_cache(void) const char * df_get_string(const char *if_name, boolean_t isv6, uint_t param) { + const char *agent_defaults_path = df_find_defaults(); char *value; char paramstr[256]; char name[256]; @@ -175,10 +204,11 @@ df_get_string(const char *if_name, boolean_t isv6, uint_t param) if (param >= (sizeof (defaults) / sizeof (*defaults))) return (NULL); - if (stat(DHCP_AGENT_DEFAULTS, &statbuf) != 0) { + + if (stat(agent_defaults_path, &statbuf) != 0) { if (!df_unavail_msg) { dhcpmsg(MSG_WARNING, "cannot access %s; using " - "built-in defaults", DHCP_AGENT_DEFAULTS); + "built-in defaults", agent_defaults_path); df_unavail_msg = B_TRUE; } return (defaults[param].df_default); diff --git a/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c b/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c index 6b5a08a51a..7517f2c094 100644 --- a/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c +++ b/usr/src/cmd/cmd-inet/sbin/dhcpagent/request.c @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2011 Joyent, Inc. All rights reserved. * Copyright (c) 2016-2017, Chris Fraire <cfraire@me.com>. * * REQUESTING state of the client state machine. @@ -39,6 +40,7 @@ #include <dhcp_hostconf.h> #include <dhcpagent_util.h> #include <dhcpmsg.h> +#include <strings.h> #include "states.h" #include "util.h" @@ -645,8 +647,24 @@ accept_v4_acknak(dhcp_smach_t *dsmp, PKT_LIST *plp) stop_pkt_retransmission(dsmp); if (*plp->opts[CD_DHCP_TYPE]->value == NAK) { - dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s", - dsmp->dsm_name); + char saddr[18]; + + saddr[0] = '\0'; + if (plp->opts[CD_SERVER_ID] != NULL && + plp->opts[CD_SERVER_ID]->len == sizeof (struct in_addr)) { + struct in_addr t_server; + + bcopy(plp->opts[CD_SERVER_ID]->value, &t_server, + plp->opts[CD_SERVER_ID]->len); + (void) strlcpy(saddr, inet_ntoa(t_server), + sizeof (saddr)); + } + + dhcpmsg(MSG_WARNING, "accept_v4_acknak: NAK on interface %s " + "from %s %s", + dsmp->dsm_name, + inet_ntoa(plp->pktfrom.v4.sin_addr), saddr); + dsmp->dsm_bad_offers++; free_pkt_entry(plp); dhcp_restart(dsmp); diff --git a/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile b/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile index 7d72bad58f..0efa270e8b 100644 --- a/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile +++ b/usr/src/cmd/cmd-inet/usr.lib/wpad/Makefile @@ -34,8 +34,7 @@ include ../../../Makefile.cmd ROOTMANIFESTDIR = $(ROOTSVCNETWORK) LDLIBS += -ldladm -ldlpi -NATIVE_LIBS += libcrypto.so -all install := LDLIBS += -lcrypto +all install := LDLIBS += -lsunw_crypto SMOFF += all_func_returns diff --git a/usr/src/cmd/cmd-inet/usr.sbin/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/Makefile index 97476c95d9..98971bf5b0 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/Makefile +++ b/usr/src/cmd/cmd-inet/usr.sbin/Makefile @@ -166,6 +166,7 @@ route := CPPFLAGS += -DNDEBUG ndd := LDLIBS += -ldladm -lipadm $(RELEASE_BUILD)ndd := CERRWARN += -_gcc=-Wno-unused in.comsat := LDFLAGS += $(MAPFILE.NGB:%=-M%) +route := LDLIBS += -lzonecfg -lcontract .KEEP_STATE: @@ -268,7 +269,7 @@ lint: $(LINTSUBDIRS) in.telnetd.c $(LDLIBS) -lbsm -lpam -lsocket -lnsl $(LINT.c) if_mpadm.c $(LDLIBS) -lsocket -lnsl -lipmp -linetutil $(LINT.c) ipaddrsel.c $(LDLIBS) -lsocket -lnsl - $(LINT.c) route.c $(LDLIBS) -lsocket -lnsl -ltsnet + $(LINT.c) route.c $(LDLIBS) -lsocket -lnsl -ltsnet -lcontract -lzonecfg $(LINT.c) syncinit.c $(LDLIBS) -ldlpi $(LINT.c) syncloop.c $(LDLIBS) -ldlpi $(LINT.c) syncstat.c $(LDLIBS) -ldlpi diff --git a/usr/src/cmd/cmd-inet/usr.sbin/arp.c b/usr/src/cmd/cmd-inet/usr.sbin/arp.c index 19ab13fd47..aa9adac6ce 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/arp.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/arp.c @@ -58,6 +58,7 @@ #include <arpa/inet.h> #include <net/if_types.h> #include <net/if_dl.h> +#include <zone.h> static int file(char *); static int set(int, char *[]); @@ -118,7 +119,11 @@ main(int argc, char *argv[]) * is to let netstat, which prints it as part of * the MIB statistics, do it. */ - (void) execl("/usr/bin/netstat", "netstat", + char netstat_path[MAXPATHLEN]; + const char *zroot = zone_get_nroot(); + (void) snprintf(netstat_path, sizeof (netstat_path), "%s%s", zroot != NULL ? + zroot : "", "/usr/bin/netstat"); + (void) execl(netstat_path, "netstat", (n_flag ? "-np" : "-p"), "-f", "inet", (char *)0); (void) fprintf(stderr, "failed to exec netstat: %s\n", diff --git a/usr/src/cmd/cmd-inet/usr.sbin/ndp.c b/usr/src/cmd/cmd-inet/usr.sbin/ndp.c index 23b940c686..2fc19775ad 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/ndp.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/ndp.c @@ -40,6 +40,7 @@ #include <inet/ip.h> #include <net/if_dl.h> #include <net/route.h> +#include <zone.h> typedef struct sockaddr_in6 sin6_t; @@ -95,7 +96,6 @@ static int ndp_set_nce(char *, char *, char *[], int); static int ndp_set_file(char *); static char *ndp_iface = NULL; -static char *netstat_path = "/usr/bin/netstat"; static pid_t ndp_pid; static boolean_t ndp_noresolve = B_FALSE; /* Don't lookup addresses */ static boolean_t ndp_run = B_TRUE; @@ -103,6 +103,7 @@ static boolean_t ndp_run = B_TRUE; #define MAX_ATTEMPTS 5 #define MAX_OPTS 5 #define WORDSEPS " \t\r\n" +#define NETSTAT_PATH "/usr/bin/netstat" /* * Macros borrowed from route(1M) for working with PF_ROUTE messages @@ -767,6 +768,12 @@ ndp_get(int fd, struct lifreq *lifrp, void *unused) static void ndp_get_all(void) { + char netstat_path[MAXPATHLEN]; + const char *zroot = zone_get_nroot(); + + (void) snprintf(netstat_path, sizeof (netstat_path), "%s%s", zroot != NULL ? + zroot : "", NETSTAT_PATH); + (void) execl(netstat_path, "netstat", (ndp_noresolve ? "-np" : "-p"), "-f", "inet6", (char *)0); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/route.c b/usr/src/cmd/cmd-inet/usr.sbin/route.c index a2fd0d2d17..29ccf97bb1 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/route.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/route.c @@ -6,6 +6,7 @@ /* All Rights Reserved */ /* Copyright (c) 1990 Mentat Inc. */ +/* Copyright 2018, Joyent, Inc. */ /* * @@ -79,6 +80,13 @@ #include <assert.h> #include <strings.h> +#include <libcontract.h> +#include <sys/ctfs.h> +#include <sys/contract/process.h> +#include <sys/wait.h> +#include <libzonecfg.h> +#include <zone.h> + #include <libtsnet.h> #include <tsol/label.h> @@ -292,6 +300,7 @@ static void syntax_error(char *err, ...); static void usage(char *cp); static void write_to_rtfile(FILE *fp, int argc, char **argv); static void pmsg_secattr(const char *, size_t, const char *); +static void do_zone(char *); static pid_t pid; static int s; @@ -308,6 +317,7 @@ static char perm_file_sfx[] = "/etc/inet/static_routes"; static char *perm_file; static char temp_file_sfx[] = "/etc/inet/static_routes.tmp"; static char *temp_file; +static char *zonename; static struct in6_addr in6_host_mask = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; /* @@ -354,7 +364,7 @@ usage(char *cp) cp); } (void) fprintf(stderr, gettext("usage: route [ -fnpqv ] " - "[ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n")); + "[-z <zone> ] [ -R <root-dir> ] cmd [[ -<qualifers> ] args ]\n")); exit(1); /* NOTREACHED */ } @@ -418,7 +428,7 @@ main(int argc, char **argv) if (argc < 2) usage(NULL); - while ((ch = getopt(argc, argv, "R:nqdtvfp")) != EOF) { + while ((ch = getopt(argc, argv, "R:nqdtvfpz:")) != EOF) { switch (ch) { case 'n': nflag = B_TRUE; @@ -444,6 +454,9 @@ main(int argc, char **argv) case 'R': root_dir = optarg; break; + case 'z': + zonename = optarg; + break; case '?': default: usage(NULL); @@ -453,6 +466,8 @@ main(int argc, char **argv) argc -= optind; argv += optind; + do_zone(zonename); + pid = getpid(); if (tflag) s = open("/dev/null", O_WRONLY); @@ -3252,3 +3267,74 @@ pmsg_secattr(const char *sptr, size_t msglen, const char *labelstr) sizeof (buf))); } } + +static void +do_zone(char *name) +{ + zoneid_t zoneid; + zone_state_t st; + int fd, status, rc = 0; + pid_t pid; + + if (name == NULL) + return; + + if (getzoneid() != GLOBAL_ZONEID) { + (void) fprintf(stderr, + "route: -z can only be specified from the global zone\n"); + exit(EXIT_FAILURE); + } + + if (strcmp(name, GLOBAL_ZONENAME) == 0) + return; + + if (zone_get_state(name, &st) != Z_OK) + quit("unable to get zone state", errno); + + if (st != ZONE_STATE_RUNNING) { + (void) fprintf(stderr, "route: zone must be running\n"); + exit(EXIT_FAILURE); + } + + if ((zoneid = getzoneidbyname(name)) == -1) + quit("cannot determine zone id", errno); + + if ((fd = open64(CTFS_ROOT "/process/template", O_RDWR)) == -1) + quit("cannot open ctfs template", errno); + + /* + * zone_enter() does not allow contracts to straddle zones, so we must + * create a new, though largely unused contract. Once we fork, the + * child is the only member of the new contract, so it can perform a + * zone_enter(). + */ + rc |= ct_tmpl_set_critical(fd, 0); + rc |= ct_tmpl_set_informative(fd, 0); + rc |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR); + rc |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT); + if (rc || ct_tmpl_activate(fd)) { + (void) close(fd); + quit("could not create contract", errno); + } + + switch (pid = fork1()) { + case 0: + (void) ct_tmpl_clear(fd); + (void) close(fd); + if (zone_enter(zoneid) == -1) + quit("could not enter zone", errno); + return; + + case -1: + quit("fork1 failed", errno); + + default: + (void) ct_tmpl_clear(fd); + (void) close(fd); + if (waitpid(pid, &status, 0) < 0) + quit("waitpid failed", errno); + + exit(WEXITSTATUS(status)); + } + +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c b/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c index 71a2fc9853..be826baba2 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c @@ -21,10 +21,9 @@ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2012 Joyent, Inc. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -46,6 +45,7 @@ #include <libscf.h> #include <libscf_priv.h> #include <libuutil.h> +#include <ifaddrs.h> /* * This program moves routing management under SMF. We do this by giving @@ -2335,8 +2335,8 @@ out: /* * - * Return the number of IPv6 addresses configured. This answers the - * generic question, "is IPv6 configured?". We only start in.ndpd if IPv6 + * Return the number of non-loopback IPv6 addresses configured. This answers + * the generic question, "is IPv6 configured?". We only start in.ndpd if IPv6 * is configured, and we also only enable IPv6 routing daemons if IPv6 is * enabled. */ @@ -2344,28 +2344,24 @@ static int ra_numv6intfs(void) { static int num = -1; - int ipsock; - struct lifnum lifn; + int cnt; + struct ifaddrs *ifp_head, *ifp; if (num != -1) return (num); - if ((ipsock = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) { - (void) fprintf(stderr, - gettext("%1$s: unable to open %2$s: %3$s\n"), - myname, IP_DEV_NAME, strerror(errno)); + if (getifaddrs(&ifp_head) < 0) return (0); - } - lifn.lifn_family = AF_INET6; - lifn.lifn_flags = 0; - if (ioctl(ipsock, SIOCGLIFNUM, &lifn) == -1) { - (void) close(ipsock); - return (0); + cnt = 0; + for (ifp = ifp_head; ifp; ifp = ifp->ifa_next) { + if (!(ifp->ifa_flags & IFF_LOOPBACK) && + (ifp->ifa_flags & IFF_IPV6)) + cnt++; } - (void) close(ipsock); - return (num = lifn.lifn_count); + freeifaddrs(ifp_head); + return (num = cnt); } /* diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile index 4709cfe59e..4e3dd8259a 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/Makefile @@ -40,16 +40,18 @@ OBJS= nfs4_xdr.o snoop.o snoop_aarp.o snoop_adsp.o snoop_aecho.o \ snoop_pppoe.o snoop_rip.o snoop_rip6.o snoop_rpc.o snoop_rpcprint.o \ snoop_rpcsec.o snoop_rport.o snoop_rquota.o snoop_rstat.o snoop_rtmp.o \ snoop_sctp.o snoop_slp.o snoop_smb.o snoop_socks.o snoop_solarnet.o \ - snoop_tcp.o snoop_tftp.o snoop_trill.o snoop_udp.o snoop_zip.o + snoop_svp.o snoop_tcp.o snoop_tftp.o snoop_trill.o snoop_udp.o \ + snoop_vxlan.o snoop_zip.o SRCS= $(OBJS:.o=.c) HDRS= snoop.h snoop_mip.h at.h snoop_ospf.h snoop_ospf6.h include ../../../Makefile.cmd +include ../../../Makefile.ctf CPPFLAGS += -I. -I$(SRC)/common/net/dhcp \ -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -LDLIBS += -ldhcputil -ldlpi -lsocket -lnsl -ltsol +LDLIBS += -ldhcputil -ldlpi -lsocket -lnsl -ltsol -luuid LDFLAGS += $(MAPFILE.NGB:%=-M%) CERRWARN += -_gcc=-Wno-switch CERRWARN += -_gcc=-Wno-implicit-function-declaration diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c index 538cc295db..6abda78efc 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.c @@ -121,6 +121,7 @@ main(int argc, char **argv) char *output_area; int nbytes; char *datalink = NULL; + char *zonename = NULL; dlpi_handle_t dh; names[0] = '\0'; @@ -227,7 +228,7 @@ main(int argc, char **argv) } (void) setvbuf(stdout, NULL, _IOLBF, BUFSIZ); - while ((c = getopt(argc, argv, "at:CPDSi:o:Nn:s:d:I:vVp:f:c:x:U?rqz")) + while ((c = getopt(argc, argv, "at:CPDSi:o:Nn:s:d:I:vVp:f:c:x:U?rqz:Z")) != EOF) { switch (c) { case 'a': @@ -348,8 +349,11 @@ main(int argc, char **argv) case 'U': Uflg = B_TRUE; break; -#ifdef DEBUG case 'z': + zonename = optarg; + break; +#ifdef DEBUG + case 'Z': zflg = B_TRUE; break; #endif /* DEBUG */ @@ -371,7 +375,7 @@ main(int argc, char **argv) * requested was chosen, but that's too hard. */ if (!icapfile) { - use_kern_pf = open_datalink(&dh, datalink); + use_kern_pf = open_datalink(&dh, datalink, zonename); } else { use_kern_pf = B_FALSE; cap_open_read(icapfile); @@ -812,6 +816,8 @@ usage(void) (void) fprintf(stderr, "\t[ -r ] # Do not resolve address to name\n"); (void) fprintf(stderr, + "\t[ -z zone ] # Open links from named zone\n"); + (void) fprintf(stderr, "\n\t[ filter expression ]\n"); (void) fprintf(stderr, "\nExample:\n"); (void) fprintf(stderr, "\tsnoop -o saved host fred\n\n"); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h index 1d1d921f65..81add827e6 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h @@ -24,6 +24,7 @@ * Use is subject to license terms. * * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright 2018 Joyent, Inc. */ #ifndef _SNOOP_H @@ -195,7 +196,7 @@ extern void cap_open_read(const char *); extern void cap_open_write(const char *); extern void cap_read(int, int, int, void (*)(), int); extern void cap_close(void); -extern boolean_t open_datalink(dlpi_handle_t *, const char *); +extern boolean_t open_datalink(dlpi_handle_t *, const char *, const char *); extern void init_datalink(dlpi_handle_t, ulong_t, ulong_t, struct timeval *, struct Pf_ext_packetfilt *); extern void net_read(dlpi_handle_t, size_t, int, void (*)(), int); @@ -273,6 +274,8 @@ extern int interpret_socks_reply(int, char *, int); extern int interpret_trill(int, struct ether_header **, char *, int *); extern int interpret_isis(int, char *, int, boolean_t); extern int interpret_bpdu(int, char *, int); +extern int interpret_vxlan(int, char *, int); +extern int interpret_svp(int, char *, int); extern void init_ldap(void); extern boolean_t arp_for_ether(char *, struct ether_addr *); extern char *ether_ouiname(uint32_t); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c index ab6bc292ac..8fbf3fc15f 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_capture.c @@ -29,6 +29,7 @@ #include <strings.h> #include <errno.h> #include <fcntl.h> +#include <limits.h> #include <setjmp.h> #include <sys/types.h> #include <sys/signal.h> @@ -114,7 +115,7 @@ select_datalink(const char *linkname, void *arg) * about the datalink useful for building the proper packet filters. */ boolean_t -open_datalink(dlpi_handle_t *dhp, const char *linkname) +open_datalink(dlpi_handle_t *dhp, const char *linkname, const char *zonename) { int retval; int flags = DLPI_PASSIVE | DLPI_RAW; @@ -122,6 +123,9 @@ open_datalink(dlpi_handle_t *dhp, const char *linkname) dlpi_info_t dlinfo; if (linkname == NULL) { + if (zonename != NULL) + pr_err("a datalink must be specified with a zone name"); + /* * Select a datalink to use by default. Prefer datalinks that * are plumbed by IP. @@ -145,7 +149,8 @@ open_datalink(dlpi_handle_t *dhp, const char *linkname) flags |= DLPI_DEVIPNET; if (Iflg || strcmp(linkname, "lo0") == 0) flags |= DLPI_IPNETINFO; - if ((retval = dlpi_open(linkname, dhp, flags)) != DLPI_SUCCESS) { + if ((retval = dlpi_open_zone(linkname, zonename, dhp, + flags)) != DLPI_SUCCESS) { pr_err("cannot open \"%s\": %s", linkname, dlpi_strerror(retval)); } @@ -614,6 +619,10 @@ cap_open_read(const char *name) if (fstat(capfile_in, &st) < 0) pr_err("couldn't stat %s: %m", name); + if (st.st_size > INT_MAX) + pr_err("input file size (%llu bytes) exceeds maximum " + "supported size (%d bytes)", + (unsigned long long)st.st_size, INT_MAX); cap_len = st.st_size; cap_buffp = mmap(0, cap_len, PROT_READ, MAP_PRIVATE, capfile_in, 0); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c index 332069508c..f084365faf 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_ether.c @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2015 Joyent, Inc. */ #include <stdio.h> @@ -54,8 +55,9 @@ static headerlen_fn_t ether_header_len, fddi_header_len, tr_header_len, ib_header_len, ipnet_header_len, ipv4_header_len, ipv6_header_len; -static interpreter_fn_t interpret_ether, interpret_fddi, interpret_tr, +static interpreter_fn_t interpret_fddi, interpret_tr, interpret_ib, interpret_ipnet, interpret_iptun; +interpreter_fn_t interpret_ether; static void addr_copy_swap(struct ether_addr *, struct ether_addr *); static int tr_machdr_len(char *, int *, int *); diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c index 7be45437d5..77e9d97766 100644 --- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c @@ -21,6 +21,7 @@ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2018, Joyent, Inc. */ #include <stdio.h> @@ -76,6 +77,7 @@ static const struct porttable pt_udp[] = { { 560, "RMONITOR" }, { 561, "MONITOR" }, { IPPORT_SOCKS, "SOCKS" }, + { IPPORT_VXLAN, "VXLAN" }, { 0, NULL } }; @@ -128,6 +130,7 @@ static struct porttable pt_tcp[] = { { 540, "UUCP" }, { 600, "PCSERVER" }, { IPPORT_SOCKS, "SOCKS" }, + { 1296, "SVP" }, { 1524, "INGRESLOCK" }, { 2904, "M2UA" }, { 2905, "M3UA" }, @@ -425,6 +428,15 @@ interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst, (void) interpret_socks_reply(flags, data, dlen); return (1); + case IPPORT_VXLAN: + (void) interpret_vxlan(flags, data, dlen); + return (1); + case 1296: + if (proto == IPPROTO_TCP) { + (void) interpret_svp(flags, data, dlen); + return (1); + } + break; } } diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_svp.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_svp.c new file mode 100644 index 0000000000..a0768c2234 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_svp.c @@ -0,0 +1,557 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +/* + * Decode SVP (SmartDC VxLAN Protocol) packets + */ + +#include <inttypes.h> +#include <sys/crc32.h> +#include <uuid/uuid.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <stdarg.h> +#include <libvarpd_svp_prot.h> +#include "snoop.h" + +/* + * String size large enough for an IPv6 address + / + a 3 digit (or less) + * prefix length + */ +#define ADDRSTR_LEN (INET6_ADDRSTRLEN + 4) + +/* + * Large enough for all currently known status strings as well as a + * 16-bit hex value. + */ +#define STATUSSTR_LEN 32 + +/* + * Large enough for all currently known op strings, as well as a + * 16-bit hex value. + */ +#define OPSTR_LEN 32 + +/* + * Large enough for VL3 types and bulk types, as well as a 32-bit + * hex value. + */ +#define TYPESTR_LEN 32 + +static uint32_t svp_crc32_tab[] = { CRC32_TABLE }; + +#define STR(_x, _buf, _len) \ + case _x: \ + (void) strlcpy(_buf, #_x, _len); \ + break + +static void +svp_op_str(uint16_t op, char *buf, size_t buflen) +{ + switch (op) { + STR(SVP_R_UNKNOWN, buf, buflen); + STR(SVP_R_PING, buf, buflen); + STR(SVP_R_PONG, buf, buflen); + STR(SVP_R_VL2_REQ, buf, buflen); + STR(SVP_R_VL2_ACK, buf, buflen); + STR(SVP_R_VL3_REQ, buf, buflen); + STR(SVP_R_VL3_ACK, buf, buflen); + STR(SVP_R_BULK_REQ, buf, buflen); + STR(SVP_R_BULK_ACK, buf, buflen); + STR(SVP_R_LOG_REQ, buf, buflen); + STR(SVP_R_LOG_ACK, buf, buflen); + STR(SVP_R_LOG_RM, buf, buflen); + STR(SVP_R_LOG_RM_ACK, buf, buflen); + STR(SVP_R_SHOOTDOWN, buf, buflen); + default: + (void) snprintf(buf, buflen, "0x%hx", op); + } +} + +static void +svp_status_str(uint16_t status, char *buf, size_t buflen) +{ + switch (status) { + STR(SVP_S_OK, buf, buflen); + STR(SVP_S_FATAL, buf, buflen); + STR(SVP_S_NOTFOUND, buf, buflen); + STR(SVP_S_BADL3TYPE, buf, buflen); + STR(SVP_S_BADBULK, buf, buflen); + default: + (void) snprintf(buf, buflen, "0x%hx", status); + } +} + +static void +svp_vl3_type_str(uint32_t type, char *buf, size_t buflen) +{ + switch (type) { + STR(SVP_VL3_IP, buf, buflen); + STR(SVP_VL3_IPV6, buf, buflen); + default: + (void) snprintf(buf, buflen, "0x%x", type); + } +} + +static void +svp_bulk_type_str(uint32_t type, char *buf, size_t buflen) +{ + switch (type) { + STR(SVP_BULK_VL2, buf, buflen); + STR(SVP_BULK_VL3, buf, buflen); + default: + (void) snprintf(buf, buflen, "0x%x", type); + } +} + +static void +svp_log_type_str(uint32_t type, char *buf, size_t buflen) +{ + switch (type) { + STR(SVP_LOG_VL2, buf, buflen); + STR(SVP_LOG_VL3, buf, buflen); + default: + (void) snprintf(buf, buflen, "0x%x", type); + } +} +#undef STR + +static void +svp_addr_str(void *addrp, uint8_t *prefixp, char *buf, size_t buflen) +{ + struct in_addr v4; + int af = AF_INET6; + + if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)addrp)) { + af = AF_INET; + IN6_V4MAPPED_TO_INADDR((struct in6_addr *)addrp, &v4); + addrp = &v4; + } + + if (inet_ntop(af, addrp, buf, buflen) == NULL) { + uint8_t *p = addrp; + size_t i; + + (void) strlcpy(buf, "0x", buflen); + for (i = 0; i < 16; i++) { + (void) snprintf(buf + 2 + i * 2, + sizeof (buf) - 2 - i * 2, "%02hhx", p[i]); + } + } + + if (prefixp != NULL && *prefixp != 128) { + char buf2[5]; /* / + 3 digits + NUL */ + + if (af == AF_INET) + *prefixp -= 96; + + (void) snprintf(buf2, sizeof (buf2), "/%hhu", *prefixp); + (void) strlcat(buf, buf2, buflen); + } +} + +static boolean_t +svp_check_crc(char *data, int len) +{ + svp_req_t *req = (svp_req_t *)data; + uint32_t save_crc = req->svp_crc32; + uint32_t crc = -1U; + + req->svp_crc32 = 0; + CRC32(crc, (uint8_t *)data, len, -1U, svp_crc32_tab); + crc = ~crc; + req->svp_crc32 = save_crc; + + return (ntohl(save_crc) == crc ? B_TRUE : B_FALSE); +} + +static void +do_svp_vl2_req(void *data, int len) +{ + svp_vl2_req_t *vl2 = data; + + show_printf("MAC = %s", ether_ntoa((struct ether_addr *)vl2->sl2r_mac)); + show_printf("Virtual network id = %u", ntohl(vl2->sl2r_vnetid)); +} + +static void +do_svp_vl2_ack(void *data, int len) +{ + svp_vl2_ack_t *vl2a = data; + char status[STATUSSTR_LEN]; + char addr[ADDRSTR_LEN]; + + svp_status_str(ntohs(vl2a->sl2a_status), status, sizeof (status)); + svp_addr_str(vl2a->sl2a_addr, NULL, addr, sizeof (addr)); + + show_printf("Status = %s", status); + show_printf("UL3 Address = %s", addr); + show_printf("UL3 Port = %hu", ntohs(vl2a->sl2a_port)); +} + +static void +do_svp_vl3_req(void *data, int len) +{ + svp_vl3_req_t *req = data; + char type[TYPESTR_LEN]; + char addr[ADDRSTR_LEN]; + + svp_vl3_type_str(ntohl(req->sl3r_type), type, sizeof (type)); + svp_addr_str(req->sl3r_ip, NULL, addr, sizeof (addr)); + + show_printf("Virtual network id = %u", ntohl(req->sl3r_vnetid)); + show_printf("Type = %s", type); + show_printf("VL3 Address = %s", addr); +} + +static void +do_svp_vl3_ack(void *data, int len) +{ + svp_vl3_ack_t *vl3a = data; + char status[STATUSSTR_LEN]; + char addr[ADDRSTR_LEN]; + + svp_status_str(ntohl(vl3a->sl3a_status), status, sizeof (status)); + svp_addr_str(vl3a->sl3a_uip, NULL, addr, sizeof (addr)); + + show_printf("Status = %s", status); + show_printf("MAC = %s", + ether_ntoa((struct ether_addr *)vl3a->sl3a_mac)); + show_printf("UL3 Address = %s", addr); + show_printf("UL3 Port = %hu", ntohs(vl3a->sl3a_uport)); +} + +static void +do_svp_bulk_req(void *data, int len) +{ + svp_bulk_req_t *req = data; + char type[TYPESTR_LEN]; + + if (len < sizeof (svp_bulk_req_t)) { + show_printf("SVP_R_BULK_REQ runt"); + return; + } + + svp_bulk_type_str(ntohl(req->svbr_type), type, sizeof (type)); + show_printf("Type = %s", type); +} + +static void +do_svp_bulk_ack(void *data, int len) +{ + svp_bulk_ack_t *ack = data; + char status[STATUSSTR_LEN]; + char type[TYPESTR_LEN]; + + svp_status_str(ntohl(ack->svba_status), status, sizeof (status)); + svp_bulk_type_str(ntohl(ack->svba_type), type, sizeof (type)); + + show_printf("Status = %s", status); + show_printf("Type = %s", type); + + /* + * Currently the data format is undefined (see libvarp_svp_prot.h), + * so there is nothing else we can display. + */ +} + +static void +do_svp_log_req(void *data, int len) +{ + svp_log_req_t *svlr = data; + char addr[ADDRSTR_LEN]; + + svp_addr_str(svlr->svlr_ip, NULL, addr, sizeof (addr)); + + show_printf("Count = %u", ntohl(svlr->svlr_count)); + show_printf("Address = %s", addr); +} + +static void +do_svp_log_ack(void *data, int len) +{ + svp_log_ack_t *ack = data; + union { + svp_log_vl2_t *vl2; + svp_log_vl3_t *vl3; + uint32_t *vtype; + void *vd; + } u; + size_t total = 0, rlen = 0; + uint8_t prefixlen; + boolean_t is_host; + char status[STATUSSTR_LEN]; + char typestr[TYPESTR_LEN]; + char uuid[UUID_PRINTABLE_STRING_LENGTH]; + char addr[ADDRSTR_LEN]; + + u.vd = (ack + 1); + + svp_status_str(ntohl(ack->svla_status), status, sizeof (status)); + + show_printf("Status = %s", status); + len -= sizeof (*ack); + + while (len > 0) { + uint32_t type; + + if (len < sizeof (uint32_t)) { + show_printf(" Trailing runt"); + break; + } + + type = ntohl(*u.vtype); + svp_log_type_str(type, typestr, sizeof (typestr)); + + switch (type) { + case SVP_LOG_VL2: + rlen = sizeof (svp_log_vl2_t); + break; + case SVP_LOG_VL3: + rlen = sizeof (svp_log_vl3_t); + break; + default: + /* + * If we don't know the type of log record we have, + * we cannot determine the size of the record, so we + * cannot continue past this. + */ + show_printf("Log %-4zu: Log type = %s", ++total, + typestr); + return; + } + + if (len < rlen) { + show_printf("Log %-4zu %s runt", ++total, typestr); + return; + } + + /* These are the same in SVP_LOG_VL2 and SVP_LOG_VL3 records */ + show_printf("Log %-4zu Log type = %s", ++total, typestr); + + uuid_parse(uuid, u.vl2->svl2_id); + show_printf("%8s UUID = %s", "", uuid); + + switch (type) { + case SVP_LOG_VL2: + show_printf("%8s MAC = %s", "", + ether_ntoa((struct ether_addr *)u.vl2->svl2_mac)); + show_printf("%8s Vnet = %u", "", + ntohl(u.vl2->svl2_vnetid)); + u.vl2++; + break; + case SVP_LOG_VL3: + svp_addr_str(u.vl3->svl3_ip, NULL, addr, sizeof (addr)); + + show_printf("%8s VLAN = %hu", "", + ntohs(u.vl3->svl3_vlan)); + show_printf("%8s Address = %s", "", addr); + show_printf("%8s Vnet = %u", "", + ntohl(u.vl3->svl3_vnetid)); + u.vl3++; + break; + } + + len -= rlen; + show_space(); + } + show_printf("Total log records = %zu", total); +} + +static void +do_svp_lrm_req(void *data, int len) +{ + /* + * Sized large enough to hold the expected size message + * (formatted below) if there's a length mismatch. + */ + char mismatch_str[64] = { 0 }; + svp_lrm_req_t *req = data; + size_t expected_sz = sizeof (*req); + size_t i, n; + + n = ntohl(req->svrr_count); + + /* IDs are 16-byte UUIDs */ + expected_sz += n * UUID_LEN; + if (len != expected_sz) { + (void) snprintf(mismatch_str, sizeof (mismatch_str), + " (expected %zu bytes, actual size is %d bytes)", + expected_sz, len); + } + show_printf("ID Count = %u%s", n, mismatch_str); + if (len != expected_sz) + return; + + for (i = 0; i < n; i++) { + char uuid[UUID_PRINTABLE_STRING_LENGTH]; + + uuid_parse(uuid, &req->svrr_ids[UUID_LEN * i]); + show_printf("%-4s %s", (i == 0) ? "IDs:" : "", uuid); + } +} + +static void +do_svp_lrm_ack(void *data, int len) +{ + svp_lrm_ack_t *ack = data; + char status[STATUSSTR_LEN]; + + svp_status_str(ntohl(ack->svra_status), status, sizeof (status)); + show_printf("Status = %s", status); +} + +static void +do_svp_shootdown(void *data, int len) +{ + svp_shootdown_t *sd = data; + + show_printf("Vnet = %u", ntohl(sd->svsd_vnetid)); + show_printf("MAC Address = %s", + ether_ntoa((struct ether_addr *)sd->svsd_mac)); +} + +static struct svp_len_tbl { + uint16_t slt_op; + size_t slt_len; +} svp_len_tbl[] = { + { SVP_R_UNKNOWN, 0 }, + { SVP_R_PING, 0 }, + { SVP_R_PONG, 0 }, + { SVP_R_VL2_REQ, sizeof (svp_vl2_req_t) }, + { SVP_R_VL2_ACK, sizeof (svp_vl2_ack_t) }, + { SVP_R_VL3_REQ, sizeof (svp_vl3_req_t) }, + { SVP_R_VL3_ACK, sizeof (svp_vl3_ack_t) }, + { SVP_R_BULK_REQ, sizeof (svp_bulk_req_t) }, + { SVP_R_BULK_ACK, sizeof (svp_bulk_ack_t) }, + { SVP_R_LOG_REQ, sizeof (svp_log_req_t) }, + { SVP_R_LOG_ACK, 0 }, + { SVP_R_LOG_RM, sizeof (svp_lrm_req_t) }, + { SVP_R_LOG_RM_ACK, sizeof (svp_lrm_ack_t) }, + { SVP_R_SHOOTDOWN, sizeof (svp_shootdown_t) }, +}; + +static boolean_t +svp_check_runt(uint16_t op, int len) +{ + if (op > SVP_R_SHOOTDOWN) + return (B_FALSE); + + if (len < svp_len_tbl[op].slt_len) { + char opstr[OPSTR_LEN]; + + svp_op_str(op, opstr, sizeof (opstr)); + show_printf("%s Runt", opstr); + show_space(); + return (B_TRUE); + } + return (B_FALSE); +} + +int +interpret_svp(int flags, char *data, int fraglen) +{ + svp_req_t *req = (svp_req_t *)data; + char opstr[OPSTR_LEN]; + uint16_t op; + boolean_t crc_ok; + + if (fraglen < sizeof (svp_req_t)) { + if (flags & F_SUM) + (void) snprintf(get_sum_line(), MAXLINE, + "SVP RUNT"); + if (flags & F_DTAIL) + show_header("SVP RUNT: ", "Short packet", fraglen); + + return (fraglen); + } + + op = ntohs(req->svp_op); + svp_op_str(op, opstr, sizeof (opstr)); + + crc_ok = svp_check_crc(data, fraglen); + + if (flags & F_SUM) { + (void) snprintf(get_sum_line(), MAXLINE, + "SVP V=%hu OP=%s ID=%u%s", ntohs(req->svp_ver), opstr, + ntohl(req->svp_id), crc_ok ? "" : " (BAD CRC)"); + } + + if (flags & F_DTAIL) { + show_header("SVP: ", "SVP Header", sizeof (svp_req_t)); + show_space(); + show_printf("Version = %hu", ntohs(req->svp_ver)); + show_printf("Op = %s", opstr); + show_printf("Packet length = %u bytes%s", ntohl(req->svp_size), + (ntohl(req->svp_size) == fraglen - sizeof (*req)) ? + "" : " (mismatch)"); + show_printf("Id = %u", ntohl(req->svp_id)); + show_printf("CRC = %x%s", ntohl(req->svp_crc32), + crc_ok ? "" : " (bad)"); + show_space(); + + req++; + fraglen -= sizeof (*req); + + /* + * Since we cannot know the length of an unknown op, + * svp_check_runt() returns B_TRUE for both truncated packets + * and unknown packets -- we have nothing meaningful besides + * the header we could print anyway. + */ + if (svp_check_runt(op, fraglen)) + return (fraglen); + + switch (op) { + case SVP_R_VL2_REQ: + do_svp_vl2_req(req, fraglen); + break; + case SVP_R_VL2_ACK: + do_svp_vl2_ack(req, fraglen); + break; + case SVP_R_VL3_REQ: + do_svp_vl3_req(req, fraglen); + break; + case SVP_R_VL3_ACK: + do_svp_vl3_ack(req, fraglen); + break; + case SVP_R_BULK_REQ: + do_svp_bulk_req(req, fraglen); + break; + case SVP_R_BULK_ACK: + do_svp_bulk_ack(req, fraglen); + break; + case SVP_R_LOG_REQ: + do_svp_log_req(req, fraglen); + break; + case SVP_R_LOG_ACK: + do_svp_log_ack(req, fraglen); + break; + case SVP_R_LOG_RM: + do_svp_lrm_req(req, fraglen); + break; + case SVP_R_LOG_RM_ACK: + do_svp_lrm_ack(req, fraglen); + break; + case SVP_R_SHOOTDOWN: + do_svp_shootdown(req, fraglen); + break; + } + + show_space(); + } + + return (0); +} diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_vxlan.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_vxlan.c new file mode 100644 index 0000000000..36025ca8f3 --- /dev/null +++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_vxlan.c @@ -0,0 +1,68 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2015 Joyent, Inc. All rights reserved. + */ + +/* + * Decode VXLAN encapsulated packets. + */ + +#include <sys/vxlan.h> +#include "snoop.h" + +int +interpret_vxlan(int flags, char *data, int fraglen) +{ + vxlan_hdr_t *vxlan = (vxlan_hdr_t *)data; + uint32_t id, vxf; + + if (fraglen < sizeof (vxlan_hdr_t)) { + if (flags & F_SUM) + (void) snprintf(get_sum_line(), MAXLINE, + "VXLAN RUNT"); + if (flags & F_DTAIL) + show_header("VXLAN RUNT: ", "Short packet", fraglen); + + return (fraglen); + } + + id = ntohl(vxlan->vxlan_id) >> VXLAN_ID_SHIFT; + vxf = ntohl(vxlan->vxlan_flags); + + if (flags & F_SUM) { + (void) snprintf(get_sum_line(), MAXLINE, + "VXLAN VNI=%d", id); + } + + if (flags & F_DTAIL) { + show_header("VXLAN: ", "VXLAN Header", sizeof (vxlan_hdr_t)); + show_space(); + (void) snprintf(get_line(0, 0), get_line_remain(), + "Flags = 0x%08x", vxf); + (void) snprintf(get_line(0, 0), get_line_remain(), " %s", + getflag(vxf >> 24, VXLAN_F_VDI >> 24, "vni present", + "vni missing")); + (void) snprintf(get_line(0, 0), get_line_remain(), + "VXLAN network id (VNI) = %d", id); + show_space(); + } + + if (flags & (F_DTAIL | F_ALLSUM)) { + fraglen -= sizeof (vxlan_hdr_t); + data += sizeof (vxlan_hdr_t); + + return (interpret_ether(flags, data, fraglen, fraglen)); + } + + return (0); +} diff --git a/usr/src/cmd/column/Makefile b/usr/src/cmd/column/Makefile new file mode 100644 index 0000000000..b9b395b384 --- /dev/null +++ b/usr/src/cmd/column/Makefile @@ -0,0 +1,34 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# +# Copyright 2018 Joyent, Inc. +# + +PROG=column +OBJS=column.o + +include ../Makefile.cmd + +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +install: all $(ROOTPROG) + +clean: + $(RM) $(OBJS) + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/column/THIRDPARTYLICENSE b/usr/src/cmd/column/THIRDPARTYLICENSE new file mode 100644 index 0000000000..a80f56cb43 --- /dev/null +++ b/usr/src/cmd/column/THIRDPARTYLICENSE @@ -0,0 +1,26 @@ +Copyright (c) 1989, 1993, 1994 + The Regents of the University of California. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +4. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + * +THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/usr/src/cmd/column/THIRDPARTYLICENSE.descrip b/usr/src/cmd/column/THIRDPARTYLICENSE.descrip new file mode 100644 index 0000000000..42051a2982 --- /dev/null +++ b/usr/src/cmd/column/THIRDPARTYLICENSE.descrip @@ -0,0 +1 @@ +PORTIONS OF COLUMN COMMAND FUNCTIONALITY diff --git a/usr/src/cmd/column/column.c b/usr/src/cmd/column/column.c new file mode 100644 index 0000000000..5c76cb8751 --- /dev/null +++ b/usr/src/cmd/column/column.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Portions Copyright 2018 Joyent, Inc. + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/termios.h> + +#include <err.h> +#include <limits.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> +#include <wctype.h> + +#define TAB 8 +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) /* to any y */ + +static void c_columnate(void); +static void input(FILE *); +static void maketbl(void); +static void print(void); +static void r_columnate(void); +static void usage(void); +static int width(const wchar_t *); + +static int termwidth = 80; /* default terminal width */ + +static int entries; /* number of records */ +static int eval; /* exit value */ +static int maxlength; /* longest record */ +static wchar_t **list; /* array of pointers to records */ +static const wchar_t *separator = L"\t "; /* field separator for table option */ + +int +main(int argc, char **argv) +{ + struct winsize win; + FILE *fp; + int ch, tflag, xflag; + char *p; + const char *src; + wchar_t *newsep; + size_t seplen; + + (void) setlocale(LC_ALL, ""); + + if (ioctl(1, TIOCGWINSZ, &win) == -1 || !win.ws_col) { + if ((p = getenv("COLUMNS"))) + termwidth = atoi(p); + } else + termwidth = win.ws_col; + + tflag = xflag = 0; + while ((ch = getopt(argc, argv, "c:s:tx")) != -1) + switch (ch) { + case 'c': + termwidth = atoi(optarg); + break; + case 's': + src = optarg; + seplen = mbsrtowcs(NULL, &src, 0, NULL); + if (seplen == (size_t)-1) + err(1, "bad separator"); + newsep = malloc((seplen + 1) * sizeof (wchar_t)); + if (newsep == NULL) + err(1, NULL); + (void) mbsrtowcs(newsep, &src, seplen + 1, NULL); + separator = newsep; + break; + case 't': + tflag = 1; + break; + case 'x': + xflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!*argv) + input(stdin); + else for (; *argv; ++argv) + if ((fp = fopen(*argv, "rF"))) { + input(fp); + (void) fclose(fp); + } else { + warn("%s", *argv); + eval = 1; + } + + if (!entries) + exit(eval); + + maxlength = roundup(maxlength + 1, TAB); + if (tflag) + maketbl(); + else if (maxlength >= termwidth) + print(); + else if (xflag) + c_columnate(); + else + r_columnate(); + exit(eval); + + /*NOTREACHED*/ + return (eval); +} + +static void +c_columnate(void) +{ + int chcnt, col, cnt, endcol, numcols; + wchar_t **lp; + + numcols = termwidth / maxlength; + endcol = maxlength; + for (chcnt = col = 0, lp = list; ; ++lp) { + (void) wprintf(L"%ls", *lp); + chcnt += width(*lp); + if (!--entries) + break; + if (++col == numcols) { + chcnt = col = 0; + endcol = maxlength; + (void) putwchar('\n'); + } else { + while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) { + (void) putwchar('\t'); + chcnt = cnt; + } + endcol += maxlength; + } + } + if (chcnt) + (void) putwchar('\n'); +} + +static void +r_columnate(void) +{ + int base, chcnt, cnt, col, endcol, numcols, numrows, row; + + numcols = termwidth / maxlength; + numrows = entries / numcols; + if (entries % numcols) + ++numrows; + + for (row = 0; row < numrows; ++row) { + endcol = maxlength; + for (base = row, chcnt = col = 0; col < numcols; ++col) { + (void) wprintf(L"%ls", list[base]); + chcnt += width(list[base]); + if ((base += numrows) >= entries) + break; + while ((cnt = roundup(chcnt + 1, TAB)) <= endcol) { + (void) putwchar('\t'); + chcnt = cnt; + } + endcol += maxlength; + } + (void) putwchar('\n'); + } +} + +static void +print(void) +{ + int cnt; + wchar_t **lp; + + for (cnt = entries, lp = list; cnt--; ++lp) + (void) wprintf(L"%ls\n", *lp); +} + +typedef struct _tbl { + wchar_t **list; + int cols, *len; +} TBL; +#define DEFCOLS 25 + +static void +maketbl(void) +{ + TBL *t; + int coloff, cnt; + wchar_t *p, **lp; + int *lens, maxcols; + TBL *tbl; + wchar_t **cols; + wchar_t *last; + + if ((t = tbl = calloc(entries, sizeof (TBL))) == NULL) + err(1, (char *)NULL); + if ((cols = calloc((maxcols = DEFCOLS), sizeof (*cols))) == NULL) + err(1, (char *)NULL); + if ((lens = calloc(maxcols, sizeof (int))) == NULL) + err(1, (char *)NULL); + for (cnt = 0, lp = list; cnt < entries; ++cnt, ++lp, ++t) { + for (coloff = 0, p = *lp; + (cols[coloff] = wcstok(p, separator, &last)); + p = NULL) + if (++coloff == maxcols) { + if (!(cols = realloc(cols, ((uint_t)maxcols + + DEFCOLS) * sizeof (char *))) || + !(lens = realloc(lens, + ((uint_t)maxcols + DEFCOLS) * + sizeof (int)))) + err(1, NULL); + (void) memset((char *)lens + maxcols * + sizeof (int), 0, DEFCOLS * sizeof (int)); + maxcols += DEFCOLS; + } + if ((t->list = calloc(coloff, sizeof (*t->list))) == NULL) + err(1, (char *)NULL); + if ((t->len = calloc(coloff, sizeof (int))) == NULL) + err(1, (char *)NULL); + for (t->cols = coloff; --coloff >= 0; ) { + t->list[coloff] = cols[coloff]; + t->len[coloff] = width(cols[coloff]); + if (t->len[coloff] > lens[coloff]) + lens[coloff] = t->len[coloff]; + } + } + for (cnt = 0, t = tbl; cnt < entries; ++cnt, ++t) { + for (coloff = 0; coloff < t->cols - 1; ++coloff) + (void) wprintf(L"%ls%*ls", t->list[coloff], + lens[coloff] - t->len[coloff] + 2, L" "); + (void) wprintf(L"%ls\n", t->list[coloff]); + } +} + +#define DEFNUM 1000 +#define MAXLINELEN (LINE_MAX + 1) + +static void +input(FILE *fp) +{ + static int maxentry; + int len; + wchar_t *p, buf[MAXLINELEN]; + + if (!list) + if ((list = calloc((maxentry = DEFNUM), sizeof (*list))) == + NULL) + err(1, (char *)NULL); + while (fgetws(buf, MAXLINELEN, fp)) { + for (p = buf; *p && iswspace(*p); ++p) + ; + if (!*p) + continue; + if (!(p = wcschr(p, L'\n'))) { + warnx("line too long"); + eval = 1; + continue; + } + *p = L'\0'; + len = width(buf); + if (maxlength < len) + maxlength = len; + if (entries == maxentry) { + maxentry += DEFNUM; + if (!(list = realloc(list, + (uint_t)maxentry * sizeof (*list)))) + err(1, NULL); + } + list[entries] = malloc((wcslen(buf) + 1) * sizeof (wchar_t)); + if (list[entries] == NULL) + err(1, NULL); + (void) wcscpy(list[entries], buf); + entries++; + } +} + +/* Like wcswidth(), but ignores non-printing characters. */ +static int +width(const wchar_t *wcs) +{ + int w, cw; + + for (w = 0; *wcs != L'\0'; wcs++) + if ((cw = wcwidth(*wcs)) > 0) + w += cw; + return (w); +} + +static void +usage(void) +{ + + (void) fprintf(stderr, + "usage: column [-tx] [-c columns] [-s sep] [file ...]\n"); + exit(1); +} diff --git a/usr/src/cmd/connstat/connstat_tcp.c b/usr/src/cmd/connstat/connstat_tcp.c index 4cd20c9b09..bcf2c2ef5b 100644 --- a/usr/src/cmd/connstat/connstat_tcp.c +++ b/usr/src/cmd/connstat/connstat_tcp.c @@ -14,6 +14,7 @@ */ /* * Copyright (c) 2015, 2016 by Delphix. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #include <stdio.h> @@ -77,6 +78,7 @@ typedef struct tcp_fields_buf_s { uint64_t t_rtt_sum; int t_state; uint64_t t_rtt; + uint64_t t_rttvar; } tcp_fields_buf_t; static boolean_t print_tcp_state(ofmt_arg_t *, char *, uint_t); @@ -126,6 +128,8 @@ static ofmt_field_t tcp_fields[] = { offsetof(tcp_fields_buf_t, t_rtt_sum), print_uint64 }, { "RTTC", 11, offsetof(tcp_fields_buf_t, t_rtt_cnt), print_uint32 }, + { "RTTVAR", 8, + offsetof(tcp_fields_buf_t, t_rttvar), print_uint64 }, { "STATE", 12, offsetof(tcp_fields_buf_t, t_state), print_tcp_state }, { NULL, 0, 0, NULL} @@ -189,6 +193,7 @@ tcp_ci2buf(struct tcpConnEntryInfo_s *ci) fields_buf.t_rtt = (ci->ce_out_data_segs == 0 ? 0 : ci->ce_rtt_sa); fields_buf.t_rtt_sum = ci->ce_rtt_sum; fields_buf.t_rtt_cnt = ci->ce_rtt_cnt; + fields_buf.t_rttvar = ci->ce_rtt_sd; fields_buf.t_state = ci->ce_state; } diff --git a/usr/src/cmd/coreadm/coreadm.c b/usr/src/cmd/coreadm/coreadm.c index ca7edc179e..23916a7c06 100644 --- a/usr/src/cmd/coreadm/coreadm.c +++ b/usr/src/cmd/coreadm/coreadm.c @@ -244,6 +244,12 @@ main(int argc, char **argv) command); usage(); } + if (glob_pattern != NULL && glob_pattern[0] != '/') { + (void) fprintf(stderr, gettext( + "%s: The -g option must specify an absolute path\n"), + command); + usage(); + } if ((proc_pattern != NULL || proc_content != CC_CONTENT_INVALID) && npids == 0) { (void) sprintf(curpid, "%u", (uint_t)getppid()); diff --git a/usr/src/cmd/coreadm/coreadm.xml b/usr/src/cmd/coreadm/coreadm.xml index 46a4cda17a..c6198f885a 100644 --- a/usr/src/cmd/coreadm/coreadm.xml +++ b/usr/src/cmd/coreadm/coreadm.xml @@ -92,17 +92,17 @@ value='solaris.smf.value.coreadm' /> <propval name='global_pattern' - type='astring' value='' /> + type='astring' value='/%Z/cores/core.%f.%p' /> <propval name='global_content' type='astring' value='default' /> <propval name='init_pattern' - type='astring' value='core' /> + type='astring' value='/%Z/cores/core.%f.%p' /> <propval name='init_content' type='astring' value='default' /> <propval name='global_enabled' - type='boolean' value='false' /> - <propval name='process_enabled' type='boolean' value='true' /> + <propval name='process_enabled' + type='boolean' value='false' /> <propval name='global_setid_enabled' type='boolean' value='false' /> <propval name='process_setid_enabled' diff --git a/usr/src/cmd/cron/Makefile b/usr/src/cmd/cron/Makefile index 8e9a95060d..3e1eebe136 100644 --- a/usr/src/cmd/cron/Makefile +++ b/usr/src/cmd/cron/Makefile @@ -28,6 +28,7 @@ DEFAULTFILES = cron.dfl include ../Makefile.cmd +include ../Makefile.ctf MANIFEST = cron.xml @@ -48,6 +49,7 @@ ROOTVAR = $(ROOT)/var ROOTSPCRON = $(ROOTVAR)/spool/cron ROOTCROND = $(ROOTETC)/cron.d +ROOTCRONDCRONTABS = $(ROOTCROND)/crontabs ROOTCRONTABS = $(ROOTSPCRON)/crontabs ROOTATJOBS = $(ROOTSPCRON)/atjobs ROOTLIBCRON = $(ROOTLIB)/cron @@ -67,9 +69,6 @@ POFILES1= at.po crontab.po funcs.po batch.po POFILES= $(POFILES1) atrm.po $(POFILES1) := XGETFLAGS= -a -x $(PROG1).xcl -ROOTDIRS = $(ROOTSPCRON) $(ROOTCROND) \ - $(ROOTCRONTABS) $(ROOTATJOBS) - ROOTPROG = $(PROG1:%=$(ROOTUSRSBIN)/%) $(PROG2:%=$(ROOTBIN)/%) \ $(SCRIPT:%=$(ROOTBIN)/%) \ $(XPG6PROG:%=$(ROOTXPG6BIN)/%) \ @@ -103,21 +102,21 @@ XPG4ATOBJS= $(ATOBJS:%=objs.xpg4/%) $(XPG4OBJS:%=objs.xpg4/%) XPG6COMMONOBJS= $(COMMONOBJS:%=objs.xpg6/%) XPG6CTOBJS= $(CRONTABOBJS:%=objs.xpg6/%) -cron := POBJS = $(CRONOBJS) $(COMMONOBJ2) -at := POBJS = $(ATOBJS) $(COMMONOBJS) -at.xpg4 := POBJS = $(XPG4ATOBJS) $(XPG4COMMONOBJS) -atrm := POBJS = $(ATRMOBJS) $(COMMONOBJS) -atq := POBJS = $(ATQOBJS) $(COMMONOBJS) -crontab := POBJS = $(CRONTABOBJS) $(COMMONOBJS) -crontab.xpg4 := POBJS = $(XPG4CTOBJS) $(XPG4COMMONOBJS) -crontab.xpg6 := POBJS = $(XPG6CTOBJS) $(XPG6COMMONOBJS) -parsetest := POBJS = $(PARSETESTOBJS) +cron := OBJS = $(CRONOBJS) $(COMMONOBJ2) +at := OBJS = $(ATOBJS) $(COMMONOBJS) +at.xpg4 := OBJS = $(XPG4ATOBJS) $(XPG4COMMONOBJS) +atrm := OBJS = $(ATRMOBJS) $(COMMONOBJS) +atq := OBJS = $(ATQOBJS) $(COMMONOBJS) +crontab := OBJS = $(CRONTABOBJS) $(COMMONOBJS) +crontab.xpg4 := OBJS = $(XPG4CTOBJS) $(XPG4COMMONOBJS) +crontab.xpg6 := OBJS = $(XPG6CTOBJS) $(XPG6COMMONOBJS) +parsetest := OBJS = $(PARSETESTOBJS) CFLAGS += $(CCVERBOSE) NOBJS= $(CRONOBJS) $(ATOBJS) $(ATRMOBJS1) $(ATQOBJS) $(CRONTABOBJS1) \ $(COMMONOBJS) -OBJS = $(NOBJS) $(XPG4COMMONOBJS) $(XPG4ATOBJS) $(XPG4CTOBJS) \ +COBJS = $(NOBJS) $(XPG4COMMONOBJS) $(XPG4ATOBJS) $(XPG4CTOBJS) \ $(XPG6COMMONOBJS) $(XPG6CTOBJS) $(GETRESPOBJ) SRCS = $(NOBJS:%.o=%.c) $(GETRESPSRC) @@ -160,34 +159,37 @@ all : $(PROG) $(XPG4) $(XPG6) $(SCRIPT) $(XPG4SCRIPT) $(FILES) \ $(PARSETEST) install : all $(ROOTPROG) $(ROOTETCDEFAULTFILES) $(ROOTSYMLINK) \ - $(ROOTMANIFEST) $(ROOTMETHOD) + $(ROOTMANIFEST) $(ROOTMETHOD) $(ROOTCRONDCRONTABS)/root -$(PROG) $(PARSETEST): $$(POBJS) - $(LINK.c) $(POBJS) -o $@ $(LDLIBS) +$(PROG) $(PARSETEST): $$(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) -$(XPG4) : objs.xpg4 $$(POBJS) - $(LINK.c) $(POBJS) -o $@ $(LDLIBS) +$(XPG4) : objs.xpg4 $$(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) -$(XPG6) : objs.xpg6 $$(POBJS) - $(LINK.c) $(POBJS) -o $@ $(LDLIBS) +$(XPG6) : objs.xpg6 $$(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) objs.xpg6/%.o: %.c $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) objs.xpg6: -@mkdir -p $@ objs.xpg4/%.o: %.c $(COMPILE.c) -o $@ $< + $(POST_PROCESS_O) objs.xpg4: -@mkdir -p $@ objs.xpg4/values-xpg4.o: ../../lib/crt/common/values-xpg4.c $(COMPILE.c) -o $@ ../../lib/crt/common/values-xpg4.c + $(POST_PROCESS_O) %.o: $(SRC)/common/util/%.c $(COMPILE.c) $(OUTPUT_OPTION) $< @@ -211,20 +213,21 @@ att2.c : att2.l att2.ed att1.c ed - lex.yy.c < att2.ed $(MV) lex.yy.c att2.c -# Don't re-install directories installed by Targetdirs -#$(ROOTDIRS): -# $(INS.dir) - $(ROOTSYMLINK) : $(RM) $@; $(SYMLINK) $(SYMLNKDEST) $@ +$(ROOTCRONDCRONTABS)/root: crontab.root + $(RM) $@; \ + $(INS) -s -m $(FILEMODE) -f $(@D) crontab.root; \ + $(MV) $(@D)/crontab.root $@ + check: $(CHKMANIFEST) $(POFILE): $(POFILES) $(RM) $@; cat $(POFILES) > $@ clean : - $(RM) $(OBJS) att1.h att1.c att2.c + $(RM) $(COBJS) att1.h att1.c att2.c strip : $(STRIP) $(PROG) $(XPG4) $(XPG6) diff --git a/usr/src/cmd/cron/cron.c b/usr/src/cmd/cron/cron.c index faefcbebd0..8b549c3f0d 100644 --- a/usr/src/cmd/cron/cron.c +++ b/usr/src/cmd/cron/cron.c @@ -23,7 +23,7 @@ * Use is subject to license terms. * * Copyright 2013 Joshua M. Clulow <josh@sysmgr.org> - * + * Copyright 2013 Joyent, Inc. All rights reserved. * Copyright (c) 2014 Gary Mills * Copyright (c) 2016 by Delphix. All rights reserved. * Copyright 2019 OmniOS Community Edition (OmniOSce) Association. @@ -311,7 +311,8 @@ static struct usr *find_usr(char *); static int ex(struct event *e); static void read_dirs(int); static void mail(char *, char *, int); -static void readcron(struct usr *, time_t); +static void readcron(char *, struct usr *, time_t); +static void readcronfile(FILE *, struct usr *, time_t); static int next_ge(int, char *); static void free_if_unused(struct usr *); static void del_atjob(char *, char *); @@ -416,7 +417,7 @@ extern void el_delete(void); static int valid_entry(char *, int); static struct usr *create_ulist(char *, int); -static void init_cronevent(char *, int); +static void init_cronevent(char *, char *); static void init_atevent(char *, time_t, int, int); static void update_atevent(struct usr *, char *, time_t, int); @@ -755,6 +756,18 @@ read_dirs(int first) time_t tim; + if (chdir(SYSCRONDIR) != -1) { + cwd = CRON; + if ((dir = opendir(".")) != NULL) { + while ((dp = readdir(dir)) != NULL) { + if (!valid_entry(dp->d_name, CRONEVENT)) + continue; + init_cronevent(SYSCRONDIR, dp->d_name); + } + (void) closedir(dir); + } + } + if (chdir(CRONDIR) == -1) crabort(BADCD, REMOVE_FIFO|CONSOLE_MSG); cwd = CRON; @@ -763,7 +776,7 @@ read_dirs(int first) while ((dp = readdir(dir)) != NULL) { if (!valid_entry(dp->d_name, CRONEVENT)) continue; - init_cronevent(dp->d_name, first); + init_cronevent(CRONDIR, dp->d_name); } (void) closedir(dir); @@ -849,23 +862,18 @@ create_ulist(char *name, int type) } void -init_cronevent(char *name, int first) +init_cronevent(char *basedir, char *name) { struct usr *u; - if (first) { + if ((u = find_usr(name)) == NULL) { u = create_ulist(name, CRONEVENT); - readcron(u, 0); + readcron(basedir, u, 0); } else { - if ((u = find_usr(name)) == NULL) { - u = create_ulist(name, CRONEVENT); - readcron(u, 0); - } else { - u->ctexists = TRUE; - rm_ctevents(u); - el_remove(u->ctid, 0); - readcron(u, 0); - } + u->ctexists = TRUE; + rm_ctevents(u); + el_remove(u->ctid, 0); + readcron(basedir, u, 0); } } @@ -946,7 +954,7 @@ mod_ctab(char *name, time_t reftime) (void) strcpy(u->home, pw->pw_dir); u->uid = pw->pw_uid; u->gid = pw->pw_gid; - readcron(u, reftime); + readcron(CRONDIR, u, reftime); } else { u->uid = pw->pw_uid; u->gid = pw->pw_gid; @@ -969,7 +977,7 @@ mod_ctab(char *name, time_t reftime) /* user didnt have a crontab last time */ u->ctid = ecid++; u->ctevents = NULL; - readcron(u, reftime); + readcron(CRONDIR, u, reftime); return; } #ifdef DEBUG @@ -978,7 +986,7 @@ mod_ctab(char *name, time_t reftime) #endif rm_ctevents(u); el_remove(u->ctid, 0); - readcron(u, reftime); + readcron(CRONDIR, u, reftime); } } @@ -1112,8 +1120,94 @@ update_atevent(struct usr *u, char *name, time_t tim, int jobtype) static char line[CTLINESIZE]; /* holds a line from a crontab file */ static int cursor; /* cursor for the above line */ +static int +copyfile(char *name, FILE *dp) +{ + FILE *tf; + + if ((tf = fopen(name, "r")) == NULL) { + (void) fclose(dp); + return (1); + } + + while (fgets(line, CTLINESIZE, tf) != NULL) { + if (fputs(line, dp) == EOF) { + (void) fclose(tf); + (void) fclose(dp); + return (1); + } + } + (void) fclose(tf); + + return (0); +} + +static void +readcron(char *basedir, struct usr *u, time_t reftime) +{ + char *altpath; + struct stat sb; + FILE *cf; /* cf will be a user's crontab file */ + char altnamebuf[PATH_MAX]; + char namebuf[PATH_MAX]; + + if (strcmp(basedir, SYSCRONDIR) == 0) + altpath = CRONDIR; + else + altpath = SYSCRONDIR; + + if (snprintf(altnamebuf, sizeof (altnamebuf), "%s/%s", altpath, + u->name) >= sizeof (altnamebuf)) + return; + + if (snprintf(namebuf, sizeof (namebuf), "%s/%s", basedir, u->name) >= + sizeof (namebuf)) + return; + + if (stat(altnamebuf, &sb) != -1) { + /* + * There is a secondary crontab for this user. We need to + * merge the two crontabs into a temporary file for loading. + */ + int fd; + char tmpfile[PATH_MAX]; + + (void) strlcpy(tmpfile, "/tmp/cronXXXXXX", sizeof (tmpfile)); + if ((fd = mkstemp(tmpfile)) == -1) + return; + + unlink(tmpfile); + if ((cf = fdopen(fd, "w+")) == NULL) { + close(fd); + return; + } + + if (copyfile(namebuf, cf) != 0) + return; + + if (copyfile(altnamebuf, cf) != 0) + return; + + (void) fflush(cf); + rewind(cf); + + } else { + /* + * Only one crontab, open it directly. + */ + if ((cf = fopen(namebuf, "r")) == NULL) { + mail(u->name, NOREAD, ERR_UNIXERR); + return; + } + } + + readcronfile(cf, u, reftime); + + (void) fclose(cf); +} + static void -readcron(struct usr *u, time_t reftime) +readcronfile(FILE *cf, struct usr *u, time_t reftime) { /* * readcron reads in a crontab file for a user (u). The list of @@ -1121,12 +1215,9 @@ readcron(struct usr *u, time_t reftime) * this list. Each event is also entered into the main event * list. */ - FILE *cf; /* cf will be a user's crontab file */ struct event *e; int start; unsigned int i; - char namebuf[PATH_MAX]; - char *pname; struct shared *tz = NULL; struct shared *home = NULL; struct shared *shell = NULL; @@ -1134,19 +1225,6 @@ readcron(struct usr *u, time_t reftime) /* read the crontab file */ cte_init(); /* Init error handling */ - if (cwd != CRON) { - if (snprintf(namebuf, sizeof (namebuf), "%s/%s", - CRONDIR, u->name) >= sizeof (namebuf)) { - return; - } - pname = namebuf; - } else { - pname = u->name; - } - if ((cf = fopen(pname, "r")) == NULL) { - mail(u->name, NOREAD, ERR_UNIXERR); - return; - } while (fgets(line, CTLINESIZE, cf) != NULL) { char *tmp; /* process a line of a crontab file */ @@ -1282,7 +1360,6 @@ again: #endif } cte_sendmail(u->name); /* mail errors if any to user */ - (void) fclose(cf); rel_shared(tz); rel_shared(shell); rel_shared(home); @@ -2384,6 +2461,9 @@ ex(struct event *e) } else { r = audit_cron_session(e->u->name, CRONDIR, e->u->uid, e->u->gid, NULL); + if (r != 0) + r = audit_cron_session(e->u->name, SYSCRONDIR, + e->u->uid, e->u->gid, NULL); } if (r != 0) { msg("cron audit problem. job failed (%s) for user %s", diff --git a/usr/src/cmd/cron/cron.h b/usr/src/cmd/cron/cron.h index e071209673..0620572f8c 100644 --- a/usr/src/cmd/cron/cron.h +++ b/usr/src/cmd/cron/cron.h @@ -75,6 +75,10 @@ struct message { char logname[LLEN]; }; +/* anything below here can be changed */ + +#define SYSCRONDIR "/etc/cron.d/crontabs" + /* * Errors from the crontab field parser. */ diff --git a/usr/src/cmd/cron/crontab.c b/usr/src/cmd/cron/crontab.c index 06010aec83..18716bc8c2 100644 --- a/usr/src/cmd/cron/crontab.c +++ b/usr/src/cmd/cron/crontab.c @@ -73,8 +73,8 @@ #define BADUSAGE \ "usage:\n" \ "\tcrontab [-u username] [file]\n" \ - "\tcrontab [-u username] { -e | -l | -r }\n" \ - "\tcrontab { -e | -l | -r } [username]" + "\tcrontab [-u username] { -e | -g | -l | -r }\n" \ + "\tcrontab { -e | -g | -l | -r } [username]" #define INVALIDUSER "you are not a valid user (no entry in /etc/passwd)." #define NOTALLOWED "you are not authorized to use cron. Sorry." #define NOTROOT \ @@ -122,6 +122,7 @@ main(int argc, char **argv) int c, r; int rflag = 0; int lflag = 0; + int gflag = 0; int eflag = 0; int errflg = 0; char *pp; @@ -154,11 +155,14 @@ main(int argc, char **argv) exit(1); } - while ((c = getopt(argc, argv, "elru:")) != EOF) { + while ((c = getopt(argc, argv, "eglru:")) != EOF) { switch (c) { case 'e': eflag++; break; + case 'g': + gflag++; + break; case 'l': lflag++; break; @@ -180,6 +184,9 @@ main(int argc, char **argv) if (eflag + lflag + rflag > 1) errflg++; + if (gflag && !lflag) + errflg++; + if ((eflag || lflag || rflag) && argc > 0) { if (user != NULL) errflg++; @@ -253,12 +260,27 @@ main(int argc, char **argv) exit(0); } if (lflag) { - if ((fp = fopen(cf, "r")) == NULL) - crabort(BADOPEN); - while (fgets(line, CTLINESIZE, fp) != NULL) - fputs(line, stdout); - fclose(fp); - exit(0); + char sysconf[PATH_MAX]; + + if (gflag) { + if (snprintf(sysconf, sizeof (sysconf), "%s/%s", + SYSCRONDIR, login) < sizeof (sysconf) && + (fp = fopen(sysconf, "r")) != NULL) { + while (fgets(line, CTLINESIZE, fp) != NULL) + fputs(line, stdout); + fclose(fp); + exit(0); + } else { + crabort(BADOPEN); + } + } else { + if ((fp = fopen(cf, "r")) == NULL) + crabort(BADOPEN); + while (fgets(line, CTLINESIZE, fp) != NULL) + fputs(line, stdout); + fclose(fp); + exit(0); + } } if (eflag) { if ((fp = fopen(cf, "r")) == NULL) { diff --git a/usr/src/cmd/cron/crontab.root b/usr/src/cmd/cron/crontab.root new file mode 100644 index 0000000000..4153ad2d6a --- /dev/null +++ b/usr/src/cmd/cron/crontab.root @@ -0,0 +1,18 @@ +## Rotate and trim the audit logs nightly. +0 0 * * * /smartdc/bin/sdc-lastcomm -R 30 +## Run logadm hourly so that the minimum rotate interval is 1h +0 * * * * /usr/sbin/logadm +## Rotate vm.log files for any KVM VMs +0 * * * * SDC_LOG_ROLL_BACKWARD=1 /usr/vm/sbin/rotate-kvm-logs.sh >> /var/log/rotate-kvm-logs.log 2>&1 +## Headnode should phone home nightly. +0 1 * * * [ -x /opt/smartdc/bin/sdc-phonehome ] && /opt/smartdc/bin/sdc-phonehome +## Delete saved core dumps over 7 days old +15 0 * * * find /zones/*/cores -type f -mtime +7 -exec rm -f "{}" \; +## Delete archived zone data over 7 days old +30 0 * * * find /zones/archive/ -mount -maxdepth 1 -mindepth 1 -type d -mtime +7 -exec rm -rf "{}" \; +## Delete logs to be uploaded over 7 days old if they aren't being consumed +45 0 * * * [ -d /var/log/sdc/upload/ ] && find /var/log/sdc/upload/ -mount -maxdepth 1 -mindepth 1 -type f -mtime +7 -exec rm -f "{}" \; + +# NOTE: all entries above are in root's system-defined crontab; see the +# crontab(1) man page. + diff --git a/usr/src/cmd/cron/svc-cron b/usr/src/cmd/cron/svc-cron index 4def6071b8..55033608ee 100644 --- a/usr/src/cmd/cron/svc-cron +++ b/usr/src/cmd/cron/svc-cron @@ -22,8 +22,7 @@ # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. -# -# ident "%Z%%M% %I% %E% SMI" +# Copyright 2012 Joyent, Inc. All rights reserved. # # Start method script for the cron service. # @@ -37,6 +36,45 @@ if [ -p /etc/cron.d/FIFO ]; then fi fi +if smf_is_globalzone && [ -n "$(bootparams | grep '^headnode=true')" ]; then + # + # Randomize root's crontab so all HN's are not in sync. + # + utc_offset=`nawk -F= '{if ($1 == "utc_offset") print $2}' \ + /usbkey/config.inc/generic` + [ -z "$utc_offset" ] && utc_offset=0 + + n=$(date +%S) + hr=$(($n % 5)) + hr=$(($hr + $utc_offset)) + + n=$(date +%M) + mn=$(($n % 15)) + + nawk -v hr=$hr -v mn=$mn '{ + if (substr($1, 1, 1) == "#") { + print $0 + next + } + if (substr($2, 1, 1) == "*") { + print $0 + next + } + if (length($0) == 0) { + print $0 + next + } + + printf("%d %d %s %s %s %s", mn, hr, $3, $4, $5, $6) + for (i = 7; i <= NF; i++) + printf(" %s", $i) + printf("\n") + mn += 10 + }' /etc/cron.d/crontabs/root >/etc/cron.d/crontabs/root.$$ + cp /etc/cron.d/crontabs/root.$$ /etc/cron.d/crontabs/root + rm -f /etc/cron.d/crontabs/root.$$ +fi + if [ -x /usr/sbin/cron ]; then /usr/bin/rm -f /etc/cron.d/FIFO /usr/sbin/cron & diff --git a/usr/src/cmd/devfsadm/devlink.tab.sh b/usr/src/cmd/devfsadm/devlink.tab.sh index 6724fcb573..0267efeb9f 100644 --- a/usr/src/cmd/devfsadm/devlink.tab.sh +++ b/usr/src/cmd/devfsadm/devlink.tab.sh @@ -22,8 +22,7 @@ # # Copyright (c) 1998, 2000 by Sun Microsystems, Inc. # All rights reserved. -# -#ident "%Z%%M% %I% %E% SMI" +# Copyright (c) 2012 Joyent, Inc. All rights reserved. # # This is the script that generates the devlink.tab file. It is # architecture-aware, and dumps different stuff for x86 and sparc. @@ -34,8 +33,6 @@ # cat <<EOM -#ident "%Z%%M% %I% %E% SMI" -# # Copyright (c) 1998 by Sun Microsystems, Inc. # # diff --git a/usr/src/cmd/devfsadm/i386/Makefile b/usr/src/cmd/devfsadm/i386/Makefile index 1f14c93dad..75f2da3436 100644 --- a/usr/src/cmd/devfsadm/i386/Makefile +++ b/usr/src/cmd/devfsadm/i386/Makefile @@ -24,8 +24,11 @@ LINK_OBJS_i386 = \ misc_link_i386.o \ + lx_link_i386.o \ xen_link.o +lx_link_i386.o lx_link_i386.po lx_link_i386.ln := CPPFLAGS += -I$(UTSBASE)/common/brand/lx + xen_link.o xen_link.ln xen_link.po := CPPFLAGS += -I$(UTSBASE)/i86xpv include ../Makefile.com diff --git a/usr/src/cmd/devfsadm/i386/lx_link_i386.c b/usr/src/cmd/devfsadm/i386/lx_link_i386.c new file mode 100644 index 0000000000..b99a8361a0 --- /dev/null +++ b/usr/src/cmd/devfsadm/i386/lx_link_i386.c @@ -0,0 +1,81 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2016 Joyent, Inc. + */ + +#include <devfsadm.h> +#include <strings.h> +#include <stdio.h> +#include <sys/lx_ptm.h> +#include <sys/lx_autofs.h> + +static int lx_ptm(di_minor_t minor, di_node_t node); +static int lx_autofs(di_minor_t minor, di_node_t node); +static int lx_systrace(di_minor_t minor, di_node_t node); + +static devfsadm_create_t lx_create_cbt[] = { + { "pseudo", "ddi_pseudo", LX_PTM_DRV, + TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_ptm }, + { "pseudo", "ddi_pseudo", LX_AUTOFS_NAME, + TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_autofs }, + { "pseudo", "ddi_pseudo", "lx_systrace", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, lx_systrace }, +}; + +DEVFSADM_CREATE_INIT_V0(lx_create_cbt); + +static int +lx_ptm(di_minor_t minor, di_node_t node) +{ + char *mname = di_minor_name(minor); + + if (strcmp(LX_PTM_MINOR_NODE, mname) == 0) + (void) devfsadm_mklink("brand/lx/ptmx", node, minor, 0); + + return (DEVFSADM_CONTINUE); +} + +static int +lx_autofs(di_minor_t minor, di_node_t node) +{ + char *mname = di_minor_name(minor); + + if (strcmp(LX_AUTOFS_MINORNAME, mname) == 0) + (void) devfsadm_mklink("brand/lx/autofs", node, minor, 0); + + return (DEVFSADM_CONTINUE); +} + +static int +lx_systrace(di_minor_t minor, di_node_t node) +{ + char *mname = di_minor_name(minor); + char path[MAXPATHLEN]; + + (void) snprintf(path, sizeof (path), "dtrace/provider/%s", mname); + (void) devfsadm_mklink(path, node, minor, 0); + + return (DEVFSADM_CONTINUE); +} diff --git a/usr/src/cmd/devfsadm/misc_link.c b/usr/src/cmd/devfsadm/misc_link.c index 5f241df296..49be9e9b2d 100644 --- a/usr/src/cmd/devfsadm/misc_link.c +++ b/usr/src/cmd/devfsadm/misc_link.c @@ -21,7 +21,7 @@ /* * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. - * Copyright (c) 2015, Joyent, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #include <regex.h> @@ -32,6 +32,7 @@ #include <limits.h> #include <sys/zone.h> #include <sys/zcons.h> +#include <sys/zfd.h> #include <sys/cpuid_drv.h> static int display(di_minor_t minor, di_node_t node); @@ -53,6 +54,7 @@ static int av_create(di_minor_t minor, di_node_t node); static int tsalarm_create(di_minor_t minor, di_node_t node); static int ntwdt_create(di_minor_t minor, di_node_t node); static int zcons_create(di_minor_t minor, di_node_t node); +static int zfd_create(di_minor_t minor, di_node_t node); static int cpuid(di_minor_t minor, di_node_t node); static int glvc(di_minor_t minor, di_node_t node); static int ses_callback(di_minor_t minor, di_node_t node); @@ -114,12 +116,15 @@ static devfsadm_create_t misc_cbt[] = { "(^nca$)|(^rds$)|(^sdp$)|(^ipnet$)|(^dlpistub$)|(^bpf$)", TYPE_EXACT | DRV_RE, ILEVEL_1, minor_name }, + { "pseudo", "ddi_pseudo", "inotify", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name + }, { "pseudo", "ddi_pseudo", "ipd", TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name }, { "pseudo", "ddi_pseudo", "(^ipf$)|(^ipnat$)|(^ipstate$)|(^ipauth$)|" - "(^ipsync$)|(^ipscan$)|(^iplookup$)", + "(^ipsync$)|(^ipscan$)|(^iplookup$)|(^ipfev$)", TYPE_EXACT | DRV_RE, ILEVEL_0, minor_name, }, { "pseudo", "ddi_pseudo", "dld", @@ -180,6 +185,9 @@ static devfsadm_create_t misc_cbt[] = { { "pseudo", "ddi_pseudo", "zcons", TYPE_EXACT | DRV_EXACT, ILEVEL_0, zcons_create, }, + { "pseudo", "ddi_pseudo", "zfd", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, zfd_create, + }, { "pseudo", "ddi_pseudo", CPUID_DRIVER_NAME, TYPE_EXACT | DRV_EXACT, ILEVEL_0, cpuid, }, @@ -204,6 +212,9 @@ static devfsadm_create_t misc_cbt[] = { { "pseudo", "ddi_pseudo", "tpm", TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name }, + { "pseudo", "ddi_pseudo", "overlay", + TYPE_EXACT | DRV_EXACT, ILEVEL_0, minor_name + } }; DEVFSADM_CREATE_INIT_V0(misc_cbt); @@ -228,6 +239,9 @@ static devfsadm_remove_t misc_remove_cbt[] = { ZCONS_SLAVE_NAME ")$", RM_PRE | RM_HOT | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all }, + { "pseudo", "^zfd/" ZONENAME_REGEXP "/(master|slave)/[0-9]+$", + RM_PRE | RM_HOT | RM_ALWAYS, ILEVEL_0, devfsadm_rm_all + }, { "pseudo", "^" CPUID_SELF_NAME "$", RM_ALWAYS | RM_PRE | RM_HOT, ILEVEL_0, devfsadm_rm_all }, @@ -675,6 +689,35 @@ zcons_create(di_minor_t minor, di_node_t node) return (DEVFSADM_CONTINUE); } +static int +zfd_create(di_minor_t minor, di_node_t node) +{ + char *minor_str; + char *zonename; + int *id; + char path[MAXPATHLEN]; + + minor_str = di_minor_name(minor); + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zfd_zname", + &zonename) == -1) + return (DEVFSADM_CONTINUE); + + if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "zfd_id", &id) == -1) + return (DEVFSADM_CONTINUE); + + if (strncmp(minor_str, "slave", 5) == 0) { + (void) snprintf(path, sizeof (path), "zfd/%s/slave/%d", + zonename, id[0]); + } else { + (void) snprintf(path, sizeof (path), "zfd/%s/master/%d", + zonename, id[0]); + } + (void) devfsadm_mklink(path, node, minor, 0); + + return (DEVFSADM_CONTINUE); +} + /* * /dev/cpu/self/cpuid -> /devices/pseudo/cpuid@0:self */ diff --git a/usr/src/cmd/dispadmin/Makefile b/usr/src/cmd/dispadmin/Makefile index 131b8567f3..93e4ab653d 100644 --- a/usr/src/cmd/dispadmin/Makefile +++ b/usr/src/cmd/dispadmin/Makefile @@ -22,12 +22,14 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# cmd/dispadmin/Makefile +# Copyright 2019 Joyent, Inc. # PROG= dispadmin MANIFEST= scheduler.xml SVCMETHOD= svc-scheduler +ETCFILES= dispadmin.conf + SDC= SDC$(PROG) RT= RT$(PROG) TS= TS$(PROG) @@ -36,6 +38,8 @@ FSS= FSS$(PROG) FX= FX$(PROG) PROGS= $(PROG) $(RT) $(TS) $(IA) $(FSS) $(FX) $(SDC) +ROOTETCFILES= $(ETCFILES:%=$(ROOTETC)/%) + include ../Makefile.cmd CFLAGS += $(CCVERBOSE) @@ -59,6 +63,8 @@ ROOTSDC= $(SDC:%=$(ROOTDIR)/SDC/%) ROOTTS= $(TS:%=$(ROOTDIR)/TS/%) ROOTMANIFESTDIR= $(ROOTSVCSYSTEM) +$(ROOTETCFILES) := FILEMODE = 644 + # this would be simpler if we renamed rtdispadmin.c and tsdispadmin.c OBJECTS= $(PROG).o rt$(PROG).o ts$(PROG).o ia$(PROG).o \ fss$(PROG).o fx$(PROG).o sdc$(PROG).o subr.o @@ -83,20 +89,15 @@ $(ROOTDIR)/TS/% : % $(INS.file) .KEEP_STATE: - -all: $(PROGS) + +all: $(PROGS) $(PROGS): $$(OBJ) subr.o $(LINK.c) -o $@ $(OBJ) subr.o $(LDLIBS) $(POST_PROCESS) -llib-lsubr.ln: subr.c - $(LINT.c) -y -o subr subr.c - -lint := LDLIBS += -L. -lsubr - install: all $(ROOTPROG) $(ROOTRT) $(ROOTTS) $(ROOTIA) $(ROOTFSS) $(ROOTFX) \ - $(ROOTSDC) $(ROOTMANIFEST) $(ROOTSVCMETHOD) + $(ROOTSDC) $(ROOTMANIFEST) $(ROOTSVCMETHOD) $(ROOTETCFILES) # Don't re-install directories already installed by Targetdirs #$(ROOTDIRS): @@ -105,15 +106,6 @@ install: all $(ROOTPROG) $(ROOTRT) $(ROOTTS) $(ROOTIA) $(ROOTFSS) $(ROOTFX) \ check: $(CHKMANIFEST) clean: - $(RM) $(OBJECTS) $(PROGS) llib-lsubr.ln - -lint: llib-lsubr.ln - $(LINT.c) dispadmin.c $(LDLIBS) - $(LINT.c) fssdispadmin.c $(LDLIBS) - $(LINT.c) fxdispadmin.c $(LDLIBS) - $(LINT.c) iadispadmin.c $(LDLIBS) - $(LINT.c) rtdispadmin.c $(LDLIBS) - $(LINT.c) sdcdispadmin.c $(LDLIBS) - $(LINT.c) tsdispadmin.c $(LDLIBS) - + $(RM) $(OBJECTS) $(PROGS) + include ../Makefile.targ diff --git a/usr/src/cmd/dispadmin/dispadmin.conf b/usr/src/cmd/dispadmin/dispadmin.conf new file mode 100644 index 0000000000..7970647c62 --- /dev/null +++ b/usr/src/cmd/dispadmin/dispadmin.conf @@ -0,0 +1 @@ +DEFAULT_SCHEDULER=FSS diff --git a/usr/src/cmd/dladm/Makefile b/usr/src/cmd/dladm/Makefile index 57328597dd..8c93d1008a 100644 --- a/usr/src/cmd/dladm/Makefile +++ b/usr/src/cmd/dladm/Makefile @@ -20,6 +20,7 @@ # # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2018 Joyent, Inc. # # Copyright (c) 2018, Joyent, Inc. @@ -38,7 +39,7 @@ XGETFLAGS += -a -x $(PROG).xcl LDLIBS += -L$(ROOT)/lib -lsocket LDLIBS += -ldladm -ldlpi -lkstat -lsecdb -lbsm -lofmt -linetutil -ldevinfo -LDLIBS += $(ZLAZYLOAD) -lrstp $(ZNOLAZYLOAD) +LDLIBS += $(ZLAZYLOAD) -lrstp $(ZNOLAZYLOAD) -lnsl -lumem -lcustr CERRWARN += -_gcc=-Wno-switch CERRWARN += -_gcc=-Wno-unused-label diff --git a/usr/src/cmd/dladm/dladm.c b/usr/src/cmd/dladm/dladm.c index 969bcaddba..c59926be94 100644 --- a/usr/src/cmd/dladm/dladm.c +++ b/usr/src/cmd/dladm/dladm.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2017 Joyent, Inc. * Copyright 2016 Nexenta Systems, Inc. * Copyright 2020 Peter Tribble. */ @@ -62,6 +63,7 @@ #include <libdliptun.h> #include <libdlsim.h> #include <libdlbridge.h> +#include <libdloverlay.h> #include <libinetutil.h> #include <libvrrpadm.h> #include <bsm/adt.h> @@ -77,6 +79,7 @@ #include <stddef.h> #include <stp_in.h> #include <ofmt.h> +#include <libcustr.h> #define MAXPORT 256 #define MAXVNIC 256 @@ -157,6 +160,7 @@ typedef struct show_vnic_state { dladm_status_t vs_status; uint32_t vs_flags; ofmt_handle_t vs_ofmt; + char *vs_zonename; } show_vnic_state_t; typedef struct show_part_state { @@ -187,6 +191,11 @@ typedef struct show_usage_state_s { ofmt_handle_t us_ofmt; } show_usage_state_t; +typedef struct show_overlay_request_s { + boolean_t sor_failed; + ofmt_handle_t sor_ofmt; +} show_overlay_request_t; + /* * callback functions for printing output and error diagnostics. */ @@ -195,6 +204,7 @@ static ofmt_cb_t print_lacp_cb, print_phys_one_mac_cb; static ofmt_cb_t print_xaggr_cb, print_aggr_stats_cb; static ofmt_cb_t print_phys_one_hwgrp_cb, print_wlan_attr_cb; static ofmt_cb_t print_wifi_status_cb, print_link_attr_cb; +static ofmt_cb_t print_overlay_cb, print_overlay_fma_cb, print_overlay_targ_cb; typedef void cmdfunc_t(int, char **, const char *); @@ -221,6 +231,8 @@ static cmdfunc_t do_create_bridge, do_modify_bridge, do_delete_bridge; static cmdfunc_t do_add_bridge, do_remove_bridge, do_show_bridge; static cmdfunc_t do_create_iptun, do_modify_iptun, do_delete_iptun; static cmdfunc_t do_show_iptun, do_up_iptun, do_down_iptun; +static cmdfunc_t do_create_overlay, do_delete_overlay, do_modify_overlay; +static cmdfunc_t do_show_overlay; static void do_up_vnic_common(int, char **, const char *, boolean_t); @@ -256,8 +268,11 @@ static void die(const char *, ...); static void die_optdup(int); static void die_opterr(int, int, const char *); static void die_dlerr(dladm_status_t, const char *, ...); +static void die_dlerrlist(dladm_status_t, dladm_errlist_t *, + const char *, ...); static void warn(const char *, ...); static void warn_dlerr(dladm_status_t, const char *, ...); +static void warn_dlerrlist(dladm_errlist_t *); typedef struct cmd { char *c_name; @@ -267,7 +282,7 @@ typedef struct cmd { static cmd_t cmds[] = { { "rename-link", do_rename_link, - " rename-link <oldlink> <newlink>" }, + " rename-link [-z zonename] <oldlink> <newlink>" }, { "show-link", do_show_link, " show-link [-pP] [-o <field>,..] [-s [-i <interval>]] " "[<link>]\n" }, @@ -302,12 +317,13 @@ static cmd_t cmds[] = { { "show-wifi", do_show_wifi, " show-wifi [-p] [-o <field>,...] [<link>]\n" }, { "set-linkprop", do_set_linkprop, - " set-linkprop [-t] -p <prop>=<value>[,...] <name>" }, + " set-linkprop [-t] [-z zonename] -p <prop>=<value>[,...] " + "<name>" }, { "reset-linkprop", do_reset_linkprop, - " reset-linkprop [-t] [-p <prop>,...] <name>" }, + " reset-linkprop [-t] [-z zonename] [-p <prop>,...] <name>"}, { "show-linkprop", do_show_linkprop, - " show-linkprop [-cP] [-o <field>,...] [-p <prop>,...] " - "<name>\n" }, + " show-linkprop [-cP] [-o <field>,...] [-z zonename] " + "[-p <prop>,...] <name>\n" }, { "show-ether", do_show_ether, " show-ether [-px][-o <field>,...] <link>\n" }, { "create-secobj", do_create_secobj, @@ -349,10 +365,10 @@ static cmd_t cmds[] = { "\t\t {vrrp -V <vrid> -A {inet | inet6}} [-v <vid> [-f]]\n" "\t\t [-p <prop>=<value>[,...]] <vnic-link>" }, { "delete-vnic", do_delete_vnic, - " delete-vnic [-t] <vnic-link>" }, + " delete-vnic [-t] [-z zonename] <vnic-link>" }, { "show-vnic", do_show_vnic, - " show-vnic [-pP] [-l <link>] [-s [-i <interval>]] " - "[<link>]\n" }, + " show-vnic [-pP] [-l <link>] [-z zonename] " + "[-s [-i <interval>]] [<link>]\n" }, { "up-vnic", do_up_vnic, NULL }, { "create-part", do_create_part, " create-part [-t] [-f] -l <link> [-P <pkey>]\n" @@ -403,6 +419,17 @@ static cmd_t cmds[] = { " <bridge>\n" " show-bridge -t [-p] [-o <field>,...] [-s [-i <interval>]]" " <bridge>\n" }, + { "create-overlay", do_create_overlay, + " create-overlay [-t] -e <encap> -s <search> -v <vnetid>\n" + "\t\t [ -p <prop>=<value>[,...]] <overlay>" }, + { "delete-overlay", do_delete_overlay, + " delete-overlay <overlay>" }, + { "modify-overlay", do_modify_overlay, + " modify-overlay -d mac | -f | -s mac=ip:port " + "<overlay>" }, + { "show-overlay", do_show_overlay, + " show-overlay [-f | -t] [[-p] -o <field>,...] " + "[<overlay>]\n" }, { "show-usage", do_show_usage, " show-usage [-a] [-d | -F <format>] " "[-s <DD/MM/YYYY,HH:MM:SS>]\n" @@ -961,6 +988,7 @@ typedef struct show_linkprop_state { char ls_link[MAXLINKNAMELEN]; char *ls_line; char **ls_propvals; + char *ls_zonename; dladm_arg_list_t *ls_proplist; boolean_t ls_parsable; boolean_t ls_persist; @@ -1013,21 +1041,24 @@ typedef struct vnic_fields_buf_s char vnic_macaddr[18]; char vnic_macaddrtype[19]; char vnic_vid[6]; + char vnic_zone[ZONENAME_MAX]; } vnic_fields_buf_t; static const ofmt_field_t vnic_fields[] = { { "LINK", 13, offsetof(vnic_fields_buf_t, vnic_link), print_default_cb}, -{ "OVER", 13, +{ "OVER", 11, offsetof(vnic_fields_buf_t, vnic_over), print_default_cb}, -{ "SPEED", 7, +{ "SPEED", 6, offsetof(vnic_fields_buf_t, vnic_speed), print_default_cb}, { "MACADDRESS", 18, offsetof(vnic_fields_buf_t, vnic_macaddr), print_default_cb}, -{ "MACADDRTYPE", 20, +{ "MACADDRTYPE", 12, offsetof(vnic_fields_buf_t, vnic_macaddrtype), print_default_cb}, -{ "VID", 7, +{ "VID", 5, offsetof(vnic_fields_buf_t, vnic_vid), print_default_cb}, +{ "ZONE", 20, + offsetof(vnic_fields_buf_t, vnic_zone), print_default_cb}, { NULL, 0, 0, NULL}} ; @@ -1427,6 +1458,82 @@ static ofmt_field_t bridge_trill_fields[] = { offsetof(bridge_trill_fields_buf_t, bridget_nexthop), print_default_cb }, { NULL, 0, 0, NULL}}; +static const struct option overlay_create_lopts[] = { + { "encap", required_argument, NULL, 'e' }, + { "prop", required_argument, NULL, 'p' }, + { "search", required_argument, NULL, 's' }, + { "temporary", no_argument, NULL, 't' }, + { "vnetid", required_argument, NULL, 'v' }, + { NULL, 0, NULL, 0 } +}; + +static const struct option overlay_modify_lopts[] = { + { "delete-entry", required_argument, NULL, 'd' }, + { "flush-table", no_argument, NULL, 'f' }, + { "set-entry", required_argument, NULL, 's' }, + { NULL, 0, NULL, 0 } +}; + +static const struct option overlay_show_lopts[] = { + { "fma", no_argument, NULL, 'f' }, + { "target", no_argument, NULL, 't' }, + { "parsable", no_argument, NULL, 'p' }, + { "parseable", no_argument, NULL, 'p' }, + { "output", required_argument, NULL, 'o' }, + { NULL, 0, NULL, 0 } +}; + +/* + * Structures for dladm show-overlay + */ +typedef enum { + OVERLAY_LINK, + OVERLAY_PROPERTY, + OVERLAY_PERM, + OVERLAY_REQ, + OVERLAY_VALUE, + OVERLAY_DEFAULT, + OVERLAY_POSSIBLE +} overlay_field_index_t; + +static const ofmt_field_t overlay_fields[] = { +/* name, field width, index */ +{ "LINK", 19, OVERLAY_LINK, print_overlay_cb }, +{ "PROPERTY", 19, OVERLAY_PROPERTY, print_overlay_cb }, +{ "PERM", 5, OVERLAY_PERM, print_overlay_cb }, +{ "REQ", 4, OVERLAY_REQ, print_overlay_cb }, +{ "VALUE", 11, OVERLAY_VALUE, print_overlay_cb }, +{ "DEFAULT", 10, OVERLAY_DEFAULT, print_overlay_cb }, +{ "POSSIBLE", 10, OVERLAY_POSSIBLE, print_overlay_cb }, +{ NULL, 0, 0, NULL } +}; + +typedef enum { + OVERLAY_FMA_LINK, + OVERLAY_FMA_STATUS, + OVERLAY_FMA_DETAILS +} overlay_fma_field_index_t; + +static const ofmt_field_t overlay_fma_fields[] = { +{ "LINK", 20, OVERLAY_FMA_LINK, print_overlay_fma_cb }, +{ "STATUS", 8, OVERLAY_FMA_STATUS, print_overlay_fma_cb }, +{ "DETAILS", 52, OVERLAY_FMA_DETAILS, print_overlay_fma_cb }, +{ NULL, 0, 0, NULL } +}; + +typedef enum { + OVERLAY_TARG_LINK, + OVERLAY_TARG_TARGET, + OVERLAY_TARG_DEST +} overlay_targ_field_index_t; + +static const ofmt_field_t overlay_targ_fields[] = { +{ "LINK", 20, OVERLAY_TARG_LINK, print_overlay_targ_cb }, +{ "TARGET", 18, OVERLAY_TARG_TARGET, print_overlay_targ_cb }, +{ "DESTINATION", 42, OVERLAY_TARG_DEST, print_overlay_targ_cb }, +{ NULL, 0, 0, NULL } +}; + static char *progname; static sig_atomic_t signalled; @@ -1436,6 +1543,12 @@ static sig_atomic_t signalled; */ static dladm_handle_t handle = NULL; +/* + * Global error list that all routines can use. It's initialized by the main + * code. + */ +static dladm_errlist_t errlist; + #define DLADM_ETHERSTUB_NAME "etherstub" #define DLADM_IS_ETHERSTUB(id) (id == DATALINK_INVALID_LINKID) @@ -1486,6 +1599,8 @@ main(int argc, char *argv[]) "could not open /dev/dld"); } + dladm_errlist_init(&errlist); + cmdp->c_fn(argc - 1, &argv[1], cmdp->c_usage); dladm_close(handle); @@ -2497,13 +2612,17 @@ do_rename_link(int argc, char *argv[], const char *use) char *link1, *link2; char *altroot = NULL; dladm_status_t status; + char *zonename = NULL; opterr = 0; - while ((option = getopt_long(argc, argv, ":R:", lopts, NULL)) != -1) { + while ((option = getopt_long(argc, argv, ":R:z:", lopts, NULL)) != -1) { switch (option) { case 'R': altroot = optarg; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); break; @@ -2519,7 +2638,7 @@ do_rename_link(int argc, char *argv[], const char *use) link1 = argv[optind++]; link2 = argv[optind]; - if ((status = dladm_rename_link(handle, link1, link2)) != + if ((status = dladm_rename_link(handle, zonename, link1, link2)) != DLADM_STATUS_OK) die_dlerr(status, "rename operation failed"); } @@ -3408,11 +3527,12 @@ do_show_link(int argc, char *argv[], const char *use) ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; + char *zonename = NULL; bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":pPsi:o:", + while ((option = getopt_long(argc, argv, ":pPsi:o:z:", show_lopts, NULL)) != -1) { switch (option) { case 'p': @@ -3445,6 +3565,9 @@ do_show_link(int argc, char *argv[], const char *use) if (!dladm_str2interval(optarg, &interval)) die("invalid interval value '%s'", optarg); break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); break; @@ -3464,8 +3587,8 @@ do_show_link(int argc, char *argv[], const char *use) if (strlcpy(linkname, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN) die("link name too long"); - if ((status = dladm_name2info(handle, linkname, &linkid, &f, - NULL, NULL)) != DLADM_STATUS_OK) { + if ((status = dladm_zname2info(handle, zonename, linkname, + &linkid, &f, NULL, NULL)) != DLADM_STATUS_OK) { die_dlerr(status, "link %s is not valid", linkname); } @@ -4734,6 +4857,12 @@ do_create_vnic(int argc, char *argv[], const char *use) if ((flags & DLADM_OPT_FORCE) != 0 && vid == 0) die("-f option can only be used with -v"); + /* + * If creating a transient VNIC for a zone, mark it in the kernel. + */ + if (strstr(propstr, "zone=") != NULL && !(flags & DLADM_OPT_PERSIST)) + flags |= DLADM_OPT_TRANSIENT; + if (mac_prefix_len != 0 && mac_addr_type != VNIC_MAC_ADDR_TYPE_RANDOM && mac_addr_type != VNIC_MAC_ADDR_TYPE_FIXED) usage(); @@ -4778,7 +4907,7 @@ do_create_vnic(int argc, char *argv[], const char *use) status = dladm_vnic_create(handle, name, dev_linkid, mac_addr_type, mac_addr, maclen, &mac_slot, mac_prefix_len, vid, vrid, af, - &linkid, proplist, flags); + &linkid, proplist, &errlist, flags); switch (status) { case DLADM_STATUS_OK: break; @@ -4789,7 +4918,8 @@ do_create_vnic(int argc, char *argv[], const char *use) break; default: - die_dlerr(status, "vnic creation over %s failed", devname); + die_dlerrlist(status, &errlist, "vnic creation over %s failed", + devname); } dladm_free_props(proplist); @@ -4825,9 +4955,10 @@ do_delete_vnic_common(int argc, char *argv[], const char *use, datalink_id_t linkid; char *altroot = NULL; dladm_status_t status; + char *zonename = NULL; opterr = 0; - while ((option = getopt_long(argc, argv, ":R:t", lopts, + while ((option = getopt_long(argc, argv, ":R:tz:", lopts, NULL)) != -1) { switch (option) { case 't': @@ -4836,6 +4967,9 @@ do_delete_vnic_common(int argc, char *argv[], const char *use, case 'R': altroot = optarg; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); } @@ -4848,8 +4982,8 @@ do_delete_vnic_common(int argc, char *argv[], const char *use, if (altroot != NULL) altroot_cmd(altroot, argc, argv); - status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL, - NULL); + status = dladm_zname2info(handle, zonename, argv[optind], &linkid, NULL, + NULL, NULL); if (status != DLADM_STATUS_OK) die("invalid link name '%s'", argv[optind]); @@ -4981,6 +5115,9 @@ print_vnic(show_vnic_state_t *state, datalink_id_t linkid) char vnic_name[MAXLINKNAMELEN]; char mstr[MAXMACADDRLEN * 3]; vnic_fields_buf_t vbuf; + uint_t valcnt = 1; + char zonename[DLADM_PROP_VAL_MAX + 1]; + char *valptr[1]; if ((status = dladm_vnic_info(handle, linkid, vnic, state->vs_flags)) != DLADM_STATUS_OK) @@ -5010,6 +5147,18 @@ print_vnic(show_vnic_state_t *state, datalink_id_t linkid) NULL, devname, sizeof (devname)) != DLADM_STATUS_OK) (void) sprintf(devname, "?"); + + zonename[0] = '\0'; + if (!is_etherstub) { + valptr[0] = zonename; + (void) dladm_get_linkprop(handle, linkid, + DLADM_PROP_VAL_CURRENT, "zone", (char **)valptr, &valcnt); + } + + if (state->vs_zonename != NULL && + strcmp(state->vs_zonename, zonename) != 0) + return (DLADM_STATUS_OK); + state->vs_found = B_TRUE; if (state->vs_stats) { /* print vnic statistics */ @@ -5085,6 +5234,13 @@ print_vnic(show_vnic_state_t *state, datalink_id_t linkid) (void) snprintf(vbuf.vnic_vid, sizeof (vbuf.vnic_vid), "%d", vnic->va_vid); + + if (zonename[0] != '\0') + (void) snprintf(vbuf.vnic_zone, + sizeof (vbuf.vnic_zone), "%s", zonename); + else + (void) strlcpy(vbuf.vnic_zone, "--", + sizeof (vbuf.vnic_zone)); } ofmt_print(state->vs_ofmt, &vbuf); @@ -5123,10 +5279,11 @@ do_show_vnic_common(int argc, char *argv[], const char *use, ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; + char *zonename = NULL; bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":pPl:si:o:", lopts, + while ((option = getopt_long(argc, argv, ":pPl:si:o:z:", lopts, NULL)) != -1) { switch (option) { case 'p': @@ -5165,6 +5322,9 @@ do_show_vnic_common(int argc, char *argv[], const char *use, o_arg = B_TRUE; fields_str = optarg; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); } @@ -5175,8 +5335,8 @@ do_show_vnic_common(int argc, char *argv[], const char *use, /* get vnic ID (optional last argument) */ if (optind == (argc - 1)) { - status = dladm_name2info(handle, argv[optind], &linkid, NULL, - NULL, NULL); + status = dladm_zname2info(handle, zonename, argv[optind], + &linkid, NULL, NULL, NULL); if (status != DLADM_STATUS_OK) { die_dlerr(status, "invalid vnic name '%s'", argv[optind]); @@ -5187,8 +5347,8 @@ do_show_vnic_common(int argc, char *argv[], const char *use, } if (l_arg) { - status = dladm_name2info(handle, state.vs_link, &dev_linkid, - NULL, NULL, NULL); + status = dladm_zname2info(handle, zonename, state.vs_link, + &dev_linkid, NULL, NULL, NULL); if (status != DLADM_STATUS_OK) { die_dlerr(status, "invalid link name '%s'", state.vs_link); @@ -5200,6 +5360,7 @@ do_show_vnic_common(int argc, char *argv[], const char *use, state.vs_etherstub = etherstub; state.vs_found = B_FALSE; state.vs_flags = flags; + state.vs_zonename = zonename; if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0)) { if (etherstub) @@ -5288,7 +5449,7 @@ do_create_etherstub(int argc, char *argv[], const char *use) status = dladm_vnic_create(handle, name, DATALINK_INVALID_LINKID, VNIC_MAC_ADDR_TYPE_AUTO, mac_addr, ETHERADDRL, NULL, 0, 0, - VRRP_VRID_NONE, AF_UNSPEC, NULL, NULL, flags); + VRRP_VRID_NONE, AF_UNSPEC, NULL, NULL, &errlist, flags); if (status != DLADM_STATUS_OK) die_dlerr(status, "etherstub creation failed"); } @@ -6688,6 +6849,7 @@ do_show_linkprop(int argc, char **argv, const char *use) ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; + char *zonename = NULL; bzero(propstr, DLADM_STRSIZE); opterr = 0; @@ -6698,7 +6860,7 @@ do_show_linkprop(int argc, char **argv, const char *use) state.ls_header = B_TRUE; state.ls_retstatus = DLADM_STATUS_OK; - while ((option = getopt_long(argc, argv, ":p:cPo:", + while ((option = getopt_long(argc, argv, ":p:cPo:z:", prop_longopts, NULL)) != -1) { switch (option) { case 'p': @@ -6717,6 +6879,9 @@ do_show_linkprop(int argc, char **argv, const char *use) case 'o': fields_str = optarg; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); break; @@ -6724,8 +6889,8 @@ do_show_linkprop(int argc, char **argv, const char *use) } if (optind == (argc - 1)) { - if ((status = dladm_name2info(handle, argv[optind], &linkid, - NULL, NULL, NULL)) != DLADM_STATUS_OK) { + if ((status = dladm_zname2info(handle, zonename, argv[optind], + &linkid, NULL, NULL, NULL)) != DLADM_STATUS_OK) { die_dlerr(status, "link %s is not valid", argv[optind]); } } else if (optind != argc) { @@ -6736,6 +6901,7 @@ do_show_linkprop(int argc, char **argv, const char *use) != DLADM_STATUS_OK) die("invalid link properties specified"); state.ls_proplist = proplist; + state.ls_zonename = zonename; state.ls_status = DLADM_STATUS_OK; if (state.ls_parsable) @@ -6780,6 +6946,17 @@ show_linkprop_onelink(dladm_handle_t hdl, datalink_id_t linkid, void *arg) return (DLADM_WALK_CONTINUE); } + if (statep->ls_zonename != NULL) { + datalink_id_t tlinkid; + + if (dladm_zname2info(hdl, statep->ls_zonename, statep->ls_link, + &tlinkid, NULL, NULL, NULL) != DLADM_STATUS_OK || + linkid != tlinkid) { + statep->ls_status = DLADM_STATUS_NOTFOUND; + return (DLADM_WALK_CONTINUE); + } + } + if ((statep->ls_persist && !(flags & DLADM_OPT_PERSIST)) || (!statep->ls_persist && !(flags & DLADM_OPT_ACTIVE))) { statep->ls_status = DLADM_STATUS_BADARG; @@ -6862,11 +7039,12 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) dladm_status_t status = DLADM_STATUS_OK; char propstr[DLADM_STRSIZE]; dladm_arg_list_t *proplist = NULL; + char *zonename = NULL; opterr = 0; bzero(propstr, DLADM_STRSIZE); - while ((option = getopt_long(argc, argv, ":p:R:t", + while ((option = getopt_long(argc, argv, ":p:R:tz:", prop_longopts, NULL)) != -1) { switch (option) { case 'p': @@ -6881,6 +7059,9 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) case 'R': altroot = optarg; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, use); @@ -6903,8 +7084,8 @@ set_linkprop(int argc, char **argv, boolean_t reset, const char *use) altroot_cmd(altroot, argc, argv); } - status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL, - NULL); + status = dladm_zname2info(handle, zonename, argv[optind], &linkid, + NULL, NULL, NULL); if (status != DLADM_STATUS_OK) die_dlerr(status, "link %s is not valid", argv[optind]); @@ -8927,6 +9108,21 @@ warn_dlerr(dladm_status_t err, const char *format, ...) (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); } +static void +warn_dlerrlist(dladm_errlist_t *errlist) +{ + if (errlist != NULL && errlist->el_count > 0) { + int i; + for (i = 0; i < errlist->el_count; i++) { + (void) fprintf(stderr, gettext("%s: warning: "), + progname); + + (void) fprintf(stderr, "%s\n", + gettext(errlist->el_errs[i])); + } + } +} + /* * Also closes the dladm handle if it is not NULL. */ @@ -8952,6 +9148,34 @@ die_dlerr(dladm_status_t err, const char *format, ...) exit(EXIT_FAILURE); } +/* + * Like die_dlerr, but uses the errlist for additional information. + */ +/* PRINTFLIKE3 */ +static void +die_dlerrlist(dladm_status_t err, dladm_errlist_t *errlist, + const char *format, ...) +{ + va_list alist; + char errmsg[DLADM_STRSIZE]; + + warn_dlerrlist(errlist); + format = gettext(format); + (void) fprintf(stderr, "%s: ", progname); + + va_start(alist, format); + (void) vfprintf(stderr, format, alist); + va_end(alist); + (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg)); + + /* close dladm handle if it was opened */ + if (handle != NULL) + dladm_close(handle); + + exit(EXIT_FAILURE); + +} + /* PRINTFLIKE1 */ static void die(const char *format, ...) @@ -9659,3 +9883,702 @@ do_up_part(int argc, char *argv[], const char *use) (void) dladm_part_up(handle, partid, 0); } + +static void +do_create_overlay(int argc, char *argv[], const char *use) +{ + int opt; + char *encap = NULL, *endp, *search = NULL; + char name[MAXLINKNAMELEN]; + dladm_status_t status; + uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST; + uint64_t vid; + boolean_t havevid = B_FALSE; + char propstr[DLADM_STRSIZE]; + dladm_arg_list_t *proplist = NULL; + + bzero(propstr, sizeof (propstr)); + while ((opt = getopt_long(argc, argv, ":te:v:p:s:", + overlay_create_lopts, NULL)) != -1) { + switch (opt) { + case 'e': + encap = optarg; + break; + case 's': + search = optarg; + break; + case 't': + flags &= ~DLADM_OPT_PERSIST; + break; + case 'p': + (void) strlcat(propstr, optarg, DLADM_STRSIZE); + if (strlcat(propstr, ",", DLADM_STRSIZE) >= + DLADM_STRSIZE) + die("property list too long '%s'", propstr); + break; + case 'v': + vid = strtoul(optarg, &endp, 10); + if (*endp != '\0' || (vid == 0 && errno == EINVAL)) + die("couldn't parse virtual networkd id: %s", + optarg); + if (vid == ULONG_MAX && errno == ERANGE) + die("virtual networkd id too large: %s", + optarg); + havevid = B_TRUE; + break; + default: + die_opterr(optopt, opt, use); + } + } + + if (havevid == B_FALSE) + die("missing required virtual network id"); + + if (encap == NULL) + die("missing required encapsulation plugin"); + + if (search == NULL) + die("missing required search plugin"); + + if (optind != (argc - 1)) + die("missing device name"); + + if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN) + die("link name too long '%s'", argv[optind]); + + if (!dladm_valid_linkname(name)) + die("invalid link name '%s'", argv[optind]); + + if (strlen(encap) + 1 > MAXLINKNAMELEN) + die("encapsulation plugin name too long '%s'", encap); + + if (strlen(search) + 1 > MAXLINKNAMELEN) + die("search plugin name too long '%s'", encap); + + if (dladm_parse_link_props(propstr, &proplist, B_FALSE) + != DLADM_STATUS_OK) + die("invalid overlay property"); + + status = dladm_overlay_create(handle, name, encap, search, vid, + proplist, &errlist, flags); + dladm_free_props(proplist); + if (status != DLADM_STATUS_OK) { + die_dlerrlist(status, &errlist, "overlay creation failed"); + } +} + +/* ARGSUSED */ +static void +do_delete_overlay(int argc, char *argv[], const char *use) +{ + datalink_id_t linkid = DATALINK_ALL_LINKID; + dladm_status_t status; + + if (argc != 2) { + usage(); + } + + status = dladm_name2info(handle, argv[1], &linkid, NULL, NULL, NULL); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to delete %s", argv[1]); + + status = dladm_overlay_delete(handle, linkid); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to delete %s", argv[1]); +} + +typedef struct showoverlay_state { + ofmt_handle_t sho_ofmt; + const char *sho_linkname; + dladm_overlay_propinfo_handle_t sho_info; + uint8_t sho_value[DLADM_OVERLAY_PROP_SIZEMAX]; + uint32_t sho_size; +} showoverlay_state_t; + +typedef struct showoverlay_fma_state { + ofmt_handle_t shof_ofmt; + const char *shof_linkname; + dladm_overlay_status_t *shof_status; +} showoverlay_fma_state_t; + +typedef struct showoverlay_targ_state { + ofmt_handle_t shot_ofmt; + const char *shot_linkname; + const struct ether_addr *shot_key; + const dladm_overlay_point_t *shot_point; +} showoverlay_targ_state_t; + +static void +print_overlay_value(char *outbuf, uint_t bufsize, uint_t type, const void *pbuf, + const size_t psize) +{ + const struct in6_addr *ipv6; + struct in_addr ip; + + switch (type) { + case OVERLAY_PROP_T_INT: + if (psize != 1 && psize != 2 && psize != 4 && psize != 8) { + (void) snprintf(outbuf, bufsize, "?"); + break; + } + if (psize == 1) + (void) snprintf(outbuf, bufsize, "%d", *(int8_t *)pbuf); + if (psize == 2) + (void) snprintf(outbuf, bufsize, "%d", + *(int16_t *)pbuf); + if (psize == 4) + (void) snprintf(outbuf, bufsize, "%d", + *(int32_t *)pbuf); + if (psize == 8) + (void) snprintf(outbuf, bufsize, "%d", + *(int64_t *)pbuf); + break; + case OVERLAY_PROP_T_UINT: + if (psize != 1 && psize != 2 && psize != 4 && psize != 8) { + (void) snprintf(outbuf, bufsize, "?"); + break; + } + if (psize == 1) + (void) snprintf(outbuf, bufsize, "%d", + *(uint8_t *)pbuf); + if (psize == 2) + (void) snprintf(outbuf, bufsize, "%d", + *(uint16_t *)pbuf); + if (psize == 4) + (void) snprintf(outbuf, bufsize, "%d", + *(uint32_t *)pbuf); + if (psize == 8) + (void) snprintf(outbuf, bufsize, "%d", + *(uint64_t *)pbuf); + break; + case OVERLAY_PROP_T_IP: + if (psize != sizeof (struct in6_addr)) { + warn("malformed overlay IP property: %d bytes\n", + psize); + (void) snprintf(outbuf, bufsize, "--"); + break; + } + + ipv6 = pbuf; + if (IN6_IS_ADDR_V4MAPPED(ipv6)) { + IN6_V4MAPPED_TO_INADDR(ipv6, &ip); + if (inet_ntop(AF_INET, &ip, outbuf, bufsize) == NULL) { + warn("malformed overlay IP property\n"); + (void) snprintf(outbuf, bufsize, "--"); + break; + } + } else { + if (inet_ntop(AF_INET6, ipv6, outbuf, bufsize) == + NULL) { + warn("malformed overlay IP property\n"); + (void) snprintf(outbuf, bufsize, "--"); + break; + } + } + + break; + case OVERLAY_PROP_T_STRING: + (void) snprintf(outbuf, bufsize, "%s", pbuf); + break; + default: + abort(); + } + + return; + +} + +static boolean_t +print_overlay_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) +{ + dladm_status_t status; + showoverlay_state_t *sp = ofarg->ofmt_cbarg; + dladm_overlay_propinfo_handle_t infop = sp->sho_info; + const char *pname; + uint_t type, prot; + const void *def; + uint32_t defsize; + const mac_propval_range_t *rangep; + + if ((status = dladm_overlay_prop_info(infop, &pname, &type, &prot, &def, + &defsize, &rangep)) != DLADM_STATUS_OK) { + warn_dlerr(status, "failed to get get property info"); + return (B_TRUE); + } + + switch (ofarg->ofmt_id) { + case OVERLAY_LINK: + (void) snprintf(buf, bufsize, "%s", sp->sho_linkname); + break; + case OVERLAY_PROPERTY: + (void) snprintf(buf, bufsize, "%s", pname); + break; + case OVERLAY_PERM: + if ((prot & OVERLAY_PROP_PERM_RW) == OVERLAY_PROP_PERM_RW) { + (void) snprintf(buf, bufsize, "%s", "rw"); + } else if ((prot & OVERLAY_PROP_PERM_RW) == + OVERLAY_PROP_PERM_READ) { + (void) snprintf(buf, bufsize, "%s", "r-"); + } else { + (void) snprintf(buf, bufsize, "%s", "--"); + } + break; + case OVERLAY_REQ: + (void) snprintf(buf, bufsize, "%s", + prot & OVERLAY_PROP_PERM_REQ ? "y" : "-"); + break; + case OVERLAY_VALUE: + if (sp->sho_size == 0) { + (void) snprintf(buf, bufsize, "%s", "--"); + } else { + print_overlay_value(buf, bufsize, type, sp->sho_value, + sp->sho_size); + } + break; + case OVERLAY_DEFAULT: + if (defsize == 0) { + (void) snprintf(buf, bufsize, "%s", "--"); + } else { + print_overlay_value(buf, bufsize, type, def, defsize); + } + break; + case OVERLAY_POSSIBLE: { + int i; + char **vals, *ptr, *lim; + if (rangep->mpr_count == 0) { + (void) snprintf(buf, bufsize, "%s", "--"); + break; + } + + vals = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX) * + rangep->mpr_count); + if (vals == NULL) + die("insufficient memory"); + for (i = 0; i < rangep->mpr_count; i++) { + vals[i] = (char *)vals + sizeof (char *) * + rangep->mpr_count + i * DLADM_MAX_PROP_VALCNT; + } + + if (dladm_range2strs(rangep, vals) != 0) { + free(vals); + (void) snprintf(buf, bufsize, "%s", "?"); + break; + } + + ptr = buf; + lim = buf + bufsize; + for (i = 0; i < rangep->mpr_count; i++) { + ptr += snprintf(ptr, lim - ptr, "%s,", vals[i]); + if (ptr >= lim) + break; + } + if (rangep->mpr_count > 0) + buf[strlen(buf) - 1] = '\0'; + free(vals); + break; + } + default: + abort(); + } + return (B_TRUE); +} + +static int +dladm_overlay_show_one(dladm_handle_t handle, datalink_id_t linkid, + dladm_overlay_propinfo_handle_t phdl, void *arg) +{ + showoverlay_state_t *sp = arg; + sp->sho_info = phdl; + + sp->sho_size = sizeof (sp->sho_value); + if (dladm_overlay_get_prop(handle, linkid, phdl, &sp->sho_value, + &sp->sho_size) != DLADM_STATUS_OK) + return (DLADM_WALK_CONTINUE); + + ofmt_print(sp->sho_ofmt, sp); + return (DLADM_WALK_CONTINUE); +} + +static int +show_one_overlay(dladm_handle_t hdl, datalink_id_t linkid, void *arg) +{ + char buf[MAXLINKNAMELEN]; + dladm_status_t info_status; + showoverlay_state_t state; + datalink_class_t class; + show_overlay_request_t *req = arg; + + if ((info_status = dladm_datalink_id2info(hdl, linkid, NULL, &class, + NULL, buf, MAXLINKNAMELEN)) != DLADM_STATUS_OK) { + warn_dlerr(info_status, "failed to get info for " + "datalink id %u", linkid); + req->sor_failed = B_TRUE; + return (DLADM_WALK_CONTINUE); + } + + if (class != DATALINK_CLASS_OVERLAY) { + warn("%s is not an overlay", buf); + req->sor_failed = B_TRUE; + return (DLADM_WALK_CONTINUE); + } + + state.sho_linkname = buf; + state.sho_ofmt = req->sor_ofmt; + + dladm_errlist_reset(&errlist); + (void) dladm_overlay_walk_prop(handle, linkid, dladm_overlay_show_one, + &state, &errlist); + warn_dlerrlist(&errlist); + if (errlist.el_count) { + req->sor_failed = B_TRUE; + } + + return (DLADM_WALK_CONTINUE); +} + +static boolean_t +print_overlay_targ_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) +{ + char keybuf[ETHERADDRSTRL]; + const showoverlay_targ_state_t *shot = ofarg->ofmt_cbarg; + const dladm_overlay_point_t *point = shot->shot_point; + char macbuf[ETHERADDRSTRL]; + char ipbuf[INET6_ADDRSTRLEN]; + custr_t *cus; + + switch (ofarg->ofmt_id) { + case OVERLAY_TARG_LINK: + (void) snprintf(buf, bufsize, shot->shot_linkname); + break; + case OVERLAY_TARG_TARGET: + if ((point->dop_flags & DLADM_OVERLAY_F_DEFAULT) != 0) { + (void) snprintf(buf, bufsize, "*:*:*:*:*:*"); + } else { + if (ether_ntoa_r(shot->shot_key, keybuf) == NULL) { + warn("encountered malformed mac address key\n"); + return (B_FALSE); + } + (void) snprintf(buf, bufsize, "%s", keybuf); + } + break; + case OVERLAY_TARG_DEST: + if (custr_alloc_buf(&cus, buf, bufsize) != 0) { + die("ran out of memory for printing the overlay " + "target destination"); + } + + if (point->dop_dest & OVERLAY_PLUGIN_D_ETHERNET) { + if (ether_ntoa_r(&point->dop_mac, macbuf) == NULL) { + warn("encountered malformed mac address target " + "for key %s\n", keybuf); + return (B_FALSE); + } + (void) custr_append(cus, macbuf); + } + + if (point->dop_dest & OVERLAY_PLUGIN_D_IP) { + if (IN6_IS_ADDR_V4MAPPED(&point->dop_ip)) { + struct in_addr v4; + IN6_V4MAPPED_TO_INADDR(&point->dop_ip, &v4); + if (inet_ntop(AF_INET, &v4, ipbuf, + sizeof (ipbuf)) == NULL) + abort(); + } else if (inet_ntop(AF_INET6, &point->dop_ip, ipbuf, + sizeof (ipbuf)) == NULL) { + /* + * The only failrues we should get are + * EAFNOSUPPORT and ENOSPC because of buffer + * exhaustion. In either of these cases, that + * means something has gone horribly wrong. + */ + abort(); + } + if (point->dop_dest & OVERLAY_PLUGIN_D_ETHERNET) + (void) custr_appendc(cus, ','); + (void) custr_append(cus, ipbuf); + } + + if (point->dop_dest & OVERLAY_PLUGIN_D_PORT) { + if (point->dop_dest & OVERLAY_PLUGIN_D_IP) + (void) custr_appendc(cus, ':'); + else if (point->dop_dest & OVERLAY_PLUGIN_D_ETHERNET) + (void) custr_appendc(cus, ','); + (void) custr_append_printf(cus, "%u", point->dop_port); + } + + custr_free(cus); + + break; + } + return (B_TRUE); +} + +/* ARGSUSED */ +static int +show_one_overlay_table_entry(dladm_handle_t handle, datalink_id_t linkid, + const struct ether_addr *key, const dladm_overlay_point_t *point, void *arg) +{ + showoverlay_targ_state_t *shot = arg; + + shot->shot_key = key; + shot->shot_point = point; + ofmt_print(shot->shot_ofmt, shot); + + return (DLADM_WALK_CONTINUE); +} + +/* ARGSUSED */ +static int +show_one_overlay_table(dladm_handle_t handle, datalink_id_t linkid, void *arg) +{ + char linkbuf[MAXLINKNAMELEN]; + dladm_status_t info_status; + showoverlay_targ_state_t shot; + datalink_class_t class; + show_overlay_request_t *req = arg; + + if ((info_status = dladm_datalink_id2info(handle, linkid, NULL, &class, + NULL, linkbuf, MAXLINKNAMELEN)) != DLADM_STATUS_OK) { + warn_dlerr(info_status, "failed to get info for " + "datalink id %u", linkid); + req->sor_failed = B_TRUE; + return (DLADM_WALK_CONTINUE); + } + + if (class != DATALINK_CLASS_OVERLAY) { + warn("%s is not an overlay", linkbuf); + req->sor_failed = B_TRUE; + return (DLADM_WALK_CONTINUE); + } + + shot.shot_ofmt = req->sor_ofmt; + shot.shot_linkname = linkbuf; + + (void) dladm_overlay_walk_cache(handle, linkid, + show_one_overlay_table_entry, &shot); + + return (DLADM_WALK_CONTINUE); +} + +static boolean_t +print_overlay_fma_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize) +{ + showoverlay_fma_state_t *shof = ofarg->ofmt_cbarg; + dladm_overlay_status_t *st = shof->shof_status; + + switch (ofarg->ofmt_id) { + case OVERLAY_FMA_LINK: + (void) snprintf(buf, bufsize, "%s", shof->shof_linkname); + break; + case OVERLAY_FMA_STATUS: + (void) snprintf(buf, bufsize, st->dos_degraded == B_TRUE ? + "DEGRADED": "ONLINE"); + break; + case OVERLAY_FMA_DETAILS: + (void) snprintf(buf, bufsize, "%s", st->dos_degraded == B_TRUE ? + st->dos_fmamsg : "-"); + break; + default: + abort(); + } + return (B_TRUE); +} + +/* ARGSUSED */ +static void +show_one_overlay_fma_cb(dladm_handle_t handle, datalink_id_t linkid, + dladm_overlay_status_t *stat, void *arg) +{ + showoverlay_fma_state_t *shof = arg; + shof->shof_status = stat; + ofmt_print(shof->shof_ofmt, shof); +} + + +static int +show_one_overlay_fma(dladm_handle_t handle, datalink_id_t linkid, void *arg) +{ + dladm_status_t status; + char linkbuf[MAXLINKNAMELEN]; + datalink_class_t class; + showoverlay_fma_state_t shof; + show_overlay_request_t *req = arg; + + if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, linkbuf, + MAXLINKNAMELEN) != DLADM_STATUS_OK || + class != DATALINK_CLASS_OVERLAY) { + die("datalink %s is not an overlay device\n", linkbuf); + } + + shof.shof_ofmt = req->sor_ofmt; + shof.shof_linkname = linkbuf; + + status = dladm_overlay_status(handle, linkid, + show_one_overlay_fma_cb, &shof); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to obtain device status for %s", + linkbuf); + + return (DLADM_WALK_CONTINUE); +} + +static void +do_show_overlay(int argc, char *argv[], const char *use) +{ + int i, opt; + datalink_id_t linkid = DATALINK_ALL_LINKID; + dladm_status_t status; + int (*funcp)(dladm_handle_t, datalink_id_t, void *); + char *fields_str = NULL; + const ofmt_field_t *fieldsp; + ofmt_status_t oferr; + boolean_t parse; + show_overlay_request_t req; + uint_t ofmtflags; + int err; + + funcp = show_one_overlay; + fieldsp = overlay_fields; + parse = B_FALSE; + req.sor_failed = B_FALSE; + ofmtflags = OFMT_WRAP; + while ((opt = getopt_long(argc, argv, ":o:pft", overlay_show_lopts, + NULL)) != -1) { + switch (opt) { + case 'f': + funcp = show_one_overlay_fma; + fieldsp = overlay_fma_fields; + break; + case 'o': + fields_str = optarg; + break; + case 'p': + parse = B_TRUE; + ofmtflags = OFMT_PARSABLE; + break; + case 't': + funcp = show_one_overlay_table; + fieldsp = overlay_targ_fields; + break; + default: + die_opterr(optopt, opt, use); + } + } + + if (fields_str != NULL && strcasecmp(fields_str, "all") == 0) + fields_str = NULL; + + oferr = ofmt_open(fields_str, fieldsp, ofmtflags, 0, &req.sor_ofmt); + ofmt_check(oferr, parse, req.sor_ofmt, die, warn); + + err = 0; + if (argc > optind) { + for (i = optind; i < argc; i++) { + status = dladm_name2info(handle, argv[i], &linkid, + NULL, NULL, NULL); + if (status != DLADM_STATUS_OK) { + warn_dlerr(status, "failed to find %s", + argv[i]); + err = 1; + continue; + } + (void) funcp(handle, linkid, &req); + } + } else { + (void) dladm_walk_datalink_id(funcp, handle, &req, + DATALINK_CLASS_OVERLAY, DATALINK_ANY_MEDIATYPE, + DLADM_OPT_ACTIVE); + } + if (req.sor_failed) { + err = 1; + } + ofmt_close(req.sor_ofmt); + + exit(err); +} + +static void +do_modify_overlay(int argc, char *argv[], const char *use) +{ + int opt, ocnt = 0; + boolean_t flush, set, delete; + struct ether_addr e; + char *dest; + datalink_id_t linkid = DATALINK_ALL_LINKID; + dladm_status_t status; + + flush = set = delete = B_FALSE; + while ((opt = getopt_long(argc, argv, ":fd:s:", overlay_modify_lopts, + NULL)) != -1) { + switch (opt) { + case 'd': + if (delete == B_TRUE) + die_optdup('d'); + delete = B_TRUE; + ocnt++; + if (ether_aton_r(optarg, &e) == NULL) + die("invalid mac address: %s\n", optarg); + break; + case 'f': + if (flush == B_TRUE) + die_optdup('f'); + flush = B_TRUE; + ocnt++; + break; + case 's': + if (set == B_TRUE) + die_optdup('s'); + set = B_TRUE; + ocnt++; + dest = strchr(optarg, '='); + *dest = '\0'; + dest++; + if (dest == NULL) + die("malformed value, expected mac=dest, " + "got: %s\n", optarg); + if (ether_aton_r(optarg, &e) == NULL) + die("invalid mac address: %s\n", optarg); + break; + default: + die_opterr(optopt, opt, use); + } + } + + if (ocnt == 0) + die("need to specify one of -d, -f, or -s"); + if (ocnt > 1) + die("only one of -d, -f, or -s may be used"); + + if (argv[optind] == NULL) + die("missing required overlay device\n"); + if (argc > optind + 1) + die("only one overlay device may be specified\n"); + + status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL, + NULL); + if (status != DLADM_STATUS_OK) { + die_dlerr(status, "failed to find overlay %s", argv[optind]); + } + + if (flush == B_TRUE) { + status = dladm_overlay_cache_flush(handle, linkid); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to flush target cache for " + "overlay %s", argv[optind]); + } + + if (delete == B_TRUE) { + status = dladm_overlay_cache_delete(handle, linkid, &e); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to flush target %s from " + "overlay target cache %s", optarg, argv[optind]); + } + + if (set == B_TRUE) { + status = dladm_overlay_cache_set(handle, linkid, &e, dest); + if (status != DLADM_STATUS_OK) + die_dlerr(status, "failed to set target %s for overlay " + "target cache %s", optarg, argv[optind]); + } + +} diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_db.c b/usr/src/cmd/dlmgmtd/dlmgmt_db.c index 99307dbc03..6ccd9d97b8 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_db.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_db.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2016 Joyent, Inc. */ #include <assert.h> @@ -43,6 +44,7 @@ #include <libcontract.h> #include <libcontract_priv.h> #include <sys/contract/process.h> +#include <zone.h> #include "dlmgmt_impl.h" typedef enum dlmgmt_db_op { @@ -552,6 +554,10 @@ dlmgmt_db_update(dlmgmt_db_op_t op, const char *entryname, dlmgmt_link_t *linkp, linkp->ll_zoneid, flags, &err)) == NULL) return (err); + /* If transient op and onloan, use the global zone cache file. */ + if (flags == DLMGMT_ACTIVE && linkp->ll_onloan) + req->ls_zoneid = GLOBAL_ZONEID; + /* * If the return error is EINPROGRESS, this request is handled * asynchronously; return success. @@ -714,11 +720,13 @@ parse_linkprops(char *buf, dlmgmt_link_t *linkp) char attr_name[MAXLINKATTRLEN]; size_t attr_buf_len = 0; void *attr_buf = NULL; + boolean_t rename; curr = buf; len = strlen(buf); attr_name[0] = '\0'; for (i = 0; i < len; i++) { + rename = B_FALSE; char c = buf[i]; boolean_t match = (c == '=' || (c == ',' && !found_type) || c == ';'); @@ -768,6 +776,21 @@ parse_linkprops(char *buf, dlmgmt_link_t *linkp) goto parse_fail; linkp->ll_media = (uint32_t)*(int64_t *)attr_buf; + } else if (strcmp(attr_name, "zone") == 0) { + if (read_str(curr, &attr_buf) == 0) + goto parse_fail; + linkp->ll_zoneid = getzoneidbyname(attr_buf); + if (linkp->ll_zoneid == -1) { + if (errno == EFAULT) + abort(); + /* + * If we can't find the zone, assign the + * link to the GZ and mark it for being + * renamed. + */ + linkp->ll_zoneid = 0; + rename = B_TRUE; + } } else { attr_buf_len = translators[type].read_func(curr, &attr_buf); @@ -811,6 +834,16 @@ parse_linkprops(char *buf, dlmgmt_link_t *linkp) (void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr); } + + /* + * The zone that this link belongs to has died, we are + * reparenting it to the GZ and renaming it to avoid name + * collisions. + */ + if (rename == B_TRUE) { + (void) snprintf(linkp->ll_link, MAXLINKNAMELEN, + "SUNWorphan%u", (uint16_t)(gethrtime() / 1000)); + } curr = buf + i + 1; } @@ -1222,12 +1255,19 @@ generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf) ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link); if (!persist) { + char zname[ZONENAME_MAX]; /* - * We store the linkid in the active database so that dlmgmtd - * can recover in the event that it is restarted. + * We store the linkid and the zone name in the active database + * so that dlmgmtd can recover in the event that it is + * restarted. */ u64 = linkp->ll_linkid; ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64); + + if (getzonenamebyid(linkp->ll_zoneid, zname, + sizeof (zname)) != -1) { + ptr += write_str(ptr, BUFLEN(lim, ptr), "zone", zname); + } } u64 = linkp->ll_class; ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64); @@ -1382,38 +1422,88 @@ dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func) } /* + * Attempt to mitigate one of the deadlocks in the dlmgmtd architecture. + * + * dlmgmt_db_init() calls dlmgmt_process_db_req() which eventually gets to + * dlmgmt_zfop() which tries to fork, enter the zone and read the file. + * Because of the upcall architecture of dlmgmtd this can lead to deadlock + * with the following scenario: + * a) the thread preparing to fork will have acquired the malloc locks + * then attempt to suspend every thread in preparation to fork. + * b) all of the upcalls will be blocked in door_ucred() trying to malloc() + * and get the credentials of their caller. + * c) we can't suspend the in-kernel thread making the upcall. + * + * Thus, we cannot serve door requests because we're blocked in malloc() + * which fork() owns, but fork() is in turn blocked on the in-kernel thread + * making the door upcall. This is a fundamental architectural problem with + * any server handling upcalls and also trying to fork(). + * + * To minimize the chance of this deadlock occuring, we check ahead of time to + * see if the file we want to read actually exists in the zone (which it almost + * never does), so we don't need fork in that case (i.e. rarely to never). + */ +static boolean_t +zone_file_exists(char *zoneroot, char *filename) +{ + struct stat sb; + char fname[MAXPATHLEN]; + + (void) snprintf(fname, sizeof (fname), "%s/%s", zoneroot, filename); + + if (stat(fname, &sb) == -1) + return (B_FALSE); + + return (B_TRUE); +} + +/* * Initialize the datalink <link name, linkid> mapping and the link's * attributes list based on the configuration file /etc/dladm/datalink.conf * and the active configuration cache file * /etc/svc/volatile/dladm/datalink-management:default.cache. */ int -dlmgmt_db_init(zoneid_t zoneid) +dlmgmt_db_init(zoneid_t zoneid, char *zoneroot) { dlmgmt_db_req_t *req; int err; boolean_t boot = B_FALSE; + char tdir[MAXPATHLEN]; + char *path = cachefile; if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL, DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL) return (err); - if ((err = dlmgmt_process_db_req(req)) != 0) { - /* - * If we get back ENOENT, that means that the active - * configuration file doesn't exist yet, and is not an error. - * We'll create it down below after we've loaded the - * persistent configuration. - */ - if (err != ENOENT) - goto done; + /* Handle running in a non-native branded zone (i.e. has /native) */ + if (zone_file_exists(zoneroot, "/native" DLMGMT_TMPFS_DIR)) { + (void) snprintf(tdir, sizeof (tdir), "/native%s", cachefile); + path = tdir; + } + + if (zone_file_exists(zoneroot, path)) { + if ((err = dlmgmt_process_db_req(req)) != 0) { + /* + * If we get back ENOENT, that means that the active + * configuration file doesn't exist yet, and is not an + * error. We'll create it down below after we've + * loaded the persistent configuration. + */ + if (err != ENOENT) + goto done; + boot = B_TRUE; + } + } else { boot = B_TRUE; } - req->ls_flags = DLMGMT_PERSIST; - err = dlmgmt_process_db_req(req); - if (err != 0 && err != ENOENT) - goto done; + if (zone_file_exists(zoneroot, DLMGMT_PERSISTENT_DB_PATH)) { + req->ls_flags = DLMGMT_PERSIST; + err = dlmgmt_process_db_req(req); + if (err != 0 && err != ENOENT) + goto done; + } err = 0; if (rewrite_needed) { /* diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_door.c b/usr/src/cmd/dlmgmtd/dlmgmt_door.c index 11e4329669..60fa361caa 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_door.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_door.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2019 Joyent, Inc. */ /* @@ -58,6 +59,7 @@ #include <libsysevent.h> #include <libdlmgmt.h> #include <librcm.h> +#include <unistd.h> #include "dlmgmt_impl.h" typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t, @@ -423,8 +425,11 @@ dlmgmt_getname(void *argp, void *retp, size_t *sz, zoneid_t zoneid, retvalp->lr_flags = linkp->ll_flags; retvalp->lr_class = linkp->ll_class; retvalp->lr_media = linkp->ll_media; + retvalp->lr_flags |= (linkp->ll_trans == B_TRUE) ? + DLMGMT_TRANSIENT : 0; } + dlmgmt_table_unlock(); retvalp->lr_err = err; } @@ -439,6 +444,10 @@ dlmgmt_getlinkid(void *argp, void *retp, size_t *sz, zoneid_t zoneid, dlmgmt_link_t *linkp; int err = 0; + /* Enable the global zone to lookup links it has given away. */ + if (zoneid == GLOBAL_ZONEID && getlinkid->ld_zoneid != -1) + zoneid = getlinkid->ld_zoneid; + /* * Hold the reader lock to access the link */ @@ -648,6 +657,7 @@ dlmgmt_remapid(void *argp, void *retp, size_t *sz, zoneid_t zoneid, if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0) goto done; + if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) { err = EEXIST; goto done; @@ -1232,8 +1242,7 @@ dlmgmt_setzoneid(void *argp, void *retp, size_t *sz, zoneid_t zoneid, * Before we remove the link from its current zone, make sure that * there isn't a link with the same name in the destination zone. */ - if (zoneid != GLOBAL_ZONEID && - link_by_name(linkp->ll_link, newzoneid) != NULL) { + if (link_by_name(linkp->ll_link, newzoneid) != NULL) { err = EEXIST; goto done; } @@ -1245,9 +1254,10 @@ dlmgmt_setzoneid(void *argp, void *retp, size_t *sz, zoneid_t zoneid, "zone %d: %s", linkid, oldzoneid, strerror(err)); goto done; } - avl_remove(&dlmgmt_loan_avl, linkp); + linkp->ll_onloan = B_FALSE; } + if (newzoneid != GLOBAL_ZONEID) { if (zone_add_datalink(newzoneid, linkid) != 0) { err = errno; @@ -1256,7 +1266,6 @@ dlmgmt_setzoneid(void *argp, void *retp, size_t *sz, zoneid_t zoneid, (void) zone_add_datalink(oldzoneid, linkid); goto done; } - avl_add(&dlmgmt_loan_avl, linkp); linkp->ll_onloan = B_TRUE; } @@ -1309,6 +1318,10 @@ dlmgmt_zonehalt(void *argp, void *retp, size_t *sz, zoneid_t zoneid, int err = 0; dlmgmt_door_zonehalt_t *zonehalt = argp; dlmgmt_zonehalt_retval_t *retvalp = retp; + static char my_pid[10]; + + if (my_pid[0] == '\0') + (void) snprintf(my_pid, sizeof (my_pid), "%d\n", getpid()); if ((err = dlmgmt_checkprivs(0, cred)) == 0) { if (zoneid != GLOBAL_ZONEID) { diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_impl.h b/usr/src/cmd/dlmgmtd/dlmgmt_impl.h index cdfd0d8a4d..c65a0438d6 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_impl.h +++ b/usr/src/cmd/dlmgmtd/dlmgmt_impl.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2017 Joyent, Inc. */ /* @@ -60,13 +61,24 @@ typedef struct dlmgmt_link_s { datalink_class_t ll_class; uint32_t ll_media; datalink_id_t ll_linkid; + + /* + * The zone that owns the link. If this is set to the id of + * an NGZ and ll_onloan is set then the link was created and + * is owned by the GZ but is currently being loaned out to an + * NGZ. E.g., when the GZ admin creates a VNIC for exclusive + * use by an NGZ. If ll_onloan is set then ll_zoneid cannot be 0. + * + * If ll_zoneid is set to the id of an NGZ but ll_onloan is + * not set then the link was created and is owned by the NGZ. + */ zoneid_t ll_zoneid; boolean_t ll_onloan; avl_node_t ll_name_node; avl_node_t ll_id_node; - avl_node_t ll_loan_node; uint32_t ll_flags; uint32_t ll_gen; /* generation number */ + boolean_t ll_trans; /* transient link */ } dlmgmt_link_t; /* @@ -91,7 +103,6 @@ extern dladm_handle_t dld_handle; extern datalink_id_t dlmgmt_nextlinkid; extern avl_tree_t dlmgmt_name_avl; extern avl_tree_t dlmgmt_id_avl; -extern avl_tree_t dlmgmt_loan_avl; extern avl_tree_t dlmgmt_dlconf_avl; boolean_t linkattr_equal(dlmgmt_linkattr_t **, const char *, void *, @@ -138,7 +149,7 @@ void dlmgmt_handler(void *, char *, size_t, door_desc_t *, uint_t); void dlmgmt_log(int, const char *, ...); int dlmgmt_write_db_entry(const char *, dlmgmt_link_t *, uint32_t); int dlmgmt_delete_db_entry(dlmgmt_link_t *, uint32_t); -int dlmgmt_db_init(zoneid_t); +int dlmgmt_db_init(zoneid_t, char *); void dlmgmt_db_fini(zoneid_t); #ifdef __cplusplus diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_main.c b/usr/src/cmd/dlmgmtd/dlmgmt_main.c index c02610bb5f..60466fd773 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_main.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_main.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2016 Joyent, Inc. */ /* @@ -125,15 +126,24 @@ dlmgmt_door_fini(void) dlmgmt_door_fd = -1; } -int +static int dlmgmt_door_attach(zoneid_t zoneid, char *rootdir) { int fd; int err = 0; char doorpath[MAXPATHLEN]; + struct stat statbuf; - (void) snprintf(doorpath, sizeof (doorpath), "%s%s", rootdir, - DLMGMT_DOOR); + /* Handle running in a non-native branded zone (i.e. has /native) */ + (void) snprintf(doorpath, sizeof (doorpath), "%s/native%s", + rootdir, DLMGMT_TMPFS_DIR); + if (stat(doorpath, &statbuf) == 0) { + (void) snprintf(doorpath, sizeof (doorpath), "%s/native%s", + rootdir, DLMGMT_DOOR); + } else { + (void) snprintf(doorpath, sizeof (doorpath), "%s%s", + rootdir, DLMGMT_DOOR); + } /* * Create the door file for dlmgmtd. @@ -192,8 +202,16 @@ dlmgmt_zone_init(zoneid_t zoneid) (void) snprintf(tmpfsdir, sizeof (tmpfsdir), "%s%s", rootdir, DLMGMT_TMPFS_DIR); if (stat(tmpfsdir, &statbuf) < 0) { - if (mkdir(tmpfsdir, (mode_t)0755) < 0) - return (errno); + if (mkdir(tmpfsdir, (mode_t)0755) < 0) { + /* + * Handle running in a non-native branded zone + * (i.e. has /native) + */ + (void) snprintf(tmpfsdir, sizeof (tmpfsdir), + "%s/native%s", rootdir, DLMGMT_TMPFS_DIR); + if (mkdir(tmpfsdir, (mode_t)0755) < 0) + return (errno); + } } else if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { return (ENOTDIR); } @@ -203,7 +221,7 @@ dlmgmt_zone_init(zoneid_t zoneid) return (EPERM); } - if ((err = dlmgmt_db_init(zoneid)) != 0) + if ((err = dlmgmt_db_init(zoneid, rootdir)) != 0) return (err); return (dlmgmt_door_attach(zoneid, rootdir)); } @@ -214,7 +232,7 @@ dlmgmt_zone_init(zoneid_t zoneid) static int dlmgmt_allzones_init(void) { - int err, i; + int i; zoneid_t *zids = NULL; uint_t nzids, nzids_saved; @@ -235,11 +253,37 @@ again: } for (i = 0; i < nzids; i++) { - if ((err = dlmgmt_zone_init(zids[i])) != 0) - break; + int res; + zone_status_t status; + + /* + * Skip over zones that have gone away or are going down + * since we got the list. Process all zones in the list, + * logging errors for any that failed. + */ + if (zone_getattr(zids[i], ZONE_ATTR_STATUS, &status, + sizeof (status)) < 0) + continue; + switch (status) { + case ZONE_IS_SHUTTING_DOWN: + case ZONE_IS_EMPTY: + case ZONE_IS_DOWN: + case ZONE_IS_DYING: + case ZONE_IS_DEAD: + /* FALLTHRU */ + continue; + default: + break; + } + if ((res = dlmgmt_zone_init(zids[i])) != 0) { + (void) fprintf(stderr, "zone (%ld) init error %s", + zids[i], strerror(res)); + dlmgmt_log(LOG_ERR, "zone (%d) init error %s", + zids[i], strerror(res)); + } } free(zids); - return (err); + return (0); } static int diff --git a/usr/src/cmd/dlmgmtd/dlmgmt_util.c b/usr/src/cmd/dlmgmtd/dlmgmt_util.c index afcfbed37b..c8ba0009a0 100644 --- a/usr/src/cmd/dlmgmtd/dlmgmt_util.c +++ b/usr/src/cmd/dlmgmtd/dlmgmt_util.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2017 Joyent, Inc. */ /* @@ -45,13 +46,10 @@ /* * There are three datalink AVL tables. The dlmgmt_name_avl tree contains all * datalinks and is keyed by zoneid and link name. The dlmgmt_id_avl also - * contains all datalinks, and it is keyed by link ID. The dlmgmt_loan_avl is - * keyed by link name, and contains the set of global-zone links that are - * currently on loan to non-global zones. + * contains all datalinks, and it is keyed by link ID. */ avl_tree_t dlmgmt_name_avl; avl_tree_t dlmgmt_id_avl; -avl_tree_t dlmgmt_loan_avl; avl_tree_t dlmgmt_dlconf_avl; @@ -162,8 +160,6 @@ dlmgmt_linktable_init(void) offsetof(dlmgmt_link_t, ll_name_node)); avl_create(&dlmgmt_id_avl, cmp_link_by_id, sizeof (dlmgmt_link_t), offsetof(dlmgmt_link_t, ll_id_node)); - avl_create(&dlmgmt_loan_avl, cmp_link_by_name, sizeof (dlmgmt_link_t), - offsetof(dlmgmt_link_t, ll_loan_node)); avl_create(&dlmgmt_dlconf_avl, cmp_dlconf_by_id, sizeof (dlmgmt_dlconf_t), offsetof(dlmgmt_dlconf_t, ld_node)); dlmgmt_nextlinkid = 1; @@ -181,7 +177,6 @@ dlmgmt_linktable_fini(void) avl_destroy(&dlmgmt_dlconf_avl); avl_destroy(&dlmgmt_name_avl); - avl_destroy(&dlmgmt_loan_avl); avl_destroy(&dlmgmt_id_avl); } @@ -359,9 +354,9 @@ link_destroy(dlmgmt_link_t *linkp) } /* - * Set the DLMGMT_ACTIVE flag on the link to note that it is active. When a - * link becomes active and it belongs to a non-global zone, it is also added - * to that zone. + * Set the DLMGMT_ACTIVE flag on the link to note that it is active. + * When a link is active and is owned by an NGZ then it is added to + * that zone's datalink list. */ int link_activate(dlmgmt_link_t *linkp) @@ -369,27 +364,75 @@ link_activate(dlmgmt_link_t *linkp) int err = 0; zoneid_t zoneid = ALL_ZONES; + /* + * If zone_check_datalink() returns 0 it means we found the + * link in one of the NGZ's datalink lists. Otherwise the link + * is under the GZ. + */ if (zone_check_datalink(&zoneid, linkp->ll_linkid) == 0) { /* - * This link was already added to a non-global zone. This can - * happen if dlmgmtd is restarted. + * This is a bit subtle. If the following expression + * is true then the link was found in one of the NGZ's + * datalink lists but the link structure has it under + * the GZ. This means that the link is supposed to be + * loaned out to an NGZ but the dlmgmtd state is out + * of sync -- possibly due to the process restarting. + * In this case we need to sync the dlmgmtd state by + * marking it as on-loan to the NGZ it's currently + * under. */ if (zoneid != linkp->ll_zoneid) { + assert(linkp->ll_zoneid == 0); + assert(linkp->ll_onloan == B_FALSE); + + /* + * If dlmgmtd already has a link with this + * name under the NGZ then we have a problem. + */ if (link_by_name(linkp->ll_link, zoneid) != NULL) { err = EEXIST; goto done; } + /* + * Remove the current linkp entry from the + * list because it's under the wrong zoneid. + * We don't have to update the dlmgmt_id_avl + * because it compares entries by ll_linkid + * only. + */ if (avl_find(&dlmgmt_name_avl, linkp, NULL) != NULL) avl_remove(&dlmgmt_name_avl, linkp); + /* + * Update the link to reflect the fact that + * it's on-loan to an NGZ and re-add it to the + * list. + */ linkp->ll_zoneid = zoneid; avl_add(&dlmgmt_name_avl, linkp); - avl_add(&dlmgmt_loan_avl, linkp); linkp->ll_onloan = B_TRUE; + + /* + * When a VNIC is not persistent and loaned to + * a zone it is considered transient. This is + * the same logic found in do_create_vnic() + * and is needed here in the event of a + * dlmgmtd restart. + */ + if (linkp->ll_class == DATALINK_CLASS_VNIC && + !(linkp->ll_flags & DLMGMT_PERSIST)) + linkp->ll_trans = B_TRUE; } } else if (linkp->ll_zoneid != GLOBAL_ZONEID) { + /* + * In this case the link was not found under any NGZs + * but according to its ll_zoneid member it is owned + * by an NGZ. Add the datalink to the appropriate zone + * datalink list. + */ err = zone_add_datalink(linkp->ll_zoneid, linkp->ll_linkid); + assert(linkp->ll_onloan == B_FALSE); } done: if (err == 0) @@ -430,10 +473,6 @@ link_by_name(const char *name, zoneid_t zoneid) (void) strlcpy(link.ll_link, name, MAXLINKNAMELEN); link.ll_zoneid = zoneid; linkp = avl_find(&dlmgmt_name_avl, &link, NULL); - if (linkp == NULL && zoneid == GLOBAL_ZONEID) { - /* The link could be on loan to a non-global zone? */ - linkp = avl_find(&dlmgmt_loan_avl, &link, NULL); - } return (linkp); } @@ -449,6 +488,10 @@ dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media, return (EINVAL); if (dlmgmt_nextlinkid == DATALINK_INVALID_LINKID) return (ENOSPC); + if (flags & ~(DLMGMT_ACTIVE | DLMGMT_PERSIST | DLMGMT_TRANSIENT) || + ((flags & DLMGMT_PERSIST) && (flags & DLMGMT_TRANSIENT)) || + flags == 0) + return (EINVAL); if ((linkp = calloc(1, sizeof (dlmgmt_link_t))) == NULL) { err = ENOMEM; @@ -462,6 +505,15 @@ dlmgmt_create_common(const char *name, datalink_class_t class, uint32_t media, linkp->ll_zoneid = zoneid; linkp->ll_gen = 0; + /* + * While DLMGMT_TRANSIENT starts off as a flag it is converted + * into a link field since it is really a substate of + * DLMGMT_ACTIVE -- it should not survive as a flag beyond + * this point. + */ + linkp->ll_trans = (flags & DLMGMT_TRANSIENT) ? B_TRUE : B_FALSE; + flags &= ~DLMGMT_TRANSIENT; + if (avl_find(&dlmgmt_name_avl, linkp, &name_where) != NULL || avl_find(&dlmgmt_id_avl, linkp, &id_where) != NULL) { err = EEXIST; @@ -490,6 +542,12 @@ done: int dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags) { + /* + * After dlmgmt_create_common() the link flags should only + * ever include ACTIVE or PERSIST. + */ + assert((linkp->ll_flags & ~(DLMGMT_ACTIVE | DLMGMT_PERSIST)) == 0); + if ((linkp->ll_flags & flags) == 0) { /* * The link does not exist in the specified space. @@ -511,8 +569,6 @@ dlmgmt_destroy_common(dlmgmt_link_t *linkp, uint32_t flags) if ((flags & DLMGMT_ACTIVE) && linkp->ll_zoneid != GLOBAL_ZONEID) { (void) zone_remove_datalink(linkp->ll_zoneid, linkp->ll_linkid); - if (linkp->ll_onloan) - avl_remove(&dlmgmt_loan_avl, linkp); } if (linkp->ll_flags == 0) { diff --git a/usr/src/cmd/dlstat/dlstat.c b/usr/src/cmd/dlstat/dlstat.c index 915c2196d1..d11e623206 100644 --- a/usr/src/cmd/dlstat/dlstat.c +++ b/usr/src/cmd/dlstat/dlstat.c @@ -70,7 +70,7 @@ typedef struct link_chain_s { struct link_chain_s *lc_next; } link_chain_t; -typedef void * (*stats2str_t)(const char *, void *, +typedef void * (*stats2str_t)(const char *, const char *, void *, char, boolean_t); typedef struct show_state { @@ -147,6 +147,7 @@ typedef struct total_fields_buf_s { char t_rbytes[MAXSTATLEN]; char t_opackets[MAXSTATLEN]; char t_obytes[MAXSTATLEN]; + char t_zone[ZONENAME_MAX]; } total_fields_buf_t; static ofmt_field_t total_s_fields[] = { @@ -160,6 +161,8 @@ static ofmt_field_t total_s_fields[] = { offsetof(total_fields_buf_t, t_opackets), print_default_cb}, { "OBYTES", 8, offsetof(total_fields_buf_t, t_obytes), print_default_cb}, +{ "ZONE", 20, + offsetof(total_fields_buf_t, t_zone), print_default_cb}, { NULL, 0, 0, NULL}}; /* @@ -963,8 +966,8 @@ cleanup_removed_links(show_state_t *state) } void * -print_total_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_total_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { total_stat_entry_t *sentry = statentry; total_stat_t *link_stats = &sentry->tse_stats; @@ -976,6 +979,7 @@ print_total_stats(const char *linkname, void *statentry, char unit, (void) snprintf(buf->t_linkname, sizeof (buf->t_linkname), "%s", linkname); + (void) snprintf(buf->t_zone, sizeof (buf->t_zone), "%s", zonename); map_to_units(buf->t_ipackets, sizeof (buf->t_ipackets), link_stats->ts_ipackets, unit, parsable); @@ -994,8 +998,8 @@ done: } void * -print_rx_generic_ring_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_rx_generic_ring_stats(const char *linkname, const char *zonename, + void *statentry, char unit, boolean_t parsable) { ring_stat_entry_t *sentry = statentry; ring_stat_t *link_stats = &sentry->re_stats; @@ -1028,8 +1032,8 @@ done: } void * -print_tx_generic_ring_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_tx_generic_ring_stats(const char *linkname, const char *zonename, + void *statentry, char unit, boolean_t parsable) { ring_stat_entry_t *sentry = statentry; ring_stat_t *link_stats = &sentry->re_stats; @@ -1062,8 +1066,8 @@ done: } void * -print_rx_ring_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_rx_ring_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { ring_stat_entry_t *sentry = statentry; ring_stat_t *link_stats = &sentry->re_stats; @@ -1096,8 +1100,8 @@ done: } void * -print_tx_ring_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_tx_ring_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { ring_stat_entry_t *sentry = statentry; ring_stat_t *link_stats = &sentry->re_stats; @@ -1130,8 +1134,8 @@ done: } void * -print_rx_generic_lane_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_rx_generic_lane_stats(const char *linkname, const char *zonename, + void *statentry, char unit, boolean_t parsable) { rx_lane_stat_entry_t *sentry = statentry; rx_lane_stat_t *link_stats = &sentry->rle_stats; @@ -1178,8 +1182,8 @@ done: } void * -print_tx_generic_lane_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_tx_generic_lane_stats(const char *linkname, const char *zonename, + void *statentry, char unit, boolean_t parsable) { tx_lane_stat_entry_t *sentry = statentry; tx_lane_stat_t *link_stats = &sentry->tle_stats; @@ -1223,8 +1227,8 @@ done: } void * -print_rx_lane_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_rx_lane_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { rx_lane_stat_entry_t *sentry = statentry; rx_lane_stat_t *link_stats = &sentry->rle_stats; @@ -1289,8 +1293,8 @@ done: } void * -print_tx_lane_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_tx_lane_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { tx_lane_stat_entry_t *sentry = statentry; tx_lane_stat_t *link_stats = &sentry->tle_stats; @@ -1344,8 +1348,8 @@ done: } void * -print_fanout_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_fanout_stats(const char *linkname, const char *zonename, void *statentry, + char unit, boolean_t parsable) { fanout_stat_entry_t *sentry = statentry; fanout_stat_t *link_stats = &sentry->fe_stats; @@ -1398,8 +1402,8 @@ done: } void * -print_aggr_port_stats(const char *linkname, void *statentry, char unit, - boolean_t parsable) +print_aggr_port_stats(const char *linkname, const char *zonename, + void *statentry, char unit, boolean_t parsable) { aggr_port_stat_entry_t *sentry = statentry; aggr_port_stat_t *link_stats = &sentry->ape_stats; @@ -1476,7 +1480,8 @@ done: void walk_dlstat_stats(show_state_t *state, const char *linkname, - dladm_stat_type_t stattype, dladm_stat_chain_t *diff_stat) + const char *zonename, dladm_stat_type_t stattype, + dladm_stat_chain_t *diff_stat) { dladm_stat_chain_t *curr; @@ -1486,7 +1491,8 @@ walk_dlstat_stats(show_state_t *state, const char *linkname, /* Format the raw numbers for printing */ fields_buf = state->ls_stats2str[stattype](linkname, - curr->dc_statentry, state->ls_unit, state->ls_parsable); + zonename, curr->dc_statentry, state->ls_unit, + state->ls_parsable); /* Print the stats */ if (fields_buf != NULL) ofmt_print(state->ls_ofmt, fields_buf); @@ -1501,12 +1507,20 @@ show_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg) int i; dladm_stat_chain_t *diff_stat; char linkname[DLPI_LINKNAME_MAX]; + char zonename[DLADM_PROP_VAL_MAX + 1]; + char *valptr[1]; + uint_t valcnt = 1; if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname, DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) { goto done; } + valptr[0] = zonename; + if (dladm_get_linkprop(handle, linkid, DLADM_PROP_VAL_CURRENT, "zone", + (char **)valptr, &valcnt) != 0) + zonename[0] = '\0'; + for (i = 0; i < DLADM_STAT_NUM_STATS; i++) { if (state->ls_stattype[i]) { /* @@ -1514,7 +1528,8 @@ show_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg) * Stats are returned as chain of raw numbers */ diff_stat = query_link_stats(handle, linkid, arg, i); - walk_dlstat_stats(state, linkname, i, diff_stat); + walk_dlstat_stats(state, linkname, zonename, i, + diff_stat); dladm_link_stat_free(diff_stat); } } @@ -1634,7 +1649,7 @@ do_show(int argc, char *argv[], const char *use) char *o_fields_str = NULL; char *total_stat_fields = - "link,ipkts,rbytes,opkts,obytes"; + "link,ipkts,rbytes,opkts,obytes,zone"; char *rx_total_stat_fields = "link,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50"; char *tx_total_stat_fields = diff --git a/usr/src/cmd/dtrace/demo/Makefile b/usr/src/cmd/dtrace/demo/Makefile index a75a418a96..cfb35083ee 100644 --- a/usr/src/cmd/dtrace/demo/Makefile +++ b/usr/src/cmd/dtrace/demo/Makefile @@ -21,6 +21,8 @@ # # Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. # +# Copyright (c) 2018, Joyent, Inc. +# include ../../Makefile.cmd @@ -157,11 +159,11 @@ $(ROOTDEMODIR): $(ROOTDEMODIR)/%: % $(INS.file) -$(HTMLFILES): $(DFILES) $(MKDEMO) - ./$(MKDEMO) $@ +$(HTMLFILES): $(DFILES) $(MKDEMO) + $(PERL) ./$(MKDEMO) $@ -$(DFILES): $(MKDEMO) - ./$(MKDEMO) $@ +$(DFILES): $(MKDEMO) + $(PERL) ./$(MKDEMO) $@ $(ROOTDEMOFILES): $(ROOTDEMODIR) diff --git a/usr/src/cmd/dtrace/test/README b/usr/src/cmd/dtrace/test/README index 51ab650fd7..094f70e7da 100644 --- a/usr/src/cmd/dtrace/test/README +++ b/usr/src/cmd/dtrace/test/README @@ -20,13 +20,10 @@ CDDL HEADER END Copyright 2006 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. - -ident "%Z%%M% %I% %E% SMI" +Copyright 2017 Joyent, Inc. DTrace Testing Suite The SUNWdtrt package delivers a set of test programs and D source -files into the directory /opt/SUNWdtrt. For more information see -the following web site: - - http://www.opensolaris.org/os/community/dtrace/dtest +files into the directory /opt/SUNWdtrt. To run the tests: + /opt/SUNWdtrt/bin/dtest diff --git a/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile b/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile index 66479e77bc..ee93b7a258 100644 --- a/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile +++ b/usr/src/cmd/dtrace/test/cmd/jdtrace/Makefile @@ -75,7 +75,7 @@ $(PROG): $(SRCS) $(POST_PROCESS) ; $(STRIP_STABS) JFLAGS= -g -cp $(CLASSPATH) -d $(CLASSDIR) -JFLAGS += -source 1.5 -target 1.6 -Xlint:all,-options +JFLAGS += -source 1.5 -target 1.6 -Xlint:all,-options,-path COMPILE.java=$(JAVAC) $(JFLAGS) JAVASRC= JDTrace.java Getopt.java diff --git a/usr/src/cmd/dtrace/test/cmd/scripts/dtest.pl b/usr/src/cmd/dtrace/test/cmd/scripts/dtest.pl index d6f1c8c277..e7f9189822 100644 --- a/usr/src/cmd/dtrace/test/cmd/scripts/dtest.pl +++ b/usr/src/cmd/dtrace/test/cmd/scripts/dtest.pl @@ -566,7 +566,7 @@ $defdir = -d $dt_tst ? $dt_tst : '.'; $bindir = -d $dt_bin ? $dt_bin : '.'; if (!$opt_F) { - my @dependencies = ("gcc", "make", "java", "perl"); + my @dependencies = ("gcc", "cc", "make", "java", "perl", "printenv"); for my $dep (@dependencies) { if (!inpath($dep)) { diff --git a/usr/src/cmd/dtrace/test/tst/Makefile.com b/usr/src/cmd/dtrace/test/tst/Makefile.com index 0d8fb3316c..b7100f4af5 100644 --- a/usr/src/cmd/dtrace/test/tst/Makefile.com +++ b/usr/src/cmd/dtrace/test/tst/Makefile.com @@ -113,6 +113,6 @@ scripts: FRC @cd ../cmd/scripts; pwd; $(MAKE) install dstyle: FRC - @if [ -n "$(DSRCS)" ]; then $(DSTYLE) $(DSRCS); fi + @if [ -n "$(DSRCS)" ]; then $(PERL) $(DSTYLE) $(DSRCS); fi FRC: diff --git a/usr/src/cmd/dtrace/test/tst/common/Makefile b/usr/src/cmd/dtrace/test/tst/common/Makefile index a29d774069..9ec706565a 100644 --- a/usr/src/cmd/dtrace/test/tst/common/Makefile +++ b/usr/src/cmd/dtrace/test/tst/common/Makefile @@ -149,6 +149,14 @@ usdt/tst.forker.o: usdt/forker.h usdt/forker.h: usdt/forker.d $(DTRACE) -h -s usdt/forker.d -o usdt/forker.h +ustack/tst.unpriv.exe: ustack/tst.unpriv.o ustack/unpriv_helper.o + $(LINK.c) -o ustack/tst.unpriv.exe \ + ustack/tst.unpriv.o ustack/unpriv_helper.o $(LDLIBS) + $(POST_PROCESS) ; $(STRIP_STABS) + +ustack/unpriv_helper.o: ustack/unpriv_helper.d + $(COMPILE.d) -o ustack/unpriv_helper.o -s ustack/unpriv_helper.d + usdt/tst.lazyprobe.exe: usdt/tst.lazyprobe.o usdt/lazyprobe.o $(LINK.c) -o usdt/tst.lazyprobe.exe \ usdt/tst.lazyprobe.o usdt/lazyprobe.o $(LDLIBS) diff --git a/usr/src/cmd/dtrace/test/tst/common/java_api/Makefile b/usr/src/cmd/dtrace/test/tst/common/java_api/Makefile index 43daede8e8..04e6cca7b4 100644 --- a/usr/src/cmd/dtrace/test/tst/common/java_api/Makefile +++ b/usr/src/cmd/dtrace/test/tst/common/java_api/Makefile @@ -60,7 +60,7 @@ lint: install: all $(PROTO_TEST_JAR) JFLAGS= -g -cp $(CLASSPATH) -d $(CLASSDIR) -JFLAGS += -source 1.5 -target 1.6 -Xlint:all,-options,-rawtypes +JFLAGS += -source 1.5 -target 1.6 -Xlint:all,-options,-rawtypes,-path COMPILE.java=$(JAVAC) $(JFLAGS) $(TEST_JAR): $(SRCDIR)/*.java diff --git a/usr/src/cmd/dtrace/test/tst/common/llquantize/tst.range.d b/usr/src/cmd/dtrace/test/tst/common/llquantize/tst.range.d index e2882b3f8e..a9138d2f54 100644 --- a/usr/src/cmd/dtrace/test/tst/common/llquantize/tst.range.d +++ b/usr/src/cmd/dtrace/test/tst/common/llquantize/tst.range.d @@ -19,10 +19,6 @@ * CDDL HEADER END */ -/* - * Copyright (c) 2011, Joyent, Inc. All rights reserved. - */ - #pragma D option quiet BEGIN diff --git a/usr/src/cmd/dtrace/test/tst/common/mdb/tst.postmort.ksh b/usr/src/cmd/dtrace/test/tst/common/mdb/tst.postmort.ksh new file mode 100644 index 0000000000..c5921b8d28 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/mdb/tst.postmort.ksh @@ -0,0 +1,69 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +dtrace=$1 +DIR=/var/tmp/dtest.$$ + +mkdir $DIR +cd $DIR + +expected=`od -t u8 -N 8 /dev/urandom | head -1 | cut -d ' ' -f2` + +$dtrace -x bufpolicy=ring -x bufsize=10k -qs /dev/stdin > /dev/null 2>&1 <<EOF & + tick-1ms + /i < 10000/ + { + printf("%d: expected is $expected!\n", i++); + } + + tick-1ms + /i >= 10000/ + { + exit(0); + } +EOF + +background=$! + +# +# Give some time for the enabling to get there... +# +sleep 2 + +echo "::walk dtrace_state | ::dtrace" | mdb -k | tee test.out +grep "expected is $expected" test.out 2> /dev/null 1>&2 +status=$? + +kill $background + +cd / +/usr/bin/rm -rf $DIR + +exit $status diff --git a/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.c b/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.c new file mode 100644 index 0000000000..43ba244444 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.c @@ -0,0 +1,7 @@ +int +main(int argc, char *argv[]) +{ + for (;;) + ; + return (0); +} diff --git a/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.ksh b/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.ksh new file mode 100644 index 0000000000..26c430bff7 --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/ustack/tst.unpriv.ksh @@ -0,0 +1,68 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +ppriv -s A=basic,dtrace_user,dtrace_proc $$ + +if [ $# != 1 ]; then + echo expected one argument: '<'dtrace-path'>' + exit 2 +fi + +file=out.$$ +dtrace=$1 + +rm -f $file + +dir=`/bin/dirname $tst` + +$dtrace -o $file -c $dir/tst.unpriv.exe -ws /dev/stdin <<EOF + profile-1234hz + /pid == \$target/ + { + @[ustack(20, 8192)] = count(); + } + + tick-1s + { + secs++; + } + + tick-1s + /secs > 10/ + { + trace("test timed out"); + exit(1); + } + + profile-1234hz + /pid == \$target && secs > 5/ + { + raise(SIGINT); + exit(0); + } +EOF + +status=$? +exit $status diff --git a/usr/src/cmd/dtrace/test/tst/common/ustack/unpriv_helper.d b/usr/src/cmd/dtrace/test/tst/common/ustack/unpriv_helper.d new file mode 100644 index 0000000000..eb7b0e9e9d --- /dev/null +++ b/usr/src/cmd/dtrace/test/tst/common/ustack/unpriv_helper.d @@ -0,0 +1,4 @@ +dtrace:helper:ustack: +{ + this->otherstr = "doogle"; +} diff --git a/usr/src/cmd/dumpadm/Makefile b/usr/src/cmd/dumpadm/Makefile index e1303c5d0e..c6b80188fa 100644 --- a/usr/src/cmd/dumpadm/Makefile +++ b/usr/src/cmd/dumpadm/Makefile @@ -26,12 +26,12 @@ PROG = dumpadm MANIFEST = dumpadm.xml SVCMETHOD= svc-dumpadm +ETCFILES= dumpadm.conf OBJS = main.o dconf.o minfree.o utils.o swap.o SRCS = $(OBJS:.o=.c) - -lint := LINTFLAGS = -mx +ROOTETCFILES= $(ETCFILES:%=$(ROOTETC)/%) include ../Makefile.cmd @@ -52,13 +52,11 @@ $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) -install: all $(ROOTUSRSBINPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD) +install: all $(ROOTUSRSBINPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD) $(ROOTETCFILES) check: $(CHKMANIFEST) clean: $(RM) $(OBJS) -lint: lint_SRCS - include ../Makefile.targ diff --git a/usr/src/cmd/dumpadm/dconf.c b/usr/src/cmd/dumpadm/dconf.c index dc5355ba48..6e549afaa7 100644 --- a/usr/src/cmd/dumpadm/dconf.c +++ b/usr/src/cmd/dumpadm/dconf.c @@ -28,6 +28,7 @@ #include <sys/stat.h> #include <sys/swap.h> #include <sys/dumpadm.h> +#include <sys/dumphdr.h> #include <sys/utsname.h> #include <unistd.h> @@ -539,6 +540,42 @@ dconf_get_dumpsize(dumpconf_t *dcp) return (0); } +int +dconf_set_crypt(dumpconf_t *dcp, const char *keyfile) +{ + int fd; + uint8_t key[DUMP_CRYPT_KEYLEN]; + + if ((fd = open(keyfile, O_RDONLY)) == -1) { + warn(gettext("failed to open %s"), keyfile); + return (-1); + } + + if (read(fd, key, sizeof (key)) != sizeof (key)) { + warn(gettext("failed to read %d byte key from %s"), + DUMP_CRYPT_KEYLEN, keyfile); + (void) close(fd); + return (-1); + } + + (void) close(fd); + + if (ioctl(dcp->dc_dump_fd, DIOCSCRYPTKEY, key) == -1) { + warn(gettext("failed to set encryption key")); + return (-1); + } + + /* + * Reload our config flags as they may have changed. + */ + if ((dcp->dc_cflags = ioctl(dcp->dc_dump_fd, DIOCGETCONF, 0)) == -1) { + warn(gettext("failed to get kernel dump settings")); + return (-1); + } + + return (0); +} + void dconf_print(dumpconf_t *dcp, FILE *fp) { @@ -580,6 +617,8 @@ dconf_print(dumpconf_t *dcp, FILE *fp) (void) fprintf(fp, gettext(" Save compressed: %s\n"), (dcp->dc_csave == DC_UNCOMPRESSED) ? gettext("off") : gettext("on")); + (void) fprintf(fp, gettext(" Dump encrypted: %s\n"), + (dcp->dc_cflags & DUMP_ENCRYPT) ? gettext("yes") : gettext("no")); } int diff --git a/usr/src/cmd/dumpadm/dconf.h b/usr/src/cmd/dumpadm/dconf.h index 74920f0def..e2f609cee7 100644 --- a/usr/src/cmd/dumpadm/dconf.h +++ b/usr/src/cmd/dumpadm/dconf.h @@ -21,6 +21,7 @@ /* * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #ifndef _DCONF_H @@ -73,6 +74,7 @@ extern int dconf_update(dumpconf_t *, int); extern void dconf_print(dumpconf_t *, FILE *); extern int dconf_write_uuid(dumpconf_t *); extern int dconf_get_dumpsize(dumpconf_t *); +extern int dconf_set_crypt(dumpconf_t *, const char *); extern int dconf_str2device(dumpconf_t *, char *); extern int dconf_str2savdir(dumpconf_t *, char *); diff --git a/usr/src/cmd/dumpadm/dumpadm.conf b/usr/src/cmd/dumpadm/dumpadm.conf new file mode 100644 index 0000000000..804e1da11a --- /dev/null +++ b/usr/src/cmd/dumpadm/dumpadm.conf @@ -0,0 +1,11 @@ +# +# dumpadm.conf +# +# Configuration parameters for system crash dump. +# Do NOT edit this file by hand -- use dumpadm(1m) instead. +# +DUMPADM_DEVICE=/dev/zvol/dsk/zones/dump +DUMPADM_SAVDIR=/var/crash/volatile +DUMPADM_CONTENT=kernel +DUMPADM_ENABLE=no +DUMPADM_CSAVE=on diff --git a/usr/src/cmd/dumpadm/main.c b/usr/src/cmd/dumpadm/main.c index 07a7dd5207..dccafbba33 100644 --- a/usr/src/cmd/dumpadm/main.c +++ b/usr/src/cmd/dumpadm/main.c @@ -21,6 +21,7 @@ /* * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #include <sys/stat.h> @@ -36,10 +37,10 @@ static const char USAGE[] = "\ Usage: %s [-enuy] [-c kernel | curproc | all ]\n\ - [-d dump-device | swap | none ] [-m min {k|m|%%} ] [-s savecore-dir]\n\ - [-r root-dir] [-z on|off]\n"; + [-d dump-device | swap | none ] [-k key-file] [-m min {k|m|%%} ]\n\ + [-s savecore-dir] [-r root-dir] [-z on|off]\n"; -static const char OPTS[] = "einuyc:d:m:s:r:z:"; +static const char OPTS[] = "einuyc:d:m:s:r:z:k:"; static const char PATH_DEVICE[] = "/dev/dump"; static const char PATH_CONFIG[] = "/etc/dumpadm.conf"; @@ -57,6 +58,7 @@ main(int argc, char *argv[]) int dcmode = DC_CURRENT; /* kernel settings override unless -u */ int modified = 0; /* have we modified the dump config? */ char *minfstr = NULL; /* string value of -m argument */ + char *keyfile = NULL; /* key file for -k argument */ dumpconf_t dc; /* current configuration */ int chrooted = 0; int douuid = 0; @@ -136,6 +138,9 @@ main(int argc, char *argv[]) } douuid++; break; + case 'k': + keyfile = optarg; + break; case 'm': minfstr = optarg; @@ -191,6 +196,9 @@ main(int argc, char *argv[]) return (E_ERROR); } + if (keyfile != NULL && dconf_set_crypt(&dc, keyfile) == -1) + return (E_ERROR); + if (dcmode == DC_OVERRIDE) { /* * In override mode, we try to force an update. If this diff --git a/usr/src/cmd/dumpadm/svc-dumpadm b/usr/src/cmd/dumpadm/svc-dumpadm index 316e075754..488ba8d54d 100644 --- a/usr/src/cmd/dumpadm/svc-dumpadm +++ b/usr/src/cmd/dumpadm/svc-dumpadm @@ -21,6 +21,7 @@ # # # Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2020 Joyent, Inc. # . /lib/svc/share/smf_include.sh @@ -100,7 +101,7 @@ fi # how to modify the dump settings. # if [ -x /usr/sbin/dumpadm ]; then - /usr/sbin/dumpadm -u || $SMF_EXIT_ERR_CONFIG + /usr/sbin/dumpadm -u || exit $SMF_EXIT_ERR_CONFIG else echo "WARNING: /usr/sbin/dumpadm is missing or not executable" >& 2 exit $SMF_EXIT_ERR_CONFIG @@ -113,6 +114,11 @@ else exit $SMF_EXIT_ERR_CONFIG fi +if [[ -f $DUMPADM_SAVDIR/keyfile ]]; then + /usr/sbin/dumpadm -k $DUMPADM_SAVDIR/keyfile || \ + exit $SMT_EXIT_ERR_CONFIG +fi + # # If the savecore executable is absent then we're done # @@ -147,11 +153,15 @@ if [ "x$DUMPADM_ENABLE" != xno ]; then mksavedir && /usr/bin/savecore $DUMPADM_SAVDIR & fi else + keyarg="" + [[ -f "$DUMPADM_SAVDIR/keyfile" ]] && \ + keyarg="-k $DUMPADM_SAVDIR/keyfile" + # # The dump device couldn't have been dedicated before we # ran dumpadm, so we must execute savecore again. # - mksavedir && /usr/bin/savecore $DUMPADM_SAVDIR & + mksavedir && /usr/bin/savecore $keyarg $DUMPADM_SAVDIR & fi else # diff --git a/usr/src/cmd/flowadm/flowadm.c b/usr/src/cmd/flowadm/flowadm.c index 058c1e03d8..a1f1c7387e 100644 --- a/usr/src/cmd/flowadm/flowadm.c +++ b/usr/src/cmd/flowadm/flowadm.c @@ -236,9 +236,9 @@ usage(void) (void) fprintf(stderr, gettext("usage: flowadm <subcommand>" " <args>...\n" " add-flow [-t] -l <link> -a <attr>=<value>[,...]\n" - "\t\t [-p <prop>=<value>,...] <flow>\n" - " remove-flow [-t] {-l <link> | <flow>}\n" - " show-flow [-p] [-l <link>] " + "\t\t [-p <prop>=<value>,...] [-z zonename] <flow>\n" + " remove-flow [-t] [-z zonename] {-l <link> | <flow>}\n" + " show-flow [-p] [-l <link>] [-z zonename] " "[<flow>]\n\n" " set-flowprop [-t] -p <prop>=<value>[,...] <flow>\n" " reset-flowprop [-t] [-p <prop>,...] <flow>\n" @@ -336,11 +336,12 @@ do_add_flow(int argc, char *argv[]) dladm_arg_list_t *proplist = NULL; dladm_arg_list_t *attrlist = NULL; dladm_status_t status; + char *zonename = NULL; bzero(propstr, DLADM_STRSIZE); bzero(attrstr, DLADM_STRSIZE); - while ((option = getopt_long(argc, argv, "tR:l:a:p:", + while ((option = getopt_long(argc, argv, "tR:l:a:p:z:", prop_longopts, NULL)) != -1) { switch (option) { case 't': @@ -354,9 +355,6 @@ do_add_flow(int argc, char *argv[]) MAXLINKNAMELEN) >= MAXLINKNAMELEN) { die("link name too long"); } - if (dladm_name2info(handle, devname, &linkid, NULL, - NULL, NULL) != DLADM_STATUS_OK) - die("invalid link '%s'", devname); l_arg = B_TRUE; break; case 'a': @@ -371,6 +369,9 @@ do_add_flow(int argc, char *argv[]) DLADM_STRSIZE) die("property list too long '%s'", propstr); break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option); } @@ -379,6 +380,10 @@ do_add_flow(int argc, char *argv[]) die("link is required"); } + if (dladm_zname2info(handle, zonename, devname, &linkid, NULL, + NULL, NULL) != DLADM_STATUS_OK) + die("invalid link '%s'", devname); + opterr = 0; index = optind; @@ -417,11 +422,12 @@ do_remove_flow(int argc, char *argv[]) boolean_t l_arg = B_FALSE; remove_flow_state_t state; dladm_status_t status; + char *zonename = NULL; bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":tR:l:", + while ((option = getopt_long(argc, argv, ":tR:l:z:", longopts, NULL)) != -1) { switch (option) { case 't': @@ -435,12 +441,11 @@ do_remove_flow(int argc, char *argv[]) MAXLINKNAMELEN) >= MAXLINKNAMELEN) { die("link name too long"); } - if (dladm_name2info(handle, linkname, &linkid, NULL, - NULL, NULL) != DLADM_STATUS_OK) { - die("invalid link '%s'", linkname); - } l_arg = B_TRUE; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option); break; @@ -461,6 +466,12 @@ do_remove_flow(int argc, char *argv[]) /* if link is specified then flow name should not be there */ if (optind == argc-1) usage(); + + if (dladm_zname2info(handle, zonename, linkname, &linkid, NULL, + NULL, NULL) != DLADM_STATUS_OK) { + die("invalid link '%s'", linkname); + } + /* walk the link to find flows and remove them */ state.fs_tempop = t_arg; state.fs_altroot = altroot; @@ -600,11 +611,12 @@ do_show_flow(int argc, char *argv[]) ofmt_handle_t ofmt; ofmt_status_t oferr; uint_t ofmtflags = 0; + char *zonename = NULL; bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":pPl:o:", + while ((option = getopt_long(argc, argv, ":pPl:o:z:", longopts, NULL)) != -1) { switch (option) { case 'p': @@ -625,17 +637,23 @@ do_show_flow(int argc, char *argv[]) if (strlcpy(linkname, optarg, MAXLINKNAMELEN) >= MAXLINKNAMELEN) die("link name too long\n"); - if (dladm_name2info(handle, linkname, &linkid, NULL, - NULL, NULL) != DLADM_STATUS_OK) - die("invalid link '%s'", linkname); l_arg = B_TRUE; break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option); break; } } + if (l_arg) { + if (dladm_zname2info(handle, zonename, linkname, &linkid, NULL, + NULL, NULL) != DLADM_STATUS_OK) + die("invalid link '%s'", linkname); + } + /* get flow name (optional last argument */ if (optind == (argc-1)) { if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) diff --git a/usr/src/cmd/flowstat/flowstat.c b/usr/src/cmd/flowstat/flowstat.c index 23e973c8a2..a0e2b85f32 100644 --- a/usr/src/cmd/flowstat/flowstat.c +++ b/usr/src/cmd/flowstat/flowstat.c @@ -201,9 +201,9 @@ static char *progname; static dladm_handle_t handle = NULL; const char *usage_ermsg = "flowstat [-r | -t] [-i interval] " - "[-l link] [flow]\n" + "[-l link] [-z zonename] [flow]\n" " flowstat [-A] [-i interval] [-p] [ -o field[,...]]\n" - " [-u R|K|M|G|T|P] [-l link] [flow]\n" + " [-u R|K|M|G|T|P] [-l link] [-z zonename] [flow]\n" " flowstat -h [-a] [-d] [-F format]" " [-s <DD/MM/YYYY,HH:MM:SS>]\n" " [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> " @@ -566,6 +566,7 @@ main(int argc, char *argv[]) show_flow_state_t state; char *fields_str = NULL; char *o_fields_str = NULL; + char *zonename = NULL; char *total_stat_fields = "flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs"; @@ -592,10 +593,11 @@ main(int argc, char *argv[]) if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) die_dlerr(status, "could not open /dev/dld"); + linkname[0] = '\0'; bzero(&state, sizeof (state)); opterr = 0; - while ((option = getopt_long(argc, argv, ":rtApi:o:u:l:h", + while ((option = getopt_long(argc, argv, ":rtApi:o:u:l:hz:", NULL, NULL)) != -1) { switch (option) { case 'r': @@ -647,9 +649,6 @@ main(int argc, char *argv[]) if (strlcpy(linkname, optarg, MAXLINKNAMELEN) >= MAXLINKNAMELEN) die("link name too long\n"); - if (dladm_name2info(handle, linkname, &linkid, NULL, - NULL, NULL) != DLADM_STATUS_OK) - die("invalid link '%s'", linkname); break; case 'h': if (r_arg || t_arg || p_arg || o_arg || u_arg || @@ -660,6 +659,9 @@ main(int argc, char *argv[]) do_show_history(argc, argv); return (0); break; + case 'z': + zonename = optarg; + break; default: die_opterr(optopt, option, usage_ermsg); break; @@ -683,6 +685,12 @@ main(int argc, char *argv[]) die("the option -A is not compatible with " "-r, -t, -p, -o, -u, -i"); + if (linkname[0] != '\0') { + if (dladm_zname2info(handle, zonename, linkname, &linkid, NULL, + NULL, NULL) != DLADM_STATUS_OK) + die("invalid link '%s'", linkname); + } + /* get flow name (optional last argument) */ if (optind == (argc-1)) { if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN) diff --git a/usr/src/cmd/fm/fmdump/common/nvlrender.c b/usr/src/cmd/fm/fmdump/common/nvlrender.c index 2c2f5ca662..99f027a77d 100644 --- a/usr/src/cmd/fm/fmdump/common/nvlrender.c +++ b/usr/src/cmd/fm/fmdump/common/nvlrender.c @@ -83,7 +83,7 @@ fmdump_render_nvlist(nvlist_prtctl_t pctl, void *private, nvlist_t *nvl, int fmdump_print_json(fmd_log_t *lp, const fmd_log_record_t *rp, FILE *fp) { - if (nvlist_print_json(fp, rp->rec_nvl) != 0 || fprintf(fp, "\n") < 0 || + if (nvlist_print_json(fp, rp->rec_nvl) < 0 || fprintf(fp, "\n") < 0 || fflush(fp) != 0) return (-1); diff --git a/usr/src/cmd/fm/modules/common/fabric-xlate/fabric-xlate.h b/usr/src/cmd/fm/modules/common/fabric-xlate/fabric-xlate.h index f33ea9ecd6..96e1a956af 100644 --- a/usr/src/cmd/fm/modules/common/fabric-xlate/fabric-xlate.h +++ b/usr/src/cmd/fm/modules/common/fabric-xlate/fabric-xlate.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #ifndef _FABRIC_XLATE_H @@ -31,6 +32,7 @@ #include <sys/types.h> #include <sys/pcie.h> #include <sys/fm/io/pci.h> +#include <limits.h> #ifdef __cplusplus extern "C" { @@ -45,6 +47,17 @@ extern "C" { #define PF_ADDR_PIO (1 << 1) #define PF_ADDR_CFG (1 << 2) + +/* + * The fabric ereport preparation functions (fab_prep_*) in fab_erpt_tbl_t + * structures may return an error if the ereport could not be set up properly. + * Typically, these errors are errnos. It is possible that based on incoming + * ereport payload data, we might not want to generate an ereport at all: In + * this case, the preparation functions may instead return PF_EREPORT_IGNORE, + * which is set at a high value so as not to collide with the errnos. + */ +#define PF_EREPORT_IGNORE INT_MAX + extern fmd_xprt_t *fab_fmd_xprt; /* FMD transport layer handle */ extern char fab_buf[]; @@ -121,8 +134,21 @@ typedef struct fab_data { uint16_t pcie_rp_ctl; /* root complex control register */ uint32_t pcie_rp_err_status; /* pcie root complex error status reg */ uint32_t pcie_rp_err_cmd; /* pcie root complex error cmd reg */ - uint16_t pcie_rp_ce_src_id; /* pcie root complex ce sourpe id */ - uint16_t pcie_rp_ue_src_id; /* pcie root complex ue sourpe id */ + uint16_t pcie_rp_ce_src_id; /* pcie root complex ce source id */ + uint16_t pcie_rp_ue_src_id; /* pcie root complex ue source id */ + + /* + * The slot register values refer to the registers of the component's + * parent slot, not the component itself. + * + * You should only use the register values -- i.e., + * pcie_slot_{cap,control,status} -- if pcie_slot_data_valid is set to + * true. + */ + boolean_t pcie_slot_data_valid; /* true if slot data is valid */ + uint32_t pcie_slot_cap; /* pcie slot capabilities */ + uint16_t pcie_slot_control; /* pcie slot control */ + uint16_t pcie_slot_status; /* pcie slot status */ /* Flags */ boolean_t pcie_rp_send_all; /* need to send ereports on all rps */ @@ -131,7 +157,6 @@ typedef struct fab_data { typedef struct fab_erpt_tbl { const char *err_class; /* Final Ereport Class */ uint32_t reg_bit; /* Error Bit Mask */ - /* Pointer to function that prepares the ereport body */ const char *tgt_class; /* Target Ereport Class */ } fab_erpt_tbl_t; diff --git a/usr/src/cmd/fm/modules/common/fabric-xlate/fx_fabric.c b/usr/src/cmd/fm/modules/common/fabric-xlate/fx_fabric.c index 69ecf1aa8d..14ae738863 100644 --- a/usr/src/cmd/fm/modules/common/fabric-xlate/fx_fabric.c +++ b/usr/src/cmd/fm/modules/common/fabric-xlate/fx_fabric.c @@ -22,10 +22,13 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2019 Joyent, Inc. */ #include <stddef.h> #include <strings.h> #include <sys/fm/util.h> +#include <sys/pcie.h> #include "fabric-xlate.h" @@ -271,6 +274,24 @@ fab_pci_fabric_to_data(fmd_hdl_t *hdl, nvlist_t *nvl, fab_data_t *data) FAB_LOOKUP(32, "pcie_adv_rp_command", &data->pcie_rp_err_cmd); FAB_LOOKUP(16, "pcie_adv_rp_ce_src_id", &data->pcie_rp_ce_src_id); FAB_LOOKUP(16, "pcie_adv_rp_ue_src_id", &data->pcie_rp_ue_src_id); + + /* + * PCIe Parent Slot Registers + * + * These are only passed in the ereport if the parent PCIe component + * supports the registers and the registers have valid data. As such, we + * look up one slot register value first: If that value is present in + * the input ereport data, then we know the others should be there as + * well. We also set the pcie_slot_data_valid flag to ensure we know + * the slot register data is safe to use in the module. + */ + data->pcie_slot_data_valid = B_FALSE; + if (nvlist_lookup_uint32(nvl, "pcie_slot_cap", &data->pcie_slot_cap) == + 0) { + FAB_LOOKUP(16, "pcie_slot_control", &data->pcie_slot_control); + FAB_LOOKUP(16, "pcie_slot_status", &data->pcie_slot_status); + data->pcie_slot_data_valid = B_TRUE; + } } static int @@ -358,6 +379,38 @@ fab_prep_pcie_ue_erpt(fmd_hdl_t *hdl, fab_data_t *data, nvlist_t *erpt, PCIE_AER_CTL_FST_ERR_PTR_MASK); int err = fab_prep_basic_erpt(hdl, data->nvl, erpt, B_FALSE); + if (data->pcie_slot_data_valid) { + (void) nvlist_add_uint32(erpt, "pcie_slot_cap", + data->pcie_slot_cap); + (void) nvlist_add_uint16(erpt, "pcie_slot_control", + data->pcie_slot_control); + (void) nvlist_add_uint16(erpt, "pcie_slot_status", + data->pcie_slot_status); + + /* + * It is possible to see uncorrectable errors for a slot that + * are related to the slot's child device being physically + * removed from the slot. As such, in the case that the slot + * reports that it is empty, we do not want to generate an + * ereport for all errors. Generating an ereport here will cause + * the eft module to fault the device and io-retire to + * subsequently retire the device. Retiring the device makes + * little sense given that the device is physically gone; more + * confusingly, if plugged back into the slot, it would be + * marked retired already. + * + * The only error ignored for this case is Completion Timeout. + * It is possible more errors should be ignored, and if they + * are seen in the field it might be worth broadening the set + * of ignored errors. + */ + if (tbl->reg_bit == PCIE_AER_UCE_TO && + ((data->pcie_slot_status & + PCIE_SLOTSTS_PRESENCE_DETECTED) == 0x0)) { + return (PF_EREPORT_IGNORE); + } + } + /* Generate an ereport for this error bit. */ (void) snprintf(fab_buf, FM_MAX_CLASS, "ereport.io.%s.%s", PCIEX_ERROR_SUBCLASS, class); @@ -776,7 +829,7 @@ fab_xlate_pcie_erpts(fmd_hdl_t *hdl, fab_data_t *data) fmd_hdl_debug(hdl, "Sending Ereports Now"); - /* Go through the error logs and send the relavant reports */ + /* Go through the error logs and send the relevant reports */ for (tbl = fab_master_err_tbl; tbl->erpt_tbl; tbl++) { fab_send_erpt(hdl, data, tbl); } diff --git a/usr/src/cmd/fm/modules/common/fabric-xlate/fx_subr.c b/usr/src/cmd/fm/modules/common/fabric-xlate/fx_subr.c index 8593144b28..94678dbd47 100644 --- a/usr/src/cmd/fm/modules/common/fabric-xlate/fx_subr.c +++ b/usr/src/cmd/fm/modules/common/fabric-xlate/fx_subr.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #include <strings.h> #include <fm/topo_hc.h> @@ -185,6 +186,7 @@ fab_send_erpt(fmd_hdl_t *hdl, fab_data_t *data, fab_err_tbl_t *tbl) fab_erpt_tbl_t *erpt_tbl, *entry; nvlist_t *erpt; uint32_t reg; + int err; erpt_tbl = tbl->erpt_tbl; if (tbl->reg_size == 16) { @@ -200,7 +202,9 @@ fab_send_erpt(fmd_hdl_t *hdl, fab_data_t *data, fab_err_tbl_t *tbl) if (nvlist_alloc(&erpt, NV_UNIQUE_NAME, 0) != 0) goto done; - if (tbl->fab_prep(hdl, data, erpt, entry) != 0) { + + err = tbl->fab_prep(hdl, data, erpt, entry); + if (err != 0 && err != PF_EREPORT_IGNORE) { fmd_hdl_debug(hdl, "Prepping ereport failed: " "class = %s\n", entry->err_class); nvlist_free(erpt); @@ -394,7 +398,7 @@ fab_find_rppath_by_devbdf(fmd_hdl_t *hdl, nvlist_t *nvl, pcie_req_id_t bdf) xmlXPathObjectPtr xpathObj; xmlNodeSetPtr nodes; xmlNodePtr devNode; - char *retval, *temp; + char *retval, *temp; char query[500]; int i, size, bus, dev, fn; char *hcpath; @@ -577,7 +581,7 @@ fail: char * fab_find_bdf(fmd_hdl_t *hdl, nvlist_t *nvl, pcie_req_id_t bdf) { - char *retval; + char *retval; char query[500]; int bus, dev, fn; char rcpath[255]; @@ -705,7 +709,7 @@ found: propgroup: /* Retrive the "dev" propval and return */ for (devNode = devNode->children; devNode; devNode = devNode->next) { - char *tprop; + char *tprop; tprop = GET_PROP(devNode, "name"); if (STRCMP(devNode->name, "propval") && @@ -866,8 +870,8 @@ fab_pr(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl) char * fab_get_rpdev(fmd_hdl_t *hdl) { - char *retval; - char query[500]; + char *retval; + char query[500]; (void) snprintf(query, sizeof (query), "//propval[" "@name='extended-capabilities' and contains(@value, '%s')]" @@ -888,8 +892,8 @@ fab_send_erpt_all_rps(fmd_hdl_t *hdl, nvlist_t *erpt) { xmlXPathObjectPtr xpathObj; xmlNodeSetPtr nodes; - char *rppath, *hbpath; - char query[600]; + char *rppath, *hbpath; + char query[600]; nvlist_t *detector, *nvl; uint_t i, size; size_t len; diff --git a/usr/src/cmd/fm/modules/common/zfs-retire/zfs_retire.c b/usr/src/cmd/fm/modules/common/zfs-retire/zfs_retire.c index fa46bc51b3..20f5820cf1 100644 --- a/usr/src/cmd/fm/modules/common/zfs-retire/zfs_retire.c +++ b/usr/src/cmd/fm/modules/common/zfs-retire/zfs_retire.c @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2019 Joyent, Inc. */ /* * Copyright 2019 Joyent, Inc. diff --git a/usr/src/cmd/fs.d/Makefile b/usr/src/cmd/fs.d/Makefile index 2ce08652b7..1b1939e1f7 100644 --- a/usr/src/cmd/fs.d/Makefile +++ b/usr/src/cmd/fs.d/Makefile @@ -40,8 +40,8 @@ DEFAULTFILES= fs.dfl include ../Makefile.cmd -SUBDIR1= bootfs lofs zfs -SUBDIR2= dev fd pcfs nfs hsfs proc ctfs udfs ufs tmpfs \ +SUBDIR1= bootfs hyprlofs lofs zfs +SUBDIR2= dev fd pcfs nfs hsfs lxproc proc ctfs udfs ufs tmpfs \ autofs mntfs objfs sharefs smbclnt reparsed SUBDIRS= $(SUBDIR1) $(SUBDIR2) I18NDIRS= $(SUBDIR2) diff --git a/usr/src/cmd/fs.d/hyprlofs/Makefile b/usr/src/cmd/fs.d/hyprlofs/Makefile new file mode 100644 index 0000000000..1a3aaf18d3 --- /dev/null +++ b/usr/src/cmd/fs.d/hyprlofs/Makefile @@ -0,0 +1,42 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved +# + +SUBDIRS= hlcfg mount + +all:= TARGET= all +install:= TARGET= install +clean:= TARGET= clean +clobber:= TARGET= clobber +lint:= TARGET= lint + +.KEEP_STATE: + +.PARALLEL: $(SUBDIRS) + +all install clean clobber lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/cmd/fs.d/hyprlofs/hlcfg/Makefile b/usr/src/cmd/fs.d/hyprlofs/hlcfg/Makefile new file mode 100644 index 0000000000..d2ae22e9fd --- /dev/null +++ b/usr/src/cmd/fs.d/hyprlofs/hlcfg/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2012, Joyent, Inc. All rights reserved. +# + +FSTYPE= hyprlofs +LIBPROG= hlcfg + +include ../../Makefile.fstype +include ../../Makefile.mount +include ../../Makefile.mount.targ diff --git a/usr/src/cmd/fs.d/hyprlofs/hlcfg/hlcfg.c b/usr/src/cmd/fs.d/hyprlofs/hlcfg/hlcfg.c new file mode 100644 index 0000000000..16e8e32b1c --- /dev/null +++ b/usr/src/cmd/fs.d/hyprlofs/hlcfg/hlcfg.c @@ -0,0 +1,244 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2012, Joyent, Inc. All rights reserved. + */ + +/* + * This is a simple test program to exercise the hyprlofs ioctls. This is + * not designed as a full featured CLI and only does minimal error checking + * and reporting. + */ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <libgen.h> +#include <strings.h> +#include <sys/errno.h> +#include <sys/fs/hyprlofs.h> + +extern int errno; + +char *usage = "usage: <fs path> add [<file name> <alias>]+\n" + " <fs path> addl [<file name>]+\n" + " <fs path> rm [<alias>]+\n" + " <fs path> clear" + " <fs path> get"; + +typedef enum { + CMD_ADD, + CMD_RM, + CMD_CLR, + CMD_ADDL, + CMD_GET +} cmd_t; + +static int +get_entries(int fd) +{ + int err; + int i; + hyprlofs_curr_entries_t e; + hyprlofs_curr_entry_t *ep; + + e.hce_cnt = 0; + e.hce_entries = NULL; + + err = ioctl(fd, HYPRLOFS_GET_ENTRIES, &e); + if (err != 0 && errno != E2BIG) { + perror("ioctl"); + return (1); + } + + if (err == 0) { + (void) printf("success, but no entries\n"); + return (0); + } + + /* + * E2BIG is what we expect when there are existing mappings + * since the current cnt is still returned in that case. + */ + (void) printf("cnt: %d\n", e.hce_cnt); + + /* alloc array and call again, then print array */ + if ((ep = (hyprlofs_curr_entry_t *) + malloc(sizeof (hyprlofs_curr_entry_t) * e.hce_cnt)) == NULL) { + (void) fprintf(stderr, "out of memory\n"); + exit(1); + } + + e.hce_entries = ep; + errno = 0; + if (ioctl(fd, HYPRLOFS_GET_ENTRIES, &e) != 0) { + /* + * Not handling an increase here. We would need to free and + * start over to do that, but ok for a test program. + */ + perror("ioctl"); + free(ep); + return (1); + } + for (i = 0; i < e.hce_cnt; i++) + (void) printf("%s %s\n", ep[i].hce_path, ep[i].hce_name); + + free(ep); + return (0); +} + +int +main(int argc, char **argv) +{ + int i, ap; + cmd_t cmd; + int cnt = 0; + int fd; + int rv = 0; + hyprlofs_entry_t *e = NULL; + hyprlofs_entries_t ents; + + if (argc < 3) { + (void) fprintf(stderr, "%s\n", usage); + exit(1); + } + + if ((fd = open(argv[1], O_RDONLY)) < 0) { + perror("can't open hyprlofs mount"); + exit(1); + } + + if (strcmp(argv[2], "add") == 0) { + cmd = CMD_ADD; + } else if (strcmp(argv[2], "rm") == 0) { + cmd = CMD_RM; + } else if (strcmp(argv[2], "clear") == 0) { + cmd = CMD_CLR; + } else if (strcmp(argv[2], "addl") == 0) { + cmd = CMD_ADDL; + } else if (strcmp(argv[2], "get") == 0) { + cmd = CMD_GET; + } else { + (void) fprintf(stderr, "%s\n", usage); + exit(1); + } + + /* Count up the number of parameters. The arg format varies w/ cmd */ + switch (cmd) { + case CMD_ADD: + for (i = 3; i < argc; i++) { + /* argv[i] is the file path */ + + /* The next arg is the alias */ + if (++i >= argc) { + (void) fprintf(stderr, "missing alias for %s\n", + argv[i - 1]); + exit(1); + } + + cnt++; + } + break; + case CMD_ADDL: + cnt = argc - 3; + break; + case CMD_RM: + cnt = argc - 3; + break; + case CMD_CLR: /*FALLTHRU*/ + case CMD_GET: + if (argc > 3) { + (void) fprintf(stderr, "%s\n", usage); + exit(1); + } + break; + } + + if (cnt > 0) { + if ((e = (hyprlofs_entry_t *)malloc(sizeof (hyprlofs_entry_t) * + cnt)) == NULL) { + (void) fprintf(stderr, "out of memory\n"); + exit(1); + } + } + + /* + * Format up the args. + * We only setup the path member for the add cmd. + * We won't run this loop for the clear cmd. + * The addl command is special since we use basename to get the alias. + */ + for (i = 0, ap = 3; i < cnt; i++, ap++) { + if (cmd == CMD_ADDL) { + e[i].hle_path = argv[ap]; + e[i].hle_plen = strlen(e[i].hle_path); + + e[i].hle_name = basename(argv[ap]); + e[i].hle_nlen = strlen(e[i].hle_name); + + continue; + } + + if (cmd == CMD_ADD) { + e[i].hle_path = argv[ap++]; + e[i].hle_plen = strlen(e[i].hle_path); + } + + e[i].hle_name = argv[ap]; + e[i].hle_nlen = strlen(e[i].hle_name); + } + + ents.hle_entries = e; + ents.hle_len = cnt; + + switch (cmd) { + case CMD_ADD: /*FALLTHRU*/ + case CMD_ADDL: + if (ioctl(fd, HYPRLOFS_ADD_ENTRIES, &ents) < 0) { + perror("ioctl"); + rv = 1; + } + break; + case CMD_RM: + if (ioctl(fd, HYPRLOFS_RM_ENTRIES, &ents) < 0) { + perror("ioctl"); + rv = 1; + } + break; + case CMD_CLR: + if (ioctl(fd, HYPRLOFS_RM_ALL) < 0) { + perror("ioctl"); + rv = 1; + } + break; + case CMD_GET: + rv = get_entries(fd); + break; + } + + (void) close(fd); + if (cnt > 0) + free(e); + return (rv); +} diff --git a/usr/src/cmd/fs.d/hyprlofs/mount/Makefile b/usr/src/cmd/fs.d/hyprlofs/mount/Makefile new file mode 100644 index 0000000000..a0b63d211c --- /dev/null +++ b/usr/src/cmd/fs.d/hyprlofs/mount/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2012 Joyent, Inc. All rights reserved. +# + +FSTYPE= hyprlofs +LIBPROG= mount + +include ../../Makefile.fstype +include ../../Makefile.mount +include ../../Makefile.mount.targ diff --git a/usr/src/cmd/fs.d/hyprlofs/mount/mount.c b/usr/src/cmd/fs.d/hyprlofs/mount/mount.c new file mode 100644 index 0000000000..a95c9ca3c2 --- /dev/null +++ b/usr/src/cmd/fs.d/hyprlofs/mount/mount.c @@ -0,0 +1,148 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2012 Joyent, Inc. All rights reserved. + */ + +#define HLFS +#define MNTTYPE_HYFS "hyprlofs" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libintl.h> +#include <errno.h> +#include <sys/fstyp.h> +#include <sys/fsid.h> +#include <sys/mntent.h> +#include <sys/mnttab.h> +#include <sys/mount.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <fslib.h> + +#define RET_OK 0 +/* + * /sbin/mount and the fs-local method understand this exit code to + * mean that all the mount failures were related to hyprlofs mounts. Since + * this program only attempts to mount hyfs file systems, when it fails + * it returns this exit status. + */ +#define RET_ERR 111 + +static void usage(void); + +static char optbuf[MAX_MNTOPT_STR] = { '\0', }; +static int optsize = 0; + +static char fstype[] = MNTTYPE_HYFS; + +/* + * usage: mount [-Ormq] [-o options] special mountp + * + * This mount program is exec'ed by /usr/sbin/mount if '-F hyprlofs' is + * specified. + */ +int +main(int argc, char *argv[]) +{ + int c; + char *special; /* Entity being mounted */ + char *mountp; /* Entity being mounted on */ + char *savedoptbuf; + char *myname; + char typename[64]; + int flags = 0; + int errflag = 0; + int qflg = 0; + + myname = strrchr(argv[0], '/'); + myname = myname ? myname+1 : argv[0]; + (void) snprintf(typename, sizeof (typename), "%s %s", fstype, myname); + argv[0] = typename; + + while ((c = getopt(argc, argv, "o:rmOq")) != EOF) { + switch (c) { + case '?': + errflag++; + break; + + case 'o': + if (strlcpy(optbuf, optarg, sizeof (optbuf)) >= + sizeof (optbuf)) { + (void) fprintf(stderr, + gettext("%s: Invalid argument: %s\n"), + myname, optarg); + return (2); + } + optsize = strlen(optbuf); + break; + case 'O': + flags |= MS_OVERLAY; + break; + case 'r': + flags |= MS_RDONLY; + break; + + case 'm': + flags |= MS_NOMNTTAB; + break; + + case 'q': + qflg = 1; + break; + + default: + usage(); + } + } + if ((argc - optind != 2) || errflag) { + usage(); + } + special = argv[argc - 2]; + mountp = argv[argc - 1]; + + if ((savedoptbuf = strdup(optbuf)) == NULL) { + (void) fprintf(stderr, gettext("%s: out of memory\n"), + myname); + exit(2); + } + if (mount(special, mountp, flags | MS_OPTIONSTR, fstype, NULL, 0, + optbuf, MAX_MNTOPT_STR)) { + (void) fprintf(stderr, "mount: "); + perror(special); + exit(RET_ERR); + } + if (optsize && !qflg) + cmp_requested_to_actual_options(savedoptbuf, optbuf, + special, mountp); + return (0); +} + +void +usage(void) +{ + (void) fprintf(stderr, + "Usage: mount [-Ormq] [-o options] special mountpoint\n"); + exit(RET_ERR); +} diff --git a/usr/src/cmd/fs.d/lxproc/Makefile b/usr/src/cmd/fs.d/lxproc/Makefile new file mode 100644 index 0000000000..77075ef1dd --- /dev/null +++ b/usr/src/cmd/fs.d/lxproc/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +FSTYPE= lxproc +LIBPROG= mount + +include ../Makefile.fstype +include ../Makefile.mount +include ../Makefile.mount.targ diff --git a/usr/src/cmd/fs.d/lxproc/mount.c b/usr/src/cmd/fs.d/lxproc/mount.c new file mode 100644 index 0000000000..5a000997bd --- /dev/null +++ b/usr/src/cmd/fs.d/lxproc/mount.c @@ -0,0 +1,140 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2012 Joyent, Inc. All rights reserved. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libintl.h> +#include <errno.h> +#include <sys/fstyp.h> +#include <sys/fsid.h> +#include <sys/mntent.h> +#include <sys/mnttab.h> +#include <sys/mount.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <fslib.h> + +#define RET_OK 0 +#define RET_ERR 33 + +static void usage(void); + +static char optbuf[MAX_MNTOPT_STR] = { '\0', }; +static int optsize = 0; + +static char fstype[] = "lxproc"; + +/* + * usage: mount [-Ormq] [-o options] special mountp + * + * This mount program is exec'ed by /usr/sbin/mount if '-F lxproc' is + * specified. + */ +int +main(int argc, char *argv[]) +{ + int c; + char *special; /* Entity being mounted */ + char *mountp; /* Entity being mounted on */ + char *savedoptbuf; + char *myname; + char typename[64]; + int flags = 0; + int errflag = 0; + int qflg = 0; + + myname = strrchr(argv[0], '/'); + myname = myname ? myname+1 : argv[0]; + (void) snprintf(typename, sizeof (typename), "%s %s", fstype, myname); + argv[0] = typename; + + while ((c = getopt(argc, argv, "o:rmOq")) != EOF) { + switch (c) { + case '?': + errflag++; + break; + + case 'o': + if (strlcpy(optbuf, optarg, sizeof (optbuf)) >= + sizeof (optbuf)) { + (void) fprintf(stderr, + gettext("%s: Invalid argument: %s\n"), + myname, optarg); + return (2); + } + optsize = strlen(optbuf); + break; + case 'O': + flags |= MS_OVERLAY; + break; + case 'r': + flags |= MS_RDONLY; + break; + + case 'm': + flags |= MS_NOMNTTAB; + break; + + case 'q': + qflg = 1; + break; + + default: + usage(); + } + } + if ((argc - optind != 2) || errflag) { + usage(); + } + special = argv[argc - 2]; + mountp = argv[argc - 1]; + + if ((savedoptbuf = strdup(optbuf)) == NULL) { + (void) fprintf(stderr, gettext("%s: out of memory\n"), + myname); + exit(2); + } + if (mount(special, mountp, flags | MS_OPTIONSTR, fstype, NULL, 0, + optbuf, MAX_MNTOPT_STR)) { + (void) fprintf(stderr, "mount: "); + perror(special); + exit(RET_ERR); + } + if (optsize && !qflg) + cmp_requested_to_actual_options(savedoptbuf, optbuf, + special, mountp); + return (0); +} + +void +usage(void) +{ + (void) fprintf(stderr, + "Usage: mount [-Ormq] [-o options] special mountpoint\n"); + exit(RET_ERR); +} diff --git a/usr/src/cmd/fs.d/mount.c b/usr/src/cmd/fs.d/mount.c index 8c8d7034fe..19be4b28c1 100644 --- a/usr/src/cmd/fs.d/mount.c +++ b/usr/src/cmd/fs.d/mount.c @@ -18,6 +18,12 @@ * * CDDL HEADER END */ + +/* + * Copyright 2015 Nexenta Systems, Inc. All rights reserved. + * Copyright 2016 Joyent, Inc. + */ + /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ @@ -52,6 +58,7 @@ #include <stropts.h> #include <sys/conf.h> #include <locale.h> +#include <zone.h> #include <priv.h> #include "fslib.h" @@ -799,6 +806,7 @@ mnterror(int flag) void doexec(char *fstype, char *newargv[]) { + const char *zroot = zone_get_nroot(); char full_path[PATH_MAX]; char alter_path[PATH_MAX]; char *vfs_path = VFS_PATH; @@ -806,7 +814,8 @@ doexec(char *fstype, char *newargv[]) int i; /* build the full pathname of the fstype dependent command. */ - sprintf(full_path, "%s/%s/%s", vfs_path, fstype, myname); + (void) snprintf(full_path, sizeof (full_path), "%s/%s/%s/%s", + (zroot != NULL ? zroot : ""), vfs_path, fstype, myname); sprintf(alter_path, "%s/%s/%s", alt_path, fstype, myname); newargv[1] = myname; diff --git a/usr/src/cmd/fs.d/nfs/lib/nfs_sec.c b/usr/src/cmd/fs.d/nfs/lib/nfs_sec.c index 06dc44a12c..af83923825 100644 --- a/usr/src/cmd/fs.d/nfs/lib/nfs_sec.c +++ b/usr/src/cmd/fs.d/nfs/lib/nfs_sec.c @@ -45,6 +45,7 @@ #include <stdlib.h> #include <syslog.h> #include <synch.h> +#include <zone.h> #include <rpc/rpc.h> #include <nfs/nfs_sec.h> #include <rpc/rpcsec_gss.h> @@ -707,12 +708,17 @@ get_seconfig(int whichway, char *name, int num, { char line[BUFSIZ]; /* holds each line of NFSSEC_CONF */ FILE *fp; /* file stream for NFSSEC_CONF */ + char nfssec_conf[MAXPATHLEN]; + const char *zroot = zone_get_nroot(); if ((whichway == GETBYNAME) && (name == NULL)) return (SC_NOTFOUND); + (void) snprintf(nfssec_conf, sizeof (nfssec_conf), "%s%s", zroot != NULL ? + zroot : "", NFSSEC_CONF); + (void) mutex_lock(&matching_lock); - if ((fp = fopen(NFSSEC_CONF, "r")) == NULL) { + if ((fp = fopen(nfssec_conf, "r")) == NULL) { (void) mutex_unlock(&matching_lock); return (SC_OPENFAIL); } diff --git a/usr/src/cmd/fs.d/nfs/lib/smfcfg.c b/usr/src/cmd/fs.d/nfs/lib/smfcfg.c index 892f0cd052..4f9399143c 100644 --- a/usr/src/cmd/fs.d/nfs/lib/smfcfg.c +++ b/usr/src/cmd/fs.d/nfs/lib/smfcfg.c @@ -22,6 +22,7 @@ /* * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #include <stdio.h> @@ -344,8 +345,23 @@ fs_smf_get_prop(smf_fstype_t fstype, char *prop_name, char *cbuf, } else { ret = scf_error(); } - if ((ret != 0) && scf_error() != SCF_ERROR_NONE) - fprintf(stdout, gettext("%s\n"), scf_strerror(ret)); + if ((ret != 0) && scf_error() != SCF_ERROR_NONE) { + /* + * This is a workaround for the NFS service manifests not + * containing the proper properties in local zones. + * + * When in a local zone and the property doesn't exist on an NFS + * service (most likely nfs/server or nfs/client), don't print + * the error. The caller will still see the correct error code, + * but a user creating a delegated dataset or mounting an NFS + * share won't see this spurious error. + */ + if (getzoneid() == GLOBAL_ZONEID || + scf_error() != SCF_ERROR_NOT_FOUND) { + fprintf(stdout, gettext("%s\n"), scf_strerror(ret)); + } + } + out: fs_smf_fini(phandle); return (ret); diff --git a/usr/src/cmd/fs.d/nfs/lib/smfcfg.h b/usr/src/cmd/fs.d/nfs/lib/smfcfg.h index c06327d801..f0b70907aa 100644 --- a/usr/src/cmd/fs.d/nfs/lib/smfcfg.h +++ b/usr/src/cmd/fs.d/nfs/lib/smfcfg.h @@ -20,6 +20,7 @@ */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #ifndef _SMFCFG_H @@ -42,6 +43,7 @@ #include <locale.h> #include <errno.h> #include <sys/types.h> +#include <zone.h> #ifdef __cplusplus extern "C" { diff --git a/usr/src/cmd/fs.d/nfs/lockd/lockd.c b/usr/src/cmd/fs.d/nfs/lockd/lockd.c index 3541ee13d0..6ea338b01a 100644 --- a/usr/src/cmd/fs.d/nfs/lockd/lockd.c +++ b/usr/src/cmd/fs.d/nfs/lockd/lockd.c @@ -23,6 +23,7 @@ * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012 by Delphix. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -88,7 +89,7 @@ struct lm_svc_args lmargs = { .version = LM_SVC_CUR_VERS, /* fd, n_fmly, n_proto, n_rdev (below) */ - .debug = 0, + .n_v4_only = 0, .timout = 5 * 60, .grace = 90, .retransmittimeout = 5 @@ -137,6 +138,8 @@ int (*Mysvc)(int, struct netbuf, struct netconfig *) = nlmsvc; /* used by cots_listen_event() */ int max_conns_allowed = -1; /* used by cots_listen_event() */ +int debug = 0; + int main(int ac, char *av[]) { @@ -238,7 +241,7 @@ main(int ac, char *av[]) break; case 'd': /* debug */ - lmargs.debug = atoi(optarg); + debug = atoi(optarg); break; case 'g': /* grace_period */ @@ -288,12 +291,12 @@ main(int ac, char *av[]) if (optind != ac) usage(); - if (lmargs.debug) { + if (debug != 0) { printf("%s: debug= %d, conn_idle_timout= %d," " grace_period= %d, listen_backlog= %d," " max_connections= %d, max_servers= %d," " retrans_timeout= %d\n", - MyName, lmargs.debug, lmargs.timout, + MyName, debug, lmargs.timout, lmargs.grace, listen_backlog, max_conns_allowed, max_servers, lmargs.retransmittimeout); @@ -309,7 +312,7 @@ main(int ac, char *av[]) } /* Daemonize, if not debug. */ - if (lmargs.debug == 0) + if (debug == 0) pipe_fd = daemonize_init(); openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON); @@ -405,7 +408,7 @@ main(int ac, char *av[]) /* * lockd is up and running as far as we are concerned. */ - if (lmargs.debug == 0) + if (debug == 0) daemonize_fini(pipe_fd); /* diff --git a/usr/src/cmd/fs.d/nfs/mount/Makefile b/usr/src/cmd/fs.d/nfs/mount/Makefile index a53b876974..cb6c77044b 100644 --- a/usr/src/cmd/fs.d/nfs/mount/Makefile +++ b/usr/src/cmd/fs.d/nfs/mount/Makefile @@ -25,13 +25,6 @@ FSTYPE= nfs LIBPROG= mount -ROOTFS_PROG= $(LIBPROG) - -# duplicate ROOTLIBFSTYPE value needed for installation rule -# we must define this before including Makefile.fstype -ROOTLIBFSTYPE = $(ROOT)/usr/lib/fs/$(FSTYPE) -$(ROOTLIBFSTYPE)/%: $(ROOTLIBFSTYPE) % - $(RM) $@; $(SYMLINK) ../../../../etc/fs/$(FSTYPE)/$(LIBPROG) $@ include ../../Makefile.fstype @@ -69,7 +62,7 @@ CLOBBERFILES += $(LIBPROG) .KEEP_STATE: -all: $(ROOTFS_PROG) +all: $(LIBPROG) $(LIBPROG): webnfs.h $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) @@ -118,7 +111,9 @@ $(POFILE): $(SRCS) webnfs.h sed "/^domain/d" messages.po > $@ $(RM) $(POFILE).i messages.po -install: $(ROOTETCPROG) +install: all $(FSTYPEPROG) + $(RM) $(ROOTETCPROG) + $(SYMLINK) ../../../usr/lib/fs/$(FSTYPE)/$(LIBPROG) $(ROOTETCPROG) lint: webnfs.h webnfs_xdr.c webnfs_client.c lint_SRCS diff --git a/usr/src/cmd/fs.d/nfs/mount/mount.c b/usr/src/cmd/fs.d/nfs/mount/mount.c index efb1f998f3..e1206e186a 100644 --- a/usr/src/cmd/fs.d/nfs/mount/mount.c +++ b/usr/src/cmd/fs.d/nfs/mount/mount.c @@ -2104,7 +2104,7 @@ get_fh(struct nfs_args *args, char *fshost, char *fspath, int *versp, } while ((cl = clnt_create_vers(fshost, MOUNTPROG, &outvers, - vers_min, vers_to_try, "datagram_v")) == NULL) { + vers_min, vers_to_try, NULL)) == NULL) { if (rpc_createerr.cf_stat == RPC_UNKNOWNHOST) { pr_err(gettext("%s: %s\n"), fshost, clnt_spcreateerror("")); diff --git a/usr/src/cmd/fs.d/nfs/svc/nfs-server b/usr/src/cmd/fs.d/nfs/svc/nfs-server index bf8e73be98..d982829758 100644 --- a/usr/src/cmd/fs.d/nfs/svc/nfs-server +++ b/usr/src/cmd/fs.d/nfs/svc/nfs-server @@ -53,13 +53,13 @@ configure_ipfilter() # # Nothing to do if: + # - service's policy is 'use_global' # - ipfilter isn't online # - global policy is 'custom' - # - service's policy is 'use_global' # + [ "`get_policy $SMF_FMRI`" = "use_global" ] && return 0 service_check_state $IPF_FMRI $SMF_ONLINE || return 0 [ "`get_global_def_policy`" = "custom" ] && return 0 - [ "`get_policy $SMF_FMRI`" = "use_global" ] && return 0 svcadm restart $IPF_FMRI } diff --git a/usr/src/cmd/fs.d/nfs/umount/umount.c b/usr/src/cmd/fs.d/nfs/umount/umount.c index aabdc8a592..66d280bcdb 100644 --- a/usr/src/cmd/fs.d/nfs/umount/umount.c +++ b/usr/src/cmd/fs.d/nfs/umount/umount.c @@ -297,7 +297,7 @@ retry: */ timep = (quick ? &create_timeout : NULL); cl = clnt_create_timed(list[i].host, MOUNTPROG, vers, - "datagram_n", timep); + NULL, timep); /* * Do not print any error messages in case of forced * unmount. diff --git a/usr/src/cmd/halt/halt.c b/usr/src/cmd/halt/halt.c index 893539611b..ca0ed8e77f 100644 --- a/usr/src/cmd/halt/halt.c +++ b/usr/src/cmd/halt/halt.c @@ -21,6 +21,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2011 Joyent, Inc. All rights reserved. */ /* * Copyright (c) 2013, Joyent, Inc. All rights reserved. @@ -1250,6 +1251,17 @@ do_archives_update(int do_fast_reboot) pid_t pid; char *cmd_argv[MAXARGS]; +#if defined(__i386) + { + /* + * bootadm will complain and exit if not a grub root, so + * just skip running it. + */ + struct stat sb; + if (stat("/boot/grub/stage2", &sb) == -1) + return; + } +#endif /* __i386 */ cmd_argv[i++] = "/sbin/bootadm"; cmd_argv[i++] = "-ea"; @@ -1314,7 +1326,7 @@ main(int argc, char *argv[]) optstring = "dlnqfp"; usage = gettext("usage: %s [ -dlnq(p|f) ] [ boot args ]\n"); #endif - cmd = A_SHUTDOWN; + cmd = A_REBOOT; fcn = AD_BOOT; } else { (void) fprintf(stderr, @@ -1512,7 +1524,8 @@ main(int argc, char *argv[]) * check_zone_haltedness later on. */ if (zoneid == GLOBAL_ZONEID && cmd != A_DUMP) { - need_check_zones = halt_zones(); + if (!qflag) + need_check_zones = halt_zones(); } #if defined(__x86) @@ -1604,7 +1617,7 @@ main(int argc, char *argv[]) (void) signal(SIGINT, SIG_IGN); - if (!qflag && !nosync) { + if (!nosync) { struct utmpx wtmpx; bzero(&wtmpx, sizeof (struct utmpx)); diff --git a/usr/src/cmd/ibd_upgrade/ibd_delete_link.c b/usr/src/cmd/ibd_upgrade/ibd_delete_link.c index b9d10a56cd..f63630207d 100644 --- a/usr/src/cmd/ibd_upgrade/ibd_delete_link.c +++ b/usr/src/cmd/ibd_upgrade/ibd_delete_link.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent Inc. All rights reserved. */ #include <stdio.h> @@ -86,6 +87,7 @@ ibd_delete_link(dladm_handle_t dlh, char *link) getlinkid.ld_cmd = DLMGMT_CMD_GETLINKID; (void) strlcpy(getlinkid.ld_link, link, MAXLINKNAMELEN); + getlinkid.ld_zoneid = -1; if ((status = ibd_dladm_door_call(dlh, &getlinkid, sizeof (getlinkid), &retval, sizeof (retval))) != DLADM_STATUS_OK) { diff --git a/usr/src/cmd/init/init.c b/usr/src/cmd/init/init.c index 460fd42036..86f717d54a 100644 --- a/usr/src/cmd/init/init.c +++ b/usr/src/cmd/init/init.c @@ -24,6 +24,7 @@ * Copyright (c) 2013 Gary Mills * * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2020 Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -146,6 +147,8 @@ #define UT_USER_SZ 32 /* Size of a utmpx ut_user field */ #define UT_LINE_SZ 32 /* Size of a utmpx ut_line field */ +#define CHECK_SVC SCF_INSTANCE_FS_MINIMAL + /* * SLEEPTIME The number of seconds "init" sleeps between wakeups if * nothing else requires this "init" wakeup. @@ -550,6 +553,8 @@ static time_t init_boot_time; /* Substitute for kernel boot time. */ #define NSTARTD_FAILURE_TIMES 3 /* trigger after 3 failures */ #define STARTD_FAILURE_RATE_NS 5000000000LL /* 1 failure/5 seconds */ +#define STARTD_THROTTLE_RETRY 60 /* space failure retry after 60 secs */ +#define ROOT_MIN_FREE 524288 /* 512KB min. space needed in root */ static hrtime_t startd_failure_time[NSTARTD_FAILURE_TIMES]; static uint_t startd_failure_index; @@ -695,9 +700,7 @@ main(int argc, char *argv[]) console(B_FALSE, "\n\n%s Release %s Version %s %s-bit\r\n", un.sysname, un.release, un.version, bits); - console(B_FALSE, - "Copyright (c) 1983, 2010, Oracle and/or its affiliates." - " All rights reserved.\r\n"); + console(B_FALSE, "Copyright 2010-2020 Joyent, Inc.\r\n"); #else bootbanner_print(init_bootbanner_print, 0); #endif @@ -3519,6 +3522,28 @@ bail: } /* + * Attempt to confirm that svc.startd is ready to accept a user-initiated + * run-level change. startd is not ready until it has started its + * _scf_notify_wait thread to watch for events from svc.configd. This is + * inherently racy. To workaround this, we check the status of a file that + * startd will create once it has started the _scf_notify_wait thread. + * If we don't see this file after one minute, then charge ahead. + */ +static void +verify_startd_ready() +{ + struct stat64 buf; + int i; + + for (i = 0; i < 60; i++) { + if (stat64("/etc/svc/volatile/startd.ready", &buf) == 0) + return; + sleep(1); + } + console(B_TRUE, "verify startd timeout\n"); +} + +/* * Function to handle requests from users to main init running as process 1. */ static void @@ -3606,6 +3631,12 @@ userinit(int argc, char **argv) (void) audit_put_record(ADT_SUCCESS, ADT_SUCCESS, argv[1]); /* + * Before we tell init to start a run-level change, we need to be + * sure svc.startd is ready to accept that. + */ + verify_startd_ready(); + + /* * Signal init; init will take care of telling svc.startd. */ if (kill(init_pid, init_signal) == FAILURE) { @@ -4315,9 +4346,7 @@ contract_event(struct pollfd *poll) if (ret == 0) { if (cookie == STARTD_COOKIE && do_restart_startd) { - if (smf_debug) - console(B_TRUE, "Restarting " - "svc.startd.\n"); + console(B_TRUE, "Restarting svc.startd.\n"); /* * Account for the failure. If the failure rate @@ -4448,6 +4477,28 @@ startd_run(const char *cline, int tmpl, ctid_t old_ctid) if (pid == 0) { /* child */ + struct statvfs64 sbuf; + + /* + * svc.configd needs some space (a few hundred KB) in / for its + * database. One common cause for startd failure is when + * configd dies because / is full. We don't want to go into the + * fast restart loop (startd_failure_rate_critical) and enter + * maintenance so we check for this case and slow down the + * failure rate so as to keep retrying in the hope space will + * free up. + */ + if (statvfs64("/", &sbuf) != -1 && + (sbuf.f_bsize * sbuf.f_bfree) < ROOT_MIN_FREE) { + syslog(LOG_ERR, "Insufficent space (%ld) in / to " + "start svc.startd.\n", + (long)(sbuf.f_bsize * sbuf.f_bfree)); + console(B_TRUE, "Insufficent space (%ld) in / to " + "start svc.startd.\n", + (long)(sbuf.f_bsize * sbuf.f_bfree)); + sleep(STARTD_THROTTLE_RETRY); + exit(1); + } /* See the comment in efork() */ for (i = SIGHUP; i <= SIGRTMAX; ++i) { diff --git a/usr/src/cmd/init/init.dfl b/usr/src/cmd/init/init.dfl index 371b48becc..150f720b1c 100644 --- a/usr/src/cmd/init/init.dfl +++ b/usr/src/cmd/init/init.dfl @@ -29,5 +29,6 @@ # TZ, LANG, CMASK, or any of the LC_* environment variables. value may # be enclosed in double quotes (") or single quotes ('). # -TZ=PST8PDT +TZ=UTC CMASK=022 +LANG=en_US.UTF-8 diff --git a/usr/src/cmd/initpkg/mountall.sh b/usr/src/cmd/initpkg/mountall.sh index fa59a20a41..2302c7592c 100644 --- a/usr/src/cmd/initpkg/mountall.sh +++ b/usr/src/cmd/initpkg/mountall.sh @@ -29,6 +29,8 @@ # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# usage () { if [ -n "$1" ]; then @@ -149,6 +151,9 @@ isremote() { # Get list of remote FS types (just once) RemoteFSTypes=`while read t junk; do echo $t; done < /etc/dfs/fstypes` +# Ensure nfs/smbfs are remote FS types even if not delivered from fstypes +isremote "nfs" || set -A RemoteFSTypes "nfs" +isremote "smbfs" || set -A RemoteFSTypes "smbfs" # # Process command line args diff --git a/usr/src/cmd/initpkg/shutdown.sh b/usr/src/cmd/initpkg/shutdown.sh index a90213cd81..e93de5568c 100644 --- a/usr/src/cmd/initpkg/shutdown.sh +++ b/usr/src/cmd/initpkg/shutdown.sh @@ -43,7 +43,7 @@ usage() { } notify() { - /usr/sbin/wall -a <<-! + /usr/sbin/wall -Za <<-! $* ! # We used to do rwall here if showmounts had any output, but diff --git a/usr/src/cmd/initpkg/umountall.sh b/usr/src/cmd/initpkg/umountall.sh index c9a94fd8f1..4a45e19e18 100644 --- a/usr/src/cmd/initpkg/umountall.sh +++ b/usr/src/cmd/initpkg/umountall.sh @@ -25,6 +25,7 @@ # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # +# Copyright (c) 2012, Joyent, Inc. All rights reserved. # usage () { @@ -98,6 +99,9 @@ isremote() { # Get list of remote FS types (just once) RemoteFSTypes=`while read t junk; do echo $t; done < /etc/dfs/fstypes` +# Ensure nfs/smbfs are remote FS types even if not delivered from fstypes +isremote "nfs" || set -A RemoteFSTypes "nfs" +isremote "smbfs" || set -A RemoteFSTypes "smbfs" # # Process command line args diff --git a/usr/src/cmd/ipf/etc/Makefile b/usr/src/cmd/ipf/etc/Makefile index 3e06187ae9..a5eb399f56 100644 --- a/usr/src/cmd/ipf/etc/Makefile +++ b/usr/src/cmd/ipf/etc/Makefile @@ -22,42 +22,31 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -#cmd/ipf/etc/Makefile +# Copyright 2019 Joyent, Inc. # -IPFCONF= ipf.conf -IPFPROG= $(IPFCONF) +IPFFILES = ipf.conf smartos_version include ../../Makefile.cmd - ETCIPF= $(ROOTETC)/ipf -DIRS= $(ETCIPF) - -ETCIPFPROG= $(IPFPROG:%=$(ETCIPF)/%) +ROOTETCIPFFILES = $(IPFFILES:%=$(ETCIPF)/%) -$(ETCIPFPROG):= FILEMODE= 0644 +$(ETCIPF)/ipf.conf := FILEMODE= 0644 +$(ETCIPF)/smartos_version := FILEMODE= 0444 .KEEP_STATE: -all: $(IPFPROG) $(DIRS) $(ETCIPFPROG) - -install: all $(DIRS) +all: -$(PFILAP): - $(SH) $@.sh +install: all $(ROOTETCIPFFILES) -$(ETCIPF)/% : % $(ETCIPF) +$(ETCIPF)/% : % $(INS.file) -$(DIRS): - $(INS.dir) - clean: clobber: -lint: - .PARALLEL: diff --git a/usr/src/cmd/ipf/etc/smartos_version b/usr/src/cmd/ipf/etc/smartos_version new file mode 100644 index 0000000000..0cfbf08886 --- /dev/null +++ b/usr/src/cmd/ipf/etc/smartos_version @@ -0,0 +1 @@ +2 diff --git a/usr/src/cmd/ipf/lib/common/printfr.c b/usr/src/cmd/ipf/lib/common/printfr.c index 063eb87c07..b096c46e0e 100644 --- a/usr/src/cmd/ipf/lib/common/printfr.c +++ b/usr/src/cmd/ipf/lib/common/printfr.c @@ -7,9 +7,10 @@ * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. */ -#pragma ident "%Z%%M% %I% %E% SMI" +#include <uuid/uuid.h> #include "ipf.h" @@ -386,7 +387,8 @@ ioctlfunc_t iocfunc; printf(" head %s", fp->fr_grhead); if (*fp->fr_group != '\0') printf(" group %s", fp->fr_group); - if (fp->fr_logtag != FR_NOLOGTAG || *fp->fr_nattag.ipt_tag) { + if (fp->fr_logtag != FR_NOLOGTAG || *fp->fr_nattag.ipt_tag || + (fp->fr_flags & FR_CFWLOG) || !uuid_is_null(fp->fr_uuid)) { char *s = ""; printf(" set-tag("); @@ -397,6 +399,18 @@ ioctlfunc_t iocfunc; if (*fp->fr_nattag.ipt_tag) { printf("%snat=%-.*s", s, IPFTAG_LEN, fp->fr_nattag.ipt_tag); + s = ", "; + } + if (fp->fr_flags & FR_CFWLOG) { + printf("cfwlog"); + s = ", "; + } + + if (!uuid_is_null(fp->fr_uuid)) { + char uuid[UUID_PRINTABLE_STRING_LENGTH]; + + uuid_unparse(fp->fr_uuid, uuid); + printf("%suuid=%s", s, uuid); } printf(")"); } diff --git a/usr/src/cmd/ipf/svc/ipfilter b/usr/src/cmd/ipf/svc/ipfilter index 48e3e2e915..bb25316b44 100644 --- a/usr/src/cmd/ipf/svc/ipfilter +++ b/usr/src/cmd/ipf/svc/ipfilter @@ -23,9 +23,12 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2013, Joyent, Inc. All rights reserved. # Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org> # +set -o xtrace + . /lib/svc/share/smf_include.sh . /lib/svc/share/ipf_include.sh @@ -152,8 +155,25 @@ upgrade_config() svcadm refresh $SMF_FMRI >/dev/null 2>&1 } +symlink_persistent_file() +{ + persist_file=/var/fw/$1 + etc_file=$ETC_IPF_DIR/$1 + + [ ! -e $persist_file ] && return 0 + [ -L $etc_file ] && return 0 + + [ -e $etc_file ] && mv $etc_file{,.orig} + + ln -s $persist_file $etc_file +} + + configure_firewall() { + symlink_persistent_file ipnat.conf + symlink_persistent_file ipf.conf + symlink_persistent_file ipf6.conf create_global_rules || exit $SMF_EXIT_ERR_CONFIG create_global_ovr_rules || exit $SMF_EXIT_ERR_CONFIG create_services_rules || exit $SMF_EXIT_ERR_CONFIG diff --git a/usr/src/cmd/ipf/svc/ipfilter.xml b/usr/src/cmd/ipf/svc/ipfilter.xml index 5f088f5344..8b6506795f 100644 --- a/usr/src/cmd/ipf/svc/ipfilter.xml +++ b/usr/src/cmd/ipf/svc/ipfilter.xml @@ -4,6 +4,7 @@ Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org> + CDDL HEADER START The contents of this file are subject to the terms of the @@ -97,13 +98,13 @@ type='method' name='refresh' exec='/lib/svc/method/ipfilter reload' - timeout_seconds='120' > + timeout_seconds='1200' > </exec_method> - <instance name='default' enabled='false'> + <instance name='default' enabled='true'> <property_group name='firewall_config_default' type='com.sun,fw_configuration'> - <propval name='policy' type='astring' value='none' /> + <propval name='policy' type='astring' value='custom' /> <propval name='block_policy' type='astring' value='none' /> <propval name='custom_policy_file' type='astring' diff --git a/usr/src/cmd/ipf/tools/Makefile.tools b/usr/src/cmd/ipf/tools/Makefile.tools index 7c1e151762..a89fc63f97 100644 --- a/usr/src/cmd/ipf/tools/Makefile.tools +++ b/usr/src/cmd/ipf/tools/Makefile.tools @@ -23,8 +23,7 @@ # Use is subject to license terms. # # Copyright 2013 Nexenta Systems, Inc. All rights reserved. -# -# Copyright (c) 2012, Joyent Inc. All rights reserved. +# Copyright 2020 Joyent, Inc. # PROG= ipf ipfs ipmon ipnat ippool ipfstat @@ -36,7 +35,7 @@ IPFSTAT_OBJS= ipfstat.o ipfzone.o IPMON_OBJS= ipmon.o ipfzone.o ipmon_y.o ipmon_l.o IPNAT_OBJS= ipnat.o ipfzone.o ipnat_y.o ipnat_l.o IPPOOL_OBJS= ippool.o ipfzone.o ippool_y.o ippool_l.o -IPFTEST_OBJS= ipftest.o ipfzone.o \ +IPFTEST_OBJS= cfw.o ipftest.o ipfzone.o \ ip_fil.o ip_state.o ip_compat.o \ ip_frag.o ip_nat.o ip_nat6.o fil.o \ ip_htable.o ip_lookup.o \ @@ -65,12 +64,12 @@ CPPFLAGS += -I. -DIPFILTER_LOOKUP -DIPFILTER_LOG ipfstat.o := CPPFLAGS += -DSTATETOP ipfstat := LDLIBS += -lcurses -ipf := LDLIBS += -lsocket -lnsl -ipftest := LDLIBS += -lsocket -lnsl -lmd -ipfstat := LDLIBS += -lsocket -lnsl -lkvm -lelf -ipmon := LDLIBS += -lsocket -lnsl -ipnat := LDLIBS += -lsocket -lnsl -lkvm -lelf -ippool := LDLIBS += -lsocket -lnsl -lkvm -lelf +ipf := LDLIBS += -lsocket -lnsl -luuid +ipftest := LDLIBS += -lsocket -lnsl -lmd -luuid +ipfstat := LDLIBS += -lsocket -lnsl -lkvm -lelf -luuid +ipmon := LDLIBS += -lsocket -lnsl -luuid +ipnat := LDLIBS += -lsocket -lnsl -lkvm -lelf -luuid +ippool := LDLIBS += -lsocket -lnsl -lkvm -lelf -luuid CLEANFILES += $(OBJS) CLOBBERFILES += $(IPFPROG) @@ -244,17 +243,15 @@ ippool_l.o: ../ippool_l.c $(COMMONIPF)/netinet/ip_pool.h ../ippool_l.h CLEANFILES += ../ippool_l.c ../ippool_l.h ipftest: $(IPFTEST_OBJS) $(LIBIPF) $(MAPFILE.NGB) - $(LINK.c) $(ZIGNORE) -o ipftest $(IPFTEST_OBJS) $(LDLIBS) + $(LINK.c) $(ZIGNORE) -o ipftest $(IPFTEST_OBJS) $(LDLIBS) $(POST_PROCESS) clean: -$(RM) $(CLEANFILES) -lint: lint_SRCS - # # NOTE: all rules must use relative paths otherwise absolute paths will be -# embedded into the binaries making them false positives and +# embedded into the binaries making them false positives and # reported by wsdiff # diff --git a/usr/src/cmd/ipf/tools/ipf_y.y b/usr/src/cmd/ipf/tools/ipf_y.y index 7689d676c7..c8909b4e92 100644 --- a/usr/src/cmd/ipf/tools/ipf_y.y +++ b/usr/src/cmd/ipf/tools/ipf_y.y @@ -6,6 +6,7 @@ * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. */ #include "ipf.h" @@ -16,6 +17,7 @@ # define _NET_BPF_H_ # include <pcap.h> #endif +#include <uuid/uuid.h> #include "netinet/ip_pool.h" #include "netinet/ip_htable.h" #include "netinet/ipl.h" @@ -98,6 +100,7 @@ static int set_ipv6_addr = 0; union i6addr m; } ipp; union i6addr ip6; + uuid_t uuid; }; %type <port> portnum @@ -117,6 +120,7 @@ static int set_ipv6_addr = 0; %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT %token YY_RANGE_OUT YY_RANGE_IN %token <ip6> YY_IPV6 +%token <uuid> YY_UUID %token IPFY_PASS IPFY_BLOCK IPFY_COUNT IPFY_CALL %token IPFY_RETICMP IPFY_RETRST IPFY_RETICMPASDST @@ -127,6 +131,7 @@ static int set_ipv6_addr = 0; %token IPFY_HEAD IPFY_GROUP %token IPFY_AUTH IPFY_PREAUTH %token IPFY_LOG IPFY_BODY IPFY_FIRST IPFY_LEVEL IPFY_ORBLOCK +%token IPFY_UUID IPFY_CFWLOG %token IPFY_LOGTAG IPFY_MATCHTAG IPFY_SETTAG IPFY_SKIP %token IPFY_FROM IPFY_ALL IPFY_ANY IPFY_BPFV4 IPFY_BPFV6 IPFY_POOL IPFY_HASH %token IPFY_PPS @@ -518,6 +523,8 @@ taginlist: taginspec: logtag |nattag + |uuidtag + |cfwtag ; nattag: IPFY_NAT '=' YY_STR { DOALL(strncpy(fr->fr_nattag.ipt_tag,\ @@ -530,6 +537,12 @@ nattag: IPFY_NAT '=' YY_STR { DOALL(strncpy(fr->fr_nattag.ipt_tag,\ logtag: IPFY_LOG '=' YY_NUMBER { DOALL(fr->fr_logtag = $3;) } ; +cfwtag: IPFY_CFWLOG { DOALL(fr->fr_flags |= FR_CFWLOG;) } + ; + +uuidtag: IPFY_UUID '=' YY_UUID { DOALL(uuid_copy(fr->fr_uuid, $3);) } + ; + settagout: | IPFY_SETTAG '(' tagoutlist ')' ; @@ -542,6 +555,8 @@ tagoutlist: tagoutspec: logtag | nattag + | uuidtag + | cfwtag ; matchtagin: @@ -1566,6 +1581,7 @@ static struct wordtab ipfwords[96] = { { "bpf-v6", IPFY_BPFV6 }, #endif { "call", IPFY_CALL }, + { "cfwlog", IPFY_CFWLOG }, { "code", IPFY_ICMPCODE }, { "count", IPFY_COUNT }, { "dup-to", IPFY_DUPTO }, @@ -1641,6 +1657,7 @@ static struct wordtab ipfwords[96] = { { "to", IPFY_TO }, { "ttl", IPFY_TTL }, { "udp", IPFY_UDP }, + { "uuid", IPFY_UUID }, { "v6hdrs", IPF6_V6HDRS }, { "with", IPFY_WITH }, { NULL, 0 } diff --git a/usr/src/cmd/ipf/tools/ipfstat.c b/usr/src/cmd/ipf/tools/ipfstat.c index d72f6ba97a..caa9ff7468 100644 --- a/usr/src/cmd/ipf/tools/ipfstat.c +++ b/usr/src/cmd/ipf/tools/ipfstat.c @@ -165,6 +165,10 @@ static int sort_dstip __P((const void *, const void *)); static int sort_dstpt __P((const void *, const void *)); #endif +#if SOLARIS +#include "ipfzone.h" +#endif + static void usage(name) char *name; diff --git a/usr/src/cmd/ipf/tools/ipmon_y.y b/usr/src/cmd/ipf/tools/ipmon_y.y index e6dda81c35..b4fce4015d 100644 --- a/usr/src/cmd/ipf/tools/ipmon_y.y +++ b/usr/src/cmd/ipf/tools/ipmon_y.y @@ -1,11 +1,14 @@ /* * Copyright (C) 1993-2005 by Darren Reed. * See the IPFILTER.LICENCE file for details on licencing. + * + * Copyright 2019 Joyent, Inc. */ %{ #include "ipf.h" #include <syslog.h> +#include <uuid/uuid.h> #undef OPT_NAT #undef OPT_VERBOSE #include "ipmon_l.h" @@ -42,11 +45,13 @@ static ipmon_action_t *alist = NULL; struct in_addr addr; struct opt *opt; union i6addr ip6; + uuid_t uuid; } %token <num> YY_NUMBER YY_HEX %token <str> YY_STR %token <ip6> YY_IPV6 +%token <uuid> YY_UUID %token YY_COMMENT %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT %token YY_RANGE_OUT YY_RANGE_IN diff --git a/usr/src/cmd/ipf/tools/ipnat_y.y b/usr/src/cmd/ipf/tools/ipnat_y.y index d929bf413a..2c913afea2 100644 --- a/usr/src/cmd/ipf/tools/ipnat_y.y +++ b/usr/src/cmd/ipf/tools/ipnat_y.y @@ -6,6 +6,7 @@ * * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -39,6 +40,7 @@ #include <sys/time.h> #include <syslog.h> #include <net/if.h> +#include <uuid/uuid.h> #if __FreeBSD_version >= 300000 # include <net/if_var.h> #endif @@ -89,6 +91,7 @@ static void setnatproto __P((int)); int v; } ipp; union i6addr ip6; + uuid_t uuid; }; %token <num> YY_NUMBER YY_HEX @@ -97,6 +100,7 @@ static void setnatproto __P((int)); %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT %token YY_RANGE_OUT YY_RANGE_IN %token <ip6> YY_IPV6 +%token <uuid> YY_UUID %token IPNY_MAPBLOCK IPNY_RDR IPNY_PORT IPNY_PORTS IPNY_AUTO IPNY_RANGE %token IPNY_MAP IPNY_BIMAP IPNY_FROM IPNY_TO IPNY_MASK IPNY_PORTMAP IPNY_ANY diff --git a/usr/src/cmd/ipf/tools/ippool_y.y b/usr/src/cmd/ipf/tools/ippool_y.y index cca5052bd4..5aadd22206 100644 --- a/usr/src/cmd/ipf/tools/ippool_y.y +++ b/usr/src/cmd/ipf/tools/ippool_y.y @@ -6,6 +6,7 @@ * * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. */ #pragma ident "%Z%%M% %I% %E% SMI" @@ -34,6 +35,7 @@ #include <netdb.h> #include <ctype.h> #include <unistd.h> +#include <uuid/uuid.h> #include "ipf.h" #include "netinet/ip_lookup.h" @@ -66,6 +68,7 @@ static int set_ipv6_addr = 0; iphtent_t *ipe; ip_pool_node_t *ipp; union i6addr ip6; + uuid_t uuid; } %token <num> YY_NUMBER YY_HEX @@ -74,6 +77,7 @@ static int set_ipv6_addr = 0; %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT %token YY_RANGE_OUT YY_RANGE_IN %token <ip6> YY_IPV6 +%token <uuid> YY_UUID %token IPT_IPF IPT_NAT IPT_COUNT IPT_AUTH IPT_IN IPT_OUT %token IPT_TABLE IPT_GROUPMAP IPT_HASH diff --git a/usr/src/cmd/ipf/tools/lexer.c b/usr/src/cmd/ipf/tools/lexer.c index 3db3a0888b..b4ee8b3f77 100644 --- a/usr/src/cmd/ipf/tools/lexer.c +++ b/usr/src/cmd/ipf/tools/lexer.c @@ -5,6 +5,7 @@ * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. */ #include <ctype.h> @@ -14,6 +15,7 @@ #endif #include <sys/ioctl.h> #include <syslog.h> +#include <uuid/uuid.h> #ifdef TEST_LEXER # define NO_YACC union { @@ -21,6 +23,7 @@ union { char *str; struct in_addr ipa; i6addr_t ip6; + uuid_t uuid; } yylval; #endif #include "lexer.h" @@ -455,6 +458,40 @@ nextchar: } #endif + /* + * UUID: e.g., "2426e38c-9f63-c0b8-cfd5-9aaeaf992d42" or its uppercase + * variant. + */ + if (isbuilding == 0 && (ishex(c) || c == '-')) { + char uuidbuf[UUID_PRINTABLE_STRING_LENGTH], *s, oc; + int start; + + start = yypos; + s = uuidbuf; + oc = c; + + /* + * Don't worry about exact position of hexdigits and hyphens + * because uuid_parse() will provide the sanity check. + */ + do { + *s++ = c; + c = yygetc(1); + } while ((ishex(c) || c == '-') && + (s - uuidbuf < sizeof (uuidbuf))); + yyunputc(c); + *s = '\0'; + + if (uuid_parse(uuidbuf, yylval.uuid) == 0) { + rval = YY_UUID; + yyexpectaddr = 0; + goto done; + } + yypos = start; + c = oc; + } + + if (c == ':') { if (isbuilding == 1) { yyunputc(c); diff --git a/usr/src/cmd/ipf/tools/lexer.h b/usr/src/cmd/ipf/tools/lexer.h index a296cb0bc3..448b3e6ffd 100644 --- a/usr/src/cmd/ipf/tools/lexer.h +++ b/usr/src/cmd/ipf/tools/lexer.h @@ -1,4 +1,6 @@ - +/* + * Copyright 2019 Joyent, Inc. + */ typedef struct wordtab { char *w_word; int w_value; @@ -16,6 +18,7 @@ typedef struct wordtab { #define YY_IPV6 1008 #define YY_STR 1009 #define YY_IPADDR 1010 +#define YY_UUID 1011 #endif #define YYBUFSIZ 8192 diff --git a/usr/src/cmd/iscsid/iscsi-initiator b/usr/src/cmd/iscsid/iscsi-initiator index 48b97f0f35..39d983ffff 100644 --- a/usr/src/cmd/iscsid/iscsi-initiator +++ b/usr/src/cmd/iscsid/iscsi-initiator @@ -22,6 +22,7 @@ # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2020 Joyent, Inc. # # @@ -170,6 +171,25 @@ mount_iscsi() { } umount_iscsi () { + # In case any non-system zpools reside on iscsi drives, export + # all of them now so that the system won't hang later in the shutdown + # sequence when those zpools need to be unmounted. + SYS_POOL=`svcprop -p config/zpool svc:/system/smartdc/init:default \ + 2>/dev/null` + SYS_POOL=${SYS_POOL:-zones} + + zpools=$(zpool list -Ho name) + for pool in $zpools; do + if [ "$pool" == $SYS_POOL ]; then + continue + fi + zpool export $pool + got=$? + if [ $got -ne 0 ]; then + echo "Exporting zpool $pool failed ($got)" + fi + done + # # Generate iscsi mountp list from /etc/vfstab exec < /etc/vfstab diff --git a/usr/src/cmd/iscsid/iscsi-initiator.xml b/usr/src/cmd/iscsid/iscsi-initiator.xml index 42e0f21384..5f74945ac6 100644 --- a/usr/src/cmd/iscsid/iscsi-initiator.xml +++ b/usr/src/cmd/iscsid/iscsi-initiator.xml @@ -23,6 +23,7 @@ CDDL HEADER END Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + Copyright 2020 Joyent, Inc. Service manifests for the iSCSI Initiator --> @@ -98,6 +99,20 @@ potential to specialize all the properties/methods. <service_fmri value='svc:/network/loopback' /> </dependency> + <!-- + We declare a dependency on the optional iscsi-target svc so that it + will not shutdown before the initiator svc. In this way we can use + targets on the same machine and not see a failure or hang when we're + exporting any zpools that might be using local targets. + --> + <dependency + name='iscsi-target' + grouping='optional_all' + restart_on='error' + type='service'> + <service_fmri value='svc:/network/iscsi/target' /> + </dependency> + <dependent name='iscsi-initiator_multi-user' grouping='optional_all' @@ -136,7 +151,7 @@ potential to specialize all the properties/methods. <method_credential user='root' group='root' - privileges='basic,sys_devices,sys_mount' + privileges='basic,sys_config,sys_devices,sys_mount' /> </method_context> </exec_method> diff --git a/usr/src/cmd/ksh/Makefile.com b/usr/src/cmd/ksh/Makefile.com index 9f6b431e1f..b1aef300da 100644 --- a/usr/src/cmd/ksh/Makefile.com +++ b/usr/src/cmd/ksh/Makefile.com @@ -37,11 +37,13 @@ LIBSHELLBASE=../../../lib/libshell LIBSHELLSRC=$(LIBSHELLBASE)/common/sh SRCS= $(OBJECTS:%.o=$(LIBSHELLSRC)/%.c) +OBJS= $(OBJECTS) LDLIBS += -lshell # Set common AST build flags (e.g., needed to support the math stuff). include ../../../Makefile.ast +include ../../Makefile.ctf # 1. Make sure that the -D/-U defines in CFLAGS below are in sync # with usr/src/lib/libshell/Makefile.com diff --git a/usr/src/cmd/localedef/Makefile b/usr/src/cmd/localedef/Makefile index 358f63de01..8a8e5dc2e0 100644 --- a/usr/src/cmd/localedef/Makefile +++ b/usr/src/cmd/localedef/Makefile @@ -13,7 +13,7 @@ # Copyright 2017 Nexenta Systems, Inc. # Copyright 2011 EveryCity Ltd. All rights reserved. # Copyright 2013 DEY Storage Systems, Inc. -# Copyright 2016 Joyent, Inc. +# Copyright 2019 Joyent, Inc. # Copyright 2017 RackTop Systems. # @@ -23,6 +23,10 @@ include $(SRC)/cmd/localedef/Makefile.common LDLIBS += -lavl -lgen +I18NEXTFILE = $(ROOTI18NEXT)/UTF-8.x + +$(I18NEXTFILE) := FILEMODE = 0444 + .KEEP_STATE: all: $(PROG) @@ -43,9 +47,7 @@ $(POFILE): $(PIFILES) $(SED) -e '/domain/d' messages.po > $@ $(RM) $(PIFILES) messages.po -install: all $(ROOTPROG) - -lint: lint_SRCS +install: all $(ROOTPROG) $(I18NEXTFILE) clean: $(RM) $(CLEANFILES) diff --git a/usr/src/cmd/localedef/UTF-8.x b/usr/src/cmd/localedef/UTF-8.x new file mode 100644 index 0000000000..b7ab359bd4 --- /dev/null +++ b/usr/src/cmd/localedef/UTF-8.x @@ -0,0 +1,114 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 1996-1997, 2000-2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# Method file for Solaris Unicode locales. +# +# +#ident "@(#)UTF-8.x 1.4 03/08/19 SMI" +# + +METHODS + +# +# Encoding definitions to use UTF-8 (MB) and UTF-32 (WC): +file_code utf8 +process_code ucs4 + +# +# We use the following methods from the libc: +iswctype@native "__iswctype_std" "libc" "/usr/lib/" "libc.so.1" +towctrans@native "__towctrans_std" +towlower@native "__towlower_std" +towupper@native "__towupper_std" +trwctype "__trwctype_std" +wctrans "__wctrans_std" +wctype "__wctype_std" + +mbsinit "__mbsinit_gen" +mbrlen "__mbrlen_gen" + +strcoll "__strcoll_std" +strxfrm "__strxfrm_std" +wcscoll "__wcscoll_bc" +wcscoll@native "__wcscoll_std" +wcsxfrm "__wcsxfrm_bc" +wcsxfrm@native "__wcsxfrm_std" + +fnmatch "__fnmatch_std" +regcomp "__regcomp_std" +regexec "__regexec_std" +regerror "__regerror_std" +regfree "__regfree_std" + +strfmon "__strfmon_std" + +strftime "__strftime_std" +strptime "__strptime_std" +wcsftime "__wcsftime_std" + +getdate "__getdate_std" + +# +# The methods designated at below are all Unicode locale-specific methods +# coming from the methods_unicode.so.3 shared object: +eucpctowc "__u32_to_dense_u32_utf8" "localelib" "/usr/lib/locale/common" "methods_unicode.so.3" +wctoeucpc "__dense_u32_to_u32_utf8" + +iswctype "__iswctype_bc_utf8" +towctrans "__towctrans_bc_utf8" +towlower "__towlower_bc_utf8" +towupper "__towupper_bc_utf8" + +mbftowc "__mbftowc_dense_utf8" +mbftowc@native "__mbftowc_dense_native_utf8" +fgetwc "__fgetwc_dense_utf8" +fgetwc@native "__fgetwc_dense_native_utf8" +mblen "__mblen_dense_utf8" +mbstowcs "__mbstowcs_dense_utf8" +mbstowcs@native "__mbstowcs_dense_native_utf8" +mbtowc "__mbtowc_dense_utf8" +mbtowc@native "__mbtowc_dense_native_utf8" +wcstombs "__wcstombs_dense_utf8" +wcstombs@native "__wcstombs_dense_native_utf8" +wcswidth "__wcswidth_dense_utf8" +wcswidth@native "__wcswidth_dense_utf8" +wctomb "__wctomb_dense_utf8" +wctomb@native "__wctomb_dense_native_utf8" +wcwidth "__wcwidth_dense_utf8" +wcwidth@native "__wcwidth_dense_utf8" + +btowc "__btowc_dense_utf8" +btowc@native "__btowc_dense_utf8" +wctob "__wctob_dense_utf8" +wctob@native "__wctob_dense_utf8" +mbrtowc "__mbrtowc_dense_utf8" +mbrtowc@native "__mbrtowc_dense_native_utf8" +wcrtomb "__wcrtomb_dense_utf8" +wcrtomb@native "__wcrtomb_dense_native_utf8" +mbsrtowcs "__mbsrtowcs_dense_utf8" +mbsrtowcs@native "__mbsrtowcs_dense_native_utf8" +wcsrtombs "__wcsrtombs_dense_utf8" +wcsrtombs@native "__wcsrtombs_dense_native_utf8" + +END METHODS diff --git a/usr/src/cmd/lofiadm/main.c b/usr/src/cmd/lofiadm/main.c index 4a4ee348c0..6ed5b49050 100644 --- a/usr/src/cmd/lofiadm/main.c +++ b/usr/src/cmd/lofiadm/main.c @@ -412,7 +412,8 @@ out: * DO NOT use this function if the filename is actually the device name. */ static int -lofi_map_file(int lfd, struct lofi_ioctl *li, const char *filename) +lofi_map_file(int lfd, struct lofi_ioctl *li, const char *filename, + boolean_t no_devlink_flag) { int minor; @@ -425,7 +426,8 @@ lofi_map_file(int lfd, struct lofi_ioctl *li, const char *filename) "unsupported")); die(gettext("could not map file %s"), filename); } - wait_until_dev_complete(li); + if (!no_devlink_flag) + wait_until_dev_complete(li); return (minor); } @@ -436,7 +438,7 @@ lofi_map_file(int lfd, struct lofi_ioctl *li, const char *filename) static void add_mapping(int lfd, const char *devicename, const char *filename, mech_alias_t *cipher, const char *rkey, size_t rksz, boolean_t rdonly, - boolean_t label) + boolean_t label, boolean_t no_devlink_flag) { struct lofi_ioctl li; @@ -475,7 +477,7 @@ add_mapping(int lfd, const char *devicename, const char *filename, char path[MAXPATHLEN]; /* pick one via the driver */ - minor = lofi_map_file(lfd, &li, filename); + minor = lofi_map_file(lfd, &li, filename, no_devlink_flag); if (minor > 0) { make_blkdevname(&li, path, sizeof (path)); @@ -500,7 +502,8 @@ add_mapping(int lfd, const char *devicename, const char *filename, die(gettext("could not map file %s to %s"), filename, devicename); } - wait_until_dev_complete(&li); + if (!no_devlink_flag) + wait_until_dev_complete(&li); } /* @@ -1393,7 +1396,7 @@ lofi_uncompress(int lfd, const char *filename) if (statbuf.st_size == 0) return; - minor = lofi_map_file(lfd, &li, filename); + minor = lofi_map_file(lfd, &li, filename, B_FALSE); (void) snprintf(devicename, sizeof (devicename), "/dev/%s/%d", LOFI_BLOCK_NAME, minor); @@ -1935,6 +1938,7 @@ main(int argc, char *argv[]) boolean_t ephflag = B_FALSE; boolean_t compressflag = B_FALSE; boolean_t uncompressflag = B_FALSE; + boolean_t no_devlink_flag = B_FALSE; /* the next two work together for -c, -k, -T, -e options only */ boolean_t need_crypto = B_FALSE; /* if any -c, -k, -T, -e */ boolean_t cipher_only = B_TRUE; /* if -c only */ @@ -1950,7 +1954,7 @@ main(int argc, char *argv[]) (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); - while ((c = getopt(argc, argv, "a:c:Cd:efk:lrs:T:U")) != EOF) { + while ((c = getopt(argc, argv, "a:c:Cd:efk:lrs:T:UX")) != EOF) { switch (c) { case 'a': addflag = B_TRUE; @@ -2031,6 +2035,13 @@ main(int argc, char *argv[]) case 'U': uncompressflag = B_TRUE; break; + case 'X': + /* + * Private flag to skip the wait for the /dev links to + * be created. + */ + no_devlink_flag = B_TRUE; + break; case '?': default: errflag = B_TRUE; @@ -2163,7 +2174,7 @@ main(int argc, char *argv[]) */ if (addflag) add_mapping(lfd, devicename, filename, cipher, rkey, rksz, - rdflag, labelflag); + rdflag, labelflag, no_devlink_flag); else if (compressflag) lofi_compress(&lfd, filename, compress_index, segsize); else if (uncompressflag) diff --git a/usr/src/cmd/logadm/Makefile b/usr/src/cmd/logadm/Makefile index b9ee0560ea..7b959c0dba 100644 --- a/usr/src/cmd/logadm/Makefile +++ b/usr/src/cmd/logadm/Makefile @@ -20,6 +20,7 @@ # # # Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2019 Joyent, Inc. # PROG= logadm @@ -36,6 +37,7 @@ CPPFLAGS += -D_FILE_OFFSET_BITS=64 CERRWARN += -_gcc=-Wno-parentheses CERRWARN += -_gcc=-Wno-clobbered CERRWARN += $(CNOWARN_UNINIT) +CERRWARN += -_gcc=-Wno-unused-label XGETFLAGS += -a -x logadm.xcl $(ROOTETC)/$(CONFIGFILE):= FILEMODE= 644 diff --git a/usr/src/cmd/logadm/conf.c b/usr/src/cmd/logadm/conf.c index 25bcc4f63d..93837fbebf 100644 --- a/usr/src/cmd/logadm/conf.c +++ b/usr/src/cmd/logadm/conf.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013, Joyent, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. * Copyright 2018 Sebastian Wiedenroth */ @@ -279,8 +279,6 @@ conf_scan(const char *fname, char *buf, int buflen, int timescan) if (SETJMP) { err(EF_FILE, "cannot process invalid entry %s", entry); - ret = 0; - LOCAL_ERR_BREAK; } if (timescan) { @@ -529,7 +527,6 @@ conf_close(struct opts *opts) (void) unlink(tuname); err(EF_JMP, "unsafe to update configuration file " "or timestamps"); - return; } /* rename updated files into place */ diff --git a/usr/src/cmd/logadm/glob.c b/usr/src/cmd/logadm/glob.c index 338a1c6b9f..0978c876e9 100644 --- a/usr/src/cmd/logadm/glob.c +++ b/usr/src/cmd/logadm/glob.c @@ -22,6 +22,8 @@ * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * + * Copyright 2019 Joyent, Inc. + * * logadm/glob.c -- globbing routines * * these routines support two kinds of globs. first, the @@ -62,8 +64,6 @@ * braces, and don't support the more powerful reglobs required by logadm. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <libintl.h> #include <stdlib.h> @@ -100,8 +100,6 @@ glob_debrace(struct fn *fnp) while (sp != NULL && (left = strchr(sp, '{')) != NULL) if ((right = strchr(left, '}')) == NULL) { err(EF_FILE|EF_JMP, "Missing }"); - fn_list_free(ret); - return (NULL); } else { /* stuff before "left" is finished */ fn_list_appendrange(ret, sp, left); diff --git a/usr/src/cmd/logadm/logadm.conf b/usr/src/cmd/logadm/logadm.conf index 7b19473a19..39eea57578 100644 --- a/usr/src/cmd/logadm/logadm.conf +++ b/usr/src/cmd/logadm/logadm.conf @@ -19,6 +19,7 @@ # CDDL HEADER END # # Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, Joyent, Inc. # # logadm.conf # @@ -35,12 +36,18 @@ # logadm typically runs early every morning via an entry in # root's crontab (see crontab(1)). # -/var/log/syslog -C 8 -a 'kill -HUP `cat /var/run/syslog.pid`' -/var/adm/messages -C 4 -a 'kill -HUP `cat /var/run/syslog.pid`' +/var/log/syslog -C 8 -a 'kill -HUP `cat /var/run/*syslog*.pid`' +/var/adm/messages -C 4 -a 'kill -HUP `cat /var/run/*syslog*.pid`' /var/cron/log -c -s 512k -t /var/cron/olog /var/lp/logs/lpsched -C 2 -N -t '$file.$N' /var/fm/fmd/errlog -N -s 2m -M '/usr/sbin/fmadm -q rotate errlog && mv /var/fm/fmd/errlog.0- $nfile' /var/fm/fmd/fltlog -N -A 6m -s 10m -M '/usr/sbin/fmadm -q rotate fltlog && mv /var/fm/fmd/fltlog.0- $nfile' +vm_logs /var/log/vm/vmadm.log -b '/usr/vm/sbin/rotate-logs.sh -m /var/log/vm/logs/ /var/log/vm/vmadm.log' -t '/var/log/vm/vmadm_$nodename_%FT%H:%M:%S.log' -C 168 -S 1g -p 1h +fw_logs /var/log/fw/fwadm.log -b '/usr/vm/sbin/rotate-logs.sh -i /var/log/fw/logs/ /var/log/fw/fwadm.log' -t '/var/log/fw/fwadm_$nodename_%FT%H:%M:%S.log' -C 168 -S 1g -p 1h +vmadmd_logs /var/svc/log/*vmadmd*.log -C 168 -S 1g -c -p 1h -t '/var/log/vm/vmadmd_$nodename_%FT%H:%M:%S.log' +vminfod_logs /var/svc/log/*vminfod*.log -C 168 -S 1g -c -p 1h -t '/var/log/vm/vminfod_$nodename_%FT%H:%M:%S.log' +/var/log/*.log -C 2 -s 5m -c +/var/log/*.debug -C 2 -s 5m -c smf_logs /var/svc/log/*.log -C 8 -s 1m -c # # The entry below is used by turnacct(1M) diff --git a/usr/src/cmd/logadm/main.c b/usr/src/cmd/logadm/main.c index 624397d41f..15f71be166 100644 --- a/usr/src/cmd/logadm/main.c +++ b/usr/src/cmd/logadm/main.c @@ -603,17 +603,14 @@ rotatelog(struct fn *fnp, struct opts *opts) if (opts_count(opts, "N")) return (1); err(EF_WARN|EF_SYS, "%s", fname); - return (B_FALSE); } if ((stbuf.st_mode & S_IFMT) == S_IFLNK) { err(EF_WARN, "%s is a symlink", fname); - return (B_FALSE); } if ((stbuf.st_mode & S_IFMT) != S_IFREG) { err(EF_WARN, "%s is not a regular file", fname); - return (B_FALSE); } /* even if size condition is not met, this entry is "done" */ @@ -1074,7 +1071,6 @@ docmd(struct opts *opts, const char *msg, const char *cmd, (arg2) ? arg2 : "", (arg3) ? " " : "", (arg3) ? arg3 : ""); - first = B_FALSE; } err_fromfd(pfd.fd); } @@ -1084,7 +1080,6 @@ docmd(struct opts *opts, const char *msg, const char *cmd, } if (waitpid(pid, &wstat, 0) < 0) { err(EF_SYS, "waitpid"); - return; } if (!first) { @@ -1149,20 +1144,15 @@ docopytruncate(struct opts *opts, const char *file, const char *file_copy) /* open log file to be rotated and remember its chmod mask */ if ((fi = open(file, O_RDWR)) < 0) { err(EF_SYS, "cannot open file %s", file); - return; } if (fstat(fi, &s) < 0) { err(EF_SYS, "cannot access: %s", file); - (void) close(fi); - return; } /* create new file for copy destination with correct attributes */ if ((fo = open(file_copy, O_CREAT|O_TRUNC|O_WRONLY, s.st_mode)) < 0) { err(EF_SYS, "cannot create file: %s", file_copy); - (void) close(fi); - return; } (void) fchown(fo, s.st_uid, s.st_gid); @@ -1188,10 +1178,6 @@ docopytruncate(struct opts *opts, const char *file, const char *file_copy) do { if (fstat(fi, &s) < 0) { err(EF_SYS, "cannot stat: %s", file); - (void) close(fi); - (void) close(fo); - (void) remove(file_copy); - return; } if ((rem = s.st_size - written) < thresh) { @@ -1231,10 +1217,6 @@ docopytruncate(struct opts *opts, const char *file, const char *file_copy) } err(EF_SYS, "cannot write into file %s", file_copy); - (void) close(fi); - (void) close(fo); - (void) remove(file_copy); - return; } } while (len >= 0); @@ -1249,12 +1231,6 @@ docopytruncate(struct opts *opts, const char *file, const char *file_copy) while ((len = read(fi, buf, sizeof (buf))) > 0) if (write(fo, buf, len) != len) { err(EF_SYS, "cannot write into file %s", file_copy); - (void) lockf(fi, F_ULOCK, 0); - (void) fchmod(fi, s.st_mode); - (void) close(fi); - (void) close(fo); - (void) remove(file_copy); - return; } (void) ftruncate(fi, 0); diff --git a/usr/src/cmd/login/login.dfl b/usr/src/cmd/login/login.dfl index 13ecd51113..4515dfcc6a 100644 --- a/usr/src/cmd/login/login.dfl +++ b/usr/src/cmd/login/login.dfl @@ -36,7 +36,7 @@ # any of the currently enabled /dev/vt/# virtual terminal devices. # Comment this line out to allow remote login by root. # -CONSOLE=/dev/console +#CONSOLE=/dev/console # PASSREQ determines if login requires a password. # @@ -47,19 +47,16 @@ PASSREQ=YES ALTSHELL=YES # PATH sets the initial shell PATH variable -# sample with GNU tools in front of the path -# PATH=/usr/gnu/bin:/usr/bin:/usr/sbin:/sbin -# sample with XPG4 tools in front of the path -# PATH=/usr/xpg4/bin:/usr/bin:/usr/sbin:/sbin -PATH=/usr/bin:/usr/sbin:/sbin:/usr/gnu/bin +# +#PATH=/usr/bin: # SUPATH sets the initial shell PATH variable for root # -SUPATH=/usr/sbin:/sbin:/usr/bin +#SUPATH=/usr/sbin:/usr/bin # TIMEOUT sets the number of seconds (between 0 and 900) to wait before # abandoning a login session. -# +# #TIMEOUT=300 # UMASK sets the initial shell file creation mode mask. See umask(1). @@ -80,9 +77,9 @@ SYSLOG=YES #SLEEPTIME=4 # DISABLETIME If present, and greater than zero, the number of seconds -# login will wait after RETRIES failed attempts or the PAM framework returns +# login will wait after RETRIES failed attempts or the PAM framework returns # PAM_ABORT. Default is 20. Minimum is 0. No maximum is imposed. -# +# #DISABLETIME=20 # RETRIES determines the number of failed logins that will be diff --git a/usr/src/cmd/machid/Makefile b/usr/src/cmd/machid/Makefile new file mode 100644 index 0000000000..6a3c7138dc --- /dev/null +++ b/usr/src/cmd/machid/Makefile @@ -0,0 +1,80 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +PROG= machid + +include ../Makefile.cmd + +# +# List of all links present on all architectures and machines. +# +# Note that this function is obsolesent and we don't generally +# add to this list (see psarc/1992/171). +# +FIRSTLINK = i286 +LINKS = i386 i486 i860 i86pc iAPX286 \ + m68k mc68000 mc68010 mc68020 mc68030 mc68040 \ + sparc sun sun2 sun3 sun3x sun4 sun4c sun4m sun4d sun4e \ + u370 u3b u3b15 u3b2 u3b5 vax pdp11 + +ROOTFIRSTLINK = $(ROOTBIN)/$(FIRSTLINK) +ROOTLINKS = $(LINKS:%=$(ROOTBIN)/%) + +# +# Install the program as the first machine in the list. +# +INSTALLIT= $(INS.link) +$(ROOTFIRSTLINK):= INSTALLIT = $(INS.rename) +$(ROOTLINKS):= INSLINKTARGET = $(ROOTFIRSTLINK) + +$(ROOTLINKS): $(ROOTFIRSTLINK) + +# +# Link installation rules +# +$(ROOTBIN)/%: $(PROG) + $(INSTALLIT) + +$(ROOTFIRSTLINK): $(ROOTBIN) + +$(ROOTBIN): + $(INS.dir) + +CFLAGS += $(CCVERBOSE) + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTLINKS) + +clean: + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/machid/machid.c b/usr/src/cmd/machid/machid.c new file mode 100644 index 0000000000..c3d57b91f9 --- /dev/null +++ b/usr/src/cmd/machid/machid.c @@ -0,0 +1,137 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1993-1994, by Sun Microsystems, Inc. + */ + +/* + * This program replicates the function of the links from a machine name + * (such as sun4c) through /usr/kvm to true or false as appropriate. It + * knows the correct special cases. + * + * IMPORTANT NOTE: + * + * Do not modify this program to know about additional special cases or + * reflect new platforms or instruction set architectures. This is a + * deprecated interface and strictly for backwards compatibility. This + * is psarc/1992/171. Note the following excerpt from the opinion: + * + * It is most important to note that the manual page states in + * the NOTES section: "The machid family of commands is + * obsolete. Use uname -p and uname -m instead." + * + * The intent of Kernel Architecture Project team is to provide + * only enough functionality to mimic the existing definitions + * on the SPARC and Intel x86 versions of Solaris 2.x. No new + * identifiers will ever be added to the documented and + * undocumented identifiers listed above. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <sys/systeminfo.h> + +static char static_buf[SYS_NMLN]; +static char *progname; + +static void get_info_item(int command, char **buf, long *count); + +/* ARGSUSED */ +int +main(int argc, char *argv[], char *envp[]) +{ + char *buf = &static_buf[0]; + long buflen = SYS_NMLN; + + if ((progname = strrchr(argv[0], '/')) == NULL) + progname = argv[0]; + else + progname++; + + /* + * First possible match is on the processor type. + * + * Special case for architectures: i386 matches i486 and visa versa. + */ + get_info_item(SI_ARCHITECTURE, &buf, &buflen); + if (strcmp(buf, progname) == 0) + return (0); + if ((strcmp(buf, "i386") == 0 && strcmp(progname, "i486") == 0) || + (strcmp(buf, "i486") == 0 && strcmp(progname, "i386") == 0)) + return (0); + + /* + * Next possible match is the machine, or more exactly, the value + * which would be returned by uname(2) in the machine field or uname(1) + * with the -m option. For historical reasons this is really is + * often a class of platforms which are identical to userland processes + * such as sun4c, sun4m, etc. + */ + get_info_item(SI_MACHINE, &buf, &buflen); + if (strcmp(buf, progname) == 0) + return (0); + + /* + * Finally, match the vendor. We hardwire in one historical match. + */ + get_info_item(SI_HW_PROVIDER, &buf, &buflen); + if (strcmp(buf, progname) == 0) + return (0); + if (strcasecmp(buf, "Sun_Microsystems") == 0 && + strcmp("sun", progname) == 0) + return (0); + + return (255); +} + +/* + * get_info_item is a wrapper around the sysinfo system call. It makes sure + * the buffer is large enough, returning a larger buffer if needed. On + * unrecoverable error, it exits. An error message doesn't help and makes + * this tiny program link stdio and maybe deal with internationalization, + * so the best thing is to die silently. Note that the larger buffer is + * retained for later use. Reality is that the buffer will always be big + * enough, but this is coded to the spec rather than implementation. + */ +static void +get_info_item(int command, char **buf, long *count) +{ + long error; + + error = sysinfo(command, *buf, *count); + if (error > *count) { + *count = error; + if (*buf != static_buf) { + free(*buf); + } + *buf = (char *) malloc(*count); + error = sysinfo(command, *buf, *count); + } + + if (error == -1) + exit(-1); +} diff --git a/usr/src/cmd/man/Makefile.com b/usr/src/cmd/man/Makefile.com new file mode 100644 index 0000000000..8f1b3adf7d --- /dev/null +++ b/usr/src/cmd/man/Makefile.com @@ -0,0 +1,23 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2012 Nexenta Systems, Inc. All rights reserved. +# Copyright 2014 Garrett D'Amore <garrett@damore.org> +# + +PROG= man +LINKS= apropos whatis catman +LIBLINKS = makewhatis +OBJS= makewhatis.o man.o stringlist.o +SRCS= $(OBJS:%.o=%.c) + + diff --git a/usr/src/cmd/mdb/Makefile.common b/usr/src/cmd/mdb/Makefile.common index ff81215380..4fc3f3d317 100644 --- a/usr/src/cmd/mdb/Makefile.common +++ b/usr/src/cmd/mdb/Makefile.common @@ -103,6 +103,7 @@ COMMON_MODULES_KVM = \ stmf_sbd \ ufs \ usba \ + xhci \ zfs CLOSED_COMMON_MODULES_KVM = \ diff --git a/usr/src/cmd/mdb/Makefile.module b/usr/src/cmd/mdb/Makefile.module index b61ed79ad4..ee89bd5a18 100644 --- a/usr/src/cmd/mdb/Makefile.module +++ b/usr/src/cmd/mdb/Makefile.module @@ -104,7 +104,7 @@ LINTFILES_raw = $(LINTOBJS) LINTFILES = $(LINTFILES_$(MDBTGT)) # -# Python specific flags. To try and make life easier for folks how are +# Python specific flags. To try and make life easier for folks who are # building with an LFS python, we attempt to use -isystem when it's # available. # @@ -112,6 +112,12 @@ PYCPPFLAGS = -_gcc=-isystem -_gcc=$(ADJUNCT_PROTO)/usr/include/python$(PYTHON_V PYCPPFLAGS += -_cc=-I$(ADJUNCT_PROTO)/usr/include/python$(PYTHON_VERSION) PYLNFLAGS = -I$(ADJUNCT_PROTO)/usr/include/python$(PYTHON_VERSION) +# +# At this time, we do not have python27 in the adjunct proto area, only +# python26. As such, we explicitly override the python version dmod here. +# +PYTHON_VERSION = 2.6 + kvm_TGTFLAGS = -D_KERNEL proc_TGTFLAGS = -D_USER diff --git a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c index 10c622443a..9943773424 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_cmds.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_cmds.c @@ -26,7 +26,7 @@ /* * Copyright (c) 2012 by Delphix. All rights reserved. - * Copyright (c) 2019 Joyent, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. * Copyright (c) 2013 Josef 'Jeff' Sipek <jeffpc@josefsipek.net> * Copyright (c) 2015, 2017 by Delphix. All rights reserved. * Copyright 2018 OmniOS Community Edition (OmniOSce) Association. @@ -2165,6 +2165,46 @@ cmd_dis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) return (DCMD_OK); } +static void +dis_help(void) +{ + static const char dis_desc[] = +"Disassembles instructions starting at the final argument or the current\n" +"value of dot. If the address is the start of a function, the entire\n" +"function is disassembled, or else a window of instructions before and after\n" +"the disassembled address are displayed.\n" +"\n"; + + static const char dis_opts[] = +" -a Print instruction addresses as numeric values instead of \n" +" symbolic values.\n" +" -b Print instruction addresses as both numeric and symbolic " +"values.\n" +" -f Read instructions from the target's object file instead of the \n" +" target's virtual address space.\n" +" -n instr Display 'instr' instructions before and after the given " +"address.\n" +" -w Force window behavior, even at the start of a function.\n" +"\n"; + + static const char dis_examples[] = +" ::dis\n" +" clock::dis\n" +" ::dis gethrtime\n" +" set_freemem+0x16::dis -n 4\n" +"\n"; + + mdb_printf("%s", dis_desc); + (void) mdb_dec_indent(2); + mdb_printf("%<b>OPTIONS%</b>\n"); + (void) mdb_inc_indent(2); + mdb_printf("%s", dis_opts); + (void) mdb_dec_indent(2); + mdb_printf("%<b>EXAMPLES%</b>\n"); + (void) mdb_inc_indent(2); + (void) mdb_printf("%s", dis_examples); +} + /*ARGSUSED*/ static int walk_step(uintptr_t addr, const void *data, void *private) @@ -3105,7 +3145,8 @@ const mdb_dcmd_t mdb_dcmd_builtins[] = { { "dcmds", "[[-n] pattern]", "list available debugger commands", cmd_dcmds, cmd_dcmds_help }, { "delete", "?[id|all]", "delete traced software events", cmd_delete }, - { "dis", "?[-abfw] [-n cnt] [addr]", "disassemble near addr", cmd_dis }, + { "dis", "?[-abfw] [-n cnt] [addr]", "disassemble near addr", cmd_dis, + dis_help }, { "disasms", NULL, "list available disassemblers", cmd_disasms }, { "dismode", "[mode]", "get/set disassembly mode", cmd_dismode }, { "dmods", "[-l] [mod]", "list loaded debugger modules", cmd_dmods }, diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c index 90cedaea4e..9cc7c6f1a0 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c @@ -838,6 +838,7 @@ static int member_info_cb(const char *name, mdb_ctf_id_t id, ulong_t off, void *data) { mbr_info_t *mbrp = data; + int kind, ret; if (strcmp(name, mbrp->mbr_member) == 0) { if (mbrp->mbr_offp != NULL) @@ -848,7 +849,46 @@ member_info_cb(const char *name, mdb_ctf_id_t id, ulong_t off, void *data) return (1); } - return (0); + /* + * C11 as well as earlier GNU extensions allow an embedded struct + * or union to be unnamed as long as there are no unambiguous member + * names. If we encounter a SOU member with a 0-length name, + * recurse into it and see if any of them match. + */ + if (strlen(name) != 0) + return (0); + + kind = mdb_ctf_type_kind(id); + if (kind == CTF_ERR) + return (-1); + if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) + return (0); + + /* + * Search the unnamed SOU for mbrp->mbr_member, possibly recursing if + * it also contains unnamed members. If the desired member is found. + * *mbrp->mbr_offp will contain the offset of the member relative to + * this unnamed SOU (if the offset was requested) -- i.e. + * we effectively have *mbrp->mbr_offp == offsetof("", member). We want + * unnamed SOUs to act as members of the enclosing SOUs, so we need to + * return the offset as relative to the outer SOU. Since 'off' is + * the offset of the unnamed SOU relative to the enclosing SOU (i.e. + * off == offsetof(outer, "")), we add the two together to produce the + * desired offset. This can recurse as necessary -- the compiler + * prevents any ambiguities from occurring (or else it wouldn't be + * able to compile the code), and the result will be relative to + * the start of the SOU given in the mdb_ctf_member_info() call. + */ + ret = mdb_ctf_member_iter(id, member_info_cb, mbrp); + if (ret == -1) + return (-1); + if (ret == 0) + return (0); + + if (mbrp->mbr_offp != NULL) + *(mbrp->mbr_offp) += off; + + return (1); } int @@ -856,10 +896,21 @@ mdb_ctf_member_info(mdb_ctf_id_t id, const char *member, ulong_t *offp, mdb_ctf_id_t *typep) { mbr_info_t mbr; + /* + * We want the resulting offset (if requested -- offp != NULL) to + * be relative to the start of _this_ SOU. If we have to search any + * embedded unnamed SOUs, instead of merely assigning the resulting + * offset value to mbr_offp, we will have to add the offsets of + * any nested SOUs along the way (see comments in member_info_cb()). + * Therefore, initialize off to 0 here so we do not need to worry about + * recursion depth in member_info_cb (otherwise we would need to set + * mbr_offp when depth = 1, and add when depth > 1). + */ + ulong_t off = 0; int rc; mbr.mbr_member = member; - mbr.mbr_offp = offp; + mbr.mbr_offp = &off; mbr.mbr_typep = typep; rc = mdb_ctf_member_iter(id, member_info_cb, &mbr); @@ -872,6 +923,9 @@ mdb_ctf_member_info(mdb_ctf_id_t id, const char *member, ulong_t *offp, if (rc == 0) return (set_errno(EMDB_CTFNOMEMB)); + if (offp != NULL) + *offp = off; + return (0); } diff --git a/usr/src/cmd/mdb/common/mdb/mdb_main.c b/usr/src/cmd/mdb/common/mdb/mdb_main.c index 8747464328..e1c2ce5fa4 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_main.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_main.c @@ -51,6 +51,7 @@ #include <libctf.h> #include <errno.h> #include <kvm.h> +#include <zone.h> #include <mdb/mdb_lex.h> #include <mdb/mdb_debug.h> @@ -818,9 +819,15 @@ main(int argc, char *argv[], char *envp[]) if (strchr(pidarg, '/') != NULL) (void) mdb_iob_snprintf(object, MAXPATHLEN, "%s/object/a.out", pidarg); - else + else { + const char *root; + (void) mdb_iob_snprintf(object, MAXPATHLEN, - "/proc/%s/object/a.out", pidarg); + "%s/proc/%s/object/a.out", + (root = zone_get_nroot()) != NULL ? root : "", + pidarg); + } + tgt_argv[tgt_argc++] = object; tgt_argv[tgt_argc++] = pidarg; } diff --git a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h index d85bba5e2a..0d6ea3821c 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_modapi.h +++ b/usr/src/cmd/mdb/common/mdb/mdb_modapi.h @@ -80,11 +80,6 @@ extern "C" { #define DCMD_HDRSPEC(fl) (((fl) & DCMD_LOOPFIRST) || !((fl) & DCMD_LOOP)) /* - * Debugger tab command function flags - */ -#define DCMD_TAB_SPACE 0x01 /* Tab cb invoked with trailing space */ - -/* * Debugger command function return values: */ #define DCMD_OK 0 /* Dcmd completed successfully */ @@ -113,10 +108,18 @@ typedef struct mdb_arg { } a_un; } mdb_arg_t; +#if (MDB_API_VERSION >= 4) +/* + * Debugger tab command function flags + */ +#define DCMD_TAB_SPACE 0x01 /* Tab cb invoked with trailing space */ + typedef struct mdb_tab_cookie mdb_tab_cookie_t; -typedef int mdb_dcmd_f(uintptr_t, uint_t, int, const mdb_arg_t *); typedef int mdb_dcmd_tab_f(mdb_tab_cookie_t *, uint_t, int, const mdb_arg_t *); +#endif /* MDB_API_VERSION >= 4 */ + +typedef int mdb_dcmd_f(uintptr_t, uint_t, int, const mdb_arg_t *); typedef struct mdb_dcmd { const char *dc_name; /* Command name */ @@ -124,7 +127,9 @@ typedef struct mdb_dcmd { const char *dc_descr; /* Description */ mdb_dcmd_f *dc_funcp; /* Command function */ void (*dc_help)(void); /* Command help function (or NULL) */ +#if (MDB_API_VERSION >= 4) mdb_dcmd_tab_f *dc_tabp; /* Tab completion function */ +#endif } mdb_dcmd_t; #define WALK_ERR -1 /* Walk fatal error (terminate walk) */ @@ -346,6 +351,7 @@ typedef void (*mdb_callback_f)(void *); extern void *mdb_callback_add(int, mdb_callback_f, void *); extern void mdb_callback_remove(void *); +#if (MDB_API_VERSION >= 4) #define MDB_TABC_ALL_TYPES 0x1 /* Include array types in type output */ #define MDB_TABC_MEMBERS 0x2 /* Tab comp. types with members */ #define MDB_TABC_NOPOINT 0x4 /* Tab comp. everything but pointers */ @@ -370,6 +376,7 @@ extern int mdb_tab_typename(int *, const mdb_arg_t **, char *buf, size_t len); */ extern int mdb_tab_complete_mt(mdb_tab_cookie_t *, uint_t, int, const mdb_arg_t *); +#endif /* MDB_API_VERSION >= 4 */ extern size_t strlcat(char *, const char *, size_t); extern char *strcat(char *, const char *); diff --git a/usr/src/cmd/mdb/common/mdb/mdb_print.c b/usr/src/cmd/mdb/common/mdb/mdb_print.c index 62338f78a2..bd23ef681f 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_print.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_print.c @@ -1699,6 +1699,52 @@ elt_print(const char *name, mdb_ctf_id_t id, mdb_ctf_id_t base, mdb_printf("%s%s", pap->pa_prefix, (depth == 0) ? "" : pap->pa_suffix); mdb_printf("%s", name); + + /* + * When no name is present (i.e. an unnamed struct or union), + * display '(anon)' instead only when no prefix is present. + * When printing out a struct or union (sou), prefixes are + * only present (i.e. !NULL or non-empty) when printing + * individual members of that sou, e.g. + * `::print struct foo f_member`. When printing an entire sou, + * the prefix will be NULL or empty. We end up with: + * + * > ::print struct foo + * { + * ... + * f_member = 0xabcd + * (anon) = { + * anon_member = 0x1234 + * .... + * } + * ... + * } + * + * and + * + * > ::print struct foo anon_member + * anon_member = 0x1234 + * + * instead of: + * + * > ::print struct foo + * { + * ... + * f_member = 0xabcd + * = { + * anon_member = 0x1234 + * } + * ... + * } + * + * and + * + * > ::print struct foo anon_member + * anon_member(anon) = 0x1234 + */ + if (depth > 0 && strlen(name) == 0 && + (pap->pa_prefix == NULL || strlen(pap->pa_prefix) == 0)) + mdb_printf("(anon)"); } if ((pap->pa_flags & PA_SHOWTYPE) && kind == CTF_K_INTEGER) { @@ -1717,8 +1763,9 @@ elt_print(const char *name, mdb_ctf_id_t id, mdb_ctf_id_t base, } if (depth != 0 || - ((pap->pa_flags & PA_SHOWNAME) && pap->pa_prefix != NULL)) + ((pap->pa_flags & PA_SHOWNAME) && pap->pa_prefix != NULL)) { mdb_printf("%s ", pap->pa_flags & PA_SHOWVAL ? " =" : ""); + } if (depth == 0 && pap->pa_prefix != NULL) name = pap->pa_prefix; @@ -2124,10 +2171,10 @@ cmd_print_tab_common(mdb_tab_cookie_t *mcp, uint_t flags, int argc, /* * This is the reason that tab completion was created. We're going to go - * along and walk the delimiters until we find something a member that - * we don't recognize, at which point we'll try and tab complete it. - * Note that ::print takes multiple args, so this is going to operate on - * whatever the last arg that we have is. + * along and walk the delimiters until we find something in a member + * that we don't recognize, at which point we'll try and tab complete + * it. Note that ::print takes multiple args, so this is going to + * operate on whatever the last arg that we have is. */ if (mdb_ctf_lookup_by_name(tn, &id) != 0) return (1); @@ -2137,11 +2184,11 @@ cmd_print_tab_common(mdb_tab_cookie_t *mcp, uint_t flags, int argc, delim = parse_delimiter(&start); /* - * If we hit the case where we actually have no delimiters, than we need + * If we hit the case where we actually have no delimiters, then we need * to make sure that we properly set up the fields the loops would. */ if (delim == MEMBER_DELIM_DONE) - (void) mdb_snprintf(member, sizeof (member), "%s", start); + (void) mdb_snprintf(member, sizeof (member), start); while (delim != MEMBER_DELIM_DONE) { switch (delim) { @@ -2198,7 +2245,7 @@ cmd_print_tab_common(mdb_tab_cookie_t *mcp, uint_t flags, int argc, /* * We are going to try to resolve this name as a member. There - * are a few two different questions that we need to answer. The + * are a two different questions that we need to answer. The * first is do we recognize this member. The second is are we at * the end of the string. If we encounter a member that we don't * recognize before the end, then we have to error out and can't @@ -2228,6 +2275,7 @@ cmd_print_tab_common(mdb_tab_cookie_t *mcp, uint_t flags, int argc, * already have in rid. */ return (mdb_tab_complete_member_by_id(mcp, rid, member)); + } int @@ -2437,8 +2485,10 @@ cmd_print(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) kind = mdb_ctf_type_kind(rid); if (last_deref && IS_SOU(kind)) { char *end; + size_t len = strlen(member); (void) mdb_snprintf(buf, sizeof (buf), - "%s", member); + "%s", (len == 0) ? + "<anon>" : member); end = strrchr(buf, '['); *end = '\0'; pa.pa_suffix = "->"; @@ -2557,8 +2607,7 @@ print_help(void) } static int -printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt, - boolean_t sign) +printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt, int sign) { ssize_t size; mdb_ctf_id_t base; @@ -2576,7 +2625,7 @@ printf_signed(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt, } u; if (mdb_ctf_type_resolve(id, &base) == -1) { - mdb_warn("could not resolve type"); + mdb_warn("could not resolve type\n"); return (DCMD_ABORT); } @@ -2814,7 +2863,6 @@ printf_string(mdb_ctf_id_t id, uintptr_t addr, ulong_t off, char *fmt) if (size != 1) { mdb_warn("string format specifier requires " "an array of characters\n"); - return (DCMD_ABORT); } bzero(buf, sizeof (buf)); @@ -2948,7 +2996,7 @@ cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) if (argc == 0 || argv[0].a_type != MDB_TYPE_STRING) { mdb_warn("expected a format string\n"); - return (DCMD_USAGE); + return (DCMD_ABORT); } /* @@ -2957,12 +3005,6 @@ cmd_printf(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) * subset of mdb_printf() format strings that we allow. */ fmt = argv[0].a_un.a_str; - /* - * 'dest' must be large enough to hold a copy of the format string, - * plus a NUL and up to 2 additional characters for each conversion - * in the format string. This gives us a bloat factor of 5/2 ~= 3. - * e.g. "%d" (strlen of 2) --> "%lld\0" (need 5 bytes) - */ dest = mdb_zalloc(strlen(fmt) * 3, UM_SLEEP | UM_GC); fmts = mdb_zalloc(strlen(fmt) * sizeof (char *), UM_SLEEP | UM_GC); funcs = mdb_zalloc(strlen(fmt) * sizeof (void *), UM_SLEEP | UM_GC); @@ -3146,22 +3188,22 @@ static char _mdb_printf_help[] = "\n" " %% Prints the '%' symbol.\n" " %a Prints the member in symbolic form.\n" -" %d Prints the member as a decimal integer. If the member is a signed\n" +" %d Prints the member as a decimal integer. If the member is a signed\n" " integer type, the output will be signed.\n" " %H Prints the member as a human-readable size.\n" " %I Prints the member as an IPv4 address (must be 32-bit integer type).\n" " %N Prints the member as an IPv6 address (must be of type in6_addr_t).\n" " %o Prints the member as an unsigned octal integer.\n" " %p Prints the member as a pointer, in hexadecimal.\n" -" %q Prints the member in signed octal. Honk if you ever use this!\n" -" %r Prints the member as an unsigned value in the current output radix.\n" -" %R Prints the member as a signed value in the current output radix.\n" +" %q Prints the member in signed octal. Honk if you ever use this!\n" +" %r Prints the member as an unsigned value in the current output radix. \n" +" %R Prints the member as a signed value in the current output radix. \n" " %s Prints the member as a string (requires a pointer or an array of\n" " characters).\n" " %u Prints the member as an unsigned decimal integer.\n" " %x Prints the member in hexadecimal.\n" " %X Prints the member in hexadecimal, using the characters A-F as the\n" -" digits for the values 10-15.\n" +" digits for the values 10-15. \n" " %Y Prints the member as a time_t as the string " "'year month day HH:MM:SS'.\n" "\n" @@ -3174,13 +3216,13 @@ static char _mdb_printf_help[] = "\n" "The following flag specifers are recognized by ::printf:\n" "\n" -" %- Left-justify the output within the specified field width. If the\n" +" %- Left-justify the output within the specified field width. If the\n" " width of the output is less than the specified field width, the\n" -" output will be padded with blanks on the right-hand side. Without\n" +" output will be padded with blanks on the right-hand side. Without\n" " %-, values are right-justified by default.\n" "\n" " %0 Zero-fill the output field if the output is right-justified and the\n" -" width of the output is less than the specified field width. Without\n" +" width of the output is less than the specified field width. Without\n" " %0, right-justified values are prepended with blanks in order to\n" " fill the field.\n" "\n" diff --git a/usr/src/cmd/mdb/common/mdb/mdb_tab.c b/usr/src/cmd/mdb/common/mdb/mdb_tab.c index 8addc11a46..cd6995f887 100644 --- a/usr/src/cmd/mdb/common/mdb/mdb_tab.c +++ b/usr/src/cmd/mdb/common/mdb/mdb_tab.c @@ -388,11 +388,6 @@ mdb_tab_size(mdb_tab_cookie_t *mcp) return (mdb_nv_size(&mcp->mtc_nv)); } -/* - * Determine whether the specified name is a valid tab completion for - * the given command. If the name is a valid tab completion then - * it will be saved in the mdb_tab_cookie_t. - */ void mdb_tab_insert(mdb_tab_cookie_t *mcp, const char *name) { @@ -501,31 +496,18 @@ tab_complete_type(mdb_ctf_id_t id, void *arg) mdb_tab_cookie_t *mcp = arg; uint_t flags = (uint_t)(uintptr_t)mcp->mtc_cba; - /* - * CTF data includes types that mdb commands don't understand. Before - * we resolve the actual type prune any entry that is a type we - * don't care about. - */ - switch (mdb_ctf_type_kind(id)) { - case CTF_K_CONST: - case CTF_K_RESTRICT: - case CTF_K_VOLATILE: - return (0); - } - if (mdb_ctf_type_resolve(id, &rid) != 0) return (1); rkind = mdb_ctf_type_kind(rid); - - if ((flags & MDB_TABC_MEMBERS) && rkind != CTF_K_STRUCT && + if (flags & MDB_TABC_MEMBERS && rkind != CTF_K_STRUCT && rkind != CTF_K_UNION) return (0); - if ((flags & MDB_TABC_NOPOINT) && rkind == CTF_K_POINTER) + if (flags & MDB_TABC_NOPOINT && rkind == CTF_K_POINTER) return (0); - if ((flags & MDB_TABC_NOARRAY) && rkind == CTF_K_ARRAY) + if (flags & MDB_TABC_NOARRAY && rkind == CTF_K_ARRAY) return (0); (void) mdb_ctf_type_name(id, buf, sizeof (buf)); diff --git a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files index 9170118470..d371cf70fe 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/Makefile.files +++ b/usr/src/cmd/mdb/common/modules/genunix/Makefile.files @@ -72,6 +72,7 @@ GENUNIX_SRCS = \ pci.c \ pg.c \ rctl.c \ + refhash.c \ refstr.c \ sobj.c \ streams.c \ diff --git a/usr/src/cmd/mdb/common/modules/genunix/ctxop.c b/usr/src/cmd/mdb/common/modules/genunix/ctxop.c index d78432b743..c8a59ce9ac 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/ctxop.c +++ b/usr/src/cmd/mdb/common/modules/genunix/ctxop.c @@ -27,8 +27,9 @@ * Copyright 2018 Joyent, Inc. */ -#include <mdb/mdb_modapi.h> +#include <sys/mdb_modapi.h> #include <mdb/mdb_ctf.h> +#include <sys/thread.h> #include "ctxop.h" struct ctxop_walk_state { diff --git a/usr/src/cmd/mdb/common/modules/genunix/genunix.c b/usr/src/cmd/mdb/common/modules/genunix/genunix.c index f8d2de5f12..32370ba7e1 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/genunix.c +++ b/usr/src/cmd/mdb/common/modules/genunix/genunix.c @@ -99,6 +99,7 @@ #include "pci.h" #include "pg.h" #include "rctl.h" +#include "refhash.h" #include "sobj.h" #include "streams.h" #include "sysevent.h" @@ -116,10 +117,6 @@ */ #define NINTR 16 -#define KILOS 10 -#define MEGS 20 -#define GIGS 30 - #ifndef STACK_BIAS #define STACK_BIAS 0 #endif @@ -2136,24 +2133,24 @@ typedef struct datafmt { } datafmt_t; static datafmt_t kmemfmt[] = { - { "cache ", "name ", - "-------------------------", "%-25s " }, - { " buf", " size", "------", "%6u " }, - { " buf", "in use", "------", "%6u " }, - { " buf", " total", "------", "%6u " }, - { " memory", " in use", "----------", "%10lu%c " }, - { " alloc", " succeed", "---------", "%9u " }, - { "alloc", " fail", "-----", "%5u " }, + { "cache ", "name ", + "------------------------------", "%-30s " }, + { " buf", " size", "-----", "%5H " }, + { " buf", " in use", "---------", "%9u " }, + { " buf", " total", "---------", "%9u " }, + { "memory", "in use", "------", "%6lH " }, + { " alloc", " succeed", "----------", "%10u " }, + { "alloc", " fail", "-----", "%5u" }, { NULL, NULL, NULL, NULL } }; static datafmt_t vmemfmt[] = { - { "vmem ", "name ", - "-------------------------", "%-*s " }, - { " memory", " in use", "----------", "%9llu%c " }, - { " memory", " total", "-----------", "%10llu%c " }, - { " memory", " import", "----------", "%9llu%c " }, - { " alloc", " succeed", "---------", "%9llu " }, + { "vmem ", "name ", + "------------------------------", "%-*s " }, + { " memory", " in use", "---------", "%9llH " }, + { " memory", " total", "----------", "%10llH " }, + { " memory", " import", "---------", "%9llH " }, + { " alloc", " succeed", "----------", "%10llu " }, { "alloc", " fail", "-----", "%5llu " }, { NULL, NULL, NULL, NULL } }; @@ -2205,15 +2202,9 @@ typedef struct kmastat_vmem { int kv_fail; } kmastat_vmem_t; -typedef struct kmastat_args { - kmastat_vmem_t **ka_kvpp; - uint_t ka_shift; -} kmastat_args_t; - static int -kmastat_cache(uintptr_t addr, const kmem_cache_t *cp, kmastat_args_t *kap) +kmastat_cache(uintptr_t addr, const kmem_cache_t *cp, kmastat_vmem_t **kvpp) { - kmastat_vmem_t **kvpp = kap->ka_kvpp; kmastat_vmem_t *kv; datafmt_t *dfp = kmemfmt; int magsize; @@ -2254,9 +2245,7 @@ out: mdb_printf((dfp++)->fmt, cp->cache_bufsize); mdb_printf((dfp++)->fmt, total - avail); mdb_printf((dfp++)->fmt, total); - mdb_printf((dfp++)->fmt, meminuse >> kap->ka_shift, - kap->ka_shift == GIGS ? 'G' : kap->ka_shift == MEGS ? 'M' : - kap->ka_shift == KILOS ? 'K' : 'B'); + mdb_printf((dfp++)->fmt, meminuse); mdb_printf((dfp++)->fmt, alloc); mdb_printf((dfp++)->fmt, cp->cache_alloc_fail); mdb_printf("\n"); @@ -2265,9 +2254,8 @@ out: } static int -kmastat_vmem_totals(uintptr_t addr, const vmem_t *v, kmastat_args_t *kap) +kmastat_vmem_totals(uintptr_t addr, const vmem_t *v, kmastat_vmem_t *kv) { - kmastat_vmem_t *kv = *kap->ka_kvpp; size_t len; while (kv != NULL && kv->kv_addr != addr) @@ -2276,20 +2264,18 @@ kmastat_vmem_totals(uintptr_t addr, const vmem_t *v, kmastat_args_t *kap) if (kv == NULL || kv->kv_alloc == 0) return (WALK_NEXT); - len = MIN(17, strlen(v->vm_name)); + len = MIN(22, strlen(v->vm_name)); - mdb_printf("Total [%s]%*s %6s %6s %6s %10lu%c %9u %5u\n", v->vm_name, - 17 - len, "", "", "", "", - kv->kv_meminuse >> kap->ka_shift, - kap->ka_shift == GIGS ? 'G' : kap->ka_shift == MEGS ? 'M' : - kap->ka_shift == KILOS ? 'K' : 'B', kv->kv_alloc, kv->kv_fail); + mdb_printf("Total [%s]%*s %5s %9s %9s %6lH %10u %5u\n", v->vm_name, + 22 - len, "", "", "", "", + kv->kv_meminuse, kv->kv_alloc, kv->kv_fail); return (WALK_NEXT); } /*ARGSUSED*/ static int -kmastat_vmem(uintptr_t addr, const vmem_t *v, const uint_t *shiftp) +kmastat_vmem(uintptr_t addr, const vmem_t *v, const void *ignored) { datafmt_t *dfp = vmemfmt; const vmem_kstat_t *vkp = &v->vm_kstat; @@ -2307,16 +2293,10 @@ kmastat_vmem(uintptr_t addr, const vmem_t *v, const uint_t *shiftp) } mdb_printf("%*s", ident, ""); - mdb_printf((dfp++)->fmt, 25 - ident, v->vm_name); - mdb_printf((dfp++)->fmt, vkp->vk_mem_inuse.value.ui64 >> *shiftp, - *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' : - *shiftp == KILOS ? 'K' : 'B'); - mdb_printf((dfp++)->fmt, vkp->vk_mem_total.value.ui64 >> *shiftp, - *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' : - *shiftp == KILOS ? 'K' : 'B'); - mdb_printf((dfp++)->fmt, vkp->vk_mem_import.value.ui64 >> *shiftp, - *shiftp == GIGS ? 'G' : *shiftp == MEGS ? 'M' : - *shiftp == KILOS ? 'K' : 'B'); + mdb_printf((dfp++)->fmt, 30 - ident, v->vm_name); + mdb_printf((dfp++)->fmt, vkp->vk_mem_inuse.value.ui64); + mdb_printf((dfp++)->fmt, vkp->vk_mem_total.value.ui64); + mdb_printf((dfp++)->fmt, vkp->vk_mem_import.value.ui64); mdb_printf((dfp++)->fmt, vkp->vk_alloc.value.ui64); mdb_printf((dfp++)->fmt, vkp->vk_fail.value.ui64); @@ -2331,44 +2311,35 @@ kmastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { kmastat_vmem_t *kv = NULL; datafmt_t *dfp; - kmastat_args_t ka; - - ka.ka_shift = 0; - if (mdb_getopts(argc, argv, - 'k', MDB_OPT_SETBITS, KILOS, &ka.ka_shift, - 'm', MDB_OPT_SETBITS, MEGS, &ka.ka_shift, - 'g', MDB_OPT_SETBITS, GIGS, &ka.ka_shift, NULL) != argc) - return (DCMD_USAGE); for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) - mdb_printf("%s ", dfp->hdr1); + mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->hdr1); mdb_printf("\n"); for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) - mdb_printf("%s ", dfp->hdr2); + mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->hdr2); mdb_printf("\n"); for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) - mdb_printf("%s ", dfp->dashes); + mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->dashes); mdb_printf("\n"); - ka.ka_kvpp = &kv; - if (mdb_walk("kmem_cache", (mdb_walk_cb_t)kmastat_cache, &ka) == -1) { + if (mdb_walk("kmem_cache", (mdb_walk_cb_t)kmastat_cache, &kv) == -1) { mdb_warn("can't walk 'kmem_cache'"); return (DCMD_ERR); } for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) - mdb_printf("%s ", dfp->dashes); + mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->dashes); mdb_printf("\n"); - if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem_totals, &ka) == -1) { + if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem_totals, kv) == -1) { mdb_warn("can't walk 'vmem'"); return (DCMD_ERR); } for (dfp = kmemfmt; dfp->hdr1 != NULL; dfp++) - mdb_printf("%s ", dfp->dashes); + mdb_printf("%s%s", dfp == kmemfmt ? "" : " ", dfp->dashes); mdb_printf("\n"); mdb_printf("\n"); @@ -2385,7 +2356,7 @@ kmastat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) mdb_printf("%s ", dfp->dashes); mdb_printf("\n"); - if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem, &ka.ka_shift) == -1) { + if (mdb_walk("vmem", (mdb_walk_cb_t)kmastat_vmem, NULL) == -1) { mdb_warn("can't walk 'vmem'"); return (DCMD_ERR); } @@ -4494,6 +4465,8 @@ static const mdb_dcmd_t dcmds[] = { /* from zone.c */ { "zid2zone", ":", "find the zone_t with the given zone id", zid2zone }, + { "zdid2zone", ":", "find the zone_t with the given zone debug id", + zdid2zone }, { "zone", "?[-r [-v]]", "display kernel zone(s)", zoneprt }, { "zsd", ":[-v] [zsd_key]", "display zone-specific-data entries for " "selected zones", zsd }, @@ -4823,6 +4796,10 @@ static const mdb_walker_t walkers[] = { { "rctl_val", "given a rctl_t, walk all rctl_val entries associated", rctl_val_walk_init, rctl_val_walk_step }, + /* from refhash.c */ + { REFHASH_WALK_NAME, REFHASH_WALK_DESC, + refhash_walk_init, refhash_walk_step, NULL }, + /* from sobj.c */ { "blocked", "walk threads blocked on a given sobj", blocked_walk_init, blocked_walk_step, NULL }, diff --git a/usr/src/cmd/mdb/common/modules/genunix/refhash.c b/usr/src/cmd/mdb/common/modules/genunix/refhash.c new file mode 100644 index 0000000000..4b4c92dc14 --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/genunix/refhash.c @@ -0,0 +1,61 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018, Joyent, Inc. + */ + +#include <mdb/mdb_modapi.h> +#include <mdb/mdb_ctf.h> + +#include <inttypes.h> +#include <sys/refhash.h> + +typedef struct refhash_walk_data { + size_t rwd_offset; +} refhash_walk_data_t; + +int +refhash_walk_init(mdb_walk_state_t *wsp) +{ + refhash_t refh = { 0 }; + refhash_walk_data_t *rwd; + int offset; + + /* mdb_ctf_offsetof_by_name() will print any errors */ + if ((offset = mdb_ctf_offsetof_by_name("refhash_t", "rh_objs")) == -1) + return (WALK_ERR); + + if (mdb_vread(&refh, sizeof (refhash_t), wsp->walk_addr) == -1) { + mdb_warn("failed to read refhash_t at %#lx", wsp->walk_addr); + return (WALK_ERR); + } + + rwd = wsp->walk_data = mdb_zalloc(sizeof (*rwd), UM_SLEEP | UM_GC); + rwd->rwd_offset = refh.rh_link_off; + + wsp->walk_addr += offset; + if (mdb_layered_walk("list", wsp) == -1) { + mdb_warn("can't walk refhash_t"); + return (WALK_ERR); + } + + return (WALK_NEXT); +} + +int +refhash_walk_step(mdb_walk_state_t *wsp) +{ + refhash_walk_data_t *rwd = wsp->walk_data; + uintptr_t addr = wsp->walk_addr - rwd->rwd_offset; + + return (wsp->walk_callback(addr, wsp->walk_layer, wsp->walk_cbdata)); +} diff --git a/usr/src/cmd/mdb/common/modules/genunix/refhash.h b/usr/src/cmd/mdb/common/modules/genunix/refhash.h new file mode 100644 index 0000000000..1e91ced8d5 --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/genunix/refhash.h @@ -0,0 +1,35 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2018, Joyent, Inc. + */ + +#ifndef _REFHASH_H +#define _REFHASH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define REFHASH_WALK_NAME "refhash" +#define REFHASH_WALK_DESC "walk a refhash" + +struct mdb_walk_state; + +extern int refhash_walk_init(struct mdb_walk_state *); +extern int refhash_walk_step(struct mdb_walk_state *); + +#ifdef __cplusplus +} +#endif + +#endif /* _REFHASH_H */ diff --git a/usr/src/cmd/mdb/common/modules/genunix/zone.c b/usr/src/cmd/mdb/common/modules/genunix/zone.c index 49cd3e7b6e..57545d94a3 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/zone.c +++ b/usr/src/cmd/mdb/common/modules/genunix/zone.c @@ -20,7 +20,7 @@ */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2018, Joyent, Inc. All rights reserved. */ #include <mdb/mdb_param.h> @@ -34,9 +34,9 @@ #define ZONE_NAMELEN 20 #ifdef _LP64 -#define ZONE_PATHLEN 32 +#define ZONE_PATHLEN 25 #else -#define ZONE_PATHLEN 40 +#define ZONE_PATHLEN 33 #endif /* @@ -52,7 +52,8 @@ char *zone_status_names[] = { "empty", /* ZONE_IS_EMPTY */ "down", /* ZONE_IS_DOWN */ "dying", /* ZONE_IS_DYING */ - "dead" /* ZONE_IS_DEAD */ + "dead", /* ZONE_IS_DEAD */ + "free" /* ZONE_IS_FREE */ }; static int @@ -80,6 +81,31 @@ zid2zone(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) return (DCMD_OK); } +static int +zdid_lookup_cb(uintptr_t addr, const zone_t *zone, void *arg) +{ + zoneid_t zdid = *(uintptr_t *)arg; + if (zone->zone_did == zdid) + mdb_printf("%p\n", addr); + + return (WALK_NEXT); +} + +/*ARGSUSED*/ +int +zdid2zone(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) +{ + if (!(flags & DCMD_ADDRSPEC) || argc != 0) + return (DCMD_USAGE); + + if (mdb_walk("zone", (mdb_walk_cb_t)zdid_lookup_cb, &addr) == -1) { + mdb_warn("failed to walk zone"); + return (DCMD_ERR); + } + + return (DCMD_OK); +} + int zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { @@ -122,10 +148,10 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) */ if (DCMD_HDRSPEC(flags)) { if (ropt_given == FALSE) - mdb_printf("%<u>%?s %6s %-13s %-20s %-s%</u>\n", + mdb_printf("%<u>%?s %4s %-13s %-19s %-s%</u>\n", "ADDR", "ID", "STATUS", "NAME", "PATH"); else - mdb_printf("%<u>%?s %6s %10s %10s %-20s%</u>\n", + mdb_printf("%<u>%?s %6s %10s %10s %-19s%</u>\n", "ADDR", "ID", "REFS", "CREFS", "NAME"); } @@ -164,7 +190,7 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) statusp = zone_status_names[zn.zone_status]; else statusp = "???"; - mdb_printf("%0?p %6d %-13s %-20s %s\n", addr, zn.zone_id, + mdb_printf("%0?p %4d %-13s %-19s %s\n", addr, zn.zone_id, statusp, name, path); } else { /* @@ -172,7 +198,7 @@ zoneprt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) * Display the zone's subsystem-specific reference counts if * the user specified the '-v' option. */ - mdb_printf("%0?p %6d %10u %10u %-20s\n", addr, zn.zone_id, + mdb_printf("%0?p %6d %10u %10u %-19s\n", addr, zn.zone_id, zn.zone_ref, zn.zone_cred_ref, name); if (vopt_given == TRUE) { GElf_Sym subsys_names_sym; @@ -410,7 +436,7 @@ zsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) * Prepare to output the specified zone's ZSD information. */ if (DCMD_HDRSPEC(flags)) - mdb_printf("%<u>%-20s %?s %?s %8s%</u>\n", "ZONE", "KEY", + mdb_printf("%<u>%-19s %?s %?s %8s%</u>\n", "ZONE", "KEY", "VALUE", "FLAGS"); len = mdb_readstr(name, ZONE_NAMELEN, (uintptr_t)zone.zone_name); if (len > 0) { @@ -419,7 +445,7 @@ zsd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) } else { (void) strcpy(name, "??"); } - mdb_printf("%-20s ", name); + mdb_printf("%-19s ", name); /* * Display the requested ZSD entries. diff --git a/usr/src/cmd/mdb/common/modules/genunix/zone.h b/usr/src/cmd/mdb/common/modules/genunix/zone.h index 0881f9bbae..94a383e41c 100644 --- a/usr/src/cmd/mdb/common/modules/genunix/zone.h +++ b/usr/src/cmd/mdb/common/modules/genunix/zone.h @@ -34,6 +34,7 @@ extern "C" { #endif extern int zid2zone(uintptr_t, uint_t, int argc, const mdb_arg_t *); +extern int zdid2zone(uintptr_t, uint_t, int argc, const mdb_arg_t *); extern int zoneprt(uintptr_t, uint_t, int argc, const mdb_arg_t *); extern int zone_walk_init(mdb_walk_state_t *); diff --git a/usr/src/cmd/mdb/common/modules/ipc/ipc.c b/usr/src/cmd/mdb/common/modules/ipc/ipc.c index bc87716015..04db8f7fcf 100644 --- a/usr/src/cmd/mdb/common/modules/ipc/ipc.c +++ b/usr/src/cmd/mdb/common/modules/ipc/ipc.c @@ -21,6 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2016 Joyent, Inc. */ #include <mdb/mdb_modapi.h> @@ -228,7 +229,9 @@ shm_print(kshmid_t *shmid, uintptr_t addr) printtime_nice("ctime: ", shmid->shm_ctime); mdb_printf("sptinfo: %-?p sptseg: %-?p\n", shmid->shm_sptinfo, shmid->shm_sptseg); - mdb_printf("sptprot: <%lb>\n", shmid->shm_sptprot, prot_flag_bits); + mdb_printf("opts: rmpend: %d prot: <%b>\n", + ((shmid->shm_opts & SHM_RM_PENDING) != 0), + (shmid->shm_opts & SHM_PROT_MASK), prot_flag_bits); } diff --git a/usr/src/cmd/mdb/common/modules/libc/libc.c b/usr/src/cmd/mdb/common/modules/libc/libc.c index 69f4beda74..8ee6af407e 100644 --- a/usr/src/cmd/mdb/common/modules/libc/libc.c +++ b/usr/src/cmd/mdb/common/modules/libc/libc.c @@ -138,6 +138,8 @@ d_ucontext(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) uc.uc_stack.ss_sp, uc.uc_stack.ss_size, stack_flags(&uc.uc_stack)); mdb_printf(" mcontext = 0x%p\n", addr + OFFSETOF(ucontext_t, uc_mcontext)); + mdb_printf(" brand = 0x%p 0x%p 0x%p\n", + uc.uc_brand_data[0], uc.uc_brand_data[1], uc.uc_brand_data[2]); return (DCMD_OK); } @@ -849,14 +851,19 @@ d_uberdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) prt_addr(uberdata.all_lwps, 1), prt_addr(uberdata.all_zombies, 0)); - HD("nthreads nzombies ndaemons pid sigacthandler"); - mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d %s\n", + HD("nthreads nzombies ndaemons pid"); + mdb_printf(OFFSTR "%-10d %-10d %-10d %-10d\n", OFFSET(nthreads), uberdata.nthreads, uberdata.nzombies, uberdata.ndaemons, - (int)uberdata.pid, - prt_addr((void *)uberdata.sigacthandler, 0)); + (int)uberdata.pid); + + HD("sigacthandler setctxt"); + mdb_printf(OFFSTR "%s %s\n", + OFFSET(sigacthandler), + prt_addr((void *)uberdata.sigacthandler, 1), + prt_addr((void *)uberdata.setctxt, 1)); HD("lwp_stacks lwp_laststack nfreestack stk_cache"); mdb_printf(OFFSTR "%s %s %-10d %d\n", @@ -879,12 +886,17 @@ d_uberdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) prt_addr(uberdata.ulwp_replace_last, 1), prt_addr(uberdata.atforklist, 0)); - HD("robustlocks robustlist progname"); - mdb_printf(OFFSTR "%s %s %s\n", + HD("robustlocks robustlist"); + mdb_printf(OFFSTR "%s %s\n", OFFSET(robustlocks), prt_addr(uberdata.robustlocks, 1), - prt_addr(uberdata.robustlist, 1), - prt_addr(uberdata.progname, 0)); + prt_addr(uberdata.robustlist, 1)); + + HD("progname ub_broot"); + mdb_printf(OFFSTR "%s %s\n", + OFFSET(progname), + prt_addr(uberdata.progname, 1), + prt_addr(uberdata.ub_broot, 1)); HD("tdb_bootstrap tdb_sync_addr_hash tdb_'count tdb_'fail"); mdb_printf(OFFSTR "%s %s %-10d %d\n", diff --git a/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c b/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c index 85f3839c96..68b2e9d362 100644 --- a/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c +++ b/usr/src/cmd/mdb/common/modules/mdb_ks/mdb_ks.c @@ -60,6 +60,12 @@ #define MDB_PATH_NELEM 256 /* Maximum path components */ +/* + * Due to mdb_param.h shenanigans, there's no simple way to include string.h + * here... + */ +extern char *strtok(char *restrict, const char *restrict); + typedef struct mdb_path { size_t mdp_nelem; /* Number of components */ uint_t mdp_complete; /* Path completely resolved? */ @@ -1811,18 +1817,73 @@ mdb_get_lbolt(void) return ((ts/nsec) - lbi.lbi_debug_time); } +#define startswith(a, b) (strncmp((a), (b), strlen(b)) == 0) + +/* + * Dig out the branch and revision of the illumos-joyent repo, if we were + * provided with it. This is a rather fragile JSON parser, in that it requires + * JSON formatted exactly as per the boot_archive.gitstatus file that + * "buildversion" is built from. + */ void mdb_print_buildversion(void) { + boolean_t in_joyent = B_FALSE; GElf_Sym sym; - if (mdb_lookup_by_name("buildversion", &sym) != 0) - return; + if (mdb_lookup_by_name("buildversion", &sym) != 0) { + /* Older kernels used this name. */ + if (mdb_lookup_by_name("gitstatus_start", &sym) != 0) + return; + } char *str = mdb_zalloc(4096, UM_SLEEP | UM_GC); if (mdb_readstr(str, 4096, sym.st_value) < 1) return; - mdb_printf("build version: %s\n", str); + /* + * Each line is of the form + * + * "repo": "smartos-live", + */ + for (char *line = strtok(str, "\n"); line != NULL; + line = strtok(NULL, "\n")) { + /* skip whitespace and first " */ + line += strspn(line, " \t\""); + + if (startswith(line, "repo")) { + line += sizeof ("repo") - 1; + line += strspn(line, " \t\":"); + + if (startswith(line, "illumos-joyent")) + in_joyent = B_TRUE; + else if (in_joyent) + return; + continue; + } + + if (!in_joyent) + continue; + + if (startswith(line, "branch")) { + char *trail = strrchr(line, '"'); + if (trail != NULL) + *trail = '\0'; + line += sizeof ("branch") - 1; + line += strspn(line, " \t\":"); + mdb_printf("git branch: %s\n", line); + continue; + } + + if (startswith(line, "rev")) { + char *trail = strrchr(line, '"'); + if (trail != NULL) + *trail = '\0'; + line += sizeof ("rev") - 1; + line += strspn(line, " \t\":"); + mdb_printf("git rev: %s\n", line); + continue; + } + } } diff --git a/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c b/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c index ecb2aba4cc..1a63798c93 100644 --- a/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c +++ b/usr/src/cmd/mdb/common/modules/mpt_sas/mpt_sas.c @@ -34,6 +34,7 @@ #include <sys/sunmdi.h> #include <sys/list.h> #include <sys/scsi/scsi.h> +#include <sys/refhash.h> #pragma pack(1) #include <sys/scsi/adapters/mpt_sas/mpi/mpi2_type.h> @@ -47,7 +48,6 @@ #pragma pack() #include <sys/scsi/adapters/mpt_sas/mptsas_var.h> -#include <sys/scsi/adapters/mpt_sas/mptsas_hash.h> struct { int value; diff --git a/usr/src/cmd/mdb/common/modules/random/random.c b/usr/src/cmd/mdb/common/modules/random/random.c index a1b41f0eb8..2da56dae32 100644 --- a/usr/src/cmd/mdb/common/modules/random/random.c +++ b/usr/src/cmd/mdb/common/modules/random/random.c @@ -24,7 +24,9 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2019 Joyent, Inc. + */ #include <sys/mdb_modapi.h> #include <mdb/mdb_ctf.h> @@ -39,10 +41,9 @@ rnd_get_stats(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { rnd_stats_t rnd_stats, rnd_stats_cpu; uint32_t random_max_ncpus; - size_t rndmag_t_size; + size_t rndmag_pad_t_size; ulong_t rndmag_t_offset; uintptr_t rndmag; - mdb_ctf_id_t rndmag_id; int i; if ((flags & DCMD_ADDRSPEC) || argc != 0) @@ -53,23 +54,20 @@ rnd_get_stats(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) return (DCMD_ERR); } - if ((mdb_ctf_lookup_by_name("rndmag_t", &rndmag_id) != 0) || - (mdb_ctf_offsetof(rndmag_id, "rm_stats", &rndmag_t_offset) != 0) || + if (((rndmag_t_offset = mdb_ctf_offsetof_by_name("rndmag_t", "rm_stats")) == -1) || (mdb_readvar(&random_max_ncpus, "random_max_ncpus") == -1) || (mdb_readvar(&rndmag, "rndmag") == -1) || - ((rndmag_t_size = mdb_ctf_type_size(rndmag_id)) == 0)) { + ((rndmag_pad_t_size = mdb_ctf_sizeof_by_name("rndmag_pad_t")) == -1)) { /* Can't find per-cpu stats. Don't add them in. */ random_max_ncpus = 0; } - rndmag_t_offset /= 8; - /* * Read and aggregate per-cpu stats if we have them. */ for (i = 0; i < random_max_ncpus; i++) { mdb_vread(&rnd_stats_cpu, sizeof (rnd_stats_cpu), - rndmag + rndmag_t_offset + i * rndmag_t_size); + rndmag + rndmag_t_offset + i * rndmag_pad_t_size); rnd_stats.rs_rndOut += rnd_stats_cpu.rs_rndOut; rnd_stats.rs_rndcOut += rnd_stats_cpu.rs_rndcOut; diff --git a/usr/src/cmd/mdb/common/modules/xhci/xhci.c b/usr/src/cmd/mdb/common/modules/xhci/xhci.c new file mode 100644 index 0000000000..1a1882e738 --- /dev/null +++ b/usr/src/cmd/mdb/common/modules/xhci/xhci.c @@ -0,0 +1,893 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2019 Joyent, Inc. + */ + +#include <sys/mdb_modapi.h> +#include <sys/usb/hcd/xhci/xhci.h> + +#define XHCI_MDB_TRB_INDENT 4 + +static const char *xhci_mdb_epctx_eptypes[] = { + "Not Valid", + "ISOCH OUT", + "BULK OUT", + "INTR OUT", + "CONTROL", + "ISOCH IN", + "BULK IN", + "INTR IN" +}; + +static const char *xhci_mdb_epctx_states[] = { + "Disabled", + "Running", + "Halted", + "Stopped", + "Error", + "<Unknown>", + "<Unknown>", + "<Unknown>" +}; + +static const mdb_bitmask_t xhci_mdb_trb_flags[] = { + { "C", XHCI_TRB_CYCLE, XHCI_TRB_CYCLE }, + { "ENT", XHCI_TRB_ENT, XHCI_TRB_ENT }, + { "ISP", XHCI_TRB_ISP, XHCI_TRB_ISP }, + { "NS", XHCI_TRB_NOSNOOP, XHCI_TRB_NOSNOOP }, + { "CH", XHCI_TRB_CHAIN, XHCI_TRB_CHAIN }, + { "IOC", XHCI_TRB_IOC, XHCI_TRB_IOC }, + { "IDT", XHCI_TRB_IDT, XHCI_TRB_IDT }, + { "BEI", XHCI_TRB_BEI, XHCI_TRB_BEI }, + { NULL, 0, 0 } +}; + +typedef struct xhci_mdb_walk_endpoint { + xhci_device_t xmwe_device; + uint_t xmwe_ep; +} xhci_mdb_walk_endpoint_t; + +static const char * +xhci_mdb_trb_code_to_str(int code) +{ + switch (code) { + case XHCI_CODE_INVALID: + return ("Invalid"); + case XHCI_CODE_SUCCESS: + return ("Success"); + case XHCI_CODE_DATA_BUF: + return ("Data Overrun or Underrun"); + case XHCI_CODE_BABBLE: + return ("Babble"); + case XHCI_CODE_TXERR: + return ("Transaction Error"); + case XHCI_CODE_TRB: + return ("Invalid TRB"); + case XHCI_CODE_STALL: + return ("Stall"); + case XHCI_CODE_RESOURCE: + return ("No Resources Available"); + case XHCI_CODE_BANDWIDTH: + return ("No Bandwidth Available"); + case XHCI_CODE_NO_SLOTS: + return ("No Slots Available"); + case XHCI_CODE_STREAM_TYPE: + return ("Stream Context Type Detected"); + case XHCI_CODE_SLOT_NOT_ON: + return ("Slot disabled"); + case XHCI_CODE_ENDP_NOT_ON: + return ("Endpoint disabled"); + case XHCI_CODE_SHORT_XFER: + return ("Short Transfer"); + case XHCI_CODE_RING_UNDERRUN: + return ("Isoch. Ring Underrun"); + case XHCI_CODE_RING_OVERRUN: + return ("Isoch. Ring Overrun"); + case XHCI_CODE_VF_RING_FULL: + return ("VF Ring Full"); + case XHCI_CODE_PARAMETER: + return ("Invalid Context Paramenter"); + case XHCI_CODE_BW_OVERRUN: + return ("Bandwidth Overrun"); + case XHCI_CODE_CONTEXT_STATE: + return ("Illegal Context Transition"); + case XHCI_CODE_NO_PING_RESP: + return ("Failed to Complete Periodic Transfer"); + case XHCI_CODE_EV_RING_FULL: + return ("Event Ring Full"); + case XHCI_CODE_INCOMPAT_DEV: + return ("Incompatible Device"); + case XHCI_CODE_MISSED_SRV: + return ("Missed Isoch. Service Window"); + case XHCI_CODE_CMD_RING_STOP: + return ("Command Ring Stop"); + case XHCI_CODE_CMD_ABORTED: + return ("Command Aborted"); + case XHCI_CODE_XFER_STOPPED: + return ("Transfer Stopped"); + case XHCI_CODE_XFER_STOPINV: + return ("Invalid Transfer Length"); + case XHCI_CODE_XFER_STOPSHORT: + return ("Stopped before End of Transfer Descriptor"); + case XHCI_CODE_MELAT: + return ("Max Exit Latency too large"); + case XHCI_CODE_RESERVED: + return ("Reserved"); + case XHCI_CODE_ISOC_OVERRUN: + return ("Isochronus Overrun"); + case XHCI_CODE_EVENT_LOST: + return ("Event Lost"); + case XHCI_CODE_UNDEFINED: + return ("Undefined Fatal Error"); + case XHCI_CODE_INVALID_SID: + return ("Invalid Stream ID"); + case XHCI_CODE_SEC_BW: + return ("Secondary Bandwith Allocation Failure"); + case XHCI_CODE_SPLITERR: + return ("USB2 SPlit Transaction Error"); + default: + break; + } + + if (code >= 192 && code <= 223) + return ("Vendor Defined Error"); + if (code >= 224 && code <= 255) + return ("Vendor Defined Info"); + + return ("Reserved"); +} + +static const char * +xhci_mdb_trb_type_to_str(int code) +{ + /* + * The macros for the types are all already shifted over based on their + * place in the TRB, so shift there again ourselves. + */ + switch (code << 10) { + case XHCI_TRB_TYPE_NORMAL: + return ("Normal"); + case XHCI_TRB_TYPE_SETUP: + return ("Setup"); + case XHCI_TRB_TYPE_DATA: + return ("Data"); + case XHCI_TRB_TYPE_STATUS: + return ("Status"); + case XHCI_TRB_TYPE_LINK: + return ("Link"); + case XHCI_TRB_TYPE_EVENT: + return ("Event"); + case XHCI_TRB_TYPE_NOOP: + return ("No-Op"); + case XHCI_CMD_ENABLE_SLOT: + return ("Enable Slot"); + case XHCI_CMD_DISABLE_SLOT: + return ("Disable Slot"); + case XHCI_CMD_ADDRESS_DEVICE: + return ("Address Device"); + case XHCI_CMD_CONFIG_EP: + return ("Configure Endpoint"); + case XHCI_CMD_EVAL_CTX: + return ("Evaluate Context"); + case XHCI_CMD_RESET_EP: + return ("Reset Endpoint"); + case XHCI_CMD_STOP_EP: + return ("Stop Endpoint"); + case XHCI_CMD_SET_TR_DEQ: + return ("Set Transfer Ring Dequeue Pointer"); + case XHCI_CMD_RESET_DEV: + return ("Reset Device"); + case XHCI_CMD_FEVENT: + return ("Force Event"); + case XHCI_CMD_NEG_BW: + return ("Negotiate Bandwidth"); + case XHCI_CMD_SET_LT: + return ("Set Latency Tolerance"); + case XHCI_CMD_GET_BW: + return ("Get Bandwidth"); + case XHCI_CMD_FHEADER: + return ("Force Header"); + case XHCI_CMD_NOOP: + return ("No-Op Command"); + case XHCI_EVT_XFER: + return ("Transfer Event"); + case XHCI_EVT_CMD_COMPLETE: + return ("Command Completion Event"); + case XHCI_EVT_PORT_CHANGE: + return ("Port Status Change Event"); + case XHCI_EVT_BW_REQUEST: + return ("Bandwidth Request Event"); + case XHCI_EVT_DOORBELL: + return ("Doorbell Event"); + case XHCI_EVT_HOST_CTRL: + return ("Host Controller Event"); + case XHCI_EVT_DEVICE_NOTIFY: + return ("Device Notification Event"); + case XHCI_EVT_MFINDEX_WRAP: + return ("MFINDEX Wrap Event"); + default: + break; + } + + if (code >= 43 && code <= 63) + return ("Vendor Defiend"); + return ("Reserved"); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_epctx(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + uint32_t info, info2, txinfo; + xhci_endpoint_context_t epctx; + + if (!(flags & DCMD_ADDRSPEC)) { + mdb_warn("::xhci_epctx requires an address\n"); + return (DCMD_USAGE); + } + + if (mdb_vread(&epctx, sizeof (epctx), addr) != sizeof (epctx)) { + mdb_warn("failed to read xhci_endpoint_context_t at %p", addr); + return (DCMD_ERR); + } + + info = LE_32(epctx.xec_info); + info2 = LE_32(epctx.xec_info2); + txinfo = LE_32(epctx.xec_txinfo); + + mdb_printf("Endpoint State: %s (%d)\n", + xhci_mdb_epctx_states[XHCI_EPCTX_STATE(info)], + XHCI_EPCTX_STATE(info)); + + mdb_printf("Mult: %d\n", XHCI_EPCTX_GET_MULT(info)); + mdb_printf("Max Streams: %d\n", XHCI_EPCTX_GET_MAXP_STREAMS(info)); + mdb_printf("LSA: %d\n", XHCI_EPCTX_GET_LSA(info)); + mdb_printf("Interval: %d\n", XHCI_EPCTX_GET_IVAL(info)); + mdb_printf("Max ESIT Hi: %d\n", XHCI_EPCTX_GET_MAX_ESIT_HI(info)); + + mdb_printf("CErr: %d\n", XHCI_EPCTX_GET_CERR(info2)); + mdb_printf("EP Type: %s (%d)\n", + xhci_mdb_epctx_eptypes[XHCI_EPCTX_GET_EPTYPE(info2)], + XHCI_EPCTX_GET_EPTYPE(info2)); + mdb_printf("Host Initiate Disable: %d\n", XHCI_EPCTX_GET_HID(info2)); + mdb_printf("Max Burst: %d\n", XHCI_EPCTX_GET_MAXB(info2)); + mdb_printf("Max Packet Size: %d\n", XHCI_EPCTX_GET_MPS(info2)); + + mdb_printf("Ring DCS: %d\n", LE_64(epctx.xec_dequeue) & 0x1); + mdb_printf("Ring PA: 0x%lx\n", LE_64(epctx.xec_dequeue) & ~0xf); + + mdb_printf("Average TRB Length: %d\n", XHCI_EPCTX_AVG_TRB_LEN(txinfo)); + mdb_printf("Max ESIT: %d\n", XHCI_EPCTX_GET_MAX_ESIT_PAYLOAD(txinfo)); + + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_slotctx(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + uint32_t info, info2, tt, state; + xhci_slot_context_t sctx; + + if (!(flags & DCMD_ADDRSPEC)) { + mdb_warn("::xhci_slotctx requires an address\n"); + return (DCMD_USAGE); + } + + if (mdb_vread(&sctx, sizeof (sctx), addr) != sizeof (sctx)) { + mdb_warn("failed to read xhci_slot_context_t at %p", addr); + return (DCMD_ERR); + } + + info = LE_32(sctx.xsc_info); + info2 = LE_32(sctx.xsc_info2); + tt = LE_32(sctx.xsc_tt); + state = LE_32(sctx.xsc_state); + + mdb_printf("Route: 0x%x\n", XHCI_SCTX_GET_ROUTE(info)); + + mdb_printf("Slot Speed: "); + switch (XHCI_SCTX_GET_SPEED(info)) { + case XHCI_SPEED_FULL: + mdb_printf("Full"); + break; + case XHCI_SPEED_LOW: + mdb_printf("Low"); + break; + case XHCI_SPEED_HIGH: + mdb_printf("High"); + break; + case XHCI_SPEED_SUPER: + mdb_printf("Super"); + break; + default: + mdb_printf("Unknown"); + break; + } + mdb_printf(" (%d)\n", XHCI_SCTX_GET_SPEED(info)); + + + mdb_printf("MTT: %d\n", XHCI_SCTX_GET_MTT(info)); + mdb_printf("HUB: %d\n", XHCI_SCTX_GET_HUB(info)); + mdb_printf("DCI: %d\n", XHCI_SCTX_GET_DCI(info)); + + mdb_printf("Max Exit Latency: %d\n", XHCI_SCTX_GET_MAX_EL(info2)); + mdb_printf("Root Hub Port: %d\n", XHCI_SCTX_GET_RHPORT(info2)); + mdb_printf("Hub Number of Ports: %d\n", XHCI_SCTX_GET_NPORTS(info2)); + + mdb_printf("TT Hub Slot id: %d\n", XHCI_SCTX_GET_TT_HUB_SID(tt)); + mdb_printf("TT Port Number: %d\n", XHCI_SCTX_GET_TT_PORT_NUM(tt)); + mdb_printf("TT Think Time: %d\n", XHCI_SCTX_GET_TT_THINK_TIME(tt)); + mdb_printf("IRQ Target: %d\n", XHCI_SCTX_GET_IRQ_TARGET(tt)); + + mdb_printf("Device Address: 0x%x\n", XHCI_SCTX_GET_DEV_ADDR(state)); + mdb_printf("Slot State: "); + switch (XHCI_SCTX_GET_SLOT_STATE(state)) { + case XHCI_SLOT_DIS_ENAB: + mdb_printf("Disabled/Enabled"); + break; + case XHCI_SLOT_DEFAULT: + mdb_printf("Default"); + break; + case XHCI_SLOT_ADDRESSED: + mdb_printf("Addressed"); + break; + case XHCI_SLOT_CONFIGURED: + mdb_printf("Configured"); + break; + default: + mdb_printf("Unknown"); + break; + } + mdb_printf(" (%d)\n", XHCI_SCTX_GET_SLOT_STATE(state)); + + return (DCMD_OK); +} + +static int +xhci_mdb_print_transfer_event(uint64_t pa, uint32_t status, uint32_t flags) +{ + mdb_printf("TRB Address: 0x%lx\n", pa); + mdb_printf("Transfer Length (Remain): %d\n", XHCI_TRB_REMAIN(status)); + mdb_printf("Completion Code: %s (%d)\n", + xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)), + XHCI_TRB_GET_CODE(status)); + + mdb_printf("Cycle: %d\n", XHCI_TRB_GET_CYCLE(flags)); + mdb_printf("Event Data: %d\n", XHCI_TRB_GET_ED(flags)); + mdb_printf("Endpoint ID: %d\n", XHCI_TRB_GET_EP(flags)); + mdb_printf("Slot ID: %d\n", XHCI_TRB_GET_SLOT(flags)); + mdb_dec_indent(XHCI_MDB_TRB_INDENT); + + return (DCMD_OK); +} + +static int +xhci_mdb_print_command_event(uint64_t pa, uint32_t status, uint32_t flags) +{ + mdb_printf("TRB Address: 0x%lx\n", pa); + mdb_printf("Command Param: 0x%x\n", XHCI_TRB_REMAIN(status)); + mdb_printf("Completion Code: %s (%d)\n", + xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)), + XHCI_TRB_GET_CODE(status)); + + mdb_printf("Cycle: %d\n", XHCI_TRB_GET_CYCLE(flags)); + /* Skip VF ID as we don't support VFs */ + mdb_printf("Slot ID: %d\n", XHCI_TRB_GET_SLOT(flags)); + mdb_dec_indent(XHCI_MDB_TRB_INDENT); + + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_psc(uint64_t pa, uint32_t status, uint32_t flags) +{ + mdb_printf("Port: %d\n", XHCI_TRB_PORTID(pa)); + mdb_printf("Completion Code: %s (%d)\n", + xhci_mdb_trb_code_to_str(XHCI_TRB_GET_CODE(status)), + XHCI_TRB_GET_CODE(status)); + mdb_dec_indent(XHCI_MDB_TRB_INDENT); + return (DCMD_OK); +} + +static int +xhci_mdb_print_normal_trb(uint64_t pa, uint32_t status, uint32_t flags) +{ + mdb_printf("TRB Address: 0x%lx\n", pa); + mdb_printf("TRB Length: %d bytes\n", XHCI_TRB_LEN(status)); + mdb_printf("TRB TD Size: %d packets\n", XHCI_TRB_GET_TDREM(status)); + mdb_printf("TRB Interrupt: %d\n", XHCI_TRB_GET_INTR(status)); + mdb_printf("TRB Flags: %b (0x%x)\n", flags, xhci_mdb_trb_flags, + XHCI_TRB_GET_FLAGS(flags)); + mdb_dec_indent(XHCI_MDB_TRB_INDENT); + + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_trb(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + xhci_trb_t trb; + uint64_t pa; + uint32_t status, trbflags, type; + + if (!(flags & DCMD_ADDRSPEC)) { + mdb_warn("::xhci_trb expects an address\n"); + return (DCMD_USAGE); + } + + if (mdb_vread(&trb, sizeof (trb), addr) != sizeof (trb)) { + mdb_warn("failed to read xhci_trb_t at 0x%x", addr); + return (DCMD_ERR); + } + + pa = LE_64(trb.trb_addr); + status = LE_32(trb.trb_status); + trbflags = LE_32(trb.trb_flags); + + type = XHCI_TRB_GET_TYPE(trbflags); + + if ((flags & DCMD_LOOP) && !(flags & DCMD_LOOPFIRST)) + mdb_printf("\n"); + + mdb_set_dot(addr + sizeof (xhci_trb_t)); + mdb_printf("%s TRB (%d)\n", xhci_mdb_trb_type_to_str(type), type); + mdb_inc_indent(XHCI_MDB_TRB_INDENT); + + switch (XHCI_RING_TYPE_SHIFT(type)) { + case XHCI_EVT_XFER: + return (xhci_mdb_print_transfer_event(pa, status, trbflags)); + case XHCI_EVT_CMD_COMPLETE: + return (xhci_mdb_print_command_event(pa, status, trbflags)); + case XHCI_EVT_PORT_CHANGE: + return (xhci_mdb_print_psc(pa, status, trbflags)); + case XHCI_TRB_TYPE_NORMAL: + return (xhci_mdb_print_normal_trb(pa, status, trbflags)); + } + + /* + * Just print generic information if we don't have a specific printer + * for that TRB type. + */ + mdb_printf("TRB Address: 0x%lx\n", pa); + mdb_printf("TRB Status: 0x%x\n", status); + mdb_printf("TRB Flags: 0x%x\n", trbflags); + mdb_dec_indent(XHCI_MDB_TRB_INDENT); + + return (DCMD_OK); +} + +static int +xhci_mdb_walk_xhci_init(mdb_walk_state_t *wsp) +{ + GElf_Sym sym; + uintptr_t addr; + + if (wsp->walk_addr != 0) { + mdb_warn("::walk xhci only supports global walks\n"); + return (WALK_ERR); + } + + if (mdb_lookup_by_obj("xhci", "xhci_soft_state", &sym) != 0) { + mdb_warn("failed to find xhci_soft_state symbol"); + return (WALK_ERR); + } + + if (mdb_vread(&addr, sizeof (addr), sym.st_value) != sizeof (addr)) { + mdb_warn("failed to read xhci_soft_state at %p", addr); + return (WALK_ERR); + } + + wsp->walk_addr = addr; + if (mdb_layered_walk("softstate", wsp) != 0) { + mdb_warn("failed to walk softstate"); + return (WALK_ERR); + } + + return (WALK_NEXT); +} + +static int +xhci_mdb_walk_xhci_step(mdb_walk_state_t *wsp) +{ + xhci_t xhci; + + if (mdb_vread(&xhci, sizeof (xhci), wsp->walk_addr) != sizeof (xhci)) { + mdb_warn("failed to read xhci_t at %p", wsp->walk_addr); + return (WALK_ERR); + } + + return (wsp->walk_callback(wsp->walk_addr, &xhci, wsp->walk_cbdata)); +} + +static int +xhci_mdb_walk_xhci_device_init(mdb_walk_state_t *wsp) +{ + uintptr_t addr; + + if (wsp->walk_addr == 0) { + mdb_warn("::walk xhci_device requires an xhci_t\n"); + return (WALK_ERR); + } + + addr = wsp->walk_addr; + addr += offsetof(xhci_t, xhci_usba); + addr += offsetof(xhci_usba_t, xa_devices); + wsp->walk_addr = (uintptr_t)addr; + if (mdb_layered_walk("list", wsp) != 0) { + mdb_warn("failed to walk list"); + return (WALK_ERR); + } + + return (WALK_NEXT); +} + +static int +xhci_mdb_walk_xhci_device_step(mdb_walk_state_t *wsp) +{ + xhci_device_t xd; + + if (mdb_vread(&xd, sizeof (xd), wsp->walk_addr) != sizeof (xd)) { + mdb_warn("failed to read xhci_device_t at %p", wsp->walk_addr); + return (WALK_ERR); + } + + return (wsp->walk_callback(wsp->walk_addr, &xd, wsp->walk_cbdata)); +} + +static int +xhci_mdb_walk_xhci_endpoint_init(mdb_walk_state_t *wsp) +{ + xhci_mdb_walk_endpoint_t *xm; + xhci_device_t *xd; + + if (wsp->walk_addr == 0) { + mdb_warn("::walk xhci_endpoint requires an xhci_device_t\n"); + return (WALK_ERR); + } + + xm = mdb_alloc(sizeof (xhci_mdb_walk_endpoint_t), UM_SLEEP | UM_GC); + xm->xmwe_ep = 0; + xd = &xm->xmwe_device; + if (mdb_vread(xd, sizeof (*xd), wsp->walk_addr) != sizeof (*xd)) { + mdb_warn("failed to read xhci_endpoint_t at %p", + wsp->walk_addr); + return (WALK_ERR); + } + wsp->walk_data = xm; + + return (WALK_NEXT); +} + +static int +xhci_mdb_walk_xhci_endpoint_step(mdb_walk_state_t *wsp) +{ + int ret; + uintptr_t addr; + xhci_mdb_walk_endpoint_t *xm = wsp->walk_data; + + if (xm->xmwe_ep >= XHCI_NUM_ENDPOINTS) + return (WALK_DONE); + + addr = (uintptr_t)xm->xmwe_device.xd_endpoints[xm->xmwe_ep]; + if (addr != (uintptr_t)NULL) { + xhci_endpoint_t xe; + + if (mdb_vread(&xe, sizeof (xe), addr) != sizeof (xe)) { + mdb_warn("failed to read xhci_endpoint_t at %p", + xm->xmwe_device.xd_endpoints[xm->xmwe_ep]); + return (WALK_ERR); + } + + ret = wsp->walk_callback(addr, &xe, wsp->walk_cbdata); + } else { + ret = WALK_NEXT; + } + xm->xmwe_ep++; + + return (ret); +} + +typedef struct xhci_mdb_find { + int xmf_slot; + int xmf_ep; + uintptr_t xmf_addr; +} xhci_mdb_find_t; + +static int +xhci_mdb_find_endpoint_cb(uintptr_t addr, const void *data, void *arg) +{ + const xhci_endpoint_t *xep = data; + xhci_mdb_find_t *xmf = arg; + + /* + * The endpoints that are presented here are off by one from the actual + * endpoint ID in the xhci_endpoint_t, as we're really displaying the + * index into the device input context. + */ + if (xep->xep_num + 1 == xmf->xmf_ep) { + xmf->xmf_addr = addr; + return (WALK_DONE); + } + + return (WALK_NEXT); +} + +static int +xhci_mdb_find_device_cb(uintptr_t addr, const void *data, void *arg) +{ + const xhci_device_t *xd = data; + xhci_mdb_find_t *xmf = arg; + + if (xd->xd_slot == xmf->xmf_slot) { + if (xmf->xmf_ep == -1) { + xmf->xmf_addr = addr; + return (WALK_DONE); + } + + if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_find_endpoint_cb, + xmf, addr) == -1) { + mdb_warn("failed to walk xhci_endpoint at %p", addr); + return (WALK_ERR); + } + + return (WALK_DONE); + } + + return (WALK_NEXT); +} + +static int +xhci_mdb_find(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + uintptr_t ep, slot; + boolean_t ep_set, slot_set; + xhci_mdb_find_t xmf; + + if ((flags & DCMD_ADDRSPEC) == 0) + return (DCMD_USAGE); + + ep_set = slot_set = B_FALSE; + if (mdb_getopts(argc, argv, 'e', MDB_OPT_UINTPTR_SET, &ep_set, &ep, + 's', MDB_OPT_UINTPTR_SET, &slot_set, &slot, NULL) != argc) + return (DCMD_USAGE); + + if (!slot_set) { + mdb_warn("-s is required\n"); + return (DCMD_USAGE); + } + + xmf.xmf_slot = (int)slot; + if (ep_set) + xmf.xmf_ep = (int)ep; + else + xmf.xmf_ep = -1; + xmf.xmf_addr = 0; + + if (mdb_pwalk("xhci`xhci_device", xhci_mdb_find_device_cb, + &xmf, addr) == -1) { + mdb_warn("failed to walk xhci_device at %p", addr); + return (DCMD_ERR); + } + + if (xmf.xmf_addr == 0) { + if (ep_set) { + mdb_warn("failed to find xhci_endpoint_t for slot %d " + "and endpoint %d\n", slot, ep); + } else { + mdb_warn("failed to find xhci_device_t for slot %d\n", + slot); + } + return (DCMD_ERR); + } + + mdb_printf("%p\n", xmf.xmf_addr); + return (DCMD_OK); +} + +/* ARGSUSED */ +static int +xhci_mdb_endpoint_count(uintptr_t addr, const void *ep, void *arg) +{ + int *countp = arg; + + *countp += 1; + return (WALK_NEXT); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_endpoint_summary(uintptr_t addr, const void *ep, void *arg) +{ + const xhci_device_t *xd = arg; + const xhci_endpoint_t *xep = ep; + const char *type; + const char *state; + xhci_endpoint_context_t epctx; + int eptype; + + if (mdb_vread(&epctx, sizeof (epctx), + (uintptr_t)xd->xd_endout[xep->xep_num]) != sizeof (epctx)) { + mdb_warn("failed to read endpoint context at %p", + xd->xd_endout[xep->xep_num]); + return (WALK_ERR); + } + + eptype = XHCI_EPCTX_GET_EPTYPE(LE_32(epctx.xec_info2)); + type = xhci_mdb_epctx_eptypes[eptype]; + state = xhci_mdb_epctx_states[XHCI_EPCTX_STATE(LE_32(epctx.xec_info))]; + + mdb_printf("%-4d %-10s %-10s 0x%-04x 0x%-04x\n", xep->xep_num, type, + state, xep->xep_ring.xr_head, xep->xep_ring.xr_tail); + + return (WALK_NEXT); +} + +/* ARGSUSED */ +static int +xhci_mdb_print_device(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + int count; + xhci_device_t xd; + usba_device_t ud; + char product[256], mfg[256]; + + if (!(flags & DCMD_ADDRSPEC)) { + return (mdb_eval("::walk xhci`xhci | ::walk xhci`xhci_device | " + "::xhci_device")); + } + + if (mdb_vread(&xd, sizeof (xd), addr) != sizeof (xd)) { + mdb_warn("failed to read xhci_device_t at 0x%x", addr); + return (DCMD_ERR); + } + + if (mdb_vread(&ud, sizeof (ud), (uintptr_t)xd.xd_usbdev) != + sizeof (ud)) { + mdb_warn("failed to read usba_device_t at %p\n", xd.xd_usbdev); + return (DCMD_ERR); + } + + if (ud.usb_mfg_str == NULL || mdb_readstr(mfg, sizeof (mfg), + (uintptr_t)ud.usb_mfg_str) <= 0) { + (void) strlcpy(mfg, "Unknown Manufacturer", sizeof (mfg)); + } + + if (ud.usb_product_str == NULL || mdb_readstr(product, sizeof (product), + (uintptr_t)ud.usb_product_str) <= 0) { + (void) strlcpy(product, "Unknown Product", sizeof (product)); + } + + mdb_printf("%<b>%s - %s%</b>\n", mfg, product); + + count = 0; + if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_endpoint_count, &count, + addr) == -1) { + mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr); + return (DCMD_ERR); + } + + mdb_printf("Port %02d | Slot %02d | # Endpoints %02d\n", xd.xd_port, + xd.xd_slot, count); + mdb_printf("%<u>%-4s %-10s %-10s %-6s %-6s%</u>\n", "EP", "Type", + "State", "Head", "Tail"); + + if (mdb_pwalk("xhci`xhci_endpoint", xhci_mdb_print_endpoint_summary, + &xd, addr) == -1) { + mdb_warn("failed to walk xhci_endpoint rooted at 0x%x", addr); + return (DCMD_ERR); + } + + + mdb_printf("\n"); + + return (DCMD_OK); +} + +static int +xhci_mdb_find_trb(uintptr_t addr, uint_t flags, int argc, + const mdb_arg_t *argv) +{ + xhci_ring_t xr; + uint64_t base, max, target; + + if (!(flags & DCMD_ADDRSPEC)) { + mdb_warn("missing required xhci_ring_t\n"); + return (DCMD_USAGE); + } + + if (argc == 0) { + mdb_warn("missing required PA of ring\n"); + return (DCMD_USAGE); + } + + if (argc > 1) { + mdb_warn("too many arguments\n"); + return (DCMD_USAGE); + } + + if (mdb_vread(&xr, sizeof (xr), addr) != sizeof (xr)) { + mdb_warn("failed to read xhci_ring_t at %p", addr); + return (DCMD_USAGE); + } + + if (argv[0].a_type == MDB_TYPE_IMMEDIATE) { + target = argv[0].a_un.a_val; + } else if (argv[0].a_type == MDB_TYPE_STRING) { + target = mdb_strtoull(argv[0].a_un.a_str); + } else { + mdb_warn("argument is an unknown supported type: %d\n", + argv[0].a_type); + return (DCMD_USAGE); + } + target = roundup(target, sizeof (xhci_trb_t)); + + base = xr.xr_dma.xdb_cookies[0].dmac_laddress; + max = base + xr.xr_ntrb * sizeof (xhci_trb_t); + + if (target < base || target > max) { + mdb_warn("target address %p is outside the range of PAs for " + "TRBs in the ring [%p, %p)", target, base, max); + return (DCMD_ERR); + } + target -= base; + mdb_printf("0x%" PRIx64 "\n", target + (uintptr_t)xr.xr_trb); + + return (DCMD_OK); +} + +static const mdb_dcmd_t xhci_dcmds[] = { + { "xhci_epctx", ":", "print endpoint context", + xhci_mdb_print_epctx, NULL }, + { "xhci_slotctx", ":", "print slot context", + xhci_mdb_print_slotctx, NULL }, + { "xhci_trb", ":", "print TRB", + xhci_mdb_print_trb, NULL }, + { "xhci_find", ": -s slot [-e endpiont]", + "find given xhci slot or endpoint", + xhci_mdb_find, NULL }, + { "xhci_device", ":", "device summary", + xhci_mdb_print_device, NULL }, + { "xhci_find_trb", ": pa", "find trb with PA in ring", + xhci_mdb_find_trb, NULL }, + { NULL } +}; + +static const mdb_walker_t xhci_walkers[] = { + { "xhci", "walk list of xhci_t structures", + xhci_mdb_walk_xhci_init, xhci_mdb_walk_xhci_step, NULL }, + { "xhci_device", "walk list of xhci_device_t structures", + xhci_mdb_walk_xhci_device_init, xhci_mdb_walk_xhci_device_step, + NULL }, + { "xhci_endpoint", "walk list of xhci_endpoint_t structures", + xhci_mdb_walk_xhci_endpoint_init, xhci_mdb_walk_xhci_endpoint_step, + NULL }, + { NULL } +}; + +static const mdb_modinfo_t xhci_modinfo = { + MDB_API_VERSION, xhci_dcmds, xhci_walkers +}; + +const mdb_modinfo_t * +_mdb_init(void) +{ + return (&xhci_modinfo); +} diff --git a/usr/src/cmd/mdb/intel/amd64/libpython/Makefile b/usr/src/cmd/mdb/intel/amd64/libpython/Makefile index f454b29658..d78b5e0fc2 100644 --- a/usr/src/cmd/mdb/intel/amd64/libpython/Makefile +++ b/usr/src/cmd/mdb/intel/amd64/libpython/Makefile @@ -42,7 +42,6 @@ include ../../../Makefile.module %.ln := CPPFLAGS += $(PYLNFLAGS) LINTFLAGS += -erroff=E_MACRO_REDEFINED - dmod/$(MODULE) := LDLIBS += -lproc %.o: $(MODSRCS_DIR)/%.c diff --git a/usr/src/cmd/mdb/intel/amd64/libumem/Makefile b/usr/src/cmd/mdb/intel/amd64/libumem/Makefile index c13578207d..5a9b46141d 100644 --- a/usr/src/cmd/mdb/intel/amd64/libumem/Makefile +++ b/usr/src/cmd/mdb/intel/amd64/libumem/Makefile @@ -22,6 +22,7 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2012 Joyent, Inc. All rights reserved. MODULE = libumem.so MDBTGT = proc diff --git a/usr/src/cmd/mdb/intel/amd64/xhci/Makefile b/usr/src/cmd/mdb/intel/amd64/xhci/Makefile new file mode 100644 index 0000000000..0de7a701a7 --- /dev/null +++ b/usr/src/cmd/mdb/intel/amd64/xhci/Makefile @@ -0,0 +1,27 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent, Inc. +# + + +MODULE = xhci.so +MDBTGT = kvm + +MODSRCS = xhci.c + +include ../../../../Makefile.cmd +include ../../../../Makefile.cmd.64 +include ../../Makefile.amd64 +include ../../../Makefile.module + +CPPFLAGS += -I$(SRC)/uts/common diff --git a/usr/src/cmd/mdb/intel/ia32/libumem/Makefile b/usr/src/cmd/mdb/intel/ia32/libumem/Makefile index 51f0985cf2..ca4d07a66a 100644 --- a/usr/src/cmd/mdb/intel/ia32/libumem/Makefile +++ b/usr/src/cmd/mdb/intel/ia32/libumem/Makefile @@ -22,6 +22,7 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2012 Joyent, Inc. All rights reserved. MODULE = libumem.so MDBTGT = proc diff --git a/usr/src/cmd/mdb/intel/ia32/xhci/Makefile b/usr/src/cmd/mdb/intel/ia32/xhci/Makefile new file mode 100644 index 0000000000..6fb72a3b2d --- /dev/null +++ b/usr/src/cmd/mdb/intel/ia32/xhci/Makefile @@ -0,0 +1,25 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2016 Joyent, Inc. +# + +MODULE = xhci.so +MDBTGT = kvm + +MODSRCS = xhci.c + +include ../../../../Makefile.cmd +include ../../Makefile.ia32 +include ../../../Makefile.module + +CPPFLAGS += -I$(SRC)/uts/common diff --git a/usr/src/cmd/mdb/sparc/v7/libumem/Makefile b/usr/src/cmd/mdb/sparc/v7/libumem/Makefile index c9ca51f625..7475d38815 100644 --- a/usr/src/cmd/mdb/sparc/v7/libumem/Makefile +++ b/usr/src/cmd/mdb/sparc/v7/libumem/Makefile @@ -22,6 +22,7 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2012 Joyent, Inc. All rights reserved. MODULE = libumem.so MDBTGT = proc diff --git a/usr/src/cmd/mdb/sparc/v9/libumem/Makefile b/usr/src/cmd/mdb/sparc/v9/libumem/Makefile index dbb51cd96f..cada3a7d9c 100644 --- a/usr/src/cmd/mdb/sparc/v9/libumem/Makefile +++ b/usr/src/cmd/mdb/sparc/v9/libumem/Makefile @@ -22,6 +22,7 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright (c) 2012 Joyent, Inc. All rights reserved. MODULE = libumem.so MDBTGT = proc diff --git a/usr/src/cmd/netfiles/Makefile b/usr/src/cmd/netfiles/Makefile index 0ae84c2276..aa6886311d 100644 --- a/usr/src/cmd/netfiles/Makefile +++ b/usr/src/cmd/netfiles/Makefile @@ -21,6 +21,8 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2019 Joyent, Inc. +# TXTS= nsswitch.conf netconfig hosts services DEFAULTFILES= nss.dfl @@ -29,7 +31,7 @@ include ../Makefile.cmd FILES= hosts services ETCFILES= netconfig nsswitch.conf nsswitch.files nsswitch.nis \ - nsswitch.dns nsswitch.ldap nsswitch.ad + nsswitch.dns nsswitch.ldap nsswitch.ad resolv.conf ROOTNET= $(ROOTETC)/net TICLTS= $(ROOTNET)/ticlts diff --git a/usr/src/cmd/netfiles/nsswitch.conf b/usr/src/cmd/netfiles/nsswitch.conf index 58ec600c9a..166e5e0ca3 100644 --- a/usr/src/cmd/netfiles/nsswitch.conf +++ b/usr/src/cmd/netfiles/nsswitch.conf @@ -29,8 +29,8 @@ passwd: files group: files -hosts: files -ipnodes: files +hosts: files dns mdns +ipnodes: files dns mdns networks: files protocols: files rpc: files diff --git a/usr/src/cmd/netfiles/resolv.conf b/usr/src/cmd/netfiles/resolv.conf new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/usr/src/cmd/netfiles/resolv.conf diff --git a/usr/src/cmd/nicstat/Makefile b/usr/src/cmd/nicstat/Makefile new file mode 100644 index 0000000000..935011119a --- /dev/null +++ b/usr/src/cmd/nicstat/Makefile @@ -0,0 +1,31 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2013 Joyent, Inc. All rights reserved. +# + +PROG = nicstat + +include ../Makefile.cmd + +all: $(PROG) + +install: all .WAIT $(ROOTPROG) + +clean: + +$(ROOTBINPROG): $(PROG) + $(INS.file) + +lint: + +include ../Makefile.targ diff --git a/usr/src/cmd/nicstat/nicstat.pl b/usr/src/cmd/nicstat/nicstat.pl new file mode 100644 index 0000000000..fae4c797df --- /dev/null +++ b/usr/src/cmd/nicstat/nicstat.pl @@ -0,0 +1,424 @@ +#!/usr/perl5/bin/perl -w +# +# nicstat - print network traffic, Kbyte/s read and written. +# Solaris 8+, Perl (Sun::Solaris::Kstat). +# +# "netstat -i" only gives a packet count, this program gives Kbytes. +# +# 04-Apr-2011, ver 1.00J +# +# USAGE: nicstat [-hsz] [-i int[,int...]] | [interval [count]] +# +# -h # help +# -s # print summary output +# -z # skip zero lines +# -i int[,int...] # print these instances only +# eg, +# nicstat # print summary since boot +# nicstat 1 # print continually, every 1 second +# nicstat 1 5 # print 5 times, every 1 second +# nicstat -i hme0 # only examine hme0 +# +# This prints out the KB/s transferred for all the network cards (NICs), +# including packet counts and average sizes. The first line is the summary +# data since boot. +# +# FIELDS: +# Int Interface +# rKB/s read Kbytes/s +# wKB/s write Kbytes/s +# rPk/s read Packets/s +# wPk/s write Packets/s +# rAvs read Average size, bytes +# wAvs write Average size, bytes +# %Util %Utilisation (r or w/ifspeed) +# Sat Saturation (defer, nocanput, norecvbuf, noxmtbuf) +# +# NOTES: +# +# - Some unusual network cards may not provide all the details to Kstat, +# (or provide different symbols). Check for newer versions of this program, +# and the @Network array in the code below. +# - Utilisation is based on bytes transferred divided by speed of the interface +# (if the speed is known). It should be impossible to reach 100% as there +# are overheads due to bus negotiation and timing. +# - Loopback interfaces may only provide packet counts (if anything), and so +# bytes and %util will always be zero. Newer versions of Solaris (newer than +# Solaris 10 6/06) may provide loopback byte stats. +# - Saturation is determined by counting read and write errors caused by the +# interface running at saturation. This approach is not ideal, and the value +# reported is often lower than it should be (eg, 0.0). Reading the rKB/s and +# wKB/s fields may be more useful. +# +# SEE ALSO: +# nicstat.c # the C version, also on my website +# kstat -n hme0 [interval [count]] # or qfe0, ... +# netstat -iI hme0 [interval [count]] +# se netstat.se [interval] # SE Toolkit +# se nx.se [interval] # SE Toolkit +# +# COPYRIGHT: Copyright (c) 2013 Brendan Gregg. +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at docs/cddl1.txt or +# http://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at docs/cddl1.txt. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2015 Joyent, Inc. All rights reserved. +# +# Author: Brendan Gregg [Sydney, Australia] +# +# 18-Jul-2004 Brendan Gregg Created this. +# 07-Jan-2005 " " added saturation value. +# 07-Jan-2005 " " added summary style (from Peter Tribble). +# 23-Jan-2006 " " Tweaked style. +# 11-Aug-2006 " " Improved output neatness. +# 30-Sep-2006 " " Added loopback, tweaked output. +# 04-Apr-2011 brendan@joyent.com Updated for smartmachines. + +use strict; +use Getopt::Std; +use Sun::Solaris::Kstat; +my $Kstat = Sun::Solaris::Kstat->new(); + + +# +# Process command line args +# +usage() if defined $ARGV[0] and $ARGV[0] eq "--help"; +getopts('hi:sz') or usage(); +usage() if defined $main::opt_h; +my $STYLE = defined $main::opt_s ? $main::opt_s : 0; +my $SKIPZERO = defined $main::opt_z ? $main::opt_z : 0; + +# process [interval [count]], +my ($interval, $loop_max); +if (defined $ARGV[0]) { + $interval = $ARGV[0]; + $loop_max = defined $ARGV[1] ? $ARGV[1] : 2**32; + usage() if $interval == 0; +} +else { + $interval = 1; + $loop_max = 1; +} + +# check for -i, +my %NetworkOnly; # network interfaces to print +my $NETWORKONLY = 0; # match on network interfaces +if (defined $main::opt_i) { + foreach my $net (split /,/, $main::opt_i) { + $NetworkOnly{$net} = 1; + } + $NETWORKONLY = 1; +} + +# globals, +my $loop = 0; # current loop number +my $PAGESIZE = 20; # max lines per header +my $line = $PAGESIZE; # counter for lines printed +my %NetworkNames; # Kstat network interfaces +my %NetworkData; # network interface data +my %NetworkDataOld; # network interface data +$main::opt_h = 0; +$| = 1; # autoflush + +# kstat "link" module includes: +my @Network = qw(dmfe bge be bnx ce eri eth external ge hme igb ige internal ixgbe le net ppp qfe rtls); +my %Network; +$Network{$_} = 1 foreach (@Network); +my $ZONENAME = `/usr/bin/zonename`; +chomp $ZONENAME; + +### Determine network interfaces +unless (find_nets()) { + if ($NETWORKONLY) { + print STDERR "ERROR1: $main::opt_i matched no network interfaces.\n"; + } + else { + print STDERR "ERROR1: No network interfaces found!\n"; + } + exit 1; +} + + +# +# Main +# +while (1) { + + ### Print Header + if ($line >= $PAGESIZE) { + if ($STYLE == 0) { + printf "%8s %12s %7s %7s %7s %7s %7s %7s %6s %5s\n", + "Time", "Int", "rKB/s", "wKB/s", "rPk/s", "wPk/s", "rAvs", + "wAvs", "%Util", "Sat"; + } + elsif ($STYLE == 1) { + printf "%8s %12s %14s %14s\n", "Time", "Int", "rKB/s", "wKB/s"; + } + + $line = 0; + } + + ### Get new data + my (@NetworkData) = fetch_net_data(); + + foreach my $network_data (@NetworkData) { + + ### Extract values + my ($int, $rbytes, $wbytes, $rpackets, $wpackets, $speed, $sat, $time) + = split /:/, $network_data; + + ### Retrieve old values + my ($old_rbytes, $old_wbytes, $old_rpackets, $old_wpackets, $old_sat, + $old_time); + if (defined $NetworkDataOld{$int}) { + ($old_rbytes, $old_wbytes, $old_rpackets, $old_wpackets, + $old_sat, $old_time) = split /:/, $NetworkDataOld{$int}; + } + else { + $old_rbytes = $old_wbytes = $old_rpackets = $old_wpackets + = $old_sat = $old_time = 0; + } + + # + # Calculate statistics + # + + # delta time + my $tdiff = $time - $old_time; + + # per second values + my $rbps = ($rbytes - $old_rbytes) / $tdiff; + my $wbps = ($wbytes - $old_wbytes) / $tdiff; + my $rkps = $rbps / 1024; + my $wkps = $wbps / 1024; + my $rpps = ($rpackets - $old_rpackets) / $tdiff; + my $wpps = ($wpackets - $old_wpackets) / $tdiff; + my $ravs = $rpps > 0 ? $rbps / $rpps : 0; + my $wavs = $wpps > 0 ? $wbps / $wpps : 0; + + # skip zero lines if asked + next if $SKIPZERO and ($rbps + $wbps) == 0; + + # % utilisation + my $util; + if ($speed > 0) { + # the following has a mysterious "800", it is 100 + # for the % conversion, and 8 for bytes2bits. + my $rutil = $rbps * 800 / $speed; + my $wutil = $wbps * 800 / $speed; + $util = $rutil > $wutil ? $rutil : $wutil; + $util = 100 if $util > 100; + } + else { + $util = 0; + } + + # saturation per sec + my $sats = ($sat - $old_sat) / $tdiff; + + # + # Print statistics + # + if ($rbps ne "") { + my @Time = localtime(); + + if ($STYLE == 0) { + printf "%02d:%02d:%02d %12s ", + $Time[2], $Time[1], $Time[0], $int; + print_neat($rkps); + print_neat($wkps); + print_neat($rpps); + print_neat($wpps); + print_neat($ravs); + print_neat($wavs); + printf "%6.2f %5.2f\n", $util, $sats; + } + elsif ($STYLE == 1) { + printf "%02d:%02d:%02d %12s %14.3f %14.3f\n", + $Time[2], $Time[1], $Time[0], $int, $rkps, $wkps; + } + + $line++; + + # for multiple interfaces, always print the header + $line += $PAGESIZE if @NetworkData > 1; + } + + ### Store old values + $NetworkDataOld{$int} + = "$rbytes:$wbytes:$rpackets:$wpackets:$sat:$time"; + } + + ### Check for end + last if ++$loop == $loop_max; + + ### Interval + sleep $interval; +} + + +# find_nets - walk Kstat to discover network interfaces. +# +# This walks %Kstat and populates a %NetworkNames with discovered +# network interfaces. +# +sub find_nets { + my $found = 0; + + ### Loop over all Kstat modules + foreach my $module (keys %$Kstat) { + my $Modules = $Kstat->{$module}; + + foreach my $instance (keys %$Modules) { + my $Instances = $Modules->{$instance}; + + foreach my $name (keys %$Instances) { + + ### Skip interface if asked + if ($NETWORKONLY) { + next unless $NetworkOnly{$name}; + } + + my $Names = $Instances->{$name}; + + # Check this is a network device. + # Matching on ifspeed has been more reliable than "class" + # we also match loopback and "link" interfaces. + if (defined $$Names{ifspeed} || $module eq "lo" + || $module eq "link") { + next if $name eq "mac"; + if ($module eq "link") { + my $nname = $name; + $nname =~ s/\d+$//; + next unless defined $Network{$nname} + or $ZONENAME eq $nname + or $ZONENAME eq "global"; + } + ### Save network interface + $NetworkNames{$name} = $Names; + $found++; + } + } + } + } + + return $found; +} + +# fetch - fetch Kstat data for the network interfaces. +# +# This uses the interfaces in %NetworkNames and returns useful Kstat data. +# The Kstat values used are rbytes64, obytes64, ipackets64, opackets64 +# (or the 32 bit versions if the 64 bit values are not there). +# +sub fetch_net_data { + my ($rbytes, $wbytes, $rpackets, $wpackets, $speed, $time); + my @NetworkData = (); + + $Kstat->update(); + + ### Loop over previously found network interfaces + foreach my $name (sort keys %NetworkNames) { + my $Names = $NetworkNames{$name}; + + if (defined $$Names{opackets}) { + + ### Fetch write bytes + if (defined $$Names{obytes64}) { + $rbytes = $$Names{rbytes64}; + $wbytes = $$Names{obytes64}; + } + elsif (defined $$Names{obytes}) { + $rbytes = $$Names{rbytes}; + $wbytes = $$Names{obytes}; + } else { + $rbytes = $wbytes = 0; + } + + ### Fetch read bytes + if (defined $$Names{opackets64}) { + $rpackets = $$Names{ipackets64}; + $wpackets = $$Names{opackets64}; + } + else { + $rpackets = $$Names{ipackets}; + $wpackets = $$Names{opackets}; + } + + ### Fetch interface speed + if (defined $$Names{ifspeed}) { + $speed = $$Names{ifspeed}; + } + else { + # if we can't fetch the speed, print the + # %Util as 0.0 . To do this we, + $speed = 2 ** 48; + } + + ### Determine saturation value + my $sat = 0; + if (defined $$Names{nocanput} or defined $$Names{norcvbuf}) { + $sat += defined $$Names{defer} ? $$Names{defer} : 0; + $sat += defined $$Names{nocanput} ? $$Names{nocanput} : 0; + $sat += defined $$Names{norcvbuf} ? $$Names{norcvbuf} : 0; + $sat += defined $$Names{noxmtbuf} ? $$Names{noxmtbuf} : 0; + } + + ### use the last snaptime value, + $time = $$Names{snaptime}; + + ### store data + push @NetworkData, "$name:$rbytes:$wbytes:" . + "$rpackets:$wpackets:$speed:$sat:$time"; + } + } + + return @NetworkData; +} + +# print_neat - print a float with decimal places if appropriate. +# +# This specifically keeps the width to 7 characters, if possible, plus +# a trailing space. +# +sub print_neat { + my $num = shift; + if ($num >= 100000) { + printf "%7d ", $num; + } elsif ($num >= 100) { + printf "%7.1f ", $num; + } else { + printf "%7.2f ", $num; + } +} + +# usage - print usage and exit. +# +sub usage { + print STDERR <<END; +USAGE: nicstat [-hsz] [-i int[,int...]] | [interval [count]] + eg, nicstat # print summary since boot + nicstat 1 # print continually every 1 second + nicstat 1 5 # print 5 times, every 1 second + nicstat -s # summary output + nicstat -i hme0 # print hme0 only +END + exit 1; +} diff --git a/usr/src/cmd/nsadmin/Makefile b/usr/src/cmd/nsadmin/Makefile index 533351bd86..45bdcc6701 100644 --- a/usr/src/cmd/nsadmin/Makefile +++ b/usr/src/cmd/nsadmin/Makefile @@ -25,64 +25,85 @@ # Copyright 2019 Joyent, Inc. # -PROG= profile .login ksh.kshrc system -PROGSKEL= .profile .kshrc -PROGROOT= .profile .bashrc +ETCFILES = profile .login ksh.kshrc system zshrc +ETCSKELFILES = .profile .kshrc .bashrc +ETCBASHFILES = bash_completion +ETCBASHCOMPLETIONDFILES = dladm zones zone_alias +ROOTFILES = .profile .bashrc .bash_profile include ../Makefile.cmd -ROOTROOT= $(ROOT)/root +ROOTETCFILES = $(ETCFILES:%=$(ROOTETC)/%) ROOTETCSKEL= $(ROOTETC)/skel -ROOTETCSKELPROG= $(PROGSKEL:%=$(ROOTETCSKEL)/%) +ROOTETCSKELFILES = $(ETCSKELFILES:%=$(ROOTETCSKEL)/%) ROOTETCVERSIONS= $(ROOTETC)/versions -ROOTETCVERSIONSPROG= $(ROOTETCVERSIONS)/build -ROOTROOTPROG= $(PROGROOT:%=$(ROOTROOT)/%) +ROOTETCVERSIONSFILES = $(ROOTETCVERSIONS)/build +ETCBASHDIR = $(ROOTETC)/bash +ROOTETCBASHFILES = $(ETCBASHFILES:%=$(ETCBASHDIR)/%) +ETCBASHCOMPLETIONDDIR = $(ETCBASHDIR)/bash_completion.d +ROOTETCBASHCOMPLETIONDFILES = \ + $(ETCBASHCOMPLETIONDFILES:%=$(ETCBASHCOMPLETIONDDIR)/%) +ROOTROOT= $(ROOT)/root +ROOTROOTFILES = $(ROOTFILES:%=$(ROOTROOT)/%) + +FILES = $(ROOTETCFILES) $(ROOTETCSKELFILES) \ + $(ROOTETCBASHFILES) $(ROOTETCBASHCOMPLETIONDFILES) \ + $(ROOTROOTFILES) $(ROOTETCVERSIONSFILES) + FILEMODE= 0644 -CLOBBERFILES = profile .bashrc .profile .login .kshrc +.KEEP_STATE: + +all: + +# Use $^ instead of $< - it's broken without % +INS.file = $(RM) $@; $(INS) -s -m $(FILEMODE) -f $(@D) $^ +INS.rename = $(INS.file); $(MV) $(@D)/$(^F) $@ + +$(ROOTROOT) \ +$(ROOTETCVERSIONS) \ +$(ETCBASHDIR) \ +$(ROOTETCBASHCOMPLETIONDDIR) \ +$(ROOTETCSKEL): + $(INS.dir) -.login: login.csh - $(RM) .login - $(CP) login.csh .login +$(ROOTETC)/profile: etc-profile.sh + $(INS.rename) -profile: etc-profile.sh - $(RM) profile - $(CP) etc-profile.sh $@ +$(ROOTETC)/.login: login.csh + $(INS.rename) -.profile: dot-profile.sh - $(RM) .profile - $(CP) dot-profile.sh $@ +# skel file and root's default +$(ROOTROOT)/.kshrc $(ROOTETCSKEL)/.kshrc: dot-kshrc.sh + $(INS.rename) -.kshrc: dot-kshrc.sh - $(RM) .kshrc - $(CP) dot-kshrc.sh $@ +# skel file and root's default +$(ROOTROOT)/.profile $(ROOTETCSKEL)/.profile: dot-profile.sh + $(INS.rename) -.bashrc: bashrc.sh - $(RM) .bashrc - $(CP) bashrc.sh $@ +$(ROOTETCSKEL)/.bashrc: etc-skel-bashrc.sh + $(INS.rename) -.KEEP_STATE: +$(ROOTROOT)/.bash_profile: dot-bash_profile.sh + $(INS.rename) -all: $(PROG) $(PROGSKEL) +$(ROOTROOT)/.bashrc: dot-bashrc.sh + $(INS.rename) -$(ROOTROOT)/% \ -$(ROOTETCSKEL)/%: % +$(ROOTETCBASHDIR)/%: % $(INS.file) -$(ROOTROOT) \ -$(ROOTETCVERSIONS) \ -$(ROOTETCSKEL): - $(INS.dir) +$(ROOTETCBASHCOMPLETIONDDIR)/%: % + $(INS.file) -$(ROOTETCVERSIONSPROG): $(ROOTETCVERSIONS) FRC +$(ROOTETCVERSIONSFILES): $(ROOTETCVERSIONS) FRC if [[ -n "$$BUILDVERSION_EXEC" ]]; then \ - $$BUILDVERSION_EXEC >$(ROOTETCVERSIONSPROG) ; \ + $$BUILDVERSION_EXEC >$(ROOTETCVERSIONSFILES) ; \ else \ - touch $(ROOTETCVERSIONSPROG) ; \ + touch $(ROOTETCVERSIONSFILES) ; \ fi -install: all $(ROOTETCSKEL) $(ROOTETCPROG) \ - $(ROOTETCSKELPROG) $(ROOTROOTPROG) $(ROOTETCVERSIONSPROG) +install: all $(FILES) clean: diff --git a/usr/src/cmd/nsadmin/bash/bash_completion b/usr/src/cmd/nsadmin/bash/bash_completion new file mode 100755 index 0000000000..71f3e9e63f --- /dev/null +++ b/usr/src/cmd/nsadmin/bash/bash_completion @@ -0,0 +1,9417 @@ +# +# This file contains an example set of shell completions that can be used with +# bash(1). These completions allow a user to complete filenames, commands +# name, command line options, and command line arguments using the [tab] key. +# The completions defined here are specific to the GNU command set, as a result +# they will provide the choice of GNU command line options in response to the +# [tab] key. For the completed options to match the command implementation, +# you may have to have /usr/gnu/bin at the head of your PATH. +# +# These completions are not included in the default bash(1) environment. To +# include them in the default environment, it is recommended that this file be +# copied to /etc/bash/bash_completion and be sourced from either a system wide +# bashrc in /etc/bash/bashrc or individual bashrcs in ~/.bashrc via +# [ -f /etc/bash/bash_completion ] && . /etc/bash/bash_completion +# + +# bash_completion - programmable completion functions for bash 3.x +# (backwards compatible with bash 2.05b) +# +# $Id: bash_completion,v 1.872 2006/03/01 16:20:18 ianmacd Exp $ +# +# Copyright (C) Ian Macdonald <ian@caliban.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# The latest version of this software can be obtained here: +# +# http://www.caliban.org/bash/index.shtml#completion +# +# RELEASE: 20060301 + +if [[ $- == *v* ]]; then + BASH_COMPLETION_ORIGINAL_V_VALUE="-v" +else + BASH_COMPLETION_ORIGINAL_V_VALUE="+v" +fi + +if [[ -n $BASH_COMPLETION_DEBUG ]]; then + set -v +else + set +v +fi + +# Alter the following to reflect the location of this file. +# +[ -n "$BASH_COMPLETION" ] || BASH_COMPLETION=/etc/bash/bash_completion +[ -n "$BASH_COMPLETION_DIR" ] || BASH_COMPLETION_DIR=/etc/bash/bash_completion.d +readonly BASH_COMPLETION BASH_COMPLETION_DIR + +# Set a couple of useful vars +# +UNAME=$( uname -s ) +# strip OS type and version under Cygwin (e.g. CYGWIN_NT-5.1 => Cygwin) +UNAME=${UNAME/CYGWIN_*/Cygwin} +RELEASE=$( uname -r ) + +# features supported by bash 2.05 and higher +if [ ${BASH_VERSINFO[0]} -eq 2 ] && [[ ${BASH_VERSINFO[1]} > 04 ]] || + [ ${BASH_VERSINFO[0]} -gt 2 ]; then + declare -r bash205=$BASH_VERSION 2>/dev/null || : + default="-o default" + dirnames="-o dirnames" + filenames="-o filenames" +fi +# features supported by bash 2.05b and higher +if [ ${BASH_VERSINFO[0]} -eq 2 ] && [[ ${BASH_VERSINFO[1]} = "05b" ]] || + [ ${BASH_VERSINFO[0]} -gt 2 ]; then + declare -r bash205b=$BASH_VERSION 2>/dev/null || : + nospace="-o nospace" +fi +# features supported by bash 3.0 and higher +if [ ${BASH_VERSINFO[0]} -gt 2 ]; then + declare -r bash3=$BASH_VERSION 2>/dev/null || : + bashdefault="-o bashdefault" + plusdirs="-o plusdirs" +fi + +# Turn on extended globbing and programmable completion +shopt -s extglob progcomp + +# A lot of the following one-liners were taken directly from the +# completion examples provided with the bash 2.04 source distribution + +# Make directory commands see only directories +complete -d pushd + +# The following section lists completions that are redefined later +# Do NOT break these over multiple lines. +# +# START exclude -- do NOT remove this line +complete -f -X '!*.?(t)bz?(2)' bunzip2 bzcat bzcmp bzdiff bzegrep bzfgrep bzgrep +complete -f -X '!*.@(zip|ZIP|jar|JAR|exe|EXE|pk3|war|wsz|ear|zargo|xpi|sxw|ott)' unzip zipinfo +complete -f -X '*.Z' compress znew +complete -f -X '!*.@(Z|gz|tgz|Gz|dz)' gunzip zcmp zdiff zcat zegrep zfgrep zgrep zless zmore +complete -f -X '!*.Z' uncompress +complete -f -X '!*.@(gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX)' ee display +complete -f -X '!*.@(gif|jp?(e)g|tif?(f)|png|p[bgp]m|bmp|x[bp]m|rle|rgb|pcx|fits|pm|GIF|JPG|JP?(E)G|TIF?(F)|PNG|P[BGP]M|BMP|X[BP]M|RLE|RGB|PCX|FITS|PM)' xv qiv +complete -f -X '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ|.bz2|.BZ2|.Z))' gv ggv kghostview +complete -f -X '!*.@(dvi|DVI)?(.@(gz|Z|bz2))' xdvi +complete -f -X '!*.@(dvi|DVI)?(.@(gz|bz2))' kdvi +complete -f -X '!*.@(dvi|DVI)' dvips dviselect dvitype dvipdf advi dvipdfm dvipdfmx +complete -f -X '!*.@(pdf|PDF)' acroread gpdf xpdf +complete -f -X '!*.@(?(e)ps|?(E)PS|pdf|PDF)' kpdf +complete -f -X '!*.@(@(?(e)ps|?(E)PS|pdf|PDF)?(.gz|.GZ)|cb(r|z)|CB(R|Z)|djv?(u)|DJV?(U)||dvi|DVI|gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX)' evince +complete -f -X '!*.@(?(e)ps|?(E)PS)' ps2pdf +complete -f -X '!*.texi*' makeinfo texi2html +complete -f -X '!*.@(?(la)tex|?(LA)TEX|texi|TEXI|dtx|DTX|ins|INS)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi +complete -f -X '!*.@(mp3|MP3)' mpg123 mpg321 madplay +complete -f -X '!*.@(mp?(e)g|MP?(E)G|wma|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|viv|rm|ram|yuv|mov|MOV|qt|QT|wmv|mp3|MP3|m4v|M4V|ogg|OGG|ogm|OGM|mp4|MP4|wav|WAV|asx|ASX|mng|MNG|srt)' xine aaxine fbxine kaffeine +complete -f -X '!*.@(avi|asf|wmv)' aviplay +complete -f -X '!*.@(rm?(j)|ra?(m)|smi?(l))' realplay +complete -f -X '!*.@(mpg|mpeg|avi|mov|qt)' xanim +complete -f -X '!*.@(ogg|OGG|m3u|flac|spx)' ogg123 +complete -f -X '!*.@(mp3|MP3|ogg|OGG|pls|m3u)' gqmpeg freeamp +complete -f -X '!*.fig' xfig +complete -f -X '!*.@(mid?(i)|MID?(I))' playmidi +complete -f -X '!*.@(mid?(i)|MID?(I)|rmi|RMI|rcp|RCP|[gr]36|[GR]36|g18|G18|mod|MOD|xm|XM|it|IT|x3m|X3M)' timidity +complete -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|CLASS)' vi vim gvim rvim view rview rgvim rgview gview +complete -f -X '*.@(o|so|so.!(conf)|a|rpm|gif|GIF|jp?(e)g|JP?(E)G|mp3|MP3|mp?(e)g|MPG|avi|AVI|asf|ASF|ogg|OGG|class|CLASS)' emacs +complete -f -X '!*.@(exe|EXE|com|COM|scr|SCR|exe.so)' wine +complete -f -X '!*.@(zip|ZIP|z|Z|gz|GZ|tgz|TGZ)' bzme +complete -f -X '!*.@(?([xX]|[sS])[hH][tT][mM]?([lL]))' netscape mozilla lynx opera galeon curl dillo elinks amaya +complete -f -X '!*.@(sxw|stw|sxg|sgl|doc|dot|rtf|txt|htm|html|odt|ott|odm)' oowriter +complete -f -X '!*.@(sxi|sti|pps|ppt|pot|odp|otp)' ooimpress +complete -f -X '!*.@(sxc|stc|xls|xlw|xlt|csv|ods|ots)' oocalc +complete -f -X '!*.@(sxd|std|sda|sdd|odg|otg)' oodraw +complete -f -X '!*.@(sxm|smf|mml|odf)' oomath +complete -f -X '!*.odb' oobase +complete -f -X '!*.rpm' rpm2cpio +# FINISH exclude -- do not remove this line + +# start of section containing compspecs that can be handled within bash + +# user commands see only users +complete -u su usermod userdel passwd chage write chfn groups slay w sux + +# group commands see only groups +[ -n "$bash205" ] && complete -g groupmod groupdel newgrp 2>/dev/null + +# bg completes with stopped jobs +complete -A stopped -P '%' bg + +# other job commands +complete -j -P '%' fg jobs disown + +# readonly and unset complete with shell variables +complete -v readonly unset + +# set completes with set options +complete -A setopt set + +# shopt completes with shopt options +complete -A shopt shopt + +# helptopics +complete -A helptopic help + +# unalias completes with aliases +complete -a unalias + +# bind completes with readline bindings (make this more intelligent) +complete -A binding bind + +# type and which complete on commands +complete -c command type which + +# builtin completes on builtins +complete -b builtin + +# start of section containing completion functions called by other functions + +# This function checks whether we have a given program on the system. +# No need for bulky functions in memory if we don't. +# +have() +{ + unset -v have + PATH=/usr/gnu/bin:$PATH:/sbin:/usr/sbin type $1 &>/dev/null && + have="yes" +} + +# use GNU sed if we have it, since its extensions are still used in our code +# +[ $UNAME != Linux ] && have gsed && alias sed=gsed + +# This function checks whether a given readline variable +# is `on'. +# +_rl_enabled() +{ + [[ "$( bind -v )" = *$1+([[:space:]])on* ]] +} + +# This function shell-quotes the argument +quote() +{ + echo \'${1//\'/\'\\\'\'}\' #'# Help vim syntax highlighting +} + +# This function quotes the argument in a way so that readline dequoting +# results in the original argument +quote_readline() +{ + local t="${1//\\/\\\\}" + echo \'${t//\'/\'\\\'\'}\' #'# Help vim syntax highlighting +} + +# This function shell-dequotes the argument +dequote() +{ + eval echo "$1" +} + + +# Get the word to complete +# This is nicer than ${COMP_WORDS[$COMP_CWORD]}, since it handles cases +# where the user is completing in the middle of a word. +# (For example, if the line is "ls foobar", +# and the cursor is here --------> ^ +# it will complete just "foo", not "foobar", which is what the user wants.) +_get_cword() +{ + if [[ "${#COMP_WORDS[COMP_CWORD]}" -eq 0 ]] || [[ "$COMP_POINT" == "${#COMP_LINE}" ]]; then + echo "${COMP_WORDS[COMP_CWORD]}" + else + local i + local cur="$COMP_LINE" + local index="$COMP_POINT" + for (( i = 0; i <= COMP_CWORD; ++i )); do + while [[ "${#cur}" -ge ${#COMP_WORDS[i]} ]] && [[ "${cur:0:${#COMP_WORDS[i]}}" != "${COMP_WORDS[i]}" ]]; do + cur="${cur:1}" + index="$(( index - 1 ))" + done + if [[ "$i" -lt "$COMP_CWORD" ]]; then + local old_size="${#cur}" + cur="${cur#${COMP_WORDS[i]}}" + local new_size="${#cur}" + index="$(( index - old_size + new_size ))" + fi + done + + if [[ "${COMP_WORDS[COMP_CWORD]:0:${#cur}}" != "$cur" ]]; then + # We messed up! At least return the whole word so things keep working + echo "${COMP_WORDS[COMP_CWORD]}" + else + echo "${cur:0:$index}" + fi + fi +} + + +# This function performs file and directory completion. It's better than +# simply using 'compgen -f', because it honours spaces in filenames. +# If passed -d, it completes only on directories. If passed anything else, +# it's assumed to be a file glob to complete on. +# +_filedir() +{ + local IFS=$'\t\n' xspec + + _expand || return 0 + + local toks=( ) tmp + while read -r tmp; do + [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp + done < <( compgen -d -- "$(quote_readline "$cur")" ) + + if [[ "$1" != -d ]]; then + xspec=${1:+"!*.$1"} + while read -r tmp; do + [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp + done < <( compgen -f -X "$xspec" -- "$(quote_readline "$cur")" ) + fi + + COMPREPLY=( "${COMPREPLY[@]}" "${toks[@]}" ) +} + +# This function completes on signal names +# +_signals() +{ + local i + + # standard signal completion is rather braindead, so we need + # to hack around to get what we want here, which is to + # complete on a dash, followed by the signal name minus + # the SIG prefix + COMPREPLY=( $( compgen -A signal SIG${cur#-} )) + for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do + COMPREPLY[i]=-${COMPREPLY[i]#SIG} + done +} + +# This function completes on configured network interfaces +# +_configured_interfaces() +{ + if [ -f /etc/debian_version ]; then + # Debian system + COMPREPLY=( $( sed -ne 's|^iface \([^ ]\+\).*$|\1|p' \ + /etc/network/interfaces ) ) + elif [ -f /etc/SuSE-release ]; then + # SuSE system + COMPREPLY=( $( command ls \ + /etc/sysconfig/network/ifcfg-* | \ + sed -ne 's|.*ifcfg-\('$cur'.*\)|\1|p' ) ) + elif [ -f /etc/pld-release ]; then + # PLD Linux + COMPREPLY=( $( command ls -B \ + /etc/sysconfig/interfaces | \ + sed -ne 's|.*ifcfg-\('$cur'.*\)|\1|p' ) ) + else + # Assume Red Hat + COMPREPLY=( $( command ls \ + /etc/sysconfig/network-scripts/ifcfg-* | \ + sed -ne 's|.*ifcfg-\('$cur'.*\)|\1|p' ) ) + fi +} + +# This function completes on all available network interfaces +# -a: restrict to active interfaces only +# -w: restrict to wireless interfaces only +# +_available_interfaces() +{ + local cmd + + if [ "${1:-}" = -w ]; then + cmd="iwconfig" + elif [ "${1:-}" = -a ]; then + cmd="ifconfig" + else + cmd="ifconfig -a" + fi + + COMPREPLY=( $( eval $cmd 2>/dev/null | \ + sed -ne 's|^\('$cur'[^[:space:][:punct:]]\{1,\}\).*$|\1|p') ) +} + +# This function expands tildes in pathnames +# +_expand() +{ + # FIXME: Why was this here? + # [ "$cur" != "${cur%\\}" ] && cur="$cur\\" + + # expand ~username type directory specifications + if [[ "$cur" == \~*/* ]]; then + eval cur=$cur + elif [[ "$cur" == \~* ]]; then + cur=${cur#\~} + COMPREPLY=( $( compgen -P '~' -u $cur ) ) + return ${#COMPREPLY[@]} + fi +} + +# This function completes on process IDs. +# AIX and Solaris ps prefers X/Open syntax. +[ $UNAME = SunOS -o $UNAME = AIX ] && +_pids() +{ + COMPREPLY=( $( compgen -W '$( command ps -efo pid | sed 1d )' -- $cur )) +} || +_pids() +{ + COMPREPLY=( $( compgen -W '$( command ps axo pid | sed 1d )' -- $cur ) ) +} + +# This function completes on process group IDs. +# AIX and SunOS prefer X/Open, all else should be BSD. +[ $UNAME = SunOS -o $UNAME = AIX ] && +_pgids() +{ + COMPREPLY=( $( compgen -W '$( command ps -efo pgid | sed 1d )' -- $cur )) +} || +_pgids() +{ + COMPREPLY=( $( compgen -W '$( command ps axo pgid | sed 1d )' -- $cur )) +} + +# This function completes on user IDs +# +_uids() +{ + if type getent &>/dev/null; then + COMPREPLY=( $( getent passwd | \ + awk -F: '{if ($3 ~ /^'$cur'/) print $3}' ) ) + elif type perl &>/dev/null; then + COMPREPLY=( $( compgen -W '$( perl -e '"'"'while (($uid) = (getpwent)[2]) { print $uid . "\n" }'"'"' )' -- $cur ) ) + else + # make do with /etc/passwd + COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($3 ~ /^'$cur'/) print $3}'\ + /etc/passwd ) ) + fi +} + +# This function completes on group IDs +# +_gids() +{ + if type getent &>/dev/null; then + COMPREPLY=( $( getent group | \ + awk -F: '{if ($3 ~ /^'$cur'/) print $3}' ) ) + elif type perl &>/dev/null; then + COMPREPLY=( $( compgen -W '$( perl -e '"'"'while (($gid) = (getgrent)[2]) { print $gid . "\n" }'"'"' )' -- $cur ) ) + else + # make do with /etc/group + COMPREPLY=( $( awk 'BEGIN {FS=":"} {if ($3 ~ /^'$cur'/) print $3}'\ + /etc/group ) ) + fi +} + +# This function completes on services +# +_services() +{ + local sysvdir famdir + [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d || sysvdir=/etc/init.d + famdir=/etc/xinetd.d + COMPREPLY=( $( builtin echo $sysvdir/!(*.rpmsave|*.rpmorig|*~|functions)) ) + + if [ -d $famdir ]; then + COMPREPLY=( "${COMPREPLY[@]}" $( builtin echo $famdir/!(*.rpmsave|*.rpmorig|*~)) ) + fi + + COMPREPLY=( $( compgen -W '${COMPREPLY[@]#@($sysvdir|$famdir)/}' -- $cur ) ) +} + +# This function complete on modules +# +_modules() +{ + local modpath + modpath=/lib/modules/$1 + COMPREPLY=( $( command ls -R $modpath | \ + sed -ne 's/^\('$cur'.*\)\.k\?o\(\|.gz\)$/\1/p') ) +} + +# this function complete on user:group format +# +_usergroup() +{ + local IFS=$'\n' + cur=${cur//\\\\ / } + if [[ $cur = *@(\\:|.)* ]] && [ -n "$bash205" ]; then + user=${cur%%*([^:.])} + COMPREPLY=( $(compgen -P ${user/\\\\} -g -- ${cur##*[.:]}) ) + elif [[ $cur = *:* ]] && [ -n "$bash205" ]; then + COMPREPLY=( $( compgen -g -- ${cur##*[.:]} ) ) + else + COMPREPLY=( $( compgen -S : -u -- $cur ) ) + fi +} + +# this function count the number of mandatory args +# +_count_args() +{ + args=1 + for (( i=1; i < COMP_CWORD; i++ )); do + if [[ "${COMP_WORDS[i]}" != -* ]]; then + args=$(($args+1)) + fi + done +} + +# start of section containing completion functions for bash built-ins + +# bash alias completion +# +_alias() +{ + local cur + + COMPREPLY=() + cur=${COMP_WORDS[$COMP_CWORD]} + + case "$COMP_LINE" in + *[^=]) + COMPREPLY=( $( compgen -A alias -S '=' -- $cur ) ) + ;; + *=) + COMPREPLY=( "$( alias ${cur%=} 2>/dev/null | \ + sed -e 's|^alias '$cur'\(.*\)$|\1|' )" ) + ;; + esac +} +complete -F _alias $nospace alias + +# bash export completion +# +_export() +{ + local cur + + COMPREPLY=() + cur=${COMP_WORDS[$COMP_CWORD]} + + case "$COMP_LINE" in + *=\$*) + COMPREPLY=( $( compgen -v -P '$' -- ${cur#*=\$} ) ) + ;; + *[^=]) + COMPREPLY=( $( compgen -v -S '=' -- $cur ) ) + ;; + *=) + COMPREPLY=( "$( eval echo -n \"$`echo ${cur%=}`\" | + ( echo -n \' + sed -e 's/'\''/'\''\\\'\'''\''/g' + echo -n \' ) )" ) + ;; + esac +} +complete -F _export $default $nospace export + +# bash shell function completion +# +_function() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [[ $1 == @(declare|typeset) ]]; then + if [ "$prev" = -f ]; then + COMPREPLY=( $( compgen -A function -- $cur ) ) + elif [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-a -f -F -i -r -x -p' -- \ + $cur ) ) + fi + elif [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $( compgen -A function -- $cur ) ) + else + COMPREPLY=( "() $( type -- ${COMP_WORDS[1]} | sed -e 1,2d )" ) + fi +} +complete -F _function function declare typeset + +# bash complete completion +# +_complete() +{ + local cur prev options + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case $prev in + -o) + options="default dirnames filenames" + [ -n "$bash205b" ] && options="$options nospace" + [ -n "$bash3" ] && options="$options bashdefault plusdirs" + COMPREPLY=( $( compgen -W "$options" -- $cur ) ) + return 0 + ;; + + -A) + COMPREPLY=( $( compgen -W 'alias arrayvar binding \ + builtin command directory disabled enabled \ + export file function group helptopic hostname \ + job keyword running service setopt shopt \ + signal stopped user variable' -- $cur ) ) + return 0 + ;; + + -C) + COMPREPLY=( $( compgen -A command -- $cur ) ) + return 0 + ;; + -F) + COMPREPLY=( $( compgen -A function -- $cur ) ) + return 0 + ;; + -@(p|r)) + COMPREPLY=( $( complete -p | sed -e 's|.* ||' | \ + grep "^$cur" ) ) + return 0 + ;; + + esac + + if [[ "$cur" == -* ]]; then + # relevant options completion + options="-a -b -c -d -e -f -g -j -k -s -v -u -A -G -W -P -S -X -F -C" + [ -n "$bash205" ] && options="$options -o" + COMPREPLY=( $( compgen -W "$options" -- $cur ) ) + else + COMPREPLY=( $( compgen -A command -- $cur ) ) + fi +} +complete -F _complete complete + +# start of section containing completion functions for external programs + +# a little help for FreeBSD ports users +[ $UNAME = FreeBSD ] && complete -W 'index search fetch fetch-list \ + extract patch configure build install reinstall \ + deinstall clean clean-depends kernel buildworld' make + +# This completes on a list of all available service scripts for the +# 'service' command and/or the SysV init.d directory, followed by +# that script's available commands +# +{ have service || [ -d /etc/init.d/ ]; } && +_service() +{ + local cur sysvdir + + COMPREPLY=() + prev=${COMP_WORDS[COMP_CWORD-1]} + cur=`_get_cword` + + # don't complete for things like killall, ssh and mysql if it's + # the standalone command, rather than the init script + [[ ${COMP_WORDS[0]} != @(*init.d/!(functions|~)|service) ]] && return 0 + + # don't complete past 2nd token + [ $COMP_CWORD -gt 2 ] && return 0 + + [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d \ + || sysvdir=/etc/init.d + + if [[ $COMP_CWORD -eq 1 ]] && [[ $prev == "service" ]]; then + _services + else + COMPREPLY=( $( compgen -W '`sed -ne "y/|/ /; \ + s/^.*Usage.*{\(.*\)}.*$/\1/p" \ + $sysvdir/${prev##*/} 2>/dev/null`' -- $cur ) ) + fi + + return 0 +} && +complete -F _service service +[ -d /etc/init.d/ ] && complete -F _service $default \ + $(for i in /etc/init.d/*; do echo ${i##*/}; done) + +# chown(1) completion +# +_chown() +{ + local cur + cur=`_get_cword` + + # options completion + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-c -h -f -R -v --changes \ + --dereference --no-dereference --from= --silent --quiet \ + --reference= --recursive --verbose --help --version' -- $cur ) ) + else + _count_args + + case $args in + 1) + _usergroup + ;; + *) + _filedir + ;; + esac + fi +} +complete -F _chown $filenames chown + +# chgrp(1) completion +# +_chgrp() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + cur=${cur//\\\\/} + prev=${COMP_WORDS[COMP_CWORD-1]} + + # options completion + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-c -h -f -R -v --changes \ + --dereference --no-dereference --silent --quiet \ + --reference= --recursive --verbose --help --version' -- $cur ) ) + return 0 + fi + + # first parameter on line or first since an option? + if [ $COMP_CWORD -eq 1 ] && [[ "$cur" != -* ]] || \ + [[ "$prev" == -* ]] && [ -n "$bash205" ]; then + local IFS=$'\n' + COMPREPLY=( $( compgen -g $cur 2>/dev/null ) ) + else + _filedir || return 0 + fi + + return 0 +} +complete -F _chgrp $filenames chgrp + +# umount(8) completion. This relies on the mount point being the third +# space-delimited field in the output of mount(8) +# +_umount() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + OLDIFS="$IFS" + IFS="\n" + COMPREPLY=( $( compgen -W '$( mount | cut -d" " -f 3 )' -- $cur ) ) + IFS="$OLDIFS" + + return 0 +} +complete -F _umount $dirnames umount + +# mount(8) completion. This will pull a list of possible mounts out of +# /etc/{,v}fstab, unless the word being completed contains a ':', which +# would indicate the specification of an NFS server. In that case, we +# query the server for a list of all available exports and complete on +# that instead. +# +_mount() +{ local cur i sm host + + COMPREPLY=() + cur=`_get_cword` + [[ "$cur" == \\ ]] && cur="/" + + for i in {,/usr}/{,s}bin/showmount; do [ -x $i ] && sm=$i && break; done + + if [ -n "$sm" ] && [[ "$cur" == *:* ]]; then + COMPREPLY=( $( $sm -e ${cur%%:*} | sed 1d | \ + grep ^${cur#*:} | awk '{print $1}' ) ) + elif [[ "$cur" == //* ]]; then + host=${cur#//} + host=${host%%/*} + if [ -n "$host" ]; then + COMPREPLY=( $( compgen -W "$( echo $( smbclient -d 0 -NL $host 2>/dev/null| + sed -ne '/^['"$'\t '"']*Sharename/,/^$/p' | + sed -ne '3,$s|^[^A-Za-z]*\([^'"$'\t '"']*\).*$|//'$host'/\1|p' ) )" -- "$cur" ) ) + fi + elif [ -r /etc/vfstab ]; then + # Solaris + COMPREPLY=( $( awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' \ + /etc/vfstab | grep "^$cur" ) ) + elif [ ! -e /etc/fstab ]; then + # probably Cygwin + COMPREPLY=( $( mount | awk '! /^[ \t]*#/ {if ($3 ~ /\//) print $3}' \ + | grep "^$cur" ) ) + else + # probably Linux + COMPREPLY=( $( awk '! /^[ \t]*#/ {if ($2 ~ /\//) print $2}' \ + /etc/fstab | grep "^$cur" ) ) + fi + + return 0 +} +complete -F _mount $default $filenames mount + +# Linux rmmod(8) completion. This completes on a list of all currently +# installed kernel modules. +# +have rmmod && { +_rmmod() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + COMPREPLY=( $( /sbin/lsmod | \ + awk '{if (NR != 1 && $1 ~ /^'$cur'/) print $1}' 2>/dev/null )) + return 0 +} +complete -F _rmmod rmmod + +# Linux insmod(8), modprobe(8) and modinfo(8) completion. This completes on a +# list of all available modules for the version of the kernel currently +# running. +# +_insmod() +{ + local cur prev modpath + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + # behave like lsmod for modprobe -r + if [ $1 = "modprobe" ] && + [ "${COMP_WORDS[1]}" = "-r" ]; then + COMPREPLY=( $( /sbin/lsmod | \ + awk '{if (NR != 1 && $1 ~ /^'$cur'/) print $1}' ) ) + return 0 + fi + + # do filename completion if we're giving a path to a module + if [[ "$cur" == */* ]]; then + _filedir '@(?(k)o?(.gz))' + return 0 + fi + + if [ $COMP_CWORD -gt 1 ] && + [[ "${COMP_WORDS[COMP_CWORD-1]}" != -* ]]; then + # do module parameter completion + COMPREPLY=( $( /sbin/modinfo -p ${COMP_WORDS[1]} 2>/dev/null | \ + awk '{if ($1 ~ /^parm:/ && $2 ~ /^'$cur'/) { print $2 } \ + else if ($1 !~ /:/ && $1 ~ /^'$cur'/) { print $1 }}' ) ) + else + _modules $(uname -r) + fi + + return 0 +} +complete -F _insmod $filenames insmod modprobe modinfo +} + +# man(1) completion +# +[ $UNAME = GNU -o $UNAME = Linux -o $UNAME = Darwin \ + -o $UNAME = FreeBSD -o $UNAME = SunOS -o $UNAME = Cygwin \ + -o $UNAME = OpenBSD ] && +_man() +{ + local cur prev sect manpath UNAME + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + _expand || return 0 + + # default completion if parameter contains / + if [[ "$cur" == */* ]]; then + _filedir + return 0 + fi + + UNAME=$( uname -s ) + # strip OS type and version under Cygwin + UNAME=${UNAME/CYGWIN_*/Cygwin} + if [ $UNAME = GNU -o $UNAME = Linux -o $UNAME = FreeBSD \ + -o $UNAME = Cygwin ]; then + manpath=$( manpath 2>/dev/null || command man --path ) + else + manpath=$MANPATH + fi + + if [ -z "$manpath" ]; then + COMPREPLY=( $( compgen -c -- $cur ) ) + return 0 + fi + + # determine manual section to search + [[ "$prev" == [0-9ln] ]] && sect=$prev || sect='*' + + manpath=$manpath: + if [ -n "$cur" ]; then + manpath="${manpath//://*man$sect/$cur* } ${manpath//://*cat$sect/$cur* }" + else + manpath="${manpath//://*man$sect/ } ${manpath//://*cat$sect/ }" + fi + + # redirect stderr for when path doesn't exist + COMPREPLY=( $( eval command ls "$manpath" 2>/dev/null ) ) + # weed out directory path names and paths to man pages + COMPREPLY=( ${COMPREPLY[@]##*/?(:)} ) + # strip suffix from man pages + COMPREPLY=( ${COMPREPLY[@]%.@(gz|bz2)} ) + COMPREPLY=( $( compgen -W '${COMPREPLY[@]%.*}' -- "${cur//\\\\/}" ) ) + + [[ "$prev" != [0-9ln] ]] && _filedir '[0-9ln]' + + return 0 +} +[ $UNAME = GNU -o $UNAME = Linux -o $UNAME = Darwin \ + -o $UNAME = FreeBSD -o $UNAME = SunOS -o $UNAME = Cygwin \ + -o $UNAME = OpenBSD ] && \ +complete -F _man $filenames man apropos whatis + +# renice(8) completion +# +_renice() +{ + local command cur curopt i + + COMPREPLY=() + cur=`_get_cword` + command=$1 + + i=0 + # walk back through command line and find last option + while [ $i -le $COMP_CWORD -a ${#COMPREPLY[@]} -eq 0 ]; do + curopt=${COMP_WORDS[COMP_CWORD-$i]} + case "$curopt" in + -u) + COMPREPLY=( $( compgen -u -- $cur ) ) + ;; + -g) + _pgids + ;; + -p|$command) + _pids + ;; + esac + i=$(( ++i )) + done +} +complete -F _renice renice + +# kill(1) completion +# +_kill() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then + # return list of available signals + _signals + else + # return list of available PIDs + _pids + fi +} +complete -F _kill kill + +# Linux and FreeBSD killall(1) completion. +# +[ $UNAME = Linux -o $UNAME = FreeBSD ] && +_killall() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD -eq 1 ] && [[ "$cur" == -* ]]; then + _signals + else + COMPREPLY=( $( compgen -W '$( command ps axo command | \ + sed -ne "1d; s/^\[\?\([^-][^] ]*\).*$/\1/p" | \ + sed -e "s/.*\///" )' -- $cur ) ) + fi + + return 0 +} +[ $UNAME = Linux -o $UNAME = FreeBSD ] && complete -F _killall killall pkill + +# Linux and FreeBSD pgrep(1) completion. +# +[ $UNAME = Linux -o $UNAME = FreeBSD ] && +_pgrep() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + COMPREPLY=( $( compgen -W '$( command ps axo command | \ + sed -ne "1d; s/^\[\?\([^-][^] ]*\).*$/\1/p" | \ + sed -e "s/.*\///" )' -- $cur ) ) + + return 0 +} +[ $UNAME = Linux -o $UNAME = FreeBSD ] && complete -F _pgrep pgrep +# Linux pidof(8) completion. +[ $UNAME = Linux ] && complete -F _pgrep pidof + +# GNU find(1) completion. This makes heavy use of ksh style extended +# globs and contains Linux specific code for completing the parameter +# to the -fstype option. +# +_find() +{ + local cur prev i exprfound onlyonce + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(max|min)depth) + COMPREPLY=( $( compgen -W '0 1 2 3 4 5 6 7 8 9' -- $cur ) ) + return 0 + ;; + -?(a|c)newer|-fls|-fprint?(0|f)|-?(i)?(l)name|-?(i)wholename) + _filedir + return 0 + ;; + -fstype) + # this is highly non-portable + [ -e /proc/filesystems ] && + COMPREPLY=( $( cut -d$'\t' -f 2 /proc/filesystems | \ + grep "^$cur" ) ) + return 0 + ;; + -gid) + _gids + return 0 + ;; + -group) + if [ -n "$bash205" ]; then + COMPREPLY=( $( compgen -g -- $cur 2>/dev/null) ) + fi + return 0 + ;; + -?(x)type) + COMPREPLY=( $( compgen -W 'b c d p f l s' -- $cur ) ) + return 0 + ;; + -uid) + _uids + return 0 + ;; + -user) + COMPREPLY=( $( compgen -u -- $cur ) ) + return 0 + ;; + -exec|-ok) + COMP_WORDS=(COMP_WORDS[0] $cur) + COMP_CWORD=1 + _command + return 0 + ;; + -[acm]min|-[acm]time|-?(i)?(l)name|-inum|-?(i)path|-?(i)regex| \ + -links|-perm|-size|-used|-printf) + # do nothing, just wait for a parameter to be given + return 0 + ;; + esac + + _expand || return 0 + + # set exprfound to 1 if there is already an expression present + for i in ${COMP_WORDS[@]}; do + [[ "$i" = [-\(\),\!]* ]] && exprfound=1 && break + done + + # handle case where first parameter is not a dash option + if [ "$exprfound" != 1 ] && [[ "$cur" != [-\(\),\!]* ]]; then + _filedir -d + return 0 + fi + + # complete using basic options + COMPREPLY=( $( compgen -W '-daystart -depth -follow -help -maxdepth \ + -mindepth -mount -noleaf -version -xdev -amin -anewer \ + -atime -cmin -cnewer -ctime -empty -false -fstype \ + -gid -group -ilname -iname -inum -ipath -iregex \ + -wholename \ + -links -lname -mmin -mtime -name -newer -nouser \ + -nogroup -perm -regex -size -true -type -uid -used \ + -user -xtype -exec -fls -fprint -fprint0 -fprintf -ok \ + -print -print0 -printf -prune -ls' -- $cur ) ) + + # this removes any options from the list of completions that have + # already been specified somewhere on the command line, as long as + # these options can only be used once (in a word, "options", in + # opposition to "tests" and "actions", as in the find(1) manpage). + onlyonce=' -daystart -depth -follow -help -maxdepth -mindepth -mount \ + -noleaf -version -xdev ' + COMPREPLY=( $( echo "${COMP_WORDS[@]}" | \ + (while read -d ' ' i; do + [ "$i" == "" ] || + [ "${onlyonce/ ${i%% *} / }" == "$onlyonce" ] && + continue + # flatten array with spaces on either side, + # otherwise we cannot grep on word boundaries of + # first and last word + COMPREPLY=" ${COMPREPLY[@]} " + # remove word from list of completions + COMPREPLY=( ${COMPREPLY/ ${i%% *} / } ) + done + echo "${COMPREPLY[@]}") + ) ) + + _filedir + + return 0 +} +complete -F _find $filenames find + +# Linux iwconfig(8) completion +# +[ $UNAME = Linux ] && have iwconfig && +_iwconfig() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case $prev in + mode) + COMPREPLY=( $( compgen -W 'managed ad-hoc master \ + repeater secondary monitor' -- $cur ) ) + return 0 + ;; + essid) + COMPREPLY=( $( compgen -W 'on off any' -- $cur ) ) + if [ -n "${COMP_IWLIST_SCAN:-}" ]; then + COMPREPLY=( "${COMPREPLY[@]}" \ + $( iwlist ${COMP_WORDS[1]} scan | \ + awk -F '"' '/ESSID/ {print $2}' | \ + grep "^$cur" )) + fi + return 0 + ;; + nwid) + COMPREPLY=( $( compgen -W 'on off' -- $cur ) ) + return 0 + ;; + channel) + COMPREPLY=( $( iwlist ${COMP_WORDS[1]} channel | \ + awk '/^[[:space:]]*Channel/ {print $2}' | \ + grep "^$cur" ) ) + return 0 + ;; + + freq) + COMPREPLY=( $( iwlist ${COMP_WORDS[1]} channel | \ + awk '/^[[:space:]]*Channel/ {print $4"G"}' | \ + grep "^$cur" ) ) + return 0 + ;; + ap) + COMPREPLY=( $( compgen -W 'on off any' -- $cur ) ) + if [ -n "${COMP_IWLIST_SCAN:-}" ]; then + COMPREPLY=( "${COMPREPLY[@]}" \ + $( iwlist ${COMP_WORDS[1]} scan | \ + awk -F ': ' '/Address/ {print $2}' | \ + grep "^$cur" ) ) + fi + return 0 + ;; + rate) + COMPREPLY=( $( compgen -W 'auto fixed' -- $cur ) ) + COMPREPLY=( "${COMPREPLY[@]}" \ + $( iwlist ${COMP_WORDS[1]} rate | \ + awk '/^[[:space:]]*[0-9]/ {print $1"M"}' | \ + grep "^$cur" ) ) + return 0 + ;; + rts) + COMPREPLY=( $( compgen -W 'auto fixed off' -- $cur ) ) + return 0 + ;; + frag) + COMPREPLY=( $( compgen -W 'auto fixed off' -- $cur ) ) + return 0 + ;; + key) + COMPREPLY=( $( compgen -W 'off on open restricted' -- $cur ) ) + return 0 + ;; + enc) + COMPREPLY=( $( compgen -W 'off on open restricted' -- $cur ) ) + return 0 + ;; + power) + COMPREPLY=( $( compgen -W 'period timeout off on' -- $cur ) ) + return 0 + ;; + txpower) + COMPREPLY=( $( compgen -W 'off on auto' -- $cur ) ) + return 0 + ;; + retry) + COMPREPLY=( $( compgen -W 'limit lifetime' -- $cur ) ) + return 0 + ;; + esac + + if [ $COMP_CWORD -eq 1 ]; then + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--help --version' -- $cur ) ) + else + _available_interfaces -w + fi + else + COMPREPLY=( $( compgen -W 'essid nwid mode freq channel sens mode \ + ap nick rate rts frag enc key power txpower commit' -- $cur ) ) + fi + +} && +complete -F _iwconfig iwconfig + +# Linux iwlist(8) completion +# +[ $UNAME = Linux ] && have iwlist && +_iwlist() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [ $COMP_CWORD -eq 1 ]; then + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--help --version' -- $cur ) ) + else + _available_interfaces -w + fi + else + COMPREPLY=( $( compgen -W 'scan scanning freq frequency \ + channel rate bit bitrate key enc encryption power \ + txpower retry ap accesspoint peers event' -- $cur ) ) + fi +} && +complete -F _iwlist iwlist + +# Linux iwspy(8) completion +# +[ $UNAME = Linux ] && have iwspy && +_iwspy() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD -eq 1 ]; then + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--help --version' -- $cur ) ) + else + _available_interfaces -w + fi + else + COMPREPLY=( $( compgen -W 'setthr getthr off' -- $cur ) ) + fi +} && +complete -F _iwspy iwspy + +# Linux iwpriv(8) completion +# +[ $UNAME = Linux ] && have iwpriv && +_iwpriv() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + roam) + COMPREPLY=( $( compgen -W 'on off' -- $cur ) ) + return 0 + ;; + port) + COMPREPLY=( $( compgen -W 'ad-hoc managed' -- $cur ) ) + return 0 + ;; + esac + + if [ $COMP_CWORD -eq 1 ]; then + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--help --version' -- $cur ) ) + else + _available_interfaces -w + fi + else + COMPREPLY=( $( compgen -W '--all roam port' -- $cur ) ) + fi +} && +complete -F _iwpriv iwpriv + +# RedHat & Debian GNU/Linux if{up,down} completion +# +[ $UNAME = Linux ] && { have ifup || have ifdown; } && +_ifupdown() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD -eq 1 ]; then + _configured_interfaces + COMPREPLY=( $(compgen -W '${COMPREPLY[@]}' -- "$cur") ) + fi + + return 0 +} && +complete -F _ifupdown ifup ifdown +[ $UNAME = Linux ] && have ifstatus && complete -F _ifupdown ifstatus + +# Linux ipsec(8) completion (for FreeS/WAN) +# +[ $UNAME = Linux ] && have ipsec && +_ipsec() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + + if [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $( compgen -W 'auto barf eroute klipsdebug look \ + manual pluto ranbits rsasigkey \ + setup showdefaults showhostkey spi \ + spigrp tncfg whack' -- $cur ) ) + return 0 + fi + + case ${COMP_WORDS[1]} in + auto) + COMPREPLY=( $( compgen -W '--asynchronous --up --add --delete \ + --replace --down --route --unroute \ + --ready --status --rereadsecrets' \ + -- $cur ) ) + ;; + manual) + COMPREPLY=( $( compgen -W '--up --down --route --unroute \ + --union' -- $cur ) ) + ;; + ranbits) + COMPREPLY=( $( compgen -W '--quick --continuous --bytes' \ + -- $cur ) ) + ;; + setup) + COMPREPLY=( $( compgen -W '--start --stop --restart' -- $cur ) ) + ;; + + *) + ;; + esac + + return 0 +} && +complete -F _ipsec ipsec + +# Postfix completion. +# +have postfix && { +# postfix(1) +# +_postfix() +{ + local cur prev + + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [[ $cur == '-' ]]; then + COMPREPLY=(-c -D -v) + return 0 + fi + if [[ $prev == '-c' ]]; then + _filedir -d + return 0 + fi + if [[ $prev == '-D' ]]; then + COMPREPLY=( $( compgen -W 'start' -- "`get_cword`" ) ) + return 0 + fi + COMPREPLY=( $( compgen -W 'start stop reload abort flush check' -- \ + "`get_cword`" ) ) +} +complete -F _postfix postfix + +# postalias(1) and postmap(1) +# +_postmap() +{ + local cur prev len idx + + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [[ $cur == '-' ]]; then + COMPREPLY=(-N -f -i -n -o -p -r -v -w -c -d -q) + return 0 + fi + if [[ $prev == '-c' ]]; then + _filedir -d + return 0 + fi + if [[ $prev == -[dq] ]]; then + return 0 + fi + + if [[ "$cur" == *:* ]]; then + COMPREPLY=( $( compgen -f -- ${cur#*:} ) ) + else + len=${#cur} + idx=0 + for pval in $( /usr/sbin/postconf -m ); do + if [[ "$cur" == "${pval:0:$len}" ]]; then + COMPREPLY[$idx]="$pval:" + idx=$(($idx+1)) + fi + done + if [[ $idx -eq 0 ]]; then + COMPREPLY=( $( compgen -f -- "$cur" ) ) + fi + fi + return 0 +} +complete -F _postmap postmap postalias + +# postcat(1) +# +_postcat() +{ + local cur prev pval len idx qfile + + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [[ $cur == '-' ]]; then + COMPREPLY=(-c -q -v) + return 0 + fi + if [[ $prev == '-c' ]]; then + _filedir -d + return 0 + fi + + qfile=0 + for idx in "${COMP_WORDS[@]}"; do + [[ "$idx" = -q ]] && qfile=1 && break + done + if [[ $qfile == 1 ]]; then + len=${#cur} + idx=0 + for pval in $( mailq | \ + sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; s/[* !].*$//' ); do + if [[ "$cur" == "${pval:0:$len}" ]]; then + COMPREPLY[$idx]=$pval + idx=$(($idx+1)) + fi + done + return 0 + else + _filedir + return 0 + fi +} +complete -F _postcat postcat + +# postconf(1) +# +_postconf() +{ + local cur prev pval len idx eqext + + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + if [[ $cur == '-' ]]; then + COMPREPLY=(-c -d -e -h -m -l -n -v) + return 0 + fi + if [[ $prev == '-c' ]]; then + _filedir -d + return 0 + fi + if [[ $prev == '-e' ]]; then + cur=${cur#[\"\']} + eqext='=' + fi + len=${#cur} + idx=0 + for pval in $( /usr/sbin/postconf | cut -d ' ' -f 1 ); do + if [[ "$cur" == "${pval:0:$len}" ]]; then + COMPREPLY[$idx]="$pval$eqext" + idx=$(($idx+1)) + fi + done + return 0 +} +complete -F _postconf postconf + +# postsuper(1) +# +_postsuper() +{ + local cur prev pval len idx + + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [[ $cur == '-' ]]; then + COMPREPLY=(-c -d -h -H -p -r -s -v) + return 0 + fi + case $prev in + -[dr]) + len=${#cur} + idx=0 + for pval in $( echo ALL; mailq | \ + sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; s/[* !].*$//' ); do + if [[ "$cur" == "${pval:0:$len}" ]]; then + COMPREPLY[$idx]=$pval + idx=$(($idx+1)) + fi + done + return 0 + ;; + -h) + len=${#cur} + idx=0 + for pval in $( echo ALL; mailq | \ + sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; s/[* ].*$//; /!$/d' ); do + if [[ "$cur" == "${pval:0:$len}" ]]; then + COMPREPLY[$idx]=$pval + idx=$(($idx+1)) + fi + done + return 0 + ;; + -H) + len=${#cur} + idx=0 + for pval in $( echo ALL; mailq | \ + sed -e '1d; $d; /^[^0-9A-Z]\|^$/d; /^[0-9A-Z]*[* ]/d; s/!.*$//' ); do + if [[ "$cur" == "${pval:0:$len}" ]]; then + COMPREPLY[$idx]=$pval + idx=$(($idx+1)) + fi + done + return 0 + ;; + esac + COMPREPLY=( $( compgen -W 'hold incoming active deferred' -- $cur ) ) + return 0 +} +complete -F _postsuper postsuper +} + +# cvs(1) completion +# +have cvs && { +set_prefix() +{ + [ -z ${prefix:-} ] || prefix=${cur%/*}/ + [ -r ${prefix:-}CVS/Entries ] || prefix="" +} + +get_entries() +{ + local IFS=$'\n' + [ -r ${prefix:-}CVS/Entries ] && \ + entries=$(cut -d/ -f2 -s ${prefix:-}CVS/Entries) +} + +get_modules() +{ + if [ -n "$prefix" ]; then + COMPREPLY=( $( command ls -d ${cvsroot}/${prefix}/!(CVSROOT) ) ) + else + COMPREPLY=( $( command ls -d ${cvsroot}/!(CVSROOT) ) ) + fi +} + +_cvs() +{ + local cur count mode i cvsroot cvsroots pwd + local -a flags miss files entries changed newremoved + + COMPREPLY=() + cur=`_get_cword` + + count=0 + for i in "${COMP_WORDS[@]}"; do + [ $count -eq $COMP_CWORD ] && break + # Last parameter was the CVSROOT, now go back to mode selection + if [ "${COMP_WORDS[((count))]}" == "$cvsroot" -a "$mode" == "cvsroot" ]; then + mode="" + fi + if [ -z "$mode" ]; then + case $i in + -d) + mode=cvsroot + cvsroot=${COMP_WORDS[((count+1))]} + ;; + @(ad?(d)|new)) + mode=add + ;; + @(adm?(in)|rcs)) + mode=admin + ;; + ann?(notate)) + mode=annotate + ;; + @(checkout|co|get)) + mode=checkout + ;; + @(com?(mit)|ci)) + mode=commit + ;; + di?(f?(f))) + mode=diff + ;; + ex?(p?(ort))) + mode=export + ;; + ?(un)edit) + mode=$i + ;; + hi?(s?(tory))) + mode=history + ;; + im?(p?(ort))) + mode=import + ;; + re?(l?(ease))) + mode=release + ;; + ?(r)log) + mode=log + ;; + @(rdiff|patch)) + mode=rdiff + ;; + @(remove|rm|delete)) + mode=remove + ;; + @(rtag|rfreeze)) + mode=rtag + ;; + st?(at?(us))) + mode=status + ;; + @(tag|freeze)) + mode=tag + ;; + up?(d?(ate))) + mode=update + ;; + *) + ;; + esac + elif [[ "$i" = -* ]]; then + flags=( "${flags[@]}" $i ) + fi + count=$((++count)) + done + + case "$mode" in + add) + if [[ "$cur" != -* ]]; then + set_prefix + if [ $COMP_CWORD -gt 1 -a -r ${prefix:-}CVS/Entries ]; then + get_entries + [ -z "$cur" ] && \ + files=$( command ls -Ad !(CVS) ) || \ + files=$( command ls -d ${cur}* 2>/dev/null ) + for i in "${entries[@]}"; do + files=( ${files[@]/#$i//} ) + done + COMPREPLY=( $( compgen -W '${files[@]}' -- \ + $cur ) ) + fi + else + COMPREPLY=( $( compgen -W '-k -m' -- $cur ) ) + fi + ;; + admin) + if [[ "$cur" = -* ]]; then + COMPREPLY=( $( compgen -W '-i -a -A -e -b -c -k -l -u \ + -L -U -m -M -n -N -o -q -I \ + -s -t -t- -T -V -x -z' -- \ + $cur ) ) + fi + ;; + annotate) + if [[ "$cur" = -* ]]; then + COMPREPLY=( $( compgen -W '-D -F -f -l -R -r' -- $cur ) ) + else + get_entries + COMPREPLY=( $( compgen -W '${entries[@]}' -- $cur ) ) + fi + ;; + checkout) + if [[ "$cur" != -* ]]; then + [ -z "$cvsroot" ] && cvsroot=$CVSROOT + COMPREPLY=( $( cvs -d "$cvsroot" co -c 2> /dev/null | \ + awk '{print $1}' ) ) + COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- $cur ) ) + else + COMPREPLY=( $( compgen -W '-A -N -P -R -c -f -l -n -p \ + -s -r -D -d -k -j' -- $cur ) ) + fi + ;; + commit) + set_prefix + + if [[ "$cur" != -* ]] && [ -r ${prefix:-}CVS/Entries ]; then + # if $COMP_CVS_REMOTE is not null, 'cvs commit' will + # complete on remotely checked-out files (requires + # passwordless access to the remote repository + if [ -n "${COMP_CVS_REMOTE:-}" ]; then + # this is the least computationally intensive + # way found so far, but other changes + # (something other than changed/removed/new) + # may be missing + changed=( $( cvs -q diff --brief 2>&1 | \ + sed -ne 's/^Files [^ ]* and \([^ ]*\) differ$/\1/p' ) ) + newremoved=( $( cvs -q diff --brief 2>&1 | \ + sed -ne 's/^cvs diff: \([^ ]*\) .*, no comparison available$/\1/p' ) ) + COMPREPLY=( $( compgen -W '${changed[@]:-} \ + ${newremoved[@]:-}' -- $cur ) ) + else + _filedir + fi + else + COMPREPLY=( $( compgen -W '-n -R -l -f -F -m -r' -- \ + $cur ) ) + fi + ;; + cvsroot) + if [ -r ~/.cvspass ]; then + # Ugly escaping because of bash treating ':' specially + cvsroots=$( sed 's/^[^ ]* //; s/:/\\:/g' ~/.cvspass ) + COMPREPLY=( $( compgen -W '$cvsroots' -- $cur ) ) + fi + ;; + export) + if [[ "$cur" != -* ]]; then + [ -z "$cvsroot" ] && cvsroot=$CVSROOT + COMPREPLY=( $( cvs -d "$cvsroot" co -c | awk '{print $1}' ) ) + COMPREPLY=( $( compgen -W '${COMPREPLY[@]}' -- $cur ) ) + else + COMPREPLY=( $( compgen -W '-N -f -l -R -n \ + -r -D -d -k' -- $cur ) ) + fi + ;; + diff) + if [[ "$cur" == -* ]]; then + _longopt diff + else + get_entries + COMPREPLY=( $( compgen -W '${entries[@]:-}' -- $cur ) ) + fi + ;; + remove) + if [[ "$cur" != -* ]]; then + set_prefix + if [ $COMP_CWORD -gt 1 -a -r ${prefix:-}CVS/Entries ]; then + get_entries + # find out what files are missing + for i in "${entries[@]}"; do + [ ! -r "$i" ] && miss=( "${miss[@]}" $i ) + done + COMPREPLY=( $(compgen -W '${miss[@]:-}' -- $cur) ) + fi + else + COMPREPLY=( $( compgen -W '-f -l -R' -- $cur ) ) + fi + ;; + import) + if [[ "$cur" != -* ]]; then + # starts with same algorithm as checkout + [ -z "$cvsroot" ] && cvsroot=$CVSROOT + prefix=${cur%/*} + if [ -r ${cvsroot}/${prefix} ]; then + get_modules + COMPREPLY=( ${COMPREPLY[@]#$cvsroot} ) + COMPREPLY=( ${COMPREPLY[@]#\/} ) + fi + pwd=$( pwd ) + pwd=${pwd##*/} + COMPREPLY=( $( compgen -W '${COMPREPLY[@]} $pwd' -- \ + $cur ) ) + else + COMPREPLY=( $( compgen -W '-d -k -I -b -m -W' -- $cur )) + fi + ;; + update) + if [[ "$cur" = -* ]]; then + COMPREPLY=( $( compgen -W '-A -P -C -d -f -l -R -p \ + -k -r -D -j -I -W' -- \ + $cur ) ) + fi + ;; + "") + COMPREPLY=( $( compgen -W 'add admin annotate checkout ci co \ + commit diff delete edit export \ + freeze get history import log new \ + patch rcs rdiff release remove \ + rfreeze rlog rm rtag stat status \ + tag unedit up update -H -Q -q -b \ + -d -e -f -l -n -t -r -v -w -x -z \ + --help --version' -- $cur ) ) + ;; + *) + ;; + esac + + return 0 +} +complete -F _cvs $default cvs +} + +have rpm && { +# helper functions for rpm completion +# +_rpm_installed_packages() +{ + local ver nodig nosig + + if [ -r /var/log/rpmpkgs -a \ + /var/log/rpmpkgs -nt /var/lib/rpm/Packages ]; then + # using RHL 7.2 or later - this is quicker than querying the DB + COMPREPLY=( $( sed -ne \ + 's|^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9a-z.@]\+.*\.rpm$|\1|p' \ + /var/log/rpmpkgs ) ) + else + nodig="" + nosig="" + ver=$(rpm --version) + ver=${ver##* } + + if [[ "$ver" > "4.0.4" ]]; then + nodig="--nodigest" + fi + if [[ "$ver" > "4.0.99" ]]; then + nosig="--nosignature" + fi + + COMPREPLY=( $( rpm -qa $nodig $nosig | sed -ne \ + 's|^\('$cur'.*\)-[0-9a-zA-Z._]\+-[0-9a-z.@]\+$|\1|p' ) ) + fi +} + +_rpm_groups() +{ + local IFS=$'\t' + # remove trailing backslash, or grep will complain + cur=${cur%"\\"} + COMPREPLY=( $( rpm -qa $nodig $nosig --queryformat '%{group}\n' | \ + grep "^$cur" ) ) + # backslash escape spaces and translate newlines to tabs + COMPREPLY=( $( echo "${COMPREPLY[@]}" | sed 's/ /\\ /g' | tr '\n' '\t' ) ) +} + +# rpm(8) completion +# +_rpm() +{ + local cur prev ver nodig nosig + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + nodig="" + nosig="" + ver=$(rpm --version); ver=${ver##* } + + if [[ "$ver" > "4.0.4" ]]; then + nodig="--nodigest" + fi + if [[ "$ver" > "4.0.99" ]]; then + nosig="--nosignature" + fi + + if [ $COMP_CWORD -eq 1 ]; then + # first parameter on line + case "$cur" in + -b*) + COMPREPLY=( $( compgen -W '-ba -bb -bc -bi -bl -bp -bs'\ + -- $cur ) ) + ;; + -t*) + COMPREPLY=( $( compgen -W '-ta -tb -tc -ti -tl -tp -ts'\ + -- $cur ) ) + ;; + --*) + COMPREPLY=( $( compgen -W '--help --version --initdb \ + --checksig --recompile --rebuild --resign --addsign \ + --rebuilddb --showrc --setperms --setugids --tarbuild \ + --eval --install --upgrade --query --freshen --erase \ + --verify --querytags --rmsource --rmspec --clean \ + --import' -- $cur ) ) + ;; + *) + COMPREPLY=( $( compgen -W '-b -e -F -i -q -t -U -V' \ + -- $cur ) ) + ;; + esac + + return 0 + fi + + case "$prev" in + --@(@(db|exclude)path|prefix|relocate|root)) + _filedir -d + return 0 + ;; + --eval) + # get a list of macros + COMPREPLY=( $( sed -ne 's|^\(%'${cur#\%}'[^ '$'\t'']*\).*$|\1|p' \ + /usr/lib/rpm/macros ) ) + return 0 + ;; + --pipe) + COMPREPLY=( $( compgen -c -- $cur ) ) + return 0 + ;; + --rcfile) + _filedir + return 0 + ;; + --specfile) + # complete on .spec files + _filedir spec + return 0 + ;; + --whatprovides) + if [[ "$cur" == */* ]]; then + _filedir + else + # complete on capabilities + COMPREPLY=( $( rpm -qa $nodig $nosig --queryformat \ + '%{providename}\n' | grep "^$cur" ) ) + fi + return 0 + ;; + --whatrequires) + # complete on capabilities + COMPREPLY=( $( rpm -qa $nodig $nosig --queryformat \ + '%{requirename}\n' | grep "^$cur" ) ) + return 0 + ;; + esac + + case "${COMP_WORDS[1]}" in + -@([iFU]*|-install|-freshen|-upgrade)) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--percent --force --test \ + --replacepkgs --replacefiles --root --excludedocs \ + --includedocs --noscripts --rcfile --ignorearch \ + --dbpath --prefix --ignoreos --nodeps --allfiles \ + --ftpproxy --ftpport --justdb --httpproxy --httpport \ + --noorder --relocate --badreloc --notriggers \ + --excludepath --ignoresize --oldpackage --define \ + --eval --pipe --queryformat --repackage --nosuggests \ + --nodigest --nosignature' -- $cur ) ) + else + _filedir 'rpm' + fi + ;; + -@(e|-erase)) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--allmatches --noscripts \ + --notriggers --nodeps --test --repackage' -- $cur ) ) + else + _rpm_installed_packages + fi + ;; + -@(q*|-query)) + # check whether we're doing file completion + if [ "${COMP_LINE#* -*([^ -])f}" != "$COMP_LINE" ]; then + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--scripts --root \ + --rcfile --requires --ftpport --ftpproxy \ + --httpproxy --httpport --provides --triggers \ + --dump --changelog --dbpath \ + --last --filesbypkg \ + --info --list --state \ + --docfiles --configfiles --queryformat \ + --conflicts --obsoletes \ + --nodigest --nosignature \ + --triggerscripts' -- $cur ) ) + else + _filedir + fi + elif [ "${COMP_LINE#* -*([^ -])g}" != "$COMP_LINE" ]; then + _rpm_groups + elif [ "${COMP_LINE#* -*([^ -])p}" != "$COMP_LINE" ]; then + # uninstalled package completion + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--scripts --root \ + --rcfile --whatprovides --whatrequires \ + --requires --triggeredby --ftpport --ftpproxy \ + --httpproxy --httpport --provides --triggers \ + --dump --changelog --dbpath --filesbypkg \ + --define --eval --pipe --showrc --info --list \ + --state --docfiles --configfiles --queryformat\ + --conflicts --obsoletes --nodigest \ + --nosignature' -- $cur ) ) + else + _filedir 'rpm' + fi + else + # installed package completion + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--scripts --root \ + --rcfile --whatprovides --whatrequires \ + --requires --triggeredby --ftpport --ftpproxy \ + --httpproxy --httpport --provides --triggers \ + --dump --changelog --dbpath --specfile \ + --querybynumber --last --filesbypkg --define \ + --eval --pipe --showrc --info --list --state \ + --docfiles --configfiles --queryformat \ + --conflicts --obsoletes --pkgid --hdrid \ + --fileid --tid --nodigest --nosignature \ + --triggerscripts' -- $cur ) ) + elif [ "${COMP_LINE#* -*([^ -])a}" == "$COMP_LINE" ]; then + _rpm_installed_packages + fi + fi + ;; + -@(K*|-checksig)) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--nopgp --nogpg --nomd5 \ + --nodigest --nosignature' -- $cur ) ) + else + _filedir 'rpm' + fi + ;; + -@([Vy]*|-verify)) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--root --rcfile --dbpath \ + --nodeps --nogroup --nolinkto --nomode --nomtime \ + --nordev --nouser --nofiles --noscripts --nomd5 \ + --querytags --specfile --whatrequires --whatprovides \ + --nodigest --nosignature' -- $cur ) ) + # check whether we're doing file completion + elif [ "${COMP_LINE#* -*([^ -])f}" != "$COMP_LINE" ]; then + _filedir + elif [ "${COMP_LINE#* -*([^ -])g}" != "$COMP_LINE" ]; then + _rpm_groups + elif [ "${COMP_LINE#* -*([^ -])p}" != "$COMP_LINE" ]; then + _filedir 'rpm' + else + _rpm_installed_packages + fi + ;; + -[bt]*) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--short-circuit --timecheck \ + --clean --rmsource --rmspec --test --sign --buildroot \ + --target -- buildarch --buildos --nobuild --nodeps \ + --nodirtokens' -- $cur ) ) + elif [[ ${COMP_WORDS[1]} == -b* ]]; then + _filedir 'spec' + else + _filedir '@(tgz|tar.@(gz|bz2))' + fi + ;; + --re@(build|compile)) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--nodeps --rmsource \ + --rmspec --sign --nodirtokens --target' -- $cur ) ) + else + _filedir '?(no)src.rpm' + fi + ;; + --tarbuild) + _filedir '@(tgz|tar.@(gz|bz2))' + ;; + --@(re|add)sign) + _filedir 'rpm' + ;; + --set@(perms|gids)) + _rpm_installed_packages + ;; + --@(clean|rms@(ource|pec))) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--clean --rmsource \ + --rmspec' -- $cur ) ) + else + _filedir 'spec' + fi + ;; + --@(import|dbpath|root)) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--import --dbpath --root' \ + -- $cur ) ) + else + _filedir + fi + ;; + esac + + return 0 +} +complete -F _rpm $filenames rpm rpmbuild +} + +# Debian apt-get(8) completion. +# +have apt-get && +_apt_get() +{ + local cur prev special i + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do + if [[ ${COMP_WORDS[i]} == @(install|remove|purge|source|build-dep) ]]; then + special=${COMP_WORDS[i]} + fi + done + + if [ -n "$special" ]; then + case $special in + remove|purge) + if [ -f /etc/debian_version ]; then + # Debian system + COMPREPLY=( $( _comp_dpkg_installed_packages \ + $cur ) ) + else + # assume RPM based + _rpm_installed_packages + fi + return 0 + ;; + *) + COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) ) + return 0 + ;; + + esac + fi + + case "$prev" in + -@(c|-config-file)) + _filedir + return 0 + ;; + + -@(t|-target-release|-default-release)) + COMPREPLY=( $( apt-cache policy | \ + grep "release.o=Debian,a=$cur" | \ + sed -e "s/.*a=\(\w*\).*/\1/" | uniq 2> /dev/null) ) + return 0 + ;; + + esac + + if [[ "$cur" == -* ]]; then + + COMPREPLY=( $( compgen -W '-d -f -h -v -m -q -s -y \ + -u -t -b -c -o --download-only --fix-broken \ + --help --version --ignore-missing \ + --fix-missing --no-download --quiet --simulate \ + --just-print --dry-run --recon --no-act --yes \ + --assume-yes --show-upgraded --only-source \ + --compile --build --ignore-hold \ + --target-release --no-upgrade --force-yes \ + --print-uris --purge --reinstall \ + --list-cleanup --default-release \ + --trivial-only --no-remove --diff-only \ + --tar-only --config-file --option --auto-remove' -- $cur ) ) + else + + COMPREPLY=( $( compgen -W 'update upgrade dselect-upgrade \ + dist-upgrade install remove purge source \ + build-dep check clean autoclean autoremove' \ + -- $cur ) ) + + fi + + + return 0 +} && +complete -F _apt_get $filenames apt-get + +# Debian apt-cache(8) completion. +# +have apt-cache && +_apt_cache() +{ + local cur prev special i + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + + if [ "$cur" != show ]; then + for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do + if [[ ${COMP_WORDS[i]} == @(add|depends|dotty|policy|rdepends|madison|show?(pkg|src|)) ]]; then + special=${COMP_WORDS[i]} + fi + done + fi + + + if [ -n "$special" ]; then + case $special in + add) + _filedir + return 0 + ;; + + *) + COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) ) + return 0 + ;; + + esac + fi + + + case "$prev" in + -@(c|p|s|-config-file|-@(pkg|src)-cache)) + _filedir + return 0 + ;; + search) + if [[ "$cur" != -* ]]; then + return 0 + fi + ;; + esac + + if [[ "$cur" == -* ]]; then + + COMPREPLY=( $( compgen -W '-h -v -p -s -q -i -f -a -g -c \ + -o --help --version --pkg-cache --src-cache \ + --quiet --important --full --all-versions \ + --no-all-versions --generate --no-generate \ + --names-only --all-names --recurse \ + --config-file --option' -- $cur ) ) + else + + COMPREPLY=( $( compgen -W 'add gencaches show showpkg showsrc \ + stats dump dumpavail unmet search search \ + depends rdepends pkgnames dotty xvcg \ + policy madison' -- $cur ) ) + + fi + + + return 0 +} && +complete -F _apt_cache $filenames apt-cache + + +# Debian aptitude(1) completion +# +have aptitude && { +have grep-status && { +_comp_dpkg_hold_packages() +{ + grep-status -P -e "^$1" -a -FStatus 'hold' -n -s Package +} +} || { +_comp_dpkg_hold_packages() +{ + grep -B 2 'hold' /var/lib/dpkg/status | grep "Package: $1" \ + | cut -d\ -f2 +} +} + +_aptitude() +{ + local cur dashoptions prev special i + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + + dashoptions='-S -u -i -h --help --version -s --simulate -d \ + --download-only -P --prompt -y --assume-yes -F \ + --display-format -O --sort -w --width -f -r -g \ + --with-recommends --with-suggests -R -G \ + --without-recommends --without-suggests -t \ + --target-release -V --show-versions -D --show-deps\ + -Z -v --verbose --purge-unused' + + for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do + if [[ ${COMP_WORDS[i]} == @(install|reinstall|hold|unhold|markauto|unmarkauto|dist-upgrade|full-upgrade|download|show|forbid-version|purge|remove|changelog|why|why-not|keep|keep-all) ]]; then + special=${COMP_WORDS[i]} + fi + #exclude some mutually exclusive options + [[ ${COMP_WORDS[i]} == '-u' ]] && dashoptions=${dashoptions/-i} + [[ ${COMP_WORDS[i]} == '-i' ]] && dashoptions=${dashoptions/-u} + done + + if [[ -n "$special" ]]; then + case $special in + @(install|hold|markauto|unmarkauto|dist-upgrade|full-upgrade|download|show|changelog|why|why-not)) + COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) ) + return 0 + ;; + @(purge|remove|reinstall|forbid-version)) + COMPREPLY=( $( _comp_dpkg_installed_packages $cur ) ) + return 0 + ;; + unhold) + COMPREPLY=( $( _comp_dpkg_hold_packages $cur ) ) + return 0 + ;; + + esac + fi + + case $prev in + # don't complete anything if these options are found + @(autoclean|clean|forget-new|search|upgrade|safe-upgrade|update|keep-all)) + return 0 + ;; + + -S) + _filedir + return 0 + ;; + + -@(t|-target-release|-default-release)) + COMPREPLY=( $( apt-cache policy | \ + grep "release.o=Debian,a=$cur" | \ + sed -e "s/.*a=\(\w*\).*/\1/" | uniq 2> /dev/null ) ) + return 0 + ;; + + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W "$dashoptions" -- $cur ) ) + else + COMPREPLY=( $( compgen -W 'update upgrade safe-upgrade forget-new clean \ + autoclean install reinstall remove \ + hold unhold purge markauto unmarkauto why why-not \ + dist-upgrade full-upgrade download search show \ + forbid-version changelog keep-all' -- $cur ) ) + fi + + + return 0 +} +complete -F _aptitude $default aptitude +} + +# Debian apt-build(1) completion. +# +have apt-build && +_apt_build() +{ + local cur prev special i + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do + if [[ ${COMP_WORDS[i]} == @(install|remove|source|info|clean) ]]; then + special=${COMP_WORDS[i]} + fi + done + + if [ -n "$special" ]; then + case $special in + @(install|source|info)) + COMPREPLY=( $( apt-cache pkgnames $cur 2> /dev/null ) ) + return 0 + ;; + remove) + COMPREPLY=( $( _comp_dpkg_installed_packages \ + $cur ) ) + return 0 + ;; + *) + return 0 + ;; + esac + fi + + case "$prev" in + + --@(patch|build-dir|repository-dir)) + _filedir + return 0 + ;; + + -@(h|-help)) + return 0 + ;; + + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--help --show-upgraded -u --build-dir \ + --repository-dir --build-only \ + --build-command --reinstall --rebuild \ + --remove-builddep --no-wrapper --purge \ + --patch --patch-strip -p --yes -y \ + --version -v --no-source' -- $cur ) ) + + else + COMPREPLY=( $( compgen -W 'update upgrade install remove \ + source dist-upgrade world clean info \ + clean-build update-repository ' -- $cur ) ) + fi + + + return 0 +} && +complete -F _apt_build $filenames apt-build + +# chsh(1) completion +# +_chsh() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [ "$prev" = "-s" ]; then + if [ -f /etc/debian_version ]; then + COMPREPLY=( $( </etc/shells ) ) + else + COMPREPLY=( $( chsh -l | grep "^$cur" ) ) + fi + else + COMPREPLY=( $( compgen -u -- $cur ) ) + fi + + return 0 +} +complete -F _chsh chsh + +# chkconfig(8) completion +# +have chkconfig && +_chkconfig() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + @([1-6]|--@(list|add|del))) + _services + return 0 + ;; + --level) + COMPREPLY=( $( compgen -W '1 2 3 4 5 6' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--list --add --del --level' -- $cur ) ) + else + if [ $COMP_CWORD -eq 2 -o $COMP_CWORD -eq 4 ]; then + COMPREPLY=( $( compgen -W 'on off reset' -- $cur ) ) + else + _services + fi + fi +} && +complete -F _chkconfig chkconfig + +# This function provides simple user@host completion +# +_user_at_host() { + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ $cur == *@* ]]; then + _known_hosts + else + COMPREPLY=( $( compgen -u -- "$cur" ) ) + fi + + return 0 +} +shopt -u hostcomplete && complete -F _user_at_host $nospace talk ytalk finger + +# This function performs host completion based on ssh's known_hosts files, +# defaulting to standard host completion if they don't exist. +# +_known_hosts() +{ + local cur curd ocur user suffix aliases global_kh user_kh hosts i host + local -a kh khd config + + COMPREPLY=() + cur=`_get_cword` + ocur=$cur + + [ "$1" = -a ] || [ "$2" = -a ] && aliases='yes' + [ "$1" = -c ] || [ "$2" = -c ] && suffix=':' + [[ $cur == *@* ]] && user=${cur%@*}@ && cur=${cur#*@} + kh=() + + # ssh config files + [ -r /etc/ssh/ssh_config ] && + config=( "${config[@]}" "/etc/ssh/ssh_config" ) + [ -r "${HOME}/.ssh/config" ] && + config=( "${config[@]}" "${HOME}/.ssh/config" ) + [ -r "${HOME}/.ssh2/config" ] && + config=( "${config[@]}" "${HOME}/.ssh2/config" ) + + if [ ${#config[@]} -gt 0 ]; then + # expand path (if present) to global known hosts file + global_kh=$( eval echo $( sed -ne 's/^[ \t]*[Gg][Ll][Oo][Bb][Aa][Ll][Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee]['"$'\t '"']*\(.*\)$/\1/p' "${config[@]}" ) ) + # expand path (if present) to user known hosts file + user_kh=$( eval echo $( sed -ne 's/^[ \t]*[Uu][Ss][Ee][Rr][Kk][Nn][Oo][Ww][Nn][Hh][Oo][Ss][Tt][Ss][Ff][Ii][Ll][Ee]['"$'\t '"']*\(.*\)$/\1/p' "${config[@]}" ) ) + fi + + # Global known_hosts files + [ -r "$global_kh" ] && + kh=( "${kh[@]}" "$global_kh" ) + [ -r /etc/ssh/ssh_known_hosts ] && + kh=( "${kh[@]}" /etc/ssh/ssh_known_hosts ) + [ -r /etc/ssh/ssh_known_hosts2 ] && + kh=( "${kh[@]}" /etc/ssh/ssh_known_hosts2 ) + [ -r /etc/known_hosts ] && + kh=( "${kh[@]}" /etc/known_hosts ) + [ -r /etc/known_hosts2 ] && + kh=( "${kh[@]}" /etc/known_hosts2 ) + [ -d /etc/ssh2/knownhosts ] && + khd=( "${khd[@]}" /etc/ssh2/knownhosts/*pub ) + + # User known_hosts files + [ -r "$user_kh" ] && + kh=( "${kh[@]}" "$user_kh" ) + [ -r ~/.ssh/known_hosts ] && + kh=( "${kh[@]}" ~/.ssh/known_hosts ) + [ -r ~/.ssh/known_hosts2 ] && + kh=( "${kh[@]}" ~/.ssh/known_hosts2 ) + [ -d ~/.ssh2/hostkeys ] && + khd=( "${khd[@]}" ~/.ssh2/hostkeys/*pub ) + + # If we have known_hosts files to use + if [ ${#kh[@]} -gt 0 -o ${#khd[@]} -gt 0 ]; then + # Escape slashes and dots in paths for awk + cur=${cur//\//\\\/} + cur=${cur//\./\\\.} + curd=$cur + + if [[ "$cur" == [0-9]*.* ]]; then + # Digits followed by a dot - just search for that + cur="^$cur.*" + elif [[ "$cur" == [0-9]* ]]; then + # Digits followed by no dot - search for digits followed + # by a dot + cur="^$cur.*\." + elif [ -z "$cur" ]; then + # A blank - search for a dot or an alpha character + cur="[a-z.]" + else + cur="^$cur" + fi + + if [ ${#kh[@]} -gt 0 ]; then + + # FS needs to look for a comma separated list + COMPREPLY=( $( awk 'BEGIN {FS=","} + /^[^|]/ {for (i=1; i<=2; ++i) { \ + gsub(" .*$", "", $i); \ + if ($i ~ /'$cur'/) {print $i} \ + }}' "${kh[@]}" 2>/dev/null ) ) + fi + if [ ${#khd[@]} -gt 0 ]; then + # Needs to look for files called + # .../.ssh2/key_22_<hostname>.pub + # dont fork any processes, because in a cluster environment, + # there can be hundreds of hostkeys + for i in "${khd[@]}" ; do + if [[ "$i" == *key_22_$curd*.pub ]] && [ -r "$i" ] ; then + host=${i/#*key_22_/} + host=${host/%.pub/} + COMPREPLY=( "${COMPREPLY[@]}" $host ) + fi + done + fi + + # append any available aliases from config files + if [ ${#config[@]} -gt 0 ] && [ -n "$aliases" ]; then + local host_aliases=$( sed -ne 's/^[Hh][Oo][Ss][Tt]\([Nn][Aa][Mm][Ee]\)\?['"$'\t '"']\+\([^*?]*\)$/\2/p' "${config[@]}" ) + hosts=$( compgen -W "$host_aliases" -- $ocur ) + COMPREPLY=( "${COMPREPLY[@]}" $hosts ) + fi + + # Now add results of normal hostname completion + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -A hostname -- $ocur ) ) + + # apply suffix + for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do + COMPREPLY[i]=$user${COMPREPLY[i]}$suffix + done + else + # Just do normal hostname completion + COMPREPLY=( $( compgen -A hostname -S "$suffix" -- $cur ) ) + fi + + return 0 +} +complete -F _known_hosts traceroute traceroute6 tracepath tracepath6 \ + ping ping6 fping fping6 telnet host nslookup rsh rlogin ftp dig ssh-installkeys mtr + +# ssh(1) completion +# +have ssh && { +_ssh() +{ + local cur prev + local -a config + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -*c) + COMPREPLY=( $( compgen -W 'blowfish 3des 3des-cbc blowfish-cbc \ + arcfour cast128-cbc' -- $cur ) ) + ;; + -*i) + _filedir + ;; + -*l) + COMPREPLY=( $( compgen -u -- $cur ) ) + ;; + *) + _known_hosts -a + + [ $COMP_CWORD -eq 1 ] || \ + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -c -- $cur ) ) + esac + + return 0 +} +shopt -u hostcomplete && complete -F _ssh ssh slogin sftp xhost autossh + +# scp(1) completion +# +_scp() +{ + local cur userhost path + + COMPREPLY=() + cur=`_get_cword` + + _expand || return 0 + + if [[ "$cur" == *:* ]]; then + local IFS=$'\t\n' + # remove backslash escape from : + cur=${cur/\\:/:} + userhost=${cur%%?(\\):*} + path=${cur#*:} + # unescape spaces + path=${path//\\\\\\\\ / } + if [ -z "$path" ]; then + # default to home dir of specified user on remote host + path=$(ssh -o 'Batchmode yes' $userhost pwd 2>/dev/null) + fi + # escape spaces; remove executables, aliases, pipes and sockets; + # add space at end of file names + COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \ + command ls -aF1d "$path*" 2>/dev/null | \ + sed -e "s/[][(){}<>\",:;^&!$&=?\`|\\ ']/\\\\\\\\\\\\&/g" \ + -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' ) ) + return 0 + fi + + [[ "$cur" == */* ]] || _known_hosts -c -a + local IFS=$'\t\n' + COMPREPLY=( "${COMPREPLY[@]}" $( command ls -aF1d $cur* \ + 2>/dev/null | sed \ + -e "s/[][(){}<>\",:;^&!$&=?\`|\\ ']/\\\\&/g" \ + -e 's/[*@|=]$//g' -e 's/[^\/]$/& /g' ) ) + return 0 +} +complete -F _scp $nospace scp +} + +# rsync(1) completion +# +have rsync && +_rsync() +{ + local cur prev shell i userhost path + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + _expand || return 0 + + case "$prev" in + --@(config|password-file|include-from|exclude-from)) + _filedir + return 0 + ;; + -@(T|-temp-dir|-compare-dest)) + _filedir -d + return 0 + ;; + -@(e|-rsh)) + COMPREPLY=( $( compgen -W 'rsh ssh' -- $cur ) ) + return 0 + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W '-v -q -c -a -r -R -b -u -l -L -H \ + -p -o -g -D -t -S -n -W -x -B -e -C -I -T -P \ + -z -h -4 -6 --verbose --quiet --checksum \ + --archive --recursive --relative --backup \ + --backup-dir --suffix= --update --links \ + --copy-links --copy-unsafe-links --safe-links \ + --hard-links --perms --owner --group --devices\ + --times --sparse --dry-run --whole-file \ + --no-whole-file --one-file-system \ + --block-size= --rsh= --rsync-path= \ + --cvs-exclude --existing --ignore-existing \ + --delete --delete-excluded --delete-after \ + --ignore-errors --max-delete= --partial \ + --force --numeric-ids --timeout= \ + --ignore-times --size-only --modify-window= \ + --temp-dir= --compare-dest= --compress \ + --exclude= --exclude-from= --include= \ + --include-from= --version --daemon --no-detach\ + --address= --config= --port= --blocking-io \ + --no-blocking-io --stats --progress \ + --log-format= --password-file= --bwlimit= \ + --write-batch= --read-batch= --help' -- $cur )) + ;; + *:*) + # find which remote shell is used + shell=rsh + for (( i=1; i < COMP_CWORD; i++ )); do + if [[ "${COMP_WORDS[i]}" == -@(e|-rsh) ]]; then + shell=${COMP_WORDS[i+1]} + break + fi + done + if [[ "$shell" == ssh ]]; then + # remove backslash escape from : + cur=${cur/\\:/:} + userhost=${cur%%?(\\):*} + path=${cur#*:} + # unescape spaces + path=${path//\\\\\\\\ / } + if [ -z "$path" ]; then + # default to home dir of specified + # user on remote host + path=$(ssh -o 'Batchmode yes' \ + $userhost pwd 2>/dev/null) + fi + # escape spaces; remove executables, aliases, pipes + # and sockets; add space at end of file names + COMPREPLY=( $( ssh -o 'Batchmode yes' $userhost \ + command ls -aF1d "$path*" 2>/dev/null | \ + sed -e 's/ /\\\\\\\ /g' -e 's/[*@|=]$//g' \ + -e 's/[^\/]$/& /g' ) ) + fi + ;; + *) + _known_hosts -c -a + _filedir + ;; + esac + + return 0 +} && +complete -F _rsync $nospace $filenames rsync + +# Linux route(8) completion +# +[ $UNAME = Linux ] && +_route() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [ "$prev" = dev ]; then + COMPREPLY=( $( ifconfig -a | sed -ne 's|^\('$cur'[^ ]*\).*$|\1|p' )) + return 0 + fi + + COMPREPLY=( $( compgen -W 'add del -host -net netmask metric mss \ + window irtt reject mod dyn reinstate dev \ + default gw' -- $cur ) ) + + COMPREPLY=( $( echo " ${COMP_WORDS[@]}" | \ + (while read -d ' ' i; do + [ "$i" == "" ] && continue + # flatten array with spaces on either side, + # otherwise we cannot grep on word + # boundaries of first and last word + COMPREPLY=" ${COMPREPLY[@]} " + # remove word from list of completions + COMPREPLY=( ${COMPREPLY/ $i / } ) + done + echo "${COMPREPLY[@]}") + ) ) + return 0 +} +[ $UNAME = Linux ] && complete -F _route route + +# GNU make(1) completion +# +have make || have gmake || have gnumake || have pmake && +_make() +{ + local file makef makef_dir="." makef_inc cur prev i + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + # --name value style option + case $prev in + -@(f|o|W)) + _filedir + return 0 + ;; + -@(I|C)) + _filedir -d + return 0 + ;; + esac + + # --name=value style option + if [[ "$cur" == *=* ]]; then + prev=${cur/=*/} + cur=${cur/*=/} + case "$prev" in + --@(file|makefile)) + _filedir + return 0 + ;; + --@(directory|include-dir)) + _filedir -d + return 0 + ;; + esac + fi + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-b -m -B -C -d -e -f -h -i -I\ + -j -l -k -n -o -p -q -r -R - s -S -t -v -w -W \ + --always-make --directory= --debug \ + --environment-overrides --file= --makefile= --help \ + --ignore-errors --include-dir= --jobs --load-average \ + --max-load --keep-going --just-print --dry-run \ + --recon --old-file= --assume-old= --print-data-base \ + --question --no-builtin-rules --no-builtin-variables \ + --silent --quiet --no-keep-goind --stop --touch \ + --version --print-directory --no-print-directory \ + --what-if= --new-file= --assume-new= \ + --warn-undefined-variables' -- $cur ) ) + else + # before we check for makefiles, see if a path was specified + # with -C + for (( i=0; i < ${#COMP_WORDS[@]}; i++ )); do + if [[ ${COMP_WORDS[i]} == -C ]]; then + # eval for tilde expansion + eval makef_dir=${COMP_WORDS[i+1]} + break + fi + done + + # make reads `GNUmakefile', then `makefile', then `Makefile' + if [ -f ${makef_dir}/GNUmakefile ]; then + makef=${makef_dir}/GNUmakefile + elif [ -f ${makef_dir}/makefile ]; then + makef=${makef_dir}/makefile + elif [ -f ${makef_dir}/Makefile ]; then + makef=${makef_dir}/Makefile + else + makef=${makef_dir}/*.mk # local convention + fi + + # before we scan for targets, see if a Makefile name was + # specified with -f + for (( i=0; i < ${#COMP_WORDS[@]}; i++ )); do + if [[ ${COMP_WORDS[i]} == -f ]]; then + # eval for tilde expansion + eval makef=${COMP_WORDS[i+1]} + break + fi + done + + [ ! -f $makef ] && return 0 + + # deal with included Makefiles + makef_inc=$( grep -E '^-?include' $makef | sed -e "s,^.* ,"$makef_dir"/," ) + + for file in $makef_inc; do + [ -f $file ] && makef="$makef $file" + done + + COMPREPLY=( $( awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ \ + {split($1,A,/ /);for(i in A)print A[i]}' \ + $makef 2>/dev/null | command grep "^$cur" )) + fi +} && +complete -f -F _make $filenames make gmake gnumake pmake + +# GNU tar(1) completion +# +_tar() +{ + local cur ext regex tar untar + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $( compgen -W 'c t x u r d A' -- $cur ) ) + return 0 + fi + + case "${COMP_WORDS[1]}" in + ?(-)[cr]*f) + _filedir + return 0 + ;; + +([^IZzjy])f) + ext='t@(ar?(.@(Z|gz|bz?(2)))|gz|bz?(2))' + regex='t\(ar\(\.\(Z\|gz\|bz2\?\)\)\?\|gz\|bz2\?\)' + ;; + *[Zz]*f) + ext='t?(ar.)@(gz|Z)' + regex='t\(ar\.\)\?\(gz\|Z\)' + ;; + *[Ijy]*f) + ext='t?(ar.)bz?(2)' + regex='t\(ar\.\)\?bz2\?' + ;; + *) + _filedir + return 0 + ;; + + esac + + if [[ "$COMP_LINE" == *$ext' ' ]]; then + # complete on files in tar file + # + # get name of tar file from command line + tar=$( echo "$COMP_LINE" | \ + sed -e 's/^.* \([^ ]*'$regex'\) .*$/\1/' ) + # devise how to untar and list it + untar=t${COMP_WORDS[1]//[^Izjyf]/} + + COMPREPLY=( $( compgen -W "$( echo $( tar $untar $tar \ + 2>/dev/null ) )" -- "$cur" ) ) + return 0 + fi + + # file completion on relevant files + _filedir "$ext" + + return 0 +} +[ -n "${COMP_TAR_INTERNAL_PATHS:-}" ] && complete -F _tar $dirnames tar || + complete -F _tar $filenames tar + +# jar(1) completion +# +have jar && +_jar() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD = 1 ]; then + COMPREPLY=( $( compgen -W 'c t x u' -- $cur ) ) + return 0 + fi + + case "${COMP_WORDS[1]}" in + *c*f) + _filedir + ;; + *f) + _filedir '?(e|j|w)ar' + ;; + *) + _filedir + ;; + esac +} && +complete -F _jar $filenames jar + +# Linux iptables(8) completion +# +have iptables && +_iptables() +{ + local cur prev table chain + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + chain='s/^Chain \([^ ]\+\).*$/\1/p' + + if [[ $COMP_LINE == *-t\ *filter* ]]; then + table="-t filter" + elif [[ $COMP_LINE == *-t\ *nat* ]]; then + table="-t nat" + elif [[ $COMP_LINE == *-t\ *mangle* ]]; then + table="-t mangle" + fi + + case "$prev" in + -*[AIDRPFXLZ]) + COMPREPLY=( $( compgen -W '`iptables $table -nL | \ + sed -ne "s/^Chain \([^ ]\+\).*$/\1/p"`' -- $cur ) ) + ;; + -*t) + COMPREPLY=( $( compgen -W 'nat filter mangle' -- $cur ) ) + ;; + -j) + if [ "$table" = "-t filter" -o "$table" = "" ]; then + COMPREPLY=( $( compgen -W 'ACCEPT DROP LOG ULOG REJECT \ + `iptables $table -nL | sed -ne "$chain" \ + -e "s/INPUT|OUTPUT|FORWARD|PREROUTING|POSTROUTING//"`' -- \ + $cur ) ) + elif [ "$table" = "-t nat" ]; then + COMPREPLY=( $( compgen -W 'ACCEPT DROP LOG ULOG REJECT \ + MIRROR SNAT DNAT MASQUERADE `iptables $table -nL | \ + sed -ne "$chain" -e "s/OUTPUT|PREROUTING|POSTROUTING//"`' \ + -- $cur ) ) + elif [ "$table" = "-t mangle" ]; then + COMPREPLY=( $( compgen -W 'ACCEPT DROP LOG ULOG REJECT \ + MARK TOS `iptables $table -nL | sed -ne "$chain" \ + -e "s/INPUT|OUTPUT|FORWARD|PREROUTING|POSTROUTING//"`' -- \ + $cur ) ) + fi + ;; + *) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-i -o -s -d -p -f -m --append \ + --delete --insert --replace --list --flush --zero --new \ + --delete-chain --policy --rename-chain --proto --source \ + --destination --in-interface --jump --match --numeric \ + --out-interface --table --verbose --line-numbers --exact \ + --fragment --modprobe= --set-counters --version' -- "$cur") ) + fi + ;; + esac + +} && +complete -F _iptables iptables + +# tcpdump(8) completion +# +have tcpdump && +_tcpdump() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(r|w|F)) + _filedir + return 0 + ;; + -i) + _available_interfaces -a + return 0 + ;; + esac + + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-a -d -e -f -l -n -N -O -p \ + -q -R -S -t -u -v -x -C -F -i -m -r -s -T -w \ + -E' -- $cur ) ) + fi + +} && +complete -F _tcpdump tcpdump + +# autorpm(8) completion +# +have autorpm && +_autorpm() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + COMPREPLY=( $( compgen -W '--notty --debug --help --version \ + auto add fullinfo info help install list \ + remove set' -- $cur ) ) + +} && +complete -F _autorpm autorpm + +# This meta-cd function observes the CDPATH variable, so that cd additionally +# completes on directories under those specified in CDPATH. +# +_cd() +{ + local IFS=$'\t\n' cur=`_get_cword` i j k + + # try to allow variable completion + if [[ "$cur" == ?(\\)\$* ]]; then + COMPREPLY=( $( compgen -v -P '$' -- "${cur#?(\\)$}" ) ) + return 0 + fi + + # Use standard dir completion if no CDPATH or parameter starts with /, + # ./ or ../ + if [ -z "${CDPATH:-}" ] || [[ "$cur" == ?(.)?(.)/* ]]; then + _filedir -d + return 0 + fi + + local -r mark_dirs=$(_rl_enabled mark-directories && echo y) + local -r mark_symdirs=$(_rl_enabled mark-symlinked-directories && echo y) + + # we have a CDPATH, so loop on its contents + for i in ${CDPATH//:/$'\t'}; do + # create an array of matched subdirs + k="${#COMPREPLY[@]}" + for j in $( compgen -d $i/$cur ); do + if [[ ( $mark_symdirs && -h $j || $mark_dirs && ! -h $j ) && ! -d ${j#$i/} ]]; then + j="${j}/" + fi + COMPREPLY[k++]=${j#$i/} + done + done + + _filedir -d + + if [[ ${#COMPREPLY[@]} -eq 1 ]]; then + i=${COMPREPLY[0]} + if [ "$i" == "$cur" ] && [[ $i != "*/" ]]; then + COMPREPLY[0]="${i}/" + fi + fi + + return 0 +} +if shopt -q cdable_vars; then + complete -v -F _cd $nospace $filenames cd +else + complete -F _cd $nospace $filenames cd +fi + +_remove_comp_word() +{ + if [[ COMP_CWORD -eq 0 ]]; then + return + elif [[ ${#COMP_WORDS[@]} -ge 2 ]]; then + local old_cw0="${COMP_WORDS[0]}" + local new_cw0="${COMP_WORDS[1]}" + local old_length="${#COMP_LINE}" + COMP_LINE=${COMP_LINE#${old_cw0}} + local head=${COMP_LINE:0:${#new_cw0}} + local i=1 + while [[ $head != $new_cw0 ]]; do + COMP_LINE=${COMP_LINE:1} + head=${COMP_LINE:0:${#new_cw0}} + if (( ++i > 10 )); then + break + fi + done + local new_length="${#COMP_LINE}" + COMP_POINT=$(( COMP_POINT + new_length - old_length)) + + COMP_CWORD=$(( COMP_CWORD - 1 )) + for (( i=0; i < ${#COMP_WORDS[@]} - 1; ++i )); do + COMP_WORDS[i]="${COMP_WORDS[i+1]}" + done + unset COMP_WORDS[${#COMP_WORDS[@]}-1] + else + return + fi +} + +# A meta-command completion function for commands like sudo(8), which need to +# first complete on a command, then complete according to that command's own +# completion definition - currently not quite foolproof (e.g. mount and umount +# don't work properly), but still quite useful. +# +_command() +{ + local cur func cline cspec noglob cmd done i \ + _COMMAND_FUNC _COMMAND_FUNC_ARGS + + _remove_comp_word + COMPREPLY=() + cur=`_get_cword` + # If the the first arguments following our meta-command-invoker are + # switches, get rid of them. Most definitely not foolproof. + done= + while [ -z $done ] ; do + cmd=${COMP_WORDS[0]} + if [[ "$cmd" == -* ]] && [ $COMP_CWORD -ge 1 ]; then + _remove_comp_word + elif [[ "$cmd" == -* ]] && [[ $COMP_CWORD -eq 0 ]]; then + return + else + done=1 + fi + done + + if [ $COMP_CWORD -eq 0 ]; then + COMPREPLY=( $( compgen -c -- $cur ) ) + elif complete -p $cmd &>/dev/null; then + cspec=$( complete -p $cmd ) + if [ "${cspec#* -F }" != "$cspec" ]; then + # COMP_CWORD and COMP_WORDS() are not read-only, + # so we can set them before handing off to regular + # completion routine + + # get function name + func=${cspec#*-F } + func=${func%% *} + + if [[ ${#COMP_WORDS[@]} -ge 2 ]]; then + $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}" "${COMP_WORDS[${#COMP_WORDS[@]}-2]}" + else + $func $cmd "${COMP_WORDS[${#COMP_WORDS[@]}-1]}" + fi + + # remove any \: generated by a command that doesn't + # default to filenames or dirnames (e.g. sudo chown) + # FIXME: I'm pretty sure this does not work! + if [ "${cspec#*-o }" != "$cspec" ]; then + cspec=${cspec#*-o } + cspec=${cspec%% *} + if [[ "$cspec" != @(dir|file)names ]]; then + COMPREPLY=("${COMPREPLY[@]//\\\\:/:}") + fi + fi + elif [ -n "$cspec" ]; then + cspec=${cspec#complete}; + cspec=${cspec%%$cmd}; + COMPREPLY=( $( eval compgen "$cspec" -- "$cur" ) ); + fi + fi + + [ ${#COMPREPLY[@]} -eq 0 ] && _filedir +} +complete -F _command $filenames nohup exec nice eval strace time ltrace then \ + else do vsound command xargs + +_root_command() +{ + PATH=/usr/gnu/bin:$PATH:/sbin:/usr/sbin _command $1 $2 $3 +} +complete -F _root_command $filenames sudo fakeroot really + +# ant(1) completion +# +have ant && { +_ant() +{ + local cur prev buildfile i + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -buildfile|-f) + _filedir 'xml' + return 0 + ;; + -logfile) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + # relevant options completion + COMPREPLY=( $( compgen -W '-help -projecthelp -version -quiet \ + -verbose -debug -emacs -logfile -logger \ + -listener -buildfile -f -D -find' -- $cur ) ) + else + # available targets completion + # find which buildfile to use + buildfile=build.xml + for (( i=1; i < COMP_CWORD; i++ )); do + if [[ "${COMP_WORDS[i]}" == -buildfile ]]; then + buildfile=${COMP_WORDS[i+1]} + break + fi + done + [ ! -f $buildfile ] && return 0 + + # parse buildfile for targets + COMPREPLY=( $( awk -F'"' '/<target name="/ {print $2}' \ + $buildfile | grep "^$cur" ) + $( awk -F"'" "/<target name='/ "'{print $2}' \ + $buildfile | grep "^$cur" ) + $( awk -F'"' '/<target [^n]/ {if ($1 ~ /name=/) { print $2 } else if ($3 ~ /name=/) {print $4} else if ($5 ~ /name=/) {print $6}}' \ + $buildfile | grep "^$cur" ) ) + fi +} +have complete-ant-cmd.pl && \ + complete -C complete-ant-cmd.pl -F _ant $filenames ant || \ + complete -F _ant $filenames ant +} + +have nslookup && +_nslookup() +{ + local cur + + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]#-} + + COMPREPLY=( $( compgen -P '-' -W 'all class= debug d2 domain= \ + srchlist= defname search port= querytype= \ + type= recurse retry root timeout vc \ + ignoretc' -- $cur ) ) +} && +complete -F _nslookup nslookup + +# mysqladmin(1) completion +# +have mysqladmin && +_mysqladmin() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -u) + COMPREPLY=( $( compgen -u -- $cur ) ) + return 0 + ;; + *) + ;; + esac + + COMPREPLY=( $( compgen -W '-# -f -? -C -h -p -P -i -r -E -s -S -t -u \ + -v -V -w' -- $cur ) ) + + COMPREPLY=( "${COMPREPLY[@]}" \ + $( compgen -W 'create drop extended-status flush-hosts \ + flush-logs flush-status flush-tables \ + flush-threads flush-privileges kill \ + password ping processlist reload refresh \ + shutdown status variables version' \ + -- $cur ) ) +} && +complete -F _mysqladmin mysqladmin + +# gzip(1) completion +# +have gzip && +_gzip() +{ + local cur prev xspec IFS=$'\t\n' + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-c -d -f \ + -h -l -L -n -N -q -r -S -t -v -V \ + -1 -2 -3 -4 -5 -6 -7 -8 -9 \ + --stdout --decompress --force --help --list \ + --license --no-name --name --quiet --recursive \ + --suffix --test --verbose --version --fast \ + --best' -- $cur ) ) + return 0 + fi + + xspec="*.?(t)gz" + if [[ "$prev" == --* ]]; then + [[ "$prev" == --decompress || \ + "$prev" == --list || \ + "$prev" == --test ]] && xspec="!"$xspec + [[ "$prev" == --force ]] && xspec= + elif [[ "$prev" == -* ]]; then + [[ "$prev" == -*[dlt]* ]] && xspec="!"$xspec + [[ "$prev" == -*f* ]] && xspec= + elif [ "$prev" = '>' ]; then + xspec= + elif [ "$prev" = '<' ]; then + xspec= + fi + + _expand || return 0 + + COMPREPLY=( $( compgen -f -X "$xspec" -- $cur ) \ + $( compgen -d -- $cur ) ) +} && +complete -F _gzip $filenames gzip + +# bzip2(1) completion +# +have bzip2 && +_bzip2() +{ + local cur prev xspec IFS=$'\t\n' + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-c -d -f -h -k -L -q -s \ + -t -v -V -z -1 -2 -3 -4 -5 -6 -7 -8 -9 \ + --help --decompress --compress --keep --force \ + --test --stdout --quiet --verbose --license \ + --version --small --fast --best' -- $cur ) ) + return 0 + fi + + xspec="*.bz2" + if [[ "$prev" == --* ]]; then + [[ "$prev" == --decompress || \ + "$prev" == --list || \ + "$prev" == --test ]] && xspec="!"$xspec + [[ "$prev" == --compress ]] && xspec= + elif [[ "$prev" == -* ]]; then + [[ "$prev" == -*[dt]* ]] && xspec="!"$xspec + [[ "$prev" == -*z* ]] && xspec= + fi + + _expand || return 0 + + COMPREPLY=( $( compgen -f -X "$xspec" -- $cur ) \ + $( compgen -d -- $cur ) ) +} && +complete -F _bzip2 $filenames bzip2 + +# openssl(1) completion +# +have openssl && { +_openssl_sections() +{ + local config + + config=/etc/ssl/openssl.cnf + [ ! -f $config ] && config=/usr/share/ssl/openssl.cnf + for (( i=2; i < COMP_CWORD; i++ )); do + if [[ "${COMP_WORDS[i]}" == -config ]]; then + config=${COMP_WORDS[i+1]} + break + fi + done + [ ! -f $config ] && return 0 + + COMPREPLY=( $( awk '/\[.*\]/ {print $2} ' $config | grep "^$cur" ) ) +} + +_openssl() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $( compgen -W 'asn1parse ca ciphers crl crl2pkcs7 \ + dgst dh dhparam dsa dsaparam enc errstr gendh gendsa \ + genrsa nseq passwd pkcs12 pkcs7 pkcs8 rand req rsa \ + rsautl s_client s_server s_time sess_id smime speed \ + spkac verify version x509 md2 md4 md5 mdc2 rmd160 sha \ + sha1 base64 bf bf-cbc bf-cfb bf-ecb bf-ofb cast \ + cast-cbc cast5-cbc cast5-cfb cast5-ecb cast5-ofb des \ + des-cbc des-cfb des-ecb des-ede des-ede-cbc \ + des-ede-cfb des-ede-ofb des-ede3 des-ede3-cbc \ + des-ede3-cfb des-ede3-ofb des-ofb des3 desx rc2 \ + rc2-40-cbc rc2-64-cbc rc2-cbc rc2-cfb rc2-ecb rc2-ofb \ + rc4 rc4-40' -- $cur ) ) + else + prev=${COMP_WORDS[COMP_CWORD-1]} + case ${COMP_WORDS[1]} in + asn1parse) + case $prev in + -inform) + COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) ) + return 0 + ;; + -@(in|out|oid)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-inform -in -out -noout -offset \ + -length -i -oid -strparse' -- $cur ) ) + fi + ;; + ca) + case $prev in + -@(config|revoke|cert|in|out|spkac|ss_cert)) + _filedir + return 0 + ;; + -outdir) + _filedir -d + return 0 + ;; + -@(name|crlexts|extensions)) + _openssl_sections + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-verbose -config -name \ + -gencrl -revoke -crldays -crlhours -crlexts \ + -startdate -enddate -days -md -policy -keyfile \ + -key -passin -cert -in -out -notext -outdir \ + -infiles -spkac -ss_cert -preserveDN -batch \ + -msie_hack -extensions' -- $cur ) ) + fi + ;; + ciphers) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-v -ssl2 -ssl3 -tls1' -- $cur ) ) + fi + ;; + crl) + case $prev in + -@(in|out)form) + COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) ) + return 0 + ;; + -@(in|out|CAfile)) + _filedir + return 0 + ;; + -CAPath) + _filedir -d + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-inform -outform -text -in -out -noout \ + -hash -issuer -lastupdate -nextupdate -CAfile -CApath' -- $cur ) ) + fi + ;; + crl2pkcs7) + case $prev in + -@(in|out)form) + COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) ) + return 0 + ;; + -@(in|out)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-inform -outform -in -out -print_certs' -- $cur ) ) + fi + ;; + dgst) + case $prev in + -@(out|sign|verify|prvrify|signature)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-md5 -md4 -md2 -sha1 -sha -mdc2 -ripemd160 -dss1 \ + -c -d -hex -binary -out -sign -verify -prverify -signature' -- $cur ) ) + else + _filedir + fi + ;; + dsa) + case $prev in + -@(in|out)form) + COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) ) + return 0 + ;; + -@(in|out)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-inform -outform -in -passin -out -passout -des -des3 -idea -text -noout \ + -modulus -pubin -pubout' -- $cur ) ) + fi + ;; + dsaparam) + case $prev in + -@(in|out)form) + COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) ) + return 0 + ;; + -@(in|out|rand)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-inform -outform -in -out -noout \ + -text -C -rand -genkey' -- $cur ) ) + fi + ;; + enc) + case $prev in + -@(in|out|kfile)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-ciphername -in -out -pass \ + -e -d -a -A -k -kfile -S -K -iv -p -P -bufsize -debug' -- $cur ) ) + fi + ;; + dhparam) + case $prev in + -@(in|out)form) + COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) ) + return 0 + ;; + -@(in|out|rand)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-inform -outform -in -out -dsaparam -noout \ + -text -C -2 -5 -rand' -- $cur ) ) + fi + ;; + gendsa) + case $prev in + -@(out|rand)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-out -des -des3 -idea -rand' -- $cur ) ) + else + _filedir + fi + ;; + genrsa) + case $prev in + -@(out|rand)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-out -passout -des -des3 -idea -f4 -3 -rand' -- $cur ) ) + fi + ;; + pkcs7) + case $prev in + -@(in|out)form) + COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) ) + return 0 + ;; + -@(in|out)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-inform -outform -in -out -print_certs -text -noout' -- $cur ) ) + fi + ;; + rand) + case $prev in + -@(out|rand)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-out -rand -base64' -- $cur ) ) + fi + ;; + req) + case "$prev" in + -@(in|out|key)form) + COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) ) + return 0 + ;; + + -@(in|out|rand|key|keyout|config)) + _filedir + return 0 + ;; + -extensions) + _openssl_sections + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-inform -outform -in \ + -passin -out -passout -text -noout -verify \ + -modulus -new -rand -newkey -newkey -nodes \ + -key -keyform -keyout -md5 -sha1 -md2 -mdc2 \ + -config -x509 -days -asn1-kludge -newhdr \ + -extensions -reqexts section' -- $cur ) ) + fi + ;; + rsa) + case $prev in + -@(in|out)form) + COMPREPLY=( $( compgen -W 'DER NET PEM' -- $cur ) ) + return 0 + ;; + -@(in|out)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-inform -outform -in -passin -out -passout \ + -sgckey -des -des3 -idea -text -noout -modulus -check -pubin \ + -pubout -engine' -- $cur ) ) + fi + ;; + rsautl) + case $prev in + -@(in|out|inkey)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-in -out -inkey -pubin -certin -sign -verify \ + -encrypt -decrypt -pkcs -ssl -raw -hexdump -asn1parse' -- $cur ) ) + fi + ;; + s_client) + case $prev in + -connect) + _known_hosts + return 0 + ;; + -@(cert|key|CAfile|rand)) + _filedir + return 0 + ;; + -CApath) + _filedir -d + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-connect -verify -cert -key -CApath -CAfile \ + -reconnect -pause -showcerts -debug -msg -nbio_test -state -nbio \ + -crlf -ign_eof -quiet -ssl2 -ssl3 -tls1 -no_ssl2 -no_ssl3 -no_tls1 \ + -bugs -cipher -starttls -engine -rand' -- $cur ) ) + fi + ;; + s_server) + case $prev in + -@(cert|key|dcert|dkey|dhparam|CAfile|rand)) + _filedir + return 0 + ;; + -CApath) + _filedir -d + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-accept -context -verify -Verify -cert -key \ + -dcert -dkey -dhparam -nbio -nbio_test -crlf -debug -msg -state -CApath \ + -CAfile -nocert -cipher -quiet -no_tmp_rsa -ssl2 -ssl3 -tls1 -no_ssl2 \ + -no_ssl3 -no_tls1 -no_dhe -bugs -hack -www -WWW -HTTP -engine -id_prefix \ + -rand' -- $cur ) ) + fi + ;; + s_time) + case $prev in + -connect) + _known_hosts + return 0 + ;; + -@(cert|key|CAfile)) + _filedir + return 0 + ;; + -CApath) + _filedir -d + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-connect -www -cert -key -CApath -CAfile -reuse \ + -new -verify -nbio -time -ssl2 -ssl3 -bugs -cipher' -- $cur ) ) + fi + ;; + + sess_id) + case $prev in + -@(in|out)form) + COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) ) + return 0 + ;; + -@(in|out)) + _filedir + return 0 + ;; + esac + + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-inform -outform -in -out -text -noout \ + -context ID' -- $cur ) ) + fi + ;; + smime) + case $prev in + -@(in|out)form) + COMPREPLY=( $( compgen -W 'SMIME DER PEM' -- $cur ) ) + return 0 + ;; + -@(in|out|certfile|signer|recip|inkey|content|rand)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-encrypt -decrypt -sign -verify -pk7out -des -des3 \ + -rc2-40 -rc2-64 -rc2-128 -aes128 -aes192 -aes256 -in -certfile -signer \ + -recip -inform -passin -inkey -out -outform -content -to -from -subject \ + -text -rand' -- $cur ) ) + else + _filedir + fi + ;; + speed) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-engine' -- $cur ) ) + else + COMPREPLY=( $( compgen -W 'md2 mdc2 md5 hmac sha1 rmd160 idea-cbc \ + rc2-cbc rc5-cbc bf-cbc des-cbc des-ede3 rc4 rsa512 rsa1024 rsa2048 \ + rsa4096 dsa512 dsa1024 dsa2048 idea rc2 des rsa blowfish' -- $cur ) ) + fi + ;; + verify) + case $prev in + -@(CAfile|untrusted)) + _filedir + return 0 + ;; + -CApath) + _filedir -d + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-CApath -CAfile -purpose -untrusted -help -issuer_checks \ + -verbose -certificates' -- $cur ) ) + else + _filedir + fi + ;; + x509) + case "$prev" in + -@(in|out|CA|CAkey|CAserial|extfile)) + _filedir + return 0 + ;; + -@(in|out)form) + COMPREPLY=( $( compgen -W 'DER PEM NET' -- $cur ) ) + return 0 + ;; + -@(key|CA|CAkey)form) + COMPREPLY=( $( compgen -W 'DER PEM' -- $cur ) ) + return 0 + ;; + -extensions) + _openssl_sections + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-inform -outform \ + -keyform -CAform -CAkeyform -in -out \ + -serial -hash -subject -issuer -nameopt \ + -email -startdate -enddate -purpose \ + -dates -modulus -fingerprint -alias \ + -noout -trustout -clrtrust -clrreject \ + -addtrust -addreject -setalias -days \ + -set_serial -signkey -x509toreq -req \ + -CA -CAkey -CAcreateserial -CAserial \ + -text -C -md2 -md5 -sha1 -mdc2 -clrext \ + -extfile -extensions -engine' -- $cur ) ) + fi + ;; + @(md5|md4|md2|sha1|sha|mdc2|ripemd160)) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-c -d' -- $cur ) ) + else + _filedir + fi + ;; + esac + fi + + return 0 +} +complete -F _openssl $default openssl +} + +# screen(1) completion +# +have screen && +_screen() +{ + local cur prev preprev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + [ "$COMP_CWORD" -ge 2 ] && preprev=${COMP_WORDS[COMP_CWORD-2]} + + if [ "$preprev" = "-d" -o "$preprev" = "-D" -a "$prev" = "-r" -o \ + "$prev" = "-R" ]; then + # list all + COMPREPLY=( $( command screen -ls | \ + sed -ne 's|^['$'\t'']\+\('$cur'[0-9]\+\.[^'$'\t'']\+\).*$|\1|p' ) ) + else + case "$prev" in + -[rR]) + # list detached + COMPREPLY=( $( command screen -ls | \ + sed -ne 's|^['$'\t'']\+\('$cur'[0-9]\+\.[^'$'\t'']\+\).*Detached.*$|\1|p' ) ) + ;; + -[dDx]) + # list attached + COMPREPLY=( $( command screen -ls | \ + sed -ne 's|^['$'\t'']\+\('$cur'[0-9]\+\.[^'$'\t'']\+\).*Attached.*$|\1|p' ) ) + ;; + -s) + # shells + COMPREPLY=( $( grep ^${cur:-[^#]} /etc/shells ) ) + ;; + *) + ;; + esac + fi + + return 0 +} && +complete -F _screen $default screen + +# lftp(1) bookmark completion +# +have lftp && +_lftp() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD -eq 1 ] && [ -f ~/.lftp/bookmarks ]; then + COMPREPLY=( $( compgen -W '$( sed -ne "s/^\(.*\)'$'\t''.*$/\1/p" \ + ~/.lftp/bookmarks )' -- $cur ) ) + fi + + return 0 +} && +complete -F _lftp $default lftp + +# ncftp(1) bookmark completion +# +have ncftp && +_ncftp() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD -eq 1 ] && [ -f ~/.ncftp/bookmarks ]; then + COMPREPLY=( $( compgen -W '$( sed -ne "s/^\([^,]\{1,\}\),.*$/\1/p" \ + ~/.ncftp/bookmarks )' -- $cur ) ) + fi + + return 0 +} && +complete -F _ncftp $default ncftp + +# gdb(1) completion +# +have gdb && +_gdb() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + if [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $( compgen -c -- $cur ) ) + elif [ $COMP_CWORD -eq 2 ]; then + prev=${prev##*/} + COMPREPLY=( $( compgen -fW "$( command ps axo comm,pid | \ + awk '{if ($1 ~ /^'"$prev"'/) print $2}' ) )" \ + -- "$cur" ) ) + fi +} && +complete -F _gdb $filenames gdb + +# Postgresql completion +# +have psql && { +_pg_databases() +{ + return + COMPREPLY=( $( psql -l 2>/dev/null | \ + sed -e '1,/^-/d' -e '/^(/,$d' | \ + awk '{print $1}' | grep "^$cur" ) ) +} + +_pg_users() +{ + #COMPREPLY=( $( psql -qtc 'select usename from pg_user' template1 2>/dev/null | \ + # grep "^ $cur" ) ) + #[ ${#COMPREPLY[@]} -eq 0 ] && + COMPREPLY=( $( compgen -u -- $cur ) ) +} + +# createdb(1) completion +# +_createdb() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(h|-host=)) + _known_hosts + return 0 + ;; + -@(U|-username=)) + _pg_users + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-D -T -E -h -p -U -W -e -q \ + --location= --template= --encoding= --host= --port= \ + --username= --password --echo --quiet --help' -- $cur )) + else + _pg_databases + fi +} +complete -F _createdb $default createdb + +# dropdb(1) completion +# +_dropdb() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(h|-host=)) + _known_hosts + return 0 + ;; + -@(U|-username=)) + _pg_users + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-h -p -U -W -e -q \ + --host= --port= --username= --password \ + --interactive --echo --quiet --help' -- $cur ) ) + else + _pg_databases + fi +} +complete -F _dropdb $default dropdb + +# psql(1) completion +# +_psql() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -h|--host) + _known_hosts + return 0 + ;; + -U|--username) + _pg_users + return 0 + ;; + -d|--dbname) + _pg_databases + return 0 + ;; + -@(o|f)|--output|--file) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + # return list of available options + COMPREPLY=( $( compgen -W '-a --echo-all -A --no-align \ + -c --command -d --dbname -e --echo-queries \ + -E --echo-hidden -f --file -F --filed-separator \ + -h --host -H --html -l --list -n -o --output \ + -p --port -P --pset -q -R --record-separator \ + -s --single-step -S --single-line -t --tuples-only \ + -T --table-attr -U --username -v --variable \ + -V --version -W --password -x --expanded -X --nopsqlrc \ + -? --help ' -- $cur ) ) + else + # return list of available databases + _pg_databases + fi +} +complete -F _psql $default psql +} + +_longopt() +{ + local cur opt + + cur=`_get_cword` + + if [[ "$cur" == --*=* ]]; then + opt=${cur%%=*} + # cut backslash that gets inserted before '=' sign + opt=${opt%\\*} + cur=${cur#*=} + _filedir + COMPREPLY=( $( compgen -P "$opt=" -W '${COMPREPLY[@]}' -- $cur)) + return 0 + fi + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( $1 --help 2>&1 | sed -e '/--/!d' \ + -e 's/.*\(--[-A-Za-z0-9]\+=\?\).*/\1/' | \ + command grep "^$cur" | sort -u ) ) + elif [[ "$1" == @(mk|rm)dir ]]; then + _filedir -d + else + _filedir + fi +} +# makeinfo and texi2dvi are defined elsewhere. +for i in a2ps autoconf automake bc gprof ld nm objcopy objdump readelf strip \ + bison cpio diff patch enscript cp df dir du ln ls mkfifo mknod mv rm \ + touch vdir awk gperf grep grub indent less m4 sed shar date \ + tee who texindex cat csplit cut expand fmt fold head \ + md5sum nl od paste pr ptx sha1sum sort split tac tail tr unexpand \ + uniq wc ldd bash id irb mkdir rmdir; do + have $i && complete -F _longopt $filenames $i +done + +# These commands use filenames, so '-o filenames' is not needed. +for i in env netstat seq uname units wget; do + have $i && complete -F _longopt $default $i +done +unset i + +# gcc(1) completion +# +# The only unusual feature is that we don't parse "gcc --help -v" output +# directly, because that would include the options of all the other backend +# tools (linker, assembler, preprocessor, etc) without any indication that +# you cannot feed such options to the gcc driver directly. (For example, the +# linker takes a -z option, but you must type -Wl,-z for gcc.) Instead, we +# ask the driver ("g++") for the name of the compiler ("cc1"), and parse the +# --help output of the compiler. +# +have gcc && +_gcc() +{ + local cur cc backend + + COMPREPLY=() + cur=`_get_cword` + + _expand || return 0 + + case "$1" in + gcj) + backend=jc1 + ;; + gpc) + backend=gpc1 + ;; + *77) + backend=f771 + ;; + *) + backend=cc1 # (near-)universal backend + ;; + esac + + if [[ "$cur" == -* ]]; then + cc=$( $1 -print-prog-name=$backend ) + # sink stderr: + # for C/C++/ObjectiveC it's useless + # for FORTRAN/Java it's an error + COMPREPLY=( $( $cc --help 2>/dev/null | tr '\t' ' ' | \ + sed -e '/^ *-/!d' -e 's/ *-\([^ ]*\).*/-\1/' | \ + command grep "^$cur" | sort -u ) ) + else + _filedir + fi +} && +complete $filenames -F _gcc gcc g++ c++ g77 gcj gpc +[ $UNAME = GNU -o $UNAME = Linux -o $UNAME = Cygwin ] && \ +[ -n "${have:-}" ] && complete $filenames -F _gcc cc + +# Linux cardctl(8) completion +# +have cardctl && +_cardctl() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $( compgen -W 'status config ident suspend \ + resume reset eject insert scheme' \ + -- $cur ) ) + fi +} && +complete -F _cardctl cardctl + +# This function is required by _dpkg() and _dpkg-reconfigure() +# +have dpkg && { +_comp_dpkg_installed_packages() +{ + grep -A 1 "Package: $1" /var/lib/dpkg/status | \ + grep -B 1 -E "ok installed|half-installed|unpacked| \ + half-configured|config-files" | \ + grep "Package: $1" | cut -d\ -f2 +} + +# Debian dpkg(8) completion +# +_dpkg() +{ + local cur prev i + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + i=$COMP_CWORD + + _expand || return 0 + + # find the last option flag + if [[ $cur != -* ]]; then + while [[ $prev != -* && $i != 1 ]]; do + i=$((i-1)) + prev=${COMP_WORDS[i-1]} + done + fi + + case "$prev" in + -@(c|i|A|I|f|e|x|X|-@(install|unpack|record-avail|contents|info|fsys-tarfile|field|control|extract))) + _filedir '?(u)deb' + return 0 + ;; + -@(b|-build)) + _filedir -d + return 0 + ;; + -@(s|p|l|-@(status|print-avail|list))) + COMPREPLY=( $( apt-cache pkgnames $cur 2>/dev/null ) ) + return 0 + ;; + -@(S|-search)) + _filedir + return 0 + ;; + -@(r|L|P|-@(remove|purge|listfiles))) + COMPREPLY=( $( _comp_dpkg_installed_packages $cur ) ) + return 0 + ;; + *) + + COMPREPLY=( $( compgen -W '-i --install --unpack -A --record-avail \ + --configure -r --remove -P --purge --get-selections \ + --set-selections --update-avail --merge-avail \ + --clear-avail --command-fd --forget-old-unavail -s \ + --status -p --print-avail -L --listfiles -l --list \ + -S --search -C --audit --print-architecture \ + --print-gnu-build-architecture \ + --print-installation-architecture \ + --compare-versions --help --version --force-help \ + --force-all --force-auto-select --force-downgrade \ + --force-configure-any --force-hold --force-bad-path \ + --force-not-root --force-overwrite \ + --force-overwrite-diverted --force-bad-verify \ + --force-depends-version --force-depends \ + --force-confnew --force-confold --force-confdef \ + --force-confmiss --force-conflicts --force-architecture\ + --force-overwrite-dir --force-remove-reinstreq \ + --force-remove-essential -Dh \ + --debug=help --licence --admindir= --root= --instdir= \ + -O --selected-only -E --skip-same-version \ + -G --refuse-downgrade -B --auto-deconfigure \ + --no-debsig --no-act -D --debug= --status-fd \ + -b --build -I --info -f --field -c --contents \ + -x --extract -X --vextract --fsys-tarfile -e --control \ + --ignore-depends= --abort-after' -- $cur ) ) + ;; + esac + + +} +complete -F _dpkg $filenames dpkg dpkg-deb +} + +# Debian GNU dpkg-reconfigure(8) completion +# +have dpkg-reconfigure && +_dpkg_reconfigure() +{ + local cur prev opt + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + + case "$prev" in + -@(f|-frontend)) + opt=( $( echo /usr/share/perl5/Debconf/FrontEnd/* ) ) + opt=( ${opt[@]##*/} ) + opt=( ${opt[@]%.pm} ) + COMPREPLY=( $( compgen -W '${opt[@]}' -- $cur ) ) + return 0 + ;; + -@(p|-priority)) + COMPREPLY=( $( compgen -W 'low medium high critical' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-f --frontend -p --priority -a --all \ + -u --unseen-only -h --help -s --showold \ + --force --terse' -- $cur ) ) + else + COMPREPLY=( $( _comp_dpkg_installed_packages $cur ) ) + fi +} && +complete -F _dpkg_reconfigure $default dpkg-reconfigure + +# Debian dpkg-source completion +# +have dpkg-source && +_dpkg_source() +{ + local cur prev options work i action packopts unpackopts + + packopts="-c -l -F -V -T -D -U -W -E -sa -i -I -sk -sp -su -sr -ss -sn -sA -sK -sP -sU -sR" + unpackopts="-sp -sn -su" + options=`echo "-x -b $packopts $unpackopts" | xargs echo | sort -u | xargs echo` + + COMPREPLY=() + if [ "$1" != "dpkg-source" ]; then + exit 1 + fi + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + action="options" + for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do + if [[ ${COMP_WORDS[$i]} == "-x" ]]; then + action=unpack + elif [[ ${COMP_WORDS[$i]} == "-b" ]]; then + action=pack + elif [[ ${COMP_WORDS[$i]} == "-h" ]]; then + action=help + fi + done + # if currently seeing a complete option, return just itself. + for i in $options; do + if [ "$cur" = "$i" ]; then + COMPREPLY=( "$cur" ) + return 0 + fi + done + case "$action" in + "unpack") + if [ "$cur" = "-" -o "$cur" = "-s" ]; then + COMPREPLY=( $unpackots ) + return 0 + fi + case "$prev" in + "-x") + COMPREPLY=( $( compgen -d -- "$cur" ) \ + $( compgen -f -X '!*.dsc' -- "$cur" ) ) + return 0 + ;; + *) + COMPREPLY=( $unpackopts $(compgen -d -f -- "$cur" ) ) + return 0 + ;; + esac + return 0 + ;; + "pack") + if [ "$cur" = "-" ]; then + COMPREPLY=( $packopts ) + return 0 + fi + if [ "$cur" = "-s" ]; then + COMPREPLY=( "-sa" "-sk" "-sp" "-su" "-sr" "-ss" "-sn" \ + "-sA" "-sK" "-sP" "-sU" "-sR" ) + return 0 + fi + case "$prev" in + "-b") + COMPREPLY=( $( compgen -d -- "$cur" ) ) + return 0 + ;; + "-c"|"-l"|"-T"|"-i"|"-I") + # -c: get controlfile + # -l: get per-version info from this file + # -T: read variables here, not debian/substvars + # -i: <regexp> filter out files to ignore diffs of. + # -I: filter out files when building tarballs. + # return directory names and file names + COMPREPLY=( $( compgen -d -f ) ) + return 0 + ;; + "-F") + # -F: force change log format + COMPREPLY=( $( ( cd /usr/lib/dpkg/parsechangelog; compgen -f "$cur" ) ) ) + return 0 + ;; + "-V"|"-D") + # -V: set a substitution variable + # we don't know anything about possible variables or values + # so we don't try to suggest any completion. + COMPREPLY=() + return 0 + ;; + "-D") + # -D: override or add a .dsc field and value + # if $cur doesn't contain a = yet, suggest variable names + if echo -- "$cur" | grep -q "="; then + # $cur contains a "=" + COMPREPLY=() + return 0 + else + COMPREPLY=( Format Source Version Binary Maintainer Uploader Architecture Standards-Version Build-Depends Files ) + return 0 + fi + ;; + "-U") + # -U: remove a field + # Suggest possible fieldnames + COMPREPLY=( Format Source Version Binary Maintainer Uploader Architecture Standards-Version Build-Depends Files ) + return 0 + ;; + *) + COMPREPLY=( $packopts ) + return 0 + ;; + esac + return 0 + ;; + *) + # if seeing a partial option, return possible completions. + if [ "$cur" = "-s" ]; then + COMPREPLY=( "-sa" "-sk" "-sp" "-su" "-sr" "-ss" "-sn" \ + "-sA" "-sK" "-sP" "-sU" "-sR" ) + return 0 + fi + # else return all possible options. + COMPREPLY=( $options ) + return 0 + ;; + esac +} && +complete -F _dpkg_source dpkg-source + +# Debian Linux dselect(8) completion. +# +have dselect && +_dselect() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + --admindir) + _filedir -d + return 0 + ;; + + -@(D|debug)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--admindir --help --version --licence \ + --license --expert --debug' -- $cur ) ) + else + COMPREPLY=( $( compgen -W 'access update select install config \ + remove quit' -- $cur ) ) + fi + + + return 0 +} && +complete -F _dselect $filenames dselect + +# Java completion +# + +# available path elements completion +have java && { +_java_path() +{ + cur=${cur##*:} + _filedir '@(jar|zip)' +} + +# exact classpath determination +_java_find_classpath() +{ + local i + + # search first in current options + for (( i=1; i < COMP_CWORD; i++ )); do + if [[ "${COMP_WORDS[i]}" == -@(cp|classpath) ]]; then + classpath=${COMP_WORDS[i+1]} + break + fi + done + + # default to environment + [ -z "$classpath" ] && classpath=$CLASSPATH + + # default to current directory + [ -z "$classpath" ] && classpath=. +} + +# exact sourcepath determination +_java_find_sourcepath() +{ + local i + + # search first in current options + for (( i=1; i < COMP_CWORD; i++ )); do + if [[ "${COMP_WORDS[i]}" == -sourcepath ]]; then + sourcepath=${COMP_WORDS[i+1]} + break + fi + done + + # default to classpath + [ -z "$sourcepath" ] && _java_find_classpath + sourcepath=$classpath +} + +# available classes completion +_java_classes() +{ + local classpath i + + # find which classpath to use + _java_find_classpath + + # convert package syntax to path syntax + cur=${cur//.//} + # parse each classpath element for classes + for i in ${classpath//:/ }; do + if [ -r $i ] && [[ "$i" == *.@(jar|zip) ]]; then + if type zipinfo &> /dev/null; then + COMPREPLY=( "${COMPREPLY[@]}" $( zipinfo -1 \ + "$i" | grep "^$cur" | grep '\.class$' | \ + grep -v "\\$" ) ) + else + COMPREPLY=( "${COMPREPLY[@]}" $( jar tf "$i" \ + "$cur" | grep "\.class$" | grep -v "\\$" ) ) + fi + + elif [ -d $i ]; then + i=${i%/} + COMPREPLY=( "${COMPREPLY[@]}" $( find "$i" -type f \ + -path "$i/$cur*.class" 2>/dev/null | \ + grep -v "\\$" | sed -e "s|^$i/||" ) ) + fi + done + + # remove class extension + COMPREPLY=( ${COMPREPLY[@]%.class} ) + # convert path syntax to package syntax + COMPREPLY=( ${COMPREPLY[@]//\//.} ) +} + +# available packages completion +_java_packages() +{ + local sourcepath i + + # find wich sourcepath to use + _java_find_sourcepath + + # convert package syntax to path syntax + cur=${cur//.//} + # parse each sourcepath element for packages + for i in ${sourcepath//:/ }; do + if [ -d $i ]; then + COMPREPLY=( "${COMPREPLY[@]}" $( command ls -F -d \ + $i/$cur* 2>/dev/null | sed -e 's|^'$i'/||' ) ) + fi + done + # keep only packages + COMPREPLY=( $( echo "${COMPREPLY[@]}" | tr " " "\n" | grep "/$" ) ) + # remove packages extension + COMPREPLY=( ${COMPREPLY[@]%/} ) + # convert path syntax to package syntax + cur=${COMPREPLY[@]//\//.} +} + +# java completion +# +_java() +{ + local cur prev i + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + for ((i=1; i < $COMP_CWORD; i++)); do + case ${COMP_WORDS[$i]} in + -cp|-classpath) + ((i++)) # skip the classpath string. + ;; + -*) + # this is an option, not a class/jarfile name. + ;; + *) + # once we've seen a class, just do filename completion + _filedir + return 0 + ;; + esac + done + + case $prev in + -@(cp|classpath)) + _java_path + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + # relevant options completion + COMPREPLY=( $( compgen -W '-client -hotspot -server -classic \ + -cp -classpath -D -verbose -verbose:class \ + -verbose:gc -version:jni -version \ + -showversion -? -help -X -jar \ + -ea -enableassertions -da -disableassertions \ + -esa -enablesystemassertions \ + -dsa -disablesystemassertions ' -- $cur ) ) + else + if [[ "$prev" == -jar ]]; then + # jar file completion + _filedir jar + else + # classes completion + _java_classes + fi + fi +} +complete -F _java $filenames java +} + +# javadoc completion +# +have javadoc && +_javadoc() +{ + COMPREPLY=() + local cur prev + + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case $prev in + -@(overview|helpfile|stylesheetfile)) + _filedir + return 0 + ;; + -d) + _filedir -d + return 0 + ;; + -@(classpath|bootclasspath|docletpath|sourcepath|extdirs)) + _java_path + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + # relevant options completion + COMPREPLY=( $( compgen -W '-overview -public -protected \ + -package -private -help -doclet -docletpath \ + -sourcepath -classpath -exclude -subpackages \ + -breakiterator -bootclasspath -source -extdirs \ + -verbose -locale -encoding -J -d -use -version \ + -author -docfilessubdirs -splitindex \ + -windowtitle -doctitle -header -footer -bottom \ + -link -linkoffline -excludedocfilessubdir \ + -group -nocomment -nodeprecated -noqualifier \ + -nosince -nodeprecatedlist -notree -noindex \ + -nohelp -nonavbar -quiet -serialwarn -tag \ + -taglet -tagletpath -charset -helpfile \ + -linksource -stylesheetfile -docencoding' -- \ + $cur ) ) + else + # source files completion + _filedir java + # packages completion + _java_packages + fi +} && +complete -F _javadoc $filenames javadoc + +# javac completion +# +have javac && +_javac() +{ + COMPREPLY=() + local cur prev + + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case $prev in + -d) + _filedir -d + return 0 + ;; + -@(classpath|bootclasspath|sourcepath|extdirs)) + _java_path + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + # relevant options completion + COMPREPLY=( $( compgen -W '-g -g:none -g:lines -g:vars\ + -g:source -O -nowarn -verbose -deprecation -classpath\ + -sourcepath -bootclasspath -extdirs -d -encoding -source\ + -target -help' -- $cur ) ) + else + # source files completion + _filedir java + fi +} && +complete -F _javac $filenames javac + +# PINE address-book completion +# +have pine && +_pineaddr() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + COMPREPLY=( $( compgen -W '$( awk "{print \$1}" ~/.addressbook 2>/dev/null)' \ + -- $cur ) ) +} && +complete -F _pineaddr $default pine + +# mutt completion +# +# Mutt doesn't have an "addressbook" like Pine, but it has aliases and +# a "query" function to retrieve addresses, so that's what we use here. +have mutt || have muttng && { +_muttaddr() +{ + _muttaliases + _muttquery + return 0 +} + +_muttconffiles() +{ + local file sofar + local -a newconffiles + + sofar=" $1 " + shift + while [[ "$1" ]]; do + newconffiles=( $(sed -rn 's|^source[[:space:]]+([^[:space:]]+).*$|\1|p' $(eval echo $1) ) ) + for file in "${newconffiles[@]}"; do + [[ ! "$file" ]] || [[ "${sofar/ ${file} / }" != "$sofar" ]] && + continue + sofar="$sofar $file" + sofar=" $(eval _muttconffiles \"$sofar\" $file) " + done + shift + done + echo $sofar +} + +_muttaliases() +{ + local cur muttrc + local -a conffiles aliases + cur=`_get_cword` + + [ -f ~/.${muttcmd}/${muttcmd}rc ] && muttrc="~/.${muttcmd}/${muttcmd}rc" + [ -f ~/.${muttcmd}rc ] && muttrc="~/.${muttcmd}rc" + [ -z "$muttrc" ] && return 0 + + conffiles=( $(eval _muttconffiles $muttrc $muttrc) ) + aliases=( $( sed -rn 's|^alias[[:space:]]+([^[:space:]]+).*$|\1|p' \ + $(eval echo "${conffiles[@]}") ) ) + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "${aliases[*]}" -- $cur ) ) + + return 0 +} + +_muttquery() +{ + local cur querycmd + local -a queryresults + cur=`_get_cword` + + querycmd="$( $muttcmd -Q query_command | sed -r 's|^query_command=\"(.*)\"$|\1|; s|%s|'$cur'|' )" + if [ -z "$cur" -o -z "$querycmd" ]; then + queryresults=() + else + queryresults=( $( $querycmd | \ + sed -nr '2,$s|^([^[:space:]]+).*|\1|p' ) ) + fi + + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "${queryresults[*]}" \ + -- $cur ) ) + + return 0 +} + +_muttfiledir() +{ + local cur folder spoolfile + cur=`_get_cword` + + # This is currently not working so well. Perhaps this function should + # just call _filedir() for the moment. + if [[ $cur == [=+]* ]]; then + folder="$( $muttcmd -Q folder | sed -r 's|^folder=\"(.*)\"$|\1|' )" + : folder:=~/Mail + + # Match any file in $folder beginning with $cur + # (minus the leading '=' sign). + COMPREPLY=( $( compgen -f -- "$folder/${cur:1}" ) ) + COMPREPLY=( ${COMPREPLY[@]#$folder/} ) + return 0 + elif [ "$cur" == !* ]; then + spoolfile="$( $muttcmd -Q spoolfile | sed -r 's|^spoolfile=\"(.*)\"$|\1|' )" + [ ! -z "$spoolfile" ] && eval cur="${cur/^!/$spoolfile}"; + fi + _filedir + + return 0 +} + +_mutt() +{ + local cur prev + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + COMPREPLY=() + + [ ${COMP_WORDS[0]} == muttng ] && muttcmd="muttng" || muttcmd="mutt" + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W '-A -a -b -c -e -f -F -H -i -m -n \ + -p -Q -R -s -v -x -y -z -Z -h' \ + -- $cur ) ) + return 0 + ;; + *) + case "$prev" in + -@(a|f|F|H|i)) + _muttfiledir + return 0 + ;; + -A) + _muttaliases + return 0 + ;; + -@(e|m|Q|s|h|p|R|v|y|z|Z)) + return 0 + ;; + *) + _muttaddr + return 0 + ;; + esac + ;; + esac + +} +complete -F _mutt $default $filenames mutt muttng +} + +_configure_func() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + # if $COMP_CONFIGURE_HINTS is not null, then completions of the form + # --option=SETTING will include 'SETTING' as a contextual hint + [[ "$cur" != -* ]] && return 0 + + if [ -n "$COMP_CONFIGURE_HINTS" ]; then + COMPREPLY=( $( $1 --help | awk '/^ --[A-Za-z]/ { print $1; if ($2 ~ /--[A-Za-z]/) print $2 }' | sed -e 's/[[,].*//g' | grep ^$cur ) ) + + else + COMPREPLY=( $( $1 --help | awk '/^ --[A-Za-z]/ { print $1; if ($2 ~ /--[A-Za-z]/) print $2 }' | sed -e 's/[[,=].*//g' | grep ^$cur ) ) + fi +} +complete -F _configure_func $default configure + +# Debian reportbug(1) completion +# +have reportbug && +_reportbug() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -f|--filename|-i|--include|--mta|-o|--output) + _filedir + return 0 + ;; + -B|--bts) + COMPREPLY=( $( compgen -W "debian guug kde mandrake help" -- \ + $cur )) + return 0 + ;; + -e|--editor|--mua) + COMP_WORDS=(COMP_WORDS[0] $cur) + COMP_CWORD=1 + _command + return 0 + ;; + --mode) + COMPREPLY=( $( compgen -W "novice standard expert" -- $cur ) ) + return 0 + ;; + -S|--severity) + COMPREPLY=( $( compgen -W "grave serious important normal \ + minor wishlist" -- $cur ) ) + return 0 + ;; + -u|--ui|--interface) + COMPREPLY=( $( compgen -W "newt text gnome" -- $cur ) ) + return 0 + ;; + -t|--type) + COMPREPLY=( $( compgen -W "gnats debbugs" -- $cur ) ) + return 0 + ;; + -T|--tags) + COMPREPLY=( $( compgen -W "none patch security upstream sid \ + woody potato sarge fixed" -- $cur )) + return 0 + ;; + *) + ;; + esac + + COMPREPLY=($( compgen -W '-h --help -v --version -a --af -b \ + --no-query-bts --query-bts -B --bts -c --configure \ + --no-config-files --check-available -d --debug \ + --no-check-available -e --editor --email -f \ + --filename -g --gnupg -H --header -i --include -j \ + --justification -l --ldap --no-ldap -L --list-cc -m \ + --maintonly --mode --mua --mta --mutt -n --mh --nmh \ + -o --output -p --print -P --pgp --proxy --http_proxy\ + -q --quiet -Q --query-only --realname --report-quiet \ + --reply-to --replyto -s --subject -S --severity \ + --smtphost -t --type -T --tags --template -V -x \ + --no-cc --package-version -z --no-compress \ + --ui --interface -u \ + wnpp boot-floppies kernel-image' -- $cur ) \ + $( apt-cache pkgnames -- $cur 2> /dev/null ) ) + _filedir + return 0 +} && +complete -F _reportbug $filenames reportbug + +# Debian querybts(1) completion +# +have querybts && +_querybts() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -B|--bts) + COMPREPLY=( $( compgen -W "debian guug kde mandrake help" -- \ + $cur )) + return 0 + ;; + -u|--ui|--interface) + COMPREPLY=($( compgen -W "newt text gnome" -- $cur )) + return 0 + ;; + *) + ;; + esac + + COMPREPLY=($( compgen -W '-h --help -v --version -A --archive \ + -B --bts -l --ldap --no-ldap --proxy= --http_proxy= \ + -s --source -w --web -u --ui --interface \ + wnpp boot-floppies' -- $cur ) \ + $( apt-cache pkgnames -- $cur 2> /dev/null ) ) +} && +complete -F _querybts $filenames querybts + +# update-alternatives completion +# +have update-alternatives && { +installed_alternatives() +{ + local admindir + # find the admin dir + for i in alternatives dpkg/alternatives rpm/alternatives; do + [ -d /var/lib/$i ] && admindir=/var/lib/$i && break + done + for (( i=1; i < COMP_CWORD; i++ )); do + if [[ "${COMP_WORDS[i]}" == --admindir ]]; then + admindir=${COMP_WORDS[i+1]} + break + fi + done + COMPREPLY=( $( command ls $admindir | grep "^$cur" ) ) +} + +_update_alternatives() +{ + local cur prev mode args i + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + --@(altdir|admindir)) + _filedir -d + return 0 + ;; + --@(help|version)) + return 0 + ;; + esac + + # find wich mode to use and how many real args used so far + for (( i=1; i < COMP_CWORD; i++ )); do + if [[ "${COMP_WORDS[i]}" == --@(install|remove|auto|display|config|remove-all) ]]; then + mode=${COMP_WORDS[i]} + args=$(($COMP_CWORD - i)) + break + fi + done + + case $mode in + --install) + case $args in + 1) + _filedir + ;; + 2) + installed_alternatives + ;; + 3) + _filedir + ;; + esac + ;; + --remove) + case $args in + 1) + installed_alternatives + ;; + 2) + _filedir + ;; + esac + ;; + --auto) + installed_alternatives + ;; + --remove-all) + installed_alternatives + ;; + --display) + installed_alternatives + ;; + --config) + installed_alternatives + ;; + *) + COMPREPLY=( $( compgen -W '--verbose --quiet --help --version \ + --altdir --admindir' -- $cur ) \ + $( compgen -W '--install --remove --auto --display \ + --config' -- $cur ) ) + esac +} +complete -F _update_alternatives update-alternatives +} + +# Python completion +# +have python && +_python() +{ + local prev cur + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]##*/} + + case "$prev" in + -Q) + COMPREPLY=( $( compgen -W "old new warn warnall" -- $cur ) ) + return 0 + ;; + -W) + COMPREPLY=( $( compgen -W "ignore default all module once error" -- $cur ) ) + return 0 + ;; + -c) + _filedir '@(py|pyc|pyo)' + return 0 + ;; + !(python|-?)) + [[ ${COMP_WORDS[COMP_CWORD-2]} != -@(Q|W) ]] && _filedir + ;; + esac + + + # if '-c' is already given, complete all kind of files. + for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do + if [[ ${COMP_WORDS[i]} == -c ]]; then + _filedir + fi + done + + + if [[ "$cur" != -* ]]; then + _filedir '@(py|pyc|pyo)' + else + COMPREPLY=( $( compgen -W "- -d -E -h -i -O -Q -S -t -u \ + -U -v -V -W -x -c" -- $cur ) ) + fi + + + + return 0 +} && +complete -F _python $filenames python + +# Perl completion +# +have perl && +{ +_perlmodules() +{ + COMPREPLY=( $( compgen -P "$prefix" -W "$( perl -e 'sub mods { my ($base,$dir)=@_; return if $base !~ /^\Q$ENV{cur}/; chdir($dir) or return; for (glob(q[*.pm])) {s/\.pm$//; print qq[$base$_\n]}; mods(/^(?:[.\d]+|$Config{archname}-$Config{osname}|auto)$/ ? undef : qq[${base}${_}\\\\:\\\\:],qq[$dir/$_]) for grep {-d} glob(q[*]); } mods(undef,$_) for @INC;' )" -- $cur ) ) +} + +_perl() +{ + local cur prev prefix temp + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + prefix="" + + # completing an option (may or may not be separated by a space) + if [[ "$cur" == -?* ]]; then + temp=$cur + prev=${temp:0:2} + cur=${temp:2} + prefix=$prev + fi + + # only handle module completion for now + case "$prev" in + -I|-x) + COMPREPLY=( $( compgen -d -P "$prev" -- "$cur" ) ) + return 0 + ;; + -m|-M) + _perlmodules + return 0 + ;; + esac + + # handle case where first parameter is not a dash option + if [[ "${COMP_WORDS[COMP_CWORD]}" != -* ]]; then + _filedir + return 0 + fi + + # complete using basic options + COMPREPLY=( $( compgen -W '-C -s -T -u -U -W -X -h -v -V -c -w -d -D -p \ + -n -a -F -l -0 -I -m -M -P -S -x -i -e ' -- $cur ) ) + return 0 +} +complete -F _perl $filenames perl + +_perldoc() +{ + local cur prev prefix temp + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + prefix="" + + # completing an option (may or may not be separated by a space) + if [[ "$cur" == -?* ]]; then + temp=$cur + prev=${temp:0:2} + cur=${temp:2} + prefix=$prev + fi + + # complete builtin perl functions + case $prev in + -f) + COMPREPLY=( $( compgen -W 'chomp chop chr crypt hex index lc \ + lcfirst length oct ord pack q qq reverse rindex sprintf \ + substr tr uc ucfirst y m pos quotemeta s split study qr abs \ + atan2 cos exp hex int log oct rand sin sqrt srand pop push \ + shift splice unshift grep join map qw reverse sort unpack \ + delete each exists keys values binmode close closedir \ + dbmclose dbmopen die eof fileno flock format getc print \ + printf read readdir rewinddir seek seekdir select syscall \ + sysread sysseek syswrite tell telldir truncate warn write \ + pack read syscall sysread syswrite unpack vec -X chdir chmod \ + chown chroot fcntl glob ioctl link lstat mkdir open opendir \ + readlink rename rmdir stat symlink umask unlink utime caller \ + continue do dump eval exit goto last next redo return \ + sub wantarray caller import local my our package use defined \ + formline reset scalar undef \ + alarm exec fork getpgrp getppid getpriority kill pipe qx \ + setpgrp setpriority sleep system times wait waitpid \ + import no package require use bless dbmclose dbmopen package \ + ref tie tied untie use accept bind connect getpeername \ + getsockname getsockopt listen recv send setsockopt shutdown \ + socket socketpair msgctl msgget msgrcv msgsnd semctl semget \ + semop shmctl shmget shmread shmwrite endgrent endhostent \ + endnetent endpwent getgrent getgrgid getgrnam getlogin \ + getpwent getpwnam getpwuid setgrent setpwent endprotoent \ + endservent gethostbyaddr gethostbyname gethostent \ + getnetbyaddr getnetbyname getnetent getprotobyname \ + getprotobynumber getprotoent getservbyname getservbyport \ + getservent sethostent setnetent setprotoent setservent \ + gmtime localtime time times' -- $cur ) ) + return 0 + ;; + esac + + case $cur in + -*) + COMPREPLY=( $( compgen -W '-h -v -t -u -m -l -F -X -f -q' -- $cur )) + return 0 + ;; + */*) + return 0 + ;; + *) + _perlmodules + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W '$( PAGER=cat man perl 2>/dev/null | sed -ne "/perl.*Perl overview/,/perlwin32/s/^[^a-z0-9]*\([a-z0-9]*\).*$/\1/p")' -- $cur ) ) + + return 0 + ;; + esac +} +complete -F _perldoc $default perldoc +} + +# rcs(1) completion +# +have rcs && +_rcs() +{ + local cur prev file dir i + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + file=${cur##*/} + dir=${cur%/*} + + # deal with relative directory + [ "$file" = "$dir" ] && dir=. + + COMPREPLY=( $( compgen -f "$dir/RCS/$file" ) ) + + for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do + file=${COMPREPLY[$i]##*/} + dir=${COMPREPLY[$i]%RCS/*} + COMPREPLY[$i]=$dir$file + done + + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -G "$dir/$file*,v" ) ) + + for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do + COMPREPLY[$i]=${COMPREPLY[$i]%,v} + done + + # default to files if nothing returned and we're checking in. + # otherwise, default to directories + [ ${#COMPREPLY[@]} -eq 0 -a $1 = ci ] && _filedir || _filedir -d +} && +complete -F _rcs $filenames ci co rlog rcs rcsdiff + +# lilo(8) completion +# +have lilo && { +_lilo_labels() +{ + COMPREPLY=( $( awk -F'=' '/label/ {print $2}' \ + /etc/lilo.conf | sed -e 's/"//g' | grep "^$cur" ) ) +} + +_lilo() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case $prev in + -@(C|i|m|s|S)) + _filedir + return 0 + ;; + -r) + _filedir -d + return 0 + ;; + -@(I|D|R)) + # label completion + _lilo_labels + return 0 + ;; + -@(A|b|M|u|U)) + # device completion + cur=${cur:=/dev/} + _filedir + return 0 + ;; + -T) + # topic completion + COMPREPLY=( $( compgen -W 'help ChRul EBDA geom geom= \ + table= video' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + # relevant options completion + COMPREPLY=( $( compgen -W '-A -b -c -C -d -f -g -i -I -l -L -m \ + -M -p -P -q -r -R -s -S -t -T -u -U -v -V -w -x -z' -- \ + $cur ) ) + fi +} +complete -F _lilo lilo +} + +# links completion +# +have links && +_links() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + case "$cur" in + --*) + COMPREPLY=( $( compgen -W '--help' -- $cur ) ) + ;; + -*) + COMPREPLY=( $( compgen -W '-async-dns -max-connections \ + -max-connections-to-host -retries \ + -receive-timeout -unrestartable-receive-timeout\ + -format-cache-size -memory-cache-size \ + -http-proxy -ftp-proxy -download-dir \ + -assume-codepage -anonymous -dump -no-connect \ + -source -version -help' -- $cur ) ) + ;; + *) + if [ -r ~/.links/links.his ]; then + COMPREPLY=( $( compgen -W '$( < ~/.links/links.his )' \ + -- $cur ) ) + fi + _filedir '@(htm|html)' + return 0 + ;; + esac + + return 0 +} && +complete -F _links $filenames links + +[ $UNAME = FreeBSD ] && { +# FreeBSD package management tool completion +# +_pkg_delete() +{ + local cur pkgdir prev + + pkgdir=${PKG_DBDIR:-/var/db/pkg}/ + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + [ "$prev" = "-o" -o "$prev" = "-p" -o "$prev" = "-W" ] && return 0 + + COMPREPLY=( $( compgen -d $pkgdir$cur ) ) + COMPREPLY=( ${COMPREPLY[@]#$pkgdir} ) + + return 0 +} +complete -F _pkg_delete $dirnames pkg_delete pkg_info +have pkg_deinstall && complete -F _pkg_delete $dirnames pkg_deinstall + +# FreeBSD kernel module commands +# +_kldload() +{ + local cur moddir + + moddir=/modules/ + [ -d $moddir ] || moddir=/boot/kernel/ + cur=`_get_cword` + + COMPREPLY=( $( compgen -f $moddir$cur ) ) + COMPREPLY=( ${COMPREPLY[@]#$moddir} ) + COMPREPLY=( ${COMPREPLY[@]%.ko} ) + + return 0 +} +complete -F _kldload $filenames kldload + +_kldunload() +{ + local cur + cur=`_get_cword` + COMPREPLY=( $(kldstat | sed -ne "s/^.*[ \t]\+\($cur[a-z_]\+\).ko$/\1/p") ) +} +complete -F _kldunload $filenames kldunload +} + +# FreeBSD portupgrade completion +# +have portupgrade && +_portupgrade() +{ + local cur pkgdir prev + + pkgdir=${PKG_DBDIR:-/var/db/pkg}/ + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + [ "$prev" = "-l" -o "$prev" = "-L" -o "$prev" = "-o" ] && return 0 + + COMPREPLY=( $( compgen -d $pkgdir$cur ) ) + COMPREPLY=( ${COMPREPLY[@]#$pkgdir} ) + COMPREPLY=( ${COMPREPLY[@]%-*} ) + + return 0 +} && +complete -F _portupgrade $dirnames portupgrade + +# FreeBSD portinstall completion +# +have portinstall && +_portinstall() +{ + local cur portsdir prev indexfile + local -a COMPREPLY2 + + portsdir=${PORTSDIR:-/usr/ports}/ + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + # First try INDEX-5 + indexfile=$portsdir/INDEX-5 + # Then INDEX if INDEX-5 does not exist or system is not FreeBSD 5.x + [ "${OSTYPE%.*}" = "freebsd5" -a -f $indexfile ] || + indexfile=$portsdir/INDEX + + [ "$prev" = "-l" -o "$prev" = "-L" -o "$prev" = "-o" ] && return 0 + + COMPREPLY=( $( egrep "^$cur" < $indexfile | cut -d'|' -f1 ) ) + COMPREPLY2=( $( egrep "^[^\|]+\|$portsdir$cur" < $indexfile | \ + cut -d'|' -f2 ) ) + COMPREPLY2=( ${COMPREPLY2[@]#$portsdir} ) + COMPREPLY=( "${COMPREPLY[@]}" "${COMPREPLY2[@]}" ) + + return 0 +} && +complete -F _portinstall $dirnames portinstall + +# Slackware Linux removepkg completion +# +have removepkg && [ -f /etc/slackware-version ] && +_removepkg() +{ + local packages cur + + COMPREPLY=() + cur=`_get_cword` + + COMPREPLY=( $( (cd /var/log/packages; compgen -f -- "$cur") ) ) +} && +complete -F _removepkg $filenames removepkg && + complete $dirnames -f -X '!*.tgz' installpkg upgradepkg explodepkg + +# look(1) completion +# +have look && +_look() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD = 1 ]; then + COMPREPLY=( $( compgen -W '$(look $cur)' ) ) + fi +} && +complete -F _look $default look + +# ypcat(1) and ypmatch(1) completion +# +have ypmatch && +_ypmatch() +{ + local cur map + + COMPREPLY=() + cur=`_get_cword` + + [ $1 = ypcat ] && [ $COMP_CWORD -gt 1 ] && return 0 + [ $1 = ypmatch ] && [ $COMP_CWORD -gt 2 ] && return 0 + + if [ $1 = ypmatch ] && [ $COMP_CWORD -eq 1 ] && \ + [ ${#COMP_WORDS[@]} -eq 3 ]; then + map=${COMP_WORDS[2]} + COMPREPLY=( $( compgen -W '$( ypcat $map | \ + cut -d':' -f 1 )' -- $cur) ) + else + [ $1 = ypmatch ] && [ $COMP_CWORD -ne 2 ] && return 0 + COMPREPLY=( $( compgen -W \ + '$( echo $(ypcat -x | cut -d"\"" -f 2))' -- $cur)) + fi + + return 0 +} && +complete -F _ypmatch ypmatch ypcat + +# mplayer(1) completion +# +have mplayer && { +_mplayer_options_list() +{ + cur=${cur%\\} + COMPREPLY=( $( $1 $2 help 2> /dev/null | \ + sed -e '1,/^Available/d' | awk '{print $1}' | \ + sed -e 's/:$//' -e 's/^'${2#-}'$//' -e 's/<.*//' | \ + grep "^$cur" ) ) +} + +_mplayer() +{ + local cmd cur prev skinsdir IFS=$' \t\n' i j k=0 + + COMPREPLY=() + cmd=${COMP_WORDS[0]} + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(ac|afm|vc|vfm|ao|vo|vop|fstype)) + _mplayer_options_list mplayer $prev + return 0 + ;; + -@(oac|ovc|of)) + _mplayer_options_list mencoder $prev + return 0 + ;; + -audiofile) + _filedir '@(mp3|MP3|mpg|MPG|ogg|OGG|wav|WAV|mid|MID|flac|FLAC|mka|MKA)' + return 0 + ;; + -font) + _filedir '@(desc|ttf)' + return 0 + ;; + -sub) + _filedir '@(srt|SRT|sub|SUB|txt|TXT|utf|UTF|rar|RAR|mpsub|smi|js|ssa|SSA|aas|AAS)' + return 0 + ;; + -vobsub) + _filedir '@(idx|IDX|ifo|IFO|sub|SUB)' + IFS=$'\t\n' + COMPREPLY=( $( for i in "${COMPREPLY[@]}"; do + if [ -f $i -a -r $i ]; then + echo ${i%.*} + else + echo $i + fi + done ) ) + IFS=$' \t\n' + return 0 + ;; + -ifo) + _filedir '@(ifo|IFO)' + return 0 + ;; + -cuefile) + _filedir '@(bin|BIN|cue|CUE)' + return 0 + ;; + -skin) + # if you don't have installed mplayer in /usr you + # may want to set the MPLAYER_SKINS_DIR global variable + if [ -n "$MPLAYER_SKINS_DIR" ]; then + skinsdir=$MPLAYER_SKINS_DIR + else + skinsdir=/usr/share/mplayer/Skin + fi + + IFS=$'\t\n' + for i in ~/.mplayer/Skin $skinsdir; do + if [ -d $i -a -r $i ]; then + for j in $( compgen -d $i/$cur ); do + COMPREPLY[$k]=${j#$i/} + k=$((++k)) + done + fi + done + IFS=$' \t\n' + return 0 + ;; + -@(mixer|@(cdrom|dvd)-device|dvdauth|fb|zrdev)) + cur=${cur:=/dev/} + _filedir + return 0 + ;; + -@(edl?(out)|lircconf|menu-cfg|playlist|csslib|dumpfile)| \ + -@(subfile|vobsub|aofile|fbmodeconfig|include|o|dvdkey)| \ + -passlogfile) + _filedir + return 0 + ;; + -@(auto@(q|sync)|loop|menu-root|speed|sstep|aid|alang)| \ + -@(?(@(audio|sub)-)demuxer|bandwidth|cache|chapter)| \ + -@(dvd?(angle)|fps|frames|mc|passwd|user|sb|srate|ss|vcd)| \ + -@(vi?(d|vo)|ffactor|sid|slang|spu@(align|aa|gauss))| \ + -@(vobsubid|delay|bpp|brightness|contrast|dfbopts|display)| \ + -@(fbmode|geometry|guiwid|hue|icelayer|screen[wh]|wid)| \ + -@(monitor@(aspect|-@(dotclock|[hv]freq))|panscan|saturation)| \ + -@(xineramascreen|zr@(crop|norm|quality|[xy]doff|[vh]dec))| \ + -@(aspect|pp|x|y|xy|z|stereo|audio-@(density|delay|preload))| \ + -@(endpos|osdlevel|ffourcc|sws|channels|skiplimit|format)| \ + -@(ofps|aa@(driver|@(osd|sub)color)|vobsubout?(i@(ndex|d)))| \ + -sub@(-bg-@(alpha|color)|cp|delay|fps|pos|align|width)| \ + -sub@(font-@(blur|outline|autoscale|encoding|@(osd|text)-scale))) + return 0 + ;; + -lavdopts) + COMPREPLY=( $( compgen -W 'ec er= bug= idct= gray' \ + -- $cur ) ) + return 0 + ;; + -lavcopts) + COMPREPLY=( $( compgen -W 'vcodec= vqmin= vqscale= \ + vqmax= mbqmin= mbqmax= vqdiff= \ + vmax_b_frames= vme= vhq v4mv \ + keyint= vb_strategy= vpass= \ + aspect= vbitrate= vratetol= \ + vrc_maxrate= vrc_minrate= \ + vrc_buf_size= vb_qfactor= vi_qfactor= \ + vb_qoffset= vi_qoffset= vqblur= \ + vqcomp= vrc_eq= vrc_override= \ + vrc_init_cplx= vqsquish= vlelim= \ + vcelim= vstrict= vdpart vpsize= gray \ + vfdct= idct= lumi_mask= dark_mask= \ + tcplx_mask= scplx_mask= naq ildct \ + format= pred qpel precmp= cmp= \ + subcmp= predia= dia= trell last_pred= \ + preme= subq= psnr mpeg_quant aic umv' \ + -- $cur ) ) + return 0 + ;; + -ssf) + COMPREPLY=( $( compgen -W 'lgb= cgb= ls= cs= chs= \ + cvs=' -- $cur ) ) + return 0 + ;; + -jpeg) + COMPREPLY=( $( compgen -W 'noprogressive progressive \ + nobaseline baseline optimize= \ + smooth= quality= outdir=' -- $cur ) ) + return 0 + ;; + -xvidopts) + COMPREPLY=( $( compgen -W 'dr2 nodr2' -- $cur ) ) + return 0 + ;; + -xvidencopts) + COMPREPLY=( $( compgen -W 'pass= bitrate= \ + fixed_quant= me_quality= 4mv \ + rc_reaction_delay_factor= \ + rc_averaging_period= rc_buffer= \ + quant_range= min_key_interval= \ + max_key_interval= mpeg_quant \ + mod_quant lumi_mask hintedme \ + hintfile debug keyframe_boost= \ + kfthreshold= kfreduction=' -- $cur ) ) + return 0 + ;; + -divx4opts) + COMPREPLY=( $( compgen -W 'br= key= deinterlace q= \ + min_quant= max_quant= rc_period= \ + rc_reaction_period= crispness= \ + rc_reaction_ratio= pass= vbrpass= \ + help' -- $cur ) ) + return 0 + ;; + -info) + COMPREPLY=( $( compgen -W 'name= artist= genre= \ + subject= copyright= srcform= \ + comment= help' -- $cur ) ) + return 0 + ;; + -lameopts) + COMPREPLY=( $( compgen -W 'vbr= abr cbr br= q= aq= \ + ratio= vol= mode= padding= fast \ + preset= help' -- $cur ) ) + return 0 + ;; + -rawaudio) + COMPREPLY=( $( compgen -W 'on channels= rate= \ + samplesize= format=' -- $cur ) ) + return 0 + ;; + -rawvideo) + COMPREPLY=( $( compgen -W 'on fps= sqcif qcif cif \ + 4cif pal ntsc w= h= y420 yv12 yuy2 \ + y8 format= size=' -- $cur ) ) + return 0 + ;; + -aop) + COMPREPLY=( $( compgen -W 'list= delay= format= fout= \ + volume= mul= softclip' -- $cur ) ) + return 0 + ;; + -dxr2) + COMPREPLY=( $( compgen -W 'ar-mode= iec958-encoded \ + iec958-decoded mute ucode= 75ire bw \ + color interlaced macrovision= norm= \ + square-pixel ccir601-pixel cr-left= \ + cr-right= cr-top= cr-bot= ck-rmin= \ + ck-gmin= ck-bmin= ck-rmax= ck-gmax= \ + ck-bmax= ck-r= ck-g= ck-b= \ + ignore-cache= ol-osd= olh-cor= \ + olw-cor= olx-cor= oly-cor= overlay \ + overlay-ratio= update-cache' -- $cur )) + return 0 + ;; + -tv) + COMPREPLY=( $( compgen -W 'on noaudio driver= device= \ + input= freq= outfmt= width= height= \ + buffersize= norm= channel= chanlist= \ + audiorate= forceaudio alsa amode= \ + forcechan= adevice= audioid= volume= \ + bass= treble= balance= fps= \ + channels= immediatemode=' -- $cur ) ) + return 0 + ;; + -mf) + COMPREPLY=( $( compgen -W 'on w= h= fps= type=' \ + -- $cur ) ) + return 0 + ;; + -cdda) + COMPREPLY=( $( compgen -W 'speed= paranoia= \ + generic-dev= sector-size= overlap= \ + toc-bias toc-offset= skip noskip' \ + -- $cur ) ) + return 0 + ;; + -input) + COMPREPLY=( $( compgen -W 'conf= ar-delay ar-rate \ + keylist cmdlist js-dev file' -- $cur ) ) + return 0 + ;; + -af) + COMPREPLY=( $( compgen -W 'resample resample= \ + channels channels= format format= \ + volume volume= delay delay= pan \ + pan= sub sub= surround surround=' \ + -- $cur ) ) + return 0 + ;; + -af-adv) + COMPREPLY=( $( compgen -W 'force= list=' -- $cur ) ) + return 0 + ;; + esac + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W '-aid -alang -audio-demuxer \ + -audiofile -cdrom-device -cache -cdda \ + -channels -chapter -csslib -demuxer \ + -dvd -dvd-device -dvdangle -dvdauth \ + -dvdkey -dvdnav -forceidx -fps -frames \ + -hr-mp3-seek -idx -mc -mf -ni -nobps \ + -passwd -rawaudio -rtsp-stream-over-tcp\ + -skipopening -sb -srate -ss -tv -user \ + -vcd -vid -vivo -ifo -ffactor -font \ + -noautosub -nooverlapsub -sid -slang \ + -sub -subcc -subcp -sub-demuxer \ + -subdelay -subfont-autoscale \ + -subfont-blur -subfont-encoding \ + -subfont-osd-scale -subfont-outline \ + -subfont-text-scale -subfps -subfile \ + -subpos -unicode -utf8 -vobsub \ + -vobsubid -ac -afm -aspect -flip \ + -lavdopts -noaspect -nosound -pp -ssf \ + -stereo -sws -vc -vfm -vop -xvidopts\ + -xy -zoom -bandwidth -cuefile \ + -noextbased -rawvideo -overlapsub \ + -sub-bg-alpha -sub-bg-color -subalign \ + -subwidth -sub-no-text-pp -spualign \ + -spuaa -spugauss -pphelp -verbose -v \ + -noni -noidx -nohr-mp3-seek -extbased \ + -bps -oldpp -nozoom -noflip -nounicode \ + -noutf8' -- $cur ) ) + # add mplayer specific options + [[ "$cmd" == @(?(g)mplayer) ]] && COMPREPLY=( "${COMPREPLY[@]}" \ + $(compgen -W '-autoq -autosync -benchmark \ + -framedrop -h -help -hardframedrop \ + -identify -input -lircconf -loop \ + -nojoystick -nolirc -nortc -playlist \ + -quiet -really-quiet -rnd -sdp -skin \ + -slave -softsleep -speed -sstep \ + -use-stdin -dumpaudio -dumpfile \ + -dumpstream -dumpvideo -dumpmicrodvdsub\ + -dumpmpsub -dumpsrtsub -dumpjacosub \ + -dumpsami -dumpsub -osdlevel -af \ + -af-adv -ao -aofile -aop -delay -mixer \ + -nowaveheader -bpp -brightness \ + -contrast -display -double -dr -dxr2 \ + -fb -fbmode -fbmodeconfig -forcexv -fs \ + -geometry -hue -icelayer -jpeg \ + -monitor-dotclock -monitor-hfreq \ + -monitor-vfreq -monitoraspect \ + -nograbpointer -noslices -panscan \ + -rootwin -saturation -screenw -screenh \ + -stop-xscreensaver -vm -vo -vsync -wid \ + -xineramascreen -z -zrbw -zrcrop \ + -zrdev -zrfd -zrhelp -zrnorm -zrquality \ + -zrvdec -zrhdec -zrxdoff -zrydoff -y \ + -edl -edlout -enqueue -fixed-vo \ + -menu -menu-root -menu-cfg -shuffle \ + -format -aahelp -dfbopts -fstype \ + -guiwid -nokeepaspect -x --help \ + -aaosdcolor -aasubcolor -aadriver \ + -aaextended -aaeight' -- $cur) ) + # add mencoder specific options + [[ "$cmd" = mencoder ]] && COMPREPLY=( "${COMPREPLY[@]}" \ + $(compgen -W '-audio-density -audio-delay \ + -audio-preload -divx4opts -endpos \ + -ffourcc -include -info -lameopts \ + -lavcopts -noskip -o -oac -ofps -ovc \ + -passlogfile -skiplimit -vobsubout \ + -vobsuboutindex -vobsuboutid \ + -xvidencopts -of --verbose' -- $cur) ) + ;; + *) + _filedir '@(mp?(e)g|MP?(E)G|wm[av]|WM[AV]|avi|AVI|asf|ASF|vob|VOB|bin|BIN|dat|DAT|vcd|VCD|ps|PS|pes|PES|fli|FLI|flv|FLV|viv|VIV|rm?(j)|RM?(J)|ra?(m)|RA?(M)|yuv|YUV|mov|MOV|qt|QT|mp[34]|MP[34]|m4v|M4V|og[gm]|OG[GM]|wav|WAV|dump|DUMP|mk[av]|MK[AV]|m4a|M4A|aac|AAC|m2v|M2V|dv|DV|rmvb|RMVB|mid|MID|ts|TS|3gp|mpc|MPC|flac|FLAC|flv|FLV|divx|DIVX)' + ;; + esac + + return 0 +} +complete $filenames -F _mplayer mplayer mencoder gmplayer kplayer +} + +# KDE dcop completion +# +have dcop && +_dcop() +{ + local cur compstr + + COMPREPLY=() + cur=`_get_cword` + if [ -z $cur ]; then + compstr=${COMP_WORDS[*]} + else + compstr=$( command echo ${COMP_WORDS[*]} | sed "s/ $cur$//" ) + fi + COMPREPLY=( $( compgen -W '$( command $compstr | sed s/\(.*\)// )' -- $cur ) ) +} && +complete -F _dcop dcop + +# wvdial(1) completion +# +have wvdial && +_wvdial() +{ + local cur prev config i IFS=$'\t\n' + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case $prev in + --config) + _filedir + return 0 + ;; + esac + + case $cur in + -*) + COMPREPLY=( $( compgen -W '--config --chat \ + --remotename --help --version --no-syslog' \ + -- $cur ) ) + ;; + *) + # start with global and personal config files + config="/etc/wvdial.conf"$'\t'"$HOME/.wvdialrc" + # replace with command line config file if present + for (( i=1; i < COMP_CWORD; i++ )); do + if [[ "${COMP_WORDS[i]}" == "--config" ]]; then + config=${COMP_WORDS[i+1]} + break + fi + done + # parse config files for sections and + # remove default section + COMPREPLY=( $( sed -ne \ + "s|^\[Dialer \($cur.*\)\]$|\1|p" \ + $config 2>/dev/null |grep -v '^Defaults$')) + # escape spaces + COMPREPLY=${COMPREPLY// /\\ } + ;; + esac + +} && +complete -F _wvdial wvdial + +# gpg(1) completion +# +have gpg && +_gpg() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(s|-sign|-clearsign|-decrypt-files|-load-extension)) + _filedir + return 0 + ;; + --@(export|@(?(l|nr|nrl)sign|edit)-key)) + # return list of public keys + COMPREPLY=( $( compgen -W "$( gpg --list-keys 2>/dev/null | sed -ne 's@^pub.*/\([^ ]*\).*\(<\([^>]*\)>\).*$@\1 \3@p')" -- "$cur" )) + return 0 + ;; + -@(r|-recipient)) + COMPREPLY=( $( compgen -W "$( gpg --list-keys 2>/dev/null | sed -ne 's@^pub.*<\([^>]*\)>.*$@\1@p')" -- "$cur" )) + if [ -e ~/.gnupg/gpg.conf ]; then + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W "$( sed -ne 's@^[ \t]*group[ \t][ \t]*\([^=]*\).*$@\1@p' ~/.gnupg/gpg.conf )" -- "$cur") ) + fi + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-s -b -e -f -c -d -a -r -u -Z -o -v\ + -q -n -N $(gpg --dump-options)' -- $cur ) ) + fi + +} && +complete -F _gpg $default gpg + +# iconv(1) completion +# +have iconv && +_iconv() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(f|t|-@(from|to)-code)) + COMPREPLY=( $( compgen -W \ + '$( iconv --list | sed -e "s@//@@;" )' -- "$cur" ) ) + return 0 + ;; + esac + + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $( compgen -W '--from-code -f --to-code -t --list + --output -o --verbose' -- "$cur" ) ) + return 0 + fi +} && +complete -F _iconv $default iconv + +# dict(1) completion +# +{ have dict || have rdict; } && { +_dictdata() +{ + dict $host $port $1 2>/dev/null | sed -ne \ + 's/^['$'\t '']['$'\t '']*\([^'$'\t '']*\).*$/\1/p' +} + +_dict() +{ + local cur prev host port db dictfile + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + dictfile=/usr/share/dict/words + + for (( i=1; i < COMP_CWORD; i++ )); do + case "${COMP_WORDS[i]}" in + -@(h|--host)) + host=${COMP_WORDS[i+1]} + [ -n "$host" ] && host="-h $host" + i=$((++i)) + ;; + -@(p|-port)) + port=${COMP_WORDS[i+1]} + [ -n "$port" ] && port="-p $port" + i=$((++i)) + ;; + -@(d|-database)) + db=${COMP_WORDS[i+1]} + [ -n "$db" ] && host="-d $db" + i=$((++i)) + ;; + *) + ;; + esac + done + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $( compgen -W '-h --host -p --port -d --database \ + -m --match -s --strategy -c --config -C \ + --nocorrect -D --dbs -S --strats -H \ + --serverhelp -i --info -I --serverinfo \ + -a --noauth -u --user -k --key -V --version \ + -L --license --help -v --verbose -r --raw \ + -P --pager --debug --html --pipesize --client' \ + -- "$cur" ) ) + return 0 + fi + + case "$prev" in + -@(d|-database|i|info)) + COMPREPLY=( $( compgen -W '$( _dictdata -D )' -- "$cur" ) ) + return 0 + ;; + -@(s|-strategy)) + COMPREPLY=( $( compgen -W '$( _dictdata -S )' -- "$cur" ) ) + return 0 + ;; + *) + ;; + esac + + [ -r $dictfile ] && \ + COMPREPLY=( $( compgen -W '$( cat $dictfile )' -- "$cur" ) ) +} +complete -F _dict $default dict rdict +} + +# cdrecord(1) completion +# +have cdrecord && +_cdrecord() +{ + local cur prev i generic_options track_options track_mode + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + # foo=bar style option + if [[ "$cur" == *=* ]]; then + prev=${cur/=*/} + cur=${cur/*=/} + case "$prev" in + @(text|cue)file) + _filedir + return 0 + ;; + blank) + COMPREPLY=( $( compgen -W 'help all fast \ + track unreserve trtail unclose session' \ + -- $cur ) ) + return 0 + ;; + driveropts) + COMPREPLY=( $( compgen -W 'burnfree noburnfree\ + varirec= audiomaster forcespeed noforcespeed\ + speedread nospeedread singlesession \ + nosinglesession hidecdr nohidecdr tattooinfo\ + tattoofile=' -- $cur ) ) + return 0 + ;; + esac + fi + + generic_options=(-version -v -V -d -silent -s -force -immed -dummy \ + -dao -raw -raw96r -raw96p -raw16 -multi -msinfo -toc \ + -atip -fix -nofix -waiti -load -lock -eject -format \ + -setdropts -checkdrive -prcap -inq -scanbus -reset \ + -abort -overburn -ignsize -useinfo -packet -noclose \ + -text debug= kdebug= kd= minbuf= speed= blank= fs= \ + dev= gracetime= timeout= driver= driveropts= \ + defpregap= pktsize= mcn= textfile= cuefile=) + track_options=(-audio -swab -data -mode2 -xa -xa1 -xa2 -xamix -cdi \ + -isosize -pad padsize= -nopad -shorttrack -noshorttrack\ + pregap= -preemp -nopreemp -copy -nocopy -scms tcsize= \ + isrc= index=) + # look if previous was either a file or a track option + track_mode=0 + if [ $COMP_CWORD -gt 1 ]; then + if [ -f "$prev" ]; then + track_mode=1 + else + for (( i=0; i < ${#track_options[@]}; i++ )); do + if [[ "${track_options[i]}" == "$prev" ]]; then + track_mode=1 + break + fi + done + fi + fi + + # files are always eligible completion + _filedir + # track options are always available + COMPREPLY=( "${COMPREPLY[@]}" $( compgen -W '${track_options[@]}' -- $cur ) ) + # general options are no more available after file or track option + if [ $track_mode -eq 0 ]; then + COMPREPLY=( "${COMPREPLY[@]}" \ + $( compgen -W '${generic_options[@]}' -- $cur ) ) + fi + +} && +complete -F _cdrecord $filenames cdrecord + +# mkisofs(8) completion +# +have mkisofs && +_mkisofs() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(o|abstract|biblio|check-session|copyright|log-file|root-info|prep-boot|*-list)) + _filedir + return 0 + ;; + -*-charset) + COMPREPLY=( $( mkisofs -input-charset help 2>&1 | \ + tail +3 | grep "^$cur") ) + return 0 + ;; + -uid) + _uids + return 0 + ;; + -gid) + _gids + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-abstract -A -allow-lowercase \ + -allow-multidot -biblio -cache-inodes \ + -no-cache-inodes -b -eltorito-alt-boot -B -G \ + -hard-disk-boot -no-emul-boot -no-boot \ + -boot-load-seg -boot-load-size \ + -boot-info-table -C -c -check-oldname \ + -check-session -copyright -d -D -dir-mode \ + -dvd-video -f -file-mode -gid -gui \ + -graft-points -hide -hide-list -hidden \ + -hidden-list -hide-joliet -hide-joliet-list \ + -hide-joliet-trans-tbl -hide-rr-moved \ + -input-charset -output-charset -iso-level -J \ + -joliet-long -jcharset -l -L -log-file -m \ + -exclude-list -max-iso9660-filenames -M -N \ + -new-dir-mode -nobak -no-bak -force-rr -no-rr \ + -no-split-symlink-components \ + -no-split-symlink-fields -o -pad -no-pad \ + -path-list -P -p -print-size -quiet -R -r \ + -relaxed-filenames -sort -split-output \ + -stream-media-size -stream-file-name -sysid -T\ + -table-name -ucs-level -udf -uid \ + -use-fileversion -U -no-iso-translate -V \ + -volset -volset-size -volset-seqno -v -x -z \ + -hfs -apple -map -magic -hfs-creator \ + -hfs-type -probe -no-desktop -mac-name \ + -boot-hfs-file -part -auto -cluster-size \ + -hide-hfs -hide-hfs-list -hfs-volid \ + -icon-position -root-info -prep-boot \ + -input-hfs-charset -output-hfs-charset \ + -hfs-unlock -hfs-bless -hfs-parms --cap \ + --netatalk --double --ethershare --ushare \ + --exchange --sgi --xinet --macbin --single \ + --dave --sfm --osx-double --osx-hfs' -- $cur )) + else + _filedir + fi + +} && +complete -F _mkisofs $filenames mkisofs + +# mc(1) completion +# +have mc && +_mc() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + # -name value style option + case "$prev" in + -@(e|v|l|P)) + _filedir + return 0 + ;; + esac + + # --name=value style option + if [[ "$cur" == *=* ]]; then + prev=${cur/=*/} + cur=${cur/*=/} + case "$prev" in + --@(edit|view|ftplog|printwd)) + _filedir + return 0 + ;; + esac + fi + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-a --stickchars -b --nocolor -c \ + --color -C --colors= -d --nomouse -e --edit= -f \ + --datadir -k --resetsoft -l --ftplog= -P --printwd= \ + -s --slow -t --termcap -u --nosubshell -U --subshell \ + -v --view= -V --version -x --xterm -h --help' -- $cur ) ) + else + _filedir -d + fi +} && +complete -F _mc $filenames mc + +# yum(8) completion +# +have yum && { +_yum() +{ + local cur prev special + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + for (( i=0; i < ${#COMP_WORDS[@]}-1; i++ )); do + if [[ ${COMP_WORDS[i]} == @(install|update|upgrade|remove|erase|deplist) ]]; then + special=${COMP_WORDS[i]} + fi + done + + if [ -n "$special" ]; then + case $special in + install|deplist) + COMPREPLY=( $( compgen -W '$( yum -C list | cut -d" " -f1 )' -- $cur ) ) + return 0 + ;; + *) + _rpm_installed_packages + return 0 + ;; + esac + fi + + case $cur in + --*) + COMPREPLY=( $( compgen -W '--installroot --version --help --enablerepo --disablerepo --exclude --obsoletes --noplugins' -- $cur ) ) + return 0 + ;; + -*) + COMPREPLY=( $( compgen -W '-c -e -d -y -t -R -C -h' -- $cur ) ) + return 0 + ;; + esac + + case $prev in + list) + COMPREPLY=( $( compgen -W 'all available updates installed extras obsoletes recent' -- $cur ) ) + ;; + clean) + COMPREPLY=( $( compgen -W 'packages headers metadata cache dbcache all' -- $cur ) ) + ;; + localinstall) + _filedir rpm + ;; + -c) + _filedir + ;; + --installroot) + _filedir -d + ;; + *) + COMPREPLY=( $( compgen -W 'install update check-update upgrade remove list \ + search info provides clean groupinstall groupupdate \ + grouplist deplist erase groupinfo groupremove \ + localinstall localupdate makecache resolvedep \ + shell whatprovides' -- $cur ) ) + ;; + esac +} +complete -F _yum $filenames yum + +# yum-arch(8) completion +# +_yum_arch() +{ + local cur + COMPREPLY=() + cur=`_get_cword` + + case "$cur" in + -*) + COMPREPLY=( $( compgen -W '-d -v -vv -n -c -z -s -l -q' -- $cur ) ) + ;; + *) + _filedir -d + ;; + esac + + return 0 + +} +complete -F _yum_arch $filenames yum-arch +} + +# ImageMagick completion +# +have convert && { +_ImageMagick() +{ + local prev + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -channel) + COMPREPLY=( $( compgen -W 'Red Green Blue Opacity \ + Matte Cyan Magenta Yellow Black' -- $cur ) ) + return 0 + ;; + -colormap) + COMPREPLY=( $( compgen -W 'shared private' -- $cur ) ) + return 0 + ;; + -colorspace) + COMPREPLY=( $( compgen -W 'GRAY OHTA RGB Transparent \ + XYZ YCbCr YIQ YPbPr YUV CMYK' -- $cur ) ) + return 0 + ;; + -compose) + COMPREPLY=( $( compgen -W 'Over In Out Atop Xor Plus \ + Minus Add Subtract Difference Multiply Bumpmap\ + Copy CopyRed CopyGreen CopyBlue CopyOpacity' \ + -- $cur ) ) + return 0 + ;; + -compress) + COMPREPLY=( $( compgen -W 'None BZip Fax Group4 JPEG \ + Lossless LZW RLE Zip' -- $cur ) ) + return 0 + ;; + -dispose) + COMPREPLY=( $( compgen -W 'Undefined None Background \ + Previous' -- $cur ) ) + return 0 + ;; + -encoding) + COMPREPLY=( $( compgen -W 'AdobeCustom AdobeExpert \ + AdobeStandard AppleRoman BIG5 GB2312 Latin2 \ + None SJIScode Symbol Unicode Wansung' -- $cur)) + return 0 + ;; + -endian) + COMPREPLY=( $( compgen -W 'MSB LSB' -- $cur ) ) + return 0 + ;; + -filter) + COMPREPLY=( $( compgen -W 'Point Box Triangle Hermite \ + Hanning Hamming Blackman Gaussian Quadratic \ + Cubic Catrom Mitchell Lanczos Bessel Sinc' \ + -- $cur ) ) + return 0 + ;; + -format) + COMPREPLY=( $( convert -list format | \ + awk '/ [r-][w-][+-] / {print $1}' | \ + tr -d '*' | tr [:upper:] [:lower:] | \ + grep "^$cur" ) ) + return 0 + ;; + -gravity) + COMPREPLY=( $( compgen -W 'Northwest North NorthEast \ + West Center East SouthWest South SouthEast' \ + -- $cur ) ) + return 0 + ;; + -intent) + COMPREPLY=( $( compgen -W 'Absolute Perceptual \ + Relative Saturation' -- $cur ) ) + return 0 + ;; + -interlace) + COMPREPLY=( $( compgen -W 'None Line Plane Partition' \ + -- $cur ) ) + return 0 + ;; + -limit) + COMPREPLY=( $( compgen -W 'Disk File Map Memory' \ + -- $cur ) ) + return 0 + ;; + -list) + COMPREPLY=( $( compgen -W 'Delegate Format Magic \ + Module Resource Type' -- $cur ) ) + return 0 + ;; + -map) + COMPREPLY=( $( compgen -W 'best default gray red \ + green blue' -- $cur ) ) + _filedir + return 0 + ;; + -noise) + COMPREPLY=( $( compgen -W 'Uniform Gaussian \ + Multiplicative \ + Impulse Laplacian Poisson' -- $cur ) ) + return 0 + ;; + -preview) + COMPREPLY=( $( compgen -W 'Rotate Shear Roll Hue \ + Saturation Brightness Gamma Spiff \ + Dull Grayscale Quantize Despeckle \ + ReduceNoise AddNoise Sharpen Blur \ + Treshold EdgeDetect Spread Shade \ + Raise Segment Solarize Swirl Implode \ + Wave OilPaint CharcoalDrawing JPEG' \ + -- $cur ) ) + return 0 + ;; + -@(mask|profile|texture|tile|write)) + _filedir + return 0 + ;; + -type) + COMPREPLY=( $( compgen -W 'Bilevel Grayscale Palette \ + PaletteMatte TrueColor TrueColorMatte \ + ColorSeparation ColorSeparationlMatte \ + Optimize' -- $cur ) ) + return 0 + ;; + -units) + COMPREPLY=( $( compgen -W 'Undefined PixelsPerInch \ + PixelsPerCentimeter' -- $cur ) ) + return 0 + ;; + -virtual-pixel) + COMPREPLY=( $( compgen -W 'Constant Edge mirror tile' \ + -- $cur ) ) + return 0 + ;; + -visual) + COMPREPLY=( $( compgen -W 'StaticGray GrayScale \ + StaticColor PseudoColor TrueColor \ + DirectColor defaut visualid' -- $cur )) + return 0 + ;; + esac +} + +_convert() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + _ImageMagick + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-adjoin -affine -antialias -append \ + -authenticate -average -background -black-threshold \ + -blue-primary -blur -border -bordercolor -channel \ + -charcoal -chop -clip -coalesce -colorize -colors \ + -colorspace -comment -compress -contrast -convolve \ + -crop -cycle -debug -deconstruct -delay -density \ + -depth -despeckle -display -dispose -dither -draw \ + -edge -emboss -encoding -endian -enhance -equalize \ + -extract -fill -filter -flatten -flip -flop -font \ + -frame -fuzz -gamma -gaussian -geometry \ + -green-primary -gravity -help -implode -intent \ + -interlace -label -lat -level -limit -list -log -loop \ + -map -mask -matte -median -modulate -monochrome \ + -morph -mosaic -negate -noop -noise -normalize \ + -opaque -ordered-dither -page -paint -ping -pointsize \ + -preview -profile -quality -raise -random-threshold \ + -region -raise -red-primary -render -resize -resample \ + -roll -rotate -sample -sampling-factor -scale -scene \ + -seed -segment -shade -sharpen -shave -shear -size \ + -solarize -spread -stroke -strokewidth -swirl \ + -texture -threshold -thumbnail -tile -transform \ + -transparent -treedepth -trim -type -undercolor \ + -units -unsharp -verbose -version -view \ + -virtual-pixel -wave -white-point -white-threshold \ + -write' -- $cur ) ) + elif [[ "$cur" == +* ]]; then + COMPREPLY=( $( compgen -W '+adjoin +append +compress \ + +contrast +debug +dither +endian +gamma +label +map \ + +mask +matte +negate +noise +page +raise +render \ + +write' -- $cur ) ) + else + _filedir + fi +} +complete -F _convert $filenames convert + +_mogrify() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + _ImageMagick + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-affine -antialias -authenticate \ + -background -black-threshold -blue-primary -blur \ + -border -bordercolor -channel -charcoal -chop \ + -colorize -colors -colorspace -comment -compress \ + -contrast -convolve -crop -cycle -debug -delay \ + -density -depth -despeckle -display -dispose -dither \ + -draw -edge -emboss -encoding -endian -enhance \ + -equalize -extract -fill -filter -flip -flop -font \ + -format -frame -fuzz -gamma -gaussian -geometry \ + -green-primary -implode -interlace -help -label -lat \ + -level -limit -list -log -loop -map -mask -matte \ + -median -modulate -monochrome -negate -noop \ + -normalize -opaque -page -paint -fill -ordered-dither \ + -pointsize -profile -quality -raise -random-threshold \ + -red-primary -region -resample -resize -roll -rotate \ + -sample -sampling-factor -scale -scene -seed -segment \ + -shade -sharpen -shear -size -solarize -spread \ + -stroke -strokewidth -swirl -texture -threshold \ + -thumbnail -tile -transform -transparent -treedepth \ + -trim -type -undercolor -units -unsharp -verbose \ + -version -view -virtual-pixel -wave -white-point \ + -white-threshold' -- $cur ) ) + elif [[ "$cur" == +* ]]; then + COMPREPLY=( $( compgen -W '+compress +contrast +debug +dither \ + +endian +gamma +label +map +mask +matte +negate +page \ + +raise' -- $cur ) ) + else + _filedir + fi +} +complete -F _mogrify $filenames mogrify + +_display() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + _ImageMagick + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-authenticate -backdrop -border \ + -colormap -colors -colorspace -comment -compress \ + -contrast -crop -debug -delay -density -depth \ + -despeckle -display -dispose -dither -edge -endian \ + -enhance -extract -filter -flip -flop -frame -gamma \ + -geometry -help -immutable -interlace -label -limit \ + -log -map -matte -monochrome -negate -noop -page \ + -quality -raise -remote -roll -rotate -sample \ + -sampling-factor -scene -segment -sharpen -size \ + -texture -treedepth -trim -update -verbose -version \ + -virtual-pixel -window -window_group -write' -- $cur)) + elif [[ "$cur" == +* ]]; then + COMPREPLY=( $( compgen -W '+compress +contrast +debug +dither \ + +endian +gamma +label +map +matte +negate +page \ + +raise +write' -- $cur ) ) + else + _filedir + fi +} +complete -F _display $filenames display + +_animate() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + _ImageMagick + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-authenticate -backdrop -colormap \ + -colors -colorspace -crop -debug -delay -density \ + -depth -display -dither -extract -gamma -geometry \ + -help -interlace -limit -log -matte -map -monochrome \ + -noop -pause -remote -rotate -sampling-factor -scene \ + -size -treedepth -trim -verbose -version -visual \ + -virtual-pixel -window' -- $cur ) ) + elif [[ "$cur" == +* ]]; then + COMPREPLY=( $( compgen -W '+debug +dither +gamma +map +matte' -- $cur ) ) + else + _filedir + fi +} +complete -F _animate $filenames animate + +_identify() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + _ImageMagick + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-authenticate -debug -density \ + -depth -extract -format -help -interlace -limit -list \ + -log -size -sampling-factor -verbose -version \ + -virtual-pixel' -- $cur ) ) + elif [[ "$cur" == +* ]]; then + COMPREPLY=( $( compgen -W '+debug ' -- $cur ) ) + else + _filedir + fi +} +complete -F _identify $filenames identify + +_montage() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + _ImageMagick + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-adjoin -affine -authenticate \ + -blue-primary -blur -colors -colorspace -comment \ + -compose -compress -crop -debug -density -depth \ + -display -dispose -dither -draw -encoding -endian \ + -extract -fill -filter -flip -flop -frame -gamma \ + -geometry -gravity -green-primary -interlace -help \ + -label -limit -log -matte -mode -monochrome -noop \ + -page -pointsize -quality -red-primary -resize \ + -rotate -sampling-factor -scene -shadow -size \ + -stroke -texture -thumbnail -tile -transform \ + -transparent -treedepth -trim -type -verbose \ + -version -virtual-pixel -white-point' -- $cur ) ) + elif [[ "$cur" == +* ]]; then + COMPREPLY=( $( compgen -W '+adjoin +compress +debug +dither \ + +endian +gamma +label +matte +page' -- $cur ) ) + else + _filedir + fi +} +complete -F _montage $filenames montage + +_composite() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + _ImageMagick + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-affine -authenticate \ + -blue-primary -colors -colorspace -comment -compose \ + -compress -debug -density -depth -displace -display \ + -dispose -dissolve -dither -encoding -endian -extract \ + -filter -font -geometry -gravity -green-primary -help \ + -interlace -label -limit -log -matte -monochrome \ + -negate -page -profile -quality -red-primary -rotate \ + -resize -sampling-factor -scene -sharpen -size \ + -stegano -stereo -thumbnail -tile -transform \ + -treedepth -type -units -unsharp -verbose -version \ + -virtual-pixel -watermark -white-point -write' \ + -- $cur ) ) + elif [[ "$cur" == +* ]]; then + COMPREPLY=( $( compgen -W '+compress +debug +dither +endian +label \ + +matte +negate +page +write' -- $cur ) ) + else + _filedir + fi +} +complete -F _composite $filenames composite +} + +# dd(1) completion +# +have dd && +_dd() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + case "$cur" in + if=*|of=*) + cur=${cur#*=} + _filedir + return 0 + ;; + conv=*) + cur=${cur#*=} + COMPREPLY=( $( compgen -W 'ascii ebcdic ibm block unblock \ + lcase notrunc ucase swab noerror sync' \ + -- $cur ) ) + return 0 + ;; + esac + + _expand || return 0 + + COMPREPLY=( $( compgen -W '--help --version' -- $cur ) \ + $( compgen -W 'bs cbs conv count ibs if obs of seek skip'\ + -S '=' -- $cur ) ) +} && +complete -F _dd $nospace $filenames dd + +# CUPS cancel(1) completion +# +have cancel && +_cancel() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + COMPREPLY=( $( lpstat | cut -d' ' -f1 | grep "^$cur" ) ) +} && +complete -F _cancel $filenames cancel + +# aspell(1) completion +# +have aspell && { +_aspell_dictionary() +{ + local datadir + datadir=/usr/lib/aspell + COMPREPLY=( $( command ls $datadir/*.@(multi|alias) ) ) + COMPREPLY=( ${COMPREPLY[@]%.@(multi|alias)} ) + COMPREPLY=( $( compgen -W '${COMPREPLY[@]#$datadir/}' -- $cur ) ) +} + +_aspell() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + # --name value style option + case "$prev" in + @(-c|-p|check)) + _filedir + return 0 + ;; + @(dump|create|merge)) + COMPREPLY=( $( compgen -W 'master personal repl' -- $cur ) ) + return 0 + ;; + -d) + _aspell_dictionary + return 0 + ;; + esac + + # --name=value style option + if [[ "$cur" == *=* ]]; then + prev=${cur/=*/} + cur=${cur/*=/} + case "$prev" in + --@(conf|personal|repl|per-conf)) + _filedir + return 0 + ;; + --@(conf-dir|data-dir|dict-dir|home-dir|local-data-dir|prefix)) + _filedir -d + return 0 + ;; + --master) + _aspell_dictionary + return 0 + ;; + --mode) + COMPREPLY=( $( compgen -W 'none url email sgml tex' -- $cur ) ) + return 0 + ;; + --sug-mode) + COMPREPLY=( $( compgen -W 'ultra fast normal bad-speller' -- $cur ) ) + return 0 + ;; + --keymapping) + COMPREPLY=( $( compgen -W 'aspell ispell' -- $cur ) ) + return 0 + ;; + esac + fi + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--conf= --conf-dir= --data-dir= --dict-dir= \ + --encoding= --add-filter= --rem-filter= --mode= -e \ + -H -t --add-extra-dicts= --rem-extra-dicts= \ + --home-dir= -W --ignore= --ignore-accents \ + --dont-ignore-accents --ignore-case --dont-ignore-case \ + --ignore-repl --dont-ignore-repl --jargon= --keyboard= \ + --lang= --language-tag= --local-data-dir= -d --master= \ + --module= --add-module-search-order= \ + --rem-module-search-order= --per-conf= -p --personal= \ + --prefix= --repl= -C -B --run-together --dont-run-together \ + --run-together-limit= --run-together-min= --save-repl \ + --dont-save-repl --set-prefix --dont-set-prefix --size= \ + --spelling= --strip-accents --dont-strip-accents \ + --sug-mode= --add-word-list-path= --rem-word-list-path= \ + -b -x --backup -b|-x --dont-backup --reverse --dont-reverse \ + --time --dont-time --keymapping= --add-email-quote= \ + --rem-email-quote= --email-margin= --add-tex-command= \ + --rem-tex-command= --tex-check-comments \ + --dont-tex-check-comments --add-tex-extension= \ + --rem-tex-extension= --add-sgml-check= --rem-sgml-check= \ + --add-sgml-extension= --rem-sgml-extension=' -- $cur ) ) + else + COMPREPLY=( $( compgen -W '-? help -c check -a pipe -l list \ + config config soundslike filter -v version dump \ + create merge' -- $cur ) ) + fi + +} +complete -F _aspell $filenames aspell +} + +# xmms(1) completion +# +have xmms && +_xmms() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-h --help -r --rew -p --play \ + -u --pause -s --stop -t --play-pause -f --fwd -e \ + --enqueue -m --show-main-window -i --sm-client-id \ + -v --version' -- $cur ) ) + else + _filedir '@(mp[23]|MP[23]|ogg|OGG|wav|WAV|pls|m3u|xm|mod|s[3t]m|it|mtm|ult|flac)' + + fi + +} && +complete -F _xmms $filenames xmms + +# info(1) completion +# +have info && +_info() +{ + local cur infopath UNAME + + COMPREPLY=() + cur=`_get_cword` + + _expand || return 0 + + # default completion if parameter contains / + if [[ "$cur" == */* ]]; then + _filedir + return 0 + fi + + infopath='/usr/share/info' + + if [ "${INFOPATH: -1:1}" == ':' ]; then + infopath=${INFOPATH}${infopath} + elif [ ${INFOPATH:+set} ]; then + infopath=$INFOPATH + fi + + infopath=$infopath: + if [ -n "$cur" ]; then + infopath="${infopath//://$cur* }" + else + infopath="${infopath//:// }" + fi + + # redirect stderr for when path doesn't exist + COMPREPLY=( $( eval command ls "$infopath" 2>/dev/null ) ) + # weed out directory path names and paths to info pages + COMPREPLY=( ${COMPREPLY[@]##*/?(:)} ) + # weed out info dir file + for (( i=0 ; i < ${#COMPREPLY[@]} ; ++i )); do + if [ "${COMPREPLY[$i]}" == 'dir' ]; then + unset COMPREPLY[$i]; + fi; + done + # strip suffix from info pages + COMPREPLY=( ${COMPREPLY[@]%.@(gz|bz2)} ) + COMPREPLY=( $( compgen -W '${COMPREPLY[@]%.*}' -- "${cur//\\\\/}" ) ) + + return 0 +} && +complete -F _info $filenames info + +# dhclient(1) completion +# +have dhclient && _dhclient() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(cf|lf|pf|sf)) + _filedir + return 0 + ;; + -s) + _known_hosts + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-p -d -q -1 -r -lf -pf \ + -cf -sf -s -g -n -nw -w' -- $cur ) ) + else + _available_interfaces + fi +} && +complete -F _dhclient dhclient + +# lvm(8) completion +# +have lvm && { +_volumegroups() +{ + COMPREPLY=( $(compgen -W "$( vgscan 2>/dev/null | \ + sed -n -e 's|.*Found.*"\(.*\)".*$|\1|p' )" -- $cur ) ) +} + +_physicalvolumes() +{ + COMPREPLY=( $(compgen -W "$( pvscan 2>/dev/null | \ + sed -n -e 's|^.*PV \(.*\) VG.*$|\1|p' )" -- $cur ) ) +} + +_logicalvolumes() +{ + COMPREPLY=( $(compgen -W "$( lvscan 2>/dev/null | \ + sed -n -e "s|^.*'\(.*\)'.*$|\1|p" )" -- $cur ) ) +} + +_units() +{ + COMPREPLY=( $( compgen -W 'h s b k m g t H K M G T' -- $cur ) ) +} + +_sizes() +{ + COMPREPLY=( $( compgen -W 'k K m M g G t T' -- $cur ) ) +} + +_args() +{ + args=0 + if [[ "${COMP_WORDS[0]}" == lvm ]]; then + offset=2 + else + offset=1 + fi + for (( i=$offset; i < COMP_CWORD; i++ )); do + if [[ "${COMP_WORDS[i]}" != -* ]]; then + args=$(($args + 1)) + fi + done +} + +_lvmdiskscan() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-d --debug -h -? --help -l \ + --lvmpartition -v --verbose --version' -- $cur ) ) + fi +} +complete -F _lvmdiskscan lvmdiskscan + +_pvscan() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-d --debug -e \ + --exported -n --novolumegroup -h -? \ + --help --ignorelockingfailure -P \ + --partial -s --short -u --uuid -v \ + --verbose --version' -- $cur ) ) + fi +} +complete -F _pvscan pvscan + +_pvs() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(o|O|-options|-sort)) + COMPREPLY=( $( compgen -W 'pv_fmt pv_uuid \ + pv_size pv_free pv_used pv_name \ + pv_attr pv_pe_count \ + pv_pe_alloc_count' -- $cur ) ) + return 0 + ;; + --units) + _units + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--aligned -a --all -d --debug \ + -h -? --help --ignorelockingfailure --noheadings \ + --nosuffix -o --options -O --sort \ + --separator --unbuffered --units \ + -v --verbose --version' -- $cur ) ) + else + _physicalvolumes + fi +} +complete -F _pvs pvs + +_pvdisplay() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + --units) + _units + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-c --colon -C --columns --units \ + -v --verbose -d --debug -h --help --version' -- $cur ) ) + else + _physicalvolumes + fi +} +complete -F _pvdisplay pvdisplay + +_pvchange() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|x|-autobackup|--allocatable)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-a --all -A --autobackup \ + -d --debug -h --help -t --test -u --uuid -x \ + --allocatable -v --verbose --addtag --deltag \ + --version' -- $cur ) ) + else + _physicalvolumes + fi +} +complete -F _pvchange pvchange + +_pvcreate() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + --restorefile) + _filedir + return 0 + ;; + -@(M|-metadatatype)) + COMPREPLY=( $( compgen -W '1 2' -- $cur ) ) + return 0 + ;; + --metadatacopies) + COMPREPLY=( $( compgen -W '0 1 2' -- $cur ) ) + return 0 + ;; + --@(metadatasize|setphysicalvolumesize)) + _sizes + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--restorefile -d --debug -f \ + --force -h -? --help --labelsector -M --metadatatype \ + --metadatacopies --metadatasize \ + --setphysicalvolumesize -t --test -u --uuid uuid -v \ + --verbose -y --yes --version' -- $cur ) ) + else + _physicalvolumes + fi +} +complete -F _pvcreate pvcreate + +_pvmove() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + -@(n|-name)) + _logicalvolumes + return 0 + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--abort -A --autobackup \ + -b --background -d --debug -f --force -h -? \ + --help -i --interval -t --test -v --verbose \ + --version -n --name' -- $cur ) ) + else + _physicalvolumes + fi +} +complete -F _pvmove pvmove + +_pvremove() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-d --debug -f --force -h -? \ + --help -y --yes -t --test -v --verbose \ + --version' -- $cur ) ) + else + _physicalvolumes + fi +} +complete -F _pvremove pvremove + +_vgscan() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-d --debug -h --help \ + --ignorelockingfailure --mknodes -P \ + --partial -v --verbose --version' -- $cur ) ) + fi +} +complete -F _vgscan vgscan + +_vgs() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(o|O|-options|-sort)) + COMPREPLY=( $( compgen -W 'vg_fmt vg_uuid vg_name \ + vg_attr vg_size vg_free vg_sysid \ + vg_extent_size vg_extent_count vg_free_count \ + max_lv max_pv pv_count lv_count snap_count \ + vg_seqno' -- $cur ) ) + return 0 + ;; + --units) + _units + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--aligned -d --debug \ + -h --help --ignorelockingfailure --noheadings \ + --nosuffix -o --options -O --sort -P --partial \ + --separator --unbuffered --units \ + -v --verbose --version' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgs vgs + +_vgdisplay() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + --units) + _units + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-c --colon -C --columns --units \ + -P --partial -A --activevolumegroups -v --verbose \ + -d --debug -h --help --version' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgdisplay vgdisplay + +_vgchange() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(a|A|x|-available|-autobackup|-resizeable)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup --alloc -P \ + --partial -d --debug -h --help --ignorelockingfailure \ + -t --test -u --uuid -v --verbose --version -a \ + --available -x --resizeable -l --logicalvolume \ + --addtag --deltag' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgchange vgchange + +_vgcreate() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + -@(M|-metadatatype)) + COMPREPLY=( $( compgen -W '1 2' -- $cur ) ) + return 0 + ;; + -@(s|-physicalextentsize)) + _sizes + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup --addtag \ + --alloc -d --debug -h --help -l --maxlogicalvolumes \ + -M --metadatatype -p --maxphysicalvolumes -s \ + --physicalextentsize -t --test -v --verbose \ + --version' -- $cur ) ) + else + _args + if [ $args -eq 0 ]; then + _volumegroups + else + _physicalvolumes + fi + fi +} +complete -F _vgcreate vgcreate + +_vgremove() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-d --debug -h --help -t --test \ + -v --verbose --version' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgremove vgremove + +_vgrename() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -h \ + -? --help -t --test -v --verbose --version' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgrename vgrename + +_vgreduce() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-a --all -A --autobackup -d \ + --debug -h --help --removemissing -t --test -v \ + --verbose --version' -- $cur ) ) + + else + _args + if [ $args -eq 0 ]; then + _volumegroups + else + _physicalvolumes + fi + fi +} +complete -F _vgreduce vgreduce + +_vgextend() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + -@(L|-size)) + _sizes + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -h \ + -? --help -t --test -v --verbose --version' -- $cur ) ) + else + _args + if [ $args -eq 0 ]; then + _volumegroups + else + _physicalvolumes + fi + fi +} +complete -F _vgextend vgextend + +_vgport() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-a --all -d --debug -h \ + -? --help -v --verbose --version' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgport vgimport vgexport + +_vgck() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-d --debug -h \ + -? --help -v --verbose --version' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgck vgck + +_vgconvert() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(M|-metadatatype)) + COMPREPLY=( $( compgen -W '1 2' -- $cur ) ) + return 0 + ;; + --metadatacopies) + COMPREPLY=( $( compgen -W '0 1 2' -- $cur ) ) + return 0 + ;; + --metadatasize) + _sizes + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-d --debug -h --help --labelsector \ + -M --metadatatype --metadatacopies --metadatasize \ + -t --test -v --verbose --version' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgconvert vgconvert + +_vgcfgbackup() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(f|-file)) + _filedir + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-d --debug -f --file -h --help \ + --ignorelockingfailure -P --partial -v --verbose \ + --version' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgcfgbackup vgcfgbackup + +_vgcfgrestore() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(f|-file)) + _filedir + return 0 + ;; + -@(M|-metadatatype)) + COMPREPLY=( $( compgen -W '1 2' -- $cur ) ) + return 0 + ;; + -@(n|-name)) + _volumegroups + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-d --debug -f --file -l --list \ + -h --help -M --Metadatatype -n --name -t --test \ + -v --verbose --version' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgcfgrestore vgcfgrestore + +_vgmerge() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup -d --debug \ + -h --help -l --list -t --test -v --verbose \ + --version' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgmerge vgmerge + +_vgsplit() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + -@(M|-metadatatype)) + COMPREPLY=( $( compgen -W '1 2' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup -d --debug \ + -h --help -l --list -M --metadatatype -t --test \ + -v --verbose --version' -- $cur ) ) + else + _args + if [ $args -eq 0 -o $args -eq 1 ]; then + _volumegroups + else + _physicalvolumes + fi + fi +} +complete -F _vgsplit vgsplit + +_vgmknodes() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-d --debug -h --help -v --verbose \ + --version' -- $cur ) ) + else + _volumegroups + fi +} +complete -F _vgmknodes vgmknodes + +_lvscan() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-b --blockdevice -d --debug \ + -h -? --help --ignorelockingfailure -P \ + --partial -v --verbose --version' -- $cur ) ) + fi +} +complete -F _lvscan lvscan + +_lvs() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(o|O|-options|-sort)) + COMPREPLY=( $( compgen -W 'lv_uuid lv_name \ + lv_attr lv_minor lv_size seg_count \ + origin snap_percent segtype stripes \ + stripesize chunksize seg_start \ + seg_size' -- $cur ) ) + return 0 + ;; + --units) + _units + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--aligned -d --debug \ + -h --help --ignorelockingfailure --noheadings \ + --nosuffix -o --options -O --sort -P --partial \ + --segments --separator --unbuffered --units \ + -v --verbose --version' -- $cur ) ) + else + _logicalvolumes + fi +} +complete -F _lvs lvs + +_lvdisplay() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + --units) + _units + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-c --colon -C --columns --units \ + -P --partial -m --maps -v --verbose -d --debug -h \ + --help --version' -- $cur ) ) + else + _logicalvolumes + fi +} +complete -F _lvdisplay lvdisplay + +_lvchange() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(a|A|C|M|-available|-autobackup|-continguous|-persistent)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + -@(p|-permission)) + COMPREPLY=( $( compgen -W 'r rw' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup -a --available \ + --addtag --alloc -C --contiguous -d --debug --deltag \ + -f --force -h --help --ignorelockingfailure -M \ + --persistent --major major --minor minor -P --partial \ + -p --permission -r --readahead --refresh -t --test \ + -v --verbose --version' -- $cur ) ) + else + _logicalvolumes + fi +} +complete -F _lvchange lvchange + +_lvcreate() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|C|M|Z|-autobackup|-continguous|-persistent|-zero)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + -@(L|-size)) + _sizes + return 0 + ;; + -@(p|-permission)) + COMPREPLY=( $( compgen -W 'r rw' -- $cur ) ) + return 0 + ;; + -@(n|-name)) + _logicalvolumes + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup --addtag --alloc \ + -C --contiguous -d --debug -h -? --help -i --stripes \ + -I --stripesize -l --extents -L --size -M --persistent \ + --major --minor -n --name -p --permission -r \ + --readahead -t --test --type -v --verbose -Z --zero \ + --version' -- $cur ) ) + else + _args + if [ $args -eq 0 ]; then + _volumegroups + else + _physicalvolumes + fi + fi +} +complete -F _lvcreate lvcreate + +_lvremove() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -f \ + --force -h -? --help -t --test -v --verbose \ + --version' -- $cur ) ) + else + _logicalvolumes + fi +} +complete -F _lvremove lvremove + +_lvrename() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup -d --debug -h \ + -? --help -t --test -v --verbose --version' -- $cur ) ) + else + _logicalvolumes + fi +} +complete -F _lvrename lvrename + +_lvreduce() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + -@(L|-size)) + _sizes + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup -d \ + --debug -f --force -h --help -l --extents \ + -L --size -n --nofsck -r --resizefs -t --test \ + -v --verbose --version' -- $cur ) ) + else + _logicalvolumes + fi +} +complete -F _lvreduce lvreduce + +_lvresize() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + -@(L|-size)) + _sizes + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup --alloc -d \ + --debug -h --help -i --stripes -I --stripesize \ + -l --extents -L --size -n --nofsck -r --resizefs \ + -t --test --type -v --verbose --version' -- $cur ) ) + else + _args + if [ $args -eq 0 ]; then + _logicalvolumes + else + _physicalvolumes + fi + fi +} +complete -F _lvresize lvresize + +_lvextend() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -@(A|-autobackup)) + COMPREPLY=( $( compgen -W 'y n' -- $cur ) ) + return 0 + ;; + -@(L|-size)) + _sizes + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-A --autobackup --alloc -d \ + --debug -h --help -i --stripes -I --stripesize \ + -l --extents -L --size -n --nofsck -r --resizefs \ + -t --test --type -v --verbose --version' -- $cur ) ) + else + _args + if [ $args -eq 0 ]; then + _logicalvolumes + else + _physicalvolumes + fi + fi +} +complete -F _lvextend lvextend + +_lvm() +{ + local prev + + COMPREPLY=() + cur=`_get_cword` + + if [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $( compgen -W 'dumpconfig help lvchange \ + lvcreate lvdisplay lvextend lvmchange \ + lvmdiskscan lvmsadc lvmsar lvreduce \ + lvremove lvrename lvresize lvs lvscan \ + pvchange pvcreate pvdata pvdisplay pvmove \ + pvremove pvresize pvs pvscan vgcfgbackup \ + vgcfgrestore vgchange vgck vgconvert \ + vgcreate vgdisplay vgexport vgextend \ + vgimport vgmerge vgmknodes vgreduce \ + vgremove vgrename vgs vgscan vgsplit \ + version' -- $cur ) ) + else + case ${COMP_WORDS[1]} in + pvchange) + _pvchange + ;; + pvcreate) + _pvcreate + ;; + pvdisplay) + _pvdisplay + ;; + pvmove) + _pvmove + ;; + pvremove) + _pvremove + ;; + pvresize) + _pvresize + ;; + pvs) + _pvs + ;; + pvscan) + _pvscan + ;; + vgcfgbackup) + _vgcfgbackup + ;; + vgcfgrestore) + _vgcfgrestore + ;; + vgchange) + _vgchange + ;; + vgck) + _vgck + ;; + vgconvert) + _vgconvert + ;; + vgcreate) + _vgcreate + ;; + vgdisplay) + _vgdisplay + ;; + vgexport) + _vgexport + ;; + vgextend) + _vgextend + ;; + vgimport) + _vgimport + ;; + vgmerge) + _vgmerge + ;; + vgmknodes) + _vgmknodes + ;; + vgreduce) + _vgreduce + ;; + vgremove) + _vgremove + ;; + vgrename) + _vgrename + ;; + vgs) + _vgs + ;; + vgscan) + _vgscan + ;; + vgsplit) + _vgsplit + ;; + lvchange) + _lvchange + ;; + lvcreate) + _lvcreate + ;; + lvdisplay) + _lvdisplay + ;; + lvextend) + _lvextend + ;; + lvreduce) + _lvreduce + ;; + lvremove) + _lvremove + ;; + lvrename) + _lvrename + ;; + lvresize) + _lvresize + ;; + lvs) + _lvs + ;; + lvscan) + _lvscan + ;; + esac + fi +} +complete -F _lvm lvm +} + +# mkinitrd(8) completion +# +have mkinitrd && +_mkinitrd() +{ + local cur args + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + # --name value style option + case "$prev" in + --preload) + _modules + return 0 + ;; + esac + + # --name=value style option + if [[ "$cur" == *=* ]]; then + prev=${cur/=*/} + cur=${cur/*=/} + case "$prev" in + --@(with|builtin)) + _modules + return 0 + ;; + --@(fstab|dsdt)) + _filedir + return 0 + ;; + --tmpdir) + _filedir -d + return 0 + ;; + esac + fi + + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--version -v -f --preload \ + --with= --omit-scsi-modules --omit-raid-modules \ + --images-version --fstab= --nocompress --builtin= \ + --nopivot --noudev --allow-missing --tmpdir= \ + --initrdfs= --dsdt= --lvm-version= --froce-usb' \ + -- $cur ) ) + else + _count_args + + case $args in + 1) + _filedir + ;; + 2) + COMPREPLY=( $( command ls /lib/modules | grep "^$cur" ) ) + ;; + esac + fi + +} && +complete -F _mkinitrd mkinitrd + +# pkgconfig(1) completion +# +have pkg-config && +_pkg_config() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + # return list of available options + COMPREPLY=( $( compgen -W '-version --modversion \ + --atleast-pkgconfig-version= --libs --libs-only-l \ + --libs-only-other --libs-only-L --cflags \ + --cflags-only-I --cflags-only-other --variable= \ + --define-variable= --exists --uninstalled \ + --atleast-version= --exact-version= --max-version= \ + --list-all --debug --print-errors --silence-errors \ + --errors-to-stdout -? --help --usage' -- $cur)) + else + COMPREPLY=( $( pkg-config --list-all 2>/dev/null | \ + awk '{print $1}' | grep "^$cur" ) ) + fi +} && +complete -F _pkg_config pkg-config + + +# cpio(1) completion +# +have cpio && { +_cpio_format() +{ + COMPREPLY=( $( compgen -W 'bin odc newc crc tar ustar hpbin hpodc' -- $cur ) ) +} + +_cpio() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + # --name value style option + case $prev in + -H) + _cpio_format + return 0 + ;; + -@(E|F|I)) + _filedir + return 0 + ;; + -R) + _usergroup + return 0 + ;; + esac + + # --name=value style option + if [[ "$cur" == *=* ]]; then + prev=${cur/=*/} + cur=${cur/*=/} + case $prev in + --format) + _cpio_format + return 0 + ;; + --@(file|pattern-file)) + _filedir + return 0 + ;; + --owner) + _usergroup + return 0 + ;; + --rsh-command) + COMPREPLY=( $( compgen -c -- $cur ) ) + return 0 + ;; + esac + fi + + if [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $( compgen -W '-o --create -i --extract -p --pass-through' -- $cur) ) + else + case ${COMP_WORDS[1]} in + -@(o|-create)) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-0 -a -c -v -A -B\ + -L -V -C -H -M -O -F --file= --format=\ + --message= --null --reset-access-time\ + --verbose --dot --append --block-size=\ + --dereference --io-size= --quiet\ + --force-local --rsh-command= --help\ + --version' -- $cur ) ) + fi + ;; + -@(i|-extract)) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-b -c -d -f -m -n -r\ + -t -s -u -v -B -S -V -C -E -H -M -R -I\ + -F --file= --make-directories\ + --nonmatching\ + --preserve-modification-time\ + --numeric-uid-gid --rename -t --list\ + --swap-bytes --swap --dot\ + --unconditional --verbose --block-size=\ + --swap-halfwords --io-size=\ + --pattern-file= --format= --owner=\ + --no-preserve-owner --message=\ + --force-local --no-absolute-filenames\ + --sparse --only-verify-crc --quiet\ + --rsh-command= --help\ + --version' -- $cur ) ) + fi + ;; + -@(p|-pass-through)) + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-0 -a -d -l -m -u -v\ + -L -V -R --null --reset-access-time\ + --make-directories --link --quiet\ + --preserve-modification-time\ + --unconditional --verbose --dot\ + --dereference --owner=\ + --no-preserve-owner --sparse --help\ + --version' -- $cur ) ) + else + _filedir -d + fi + ;; + esac + fi +} +complete -F _cpio cpio +} + +# id(1) completion +# +have id && +_id() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-a -g --group -G --groups -n --name\ + -r --real -u --user --help --version' -- $cur ) ) + else + COMPREPLY=( $( compgen -u $cur ) ) + fi +} && +complete -F _id id + +# getent(1) completion +# +have getent && +_getent() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case $prev in + passwd) + COMPREPLY=( $( compgen -u $cur ) ) + return 0 + ;; + group) + COMPREPLY=( $( compgen -g $cur ) ) + return 0 + ;; + services) + COMPREPLY=( $( compgen -s $cur ) ) + return 0 + ;; + hosts) + COMPREPLY=( $( compgen -A hostname $cur ) ) + return 0 + ;; + protocols) + COMPREPLY=( $( getent protocols | awk '{print $1}' | grep "^$cur" ) ) + return 0 + ;; + networks) + COMPREPLY=( $( getent networks | awk '{print $1}' | grep "^$cur" ) ) + return 0 + ;; + esac + + + if [ $COMP_CWORD -eq 1 ]; then + COMPREPLY=( $( compgen -W 'passwd group hosts services protocols networks' -- $cur ) ) + fi +} && +complete -F _getent getent + +# ntpdate(1) completion +# +have ntpdate && +_ntpdate() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case $prev in + -k) + _filedir + return 0 + ;; + -U) + COMPREPLY=( $( compgen -u $cur ) ) + return 0 + ;; + esac + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-4 -6 -b -B -d -Q -q -s -u -v -a\ + -e -k -p -o -r -t' -- $cur ) ) + else + _known_hosts + fi +} && +complete -F _ntpdate ntpdate + +# smartctl(8) completion +# +have smartctl && { +_smartctl_quietmode() +{ + COMPREPLY=( $( compgen -W 'errorsonly silent' -- $cur ) ) +} +_smartctl_device() +{ + COMPREPLY=( $( compgen -W 'ata scsi 3ware' -- $cur ) ) +} +_smartctl_tolerance() +{ + COMPREPLY=( $( compgen -W 'warn exit ignore' -- $cur ) ) +} +_smartctl_badsum() +{ + COMPREPLY=( $( compgen -W 'normal conservative permissive verypermissive' -- $cur ) ) +} +_smartctl_report() +{ + COMPREPLY=( $( compgen -W 'ioctl ataioctl scsiioctl' -- $cur ) ) +} +_smartctl_feature() +{ + COMPREPLY=( $( compgen -W 'on off' -- $cur ) ) +} +_smartctl_log() +{ + COMPREPLY=( $( compgen -W 'error selftest selective directory' -- $cur ) ) +} +_smartctl_vendorattribute() +{ + COMPREPLY=( $( compgen -W 'help 9,minutes 9,seconds 9,halfminutes \ + 9,temp 192,emergencyretractcyclect 193,loadunload \ + 194,10xCelsius 194,unknown 198,offlinescanuncsectorct \ + 200,writeerrorcount 201,detectedtacount 220,temp' -- $cur ) ) +} +_smartctl_firmwarebug() +{ + COMPREPLY=( $( compgen -W 'none samsung samsung2' -- $cur ) ) +} +_smartctl_presets() +{ + COMPREPLY=( $( compgen -W 'use ignore show showall' -- $cur ) ) +} +_smartctl_test() +{ + COMPREPLY=( $( compgen -W 'offline short long conveyance select afterselect,on afterselect,off pending' -- $cur ) ) +} + +_smartctl() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + # --name value style option + case "$prev" in + -q) + _smartctl_quietmode + ;; + -d) + _smartctl_device + return 0 + ;; + -t) + _smartctl_tolerance + return 0 + ;; + -b) + _smartctl_badsum + return 0 + ;; + -r) + _smartctl_report + return 0 + ;; + -s) + _smartctl_feature + return 0 + ;; + -o) + _smartctl_feature + return 0 + ;; + -S) + _smartctl_feature + return 0 + ;; + -l) + _smartctl_log + return 0 + ;; + -v) + _smartctl_vendorattribute + return 0 + ;; + -F) + _smartctl_firmwarebug + return 0 + ;; + -P) + _smartctl_presets + return 0 + ;; + -t) + _smartctl_test + return 0 + ;; + esac + + # --name=value style option + if [[ "$cur" == *=* ]]; then + prev=${cur/=*/} + cur=${cur/*=/} + case "$prev" in + --quietmode) + _smartctl_quietmode + return 0 + ;; + --device) + _smartctl_device + return 0 + ;; + --tolerance) + _smartctl_tolerance + return 0 + ;; + --badsum) + _smartctl_badsum + return 0 + ;; + --report) + _smartctl_report + return 0 + ;; + --smart) + _smartctl_feature + return 0 + ;; + --offlineauto) + _smartctl_feature + return 0 + ;; + --saveauto) + _smartctl_feature + return 0 + ;; + --log) + _smartctl_log + return 0 + ;; + --vendorattribute) + _smartctl_vendorattribute + return 0 + ;; + --firmwarebug) + _smartctl_firmwarebug + return 0 + ;; + --presets) + _smartctl_presets + return 0 + ;; + --test) + _smartctl_test + return 0 + ;; + esac + fi + + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-h --help --usage -V --version \ + --copyright --license-i --info -a --all -q \ + --quietmode= -d --device= -T --tolerance= -b --badsum= \ + -r --report= -s --smart= -o --offlineauto= -S \ + --saveauto= -H --health -c --capabilities -A \ + --attributes -l --log= -v --vendorattribute= -F \ + --firmwarebug= -P --presets= -t --test= -C \ + --captive -X --abort' -- $cur ) ) + else + cur=${cur:=/dev/} + _filedir + fi +} +complete -F _smartctl smartctl +} + +# vncviewer(1) completion +# +have vncviewer && +_vncviewer() +{ + local cur prev + local -a config + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case "$prev" in + -via) + _known_hosts -a + ;; + *) + # ssh into the the server, find and ping the broadcast address, then + # sort and show the results. + COMPREPLY=( $( ssh -o 'Batchmode yes' $prev \ + "ping -bnc 4 255.255.255.255" 2>/dev/null | \ + awk -F ' ' '{print $4}' | \ + sort -n | uniq | egrep '[0-9]+\.[0-9]+\.' 2>/dev/null ) ) + esac + + return 0 +} && +complete -F _vncviewer vncviewer + +# sysctl(8) completion +# +have sysctl && +_sysctl() +{ + local cur + + COMPREPLY=() + cur=`_get_cword` + + COMPREPLY=( $( compgen -W "$(sysctl -N -a 2>/dev/null)" -- $cur ) ) + + return 0 +} && +complete -F _sysctl sysctl + +# update-rc.d(8) completion +# +# Copyright (C) 2004 Servilio Afre Puentes <servilio@gmail.com> +# +have update-rc.d && +_update_rc_d() +{ + local cur prev sysvdir services options valid_options + + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d \ + || sysvdir=/etc/init.d + + services=( $(echo $sysvdir/!(README*|*.sh|*.dpkg*|*.rpm*)) ) + services=( ${services[@]#$sysvdir/} ) + options=( -f -n ) + + if [[ $COMP_CWORD -eq 1 || "$prev" == -* ]]; then + valid_options=( $( \ + echo "${COMP_WORDS[@]} ${options[@]}" \ + | tr " " "\n" \ + | sed -ne "/$( echo "${options[@]}" | sed "s/ /\\|/g" )/p" \ + | sort | uniq -u \ + ) ) + COMPREPLY=( $( compgen -W '${options[@]} ${services[@]}' \ + -X '$( echo ${COMP_WORDS[@]} | tr " " "|" )' -- $cur ) ) + elif [[ "$prev" == ?($( echo ${services[@]} | tr " " "|" )) ]]; then + COMPREPLY=( $( compgen -W 'remove defaults start stop' -- $cur ) ) + elif [[ "$prev" == defaults && "$cur" == [0-9] ]]; then + COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 ) + elif [[ "$prev" == defaults && "$cur" == [sk]?([0-9]) ]]; then + COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 ) + elif [[ "$prev" == defaults && -z "$cur" ]]; then + COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 s k ) + elif [[ "$prev" == ?(start|stop) ]]; then + if [[ "$cur" == [0-9] || -z "$cur" ]]; then + COMPREPLY=( 0 1 2 3 4 5 6 7 8 9 ) + elif [[ "$cur" == [0-9][0-9] ]]; then + COMPREPLY=( $cur ) + else + COMPREPLY=() + fi + elif [[ "$prev" == ?([0-9][0-9]|[0-6S]) ]]; then + if [[ -z "$cur" ]]; then + if [[ $prev == [0-9][0-9] ]]; then + COMPREPLY=( 0 1 2 3 4 5 6 S ) + else + COMPREPLY=( 0 1 2 3 4 5 6 S . ) + fi + elif [[ "$cur" == [0-6S.] ]]; then + COMPREPLY=( $cur ) + else + COMPREPLY=() + fi + elif [[ "$prev" == "." ]]; then + COMPREPLY=( $(compgen -W "start stop" -- $cur) ) + else + COMPREPLY=() + fi + + return 0 +} && +complete -F _update_rc_d update-rc.d + +# invoke-rc.d(8) completion +# +# Copyright (C) 2004 Servilio Afre Puentes <servilio@gmail.com> +# +have invoke-rc.d && +_invoke_rc_d() +{ + local cur prev sysvdir services options valid_options + + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + [ -d /etc/rc.d/init.d ] && sysvdir=/etc/rc.d/init.d \ + || sysvdir=/etc/init.d + + services=( $(echo $sysvdir/!(README*|*.sh|*.dpkg*|*.rpm*)) ) + services=( ${services[@]#$sysvdir/} ) + options=( --help --quiet --force --try-anyway --disclose-deny --query --no-fallback ) + + if [[ ($COMP_CWORD -eq 1) || ("$prev" == --* ) ]]; then + valid_options=( $( \ + echo ${COMP_WORDS[@]} ${options[@]} \ + | tr " " "\n" \ + | sed -ne "/$( echo ${options[@]} | sed "s/ /\\\\|/g" )/p" \ + | sort | uniq -u \ + ) ) + COMPREPLY=( $( compgen -W '${valid_options[@]} ${services[@]}' -- \ + $cur ) ) + elif [ -x $sysvdir/$prev ]; then + COMPREPLY=( $( compgen -W '`sed -ne "y/|/ /; \ + s/^.*Usage:[ ]*[^ ]*[ ]*{*\([^}\"]*\).*$/\1/p" \ + $sysvdir/$prev`' -- \ + $cur ) ) + else + COMPREPLY=() + fi + + return 0 +} && +complete -F _invoke_rc_d invoke-rc.d + +# minicom(1) completion +# +have minicom && +_minicom() +{ + local cur prev + + COMPREPLY=() + cur=`_get_cword` + prev=${COMP_WORDS[COMP_CWORD-1]} + + case $prev in + -@(a|c)) + COMPREPLY=( $( compgen -W 'on off' -- $cur ) ) + return 0 + ;; + -@(S|C)) + _filedir + return 0 + ;; + -P) + COMPREPLY=( $( command ls /dev/tty* ) ) + COMPREPLY=( $( compgen -W '${COMPREPLY[@]} ${COMPREPLY[@]#/dev/}' -- $cur ) ) + return 0 + ;; + esac + + + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '-s -o -m -M -z -l -L -w -a -t \ + -c -S -d -p -C -T -8' -- $cur ) ) + else + COMPREPLY=( $( command ls /etc/minicom/minirc.* 2>/dev/null | sed -e 's|/etc/minicom/minirc.||' | grep "^$cur" ) ) + fi +} && +complete -F _minicom minicom + +# svn completion +# +have svn && +{ +_svn() +{ + local cur prev commands options command + + COMPREPLY=() + cur=`_get_cword` + + commands='add blame praise annotate ann cat checkout co cleanup commit \ + ci copy cp delete del remove rm diff di export help ? h import \ + info list ls lock log merge mkdir move mv rename ren \ + propdel pdel pd propedit pedit pe propget pget pg \ + proplist plist pl propset pset ps resolved revert \ + status stat st switch sw unlock update up' + + if [[ $COMP_CWORD -eq 1 ]] ; then + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--version' -- $cur ) ) + else + COMPREPLY=( $( compgen -W "$commands" -- $cur ) ) + fi + else + + prev=${COMP_WORDS[COMP_CWORD-1]} + case $prev in + --config-dir) + _filedir -d + return 0; + ;; + -@(F|-file|-targets)) + _filedir + return 0; + ;; + --encoding) + COMPREPLY=( $( compgen -W \ + '$( iconv --list | sed -e "s@//@@;" )' \ + -- "$cur" ) ) + return 0; + ;; + --@(editor|diff|diff3)-cmd) + COMP_WORDS=(COMP_WORDS[0] $cur) + COMP_CWORD=1 + _command + return 0; + ;; + esac + + command=${COMP_WORDS[1]} + + if [[ "$cur" == -* ]]; then + # possible options for the command + case $command in + add) + options='--auto-props --no-auto-props \ + --force --targets --no-ignore \ + --non-recursive -N -q --quiet' + ;; + @(blame|annotate|ann|praise)) + options='-r --revisions --username \ + --password --no-auth-cache \ + --non-interactive -v \ + --verbose --incremental --xml' + ;; + cat) + options='-r --revision --username \ + --password --no-auth-cache \ + --non-interactive' + ;; + @(checkout|co)) + options='-r --revision -q --quiet -N \ + --non-recursive --username \ + --password --no-auth-cache \ + --non-interactive \ + --ignore-externals' + ;; + cleanup) + options='--diff3-cmd' + ;; + @(commit|ci)) + options='-m --message -F --file \ + --encoding --force-log -q \ + --quiet --non-recursive -N \ + --targets --editor-cmd \ + --username --password \ + --no-auth-cache \ + --non-interactive --no-unlock' + ;; + @(copy|cp)) + options='-m --message -F --file \ + --encoding --force-log -r \ + --revision -q --quiet \ + --editor-cmd -username \ + --password --no-auth-cache \ + --non-interactive' + ;; + @(delete|del|remove|rm)) + options='--force -m --message -F \ + --file --encoding --force-log \ + -q --quiet --targets \ + --editor-cmd -username \ + --password --no-auth-cache \ + --non-interactive' + ;; + @(diff|di)) + options='-r --revision -x --extensions \ + --diff-cmd --no-diff-deleted \ + -N --non-recursive --username \ + --password --no-auth-cache \ + --non-interactive --force \ + --old --new --notice-ancestry' + ;; + export) + options='-r --revision -q --quiet \ + --username --password \ + --no-auth-cache \ + --non-interactive -N \ + --non-recursive --force \ + --native-eol --ignore-externals' + ;; + import) + options='--auto-props --no-auto-props \ + -m --message -F --file \ + --encoding --force-log -q \ + --quiet --non-recursive \ + --no-ignore --editor-cmd \ + --username --password \ + --no-auth-cache \ + --non-interactive' + ;; + info) + options='--username --password \ + --no-auth-cache \ + --non-interactive -r \ + --revision --xml --targets \ + -R --recursive --incremental' + ;; + @(list|ls)) + options='-r --revision -v --verbose -R \ + --recursive --username \ + --password --no-auth-cache \ + --non-interactive \ + --incremental --xml' + ;; + lock) + options='-m --message -F --file \ + --encoding --force-log \ + --targets --force --username \ + --password --no-auth-cache \ + --non-interactive' + ;; + log) + options='-r --revision -v --verbose \ + --targets --username \ + --password --no-auth-cache \ + --non-interactive \ + --stop-on-copy --incremental \ + --xml -q --quiet --limit' + ;; + merge) + options='-r --revision -N \ + --non-recursive -q --quiet \ + --force --dry-run --diff3-cmd \ + --username --password \ + --no-auth-cache \ + --non-interactive \ + --ignore-ancestry' + ;; + mkdir) + options='-m --message -F --file \ + --encoding --force-log -q \ + --quiet --editor-cmd \ + --username --password \ + --no-auth-cache \ + --non-interactive' + ;; + @(move|mv|rename|ren)) + options='-m --message -F --file \ + --encoding --force-log -r \ + --revision -q --quiet \ + --force --editor-cmd \ + --username --password \ + --no-auth-cache \ + --non-interactive' + ;; + @(propdel|pdel|pd)) + options='-q --quiet -R --recursive -r \ + --revision --revprop \ + --username --password \ + --no-auth-cache \ + --non-interactive' + ;; + @(propedit|pedit|pe)) + options='-r --revision --revprop \ + --encoding --editor-cmd \ + --username --password \ + --no-auth-cache \ + --non-interactive --force' + ;; + @(propget|pget|pg)) + options='-R --recursive -r --revision \ + --revprop --strict --username \ + --password --no-auth-cache \ + --non-interactive' + ;; + @(proplist|plist|pl)) + options='-v --verbose -R --recursive \ + -r --revision --revprop -q \ + --quiet --username --password \ + --no-auth-cache \ + --non-interactive' + ;; + @(propset|pset|ps)) + options='-F --file -q --quiet \ + --targets -R --recursive \ + --revprop --encoding \ + --username --password \ + --no-auth-cache \ + --non-interactive -r \ + --revision --force' + ;; + resolved) + options='--targets -R --recursive -q \ + --quiet' + ;; + revert) + options='--targets -R --recursive -q \ + --quiet' + ;; + @(status|stat|st)) + options='-u --show-updates -v \ + --verbose -N --non-recursive \ + -q --quiet --username \ + --password --no-auth-cache \ + --non-interactive --no-ignore \ + --ignore-externals \ + --incremental --xml' + ;; + @(switch|sw)) + options='--relocate -r --revision -N \ + --non-recursive -q --quiet \ + --username --password \ + --no-auth-cache \ + --non-interactive --diff3-cmd' + ;; + unlock) + options='--targets --force --username \ + --password --no-auth-cache \ + --non-interactive' + ;; + @(update|up)) + options='-r --revision -N \ + --non-recursive -q --quiet \ + --username --password \ + --no-auth-cache \ + --non-interactive \ + --diff3-cmd --ignore-externals' + ;; + esac + options="$options --help -h --config-dir" + + COMPREPLY=( $( compgen -W "$options" -- $cur ) ) + else + if [[ "$command" == @(help|h|\?) ]]; then + COMPREPLY=( $( compgen -W "$commands" -- $cur ) ) + else + _filedir + fi + fi + fi + + return 0 +} +complete -F _svn $default svn + +_svnadmin() +{ + local cur prev commands options mode + + COMPREPLY=() + cur=`_get_cword` + + commands='create deltify dump help ? hotcopy list-dblogs \ + list-unused-dblogs load lslocks lstxns recover rmlocks \ + rmtxns setlog verify' + + if [[ $COMP_CWORD -eq 1 ]] ; then + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--version' -- $cur ) ) + else + COMPREPLY=( $( compgen -W "$commands" -- $cur ) ) + fi + else + prev=${COMP_WORDS[COMP_CWORD-1]} + case $prev in + --config-dir) + _filedir -d + return 0; + ;; + --fs-type) + COMPREPLY=( $( compgen -W 'fsfs bdb' -- $cur ) ) + return 0; + ;; + esac + + command=${COMP_WORDS[1]} + + if [[ "$cur" == -* ]]; then + # possible options for the command + case $command in + create) + options='--bdb-txn-nosync \ + --bdb-log-keep --config-dir \ + --fs-type' + ;; + deltify) + options='-r --revision -q --quiet' + ;; + dump) + options='-r --revision --incremental \ + -q --quiet --deltas' + ;; + hotcopy) + options='--clean-logs' + ;; + load) + options='--ignore-uuid --force-uuid \ + --parent-dir -q --quiet \ + --use-pre-commit-hook \ + --use-post-commit-hook' + ;; + rmtxns) + options='-q --quiet' + ;; + setlog) + options='-r --revision --bypass-hooks' + ;; + esac + + options="$options --help -h" + COMPREPLY=( $( compgen -W "$options" -- $cur ) ) + else + if [[ "$command" == @(help|h|\?) ]]; then + COMPREPLY=( $( compgen -W "$commands" -- $cur ) ) + else + _filedir + fi + fi + fi + + return 0 +} +complete -F _svnadmin $default svnadmin + +_svnlook() +{ + local cur prev commands options mode + + COMPREPLY=() + cur=`_get_cword` + + commands='author cat changed date diff dirs-changed help ? h history \ + info lock log propget pget pg proplist plist pl tree uuid \ + youngest' + + if [[ $COMP_CWORD -eq 1 ]] ; then + if [[ "$cur" == -* ]]; then + COMPREPLY=( $( compgen -W '--version' -- $cur ) ) + else + COMPREPLY=( $( compgen -W "$commands" -- $cur ) ) + fi + else + command=${COMP_WORDS[1]} + + if [[ "$cur" == -* ]]; then + # possible options for the command + case $command in + @(author|cat|date|dirs-changed|info|log)) + options='-r --revision -t \ + --transaction' + ;; + changed) + options='-r --revision -t \ + --transaction --copy-info' + ;; + diff) + options='-r --revision -t \ + --transaction \ + --no-diff-deleted \ + --no-diff-added \ + --diff-copy-from' + ;; + history) + options='-r --revision --show-ids' + ;; + prop@(get|list)) + options='-r --revision -t \ + --transaction --revprop' + ;; + tree) + options='-r --revision -t \ + --transaction --show-ids \ + --full-paths' + ;; + esac + + options="$options --help -h" + COMPREPLY=( $( compgen -W "$options" -- $cur ) ) + else + if [[ "$command" == @(help|h|\?) ]]; then + COMPREPLY=( $( compgen -W "$commands" -- $cur ) ) + else + _filedir + fi + fi + fi + + return 0 +} +complete -F _svnlook $default svnlook +} + +_filedir_xspec() +{ + local IFS cur xspec + + IFS=$'\t\n' + COMPREPLY=() + cur=`_get_cword` + + _expand || return 0 + + # get first exclusion compspec that matches this command + xspec=$( sed -ne $'/^complete .*[ \t]'${1##*/}$'\([ \t]\|$\)/{p;q;}' \ + $BASH_COMPLETION ) + # prune to leave nothing but the -X spec + xspec=${xspec#*-X } + xspec=${xspec%% *} + + local toks=( ) tmp + + while read -r tmp; do + [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp + done < <( compgen -d -- "$(quote_readline "$cur")" ) + + while read -r tmp; do + [[ -n $tmp ]] && toks[${#toks[@]}]=$tmp + done < <( eval compgen -f -X $xspec -- "\$(quote_readline "\$cur")" ) + + COMPREPLY=( "${toks[@]}" ) +} +list=( $( sed -ne '/^# START exclude/,/^# FINISH exclude/p' \ + $BASH_COMPLETION | \ + # read exclusion compspecs + ( + while read line + do + # ignore compspecs that are commented out + if [ "${line#\#}" != "$line" ]; then continue; fi + line=${line%# START exclude*} + line=${line%# FINISH exclude*} + line=${line##*\'} + list=( "${list[@]}" $line ) + done + echo "${list[@]}" + ) + ) ) +# remove previous compspecs +if [ ${#list[@]} -gt 0 ]; then + eval complete -r ${list[@]} + # install new compspecs + eval complete -F _filedir_xspec $filenames "${list[@]}" +fi +unset list + +# source completion directory definitions +if [ -d $BASH_COMPLETION_DIR -a -r $BASH_COMPLETION_DIR -a \ + -x $BASH_COMPLETION_DIR ]; then + for i in $BASH_COMPLETION_DIR/*; do + [[ ${i##*/} != @(*~|*.bak|*.swp|\#*\#|*.dpkg*|.rpm*) ]] && + [ \( -f $i -o -h $i \) -a -r $i ] && . $i + done +fi +unset i + +# source user completion file +[ $BASH_COMPLETION != ~/.bash_completion -a -r ~/.bash_completion ] \ + && . ~/.bash_completion +unset -f have +unset UNAME RELEASE default dirnames filenames have nospace bashdefault \ + plusdirs + +set $BASH_COMPLETION_ORIGINAL_V_VALUE +unset BASH_COMPLETION_ORIGINAL_V_VALUE + +### Local Variables: +### mode: shell-script +### End: diff --git a/usr/src/cmd/nsadmin/bash/bash_completion.d/dladm b/usr/src/cmd/nsadmin/bash/bash_completion.d/dladm new file mode 100644 index 0000000000..179227b2a2 --- /dev/null +++ b/usr/src/cmd/nsadmin/bash/bash_completion.d/dladm @@ -0,0 +1,34 @@ +_dladm() +{ + local cur prev opts base + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + if [[ ${prev} == 'dladm' ]]; then + local cmds="rename-link show-link create-aggr delete-aggr add-aggr \ + remove-aggr modify-aggr show-aggr scan-wifi connect-wifi \ + disconnect-wifi show-wifi set-linkprop reset-linkprop \ + show-linkprop show-ether create-secobj delete-secobj show-secobj \ + create-vlan delete-vlan show-vlan create-iptun delete-iptun \ + modify-iptun show-iptun delete-phys show-phys create-vnic \ + delete-vnic show-vnic create-part delete-part show-part show-ib \ + create-etherstub delete-etherstub show-etherstub create-bridge \ + modify-bridge delete-bridge add-bridge remove-bridge show-bridge \ + show-bridge show-usage" + + COMPREPLY=( $(compgen -W "$cmds" -- ${cur}) ) + # The -z takes a zone option, ignore everything else + elif [[ ${prev} =~ -.*z$ ]]; then + local zones=$(zoneadm list -c | grep -v '^global$') + COMPREPLY=( $(compgen -W "${zones}" -- ${cur}) ) + elif [[ ${prev} =~ 'delete-vnic' ]]; then + local vnics=$(dladm show-vnic -p -o link) + COMPREPLY=( $(compgen -W "${vnics}" -- ${cur}) ) + elif [[ ${prev} =~ 'delete-etherstub' ]]; then + local stubs=$(dladm show-etherstub -p) + COMPREPLY=( $(compgen -W "${stubs}" -- ${cur}) ) + fi +} + +complete -F _dladm dladm diff --git a/usr/src/cmd/nsadmin/bash/bash_completion.d/machines b/usr/src/cmd/nsadmin/bash/bash_completion.d/machines new file mode 100644 index 0000000000..05de1b75a4 --- /dev/null +++ b/usr/src/cmd/nsadmin/bash/bash_completion.d/machines @@ -0,0 +1,19 @@ +_machine() +{ + local cur prev opts base + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + if [[ ${prev} == 'machine-json' ]]; then + machines=$(zoneadm list -cp | grep -v ':global:' | cut -d':' -f2,5 | tr ':' '\n' | sort | uniq) + COMPREPLY=( $(compgen -W "${machines}" -- ${cur}) ) + else + # Just expand files by default + COMPREPLY=( $(compgen -f -- ${cur}) ) + fi + + return 0 +} + +complete -F _machine machine-json diff --git a/usr/src/cmd/nsadmin/bash/bash_completion.d/vms b/usr/src/cmd/nsadmin/bash/bash_completion.d/vms new file mode 100644 index 0000000000..71bcac5994 --- /dev/null +++ b/usr/src/cmd/nsadmin/bash/bash_completion.d/vms @@ -0,0 +1,62 @@ +_vmadm() +{ + local cur prev opts base + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + up_patterns=$(printf " running *\$\n halting *\$\n") + + if [[ ${prev} == 'vmadm' ]]; then + COMPREPLY=( $(compgen -W "boot destroy dump kill halt list reboot reset info nmi" -- ${cur}) ) + else + case ${prev} in + boot) + local not_running=$(vmadm list -v | grep -v "^UUID" | \ + /usr/xpg4/bin/grep -v -e "${up_patterns}" | cut -d' ' -f1) + COMPREPLY=( $(compgen -W "${not_running}" -- ${cur}) ) + ;; + info|kill) + local running=$(vmadm list -v | grep -v "^UUID" | \ + /usr/xpg4/bin/grep -e "${up_patterns}" | cut -d ' ' -f1) + COMPREPLY=( $(compgen -W "${running}" -- ${cur}) ) + ;; + halt|reboot|reset|nmi|screenshot) + local running=$(vmadm list -v | grep -v "^UUID" | \ + grep " running *$" | cut -d ' ' -f1) + COMPREPLY=( $(compgen -W "${running}" -- ${cur}) ) + ;; + dump|destroy) + local all=$(vmadm list) + COMPREPLY=( $(compgen -W "${all}" -- ${cur}) ) + ;; + *) + # Just expand files by default + COMPREPLY=( $(compgen -f -- ${cur}) ) + ;; + esac + fi + + return 0 +} + +_vmcfg() +{ + local cur prev opts base + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + if [[ ${prev} == 'vmcfg' ]]; then + vms=$(vmadm list) + COMPREPLY=( $(compgen -W "${vms}" -- ${cur}) ) + else + # Just expand files by default + COMPREPLY=( $(compgen -f -- ${cur}) ) + fi + + return 0 +} + +complete -F _vmadm vmadm +complete -F _vmcfg vmcfg diff --git a/usr/src/cmd/nsadmin/bash/bash_completion.d/zone_alias b/usr/src/cmd/nsadmin/bash/bash_completion.d/zone_alias new file mode 100644 index 0000000000..5ae8e1d7c7 --- /dev/null +++ b/usr/src/cmd/nsadmin/bash/bash_completion.d/zone_alias @@ -0,0 +1,42 @@ +_zone_alias() +{ + local RES alias completed_alias results_arr uuid + # Attempt alias -> uuid mapping + alias="${COMP_WORDS[COMP_CWORD]}" + RES=$(vmadm list -H -o alias,uuid | awk -v alias="$alias" ' + BEGIN { count = 0; uuid = ""; } + $1 == alias { count = 1; aliases[1] = $1; uuid = $2; exit;} + $1 ~ "^"alias { count += 1; aliases[count] = $1; uuid = $2; } + END { if (count == 1) { + print "smartos_alias_completed " aliases[1] " " uuid; + } else { + for (i=1; i <= count; i++) { + print aliases[i] + } + } + } + ') + results_arr=( $RES ) + # we use 'smartos_alias_completed' as a flag to determine that + # a single alias was matched. + if [[ "${results_arr[0]}" == "smartos_alias_completed" ]]; then + completed_alias="${results_arr[1]}" + uuid="${results_arr[2]}" + if [[ -n "$SMARTOS_MULTILINE_ALIAS_COMPLETION" ]]; then + tput sc + tput bold + echo "[completed alias: ${completed_alias}]" + tput rc + tput sgr0 + COMPREPLY=$uuid + elif [[ "$completed_alias" == "$alias" ]]; then + COMPREPLY=$uuid + else + COMPREPLY=$completed_alias + compopt -o nospace + fi + else + COMPREPLY=( $(compgen -W "$RES" -- ${cur}) ) + fi + return 0 +} diff --git a/usr/src/cmd/nsadmin/bash/bash_completion.d/zones b/usr/src/cmd/nsadmin/bash/bash_completion.d/zones new file mode 100644 index 0000000000..e4bc5b4155 --- /dev/null +++ b/usr/src/cmd/nsadmin/bash/bash_completion.d/zones @@ -0,0 +1,50 @@ +_zlogin() +{ + local cur prev opts base + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + # The -e and -l take non-zone arguments, otherwise complete zone + if [[ ${prev} != '-e' ]] && [[ ${prev} != '-l' ]]; then + local running=$(zoneadm list | grep -v '^global$') + COMPREPLY=( $(compgen -W "${running}" -- ${cur}) ) + fi + if [[ -n "$COMPREPLY" ]]; then + return 0 + fi + + _zone_alias + return 0 +} + +_dash_z_zone() +{ + local cur prev opts base + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + # The -z takes a zone option, ignore everything else + if [[ ${prev} =~ -.*z$ ]]; then + local zones=$(zoneadm list -c | grep -v '^global$') + COMPREPLY=( $(compgen -W "${zones}" -- ${cur}) ) + if [[ -n "$COMPREPLY" ]]; then + return 0 + fi + _zone_alias + fi + return 0 +} + +complete -F _zlogin zlogin +complete -F _dash_z_zone zoneadm +complete -F _dash_z_zone zonecfg +complete -F _dash_z_zone svcs +complete -F _dash_z_zone svcadm +complete -F _dash_z_zone svcprop +complete -F _dash_z_zone pgrep +complete -F _dash_z_zone pkill +complete -F _dash_z_zone ps +complete -F _dash_z_zone ptree +complete -F _dash_z_zone wall diff --git a/usr/src/cmd/nsadmin/bashrc.sh b/usr/src/cmd/nsadmin/bashrc.sh deleted file mode 100644 index 938b0da67e..0000000000 --- a/usr/src/cmd/nsadmin/bashrc.sh +++ /dev/null @@ -1,5 +0,0 @@ -# -# Define default prompt to <username>@<hostname>:<path><"($|#) "> -# and print '#' for user "root" and '$' for normal users. -# -typeset +x PS1="\u@\h:\w\\$ " diff --git a/usr/src/cmd/nsadmin/dot-bash_profile.sh b/usr/src/cmd/nsadmin/dot-bash_profile.sh new file mode 100644 index 0000000000..dc732f6814 --- /dev/null +++ b/usr/src/cmd/nsadmin/dot-bash_profile.sh @@ -0,0 +1,12 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# +# Copyright (c) 2014, Joyent, Inc. +# + +[ -f /root/.profile ] && source /root/.profile +[ -f /root/.bashrc ] && source /root/.bashrc diff --git a/usr/src/cmd/nsadmin/dot-bashrc.sh b/usr/src/cmd/nsadmin/dot-bashrc.sh new file mode 100644 index 0000000000..0f06d70ece --- /dev/null +++ b/usr/src/cmd/nsadmin/dot-bashrc.sh @@ -0,0 +1,57 @@ +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# +# Copyright (c) 2014, Joyent, Inc. +# + +if [ "$PS1" ]; then + mt_tty=$(/usr/bin/tty 2>/dev/null) + if [[ $mt_tty =~ ^/dev/term/[abcd] ]]; then + # If we're on the serial console, we generally won't know how + # big our terminal is. Attempt to ask it using control sequences + # and resize our pty accordingly. + mt_output=$(/usr/lib/measure_terminal 2>/dev/null) + if [[ $? -eq 0 ]]; then + eval "$mt_output" + else + # We could not read the size, but we should set a 'sane' + # default as the dimensions of the previous user's terminal + # persist on the tty device. + export LINES=25 + export COLUMNS=80 + fi + /usr/bin/stty rows ${LINES} columns ${COLUMNS} 2>/dev/null + fi + unset mt_output mt_tty + shopt -s checkwinsize + if [[ -f /.dcinfo ]]; then + . /.dcinfo + DC_NAME="${SDC_DATACENTER_NAME}" + fi + if [[ -n "${DC_NAME}" ]]; then + PS1="[\u@\h (${DC_NAME}) \w]\\$ " + else + PS1="[\u@\h \w]\\$ " + fi + alias ll='ls -lF' + alias ls='ls --color=auto' + [ -n "${SSH_CLIENT}" ] && export PROMPT_COMMAND='echo -ne "\033]0;${HOSTNAME} \007" && history -a' +fi + +# Load bash completion +[ -f /etc/bash/bash_completion ] && . /etc/bash/bash_completion + +svclog() { + if [[ -z "$PAGER" ]]; then + PAGER=less + fi + $PAGER `svcs -L $1` +} + +svclogf() { + /usr/bin/tail -f `svcs -L $1` +} diff --git a/usr/src/cmd/nsadmin/dot-profile.sh b/usr/src/cmd/nsadmin/dot-profile.sh index a45e47b58d..fa6fb19714 100644 --- a/usr/src/cmd/nsadmin/dot-profile.sh +++ b/usr/src/cmd/nsadmin/dot-profile.sh @@ -1,14 +1,19 @@ # -# Uncommenting PATH below will place /usr/gnu/bin at front, -# adds /usr/sbin and /sbin to the end. +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. # -# export PATH=/usr/gnu/bin:/usr/bin:/usr/sbin:/sbin + # -# Define default prompt to <username>@<hostname>:<path><"($|#) "> -# and print '#' for user "root" and '$' for normal users. +# Copyright 2019 Joyent, Inc. # -# override default prompt for bash -# case "$0" in -# -bash) -# export PS1="\u@\h:\w\\$ " -# esac + +PATH=/usr/bin:/usr/sbin:/smartdc/bin:/opt/smartdc/bin:/opt/local/bin:/opt/local/sbin:/opt/tools/bin:/opt/tools/sbin:/opt/smartdc/agents/bin +MANPATH=/usr/share/man:/smartdc/man:/opt/smartdc/man:/opt/local/man:/opt/tools/man +PAGER=less +# If pkgsrc-tools is set up and the mozilla-rootcerts package is installed +# configure the platform curl to use it. +if [[ -f /opt/tools/share/mozilla-rootcerts/cacert.pem ]]; then + CURL_CA_BUNDLE=/opt/tools/share/mozilla-rootcerts/cacert.pem +fi +export PATH MANPATH PAGER CURL_CA_BUNDLE diff --git a/usr/src/cmd/nsadmin/etc-profile.sh b/usr/src/cmd/nsadmin/etc-profile.sh index c1dfae8eb8..b59a80791b 100644 --- a/usr/src/cmd/nsadmin/etc-profile.sh +++ b/usr/src/cmd/nsadmin/etc-profile.sh @@ -21,6 +21,7 @@ # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # Copyright 2015 Nexenta Systems, Inc. All rights reserved. +# # The profile that all logins get before using their own .profile. @@ -41,19 +42,10 @@ fi # -rsh is given its environment in its .profile. case "$0" in --bash) - # set prompt for bash - PS1="\u@\h:\w\\$ " - export PS1 - ;; -esac - -case "$0" in -sh | -ksh | -ksh93 | -jsh | -bash | -zsh) if [ ! -f .hushlogin ] then - /usr/sbin/quota # Allow the user to break the Message-Of-The-Day only. trap "trap '' 2" 2 /bin/cat -s /etc/motd @@ -61,12 +53,12 @@ case "$0" in /bin/mail -E case $? in - 0) + 0) echo "You have new mail." - ;; - 2) + ;; + 2) echo "You have mail." - ;; + ;; esac fi esac diff --git a/usr/src/cmd/nsadmin/etc-skel-bashrc.sh b/usr/src/cmd/nsadmin/etc-skel-bashrc.sh new file mode 100644 index 0000000000..128f6b79d7 --- /dev/null +++ b/usr/src/cmd/nsadmin/etc-skel-bashrc.sh @@ -0,0 +1,7 @@ +# +# Define default prompt to <username>@<hostname>:<path><"($|#) "> +# and print '#' for user "root" and '$' for normal users. +# +PS1='${LOGNAME}@$(/usr/bin/hostname):$( + [[ "${LOGNAME}" == "root" ]] && printf "%s" "${PWD/${HOME}/~}# " || + printf "%s" "${PWD/${HOME}/~}\$ ")' diff --git a/usr/src/cmd/nsadmin/system b/usr/src/cmd/nsadmin/system index 29da0b787c..8ab13b7d3f 100644 --- a/usr/src/cmd/nsadmin/system +++ b/usr/src/cmd/nsadmin/system @@ -23,6 +23,7 @@ * SYSTEM SPECIFICATION FILE * +* * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * * It is not recommended to edit this file directly but rather @@ -34,6 +35,10 @@ * recommendations on naming fragment files. * * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +* +* As SmartOS ships with /etc/ off a ramdisk, we don't support using the +* /etc/system.d mechanism. +* * moddir: * @@ -108,3 +113,53 @@ * * set test_module:debug = 0x13 +set ibft_noprobe=1 + +set noexec_user_stack=1 +set noexec_user_stack_log=1 +set rlim_fd_cur=65536 + +* Ensure that c-states are disabled +set idle_cpu_no_deep_c=1 + +* 10 GbE Tuning +set ip:ip_squeue_fanout=1 + +* +* Machines should take a crash dump and reboot when receiving an NMI +* +set pcplusmp:apic_panic_on_nmi=1 +set apix:apic_panic_on_nmi=1 + +* +* Don't use multi-threaded fast crash dump or a high compression level +* +set dump_plat_mincpu=0 +set dump_bzip2_level=1 + +* +* Want additional crash dump metrics +* +set dump_metrics_on=1 + +* +* The traditional (and essentially entirely brain dead) cfgadm(1M)-centric +* model of hotpluggin' appears to be basically unnecessary. This tunable +* enables the system to create device nodes for newly inserted devices +* automatically. See: usr/src/uts/common/io/sata/impl/sata.c:97 +* +set sata:sata_auto_online=1 + +* +* We want to limit the time spent in any one I/O to 10 seconds for targets +* that are not optical. This is still a very long time; our queue depth is +* typically 10 or less, and disks will usually fail a command after 2-3s. +* So we'd have to have multiple reads of bad sectors queued up to have any +* chance of timing out. In practice, timeouts occur because of problems with +* disk controllers or firmware, not media errors, and in those cases it will +* not help at all to wait longer. +* +set sd:sd_io_time=10 + +* Use hires tick to improve some scheduling latency issues +set hires_tick=1 diff --git a/usr/src/cmd/ssh/etc/Makefile b/usr/src/cmd/nsadmin/zshrc index 66a60e0705..1e9c83e57f 100644 --- a/usr/src/cmd/ssh/etc/Makefile +++ b/usr/src/cmd/nsadmin/zshrc @@ -18,41 +18,14 @@ # # CDDL HEADER END # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. + +# +# Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# Copyright (c) 2018, Joyent, Inc. - -MANIFEST = ssh.xml -SVCMETHOD = sshd - -include ../../Makefile.cmd - -ETCSSHDIR= $(ROOTETC)/ssh -DIRS= $(ETCSSHDIR) - -FILES= sshd_config ssh_config - -ETCSSHFILES= $(FILES:%=$(ETCSSHDIR)/%) - -$(ETCSSHFILES) := FILEMODE= 644 - -ROOTMANIFESTDIR = $(ROOTSVCNETWORK) - -$(ETCSSHDIR)/% : % - $(INS.file) - -$(DIRS): - $(INS.dir) - -$(POFILE): - -SMOFF += signed - -all lint clean clobber _msg: - -install: all $(DIRS) $(ETCSSHFILES) $(ROOTMANIFEST) $(ROOTSVCMETHOD) - -check: $(CHKMANIFEST) +# ident "@(#)zshrc 1.1 10/02/08 SMI" +# -include ../../Makefile.targ +# Turn on the "new" completion system. See zshcompsys(1). +autoload -Uz compinit +compinit -i diff --git a/usr/src/cmd/nscd/Makefile b/usr/src/cmd/nscd/Makefile index 9103db70ae..9bd4d3e18f 100644 --- a/usr/src/cmd/nscd/Makefile +++ b/usr/src/cmd/nscd/Makefile @@ -30,6 +30,7 @@ MANIFEST= name-service-cache.xml SVCMETHOD= svc-nscd include ../Makefile.cmd +include ../Makefile.ctf ROOTMANIFESTDIR= $(ROOTSVCSYSTEM) diff --git a/usr/src/cmd/passwd/Makefile b/usr/src/cmd/passwd/Makefile index 561357a16c..079e8b6050 100644 --- a/usr/src/cmd/passwd/Makefile +++ b/usr/src/cmd/passwd/Makefile @@ -33,6 +33,8 @@ lint := LDLIBS += -lpasswdutil LDFLAGS += $(ZIGNORE) LDLIBS += -lbsm -lpam -lnsl +CPPFLAGS += -D__EXTENSIONS__ + FILEMODE = 06555 XGETFLAGS += -a -x $(PROG).xcl diff --git a/usr/src/cmd/pgrep/Makefile b/usr/src/cmd/pgrep/Makefile index 67993f0ff2..09fb900da7 100644 --- a/usr/src/cmd/pgrep/Makefile +++ b/usr/src/cmd/pgrep/Makefile @@ -22,6 +22,8 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2019 Joyent, Inc. +# PROG = pgrep PKILLFILE = pkill @@ -31,13 +33,14 @@ ROOTLINKS = $(ROOTBIN)/$(PKILLFILE) OBJS = pgrep.o idtab.o psexp.o SRCS = $(OBJS:.o=.c) POFILES = $(OBJS:.o=.po) -lint := LINTFLAGS = -ux include ../Makefile.cmd CLOBBERFILES += $(PKILLFILE) + CFLAGS += $(CCVERBOSE) -CERRWARN += -_gcc=-Wno-parentheses +CSTD = $(CSTD_GNU99) + LDLIBS += -luutil -lproject -lcontract POFILE = ppgrep.po @@ -69,6 +72,4 @@ $(ROOTLINKS): $(ROOTPROG) clean: $(RM) $(OBJS) -lint: lint_SRCS - include ../Makefile.targ diff --git a/usr/src/cmd/pgrep/idtab.c b/usr/src/cmd/pgrep/idtab.c index 52d6e88cdb..56db6eab91 100644 --- a/usr/src/cmd/pgrep/idtab.c +++ b/usr/src/cmd/pgrep/idtab.c @@ -24,7 +24,9 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2019 Joyent, Inc. + */ #include <libintl.h> #include <string.h> @@ -61,7 +63,8 @@ idtab_append(idtab_t *idt, idkey_t id) if (idt->id_nelems >= idt->id_size) { size = idt->id_size ? idt->id_size * IDTAB_GROW : IDTAB_DEFSIZE; - if (data = realloc(idt->id_data, sizeof (idkey_t) * size)) { + if ((data = realloc(idt->id_data, + sizeof (idkey_t) * size)) != NULL) { idt->id_data = data; idt->id_size = size; } else { diff --git a/usr/src/cmd/pgrep/pgrep.c b/usr/src/cmd/pgrep/pgrep.c index 4531f11267..0fda6733fb 100644 --- a/usr/src/cmd/pgrep/pgrep.c +++ b/usr/src/cmd/pgrep/pgrep.c @@ -24,6 +24,10 @@ */ /* Copyright (c) 2012 by Delphix. All rights reserved */ +/* + * Copyright 2019 Joyent, Inc. + */ + #include <sys/types.h> #include <sys/stat.h> #include <sys/param.h> @@ -59,11 +63,11 @@ #define TEXT_DOMAIN "SYS_TEST" #endif -#define OPT_SETB 0x0001 /* Set the bits specified by o_bits */ -#define OPT_CLRB 0x0002 /* Clear the bits specified by o_bits */ -#define OPT_FUNC 0x0004 /* Call the function specified by o_func */ -#define OPT_STR 0x0008 /* Set the string specified by o_ptr */ -#define OPT_CRIT 0x0010 /* Option is part of selection criteria */ +#define OPT_SETB 0x0001 /* Set the bits specified by o_bits */ +#define OPT_CLRB 0x0002 /* Clear the bits specified by o_bits */ +#define OPT_FUNC 0x0004 /* Call the function specified by o_func */ +#define OPT_STR 0x0008 /* Set the string specified by o_ptr */ +#define OPT_CRIT 0x0010 /* Option is part of selection criteria */ #define F_LONG_FMT 0x0001 /* Match against long format cmd */ #define F_NEWEST 0x0002 /* Match only newest pid */ @@ -138,7 +142,7 @@ static optdesc_t g_optdtab[] = { { 0, 0, 0, 0 }, /* 'k' */ { OPT_SETB, F_LONG_OUT, 0, &g_flags }, /* 'l' */ { 0, 0, 0, 0 }, /* 'm' */ - { OPT_SETB, F_NEWEST, 0, &g_flags }, /* -n */ + { OPT_SETB, F_NEWEST, 0, &g_flags }, /* -n */ { OPT_SETB, F_OLDEST, 0, &g_flags }, /* -o */ { 0, 0, 0, 0 }, /* 'p' */ { 0, 0, 0, 0 }, /* 'q' */ @@ -173,7 +177,7 @@ static pid_t g_pid; /* Current pid */ static int g_signal = SIGTERM; /* Signal to send */ static void -print_proc(psinfo_t *psinfo) +print_proc(psinfo_t *psinfo, char *argv __unused, size_t len __unused) { if (g_flags & F_OUTPUT) (void) printf("%s%d", g_delim, (int)psinfo->pr_pid); @@ -183,7 +187,7 @@ print_proc(psinfo_t *psinfo) } } -static char * +static void mbstrip(char *buf, size_t nbytes) { wchar_t wc; @@ -191,6 +195,7 @@ mbstrip(char *buf, size_t nbytes) int n; buf[nbytes - 1] = '\0'; + p = buf; while (*p != '\0') { @@ -212,30 +217,23 @@ mbstrip(char *buf, size_t nbytes) p += n; } } - - return (buf); } static void -print_proc_long(psinfo_t *psinfo) +print_proc_long(psinfo_t *psinfo, char *argv, size_t len) { - char *name; - - if (g_flags & F_LONG_FMT) - name = mbstrip(psinfo->pr_psargs, PRARGSZ); - else - name = psinfo->pr_fname; + mbstrip(argv, len); if (g_flags & F_OUTPUT) - (void) printf("%s%5d %s", g_delim, (int)psinfo->pr_pid, name); + (void) printf("%s%5d %s", g_delim, (int)psinfo->pr_pid, argv); else { - (void) printf("%5d %s", (int)psinfo->pr_pid, name); + (void) printf("%5d %s", (int)psinfo->pr_pid, argv); g_flags |= F_OUTPUT; } } static void -kill_proc(psinfo_t *psinfo) +kill_proc(psinfo_t *psinfo, char *argv __unused, size_t len __unused) { if (psinfo->pr_pid > 0 && kill(psinfo->pr_pid, g_signal) == -1) uu_warn(gettext("Failed to signal pid %d"), @@ -268,6 +266,46 @@ open_proc_dir(const char *dirpath) return (dirp); } +static void +get_argv(int flags, psinfo_t *ps, char *buf, size_t bufsize) +{ + char *path = NULL; + ssize_t size = 0; + int fd; + + if (!(flags & F_LONG_FMT)) { + (void) strlcpy(buf, ps->pr_fname, bufsize); + return; + } + + if (getenv("SHORT_PSARGS") != NULL) { + (void) strlcpy(buf, ps->pr_psargs, bufsize); + return; + } + + if (asprintf(&path, "%s/%d/cmdline", g_procdir, + (int)ps->pr_pid) != -1 && (fd = open(path, O_RDONLY)) != -1) { + size = read(fd, buf, bufsize); + (void) close(fd); + } + + free(path); + + if (size <= 0) { + (void) strlcpy(buf, ps->pr_psargs, bufsize); + } else { + buf[bufsize - 1] = '\0'; + for (char *cp = buf; cp - buf < size; cp++) { + if (*cp == '\0' && (cp - buf) + 1 < size) + *cp = ' '; + } + } + + for (ssize_t i = strlen(buf) - 1; i >= 0 && isspace(buf[i]); i--) { + buf[i] = '\0'; + } +} + #define NEWER(ps1, ps2) \ ((ps1.pr_start.tv_sec > ps2.pr_start.tv_sec) || \ (ps1.pr_start.tv_sec == ps2.pr_start.tv_sec && \ @@ -275,9 +313,10 @@ open_proc_dir(const char *dirpath) static int scan_proc_dir(const char *dirpath, DIR *dirp, psexp_t *psexp, - void (*funcp)(psinfo_t *)) + void (*funcp)(psinfo_t *, char *, size_t)) { char procpath[MAXPATHLEN]; + char argv[PRMAXARGVLEN] = ""; psinfo_t ps, ops; dirent_t *dent; int procfd; @@ -285,9 +324,6 @@ scan_proc_dir(const char *dirpath, DIR *dirp, psexp_t *psexp, int reverse = (g_flags & F_REVERSE) ? 1 : 0; int ovalid = 0, nmatches = 0, flags = 0; - if (g_flags & F_LONG_FMT) - flags |= PSEXP_PSARGS; - if (g_flags & F_EXACT_MATCH) flags |= PSEXP_EXACT; @@ -302,12 +338,17 @@ scan_proc_dir(const char *dirpath, DIR *dirp, psexp_t *psexp, if ((procfd = open(procpath, O_RDONLY)) == -1) continue; - if ((read(procfd, &ps, sizeof (ps)) == sizeof (psinfo_t)) && - (ps.pr_nlwp != 0) && (ps.pr_pid != g_pid) && - (psexp_match(psexp, &ps, flags) ^ reverse)) { + if (read(procfd, &ps, sizeof (ps)) != sizeof (psinfo_t)) { + (void) close(procfd); + continue; + } + + get_argv(g_flags, &ps, argv, sizeof (argv)); + + if ((ps.pr_nlwp != 0) && (ps.pr_pid != g_pid) && + (psexp_match(psexp, &ps, argv, flags) ^ reverse)) { if (g_flags & F_NEWEST) { - /* LINTED - opsinfo use ok */ if (!ovalid || NEWER(ps, ops)) { (void) memcpy(&ops, &ps, sizeof (psinfo_t)); @@ -320,7 +361,7 @@ scan_proc_dir(const char *dirpath, DIR *dirp, psexp_t *psexp, ovalid = 1; } } else { - (*funcp)(&ps); + (*funcp)(&ps, argv, sizeof (argv)); nmatches++; } } @@ -329,7 +370,8 @@ scan_proc_dir(const char *dirpath, DIR *dirp, psexp_t *psexp, } if ((g_flags & (F_NEWEST | F_OLDEST)) && ovalid) { - (*funcp)(&ops); + (*funcp)(&ops, argv, sizeof (argv)); + nmatches++; } @@ -592,11 +634,13 @@ print_usage(FILE *stream) int main(int argc, char *argv[]) { - void (*funcp)(psinfo_t *); + void (*funcp)(psinfo_t *, char *, size_t); const char *optstr; optdesc_t *optd; int nmatches, c; + const char *zroot; + char buf[PATH_MAX]; DIR *dirp; @@ -626,6 +670,12 @@ main(int argc, char *argv[]) opterr = 0; + zroot = zone_get_nroot(); + if (zroot != NULL) { + (void) snprintf(buf, sizeof (buf), "%s/%s", zroot, g_procdir); + g_procdir = buf; + } + while (optind < argc) { while ((c = getopt(argc, argv, optstr)) != (int)EOF) { diff --git a/usr/src/cmd/pgrep/psexp.c b/usr/src/cmd/pgrep/psexp.c index be14393bd1..abb8699523 100644 --- a/usr/src/cmd/pgrep/psexp.c +++ b/usr/src/cmd/pgrep/psexp.c @@ -24,7 +24,9 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2019 Joyent, Inc. + */ #include <string.h> #include <stdlib.h> @@ -110,10 +112,9 @@ psexp_compile(psexp_t *psexp) psexp->__f1.id_data && !idtab_search(&psexp->__f1, psinfo->__f2) int -psexp_match(psexp_t *psexp, psinfo_t *psinfo, int flags) +psexp_match(psexp_t *psexp, psinfo_t *psinfo, const char *argv, int flags) { regmatch_t pmatch; - const char *s; if (NOMATCH(ps_euids, pr_euid)) return (0); @@ -139,14 +140,11 @@ psexp_match(psexp_t *psexp, psinfo_t *psinfo, int flags) return (0); if (psexp->ps_pat != NULL) { - s = (flags & PSEXP_PSARGS) ? - psinfo->pr_psargs : psinfo->pr_fname; - - if (regexec(&psexp->ps_reg, s, 1, &pmatch, 0) != 0) + if (regexec(&psexp->ps_reg, argv, 1, &pmatch, 0) != 0) return (0); if ((flags & PSEXP_EXACT) && - (pmatch.rm_so != 0 || s[pmatch.rm_eo] != '\0')) + (pmatch.rm_so != 0 || argv[pmatch.rm_eo] != '\0')) return (0); } diff --git a/usr/src/cmd/pgrep/psexp.h b/usr/src/cmd/pgrep/psexp.h index 741050cbe4..7de6742311 100644 --- a/usr/src/cmd/pgrep/psexp.h +++ b/usr/src/cmd/pgrep/psexp.h @@ -24,11 +24,13 @@ * Use is subject to license terms. */ +/* + * Copyright 2019 Joyent, Inc. + */ + #ifndef _PSEXP_H #define _PSEXP_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/types.h> #include <procfs.h> #include <regex.h> @@ -39,8 +41,7 @@ extern "C" { #include "idtab.h" -#define PSEXP_PSARGS 0x1 /* Match against psargs rather than fname */ -#define PSEXP_EXACT 0x2 /* Match must be exact (entire string) */ +#define PSEXP_EXACT 0x1 /* Match must be exact (entire string) */ typedef struct psexp { idtab_t ps_euids; /* Table of effective uids to match */ @@ -54,14 +55,14 @@ typedef struct psexp { idtab_t ps_taskids; /* Table of task ids to match */ idtab_t ps_zoneids; /* Table of zone ids to match */ idtab_t ps_ctids; /* Table of contract ids to match */ - const char *ps_pat; /* Uncompiled fname/psargs regexp pattern */ - regex_t ps_reg; /* Compiled fname/psargs regexp */ + const char *ps_pat; /* Uncompiled fname/argv regexp pattern */ + regex_t ps_reg; /* Compiled fname/argv regexp */ } psexp_t; extern void psexp_create(psexp_t *); extern void psexp_destroy(psexp_t *); extern int psexp_compile(psexp_t *); -extern int psexp_match(psexp_t *, psinfo_t *, int); +extern int psexp_match(psexp_t *, psinfo_t *, const char *, int); #ifdef __cplusplus } diff --git a/usr/src/cmd/prstat/prstat.c b/usr/src/cmd/prstat/prstat.c index 0ff4f51bcd..f40219d75a 100644 --- a/usr/src/cmd/prstat/prstat.c +++ b/usr/src/cmd/prstat/prstat.c @@ -182,6 +182,33 @@ optdesc_t opts = { -1 /* sort in decreasing order */ }; + +static int +proc_snprintf(char *_RESTRICT_KYWD s, size_t n, + const char *_RESTRICT_KYWD fmt, ...) +{ + static boolean_t ptools_zroot_valid = B_FALSE; + static const char *ptools_zroot = NULL; + va_list args; + int ret, nret = 0; + + if (ptools_zroot_valid == B_FALSE) { + ptools_zroot_valid = B_TRUE; + ptools_zroot = zone_get_nroot(); + } + + if (ptools_zroot != NULL) { + nret = snprintf(s, n, "%s", ptools_zroot); + if (nret > n) + return (nret); + } + va_start(args, fmt); + ret = vsnprintf(s + nret, n - nret, fmt, args); + va_end(args); + + return (ret + nret); +} + /* * Print timestamp as decimal reprentation of time_t value (-d u was specified) * or the standard date format (-d d was specified). @@ -849,9 +876,9 @@ lwp_update(lwp_info_t *lwp, pid_t pid, id_t lwpid, struct prusage *usage) static int read_procfile(fd_t **fd, char *pidstr, char *file, void *buf, size_t bufsize) { - char procfile[MAX_PROCFS_PATH]; + char procfile[PATH_MAX]; - (void) snprintf(procfile, MAX_PROCFS_PATH, + (void) proc_snprintf(procfile, PATH_MAX, "/proc/%s/%s", pidstr, file); if ((*fd = fd_open(procfile, O_RDONLY, *fd)) == NULL) return (1); @@ -1415,6 +1442,7 @@ main(int argc, char **argv) int timeout; struct pollfd pollset; char key; + char procpath[PATH_MAX]; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); @@ -1425,7 +1453,7 @@ main(int argc, char **argv) pagesize = sysconf(_SC_PAGESIZE); while ((opt = getopt(argc, argv, - "vcd:HmarRLtu:U:n:p:C:P:h:s:S:j:k:TJWz:Z")) != (int)EOF) { + "vVcd:HmarRLtu:U:n:p:C:P:h:s:S:j:k:TJWz:Z")) != (int)EOF) { switch (opt) { case 'r': opts.o_outpmode |= OPT_NORESOLVE; @@ -1505,6 +1533,9 @@ main(int argc, char **argv) while (p = strtok(NULL, ", ")) add_uid(&ruid_tbl, p); break; + case 'V': + /* obsolete argument - accepted for compatability */ + break; case 'p': fill_table(&pid_tbl, optarg, 'p'); break; @@ -1614,7 +1645,8 @@ main(int argc, char **argv) list_setkeyfunc(NULL, &opts, &lgroups, LT_LGRPS); if (opts.o_outpmode & OPT_TERMCAP) curses_on(); - if ((procdir = opendir("/proc")) == NULL) + (void) proc_snprintf(procpath, sizeof (procpath), "/proc"); + if ((procdir = opendir(procpath)) == NULL) Die(gettext("cannot open /proc directory\n")); if (opts.o_outpmode & OPT_TTY) { (void) printf(gettext("Please wait...\r")); diff --git a/usr/src/cmd/prstat/prstat.h b/usr/src/cmd/prstat/prstat.h index e205a98bd2..7d3913f731 100644 --- a/usr/src/cmd/prstat/prstat.h +++ b/usr/src/cmd/prstat/prstat.h @@ -73,6 +73,7 @@ extern "C" { #define OPT_ZONES 0x2000 /* report about zones */ #define OPT_PSETS 0x4000 /* report for specified psets */ #define OPT_LGRP 0x8000 /* report home lgroups */ + /* 0x10000 available for re-use */ #define OPT_UDATE 0x20000 /* print unix timestamp */ #define OPT_DDATE 0x40000 /* print timestamp in date(1) format */ #define OPT_NORESOLVE 0x80000 /* no nsswitch lookups */ diff --git a/usr/src/cmd/prtconf/prtconf.c b/usr/src/cmd/prtconf/prtconf.c index 9865a9190e..613fcea78c 100644 --- a/usr/src/cmd/prtconf/prtconf.c +++ b/usr/src/cmd/prtconf/prtconf.c @@ -344,10 +344,11 @@ main(int argc, char *argv[]) sizeof (hw_provider)); /* * If 0 bytes are returned (the system returns '1', for the \0), - * we're probably on x86, default to "Unknown Hardware Vendor". + * we're probably on x86, and there has been no si-hw-provider + * set in /etc/bootrc, default to Joyent. */ if (ret <= 1) { - (void) strncpy(hw_provider, "Unknown Hardware Vendor", + (void) strncpy(hw_provider, "Joyent", sizeof (hw_provider)); } (void) printf("System Configuration: %s %s\n", hw_provider, diff --git a/usr/src/cmd/ps/ps.c b/usr/src/cmd/ps/ps.c index 1a3e91689a..1387a9440a 100644 --- a/usr/src/cmd/ps/ps.c +++ b/usr/src/cmd/ps/ps.c @@ -27,7 +27,7 @@ */ /* - * Copyright (c) 2018, Joyent, Inc. + * Copyright 2019 Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -64,6 +64,8 @@ #include <sys/pset.h> #include <project.h> #include <zone.h> +#include <assert.h> +#include <stdbool.h> #define min(a, b) ((a) > (b) ? (b) : (a)) #define max(a, b) ((a) < (b) ? (b) : (a)) @@ -288,7 +290,7 @@ static int nzoneid = 0; static int kbytes_per_page; static int pidwidth; -static char *procdir = "/proc"; /* standard /proc directory */ +static char procdir[MAXPATHLEN]; /* standard /proc directory */ static struct ughead euid_tbl; /* table to store selected euid's */ static struct ughead ruid_tbl; /* table to store selected real uid's */ @@ -337,10 +339,22 @@ static int pidcmp(const void *p1, const void *p2); extern int ucbmain(int, char **); static int stdmain(int, char **); +/* also used by ucbps.c */ +void get_psargs(bool, bool, psinfo_t *, char *, size_t); +void print_psargs(char *, int); + int main(int argc, char **argv) { const char *me; + const char *zroot = zone_get_nroot(); + + /* + * If this is a branded zone, the native procfs may mounted in a + * non-standard location. Apply such a path prefix if it exists. + */ + (void) snprintf(procdir, sizeof (procdir), "%s/proc", zroot != NULL ? + zroot : ""); /* * The original two ps'es are linked in a single binary; @@ -1362,14 +1376,12 @@ prfind(int found, psinfo_t *psinfo, char **tpp) static void prcom(psinfo_t *psinfo, char *ttyp) { - char *cp; - long tm; - int bytesleft; - int wcnt, length; - wchar_t wchar; + long tm; + int wcnt; struct passwd *pwd; - int zombie_lwp; - char zonename[ZONENAME_MAX]; + int zombie_lwp; + char zonename[ZONENAME_MAX]; + char psargs[PRMAXARGVLEN] = ""; /* * If process is zombie, call zombie print routine and return. @@ -1558,44 +1570,22 @@ prcom(psinfo_t *psinfo, char *ttyp) if (psinfo->pr_time.tv_nsec > 500000000) tm++; } - (void) printf(" %4ld:%.2ld", tm / 60, tm % 60); /* [L]TIME */ + (void) printf(" %4ld:%.2ld ", tm / 60, tm % 60); /* [L]TIME */ if (zombie_lwp) { - (void) printf(" <defunct>\n"); + (void) printf("<defunct>\n"); return; } if (!fflg) { /* CMD */ wcnt = namencnt(psinfo->pr_fname, 16, 8); - (void) printf(" %.*s\n", wcnt, psinfo->pr_fname); + (void) printf("%.*s\n", wcnt, psinfo->pr_fname); return; } - - /* - * PRARGSZ == length of cmd arg string. - */ - psinfo->pr_psargs[PRARGSZ-1] = '\0'; - bytesleft = PRARGSZ; - for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) { - length = mbtowc(&wchar, cp, MB_LEN_MAX); - if (length == 0) - break; - if (length < 0 || !iswprint(wchar)) { - if (length < 0) - length = 1; - if (bytesleft <= length) { - *cp = '\0'; - break; - } - /* omit the unprintable character */ - (void) memmove(cp, cp+length, bytesleft-length); - length = 0; - } - bytesleft -= length; - } - wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, lflg ? 35 : PRARGSZ); - (void) printf(" %.*s\n", wcnt, psinfo->pr_psargs); + get_psargs(false, fflg, psinfo, psargs, sizeof (psargs)); + print_psargs(psargs, 0); + printf("\n"); } /* @@ -1650,20 +1640,98 @@ print_time(time_t tim, int width) (void) printf("%*s", width, buf); } +void +get_psargs(bool comm, bool full, psinfo_t *psinfo, char *buf, size_t bufsize) +{ + char *path = NULL; + ssize_t size = 0; + char *cp; + int fd; + + assert(psinfo->pr_psargs[PRARGSZ - 1] == '\0'); + + if (full && getenv("SHORT_PSARGS") == NULL && + asprintf(&path, "%s/%d/cmdline", procdir, + (int)psinfo->pr_pid) != -1 && (fd = open(path, O_RDONLY)) != -1) { + size = read(fd, buf, bufsize); + (void) close(fd); + } + + free(path); + + if (size <= 0) { + (void) strlcpy(buf, psinfo->pr_psargs, bufsize); + } else { + ssize_t i; + + buf[bufsize - 1] = '\0'; + + for (cp = buf; cp - buf < size; cp++) { + if (*cp == '\0' && (cp - buf) + 1 < size) + *cp = ' '; + } + + for (i = strlen(buf) - 1; i >= 0 && isspace(buf[i]); i--) { + buf[i] = '\0'; + } + } + + if (comm && (cp = strpbrk(buf, " \t\r\v\f\n")) != NULL) + *cp = '\0'; +} + +void +print_psargs(char *psargs, int width) +{ + int bytesleft; + int length; + char *cp; + int wcnt; + + bytesleft = strlen(psargs); + + for (cp = psargs; *cp != '\0'; cp += length) { + wchar_t wchar; + + length = mbtowc(&wchar, cp, MB_LEN_MAX); + + if (length == 0) + break; + + if (length < 0 || !iswprint(wchar)) { + if (length < 0) + length = 1; + if (bytesleft <= length) { + *cp = '\0'; + break; + } + /* omit the unprintable character */ + (void) memmove(cp, cp + length, bytesleft - length); + bytesleft -= length; + length = 0; + } + bytesleft -= length; + } + + wcnt = namencnt(psargs, PRMAXARGVLEN, width); + + if (width != 0) { + (void) printf("%.*s", width, psargs); + } else { + (void) printf("%-.*s", wcnt, psargs); + } +} + static void print_field(psinfo_t *psinfo, struct field *f, const char *ttyp) { + char psargs[PRMAXARGVLEN] = ""; int width = f->width; struct passwd *pwd; struct group *grp; time_t cputime; - int bytesleft; int wcnt; - wchar_t wchar; - char *cp; - int length; ulong_t mask; - char c = '\0', *csave = NULL; int zombie_lwp; zombie_lwp = (Lflg && psinfo->pr_lwp.pr_sname == 'Z'); @@ -1916,12 +1984,11 @@ print_field(psinfo_t *psinfo, struct field *f, const char *ttyp) (void) printf("%s", "<defunct>"); break; } - csave = strpbrk(psinfo->pr_psargs, " \t\r\v\f\n"); - if (csave) { - c = *csave; - *csave = '\0'; - } - /* FALLTHROUGH */ + + get_psargs(true, false, psinfo, psargs, sizeof (psargs)); + print_psargs(psargs, f->next != NULL ? width : 0); + break; + case F_ARGS: /* * PRARGSZ == length of cmd arg string. @@ -1930,38 +1997,11 @@ print_field(psinfo_t *psinfo, struct field *f, const char *ttyp) (void) printf("%-*s", width, "<defunct>"); break; } - psinfo->pr_psargs[PRARGSZ-1] = '\0'; - bytesleft = PRARGSZ; - for (cp = psinfo->pr_psargs; *cp != '\0'; cp += length) { - length = mbtowc(&wchar, cp, MB_LEN_MAX); - if (length == 0) - break; - if (length < 0 || !iswprint(wchar)) { - if (length < 0) - length = 1; - if (bytesleft <= length) { - *cp = '\0'; - break; - } - /* omit the unprintable character */ - (void) memmove(cp, cp+length, bytesleft-length); - length = 0; - } - bytesleft -= length; - } - wcnt = namencnt(psinfo->pr_psargs, PRARGSZ, width); - /* - * Print full width unless this is the last format. - */ - if (f->next != NULL) - (void) printf("%-*.*s", width, wcnt, - psinfo->pr_psargs); - else - (void) printf("%-.*s", wcnt, - psinfo->pr_psargs); - if (f->fname == F_COMM && csave) - *csave = c; + + get_psargs(false, fflg, psinfo, psargs, sizeof (psargs)); + print_psargs(psargs, f->next != NULL ? width : 0); break; + case F_TASKID: (void) printf("%*d", width, (int)psinfo->pr_taskid); break; @@ -2422,7 +2462,8 @@ namencnt(char *cmd, int csisize, int scrsize) return (8); /* default to use for illegal chars */ if ((nscrsz = wcwidth(wchar)) <= 0) return (8); - if (csiwcnt + ncsisz > csisize || scrwcnt + nscrsz > scrsize) + if (csiwcnt + ncsisz > csisize || + (scrsize != 0 && scrwcnt + nscrsz > scrsize)) break; csiwcnt += ncsisz; scrwcnt += nscrsz; diff --git a/usr/src/cmd/ps/ucbps.c b/usr/src/cmd/ps/ucbps.c index 3110e95313..c368426184 100644 --- a/usr/src/cmd/ps/ucbps.c +++ b/usr/src/cmd/ps/ucbps.c @@ -21,7 +21,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright 2012, Joyent, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ @@ -65,8 +65,10 @@ #include <wctype.h> #include <stdarg.h> #include <sys/proc.h> +#include <sys/procfs.h> #include <priv_utils.h> #include <zone.h> +#include <stdbool.h> #define NTTYS 2 /* max ttys that can be specified with the -t option */ /* only one tty can be specified with SunOS ps */ @@ -101,6 +103,7 @@ static int nflg; /* Numerical output */ static int pflg; /* Specific process id passed as argument */ static int Uflg; /* Update private database, ups_data */ static int errflg; +static int wflag; static char *gettty(); static char argbuf[ARGSIZ]; @@ -133,8 +136,7 @@ static void getarg(void); static void prtime(timestruc_t st); static void przom(psinfo_t *psinfo); static int num(char *); -static int preadargs(int, psinfo_t *, char *); -static int preadenvs(int, psinfo_t *, char *); +static int preadenvs(int, psinfo_t *, char *, size_t); static int prcom(int, psinfo_t *, char *); static int namencnt(char *, int, int); static int pscompare(const void *, const void *); @@ -142,12 +144,18 @@ static char *err_string(int); extern int scrwidth(wchar_t); /* header file? */ +/* from ps.c */ +void get_psargs(bool, bool, psinfo_t *, char *, size_t); +void print_psargs(char *, int); + int ucbmain(int argc, char **argv) { psinfo_t info; /* process information structure from /proc */ - char *psargs = NULL; /* pointer to buffer for -w and -ww options */ - char *svpsargs = NULL; + /* + * This can also store env vars, so we bump up the size. + */ + char psargs[PRMAXARGVLEN * 2] = ""; struct psent *psent; int entsize; int nent; @@ -236,11 +244,12 @@ ucbmain(int argc, char **argv) case 'U': /* update private database ups_data */ Uflg++; break; - case 'w': /* increase display width */ + case 'w': if (twidth < 132) twidth = 132; - else /* second w option */ + if (wflag) twidth = NCARGS; + wflag++; break; case 'v': /* display virtual memory format */ vflg++; @@ -372,15 +381,9 @@ ucbmain(int argc, char **argv) (void) sprintf(hdr, "%*s TT S TIME COMMAND", pidwidth + 1, "PID"); - twidth = twidth - strlen(hdr) + 6; + twidth = twidth - strlen(hdr) + 7; (void) printf("%s\n", hdr); - if (twidth > PRARGSZ && (psargs = malloc(twidth)) == NULL) { - (void) fprintf(stderr, "ps: no memory\n"); - exit(1); - } - svpsargs = psargs; - /* * Determine which processes to print info about by searching * the /proc directory and looking at each process. @@ -410,7 +413,7 @@ retry: if ((psfd = open(psname, O_RDONLY)) == -1) continue; asfd = -1; - if (psargs != NULL || eflg) { + if (eflg) { /* now we need the proc_owner privilege */ (void) __priv_bracket(PRIV_ON); @@ -470,26 +473,20 @@ retry: if (!found && !tflg && !aflg && info.pr_euid != my_uid) goto closeit; - /* - * Read the args for the -w and -ww cases - */ - if (asfd > 0) { - if ((psargs != NULL && - preadargs(asfd, &info, psargs) == -1) || - (eflg && preadenvs(asfd, &info, psargs) == -1)) { - int saverr = errno; + get_psargs(false, wflag, &info, psargs, sizeof (psargs)); - (void) close(asfd); - if (saverr == EAGAIN) - goto retry; - if (saverr != ENOENT) - (void) fprintf(stderr, - "ps: read() on %s: %s\n", - asname, err_string(saverr)); - continue; + if (eflg && asfd > 0 && + preadenvs(asfd, &info, psargs, sizeof (psargs)) == -1) { + int saverr = errno; + + (void) close(asfd); + if (saverr == EAGAIN) + goto retry; + if (saverr != ENOENT) { + (void) fprintf(stderr, "ps: read() on %s: %s\n", + asname, err_string(saverr)); } - } else { - psargs = info.pr_psargs; + continue; } if (nent >= entsize) { @@ -507,22 +504,17 @@ retry: exit(1); } *psent[nent].psinfo = info; - if (psargs == NULL) - psent[nent].psargs = NULL; - else { - if ((psent[nent].psargs = malloc(strlen(psargs)+1)) - == NULL) { - (void) fprintf(stderr, "ps: no memory\n"); - exit(1); - } - (void) strcpy(psent[nent].psargs, psargs); + + if ((psent[nent].psargs = strndup(psargs, twidth)) == NULL) { + (void) fprintf(stderr, "ps: no memory\n"); + exit(1); } + psent[nent].found = found; nent++; closeit: if (asfd > 0) (void) close(asfd); - psargs = svpsargs; } /* revert to non-privileged user */ @@ -553,100 +545,17 @@ usage() /* print usage message and quit */ } /* - * Read the process arguments from the process. - * This allows >PRARGSZ characters of arguments to be displayed but, - * unlike pr_psargs[], the process may have changed them. - */ -#define NARG 100 -static int -preadargs(int pfd, psinfo_t *psinfo, char *psargs) -{ - off_t argvoff = (off_t)psinfo->pr_argv; - size_t len; - char *psa = psargs; - int bsize = twidth; - int narg = NARG; - off_t argv[NARG]; - off_t argoff; - off_t nextargoff; - int i; -#ifdef _LP64 - caddr32_t argv32[NARG]; - int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64); -#endif - - if (psinfo->pr_nlwp == 0 || - strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0) - goto out; - - (void) memset(psa, 0, bsize--); - nextargoff = 0; - errno = EIO; - while (bsize > 0) { - if (narg == NARG) { - (void) memset(argv, 0, sizeof (argv)); -#ifdef _LP64 - if (is32) { - if ((i = pread(pfd, argv32, sizeof (argv32), - argvoff)) <= 0) { - if (i == 0 || errno == EIO) - break; - return (-1); - } - for (i = 0; i < NARG; i++) - argv[i] = argv32[i]; - } else -#endif - if ((i = pread(pfd, argv, sizeof (argv), - argvoff)) <= 0) { - if (i == 0 || errno == EIO) - break; - return (-1); - } - narg = 0; - } - if ((argoff = argv[narg++]) == 0) - break; - if (argoff != nextargoff && - (i = pread(pfd, psa, bsize, argoff)) <= 0) { - if (i == 0 || errno == EIO) - break; - return (-1); - } - len = strlen(psa); - psa += len; - *psa++ = ' '; - bsize -= len + 1; - nextargoff = argoff + len + 1; -#ifdef _LP64 - argvoff += is32? sizeof (caddr32_t) : sizeof (caddr_t); -#else - argvoff += sizeof (caddr_t); -#endif - } - while (psa > psargs && isspace(*(psa-1))) - psa--; - -out: - *psa = '\0'; - if (strlen(psinfo->pr_psargs) > strlen(psargs)) - (void) strcpy(psargs, psinfo->pr_psargs); - - return (0); -} - -/* * Read environment variables from the process. * Append them to psargs if there is room. */ +#define NARG 100 static int -preadenvs(int pfd, psinfo_t *psinfo, char *psargs) +preadenvs(int pfd, psinfo_t *psinfo, char *psargs, size_t bufsize) { off_t envpoff = (off_t)psinfo->pr_envp; int len; char *psa; - char *psainit; - int bsize; + int remaining; int nenv = NARG; off_t envp[NARG]; off_t envoff; @@ -657,18 +566,18 @@ preadenvs(int pfd, psinfo_t *psinfo, char *psargs) int is32 = (psinfo->pr_dmodel != PR_MODEL_LP64); #endif - psainit = psa = (psargs != NULL)? psargs : psinfo->pr_psargs; + psa = psargs; len = strlen(psa); psa += len; - bsize = twidth - len - 1; + remaining = bufsize - len - 1; - if (bsize <= 0 || psinfo->pr_nlwp == 0 || + if (remaining <= 0 || psinfo->pr_nlwp == 0 || strcmp(psinfo->pr_lwp.pr_clname, "SYS") == 0) return (0); nextenvoff = 0; errno = EIO; - while (bsize > 0) { + while (remaining > 0) { if (nenv == NARG) { (void) memset(envp, 0, sizeof (envp)); #ifdef _LP64 @@ -694,7 +603,7 @@ preadenvs(int pfd, psinfo_t *psinfo, char *psargs) if ((envoff = envp[nenv++]) == 0) break; if (envoff != nextenvoff && - (i = pread(pfd, psa+1, bsize, envoff)) <= 0) { + (i = pread(pfd, psa+1, remaining, envoff)) <= 0) { if (i == 0 || errno == EIO) break; return (-1); @@ -702,7 +611,7 @@ preadenvs(int pfd, psinfo_t *psinfo, char *psargs) *psa++ = ' '; len = strlen(psa); psa += len; - bsize -= len + 1; + remaining -= len + 1; nextenvoff = envoff + len + 1; #ifdef _LP64 envpoff += is32? sizeof (caddr32_t) : sizeof (caddr_t); @@ -710,7 +619,7 @@ preadenvs(int pfd, psinfo_t *psinfo, char *psargs) envpoff += sizeof (caddr_t); #endif } - while (psa > psainit && isspace(*(psa-1))) + while (psa > psargs && isspace(*(psa-1))) psa--; *psa = '\0'; @@ -842,12 +751,9 @@ prtpct(ushort_t pct) static int prcom(int found, psinfo_t *psinfo, char *psargs) { - char *cp; char *tp; - char *psa; long tm; - int i, wcnt, length; - wchar_t wchar; + int wcnt; struct tty *ttyp; /* @@ -867,7 +773,6 @@ prcom(int found, psinfo_t *psinfo, char *psargs) * info. If 't' is set, check if term is in list of desired terminals * and print it if it is. */ - i = 0; tp = gettty(psinfo); if (*tp == '?' && !found && !xflg) @@ -999,37 +904,9 @@ prcom(int found, psinfo_t *psinfo, char *psargs) (void) printf(" %.*s", wcnt, psinfo->pr_fname); return (1); } - /* - * PRARGSZ == length of cmd arg string. - */ - if (psargs == NULL) { - psa = &psinfo->pr_psargs[0]; - i = PRARGSZ; - tp = &psinfo->pr_psargs[PRARGSZ]; - } else { - psa = psargs; - i = strlen(psargs); - tp = psa + i; - } - for (cp = psa; cp < tp; /* empty */) { - if (*cp == 0) - break; - length = mbtowc(&wchar, cp, MB_LEN_MAX); - if (length < 0 || !iswprint(wchar)) { - (void) printf(" [ %.16s ]", psinfo->pr_fname); - return (1); - } - cp += length; - } - wcnt = namencnt(psa, i, maxlen); -#if 0 - /* dumps core on really long strings */ - (void) printf(" %.*s", wcnt, psa); -#else - (void) putchar(' '); - (void) fwrite(psa, 1, wcnt, stdout); -#endif + printf(" "); + print_psargs(psargs, wflag < 2 ? maxlen : 0); return (1); } diff --git a/usr/src/cmd/ptools/Makefile.bld b/usr/src/cmd/ptools/Makefile.bld index 8db9c449fe..efbfee9d04 100644 --- a/usr/src/cmd/ptools/Makefile.bld +++ b/usr/src/cmd/ptools/Makefile.bld @@ -22,6 +22,7 @@ # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2015 Joyent, Inc. # # Copyright 2019 Joyent, Inc. @@ -79,6 +80,12 @@ CERRWARN_pwait += -_smatch=off CERRWARN += $(CERRWARN_$(PROG)) +# +# Common code definitions +# +COBJS = ptools_common.o +CINC = -I../../common + # pargs depends on ../../common/elfcap components # pmadvise depends on pmap components @@ -89,14 +96,32 @@ CPPFLAGS_pargs = -I$(ELFCAP) OBJS_pargs = elfcap.o SRCS_pargs = $(ELFCAP)/elfcap.c -CPPFLAGS_pmap = -I$(PMAP) -OBJS_pmap = pmap_common.o +CPPFLAGS_pmap = -I$(PMAP) $(CINC) +OBJS_pmap = pmap_common.o $(COBJS) SRCS_pmap = $(PMAP)/pmap_common.c -CPPFLAGS_pmadvise = -I$(PMAP) -OBJS_pmadvise = pmap_common.o +CPPFLAGS_pmadvise = -I$(PMAP) $(CINC) +OBJS_pmadvise = pmap_common.o $(COBJS) SRCS_pmadvise = $(PMAP)/pmap_common.c +CPPFLAGS_preap = $(CINC) +OBJS_preap = $(COBJS) + +CPPFLAGS_psig = $(CINC) +OBJS_psig = $(COBJS) + +CPPFLAGS_ptime = $(CINC) +OBJS_ptime = $(COBJS) + +CPPFLAGS_ptree = $(CINC) +OBJS_ptree = $(COBJS) + +CPPFLAGS_pwait = $(CINC) +OBJS_pwait = $(COBJS) + +CPPFLAGS_pwdx = $(CINC) +OBJS_pwdx = $(COBJS) + LN_pargs = penv pauxv CPPFLAGS += $(CPPFLAGS_$(PROG)) @@ -124,6 +149,10 @@ pmap_common.o: $(PMAP)/pmap_common.c $(COMPILE.c) $< $(POST_PROCESS_O) +%.o: ../../common/%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + all: $(PROG) $(LN_$(PROG)) ROOTBINLN=$(LN_$(PROG):%=$(ROOTBIN)/%) diff --git a/usr/src/cmd/ptools/common/ptools_common.c b/usr/src/cmd/ptools/common/ptools_common.c new file mode 100644 index 0000000000..a747ab213e --- /dev/null +++ b/usr/src/cmd/ptools/common/ptools_common.c @@ -0,0 +1,50 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#include <sys/feature_tests.h> +#include <stdio.h> +#include <stdarg.h> +#include <sys/types.h> +#include <zone.h> + +/* + * Common routines for ptools. + */ + +int +proc_snprintf(char *_RESTRICT_KYWD s, size_t n, + const char *_RESTRICT_KYWD fmt, ...) +{ + static boolean_t ptools_zroot_valid = B_FALSE; + static const char *ptools_zroot = NULL; + va_list args; + int ret, nret = 0; + + if (ptools_zroot_valid == B_FALSE) { + ptools_zroot_valid = B_TRUE; + ptools_zroot = zone_get_nroot(); + } + + if (ptools_zroot != NULL) { + nret = snprintf(s, n, "%s", ptools_zroot); + if (nret > n) + return (nret); + } + va_start(args, fmt); + ret = vsnprintf(s + nret, n - nret, fmt, args); + va_end(args); + + return (ret + nret); +} diff --git a/usr/src/cmd/ptools/common/ptools_common.h b/usr/src/cmd/ptools/common/ptools_common.h new file mode 100644 index 0000000000..52bcae9e51 --- /dev/null +++ b/usr/src/cmd/ptools/common/ptools_common.h @@ -0,0 +1,36 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#ifndef _PTOOLS_COMMON_H +#define _PTOOLS_COMMON_H + +#include <sys/feature_tests.h> + +/* + * Common functions for the ptools. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int proc_snprintf(char *_RESTRICT_KYWD, size_t, + const char *_RESTRICT_KYWD, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _PTOOLS_COMMON_H */ diff --git a/usr/src/cmd/ptools/pargs/pargs.c b/usr/src/cmd/ptools/pargs/pargs.c index 55876dea65..6d82af21ba 100644 --- a/usr/src/cmd/ptools/pargs/pargs.c +++ b/usr/src/cmd/ptools/pargs/pargs.c @@ -823,6 +823,7 @@ static struct aux_id aux_arr[] = { { AT_BASE, "AT_BASE", at_null }, { AT_FLAGS, "AT_FLAGS", at_null }, { AT_ENTRY, "AT_ENTRY", at_null }, + { AT_RANDOM, "AT_RANDOM", at_null }, { AT_SUN_UID, "AT_SUN_UID", at_uid }, { AT_SUN_RUID, "AT_SUN_RUID", at_uid }, { AT_SUN_GID, "AT_SUN_GID", at_gid }, @@ -842,9 +843,11 @@ static struct aux_id aux_arr[] = { { AT_SUN_AUXFLAGS, "AT_SUN_AUXFLAGS", at_flags }, { AT_SUN_EMULATOR, "AT_SUN_EMULATOR", at_str }, { AT_SUN_BRANDNAME, "AT_SUN_BRANDNAME", at_str }, + { AT_SUN_BRAND_NROOT, "AT_SUN_BRAND_NROOT", at_str }, { AT_SUN_BRAND_AUX1, "AT_SUN_BRAND_AUX1", at_null }, { AT_SUN_BRAND_AUX2, "AT_SUN_BRAND_AUX2", at_null }, { AT_SUN_BRAND_AUX3, "AT_SUN_BRAND_AUX3", at_null }, + { AT_SUN_BRAND_AUX4, "AT_SUN_BRAND_AUX4", at_null }, { AT_SUN_COMMPAGE, "AT_SUN_COMMPAGE", at_null }, { AT_SUN_FPTYPE, "AT_SUN_FPTYPE", at_null }, { AT_SUN_FPSIZE, "AT_SUN_FPSIZE", at_null } diff --git a/usr/src/cmd/ptools/pfiles/pfiles.c b/usr/src/cmd/ptools/pfiles/pfiles.c index dd5ce4af11..474650faa4 100644 --- a/usr/src/cmd/ptools/pfiles/pfiles.c +++ b/usr/src/cmd/ptools/pfiles/pfiles.c @@ -511,6 +511,7 @@ show_sockaddr(const char *str, const struct sockaddr *sa, socklen_t len) case AF_KEY: p = "AF_KEY"; break; case AF_POLICY: p = "AF_POLICY"; break; case AF_LINK: p = "AF_LINK"; break; + case AF_LX_NETLINK: p = "AF_LX_NETLINK"; break; } (void) printf("\t%s: %s\n", str, p); diff --git a/usr/src/cmd/ptools/pflags/pflags.c b/usr/src/cmd/ptools/pflags/pflags.c index 8054a80d3c..f19a945d95 100644 --- a/usr/src/cmd/ptools/pflags/pflags.c +++ b/usr/src/cmd/ptools/pflags/pflags.c @@ -25,7 +25,7 @@ */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright 2015, Joyent, Inc. */ #include <stdio.h> @@ -469,6 +469,9 @@ prwhy(int why) case PR_SUSPENDED: str = "PR_SUSPENDED"; break; + case PR_BRAND: + str = "PR_BRAND"; + break; default: str = buf; (void) sprintf(str, "%d", why); diff --git a/usr/src/cmd/ptools/pmap/pmap.c b/usr/src/cmd/ptools/pmap/pmap.c index 78bfa6b596..03f8bde791 100644 --- a/usr/src/cmd/ptools/pmap/pmap.c +++ b/usr/src/cmd/ptools/pmap/pmap.c @@ -22,6 +22,7 @@ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ #include <stdio.h> @@ -42,6 +43,7 @@ #include <sys/mman.h> #include <sys/lgrp_user.h> #include <libproc.h> +#include "ptools_common.h" #include "pmap_common.h" @@ -199,7 +201,7 @@ main(int argc, char **argv) const char *bar; struct rlimit rlim; struct stat64 statbuf; - char buf[128]; + char buf[PATH_MAX]; int mapfd; int prg_gflags = PGRAB_RDONLY; int prr_flags = 0; @@ -358,7 +360,7 @@ main(int argc, char **argv) proc_unctrl_psinfo(&psinfo); if (Pstate(Pr) != PS_DEAD) { - (void) snprintf(buf, sizeof (buf), + (void) proc_snprintf(buf, sizeof (buf), "/proc/%d/map", (int)psinfo.pr_pid); if ((mapfd = open(buf, O_RDONLY)) < 0) { (void) fprintf(stderr, "%s: cannot " @@ -590,7 +592,7 @@ rmapping_iter(struct ps_prochandle *Pr, proc_map_f *func, void *cd) prmap_t *prmapp, *pmp; ssize_t n; - (void) snprintf(mapname, sizeof (mapname), + (void) proc_snprintf(mapname, sizeof (mapname), "/proc/%d/rmap", (int)Pstatus(Pr)->pr_pid); if ((mapfd = open(mapname, O_RDONLY)) < 0 || fstat(mapfd, &st) != 0) { @@ -631,7 +633,7 @@ xmapping_iter(struct ps_prochandle *Pr, proc_xmap_f *func, void *cd, int doswap) prxmap_t *prmapp, *pmp; ssize_t n; - (void) snprintf(mapname, sizeof (mapname), + (void) proc_snprintf(mapname, sizeof (mapname), "/proc/%d/xmap", (int)Pstatus(Pr)->pr_pid); if ((mapfd = open(mapname, O_RDONLY)) < 0 || fstat(mapfd, &st) != 0) { diff --git a/usr/src/cmd/ptools/pmap/pmap_common.c b/usr/src/cmd/ptools/pmap/pmap_common.c index fff55ffdbc..81f42d67f7 100644 --- a/usr/src/cmd/ptools/pmap/pmap_common.c +++ b/usr/src/cmd/ptools/pmap/pmap_common.c @@ -37,6 +37,7 @@ #include <sys/types.h> #include "pmap_common.h" +#include "ptools_common.h" /* * We compare the high memory addresses since stacks are faulted in from @@ -88,7 +89,7 @@ make_name(struct ps_prochandle *Pr, int lflag, uintptr_t addr, return (NULL); /* first see if we can find a path via /proc */ - (void) snprintf(path, sizeof (path), "/proc/%d/path/%s", + (void) proc_snprintf(path, sizeof (path), "/proc/%d/path/%s", (int)Psp->pr_pid, mapname); len = readlink(path, buf, bufsz - 1); if (len >= 0) { @@ -97,7 +98,7 @@ make_name(struct ps_prochandle *Pr, int lflag, uintptr_t addr, } /* fall back to object information reported by /proc */ - (void) snprintf(path, sizeof (path), + (void) proc_snprintf(path, sizeof (path), "/proc/%d/object/%s", (int)Psp->pr_pid, mapname); if (stat(path, &statb) == 0) { dev_t dev = statb.st_dev; diff --git a/usr/src/cmd/ptools/preap/preap.c b/usr/src/cmd/ptools/preap/preap.c index 8d30b8027c..6d8eb75611 100644 --- a/usr/src/cmd/ptools/preap/preap.c +++ b/usr/src/cmd/ptools/preap/preap.c @@ -24,8 +24,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdlib.h> #include <unistd.h> @@ -37,6 +35,8 @@ #include <sys/types.h> #include <sys/wait.h> #include <libproc.h> +#include <limits.h> +#include "ptools_common.h" #define NOREAP_TIME 60 /* wait 60 seconds before allow a reap */ @@ -53,11 +53,11 @@ intr(int sig) static int open_usage(pid_t pid, int *perr) { - char path[64]; + char path[PATH_MAX]; struct stat64 st; int fd; - (void) snprintf(path, sizeof (path), "/proc/%d/usage", (int)pid); + (void) proc_snprintf(path, sizeof (path), "/proc/%d/usage", (int)pid); /* * Attempt to open the usage file, and return the fd if we can diff --git a/usr/src/cmd/ptools/psig/psig.c b/usr/src/cmd/ptools/psig/psig.c index 2790463b9a..6733c7b8d4 100644 --- a/usr/src/cmd/ptools/psig/psig.c +++ b/usr/src/cmd/ptools/psig/psig.c @@ -21,6 +21,7 @@ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ #include <stdio.h> @@ -35,6 +36,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <libproc.h> +#include "ptools_common.h" /* evil knowledge of libc internals */ #include "../../../lib/libc/inc/thr_uberdata.h" @@ -170,7 +172,7 @@ lwp_iter(void *cd, const lwpstatus_t *lwpstatus) static int look(char *arg) { - char pathname[100]; + char pathname[PATH_MAX]; struct stat statb; int fd = -1; int sig, gcode; @@ -197,7 +199,8 @@ look(char *arg) (void) memcpy(&psinfo, psinfop, sizeof (psinfo_t)); proc_unctrl_psinfo(&psinfo); - (void) sprintf(pathname, "/proc/%d/sigact", (int)psinfo.pr_pid); + (void) proc_snprintf(pathname, sizeof (pathname), "/proc/%d/sigact", + (int)psinfo.pr_pid); if ((fd = open(pathname, O_RDONLY)) < 0) { perr("open sigact"); goto look_error; @@ -211,7 +214,7 @@ look(char *arg) action = malloc(maxsig * sizeof (struct sigaction)); if (action == NULL) { (void) fprintf(stderr, - "%s: cannot malloc() space for %d sigaction structures\n", + "%s: cannot malloc() space for %d sigaction structures\n", command, maxsig); goto look_error; } diff --git a/usr/src/cmd/ptools/ptime/ptime.c b/usr/src/cmd/ptools/ptime/ptime.c index 2da2f8d281..b1f53593c2 100644 --- a/usr/src/cmd/ptools/ptime/ptime.c +++ b/usr/src/cmd/ptools/ptime/ptime.c @@ -41,6 +41,8 @@ #include <sys/time.h> #include <signal.h> #include <libproc.h> +#include <limits.h> +#include "ptools_common.h" static int look(pid_t); static void hr_min_sec(char *, long); @@ -189,7 +191,7 @@ main(int argc, char **argv) static int look(pid_t pid) { - char pathname[100]; + char pathname[PATH_MAX]; int rval = 0; int fd; psinfo_t psinfo; @@ -203,7 +205,8 @@ look(pid_t pid) if (proc_get_psinfo(pid, &psinfo) < 0) return (perr("read psinfo")); - (void) sprintf(pathname, "/proc/%d/usage", (int)pid); + (void) proc_snprintf(pathname, sizeof (pathname), "/proc/%d/usage", + (int)pid); if ((fd = open(pathname, O_RDONLY)) < 0) return (perr("open usage")); diff --git a/usr/src/cmd/ptools/pwait/pwait.c b/usr/src/cmd/ptools/pwait/pwait.c index 0733c355cf..ec11573477 100644 --- a/usr/src/cmd/ptools/pwait/pwait.c +++ b/usr/src/cmd/ptools/pwait/pwait.c @@ -23,8 +23,6 @@ * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <stdio_ext.h> #include <ctype.h> @@ -39,6 +37,8 @@ #include <poll.h> #include <procfs.h> #include <sys/resource.h> +#include <limits.h> +#include "ptools_common.h" static int count_my_files(); static char *command; @@ -49,6 +49,7 @@ static char *command; int main(int argc, char **argv) { + char buf[PATH_MAX]; unsigned long remain = 0; struct pollfd *pollfd; struct pollfd *pfd; @@ -75,10 +76,12 @@ main(int argc, char **argv) (void) fprintf(stderr, "usage:\t%s [-v] pid ...\n", command); (void) fprintf(stderr, " (wait for processes to terminate)\n"); (void) fprintf(stderr, - " -v: verbose; report terminations to standard out\n"); + " -v: verbose; report terminations to standard out\n"); return (2); } + (void) proc_snprintf(buf, sizeof (buf), "/proc/"); + /* make sure we have enough file descriptors */ if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { int nfiles = count_my_files(); @@ -87,8 +90,8 @@ main(int argc, char **argv) rlim.rlim_cur = argc + nfiles + SLOP; if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) { (void) fprintf(stderr, - "%s: insufficient file descriptors\n", - command); + "%s: insufficient file descriptors\n", + command); return (2); } } @@ -108,11 +111,11 @@ main(int argc, char **argv) if (strchr(arg, '/') != NULL) (void) strncpy(psinfofile, arg, sizeof (psinfofile)); else { - (void) strcpy(psinfofile, "/proc/"); + (void) strcpy(psinfofile, buf); (void) strncat(psinfofile, arg, sizeof (psinfofile)-6); } (void) strncat(psinfofile, "/psinfo", - sizeof (psinfofile)-strlen(psinfofile)); + sizeof (psinfofile)-strlen(psinfofile)); pfd = &pollfd[i]; if ((pfd->fd = open(psinfofile, O_RDONLY)) >= 0) { @@ -126,7 +129,7 @@ main(int argc, char **argv) pfd->revents = 0; } else if (errno == ENOENT) { (void) fprintf(stderr, "%s: no such process: %s\n", - command, arg); + command, arg); } else { perror(arg); } @@ -160,9 +163,9 @@ main(int argc, char **argv) if (pread(pfd->fd, &psinfo, sizeof (psinfo), (off_t)0) == sizeof (psinfo)) { - (void) printf( - "%s: terminated, wait status 0x%.4x\n", - arg, psinfo.pr_wstat); + (void) printf("%s: terminated, " + "wait status 0x%.4x\n", + arg, psinfo.pr_wstat); } else { (void) printf( "%s: terminated\n", arg); @@ -170,10 +173,10 @@ main(int argc, char **argv) } if (pfd->revents & POLLNVAL) (void) printf("%s: system process\n", - arg); + arg); if (pfd->revents & ~(POLLPRI|POLLHUP|POLLNVAL)) (void) printf("%s: unknown error\n", - arg); + arg); } (void) close(pfd->fd); diff --git a/usr/src/cmd/ptools/pwdx/pwdx.c b/usr/src/cmd/ptools/pwdx/pwdx.c index adf42c0877..4a2c6f0c3f 100644 --- a/usr/src/cmd/ptools/pwdx/pwdx.c +++ b/usr/src/cmd/ptools/pwdx/pwdx.c @@ -22,10 +22,9 @@ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <unistd.h> #include <string.h> @@ -33,6 +32,8 @@ #include <libproc.h> #include <sys/param.h> +#include "ptools_common.h" + static char *command; static int @@ -49,7 +50,7 @@ show_cwd(const char *arg) return (1); } - (void) snprintf(proc, sizeof (proc), "/proc/%d/path/cwd", + (void) proc_snprintf(proc, sizeof (proc), "/proc/%d/path/cwd", (int)p.pr_pid); if ((ret = readlink(proc, cwd, sizeof (cwd) - 1)) <= 0) { diff --git a/usr/src/cmd/rcap/common/utils.c b/usr/src/cmd/rcap/common/utils.c index 799fdcef23..dd511c7c50 100644 --- a/usr/src/cmd/rcap/common/utils.c +++ b/usr/src/cmd/rcap/common/utils.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent, Inc. All rights reserved. */ #include <sys/param.h> @@ -257,77 +258,3 @@ xatoi(char *p) return (i); } } - -/* - * get_running_zones() calls zone_list(2) to find out how many zones are - * running. It then calls zone_list(2) again to fetch the list of running - * zones (stored in *zents). - */ -int -get_running_zones(uint_t *nzents, zone_entry_t **zents) -{ - zoneid_t *zids; - uint_t nzents_saved; - int i; - zone_entry_t *zentp; - zone_state_t zstate; - - *zents = NULL; - if (zone_list(NULL, nzents) != 0) { - warn(gettext("could not get zoneid list\n")); - return (E_ERROR); - } - -again: - if (*nzents == 0) - return (E_SUCCESS); - - if ((zids = (zoneid_t *)calloc(*nzents, sizeof (zoneid_t))) == NULL) { - warn(gettext("out of memory: zones will not be capped\n")); - return (E_ERROR); - } - - nzents_saved = *nzents; - - if (zone_list(zids, nzents) != 0) { - warn(gettext("could not get zone list\n")); - free(zids); - return (E_ERROR); - } - if (*nzents != nzents_saved) { - /* list changed, try again */ - free(zids); - goto again; - } - - *zents = calloc(*nzents, sizeof (zone_entry_t)); - if (*zents == NULL) { - warn(gettext("out of memory: zones will not be capped\n")); - free(zids); - return (E_ERROR); - } - - zentp = *zents; - for (i = 0; i < *nzents; i++) { - char name[ZONENAME_MAX]; - - if (getzonenamebyid(zids[i], name, sizeof (name)) < 0) { - warn(gettext("could not get name for " - "zoneid %d\n"), zids[i]); - continue; - } - - (void) strlcpy(zentp->zname, name, sizeof (zentp->zname)); - zentp->zid = zids[i]; - if (zone_get_state(name, &zstate) != Z_OK || - zstate != ZONE_STATE_RUNNING) - continue; - - - zentp++; - } - *nzents = zentp - *zents; - - free(zids); - return (E_SUCCESS); -} diff --git a/usr/src/cmd/rcap/common/utils.h b/usr/src/cmd/rcap/common/utils.h index 7196cfb4ce..cf2e17c080 100644 --- a/usr/src/cmd/rcap/common/utils.h +++ b/usr/src/cmd/rcap/common/utils.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent, Inc. All rights reserved. */ #ifndef _UTILS_H @@ -98,7 +99,6 @@ extern void vdprintfe(int, const char *, va_list); extern void dprintfe(int, char *, ...); extern void hrt2ts(hrtime_t, timestruc_t *); extern int xatoi(char *); -extern int get_running_zones(uint_t *, zone_entry_t **); #ifdef __cplusplus } diff --git a/usr/src/cmd/rcap/rcapadm/rcapadm.c b/usr/src/cmd/rcap/rcapadm/rcapadm.c index 92888b2071..b92115469a 100644 --- a/usr/src/cmd/rcap/rcapadm/rcapadm.c +++ b/usr/src/cmd/rcap/rcapadm/rcapadm.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent, Inc. All rights reserved. */ #include <sys/types.h> @@ -145,20 +146,29 @@ out: scf_handle_destroy(h); } +static int +set_zone_cap(char *zonename, uint64_t mcap) +{ + char cmd[128 + ZONENAME_MAX]; + + (void) snprintf(cmd, sizeof (cmd), "/usr/bin/prctl -r " + "-n zone.max-physical-memory -v %llu -i zone %s", mcap, zonename); + return (system(cmd)); +} + /* * Update the in-kernel memory cap for the specified zone. */ static int update_zone_mcap(char *zonename, char *maxrss) { - zoneid_t zone_id; uint64_t num; if (getzoneid() != GLOBAL_ZONEID || zonecfg_in_alt_root()) return (E_SUCCESS); /* get the running zone from the kernel */ - if ((zone_id = getzoneidbyname(zonename)) == -1) { + if (getzoneidbyname(zonename) == -1) { (void) fprintf(stderr, gettext("zone '%s' must be running\n"), zonename); return (E_ERROR); @@ -169,7 +179,7 @@ update_zone_mcap(char *zonename, char *maxrss) return (E_ERROR); } - if (zone_setattr(zone_id, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) { + if (set_zone_cap(zonename, num) == -1) { (void) fprintf(stderr, gettext("could not set memory " "cap for zone '%s'\n"), zonename); return (E_ERROR); diff --git a/usr/src/cmd/rcap/rcapd/Makefile b/usr/src/cmd/rcap/rcapd/Makefile index 77f4d6812e..355b5eef0a 100644 --- a/usr/src/cmd/rcap/rcapd/Makefile +++ b/usr/src/cmd/rcap/rcapd/Makefile @@ -22,6 +22,8 @@ # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2020 Joyent, Inc. +# include ../../Makefile.cmd include ../../Makefile.cmd.64 @@ -62,7 +64,7 @@ LINTSRCS = rcapd_main.c \ $(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG CPPFLAGS += -DDEBUG_MSG CPPFLAGS += -I$(COMMON_DIR) -LDLIBS += -lkstat -lproc -lproject -lzonecfg -lumem -lscf +LDLIBS += -lkstat -lproc -lproject -lumem -lscf LDLIBS += $(EXTRA_LDLIBS) LINTFLAGS64 += -u diff --git a/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c b/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c index db86aa6276..88403dda37 100644 --- a/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c +++ b/usr/src/cmd/rcap/rcapd/rcapd_collection_zone.c @@ -21,16 +21,17 @@ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2011 Joyent, Inc. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include <procfs.h> #include <project.h> #include <stdlib.h> #include <strings.h> #include <zone.h> #include <libzonecfg.h> +#include <dirent.h> +#include <libproc.h> #include "rcapd.h" #include "utils.h" @@ -39,61 +40,117 @@ extern boolean_t gz_capped; /* round up to next y = 2^n */ #define ROUNDUP(x, y) (((x) + ((y) - 1)) & ~((y) - 1)) -static void -update_zone(zone_entry_t *zent, void *walk_data) +static struct ps_prochandle * +grab_zone_proc(zoneid_t zid) { - void(*update_notification_cb)(char *, char *, int, uint64_t, int) = - (void(*)(char *, char *, int, uint64_t, int))walk_data; - int changes; - int64_t max_rss; + DIR *dirp; + struct dirent *dentp; + int pid, pid_self, tmp; + psinfo_t psinfo; + struct ps_prochandle *pr = NULL; + + pid_self = getpid(); + + if ((dirp = opendir("/proc")) == NULL) + return (NULL); + + while (dentp = readdir(dirp)) { + pid = atoi(dentp->d_name); + + /* Skip self */ + if (pid == pid_self) + continue; + + if (proc_get_psinfo(pid, &psinfo) != 0) + continue; + + if (psinfo.pr_zoneid != zid) + continue; + + /* attempt to grab process */ + if ((pr = Pgrab(pid, 0, &tmp)) != NULL) { + if (Psetflags(pr, PR_RLC) != 0) { + Prelease(pr, 0); + } + if (Pcreate_agent(pr) == 0) { + if (pr_getzoneid(pr) != zid) { + Prelease(pr, 0); + continue; + } + + (void) closedir(dirp); + return (pr); + } else { + Prelease(pr, 0); + } + } + } + + (void) closedir(dirp); + return (NULL); +} + +static uint64_t +get_zone_cap(zoneid_t zid) +{ + rctlblk_t *rblk; uint64_t mcap; - lcollection_t *lcol; - rcid_t colid; + struct ps_prochandle *pr; - if (zone_getattr(zent->zid, ZONE_ATTR_PHYS_MCAP, &mcap, - sizeof (mcap)) != -1 && mcap != 0) - max_rss = ROUNDUP(mcap, 1024) / 1024; - else - max_rss = 0; - - if (zent->zid == GLOBAL_ZONEID) { - if (max_rss > 0) - gz_capped = B_TRUE; - else - gz_capped = B_FALSE; + if ((rblk = (rctlblk_t *)malloc(rctlblk_size())) == NULL) + return (UINT64_MAX); + + if ((pr = grab_zone_proc(zid)) == NULL) { + free(rblk); + return (UINT64_MAX); } + if (pr_getrctl(pr, "zone.max-physical-memory", NULL, rblk, + RCTL_FIRST)) { + Pdestroy_agent(pr); + Prelease(pr, 0); + free(rblk); + return (UINT64_MAX); + } - colid.rcid_type = RCIDT_ZONE; - colid.rcid_val = zent->zid; + Pdestroy_agent(pr); + Prelease(pr, 0); - lcol = lcollection_insert_update(&colid, max_rss, zent->zname, - &changes); - if (update_notification_cb != NULL) - update_notification_cb("zone", zent->zname, changes, max_rss, - (lcol != NULL) ? lcol->lcol_mark : 0); + mcap = rctlblk_get_value(rblk); + free(rblk); + return (mcap); } - +/* + * For zones, rcapd only caps the global zone, since each non-global zone + * caps itself. + */ /* ARGSUSED */ void lcollection_update_zone(lcollection_update_type_t ut, void(*update_notification_cb)(char *, char *, int, uint64_t, int)) { - int i; - uint_t nzents; - zone_entry_t *zents; - - /* - * Enumerate running zones. - */ - if (get_running_zones(&nzents, &zents) != 0) - return; - - for (i = 0; i < nzents; i++) { - update_zone(&zents[i], (void *)update_notification_cb); + int changes; + int64_t max_rss; + uint64_t mcap; + lcollection_t *lcol; + rcid_t colid; + mcap = get_zone_cap(GLOBAL_ZONEID); + if (mcap != 0 && mcap != UINT64_MAX) { + max_rss = ROUNDUP(mcap, 1024) / 1024; + gz_capped = B_TRUE; + } else { + max_rss = UINT64_MAX / 1024; + gz_capped = B_FALSE; } - free(zents); + colid.rcid_type = RCIDT_ZONE; + colid.rcid_val = GLOBAL_ZONEID; + + lcol = lcollection_insert_update(&colid, max_rss, GLOBAL_ZONENAME, + &changes); + if (update_notification_cb != NULL) + update_notification_cb("zone", GLOBAL_ZONENAME, changes, + max_rss, (lcol != NULL) ? lcol->lcol_mark : 0); } diff --git a/usr/src/cmd/rcap/rcapd/rcapd_scanner.c b/usr/src/cmd/rcap/rcapd/rcapd_scanner.c index 3f09a7f4ed..458e8f3bef 100644 --- a/usr/src/cmd/rcap/rcapd/rcapd_scanner.c +++ b/usr/src/cmd/rcap/rcapd/rcapd_scanner.c @@ -21,6 +21,7 @@ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2012 Joyent, Inc. All rights reserved. */ #include <sys/mman.h> @@ -549,7 +550,7 @@ pageout(pid_t pid, struct ps_prochandle *Pr, caddr_t start, caddr_t end) errno = 0; res = pr_memcntl(Pr, start, (end - start), MC_SYNC, - (caddr_t)(MS_ASYNC | MS_INVALIDATE), 0, 0); + (caddr_t)(MS_ASYNC | MS_INVALCURPROC), 0, 0); debug_high("pr_memcntl [%p-%p): %d", (void *)start, (void *)end, res); /* diff --git a/usr/src/cmd/rcap/rcapstat/Makefile b/usr/src/cmd/rcap/rcapstat/Makefile index 1b4e0e6180..a886b05782 100644 --- a/usr/src/cmd/rcap/rcapstat/Makefile +++ b/usr/src/cmd/rcap/rcapstat/Makefile @@ -22,6 +22,8 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2020 Joyent, Inc. +# .KEEP_STATE: .SUFFIXES: @@ -29,7 +31,7 @@ include ../../Makefile.cmd COMMON_DIR= ../common # for Makefile.com -SRCS = rcapstat.c \ +SRCS = rcapstat.c \ utils.c LINTSRCS = $(COMMON_DIR)/utils.c \ @@ -41,9 +43,9 @@ STATCOMMONDIR = $(SRC)/cmd/stat/common STAT_COMMON_OBJS = timestamp.o STAT_COMMON_SRCS = $(STAT_COMMON_OBJS:%.o=$(STATCOMMONDIR)/%.c) -$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG -CPPFLAGS += -I$(COMMON_DIR) -I$(STATCOMMONDIR) -LDLIBS += -lumem -lzonecfg -lscf +$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG +CPPFLAGS += -I$(COMMON_DIR) -I$(STATCOMMONDIR) +LDLIBS += -lumem -lscf LDFLAGS += $(MAPFILE.NGB:%=-M%) LINTFLAGS += $(LDLIBS) -mnu @@ -54,7 +56,7 @@ OBJS = $(SRCS:%.c=%.o) rcapd_conf.o POFILES = $(OBJS:%.o=%.po) POFILE = p$(PROG).po -CLOBBERFILES += rcapd_conf.c $(POFILES) $(POFILE) +CLOBBERFILES += rcapd_conf.c $(POFILES) $(POFILE) .NO_PARALLEL: .PARALLEL: $(OBJS) $(LINTFILES) diff --git a/usr/src/cmd/rcap/rcapstat/rcapstat.c b/usr/src/cmd/rcap/rcapstat/rcapstat.c index 0632250fed..2838c6e5d5 100644 --- a/usr/src/cmd/rcap/rcapstat/rcapstat.c +++ b/usr/src/cmd/rcap/rcapstat/rcapstat.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, Joyent, Inc. All rights reserved. */ #include <sys/types.h> @@ -72,6 +73,8 @@ typedef struct col { static col_t *col_head; static int ncol; +#define RCAPD_NA "rcapd is not active (try zonememstat)\n" + static col_t * col_find(rcid_t id) { @@ -152,7 +155,7 @@ read_stats(rcid_type_t stat_type) struct stat st; if ((fd = open(STAT_FILE_DEFAULT, O_RDONLY)) < 0) { - warn(gettext("rcapd is not active\n")); + warn(gettext(RCAPD_NA)); return (E_ERROR); } @@ -173,7 +176,7 @@ read_stats(rcid_type_t stat_type) pid = hdr.rs_pid; (void) snprintf(procfile, 20, "/proc/%lld/psinfo", pid); if ((proc_fd = open(procfile, O_RDONLY)) < 0) { - warn(gettext("rcapd is not active\n")); + warn(gettext(RCAPD_NA)); (void) close(fd); return (E_ERROR); } diff --git a/usr/src/cmd/savecore/Makefile.com b/usr/src/cmd/savecore/Makefile.com index 1f6e09b231..c4ba4f89f0 100644 --- a/usr/src/cmd/savecore/Makefile.com +++ b/usr/src/cmd/savecore/Makefile.com @@ -21,7 +21,7 @@ # # Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. # -# Copyright (c) 2018, Joyent, Inc. +# Copyright 2019 Joyent, Inc. PROG= savecore SRCS= ../savecore.c ../../../uts/common/os/compress.c @@ -54,12 +54,14 @@ BZIP2OBJS = bz2blocksort.o \ bz2crctable.o \ bz2huffman.o +CHACHAOBJ = chacha.o + .KEEP_STATE: all: $(PROG) -$(PROG): $(OBJS) $(BZIP2OBJS) - $(LINK.c) -o $(PROG) $(OBJS) $(BZIP2OBJS) $(LDLIBS) +$(PROG): $(OBJS) $(BZIP2OBJS) $(CHACHAOBJ) + $(LINK.c) -o $(PROG) $(OBJS) $(BZIP2OBJS) $(CHACHAOBJ) $(LDLIBS) $(POST_PROCESS) clean: @@ -95,3 +97,8 @@ include ../../Makefile.targ bz2%.o: ../../../common/bzip2/%.c $(COMPILE.c) -o $@ -I$(SRC)/common -I$(SRC)/common/bzip2 $< $(POST_PROCESS_O) + +%.o: ../../../common/crypto/chacha/%.c + $(COMPILE.c) -o $@ -I$(SRC)/common -I$(SRC)/common/crypto/chacha $< + $(POST_PROCESS_O) + diff --git a/usr/src/cmd/savecore/savecore.c b/usr/src/cmd/savecore/savecore.c index 9b8e43e488..e0e281f61e 100644 --- a/usr/src/cmd/savecore/savecore.c +++ b/usr/src/cmd/savecore/savecore.c @@ -42,6 +42,8 @@ #include <atomic.h> #include <libnvpair.h> #include <libintl.h> +#include <assert.h> +#include <strings.h> #include <sys/mem.h> #include <sys/statvfs.h> #include <sys/dumphdr.h> @@ -55,6 +57,7 @@ #include <sys/fm/util.h> #include <fm/libfmevent.h> #include <sys/int_fmtio.h> +#include <crypto/chacha/chacha.h> /* fread/fwrite buffer size */ @@ -74,6 +77,8 @@ static long pagesize; /* dump pagesize */ static int dumpfd = -1; /* dumpfile descriptor */ static boolean_t have_dumpfile = B_TRUE; /* dumpfile existence */ static dumphdr_t corehdr, dumphdr; /* initial and terminal dumphdrs */ +static dump_crypt_t dcrypt; /* dump encryption header */ +static size_t dumphdr_size; /* size of dump header */ static boolean_t dump_incomplete; /* dumphdr indicates incomplete */ static boolean_t fm_panic; /* dump is the result of fm_panic */ static offset_t endoff; /* offset of end-of-dump header */ @@ -165,7 +170,8 @@ static void usage(void) { (void) fprintf(stderr, - "usage: %s [-L | -r] [-vd] [-f dumpfile] [dirname]\n", progname); + "usage: %s [-L | -r] [-vd] [-k keyfile] [-f dumpfile] [dirname]\n", + progname); exit(1); } @@ -329,6 +335,19 @@ Pwrite(int fd, void *buf, size_t size, off64_t off) strerror(errno)); } +static void +Read(int fd, void *buf, size_t size) +{ + ssize_t sz = read(fd, buf, size); + + if (sz < 0) + logprint(SC_SL_ERR | SC_EXIT_ERR, + "read: %s", strerror(errno)); + else if (sz != size) + logprint(SC_SL_ERR | SC_EXIT_ERR, + "read: size %ld != %ld", sz, size); +} + static void * Zalloc(size_t size) { @@ -362,9 +381,9 @@ read_dumphdr(void) dumpfd = Open(dumpfile, O_RDWR | O_DSYNC, 0644); endoff = llseek(dumpfd, -DUMP_OFFSET, SEEK_END) & -DUMP_OFFSET; Pread(dumpfd, &dumphdr, sizeof (dumphdr), endoff); - Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + sizeof (dumphdr)); pagesize = dumphdr.dump_pagesize; + dumphdr_size = sizeof (dumphdr); if (dumphdr.dump_magic != DUMP_MAGIC) logprint(SC_SL_NONE | SC_EXIT_PEND, "bad magic number %x", @@ -384,6 +403,20 @@ read_dumphdr(void) "dump is from %u-bit kernel - cannot save on %u-bit kernel", dumphdr.dump_wordsize, DUMP_WORDSIZE); + if (dumphdr.dump_flags & DF_ENCRYPTED) { + /* + * If our dump is encrypted, our encryption header follows + * our dump header. Read it, and then increment our + * dumphdr_size to assure that reads of data following the + * encryption header account for it. + */ + Pread(dumpfd, &dcrypt, sizeof (dcrypt), + endoff + sizeof (dumphdr)); + dumphdr_size += sizeof (dcrypt); + } + + Pread(dumpfd, &datahdr, sizeof (datahdr), endoff + dumphdr_size); + if (datahdr.dump_datahdr_magic == DUMP_DATAHDR_MAGIC) { if (datahdr.dump_datahdr_version != DUMP_DATAHDR_VERSION) logprint(SC_SL_NONE | SC_IF_VERBOSE | SC_EXIT_PEND, @@ -500,19 +533,76 @@ build_dump_map(int corefd, const pfn_t *pfn_table) free(inbuf); } +static void +Decrypt(offset_t dumpoff, len_t nb, char *buf, size_t sz, uint8_t *key) +{ + size_t nelems = nb / sizeof (uint64_t), i; + uint64_t *clear = (uint64_t *)buf; + uint64_t *stream = (uint64_t *)(buf + sz); + uint64_t *crypt = (uint64_t *)(buf + (2 * sz)); + uint64_t ctr = dumpoff >> DUMP_CRYPT_BLOCKSHIFT; + chacha_ctx_t ctx; + + /* + * If our size is not 8-byte aligned, prepare our ciphertext to the + * next 8-byte boundary. + */ + if (nb & (sizeof (uint64_t) - 1)) { + assert(nb < sz); + nelems++; + } + + chacha_keysetup(&ctx, key, DUMP_CRYPT_KEYLEN * 8, 0); + chacha_ivsetup(&ctx, dcrypt.dump_crypt_nonce, (uint8_t *)&ctr); + + for (i = 0; i < nelems; i++) { + stream[i] = dumpoff; + dumpoff += sizeof (uint64_t); + } + + chacha_encrypt_bytes(&ctx, (uint8_t *)stream, (uint8_t *)crypt, nb); + + for (i = 0; i < nelems; i++) + clear[i] ^= crypt[i]; +} + +static void +Verify(uint8_t *key) +{ + chacha_ctx_t ctx; + uint8_t hmac[DUMP_CRYPT_HMACLEN]; + + chacha_keysetup(&ctx, key, DUMP_CRYPT_KEYLEN * 8, 0); + chacha_ivsetup(&ctx, dcrypt.dump_crypt_nonce, NULL); + + chacha_encrypt_bytes(&ctx, (uint8_t *)&dumphdr.dump_utsname, + (uint8_t *)hmac, DUMP_CRYPT_HMACLEN); + + if (bcmp(hmac, &dcrypt.dump_crypt_hmac, DUMP_CRYPT_HMACLEN) == 0) + return; + + logprint(SC_SL_NONE | SC_EXIT_ERR, + "provided key does not match encryption key"); +} + /* * Copy whole sections of the dump device to the file. */ static void Copy(offset_t dumpoff, len_t nb, offset_t *offp, int fd, char *buf, - size_t sz) + size_t sz, uint8_t *key) { size_t nr; offset_t off = *offp; while (nb > 0) { nr = sz < nb ? sz : (size_t)nb; + Pread(dumpfd, buf, nr, dumpoff); + + if (dumphdr.dump_flags & DF_ENCRYPTED) + Decrypt(dumpoff, nr, buf, sz, key); + Pwrite(fd, buf, nr, off); off += nr; dumpoff += nr; @@ -567,21 +657,56 @@ CopyPages(offset_t *offp, int fd, char *buf, size_t sz) * Update corehdr with new offsets. */ static void -copy_crashfile(const char *corefile) +copy_crashfile(const char *corefile, const char *keyfile) { int corefd = Open(corefile, O_WRONLY | O_CREAT | O_TRUNC, 0644); + uint8_t keybuf[DUMP_CRYPT_KEYLEN]; size_t bufsz = FBUFSIZE; - char *inbuf = Zalloc(bufsz); + char *inbuf; offset_t coreoff; size_t nb; + uint8_t *key = NULL; + + if (dumphdr.dump_flags & DF_ENCRYPTED) { + int keyfd; + + if (dcrypt.dump_crypt_algo != DUMP_CRYPT_ALGO_CHACHA20) { + logprint(SC_SL_NONE | SC_EXIT_ERR, "unrecognized dump " + "encryption algorithm %u", dcrypt.dump_crypt_algo); + } + + if (keyfile == NULL) { + logprint(SC_SL_NONE | SC_EXIT_ERR, "dump is encrypted; " + "key must be provided"); + } - logprint(SC_SL_ERR | SC_IF_VERBOSE, - "Copying %s to %s/%s\n", dumpfile, savedir, corefile); + keyfd = Open(keyfile, O_RDONLY, 0600); + Read(keyfd, keybuf, sizeof (keybuf)); + (void) close(keyfd); + + /* + * For the encrypted case, we triple our buffer size to + * allow for the stream buffer and the ciphertext buffer. + */ + inbuf = Zalloc(bufsz * 3); + key = keybuf; + Verify(key); + + logprint(SC_SL_ERR | SC_IF_VERBOSE, "Decrypting and copying " + "%s to %s/%s\n", dumpfile, savedir, corefile); + } else { + inbuf = Zalloc(bufsz); + + logprint(SC_SL_ERR | SC_IF_VERBOSE, + "Copying %s to %s/%s\n", dumpfile, savedir, corefile); + } /* - * This dump file is still compressed + * This dump file is still compressed -- but it will no longer be + * encrypted. */ corehdr.dump_flags |= DF_COMPRESSED | DF_VALID; + corehdr.dump_flags &= ~DF_ENCRYPTED; /* * Leave room for corehdr, it is updated and written last @@ -595,7 +720,7 @@ copy_crashfile(const char *corefile) coreoff = roundup(coreoff, pagesize); corehdr.dump_ksyms = coreoff; Copy(dumphdr.dump_ksyms, dumphdr.dump_ksyms_csize, &coreoff, corefd, - inbuf, bufsz); + inbuf, bufsz, key); /* * Save the pfn table. @@ -603,7 +728,7 @@ copy_crashfile(const char *corefile) coreoff = roundup(coreoff, pagesize); corehdr.dump_pfn = coreoff; Copy(dumphdr.dump_pfn, dumphdr.dump_npages * sizeof (pfn_t), &coreoff, - corefd, inbuf, bufsz); + corefd, inbuf, bufsz, key); /* * Save the dump map. @@ -611,7 +736,7 @@ copy_crashfile(const char *corefile) coreoff = roundup(coreoff, pagesize); corehdr.dump_map = coreoff; Copy(dumphdr.dump_map, dumphdr.dump_nvtop * sizeof (mem_vtop_t), - &coreoff, corefd, inbuf, bufsz); + &coreoff, corefd, inbuf, bufsz, key); /* * Save the data pages. @@ -620,7 +745,7 @@ copy_crashfile(const char *corefile) corehdr.dump_data = coreoff; if (datahdr.dump_data_csize != 0) Copy(dumphdr.dump_data, datahdr.dump_data_csize, &coreoff, - corefd, inbuf, bufsz); + corefd, inbuf, bufsz, key); else CopyPages(&coreoff, corefd, inbuf, bufsz); @@ -1657,6 +1782,8 @@ main(int argc, char *argv[]) struct rlimit rl; long filebounds = -1; char namelist[30], corefile[30], boundstr[30]; + char *keyfile = NULL; + dumpfile = NULL; startts = gethrtime(); @@ -1672,7 +1799,7 @@ main(int argc, char *argv[]) if (savedir != NULL) savedir = strdup(savedir); - while ((c = getopt(argc, argv, "Lvcdmf:r")) != EOF) { + while ((c = getopt(argc, argv, "Lvcdmf:rk:")) != EOF) { switch (c) { case 'L': livedump++; @@ -1696,6 +1823,9 @@ main(int argc, char *argv[]) dumpfile = optarg; filebounds = getbounds(dumpfile); break; + case 'k': + keyfile = optarg; + break; case '?': usage(); } @@ -1756,6 +1886,18 @@ main(int argc, char *argv[]) read_dumphdr(); + if (dumphdr.dump_flags & DF_ENCRYPTED) { + if (filemode) { + logprint(SC_SL_NONE | SC_EXIT_ERR, "saved dump file is " + "erroneously encrypted"); + } + + if (!csave) { + logprint(SC_SL_NONE | SC_EXIT_ERR, "dump is encrypted; " + "cannot be saved uncompressed"); + } + } + /* * We want this message to go to the log file, but not the console. * There's no good way to do that with the existing syslog facility. @@ -1860,7 +2002,7 @@ main(int argc, char *argv[]) "Saving compressed system crash dump in %s/%s", savedir, corefile); - copy_crashfile(corefile); + copy_crashfile(corefile, keyfile); /* * Raise a fault management event that indicates the system @@ -1876,7 +2018,7 @@ main(int argc, char *argv[]) char *metrics = Zalloc(metrics_size + 1); Pread(dumpfd, metrics, metrics_size, endoff + - sizeof (dumphdr) + sizeof (datahdr)); + dumphdr_size + sizeof (datahdr)); if (sec < 1) sec = 1; diff --git a/usr/src/cmd/sed/main.c b/usr/src/cmd/sed/main.c index c928131958..b148f7293e 100644 --- a/usr/src/cmd/sed/main.c +++ b/usr/src/cmd/sed/main.c @@ -2,7 +2,7 @@ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. * Copyright (c) 2013 Johann 'Myrkraverk' Oskarsson <johann@myrkraverk.com> * Copyright (c) 2011 Gary Mills - * Copyright 2011 Nexenta Systems, Inc. All rights reserved. + * Copyright 2010 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1992 Diomidis Spinellis. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -43,9 +43,7 @@ #include <err.h> #include <errno.h> #include <fcntl.h> -#include <getopt.h> #include <libgen.h> -#include <libintl.h> #include <limits.h> #include <locale.h> #include <regex.h> @@ -54,6 +52,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <libintl.h> #include "defs.h" #include "extern.h" @@ -108,11 +107,6 @@ static char tmpfname[PATH_MAX]; /* Temporary file name (for in-place editing) */ static const char *inplace; /* Inplace edit file extension. */ ulong_t linenum; -static const struct option lopts[] = { - {"in-place", optional_argument, NULL, 'i'}, - {NULL, 0, NULL, 0} -}; - static void add_compunit(enum e_cut, char *); static void add_file(char *); static void usage(void); @@ -134,18 +128,14 @@ main(int argc, char *argv[]) fflag = 0; inplace = NULL; - while ((c = getopt_long(argc, argv, "EI::ae:f:i::lnr", lopts, NULL)) != - -1) + while ((c = getopt(argc, argv, "EI:ae:f:i:lnr")) != -1) switch (c) { case 'r': /* Gnu sed compat */ case 'E': rflags = REG_EXTENDED; break; case 'I': - if (optarg != NULL) - inplace = optarg; - else - inplace = ""; + inplace = optarg; ispan = 1; /* span across input files */ break; case 'a': @@ -162,10 +152,7 @@ main(int argc, char *argv[]) add_compunit(CU_FILE, optarg); break; case 'i': - if (optarg != NULL) - inplace = optarg; - else - inplace = ""; + inplace = optarg; ispan = 0; /* don't span across input files */ break; case 'l': @@ -206,8 +193,8 @@ main(int argc, char *argv[]) static void usage(void) { - (void) fputs(_("usage: sed script [-Ealn] [-i[extension]] [file...]\n" - " sed [-Ealn] [-i[extension]] [-e script]... " + (void) fputs(_("usage: sed script [-Ealn] [-i extension] [file...]\n" + " sed [-Ealn] [-i extension] [-e script]... " "[-f script_file]... [file...]\n"), stderr); exit(1); diff --git a/usr/src/cmd/sendmail/src/Makefile b/usr/src/cmd/sendmail/src/Makefile index 8791902399..e488312e99 100644 --- a/usr/src/cmd/sendmail/src/Makefile +++ b/usr/src/cmd/sendmail/src/Makefile @@ -46,8 +46,7 @@ LDFLAGS += $(MAPFILES:%=-M%) LDLIBS += ../libsmutil/libsmutil.a ../libsm/libsm.a -lresolv -lsocket \ -lnsl ../db/libdb.a -lldap -lsldap -lwrap -lumem \ - -lssl -lcrypto -lsasl -NATIVE_LIBS += libssl.so libcrypto.so + -lsunw_ssl -lsunw_crypto -lsasl INCPATH= -I. -I../include -I../db diff --git a/usr/src/cmd/sgs/elfdump/Makefile.targ b/usr/src/cmd/sgs/elfdump/Makefile.targ index 6122df233d..48bbbbbb87 100644 --- a/usr/src/cmd/sgs/elfdump/Makefile.targ +++ b/usr/src/cmd/sgs/elfdump/Makefile.targ @@ -79,6 +79,8 @@ delete: package \ install: all $(VAR_SGSBINPROG) $(VAR_SGSCCSLINK) + -$(RM) $(ROOTPROG) + -$(LN) $(ISAEXEC) $(ROOTPROG) include $(SRC)/cmd/Makefile.targ include $(SRC)/cmd/sgs/Makefile.targ diff --git a/usr/src/cmd/sgs/elfdump/amd64/Makefile b/usr/src/cmd/sgs/elfdump/amd64/Makefile index ec8341a69b..7c3f1eb876 100644 --- a/usr/src/cmd/sgs/elfdump/amd64/Makefile +++ b/usr/src/cmd/sgs/elfdump/amd64/Makefile @@ -39,6 +39,8 @@ CONVLIBDIR = $(CONVLIBDIR64) VAR_SGSBINPROG= $(VAR_SGSBINPROG64) VAR_SGSCCSLINK= $(VAR_SGSCCSLINK64) +install: all $(ROOTPROG64) + include ../Makefile.targ include ../../Makefile.sub.64 diff --git a/usr/src/cmd/sgs/elfdump/i386/Makefile b/usr/src/cmd/sgs/elfdump/i386/Makefile index d3cb302ac1..95390a2899 100644 --- a/usr/src/cmd/sgs/elfdump/i386/Makefile +++ b/usr/src/cmd/sgs/elfdump/i386/Makefile @@ -30,4 +30,6 @@ include ../Makefile.com ARCH = i386 +install: all $(ROOTPROG32) + include ../Makefile.targ diff --git a/usr/src/cmd/sgs/include/conv.h b/usr/src/cmd/sgs/include/conv.h index 4133866e86..2c78d95b04 100644 --- a/usr/src/cmd/sgs/include/conv.h +++ b/usr/src/cmd/sgs/include/conv.h @@ -280,7 +280,7 @@ typedef union { } Conv_bnd_obj_buf_t; /* conv_phdr_flags() */ -#define CONV_PHDR_FLAGS_BUFSIZE 88 +#define CONV_PHDR_FLAGS_BUFSIZE 244 typedef union { Conv_inv_buf_t inv_buf; char buf[CONV_PHDR_FLAGS_BUFSIZE]; diff --git a/usr/src/cmd/sgs/lex/common/main.c b/usr/src/cmd/sgs/lex/common/main.c index 17ba4808a4..a1fd526cf9 100644 --- a/usr/src/cmd/sgs/lex/common/main.c +++ b/usr/src/cmd/sgs/lex/common/main.c @@ -30,11 +30,15 @@ /* Copyright 1976, Bell Telephone Laboratories, Inc. */ +/* Copyright (c) 2013, joyent, Inc. All rights reserved. */ + #include <string.h> #include "once.h" #include "sgs.h" #include <locale.h> #include <limits.h> +#include <unistd.h> +#include <libgen.h> static wchar_t L_INITIAL[] = {'I', 'N', 'I', 'T', 'I', 'A', 'L', 0}; static void get1core(void); @@ -46,6 +50,25 @@ static void get3core(void); static void free3core(void); #endif +static int +lex_construct_path(char *buf, size_t size, const char *file, int type) +{ + int ret; + char origin[PATH_MAX]; + + if (type != 0) { + ret = readlink("/proc/self/path/a.out", origin, PATH_MAX - 1); + if (ret < 0) + error( + "lex: failed to read origin from /proc\n"); + origin[ret] = '\0'; + return (snprintf(buf, size, "%s/../%s/%s", dirname(origin), + NBASE, file)); + } + + return (snprintf(buf, size, "%s/%s/%s", NPREFIX, NBASE, file)); +} + int main(int argc, char **argv) { @@ -53,6 +76,7 @@ main(int argc, char **argv) int c; char *apath = NULL; char *ypath; + char pathbuf[PATH_MAX]; Boolean eoption = 0, woption = 0; sargv = argv; @@ -224,6 +248,11 @@ main(int argc, char **argv) free3core(); #endif + /* + * Try to find the file relative to $ORIGIN. Note that we don't touch + * antyhing related to -Y. In fact, unfortunately it's always been + * ignored it seems. + */ if (handleeuc) { if (ratfor) error("Ratfor is not supported by -w or -e option."); @@ -232,9 +261,19 @@ main(int argc, char **argv) else ypath = ratfor ? RATNAME : CNAME; - if (apath != NULL) - ypath = strcat(apath, strrchr(ypath, '/')); - fother = fopen(ypath, "r"); + if (apath == NULL) { + (void) lex_construct_path(pathbuf, sizeof (pathbuf), ypath, 1); + fother = fopen(pathbuf, "r"); + if (fother == NULL) { + (void) lex_construct_path(pathbuf, sizeof (pathbuf), + ypath, 0); + fother = fopen(pathbuf, "r"); + } + } else { + apath = strcat(apath, "/"); + ypath = strcat(apath, ypath); + fother = fopen(ypath, "r"); + } if (fother == NULL) error("Lex driver missing, file %s", ypath); while ((i = getc(fother)) != EOF) diff --git a/usr/src/cmd/sgs/lex/common/once.h b/usr/src/cmd/sgs/lex/common/once.h index 014ca00b17..9e4b0e5e00 100644 --- a/usr/src/cmd/sgs/lex/common/once.h +++ b/usr/src/cmd/sgs/lex/common/once.h @@ -26,11 +26,11 @@ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ +/* Copyright (c) 2013, joyent, Inc. All rights reserved. */ + #ifndef _ONCE_H #define _ONCE_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include "ldefs.h" /* once.c */ @@ -73,9 +73,11 @@ int peek = '\n'; /* next input character */ CHR *pushptr = pushc; CHR *slptr = slist; -#define CNAME "/usr/share/lib/ccs/ncform" -#define RATNAME "/usr/share/lib/ccs/nrform" -#define EUCNAME "/usr/share/lib/ccs/nceucform" +#define NPREFIX "/usr" +#define NBASE "/share/lib/ccs/" +#define CNAME "ncform" +#define RATNAME "nrform" +#define EUCNAME "nceucform" int ccount = 1; int casecount = 1; diff --git a/usr/src/cmd/sgs/libconv/common/corenote.c b/usr/src/cmd/sgs/libconv/common/corenote.c index bf6d109916..f86d534450 100644 --- a/usr/src/cmd/sgs/libconv/common/corenote.c +++ b/usr/src/cmd/sgs/libconv/common/corenote.c @@ -78,7 +78,7 @@ const char * conv_cnote_auxv_type(Word type, Conv_fmt_flags_t fmt_flags, Conv_inv_buf_t *inv_buf) { - static const Msg types_0_22[] = { + static const Msg types_0_25[] = { MSG_AUXV_AT_NULL, MSG_AUXV_AT_IGNORE, MSG_AUXV_AT_EXECFD, MSG_AUXV_AT_PHDR, MSG_AUXV_AT_PHENT, MSG_AUXV_AT_PHNUM, @@ -90,10 +90,11 @@ conv_cnote_auxv_type(Word type, Conv_fmt_flags_t fmt_flags, MSG_AUXV_AT_HWCAP, MSG_AUXV_AT_CLKTCK, MSG_AUXV_AT_FPUCW, MSG_AUXV_AT_DCACHEBSIZE, MSG_AUXV_AT_ICACHEBSIZE, MSG_AUXV_AT_UCACHEBSIZE, - MSG_AUXV_AT_IGNOREPPC + MSG_AUXV_AT_IGNOREPPC, MSG_AUXV_AT_SECURE, + MSG_AUXV_AT_BASE_PLATFORM, MSG_AUXV_AT_RANDOM }; - static const conv_ds_msg_t ds_types_0_22 = { - CONV_DS_MSG_INIT(0, types_0_22) }; + static const conv_ds_msg_t ds_types_0_25 = { + CONV_DS_MSG_INIT(0, types_0_25) }; static const Msg types_2000_2011[] = { MSG_AUXV_AT_SUN_UID, MSG_AUXV_AT_SUN_RUID, @@ -112,7 +113,7 @@ conv_cnote_auxv_type(Word type, Conv_fmt_flags_t fmt_flags, MSG_AUXV_AT_SUN_EMULATOR, MSG_AUXV_AT_SUN_BRANDNAME, MSG_AUXV_AT_SUN_BRAND_AUX1, MSG_AUXV_AT_SUN_BRAND_AUX2, MSG_AUXV_AT_SUN_BRAND_AUX3, MSG_AUXV_AT_SUN_HWCAP2, - 0, 0, + MSG_AUXV_AT_SUN_BRAND_NROOT, MSG_AUXV_AT_SUN_BRAND_AUX4, MSG_AUXV_AT_SUN_COMMPAGE, MSG_AUXV_AT_SUN_FPTYPE, MSG_AUXV_AT_SUN_FPSIZE }; @@ -120,7 +121,7 @@ conv_cnote_auxv_type(Word type, Conv_fmt_flags_t fmt_flags, CONV_DS_MSG_INIT(2014, types_2014_2028) }; static const conv_ds_t *ds[] = { - CONV_DS_ADDR(ds_types_0_22), CONV_DS_ADDR(ds_types_2000_2011), + CONV_DS_ADDR(ds_types_0_25), CONV_DS_ADDR(ds_types_2000_2011), CONV_DS_ADDR(ds_types_2014_2028), NULL }; return (conv_map_ds(ELFOSABI_NONE, EM_NONE, type, ds, fmt_flags, diff --git a/usr/src/cmd/sgs/libconv/common/corenote.msg b/usr/src/cmd/sgs/libconv/common/corenote.msg index a36f2bddf7..78951cfb1d 100644 --- a/usr/src/cmd/sgs/libconv/common/corenote.msg +++ b/usr/src/cmd/sgs/libconv/common/corenote.msg @@ -80,6 +80,9 @@ @ MSG_AUXV_AT_ICACHEBSIZE "ICACHEBSIZE" @ MSG_AUXV_AT_UCACHEBSIZE "UCACHEBSIZE" @ MSG_AUXV_AT_IGNOREPPC "IGNOREPPC" +@ MSG_AUXV_AT_SECURE "SECURE" +@ MSG_AUXV_AT_BASE_PLATFORM "BASE_PLATFORM" +@ MSG_AUXV_AT_RANDOM "RANDOM" @ MSG_AUXV_AT_SUN_UID "SUN_UID" @ MSG_AUXV_AT_SUN_RUID "SUN_RUID" @ MSG_AUXV_AT_SUN_GID "SUN_GID" @@ -102,6 +105,8 @@ @ MSG_AUXV_AT_SUN_BRAND_AUX2 "SUN_BRAND_AUX2" @ MSG_AUXV_AT_SUN_BRAND_AUX3 "SUN_BRAND_AUX3" @ MSG_AUXV_AT_SUN_HWCAP2 "SUN_HWCAP2" +@ MSG_AUXV_AT_SUN_BRAND_NROOT "SUN_BRAND_NROOT" +@ MSG_AUXV_AT_SUN_BRAND_AUX4 "SUN_BRAND_AUX4" @ MSG_AUXV_AT_SUN_COMMPAGE "SUN_COMMPAGE" @ MSG_AUXV_AT_SUN_FPTYPE "SUN_FPTYPE" @ MSG_AUXV_AT_SUN_FPSIZE "SUN_FPSIZE" diff --git a/usr/src/cmd/sgs/libconv/common/phdr.c b/usr/src/cmd/sgs/libconv/common/phdr.c index d2b29a201f..382a1bf9f1 100644 --- a/usr/src/cmd/sgs/libconv/common/phdr.c +++ b/usr/src/cmd/sgs/libconv/common/phdr.c @@ -25,6 +25,10 @@ */ /* + * Copyright (c) 2015, Joyent, Inc. All rights reserved. + */ + +/* * String conversion routines for program header attributes. */ #include <stdio.h> @@ -95,6 +99,7 @@ error "PT_NUM has grown. Update phdrs[]" { PT_GNU_EH_FRAME, LIN, MSG_PT_GNU_EH_FRAME }, { PT_GNU_STACK, LIN, MSG_PT_GNU_STACK }, { PT_GNU_RELRO, LIN, MSG_PT_GNU_RELRO }, + { PT_PAX_FLAGS, LIN, MSG_PT_PAX_FLAGS }, { 0 } }; @@ -109,6 +114,7 @@ error "PT_NUM has grown. Update phdrs[]" { PT_GNU_EH_FRAME, LIN, MSG_PT_GNU_EH_FRAME_CF }, { PT_GNU_STACK, LIN, MSG_PT_GNU_STACK_CF }, { PT_GNU_RELRO, LIN, MSG_PT_GNU_RELRO_CF }, + { PT_PAX_FLAGS, LIN, MSG_PT_PAX_FLAGS_CF }, { 0 } }; @@ -123,6 +129,7 @@ error "PT_NUM has grown. Update phdrs[]" { PT_GNU_EH_FRAME, LIN, MSG_PT_GNU_EH_FRAME_CFNP }, { PT_GNU_STACK, LIN, MSG_PT_GNU_STACK_CFNP }, { PT_GNU_RELRO, LIN, MSG_PT_GNU_RELRO_CFNP }, + { PT_PAX_FLAGS, LIN, MSG_PT_PAX_FLAGS_CFNP }, { 0 } }; @@ -137,6 +144,7 @@ error "PT_NUM has grown. Update phdrs[]" { PT_GNU_EH_FRAME, LIN, MSG_PT_GNU_EH_FRAME_NF }, { PT_GNU_STACK, LIN, MSG_PT_GNU_STACK_NF }, { PT_GNU_RELRO, LIN, MSG_PT_GNU_RELRO_NF }, + { PT_PAX_FLAGS, LIN, MSG_PT_PAX_FLAGS_NF }, { 0 } }; @@ -214,6 +222,18 @@ conv_phdr_flags_strings(Conv_fmt_flags_t fmt_flags) MSG_PF_X_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ MSG_PF_W_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ MSG_PF_R_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_PAGEEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_NOPAGEEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_SEGMEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_NOSEGMEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_MPROTECT_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_NOMPROTECT_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_RANDEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_NORANDEXEC_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_EMUTRAMP_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_NOEMUTRAMP_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_RANDMMAP_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ + MSG_PF_NORANDMMAP_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ MSG_PF_SUNW_FAILURE_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ MSG_PF_SUNW_KILLED_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ MSG_PF_SUNW_SIGINFO_CF_SIZE + CONV_EXPN_FIELD_DEF_SEP_SIZE + \ @@ -237,11 +257,24 @@ conv_phdr_flags_strings(Conv_fmt_flags_t fmt_flags) #define ALL ELFOSABI_NONE, EM_NONE #define SOL ELFOSABI_SOLARIS, EM_NONE +#define LIN ELFOSABI_LINUX, EM_NONE static const Val_desc2 vda_cf[] = { { PF_X, ALL, MSG_PF_X_CF }, { PF_W, ALL, MSG_PF_W_CF }, { PF_R, ALL, MSG_PF_R_CF }, + { PF_PAGEEXEC, LIN, MSG_PF_PAGEEXEC_CF }, + { PF_NOPAGEEXEC, LIN, MSG_PF_NOPAGEEXEC_CF }, + { PF_SEGMEXEC, LIN, MSG_PF_SEGMEXEC_CF }, + { PF_NOSEGMEXEC, LIN, MSG_PF_NOSEGMEXEC_CF }, + { PF_MPROTECT, LIN, MSG_PF_MPROTECT_CF }, + { PF_NOMPROTECT, LIN, MSG_PF_NOMPROTECT_CF }, + { PF_RANDEXEC, LIN, MSG_PF_RANDEXEC_CF }, + { PF_NORANDEXEC, LIN, MSG_PF_NORANDEXEC_CF }, + { PF_EMUTRAMP, LIN, MSG_PF_EMUTRAMP_CF }, + { PF_NOEMUTRAMP, LIN, MSG_PF_NOEMUTRAMP_CF }, + { PF_RANDMMAP, LIN, MSG_PF_RANDMMAP_CF }, + { PF_NORANDMMAP, LIN, MSG_PF_NORANDMMAP_CF }, { PF_SUNW_FAILURE, SOL, MSG_PF_SUNW_FAILURE_CF }, { PF_SUNW_KILLED, SOL, MSG_PF_SUNW_KILLED_CF }, { PF_SUNW_SIGINFO, SOL, MSG_PF_SUNW_SIGINFO_CF }, @@ -251,6 +284,18 @@ conv_phdr_flags_strings(Conv_fmt_flags_t fmt_flags) { PF_X, ALL, MSG_PF_X_NF }, { PF_W, ALL, MSG_PF_W_NF }, { PF_R, ALL, MSG_PF_R_NF }, + { PF_PAGEEXEC, LIN, MSG_PF_PAGEEXEC_NF }, + { PF_NOPAGEEXEC, LIN, MSG_PF_NOPAGEEXEC_NF }, + { PF_SEGMEXEC, LIN, MSG_PF_SEGMEXEC_NF }, + { PF_NOSEGMEXEC, LIN, MSG_PF_NOSEGMEXEC_NF }, + { PF_MPROTECT, LIN, MSG_PF_MPROTECT_NF }, + { PF_NOMPROTECT, LIN, MSG_PF_NOMPROTECT_NF }, + { PF_RANDEXEC, LIN, MSG_PF_RANDEXEC_NF }, + { PF_NORANDEXEC, LIN, MSG_PF_NORANDEXEC_NF }, + { PF_EMUTRAMP, LIN, MSG_PF_EMUTRAMP_NF }, + { PF_NOEMUTRAMP, LIN, MSG_PF_NOEMUTRAMP_NF }, + { PF_RANDMMAP, LIN, MSG_PF_RANDMMAP_NF }, + { PF_NORANDMMAP, LIN, MSG_PF_NORANDMMAP_NF }, { PF_SUNW_FAILURE, SOL, MSG_PF_SUNW_FAILURE_NF }, { PF_SUNW_KILLED, SOL, MSG_PF_SUNW_KILLED_NF }, { PF_SUNW_SIGINFO, SOL, MSG_PF_SUNW_SIGINFO_NF }, @@ -262,6 +307,7 @@ conv_phdr_flags_strings(Conv_fmt_flags_t fmt_flags) #undef ALL #undef SOL +#undef LIN } const char * diff --git a/usr/src/cmd/sgs/libconv/common/phdr.msg b/usr/src/cmd/sgs/libconv/common/phdr.msg index 789832a16a..e84182277b 100644 --- a/usr/src/cmd/sgs/libconv/common/phdr.msg +++ b/usr/src/cmd/sgs/libconv/common/phdr.msg @@ -24,6 +24,8 @@ # Use is subject to license terms. # +# Copyright (c) 2015, Joyent, Inc. All rights reserved. + @ MSG_PT_NULL "[ PT_NULL ]" # 0 @ MSG_PT_NULL_CF "PT_NULL" @ MSG_PT_NULL_CFNP "NULL" @@ -79,6 +81,10 @@ @ MSG_PT_GNU_RELRO_CF "PT_GNU_RELRO" @ MSG_PT_GNU_RELRO_CFNP "GNU_RELRO" @ MSG_PT_GNU_RELRO_NF "gnu_relro" +@ MSG_PT_PAX_FLAGS "[ PT_PAX_FLAGS ]" # 0x65041580 +@ MSG_PT_PAX_FLAGS_CF "PT_PAX_FLAGS" +@ MSG_PT_PAX_FLAGS_CFNP "PAX_FLAGS" +@ MSG_PT_PAX_FLAGS_NF "pax_flags" @ MSG_PT_SUNWBSS "[ PT_SUNWBSS ]" # 0x6ffffffa @ MSG_PT_SUNWBSS_CF "PT_SUNWBSS" @@ -103,6 +109,30 @@ @ MSG_PF_W_NF "w" @ MSG_PF_R_CF "PF_R" # 0x4 @ MSG_PF_R_NF "r" +@ MSG_PF_PAGEEXEC_CF "PF_PAGEEXEC" # 0x00000010 +@ MSG_PF_PAGEEXEC_NF "pageexec" +@ MSG_PF_NOPAGEEXEC_CF "PF_NOPAGEEXEC" # 0x00000020 +@ MSG_PF_NOPAGEEXEC_NF "nopageexec" +@ MSG_PF_SEGMEXEC_CF "PF_SEGMEXEC" # 0x00000040 +@ MSG_PF_SEGMEXEC_NF "segmexec" +@ MSG_PF_NOSEGMEXEC_CF "PF_NOSEGMEXEC" # 0x00000080 +@ MSG_PF_NOSEGMEXEC_NF "nosegmexec" +@ MSG_PF_MPROTECT_CF "PF_MPROTECT" # 0x00000100 +@ MSG_PF_MPROTECT_NF "mprotect" +@ MSG_PF_NOMPROTECT_CF "PF_NOMPROTECT" # 0x00000200 +@ MSG_PF_NOMPROTECT_NF "nomprotect" +@ MSG_PF_RANDEXEC_CF "PF_RANDEXEC" # 0x00000400 +@ MSG_PF_RANDEXEC_NF "randexec" +@ MSG_PF_NORANDEXEC_CF "PF_NORANDEXEC" # 0x00000800 +@ MSG_PF_NORANDEXEC_NF "norandexec" +@ MSG_PF_EMUTRAMP_CF "PF_EMUTRAMP" # 0x00001000 +@ MSG_PF_EMUTRAMP_NF "emutramp" +@ MSG_PF_NOEMUTRAMP_CF "PF_NOEMUTRAMP" # 0x00002000 +@ MSG_PF_NOEMUTRAMP_NF "noemutramp" +@ MSG_PF_RANDMMAP_CF "PF_RANDMMAP" # 0x00004000 +@ MSG_PF_RANDMMAP_NF "randmmap" +@ MSG_PF_NORANDMMAP_CF "PF_NORANDMMAP" # 0x00008000 +@ MSG_PF_NORANDMMAP_NF "norandmmap" @ MSG_PF_SUNW_FAILURE_CF "PF_SUNW_FAILURE" # 0x00100000 @ MSG_PF_SUNW_FAILURE_NF "sunw_failure" @ MSG_PF_SUNW_KILLED_CF "PF_SUNW_KILLED" # 0x00200000 diff --git a/usr/src/cmd/sgs/librtld_db/common/librtld_db.msg b/usr/src/cmd/sgs/librtld_db/common/librtld_db.msg index fbc595f5f4..5b0ad9533a 100644 --- a/usr/src/cmd/sgs/librtld_db/common/librtld_db.msg +++ b/usr/src/cmd/sgs/librtld_db/common/librtld_db.msg @@ -24,6 +24,10 @@ # Use is subject to license terms. # +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + # Message file for cmd/sgs/librtld_db. @ MSG_ID_LIBRTLD_DB @@ -104,7 +108,7 @@ @ MSG_DB_RDOBJPADE "rtld_db: rd_objpad_enable(padsize=0x%llx)" @ MSG_DB_64BIT_PREFIX "64/" @ MSG_DB_BRAND_HELPERPATH_PREFIX "%s/%s/%s/%s%s_librtld_db.so.1" -@ MSG_DB_BRAND_HELPERPATH "%s/%s/%s%s_librtld_db.so.1" +@ MSG_DB_BRAND_HELPERPATH "%s/%s/%s/%s%s_librtld_db.so.1" @ MSG_DB_HELPERNOOPS "rtld_db: helper lib loaded but ops not preset" @ MSG_DB_HELPERLOADED "rtld_db: helper library loaded for brand \"%s\"" @ MSG_DB_HELPERLOADFAILED "rtld_db: couldn't load brand helper library %s" diff --git a/usr/src/cmd/sgs/librtld_db/common/rd_elf.c b/usr/src/cmd/sgs/librtld_db/common/rd_elf.c index 737fb2ee0f..c78d39e714 100644 --- a/usr/src/cmd/sgs/librtld_db/common/rd_elf.c +++ b/usr/src/cmd/sgs/librtld_db/common/rd_elf.c @@ -23,6 +23,10 @@ * Use is subject to license terms. */ +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + #include <stdlib.h> #include <stdio.h> #include <proc_service.h> @@ -38,6 +42,14 @@ #include <sys/param.h> /* + * We want to include zone.h to pull in the prototype for zone_get_nroot(), + * but we need to avoid pulling in <sys/stream.h>, which has a definition + * of M_DATA that conflicts with the ELF-related definition in machdep_*.h. + */ +#define _SYS_STREAM_H +#include <zone.h> + +/* * 64-bit builds are going to compile this module twice, the * second time with _ELF64 defined. These defines should make * all the necessary adjustments to the code. @@ -283,7 +295,9 @@ _rd_reset32(struct rd_agent *rap) * If we are debugging a branded executable, load the appropriate * helper library, and call its initialization routine. Being unable * to load the helper library is not a critical error. (Hopefully - * we'll still be able to access some objects in the target.) + * we'll still be able to access some objects in the target.) Note + * that we pull in the native root here to allow for helper libraries + * to be properly found from within the branded zone. */ ps_pbrandname = (ps_pbrandname_fp_t)dlsym(RTLD_PROBE, "ps_pbrandname"); while ((ps_pbrandname != NULL) && @@ -294,17 +308,23 @@ _rd_reset32(struct rd_agent *rap) isa = MSG_ORIG(MSG_DB_64BIT_PREFIX); #endif /* _LP64 */ - if (rtld_db_helper_path[0] != '\0') + if (rtld_db_helper_path[0] != '\0') { (void) snprintf(brandlib, MAXPATHLEN, MSG_ORIG(MSG_DB_BRAND_HELPERPATH_PREFIX), rtld_db_helper_path, MSG_ORIG(MSG_DB_HELPER_PREFIX), brandname, isa, brandname); - else + } else { + const char *nroot = zone_get_nroot(); + + if (nroot == NULL) + nroot = ""; + (void) snprintf(brandlib, MAXPATHLEN, - MSG_ORIG(MSG_DB_BRAND_HELPERPATH), + MSG_ORIG(MSG_DB_BRAND_HELPERPATH), nroot, MSG_ORIG(MSG_DB_HELPER_PREFIX), brandname, isa, brandname); + } rap->rd_helper.rh_dlhandle = dlopen(brandlib, RTLD_LAZY | RTLD_LOCAL); diff --git a/usr/src/cmd/sgs/rtld/common/_rtld.h b/usr/src/cmd/sgs/rtld/common/_rtld.h index ece14a855e..83b7071471 100644 --- a/usr/src/cmd/sgs/rtld/common/_rtld.h +++ b/usr/src/cmd/sgs/rtld/common/_rtld.h @@ -26,7 +26,7 @@ * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. */ /* - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ #ifndef __RTLD_H #define __RTLD_H @@ -589,6 +589,8 @@ extern const char *rpl_ldflags; /* replaceable LD_FLAGS string */ extern const char *rpl_libpath; /* replaceable LD_LIBRARY string */ extern Alist *rpl_libdirs; /* and its associated Pdesc list */ extern const char *rpl_preload; /* replaceable LD_PRELOAD string */ +extern const char *rpl_ldtoxic; /* replaceable LD_TOXIC_PATH string */ +extern Alist *rpl_toxdirs; /* and associated Pdesc list */ extern const char *prm_audit; /* permanent LD_AUDIT string */ extern const char *prm_debug; /* permanent LD_DEBUG string */ diff --git a/usr/src/cmd/sgs/rtld/common/analyze.c b/usr/src/cmd/sgs/rtld/common/analyze.c index 91a7546e85..c9e66b1293 100644 --- a/usr/src/cmd/sgs/rtld/common/analyze.c +++ b/usr/src/cmd/sgs/rtld/common/analyze.c @@ -21,6 +21,7 @@ /* * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2015, Joyent, Inc. */ /* @@ -834,6 +835,57 @@ is_so_loaded(Lm_list *lml, const char *name, int *in_nfavl) } /* + * Walk the toxic path list and determine if the object in question has violated + * the toxic path. When evaluating the toxic path we need to ensure that we + * match any path that's a subdirectory of a listed entry. In other words if + * /foo/bar is toxic, something in /foo/bar/baz/ is no good. However, we need to + * ensure that we don't mark /foo/barbaz/ as bad. + */ +static int +is_load_toxic(Lm_list *lml, Rt_map *nlmp) +{ + const char *fpath; + size_t flen; + Pdesc *pdp; + Aliste idx; + + fpath = PATHNAME(nlmp); + + /* + * If we have a NULL path name, that indicates that rtld is processing + * an in-memory shared object. For example, trying to run ldd or doing + * an LD_PRELOAD on an object file. In those cases, we'll always allow + * it. + */ + if (fpath == NULL) + return (0); + + flen = strlen(fpath); + + for (ALIST_TRAVERSE(rpl_toxdirs, idx, pdp)) { + if (pdp->pd_plen == 0) + continue; + + if (strncmp(pdp->pd_pname, fpath, pdp->pd_plen) == 0) { + if (pdp->pd_pname[pdp->pd_plen-1] != '/') { + /* + * Path didn't end in a /, make sure + * we're at a directory boundary + * nonetheless. + */ + if (flen > pdp->pd_plen && + fpath[pdp->pd_plen] == '/') + return (1); + continue; + } + return (1); + } + } + + return (0); +} + +/* * Tracing is enabled by the LD_TRACE_LOADED_OPTIONS environment variable which * is normally set from ldd(1). For each link map we load, print the load name * and the full pathname of the associated object. @@ -2169,6 +2221,17 @@ load_finish(Lm_list *lml, const char *name, Rt_map *clmp, int nmode, uint_t rdflags; /* + * If this dependency is associated with a toxic path, then we must + * honor the user's request to die. + */ + if (is_load_toxic(lml, nlmp) != 0) { + eprintf(lml, ERR_FATAL, MSG_INTL(MSG_TOXIC_FILE), + PATHNAME(nlmp)); + rtldexit(lml, 1); + + } + + /* * If this dependency is associated with a required version ensure that * the version is present in the loaded file. */ diff --git a/usr/src/cmd/sgs/rtld/common/globals.c b/usr/src/cmd/sgs/rtld/common/globals.c index bb47b12540..45c08f8286 100644 --- a/usr/src/cmd/sgs/rtld/common/globals.c +++ b/usr/src/cmd/sgs/rtld/common/globals.c @@ -24,6 +24,7 @@ * All Rights Reserved * * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ #include <sys/types.h> @@ -132,6 +133,8 @@ const char *rpl_ldflags = NULL; /* replaceable LD_FLAGS string */ const char *rpl_libpath = NULL; /* replaceable LD_LIBRARY_PATH string */ Alist *rpl_libdirs = NULL; /* and associated Pdesc list */ const char *rpl_preload = NULL; /* replaceable LD_PRELOAD string */ +const char *rpl_ldtoxic = NULL; /* replaceable LD_TOXIC string */ +Alist *rpl_toxdirs = NULL; /* and associated Pdesc list */ const char *prm_audit = NULL; /* permanent LD_AUDIT string */ const char *prm_debug = NULL; /* permanent LD_DEBUG string */ diff --git a/usr/src/cmd/sgs/rtld/common/rtld.msg b/usr/src/cmd/sgs/rtld/common/rtld.msg index 94c5c5bf51..0c0cff9e86 100644 --- a/usr/src/cmd/sgs/rtld/common/rtld.msg +++ b/usr/src/cmd/sgs/rtld/common/rtld.msg @@ -21,6 +21,7 @@ # # Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2014, Joyent, Inc. All rights reserved. # @ _START_ @@ -99,9 +100,14 @@ @ MSG_SYS_MPROT "%s: mprotect failed: %s" @ MSG_SYS_MMAPANON "mmap anon failed: %s" +# Secure path failures + @ MSG_SEC_OPEN "%s: open failed: No such file in secure directories" @ MSG_SEC_ILLEGAL "%s: open failed: illegal insecure pathname" +# Toxic failures + +@ MSG_TOXIC_FILE "%s: dependency marked as toxic" # Configuration failures @@ -396,6 +402,7 @@ @ MSG_LD_PROFILE_OUTPUT "PROFILE_OUTPUT" @ MSG_LD_SFCAP "SFCAP" @ MSG_LD_SIGNAL "SIGNAL" +@ MSG_LD_TOXICPATH "TOXIC_PATH" @ MSG_LD_TRACE_OBJS "TRACE_LOADED_OBJECTS" @ MSG_LD_TRACE_OBJS_E "TRACE_LOADED_OBJECTS_E" @ MSG_LD_TRACE_OBJS_A "TRACE_LOADED_OBJECTS_A" diff --git a/usr/src/cmd/sgs/rtld/common/setup.c b/usr/src/cmd/sgs/rtld/common/setup.c index 862bb7d61f..98e3ba5d33 100644 --- a/usr/src/cmd/sgs/rtld/common/setup.c +++ b/usr/src/cmd/sgs/rtld/common/setup.c @@ -28,7 +28,7 @@ * All Rights Reserved */ /* - * Copyright (c) 2012, Joyent, Inc. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* @@ -819,6 +819,14 @@ setup(char **envp, auxv_t *auxv, Word _flags, char *_platform, int _syspagsz, return (0); } + /* + * Initialize our toxic paths + */ + if (rpl_ldtoxic != NULL) { + (void) expand_paths(mlmp, rpl_ldtoxic, &rpl_toxdirs, + AL_CNT_SEARCH, 0, PD_TKN_CAP); + } + #if defined(_ELF64) /* * If this is a 64-bit process, determine whether this process has diff --git a/usr/src/cmd/sgs/rtld/common/util.c b/usr/src/cmd/sgs/rtld/common/util.c index 7d3d193c4c..ebdd9e1943 100644 --- a/usr/src/cmd/sgs/rtld/common/util.c +++ b/usr/src/cmd/sgs/rtld/common/util.c @@ -24,6 +24,7 @@ * All Rights Reserved * * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, Joyent, Inc. All rights reserved. */ /* @@ -1428,6 +1429,7 @@ static u_longlong_t cmdisa = 0; /* command line (-e) ISA */ #define ENV_FLG_CAP_FILES 0x0080000000000ULL #define ENV_FLG_DEFERRED 0x0100000000000ULL #define ENV_FLG_NOENVIRON 0x0200000000000ULL +#define ENV_FLG_TOXICPATH 0x0400000000000ULL #define SEL_REPLACE 0x0001 #define SEL_PERMANT 0x0002 @@ -1601,8 +1603,7 @@ ld_generic_env(const char *s1, size_t len, const char *s2, Word *lmflags, if ((len == MSG_LD_FLAGS_SIZE) && (strncmp(s1, MSG_ORIG(MSG_LD_FLAGS), MSG_LD_FLAGS_SIZE) == 0)) { select |= SEL_ACT_SPEC_1; - str = (select & SEL_REPLACE) ? &rpl_ldflags : - &prm_ldflags; + str = &rpl_ldflags; variable = ENV_FLG_FLAGS; } } @@ -1813,6 +1814,8 @@ ld_generic_env(const char *s1, size_t len, const char *s2, Word *lmflags, * In case an auditor is called, which in turn might exec(2) a * subprocess, this variable is disabled, so that any subprocess * escapes ldd(1) processing. + * + * Also, look for LD_TOXIC_PATH */ else if (*s1 == 'T') { if (((len == MSG_LD_TRACE_OBJS_SIZE) && @@ -1850,7 +1853,13 @@ ld_generic_env(const char *s1, size_t len, const char *s2, Word *lmflags, select |= SEL_ACT_LML; val = LML_FLG_TRC_SEARCH; variable = ENV_FLG_TRACE_PTHS; + } else if ((len == MSG_LD_TOXICPATH_SIZE) && (strncmp(s1, + MSG_ORIG(MSG_LD_TOXICPATH), MSG_LD_TOXICPATH_SIZE) == 0)) { + select |= SEL_ACT_SPEC_1; + str = &rpl_ldtoxic; + variable = ENV_FLG_TOXICPATH; } + } /* * LD_UNREF and LD_UNUSED (internal, used by ldd(1)). @@ -1974,7 +1983,8 @@ ld_generic_env(const char *s1, size_t len, const char *s2, Word *lmflags, *lmtflags &= ~val; } else if (select & SEL_ACT_SPEC_1) { /* - * variable is either ENV_FLG_FLAGS or ENV_FLG_LIBPATH + * variable is either ENV_FLG_FLAGS, ENV_FLG_LIBPATH, or + * ENV_FLG_TOXICPATH */ if (env_flags & ENV_TYP_NULL) *str = NULL; diff --git a/usr/src/cmd/sgs/yacc/common/dextern.h b/usr/src/cmd/sgs/yacc/common/dextern.h index e90aa60468..54b441cac4 100644 --- a/usr/src/cmd/sgs/yacc/common/dextern.h +++ b/usr/src/cmd/sgs/yacc/common/dextern.h @@ -26,11 +26,13 @@ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ + #ifndef _DEXTERN_H #define _DEXTERN_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <stdio.h> #include <inttypes.h> #include <ctype.h> @@ -42,6 +44,7 @@ #include <unistd.h> #include <stdlib.h> #include <wctype.h> +#include <limits.h> #ifdef __cplusplus extern "C" { @@ -301,6 +304,12 @@ extern int wscmp(const wchar_t *, const wchar_t *); extern char *parser; +#ifndef PBUFSIZE +#define PBUFSIZE PATH_MAX +#endif + +extern char pbuf[PBUFSIZE]; + /* default settings for a number of macros */ /* name of yacc tempfiles */ @@ -324,7 +333,11 @@ extern char *parser; #endif #ifndef PARSER -#define PARSER "/usr/share/lib/ccs/yaccpar" +#define PARSER "/share/lib/ccs/yaccpar" +#endif + +#ifndef PARSERPREFIX +#define PARSERPREFIX "/usr" #endif /* diff --git a/usr/src/cmd/sgs/yacc/common/y1.c b/usr/src/cmd/sgs/yacc/common/y1.c index 845f82d367..0e67d9047b 100644 --- a/usr/src/cmd/sgs/yacc/common/y1.c +++ b/usr/src/cmd/sgs/yacc/common/y1.c @@ -26,7 +26,9 @@ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ #include "dextern.h" #include <sys/param.h> @@ -34,6 +36,7 @@ #include <unistd.h> #include <locale.h> #include <stdarg.h> /* For error() */ +#include <libgen.h> static void mktbls(void); static void others(void); @@ -236,6 +239,25 @@ mktbls() lsetsize = INIT_LSIZE + 1; } +static int +yacc_assemble_path(char *buf, size_t size, const char *file, int type) +{ + int ret; + char origin[PATH_MAX]; + + if (type != 0) { + ret = readlink("/proc/self/path/a.out", origin, PATH_MAX - 1); + if (ret < 0) + error(gettext( + "yacc: failed to read origin from /proc\n")); + origin[ret] = '\0'; + return (snprintf(buf, size, "%s/../%s", dirname(origin), + file)); + } + + return (snprintf(buf, size, "%s/%s", PARSERPREFIX, file)); +} + /* put out other arrays, copy the parsers */ static void others() @@ -244,7 +266,17 @@ others() int c, i, j; int tmpline; - finput = fopen(parser, "r"); + if (parser == NULL) { + parser = pbuf; + (void) yacc_assemble_path(pbuf, PBUFSIZE, PARSER, 1); + finput = fopen(parser, "r"); + if (finput == NULL) { + (void) yacc_assemble_path(pbuf, PBUFSIZE, PARSER, 0); + finput = fopen(parser, "r"); + } + } else { + finput = fopen(parser, "r"); + } if (finput == NULL) /* * TRANSLATION_NOTE -- This is a message from yacc. diff --git a/usr/src/cmd/sgs/yacc/common/y2.c b/usr/src/cmd/sgs/yacc/common/y2.c index fbdaf19445..0d97f4260f 100644 --- a/usr/src/cmd/sgs/yacc/common/y2.c +++ b/usr/src/cmd/sgs/yacc/common/y2.c @@ -26,6 +26,10 @@ /* Copyright (c) 1988 AT&T */ /* All Rights Reserved */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ + #include "dextern.h" #include "sgs.h" #include <stdio.h> @@ -58,7 +62,8 @@ char *infile; /* input file name */ static int numbval; /* value of an input number */ static int toksize = NAMESIZE; static wchar_t *tokname; /* input token name */ -char *parser = PARSER; /* location of common parser */ +char *parser = NULL; /* location of common parser */ +char pbuf[PBUFSIZE]; static void finact(void); static wchar_t *cstash(wchar_t *); diff --git a/usr/src/cmd/smbios/Makefile b/usr/src/cmd/smbios/Makefile index 9b2f9fb528..391fe3e53f 100644 --- a/usr/src/cmd/smbios/Makefile +++ b/usr/src/cmd/smbios/Makefile @@ -22,6 +22,8 @@ # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2018 Joyent, Inc. +# PROG = smbios OBJS = smbios.o @@ -35,6 +37,8 @@ LDLIBS += -lsmbios -ljedec FILEMODE = 0555 STRIPFLAG = +LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN + .KEEP_STATE: all: $(PROG) diff --git a/usr/src/cmd/smbios/smbios.c b/usr/src/cmd/smbios/smbios.c index dedfe29e9c..399a85501e 100644 --- a/usr/src/cmd/smbios/smbios.c +++ b/usr/src/cmd/smbios/smbios.c @@ -21,7 +21,7 @@ /* * Copyright 2015 OmniTI Computer Consulting, Inc. All rights reserved. - * Copyright (c) 2017, Joyent, Inc. + * Copyright (c) 2018, Joyent, Inc. * Copyright 2020 Oxide Computer Company * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. @@ -388,10 +388,30 @@ print_system(smbios_hdl_t *shp, FILE *fp) (void) smbios_info_system(shp, &s); + /* + * SMBIOS definition section 3.3.2.1 is clear that the first three + * fields are little-endian, but this utility traditionally got this + * wrong, and followed RFC 4122. We keep this old behavior, but also + * provide a corrected UUID. + */ oprintf(fp, " UUID: "); - for (i = 0; i < s.smbs_uuidlen; i++) { + oprintf(fp, "%02x%02x%02x%02x-%02x%02x-%02x%02x-", + s.smbs_uuid[0], s.smbs_uuid[1], s.smbs_uuid[2], s.smbs_uuid[3], + s.smbs_uuid[4], s.smbs_uuid[5], s.smbs_uuid[6], s.smbs_uuid[7]); + for (i = 8; i < s.smbs_uuidlen; i++) { + oprintf(fp, "%02x", s.smbs_uuid[i]); + if (i == 9) + oprintf(fp, "-"); + } + oprintf(fp, "\n"); + + oprintf(fp, " UUID (Endian-corrected): "); + oprintf(fp, "%08x-%04hx-%04hx-", *((uint_t *)&s.smbs_uuid[0]), + *((ushort_t *)&s.smbs_uuid[4]), + *((ushort_t *)&s.smbs_uuid[6])); + for (i = 8; i < s.smbs_uuidlen; i++) { oprintf(fp, "%02x", s.smbs_uuid[i]); - if (i == 3 || i == 5 || i == 7 || i == 9) + if (i == 9) oprintf(fp, "-"); } oprintf(fp, "\n"); diff --git a/usr/src/cmd/ssh/THIRDPARTYLICENSE.descrip b/usr/src/cmd/ssh/THIRDPARTYLICENSE.descrip deleted file mode 100644 index 7e936fffc7..0000000000 --- a/usr/src/cmd/ssh/THIRDPARTYLICENSE.descrip +++ /dev/null @@ -1 +0,0 @@ -OPENSSH SOFTWARE diff --git a/usr/src/cmd/ssh/doc/LICENCE b/usr/src/cmd/ssh/doc/LICENCE deleted file mode 100644 index 04d6fe18e3..0000000000 --- a/usr/src/cmd/ssh/doc/LICENCE +++ /dev/null @@ -1,194 +0,0 @@ -This file is part of the ssh software. - -The licences which components of this software falls under are as -follows. First, we will summarize and say that that all components -are under a BSD licence, or a licence more free than that. - -OpenSSH contains no GPL code. - -1) - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - - [Tatu continues] - * However, I am not implying to give any licenses to any patents or - * copyrights held by third parties, and the software includes parts that - * are not under my direct control. As far as I know, all included - * source code is used in accordance with the relevant license agreements - * and can be used freely for any purpose (the GNU license being the most - * restrictive); see below for details. - - [However, none of that term is relevant at this point in time. All of - these restrictively licenced software components which he talks about - have been removed from OpenSSH, ie. - - - RSA is no longer included, found in the OpenSSL library - - IDEA is no longer included, it's use is depricated - - DES is now external, in the OpenSSL library - - GMP is no longer used, and instead we call BN code from OpenSSL - - Zlib is now external, in a library - - The make-ssh-known-hosts script is no longer included - - TSS has been removed - - MD5 is now external, in the OpenSSL library - - RC4 support has been replaced with ARC4 support from OpenSSL - - Blowfish is now external, in the OpenSSL library - - [The licence continues] - - Note that any information and cryptographic algorithms used in this - software are publicly available on the Internet and at any major - bookstore, scientific library, and patent office worldwide. More - information can be found e.g. at "http://www.cs.hut.fi/crypto". - - The legal status of this program is some combination of all these - permissions and restrictions. Use only at your own responsibility. - You will be responsible for any legal consequences yourself; I am not - making any claims whether possessing or using this is legal or not in - your country, and I am not taking any responsibility on your behalf. - - - NO WARRANTY - - BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY - FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN - OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES - PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED - OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS - TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE - PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, - REPAIR OR CORRECTION. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING - WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR - REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, - INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING - OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED - TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY - YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER - PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE - POSSIBILITY OF SUCH DAMAGES. - -2) - The 32-bit CRC implementation in crc32.c is due to Gary S. Brown. - Comments in the file indicate it may be used for any purpose without - restrictions: - - * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or - * code or tables extracted from it, as desired without restriction. - -3) - The 32-bit CRC compensation attack detector in deattack.c was - contributed by CORE SDI S.A. under a BSD-style license. See - http://www.core-sdi.com/english/ssh/ for details. - - * Cryptographic attack detector for ssh - source code - * - * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. - * - * All rights reserved. Redistribution and use in source and binary - * forms, with or without modification, are permitted provided that - * this copyright notice is retained. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR - * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS - * SOFTWARE. - * - * Ariel Futoransky <futo@core-sdi.com> - * <http://www.core-sdi.com> - -3a) - Various parts are from the University of California. - - * Copyright (c) 1983, 1987, 1989-1995 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - - * Copyright (c) 1989, 1991, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - -4) - Remaining components of the software are provided under a standard - 2-term BSD licence with the following names as copyright holders: - - Markus Friedl - Theo de Raadt - Niels Provos - Dug Song - Aaron Campbell - - * 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 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. diff --git a/usr/src/cmd/ssh/etc/ssh.xml b/usr/src/cmd/ssh/etc/ssh.xml deleted file mode 100644 index f5fb471669..0000000000 --- a/usr/src/cmd/ssh/etc/ssh.xml +++ /dev/null @@ -1,177 +0,0 @@ -<?xml version="1.0"?> -<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> -<!-- - CDDL HEADER START - - The contents of this file are subject to the terms of the - Common Development and Distribution License (the "License"). - You may not use this file except in compliance with the License. - - You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - or http://www.opensolaris.org/os/licensing. - See the License for the specific language governing permissions - and limitations under the License. - - When distributing Covered Code, include this CDDL HEADER in each - file and include the License file at usr/src/OPENSOLARIS.LICENSE. - If applicable, add the following below this CDDL HEADER, with the - fields enclosed by brackets "[]" replaced with your own identifying - information: Portions Copyright [yyyy] [name of copyright owner] - - CDDL HEADER END - - Copyright 2009 Sun Microsystems, Inc. All rights reserved. - Use is subject to license terms. - - Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org> - - NOTE: This service manifest is not editable; its contents will - be overwritten by package or patch operations, including - operating system upgrade. Make customizations in a different - file. ---> - -<service_bundle type='manifest' name='SUNWsshdr:ssh'> - -<service - name='network/ssh' - type='service' - version='1'> - - <create_default_instance enabled='false' /> - - <single_instance /> - - <dependency name='fs-local' - grouping='require_all' - restart_on='none' - type='service'> - <service_fmri - value='svc:/system/filesystem/local' /> - </dependency> - - <dependency name='fs-autofs' - grouping='optional_all' - restart_on='none' - type='service'> - <service_fmri value='svc:/system/filesystem/autofs' /> - </dependency> - - <dependency name='net-loopback' - grouping='require_all' - restart_on='none' - type='service'> - <service_fmri value='svc:/network/loopback' /> - </dependency> - - <dependency name='net-physical' - grouping='require_all' - restart_on='none' - type='service'> - <service_fmri value='svc:/network/physical' /> - </dependency> - - <dependency name='cryptosvc' - grouping='require_all' - restart_on='none' - type='service'> - <service_fmri value='svc:/system/cryptosvc' /> - </dependency> - - <dependency name='utmp' - grouping='require_all' - restart_on='none' - type='service'> - <service_fmri value='svc:/system/utmp' /> - </dependency> - - <dependency name='network_ipfilter' - grouping='optional_all' - restart_on='error' - type='service'> - <service_fmri value='svc:/network/ipfilter:default' /> - </dependency> - - <dependency name='config_data' - grouping='require_all' - restart_on='restart' - type='path'> - <service_fmri - value='file://localhost/etc/ssh/sshd_config' /> - </dependency> - - <dependent - name='ssh_multi-user-server' - grouping='optional_all' - restart_on='none'> - <service_fmri - value='svc:/milestone/multi-user-server' /> - </dependent> - - <exec_method - type='method' - name='start' - exec='/lib/svc/method/sshd start' - timeout_seconds='60'/> - - <exec_method - type='method' - name='stop' - exec=':kill' - timeout_seconds='60' /> - - <exec_method - type='method' - name='refresh' - exec='/lib/svc/method/sshd restart' - timeout_seconds='60' /> - - <property_group name='startd' - type='framework'> - <!-- sub-process core dumps shouldn't restart session --> - <propval name='ignore_error' - type='astring' value='core,signal' /> - </property_group> - - <property_group name='general' type='framework'> - <!-- to start stop sshd --> - <propval name='action_authorization' type='astring' - value='solaris.smf.manage.ssh' /> - </property_group> - - <property_group name='firewall_context' type='com.sun,fw_definition'> - <propval name='name' type='astring' value='ssh' /> - <propval name='ipf_method' type='astring' - value='/lib/svc/method/sshd ipfilter' /> - </property_group> - - <property_group name='firewall_config' type='com.sun,fw_configuration'> - <propval name='policy' type='astring' value='use_global' /> - <propval name='block_policy' type='astring' - value='use_global' /> - <propval name='apply_to' type='astring' value='' /> - <propval name='apply_to_6' type='astring' value='' /> - <propval name='exceptions' type='astring' value='' /> - <propval name='exceptions_6' type='astring' value='' /> - <propval name='target' type='astring' value='' /> - <propval name='target_6' type='astring' value='' /> - <propval name='value_authorization' type='astring' - value='solaris.smf.value.firewall.config' /> - </property_group> - - <stability value='Unstable' /> - - <template> - <common_name> - <loctext xml:lang='C'> - SSH server - </loctext> - </common_name> - <documentation> - <manpage title='sshd' section='1M' manpath='/usr/share/man' /> - </documentation> - </template> - -</service> - -</service_bundle> diff --git a/usr/src/cmd/ssh/etc/ssh_config b/usr/src/cmd/ssh/etc/ssh_config deleted file mode 100644 index cdb9d97d45..0000000000 --- a/usr/src/cmd/ssh/etc/ssh_config +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright (c) 2001 by Sun Microsystems, Inc. -# All rights reserved. -# -# ident "%Z%%M% %I% %E% SMI" -# -# This file provides defaults for ssh(1). -# The values can be changed in per-user configuration files $HOME/.ssh/config -# or on the command line of ssh(1). - -# Configuration data is parsed as follows: -# 1. command line options -# 2. user-specific file -# 3. system-wide file /etc/ssh/ssh_config -# -# Any configuration value is only changed the first time it is set. -# host-specific definitions should be at the beginning of the -# configuration file, and defaults at the end. - -# Example (matches compiled in defaults): -# -# Host * -# ForwardAgent no -# ForwardX11 no -# PubkeyAuthentication yes -# PasswordAuthentication yes -# FallBackToRsh no -# UseRsh no -# BatchMode no -# CheckHostIP yes -# StrictHostKeyChecking ask -# EscapeChar ~ diff --git a/usr/src/cmd/ssh/etc/sshd b/usr/src/cmd/ssh/etc/sshd deleted file mode 100644 index d52b1afd25..0000000000 --- a/usr/src/cmd/ssh/etc/sshd +++ /dev/null @@ -1,127 +0,0 @@ -#!/sbin/sh -# -# Copyright 2010 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org> -# - -. /lib/svc/share/ipf_include.sh -. /lib/svc/share/smf_include.sh - -SSHDIR=/etc/ssh -KEYGEN="/usr/bin/ssh-keygen -q" -PIDFILE=/var/run/sshd.pid - -# Checks to see if RSA, and DSA host keys are available -# if any of these keys are not present, the respective keys are created. -create_key() -{ - keypath=$1 - keytype=$2 - - if [ ! -f $keypath ]; then - # - # HostKey keywords in sshd_config may be preceded or - # followed by a mix of any number of space or tabs, - # and optionally have an = between keyword and - # argument. We use two grep invocations such that we - # can match HostKey case insensitively but still have - # the case of the path name be significant, keeping - # the pattern somewhat more readable. - # - # The character classes below contain one literal - # space and one literal tab. - # - grep -i "^[ ]*HostKey[ ]*=\{0,1\}[ ]*$keypath" \ - $SSHDIR/sshd_config | grep "$keypath" > /dev/null 2>&1 - - if [ $? -eq 0 ]; then - echo Creating new $keytype public/private host key pair - $KEYGEN -f $keypath -t $keytype -N '' - if [ $? -ne 0 ]; then - echo "Could not create $keytype key: $keypath" - exit $SMF_EXIT_ERR_CONFIG - fi - fi - fi -} - -create_ipf_rules() -{ - FMRI=$1 - ipf_file=`fmri_to_file ${FMRI} $IPF_SUFFIX` - ipf6_file=`fmri_to_file ${FMRI} $IPF6_SUFFIX` - policy=`get_policy ${FMRI}` - - # - # Get port from /etc/ssh/sshd_config - # - tports=`grep "^Port" /etc/ssh/sshd_config 2>/dev/null | \ - awk '{print $2}'` - - echo "# $FMRI" >$ipf_file - echo "# $FMRI" >$ipf6_file - for port in $tports; do - generate_rules $FMRI $policy "tcp" $port $ipf_file - generate_rules $FMRI $policy "tcp" $port $ipf6_file _6 - done -} - -# This script is being used for two purposes: as part of an SMF -# start/stop/refresh method, and as a sysidconfig(1M)/sys-unconfig(1M) -# application. -# -# Both, the SMF methods and sysidconfig/sys-unconfig use different -# arguments.. - -case $1 in - # sysidconfig/sys-unconfig arguments (-c and -u) -'-c') - /usr/bin/ssh-keygen -A - if [ $? -ne 0 ]; then - create_key $SSHDIR/ssh_host_rsa_key rsa - create_key $SSHDIR/ssh_host_dsa_key dsa - fi - ;; - -'-u') - # sys-unconfig(1M) knows how to remove ssh host keys, so there's - # nothing to do here. - : - ;; - - # SMF arguments (start and restart [really "refresh"]) - -'ipfilter') - create_ipf_rules $2 - ;; - -'start') - # - # If host keys don't exist when the service is started, create - # them; sysidconfig is not run in every situation (such as on - # the install media). - # - /usr/bin/ssh-keygen -A - if [ $? -ne 0 ]; then - create_key $SSHDIR/ssh_host_rsa_key rsa - create_key $SSHDIR/ssh_host_dsa_key dsa - fi - - /usr/lib/ssh/sshd - ;; - -'restart') - if [ -f "$PIDFILE" ]; then - /usr/bin/kill -HUP `/usr/bin/cat $PIDFILE` - fi - ;; - -*) - echo "Usage: $0 { start | restart }" - exit 1 - ;; -esac - -exit $? diff --git a/usr/src/cmd/ssh/etc/sshd_config b/usr/src/cmd/ssh/etc/sshd_config deleted file mode 100644 index fd4aa5df46..0000000000 --- a/usr/src/cmd/ssh/etc/sshd_config +++ /dev/null @@ -1,145 +0,0 @@ -# -# Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. -# -# Configuration file for sshd(1m) (see also sshd_config(4)) -# - -# Protocol versions supported -# -# The sshd shipped in this release of Solaris has support for major versions -# 1 and 2. It is recommended due to security weaknesses in the v1 protocol -# that sites run only v2 if possible. Support for v1 is provided to help sites -# with existing ssh v1 clients/servers to transition. -# Support for v1 may not be available in a future release of Solaris. -# -# To enable support for v1 an RSA1 key must be created with ssh-keygen(1). -# RSA and DSA keys for protocol v2 are created by /etc/init.d/sshd if they -# do not already exist, RSA1 keys for protocol v1 are not automatically created. - -# Uncomment ONLY ONE of the following Protocol statements. - -# Only v2 (recommended) -Protocol 2 - -# Both v1 and v2 (not recommended) -#Protocol 2,1 - -# Only v1 (not recommended) -#Protocol 1 - -# Listen port (the IANA registered port number for ssh is 22) -Port 22 - -# The default listen address is all interfaces, this may need to be changed -# if you wish to restrict the interfaces sshd listens on for a multi homed host. -# Multiple ListenAddress entries are allowed. - -# IPv4 only -#ListenAddress 0.0.0.0 -# IPv4 & IPv6 -ListenAddress :: - -# If port forwarding is enabled (default), specify if the server can bind to -# INADDR_ANY. -# This allows the local port forwarding to work when connections are received -# from any remote host. -GatewayPorts no - -# X11 tunneling options -X11Forwarding yes -X11DisplayOffset 10 -X11UseLocalhost yes - -# The maximum number of concurrent unauthenticated connections to sshd. -# start:rate:full see sshd(1) for more information. -# The default is 10 unauthenticated clients. -#MaxStartups 10:30:60 - -# Banner to be printed before authentication starts. -#Banner /etc/issue - -# Should sshd print the /etc/motd file and check for mail. -# On Solaris it is assumed that the login shell will do these (eg /etc/profile). -PrintMotd no - -# KeepAlive specifies whether keep alive messages are sent to the client. -# See sshd(1) for detailed description of what this means. -# Note that the client may also be sending keep alive messages to the server. -KeepAlive yes - -# Syslog facility and level -SyslogFacility auth -LogLevel info - -# -# Authentication configuration -# - -# Host private key files -# Must be on a local disk and readable only by the root user (root:sys 600). -HostKey /etc/ssh/ssh_host_rsa_key -HostKey /etc/ssh/ssh_host_dsa_key - -# Length of the server key -# Default 768, Minimum 512 -ServerKeyBits 768 - -# sshd regenerates the key every KeyRegenerationInterval seconds. -# The key is never stored anywhere except the memory of sshd. -# The default is 1 hour (3600 seconds). -KeyRegenerationInterval 3600 - -# Ensure secure permissions on users .ssh directory. -StrictModes yes - -# Length of time in seconds before a client that hasn't completed -# authentication is disconnected. -# Default is 600 seconds. 0 means no time limit. -LoginGraceTime 600 - -# Maximum number of retries for authentication -# Default is 6. Default (if unset) for MaxAuthTriesLog is MaxAuthTries / 2 -MaxAuthTries 6 -MaxAuthTriesLog 3 - -# Are logins to accounts with empty passwords allowed. -# If PermitEmptyPasswords is no, pass PAM_DISALLOW_NULL_AUTHTOK -# to pam_authenticate(3PAM). -PermitEmptyPasswords no - -# To disable tunneled clear text passwords, change PasswordAuthentication to no. -PasswordAuthentication yes - -# Are root logins permitted using sshd. -# Note that sshd uses pam_authenticate(3PAM) so the root (or any other) user -# maybe denied access by a PAM module regardless of this setting. -# Valid options are yes, without-password, no. -PermitRootLogin no - -# sftp subsystem -Subsystem sftp internal-sftp - - -# SSH protocol v1 specific options -# -# The following options only apply to the v1 protocol and provide -# some form of backwards compatibility with the very weak security -# of /usr/bin/rsh. Their use is not recommended and the functionality -# will be removed when support for v1 protocol is removed. - -# Should sshd use .rhosts and .shosts for password less authentication. -IgnoreRhosts yes -RhostsAuthentication no - -# Rhosts RSA Authentication -# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts. -# If the user on the client side is not root then this won't work on -# Solaris since /usr/bin/ssh is not installed setuid. -RhostsRSAAuthentication no - -# Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication. -#IgnoreUserKnownHosts yes - -# Is pure RSA authentication allowed. -# Default is yes -RSAAuthentication yes diff --git a/usr/src/cmd/stat/Makefile b/usr/src/cmd/stat/Makefile index 34149b2b37..faffc6a437 100644 --- a/usr/src/cmd/stat/Makefile +++ b/usr/src/cmd/stat/Makefile @@ -19,7 +19,7 @@ # CDDL HEADER END # # -# Copyright 2006 Sun Microsystems, Inc. All rights reserved. +# Copyright 2011, 2012, Joyent, Inc. All rights reserved. # Use is subject to license terms. # # cmd/stat/Makefile @@ -27,7 +27,14 @@ include ../Makefile.cmd -SUBDIRS= arcstat iostat mpstat vmstat fsstat kstat +SUBDIRS= arcstat \ + fsstat \ + iostat \ + kstat \ + mpstat \ + vfsstat \ + vmstat \ + ziostat all := TARGET = all install := TARGET = install diff --git a/usr/src/cmd/stat/arcstat/Makefile b/usr/src/cmd/stat/arcstat/Makefile index 6ae60a8d3d..a98e2fee7e 100644 --- a/usr/src/cmd/stat/arcstat/Makefile +++ b/usr/src/cmd/stat/arcstat/Makefile @@ -11,6 +11,7 @@ # # Copyright 2014 Adam Stevko. All rights reserved. +# Copyright (c) 2011, Joyent, Inc. All rights reserved. # include $(SRC)/cmd/Makefile.cmd diff --git a/usr/src/cmd/stat/arcstat/arcstat.pl b/usr/src/cmd/stat/arcstat/arcstat.pl index d4f12a9e1c..d4f12a9e1c 100755..100644 --- a/usr/src/cmd/stat/arcstat/arcstat.pl +++ b/usr/src/cmd/stat/arcstat/arcstat.pl diff --git a/usr/src/cmd/ssh/Makefile b/usr/src/cmd/stat/vfsstat/Makefile index c68aa94238..04b5085243 100644 --- a/usr/src/cmd/ssh/Makefile +++ b/usr/src/cmd/stat/vfsstat/Makefile @@ -18,41 +18,24 @@ # # CDDL HEADER END # - -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. +# Copyright (c) 2011, Joyent, Inc. All rights reserved. # -include ../Makefile.cmd +include $(SRC)/cmd/Makefile.cmd -SUBDIRS= \ - etc - -CLOBBERFILES += $(MSGFILE) THIRDPARTYLICENSE +PROG= vfsstat .KEEP_STATE: -all := TARGET= all -clean := TARGET= clean -clobber := TARGET= clobber -install := TARGET= install +all: $(PROG) -all clean install: $(SUBDIRS) +install: all .WAIT $(ROOTPROG) -lint: +clean: -clobber: $(SUBDIRS) clobber_local -clobber_local: - $(RM) $(CLOBBERFILES) +$(ROOTBINPROG): $(PROG) + $(INS.file) -all install: THIRDPARTYLICENSE - -$(SUBDIRS): FRC - cd $@; pwd; $(MAKE) $(TARGET) - -# skip the summary; just include the actual license clauses. -THIRDPARTYLICENSE: doc/LICENCE - $(SED) -n '/1)/,$$p' doc/LICENCE > $@ +lint: -FRC: +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/cmd/stat/vfsstat/vfsstat.pl b/usr/src/cmd/stat/vfsstat/vfsstat.pl new file mode 100644 index 0000000000..a3780b8e63 --- /dev/null +++ b/usr/src/cmd/stat/vfsstat/vfsstat.pl @@ -0,0 +1,227 @@ +#!/usr/perl5/bin/perl -w +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2011 Joyent, Inc. +# +# vfsstat - report VFS statistics per zone +# +# USAGE: vfsstat [-hIMrzZ] [interval [count]] +# -h # help +# -I # print results per interval (where applicable) +# -M # print results in MB/s +# -r # print data in comma-separated format +# -z # hide zones with no VFS activity +# -Z # print data for all zones +# +# eg, vfsstat # print summary since zone boot +# vfsstat 1 # print continually every 1 second +# vfsstat 1 5 # print 5 times, every 1 second +# +# NOTES: +# +# - The calculations and output fields emulate those from iostat(1M) as closely +# as possible. When only one zone is actively performing disk I/O, the +# results from iostat(1M) in the global zone and vfsstat in the local zone +# should be almost identical. Note that many VFS read operations are handled +# by the ARC, so vfsstat and iostat(1M) will be similar only when most +# requests are missing in the ARC. +# +# - As with iostat(1M), a result of 100% for VFS read and write utilization does +# not mean that the syscall layer is fully saturated. Instead, that +# measurement just shows that at least one operation was pending over the last +# quanta of time examined. Since the VFS layer can process more than one +# operation concurrently, this measurement will frequently be 100% but the VFS +# layer can still accept additional requests. +# +# - This script is based on Brendan Gregg's K9Toolkit examples: +# +# http://www.brendangregg.com/k9toolkit.html +# + +use Getopt::Std; +use Sun::Solaris::Kstat; +my $Kstat = Sun::Solaris::Kstat->new(); + +# Process command line args +usage() if defined $ARGV[0] and $ARGV[0] eq "--help"; +getopts('hIMrzZ') or usage(); +usage() if defined $main::opt_h; +$main::opt_h = 0; + +my $USE_MB = defined $main::opt_M ? $main::opt_M : 0; +my $USE_INTERVAL = defined $main::opt_I ? $main::opt_I : 0; +my $USE_COMMA = defined $main::opt_r ? $main::opt_r : 0; +my $HIDE_ZEROES = defined $main::opt_z ? $main::opt_z : 0; +my $ALL_ZONES = defined $main::opt_Z ? $main::opt_Z : 0; + +my ($interval, $count); +if ( defined($ARGV[0]) ) { + $interval = $ARGV[0]; + $count = defined ($ARGV[1]) ? $ARGV[1] : 2**32; + usage() if ($interval == 0); +} else { + $interval = 1; + $count = 1; +} + +my $HEADER_FMT = $USE_COMMA ? + "r/%s,w/%s,%sr/%s,%sw/%s,ractv,wactv,read_t,writ_t,%%r,%%w," . + "d/%s,del_t,zone\n" : + " r/%s w/%s %sr/%s %sw/%s ractv wactv read_t writ_t " . + "%%r %%w d/%s del_t zone\n"; +my $DATA_FMT = $USE_COMMA ? + "%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%.1f,%d,%d,%.1f,%.1f,%s,%d\n" : + "%5.1f %5.1f %5.1f %5.1f %5.1f %5.1f %6.1f %6.1f %3d %3d " . + "%5.1f %6.1f %s (%d)\n"; + +my $BYTES_PREFIX = $USE_MB ? "M" : "k"; +my $BYTES_DIVISOR = $USE_MB ? 1024 * 1024 : 1024; +my $INTERVAL_SUFFIX = $USE_INTERVAL ? "i" : "s"; +my $NANOSEC = 1000000000; + +my @fields = ( 'reads', 'writes', 'nread', 'nwritten', 'rtime', 'wtime', + 'rlentime', 'wlentime', 'delay_cnt', 'delay_time', 'snaptime' ); + +chomp(my $curzone = (`/sbin/zonename`)); + +my %old = (); +my $rows_printed = 0; + +for (my $ii = 0; $ii < $count; $ii++) { + # Read list of visible zones and their zone IDs + my @zones = (); + my %zoneids = (); + my $zoneadm = `zoneadm list -p | cut -d: -f1,2`; + @lines = split(/\n/, $zoneadm); + foreach $line (@lines) { + @tok = split(/:/, $line); + $zoneids->{$tok[1]} = $tok[0]; + push(@zones, $tok[1]); + } + + $Kstat->update(); + + # Print the column header every 20 rows + if ($rows_printed == 0 || $ALL_ZONES) { + printf($HEADER_FMT, $INTERVAL_SUFFIX, $INTERVAL_SUFFIX, + $BYTES_PREFIX, $INTERVAL_SUFFIX, $BYTES_PREFIX, + $INTERVAL_SUFFIX, $INTERVAL_SUFFIX); + } + + $rows_printed = $rows_printed >= 20 ? 0 : $rows_printed + 1; + + foreach $zone (@zones) { + if ((!$ALL_ZONES) && ($zone ne $curzone)) { + next; + } + + if (! defined $old->{$zone}) { + $old->{$zone} = (); + foreach $field (@fields) { $old->{$zone}->{$field} = 0; } + } + + # + # Kstats have a 30-character limit (KSTAT_STRLEN) on their + # names, so if the zone name exceeds that limit, use the first + # 30 characters. + # + my $trimmed_zone = substr($zone, 0, 30); + my $zoneid = $zoneids->{$zone}; + + print_stats($zone, $zoneid, + $Kstat->{'zone_vfs'}{$zoneid}{$trimmed_zone}, $old->{$zone}); + } + + sleep ($interval); +} + +exit(0); + +sub print_stats { + my $zone = $_[0]; + my $zoneid = $_[1]; + my $data = $_[2]; + my $old = $_[3]; + + my $etime = $data->{'snaptime'} - + ($old->{'snaptime'} > 0 ? $old->{'snaptime'} : $data->{'crtime'}); + + # Calculate basic statistics + my $rate_divisor = $USE_INTERVAL ? 1 : $etime; + my $reads = ($data->{'reads'} - $old->{'reads'}) / $rate_divisor; + my $writes = ($data->{'writes'} - $old->{'writes'}) / $rate_divisor; + my $nread = ($data->{'nread'} - $old->{'nread'}) / + $rate_divisor / $BYTES_DIVISOR; + my $nwritten = ($data->{'nwritten'} - $old->{'nwritten'}) / + $rate_divisor / $BYTES_DIVISOR; + + # Calculate transactions per second + my $r_tps = ($data->{'reads'} - $old->{'reads'}) / $etime; + my $w_tps = ($data->{'writes'} - $old->{'writes'}) / $etime; + + # Calculate average length of active queue + my $r_actv = (($data->{'rlentime'} - $old->{'rlentime'}) / $NANOSEC) / + $etime; + my $w_actv = (($data->{'wlentime'} - $old->{'wlentime'}) / $NANOSEC) / + $etime; + + # Calculate average service time + # multiply by 1000 to convert to usecs for conssistency with del_t + my $read_t = ($r_tps > 0 ? $r_actv * (1000 / $r_tps) : 0.0) * 1000; + my $writ_t = ($w_tps > 0 ? $w_actv * (1000 / $w_tps) : 0.0) * 1000; + + # Calculate I/O throttle delay metrics + my $delays = $data->{'delay_cnt'} - $old->{'delay_cnt'}; + my $d_tps = $delays / $etime; + my $del_t = $delays > 0 ? + ($data->{'delay_time'} - $old->{'delay_time'}) / $delays : 0.0; + + # Calculate the % time the VFS layer is active + my $r_b_pct = ((($data->{'rtime'} - $old->{'rtime'}) / $NANOSEC) / + $etime) * 100; + my $w_b_pct = ((($data->{'wtime'} - $old->{'wtime'}) / $NANOSEC) / + $etime) * 100; + + if (! $HIDE_ZEROES || $reads != 0.0 || $writes != 0.0 || + $nread != 0.0 || $nwritten != 0.0) { + printf($DATA_FMT, $reads, $writes, $nread, $nwritten, $r_actv, + $w_actv, $read_t, $writ_t, $r_b_pct, $w_b_pct, + $d_tps, $del_t, substr($zone, 0, 8), $zoneid); + } + + # Save current calculations for next loop + foreach (@fields) { $old->{$_} = $data->{$_}; } +} + +sub usage { + print STDERR <<END; +USAGE: vfsstat [-hIMrzZ] [interval [count]] + eg, vfsstat # print summary since zone boot + vfsstat 1 # print continually every 1 second + vfsstat 1 5 # print 5 times, every 1 second + vfsstat -I # print results per interval (where applicable) + vfsstat -M # print results in MB/s + vfsstat -r # print results in comma-separated format + vfsstat -z # hide zones with no VFS activity + vfsstat -Z # print results for all zones +END + exit 1; +} diff --git a/usr/src/cmd/stat/ziostat/Makefile b/usr/src/cmd/stat/ziostat/Makefile new file mode 100644 index 0000000000..c338b59678 --- /dev/null +++ b/usr/src/cmd/stat/ziostat/Makefile @@ -0,0 +1,41 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +include $(SRC)/cmd/Makefile.cmd + +PROG= ziostat + +.KEEP_STATE: + +all: $(PROG) + +install: all .WAIT $(ROOTPROG) + +clean: + +$(ROOTBINPROG): $(PROG) + $(INS.file) + +lint: + +include $(SRC)/cmd/Makefile.targ diff --git a/usr/src/cmd/stat/ziostat/ziostat.pl b/usr/src/cmd/stat/ziostat/ziostat.pl new file mode 100755 index 0000000000..cf95d2f5a5 --- /dev/null +++ b/usr/src/cmd/stat/ziostat/ziostat.pl @@ -0,0 +1,204 @@ +#!/usr/perl5/bin/perl -w +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2011 Joyent, Inc. +# +# ziostat - report I/O statistics per zone +# +# USAGE: ziostat [-hIMrzZ] [interval [count]] +# -h # help +# -I # print results per interval (where applicable) +# -M # print results in MB/s +# -r # print data in comma-separated format +# -z # hide zones with no ZFS I/O activity +# -Z # print data for all zones +# +# eg, ziostat # print summary since zone boot +# ziostat 1 # print continually every 1 second +# ziostat 1 5 # print 5 times, every 1 second +# +# NOTES: +# +# - The calculations and output fields emulate those from iostat(1M) as closely +# as possible. When only one zone is actively performing disk I/O, the +# results from iostat(1M) in the global zone and ziostat in the local zone +# should be almost identical. +# +# - As with iostat(1M), a result of 100% for disk utilization does not mean that +# the disk is fully saturated. Instead, that measurement just shows that at +# least one operation was pending over the last quanta of time examined. +# Since disk devices can process more than one operation concurrently, this +# measurement will frequently be 100% but the disk can still offer higher +# performance. +# +# - This script is based on Brendan Gregg's K9Toolkit examples: +# +# http://www.brendangregg.com/k9toolkit.html +# + +use Getopt::Std; +use Sun::Solaris::Kstat; +my $Kstat = Sun::Solaris::Kstat->new(); + +# Process command line args +usage() if defined $ARGV[0] and $ARGV[0] eq "--help"; +getopts('hIMrzZ') or usage(); +usage() if defined $main::opt_h; +$main::opt_h = 0; + +my $USE_MB = defined $main::opt_M ? $main::opt_M : 0; +my $USE_INTERVAL = defined $main::opt_I ? $main::opt_I : 0; +my $USE_COMMA = defined $main::opt_r ? $main::opt_r : 0; +my $HIDE_ZEROES = defined $main::opt_z ? $main::opt_z : 0; +my $ALL_ZONES = defined $main::opt_Z ? $main::opt_Z : 0; + +my ($interval, $count); +if ( defined($ARGV[0]) ) { + $interval = $ARGV[0]; + $count = defined ($ARGV[1]) ? $ARGV[1] : 2**32; + usage() if ($interval == 0); +} else { + $interval = 1; + $count = 1; +} + +my $HEADER_FMT = $USE_COMMA ? + "r/%s,%sr/%s,actv,wsvc_t,asvc_t,%%b,zone\n" : + " r/%s %sr/%s actv wsvc_t asvc_t %%b zone\n"; +my $DATA_FMT = $USE_COMMA ? + "%.1f,%.1f,%.1f,%.1f,%.1f,%d,%s,%d\n" : + " %6.1f %6.1f %6.1f %6.1f %6.1f %3d %s (%d)\n"; + +my $BYTES_PREFIX = $USE_MB ? "M" : "k"; +my $BYTES_DIVISOR = $USE_MB ? 1024 * 1024 : 1024; +my $INTERVAL_SUFFIX = $USE_INTERVAL ? "i" : "s"; +my $NANOSEC = 1000000000; + +my @fields = ( 'reads', 'nread', 'waittime', 'rtime', 'rlentime', 'snaptime' ); + +chomp(my $curzone = (`/sbin/zonename`)); + +# Read list of visible zones and their zone IDs +my @zones = (); +my %zoneids = (); +my $zoneadm = `zoneadm list -p | cut -d: -f1,2`; +@lines = split(/\n/, $zoneadm); +foreach $line (@lines) { + @tok = split(/:/, $line); + $zoneids->{$tok[1]} = $tok[0]; + push(@zones, $tok[1]); +} + +my %old = (); +my $rows_printed = 0; + +$Kstat->update(); + +for (my $ii = 0; $ii < $count; $ii++) { + # Print the column header every 20 rows + if ($rows_printed == 0 || $ALL_ZONES) { + printf($HEADER_FMT, $INTERVAL_SUFFIX, $BYTES_PREFIX, + $INTERVAL_SUFFIX, $INTERVAL_SUFFIX); + } + + $rows_printed = $rows_printed >= 20 ? 0 : $rows_printed + 1; + + foreach $zone (@zones) { + if ((!$ALL_ZONES) && ($zone ne $curzone)) { + next; + } + + if (! defined $old->{$zone}) { + $old->{$zone} = (); + foreach $field (@fields) { $old->{$zone}->{$field} = 0; } + } + + # + # Kstats have a 30-character limit (KSTAT_STRLEN) on their + # names, so if the zone name exceeds that limit, use the first + # 30 characters. + # + my $trimmed_zone = substr($zone, 0, 30); + my $zoneid = $zoneids->{$zone}; + + print_stats($zone, $zoneid, + $Kstat->{'zone_zfs'}{$zoneid}{$trimmed_zone}, $old->{$zone}); + } + + sleep ($interval); + $Kstat->update(); +} + +sub print_stats { + my $zone = $_[0]; + my $zoneid = $_[1]; + my $data = $_[2]; + my $old = $_[3]; + + my $etime = $data->{'snaptime'} - + ($old->{'snaptime'} > 0 ? $old->{'snaptime'} : $data->{'crtime'}); + + # Calculate basic statistics + my $rate_divisor = $USE_INTERVAL ? 1 : $etime; + my $reads = ($data->{'reads'} - $old->{'reads'}) / $rate_divisor; + my $nread = ($data->{'nread'} - $old->{'nread'}) / + $rate_divisor / $BYTES_DIVISOR; + + # Calculate overall transactions per second + my $ops = $data->{'reads'} - $old->{'reads'}; + my $tps = $ops / $etime; + + # Calculate average length of disk run queue + my $actv = (($data->{'rlentime'} - $old->{'rlentime'}) / $NANOSEC) / + $etime; + + # Calculate average disk wait and service times + my $wsvc = $ops > 0 ? (($data->{'waittime'} - $old->{'waittime'}) / + 1000000) / $ops : 0.0; + my $asvc = $tps > 0 ? $actv * (1000 / $tps) : 0.0; + + # Calculate the % time the disk run queue is active + my $b_pct = ((($data->{'rtime'} - $old->{'rtime'}) / $NANOSEC) / + $etime) * 100; + + if (! $HIDE_ZEROES || $reads != 0.0 || $nread != 0.0 ) { + printf($DATA_FMT, $reads, $nread, $actv, $wsvc, $asvc, + $b_pct, substr($zone, 0, 8), $zoneid); + } + + # Save current calculations for next loop + foreach (@fields) { $old->{$_} = $data->{$_}; } +} + +sub usage { + print STDERR <<END; +USAGE: ziostat [-hIMrzZ] [interval [count]] + eg, ziostat # print summary since zone boot + ziostat 1 # print continually every 1 second + ziostat 1 5 # print 5 times, every 1 second + ziostat -I # print results per interval (where applicable) + ziostat -M # print results in MB/s + ziostat -r # print results in comma-separated format + ziostat -z # hide zones with no ZFS I/O activity + ziostat -Z # print results for all zones +END + exit 1; +} diff --git a/usr/src/cmd/svc/configd/backend.c b/usr/src/cmd/svc/configd/backend.c index b7ed400cfd..4c3a7d0816 100644 --- a/usr/src/cmd/svc/configd/backend.c +++ b/usr/src/cmd/svc/configd/backend.c @@ -22,6 +22,7 @@ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * Copyright 2017 Joyent, Inc. */ /* @@ -2194,7 +2195,24 @@ backend_tx_begin(backend_type_t t, backend_tx_t **txp) UPDATE_TOTALS((*txp)->bt_be, bt_exec, ts, vts); if (r == SQLITE_FULL) (*txp)->bt_full = 1; - r = backend_error((*txp)->bt_be, r, errmsg); + /* + * We explicitly handle an ENOSPC error here for the beginning of the + * transaction, instead of in backend_error, which calls backend_panic + * for this case, resulting in the death of svc.configd. That may be + * appropriate in other cases, but in this case we would rather fail so + * that configd remains up and the caller gets an approprate error. The + * failure mode is that there is not enough swap space to open the + * non-persistent database, so there won't be enough space to restart + * configd, leaving SMF in a state requiring manual intervention. + */ + if (r == SQLITE_CANTOPEN && errno == ENOSPC && + (*txp)->bt_type == BACKEND_TYPE_NONPERSIST) { + configd_info("Warning: no space to open %s\n", + bes[BACKEND_TYPE_NONPERSIST]->be_path); + r = REP_PROTOCOL_FAIL_NO_RESOURCES; + } else { + r = backend_error((*txp)->bt_be, r, errmsg); + } if (r != REP_PROTOCOL_SUCCESS) { assert(r != REP_PROTOCOL_DONE); diff --git a/usr/src/cmd/svc/configd/rc_node.c b/usr/src/cmd/svc/configd/rc_node.c index a5b968c53c..33cb2be7a2 100644 --- a/usr/src/cmd/svc/configd/rc_node.c +++ b/usr/src/cmd/svc/configd/rc_node.c @@ -24,6 +24,9 @@ * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2016 by Delphix. All rights reserved. */ +/* + * Copyright (c) 2013, Joyent, Inc. All rights reserved. + */ /* * rc_node.c - In-memory SCF object management diff --git a/usr/src/cmd/svc/milestone/Makefile b/usr/src/cmd/svc/milestone/Makefile index 901727dc9f..576576ba2e 100644 --- a/usr/src/cmd/svc/milestone/Makefile +++ b/usr/src/cmd/svc/milestone/Makefile @@ -21,6 +21,8 @@ # # Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. # +# Copyright 2019 Joyent, Inc. +# include ../../Makefile.cmd @@ -30,6 +32,7 @@ BUILTXML= \ console-login.xml FSSVCS= \ + joyent-fs.xml \ local-fs.xml \ minimal-fs.xml \ root-fs.xml \ @@ -38,6 +41,7 @@ FSSVCS= \ FSMANIFESTS= $(FSSVCS:%=$(ROOTSVCSYSTEMFILESYSTEM)/%) NETSVCS= \ + network-early-admin.xml \ network-initial.xml \ network-install.xml \ network-iptun.xml \ @@ -75,8 +79,12 @@ SYSTEMSVCS= \ early-manifest-import.xml \ identity.xml \ manifest-import.xml \ + mdata.xml \ process-security.xml \ rmtmpfiles.xml \ + smartdc-config.xml \ + smartdc-init.xml \ + smartdc-ur.xml \ vtdaemon.xml SYSTEMMANIFESTS = $(SYSTEMSVCS:%=$(ROOTSVCSYSTEM)/%) @@ -103,6 +111,7 @@ SVCMETHOD=\ console-login \ devices-audio \ devices-local \ + fs-joyent \ fs-local \ fs-minimal \ fs-root \ @@ -110,6 +119,9 @@ SVCMETHOD=\ identity-domain \ identity-node \ manifest-import \ + mdata-execute \ + mdata-fetch \ + net-early-admin \ net-loc \ net-loopback \ net-init \ @@ -122,6 +134,11 @@ SVCMETHOD=\ net-routing-setup \ net-svc \ rmtmpfiles \ + smartdc-config \ + smartdc-init \ + smartdc-ur \ + sysidtool-net \ + sysidtool-system \ vtdaemon $(ROOTSVCMETHOD) := FILEMODE = 0555 @@ -161,4 +178,4 @@ $(ROOTSVCSYSTEM)/svc/%: % $(ROOT)/lib/svc/share/%: %.share $(INS.rename) -clean lint _msg: +clean _msg: diff --git a/usr/src/cmd/svc/milestone/console-login b/usr/src/cmd/svc/milestone/console-login index c7003b103b..8ddaaf29ac 100644 --- a/usr/src/cmd/svc/milestone/console-login +++ b/usr/src/cmd/svc/milestone/console-login @@ -23,6 +23,7 @@ # # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2015 Joyent, Inc. # # This method script manages all vt logins including system # console login. @@ -38,18 +39,29 @@ . /lib/svc/share/smf_include.sh -if [ "$1" != "default" ]; then - if smf_dont_configure_vt; then - /usr/sbin/svcadm disable $SMF_FMRI - exit $SMF_EXIT_OK - fi -fi - getproparg() { val=`svcprop -p $2 $SMF_FMRI` [ -n "$val" ] && [ "$val" != "\"\"" ] && echo $1 $val } +# The service configuration invokes this script with "%i", the name of the +# instance, as the first argument. +instance="$1" +if [ -z "$instance" ]; then + exit $SMF_EXIT_ERR_CONFIG +fi + +case "$instance" in +vt*) + # This instance is for one of the virtual terminal devices. Check to + # see if the vt subsystem is initialised: + if smf_dont_configure_vt; then + /usr/sbin/svcadm disable "$SMF_FMRI" + exit $SMF_EXIT_OK + fi + ;; +esac + args="-g" val=`svcprop -p ttymon/device $SMF_FMRI` @@ -61,6 +73,20 @@ if [ "$val" = "/dev/vt/1" ]; then exit $SMF_EXIT_ERR_CONFIG fi +# In SmartOS we use ttyb for metadata so we set to a non-existent device here +# to disable the service and exit. +if [[ ${val} == "/dev/term/b" && \ + $(sysinfo | json "Product") == "SmartDC HVM" ]]; then + + val=/dev/do_not_use_ttyb_in_a_vm +fi + +if [[ ! -e $val ]]; then + # This device doesn't exist, can't run a tty on it. + /usr/sbin/svcadm disable $SMF_FMRI + exit $SMF_EXIT_OK +fi + args="$args -d $val" args="$args `getproparg -l ttymon/label`" diff --git a/usr/src/cmd/svc/milestone/fs-joyent b/usr/src/cmd/svc/milestone/fs-joyent new file mode 100755 index 0000000000..0a9dc065a3 --- /dev/null +++ b/usr/src/cmd/svc/milestone/fs-joyent @@ -0,0 +1,312 @@ +#!/bin/bash +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2020 Joyent, Inc. +# + +set -o xtrace + +fatal() +{ + echo "Error: $1" + exit $SMF_EXIT_ERR_FATAL +} + +. /lib/svc/share/smf_include.sh +. /lib/svc/share/fs_include.sh +. /lib/sdc/usb-key.sh + +# first of all, if we aren't the global zone this doesn't make any sense to run + +smf_is_globalzone || exit $SMF_EXIT_OK + +# We need the links to /dev/dsk. Rather than trying to play games with manually +# invoking syseventd ask devfsadm to do some work. +/usr/sbin/devfsadm -c disk + +function destroy_zpools +{ + for pool in $(zpool list -p -o name | grep -v NAME) ; do + zpool destroy -f ${pool} + done +} + +function mount_zfs +{ + local dataset=$1 + local mountpoint=$2 + local output= + + # + # Try to mount the ZFS dataset. If the mountpoint is busy, wait five + # seconds and try again. Fail if the mount attempt returns EBUSY three + # consecutive times. + # + for i in {1..3}; do + output=$(mount -F zfs ${dataset} ${mountpoint} 2>&1) + if [[ $? -eq 0 ]]; then + break + fi + + if [ "${output}" == "mount failed: Device busy" ]; then + sleep 5 + else + echo ${output} 1>&2 + return + fi + done + + # The mount attempt must have failed + echo ${output} 1>&2 +} + +function unlock_pool +{ + local pool=$1 + + # If the key is already loaded, don't bother trying again + local keystatus="$(zfs get -Hpo value keystatus $pool)" + if [[ "$keystatus" == "available" ]]; then + return + fi + + kbmadm unlock $pool && return + + echo "Failed to unlock $pool; recovery may be required" | \ + tee -a /dev/console >&2 + + exit $SMF_EXIT_ERR_FATAL +} + +/bin/bootparams | grep "^noimport=true" >/dev/null +if [ $? -ne 0 ]; then + # If the zpool doesn't exist, then there's nothing to mount. + + # Assume the system zpool is zones, but if a different system pool + # identifies itself (by virtue of the .system_pool file being present in the + # pool's root dataset), then use that system pool instead. + SYS_ZPOOL=zones + + # Import specified zpools, or all zpools available + pools=$(/bin/bootparams | egrep "^zpools?=" | cut -d= -f2 | tr , ' ') + if [ -z ${pools} ]; then + pools=$(zpool import | grep "pool:" | awk '{print $2}') + fi + + for pool in $pools; do + zpool import -f $pool || continue + + is_encr="$(zfs get -Hpo value encryption $pool)" + + [[ "$is_encr" != "off" ]] && unlock_pool $pool + + # Due to early, failed attempts to support the filesystem_limits + # feature we now need to ensure the dependent feature is enabled. + zpool set feature@extensible_dataset=enabled $pool + if [[ -f /$pool/.system_pool ]]; then + SYS_ZPOOL=$pool + [[ "$is_encr" != "off" ]] && kbmadm set-syspool $pool + fi + done + + svccfg -s svc:/system/smartdc/init setprop \ + config/zpool=${SYS_ZPOOL} + svccfg -s svc:/system/smartdc/init:default refresh + + # If the destroy_zpools boot parameter is set, destroy all zpools + /bin/bootparams | grep "^destroy_zpools=true" >/dev/null + if [ $? -eq 0 ]; then + destroy_zpools + fi + + # A machine is reset to its original unsetup state (i.e. a 'factory reset') + # when the smartdc:factoryreset ZFS user property is set on the var dataset. + reset=$(zfs get -H -o value smartdc:factoryreset ${SYS_ZPOOL}/var) + if [ "${reset}" == "yes" ]; then + destroy_zpools + fi + + # Capture the zpool's status output in the method's log file for + # troubleshooting. + # + # Note: It is critical that we do not run 'status -v'. If there are errors + # in the zpool error log and the zpool is large (e.g. > 200TB), then the + # lookup for the error file names can take a very long time (several hours). + # This would block the system boot until it completed. + zpool status ${SYS_ZPOOL} + if [ $? -eq 0 ]; then + + # Stash the SUNWdefault.xml file so we can update the + # persistent version after mounting zones/config. + cp /etc/zones/SUNWdefault.xml /tmp/ + + # Mount and configure all system datasets + mount_zfs ${SYS_ZPOOL}/var /var + mount_zfs ${SYS_ZPOOL}/config /etc/zones + mount_zfs ${SYS_ZPOOL}/opt /opt + + # Update the the persistent SUNWdefault.xml file to match the + # contents on ramdisk now that zones/config is mounted. + cp /tmp/SUNWdefault.xml /etc/zones/ + rm -f /tmp/SUNWdefault.xml + + # + # We include a manifest of all files shipped in the platform image, + # along with an MD5 hash of their contents. This was originally + # shipped as "/var/log/manifest", but once a machine is set up, "/var" + # now comes from the pool. The upshot of this is that every SmartOS + # machine has the manifest from the platform at setup time stored in + # "/var/log/manifest". Now that the manifest has moved to an + # accessible location, we should remove this file and replace it with a + # symbolic link. + # + if [[ -f '/var/log/manifest' && ! -L '/var/log/manifest' && + ! -e '/var/log/manifest.original' ]]; then + mv '/var/log/manifest' '/var/log/manifest.original' + ln -s '../../usr/share/smartos/manifest' '/var/log/manifest' + fi + + if [[ -z $(/bin/bootparams | grep '^smartos=true') ]]; then + mkdir -p /opt/smartdc/agents/smf + mount -O -F lofs /var/svc/manifest/site /opt/smartdc/agents/smf + fi + + if [[ -n $(/bin/bootparams | grep '^headnode=true') || \ + -n $(/bin/bootparams | grep '^smartos=true') ]]; then + mkdir /usbkey + mount_zfs ${SYS_ZPOOL}/usbkey /usbkey + fi + + if [[ -n $(/bin/bootparams | grep '^smartos=true') ]]; then + mount -F lofs /usbkey/shadow /etc/shadow + mount -F lofs /usbkey/ssh /etc/ssh + fi + + swap -a /dev/zvol/dsk/${SYS_ZPOOL}/swap || \ + fatal "failed to configure swap device" + + # + # Configure the dump device on top of a ZFS volume. In addition to the + # usual dumpadm(1m) call, there are two prerequisites for using this + # volume as a dump device: (1) that zvol must be using the noparity + # checksum algorithem, and (2) the MULTI_VDEV_CRASH_DUMP ZFS feature + # must be enabled. Prerequisite (1) is necessary since the exact + # on-disk value for ZIO_CHECKSUM_NOPARITY has changed, so to avoid a + # flag day on all systems, this service just sets that property again + # every time. + # + zfs set checksum=noparity ${SYS_ZPOOL}/dump || \ + fatal "failed to set checksum=noparity on dump zvol" + zpool set feature@multi_vdev_crash_dump=enabled ${SYS_ZPOOL} || \ + fatal "failed to enable multi_vdev_crash_dump ZFS feature" + dumpadm -y -d /dev/zvol/dsk/${SYS_ZPOOL}/dump || \ + fatal "failed to configure dump device" + + zfs list -H -o name ${SYS_ZPOOL}/cores/global >/dev/null 2>&1 + if [ $? -ne 0 ]; then + # Booting for the first time on a CN whose cores dataset is setup + # in the 6.x style. Convert to the new style. + zfs destroy -r ${SYS_ZPOOL}/cores + zfs create -o compression=gzip -o mountpoint=none ${SYS_ZPOOL}/cores + zfs create -o quota=10g -o mountpoint=/${SYS_ZPOOL}/global/cores \ + ${SYS_ZPOOL}/cores/global + fi + + ln -s /${SYS_ZPOOL}/global/cores /cores + + [[ -f /${SYS_ZPOOL}/currbooted ]] && \ + mv /${SYS_ZPOOL}/currbooted /${SYS_ZPOOL}/lastbooted + uname -v >/${SYS_ZPOOL}/currbooted + fi +fi + + +# The rest only applies to the headnode +/bin/bootparams | grep "^headnode=true" >/dev/null || exit $SMF_EXIT_OK + +# If we rebooted during an upgrade, we're in deep trouble. +if [ -d /var/upgrade_in_progress ]; then + echo "ERROR: An upgrade was in progress when the system rebooted." \ + >/dev/console + echo " The system is in an indeterminate state, unable to continue." \ + >/dev/console + exit $SMF_EXIT_ERR_FATAL +fi + +COPYINPOINT=`svcprop -p "joyentfs/usb_copy_path" ${SMF_FMRI}` +DEBUG=`svcprop -p "joyentfs/debug" ${SMF_FMRI}` + +if [[ -d /mnt ]]; then + chown root:root /mnt + chmod 700 /mnt +else + mkdir -m 700 /mnt +fi + +function make_usb_copy_if_possible +{ + [[ -n "${SYS_ZPOOL}" ]] || fatal "don't know system zpool name" + + zpool list -Ho name | grep "^${SYS_ZPOOL}\$" + if [[ $? != 0 ]]; then + echo "skipping USB copy setup: no ${SYS_ZPOOL} zpool" >/dev/console + # Still return OK, because this is the expected case for first headnode + # boot. + return 0 + fi + + USBDATASET=${SYS_ZPOOL}/usbkey + if ! zfs list -Ho name | grep "^${USBDATASET}\$" >/dev/null; then + echo "skipping USB copy setup: no zones/usbkey dataset" >/dev/console + # Still return OK, because as of HEAD-2343 a CN being converted to a HN + # will not yet have this dataset on its first boot as an HN. + return 0 + fi + + echo "" > /dev/console + echo "Moving files from USB boot device onto disk storage." > /dev/console + echo "This may take several minutes. Please note the time..." > /dev/console + echo "" > /dev/console + echo "" > /dev/console + + mkdir ${COPYINPOINT} + mount_zfs ${USBDATASET} ${COPYINPOINT} + + (cd ${USBMOUNTPOINT}; rsync -av --log-file=/dev/console --exclude private --exclude os * ${COPYINPOINT}) + if [[ -d ${USBMOUNTPOINT}/os ]]; then + (cd ${USBMOUNTPOINT}/os ; \ + for dir in $(ls -d *); do + # source comes from pcfs which we've got lowering the case + # of everything, but we normally use capital T and Z for + # buildstamp, so fix it here. + source_dir=${dir} + target_dir=$(echo ${dir} | tr "[:lower:]" "[:upper:]") + mkdir -p ${COPYINPOINT}/os + echo "Copying: ${source_dir}/ ${COPYINPOINT}/os/${target_dir}" > /dev/console + rsync -a ${source_dir}/ ${COPYINPOINT}/os/${target_dir} + done + ) + fi + + echo "" > /dev/console + echo "Done copying files from USB device" > /dev/console + return 0 +} + +USBMOUNTPOINT=$(mount_usb_key "") +if [[ $? -ne 0 ]]; then + fatal "couldn't mount USB key" +fi + +make_usb_copy_if_possible +exit $? diff --git a/usr/src/cmd/svc/milestone/fs-root b/usr/src/cmd/svc/milestone/fs-root index 9652eaaf94..f9de44c831 100644 --- a/usr/src/cmd/svc/milestone/fs-root +++ b/usr/src/cmd/svc/milestone/fs-root @@ -22,6 +22,7 @@ # # Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2015 Nexenta Systems, Inc. All rights reserved. +# Copyright 2016 Joyent, Inc. # # Make sure that the libraries essential to this stage of booting can be found. @@ -64,92 +65,27 @@ if smf_is_nonglobalzone; then exit $SMF_EXIT_OK fi -# -# Root is already mounted (by the kernel), but still needs to be -# checked, possibly remounted and entered into mnttab. First -# mount /usr if it is a separate file system. If the file system -# type is something other than zfs, mount it read-only. This must -# be done first to allow utilities such as fsck and setmnt to -# reside on /usr minimizing the space required by the root file -# system. -# -readvfstab "/usr" < $vfstab -if [ -n "$mountp" ]; then - if [ "$fstype" = zfs ]; then - mountfs - /usr $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL - else - # - # Must use -o largefiles here to ensure the - # read-only mount does not fail as a result of - # having a large file present on /usr. This gives - # fsck a chance to fix up the largefiles flag - # before we remount /usr read-write. - # - if [ "x$mntopts" = x- ]; then - mntopts='ro,largefiles' - else - checkopt largefiles $mntopts - if [ "x$option" != xlargefiles ]; then - mntopts="largefiles,$mntopts" - fi - - checkopt ro $mntopts - if [ "x$option" != xro ]; then - mntopts="ro,$mntopts" - fi - - # - # Requesting logging on a read-only mount - # causes errors to be displayed, so remove - # "logging" from the list of options for now. - # The read-write mount performed later will - # specify the logging option if appropriate. - # - - checkopt logging $mntopts - if [ "x$option" = xlogging ]; then - mntopts="$otherops" - fi - fi - - mountfs -O /usr $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL - fi -fi +/sbin/mount -F ufs -o remount,rw,nologging /devices/ramdisk:a / +/usr/sbin/lofiadm -X -a /usr.lgz # -# if we are booted from zfs, the /usr mount probably won't be a -# legacy mount. Use the standard zfs mount command instead. - -readmnttab "/" < /etc/mnttab -if [ "$fstype" = zfs ]; then - mountp=`/sbin/zfs get -H -o value mountpoint $special/usr 2>/dev/null` - # - # if mountp = /usr, there is a non-legacy mount of /usr - # in the boot environment being booted. - # - if [ "x$mountp" = "x/usr" ] ; then - /sbin/zfs mount $special/usr - if [ $? != 0 ] ; then - msg='zfs-mount failed' - echo $msg - echo "$SMF_FMRI:" $msg >/dev/msglog - exit $SMF_EXIT_ERR_FATAL - fi +# Prior to mounting /usr, devfsadm is not yet available. As such, we must +# locate the lofi block device node in /devices rather than in /dev. This +# path has changed over time so we try both the old (pre-partition support) +# and new paths. +# +lofi_devices_path='/devices/pseudo/lofi@1:disk' +if [ ! -b "$lofi_devices_path" ]; then + lofi_devices_path='/devices/pseudo/lofi@0:1' + if [ ! -b "$lofi_devices_path" ]; then + echo 'could not locate lofi block device in /devices' >&2 + exit $SMF_EXIT_ERR_FATAL fi fi -# -# Also mount /boot now so that things like keymap.sh can access -# boot properties through eeprom. Readonly isn't required because -# /boot (and other pcfs filesystems) aren't fsck'ed at boot yet. -# Also, we don't account for caching /boot as it must be on a local -# disk. So what's in vfstab is fine as it stands; just look to see -# if it's there and avoid the mount if not. -# -readvfstab "/boot" < $vfstab - -if [ -n "$mountp" ]; then - mountfs - /boot $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL +if ! /sbin/mount -F ufs -o ro "$lofi_devices_path" /usr; then + echo "could not mount /usr from $lofi_devices_path" >&2 + exit $SMF_EXIT_ERR_FATAL fi # diff --git a/usr/src/cmd/svc/milestone/fs-usr b/usr/src/cmd/svc/milestone/fs-usr index 715cb1bca3..e4b6a263e1 100644 --- a/usr/src/cmd/svc/milestone/fs-usr +++ b/usr/src/cmd/svc/milestone/fs-usr @@ -19,171 +19,23 @@ # # CDDL HEADER END # - # # Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T. # All rights reserved. # Copyright 2016 Nexenta Systems, Inc. +# Copyright 2012, Joyent, Inc. All rights reserved. # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. # - . /lib/svc/share/smf_include.sh -. /lib/svc/share/fs_include.sh - -UPDATEFILE=/etc/svc/volatile/boot_archive_needs_update - -# -# Once root is read/write we can enable the dedicated dumpdevice if it exists -# locally. This is an optimization as svc-dumpadm will attempt do this later. -# -dump_setup() -{ - [ -r /etc/dumpadm.conf ] && . /etc/dumpadm.conf - - readswapdev $DUMPADM_DEVICE < $vfstab - - # - # Make sure that the dump save area has been configured before - # proceeding. If the variable has not been defined or does not exist - # then bail out early. This will prevent us from configuring a - # dump save area before a hostname has been configured (i.e after - # sys-unconfig has been invoked). - # - [ -z "$DUMPADM_SAVDIR" ] && return - - # - # If we have a dedicated dump device, then go ahead and configure it. - # - if [ "x$special" != "x$DUMPADM_DEVICE" ]; then - if [ -x /usr/sbin/dumpadm -a -b $DUMPADM_DEVICE ]; then - /usr/sbin/dumpadm -u || exit $SMF_EXIT_ERR_CONFIG - fi - fi -} - -# -# Write a unique id into this kernel image; this will be included -# in the dump header and panicbuf of any crashdump of this image. -# -if [ -x /usr/sbin/dumpadm ]; then - /usr/sbin/dumpadm -i -fi - -rootiszfs=0 -# get the fstype of root -readmnttab / </etc/mnttab -if [ "$fstype" = zfs ] ; then - rootiszfs=1 - dump_setup -fi - -# -# Add physical swap. -# -/sbin/swapadd -1 - -# -# Check and remount the / (root) file system. -# For NFS mounts, force the llock option on. -# -if smf_is_globalzone && [ $rootiszfs = 0 ]; then - readvfstab / < $vfstab - checkfs $fsckdev $fstype $mountp || exit $SMF_EXIT_ERR_FATAL - checkopt "llock" $mntopts - mntopts='remount' - - [ -n "$otherops" ] && mntopts="${mntopts},${otherops}" - [ "$fstype" = nfs ] && mntopts="${mntopts},llock" - - mountfs -m $mountp $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL -fi - -# -# Check and remount the /usr file system (formerly mounted read-only). -# Unless root is zfs, in which case we've already mounted /usr read-write -# -if [ "$rootiszfs" = 0 ] ; then - readvfstab /usr < $vfstab - if [ "$mountp" ]; then - checkopt ro $mntopts - if [ "x$option" != xro ]; then - checkfs $fsckdev $fstype $mountp || - exit $SMF_EXIT_ERR_FATAL - if [ "x$mntopts" != x- ]; then - mntopts="remount,$mntopts" - else - mntopts="remount" - fi - - mountfs - /usr $fstype $mntopts - || - exit $SMF_EXIT_ERR_FATAL - fi - fi -fi - -# -# Check and mount the /usr/platform file system. This should only be -# present when a SunOS 5.5 (Solaris 2.5) or greater client is being -# administered by a SunOS 5.4 or less host. -# -readvfstab /usr/platform < $vfstab -if [ "$mountp" ]; then - checkfs $fsckdev $fstype $mountp || exit $SMF_EXIT_ERR_FATAL - mountfs - $mountp $fstype $mntopts - || exit $SMF_EXIT_ERR_FATAL -fi - -# -# Mount the fd file systems if mount point exists. -# -readvfstab /dev/fd < $vfstab -if [ "$mountp" -a -d /dev/fd ]; then - mountfs - /dev/fd - - - || exit $SMF_EXIT_ERR_FATAL -fi - -if [ -f "${UPDATEFILE}" ]; then - /usr/sbin/bootadm update-archive - if [ $? != 0 ]; then - cecho "" - cecho "WARNING: Automatic update of the boot archive failed." - cecho "Update the archives using 'bootadm update-archive'" - cecho "command and then reboot the system from the same device" - cecho "that was previously booted." - cecho "" - exit $SMF_EXIT_ERR_FATAL - fi - rm -f $UPDATEFILE - cecho "" - cecho "WARNING: Reboot required." - cecho "The system has updated the cache of files (boot archive) that" - cecho "is used during the early boot sequence. To avoid booting and" - cecho "running the system with the previously out-of-sync version of" - cecho "these files, the system will be restarted." - cecho "" +mount /dev/fd - bootcmd=`/usr/sbin/eeprom bootcmd | /usr/bin/sed -e 's#bootcmd=##g'` - if [ `uname -p` = "i386" ]; then - /usr/sbin/reboot -f dryrun - if [ $? = 0 ]; then - /usr/sbin/reboot -f -- "$bootcmd" - exit $SMF_EXIT_OK - fi - boot_prop=`/usr/sbin/svccfg -s svc:/system/boot-config:default \ - listprop config/auto-reboot-safe | \ - /usr/bin/nawk '{ print $3}'` - if [ "$boot_prop" = "true" ]; then - /usr/sbin/reboot -p - exit $SMF_EXIT_OK - fi - cecho "" - cecho "It has not been possible to restart automatically." - cecho "Reboot the system from the same device that was" - cecho "previously booted." - cecho "" - exit $SMF_EXIT_ERR_FATAL - fi - /usr/sbin/reboot -- "$bootcmd" +if smf_is_globalzone; then + # svc.startd makes a backup of the repo on boot. Since this is a + # live-image, the backup takes up an unnecessary 4MB in memory, so remove + # it now. + rm -f /etc/svc/repository-* fi exit $SMF_EXIT_OK diff --git a/usr/src/cmd/svc/milestone/identity-node b/usr/src/cmd/svc/milestone/identity-node index c9b20ba669..6b740f3dd1 100644 --- a/usr/src/cmd/svc/milestone/identity-node +++ b/usr/src/cmd/svc/milestone/identity-node @@ -27,18 +27,30 @@ # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # -# ident "%Z%%M% %I% %E% SMI" +# Copyright 2019 Joyent, Inc. +# . /lib/svc/share/smf_include.sh . /lib/svc/share/net_include.sh +set -o xtrace + # Make sure that the libraries essential to this stage of booting can be found. LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH # -# If DHCP was used on a primary interface then set the hostname -# that was returned. If no hostname was returned, set the name -# to be "unknown". The hostname must be set to something, because +# For the GZ, use one of the following values for hostname, in order: +# * DHCP hostname (if set on a primary interface) +# * hostname value from config file +# * hostname bootparam +# * if not a headnode: +# * admin MAC address +# * any other MAC address +# +# If none of the above could be found, default to "headnode" for headnodes, and +# "unknown" for non-headnodes. +# +# The hostname must be set to something, because # tooltalk will hang unless the name can be locally resolved. # Sendmail also requires the name to be resolvable locally. # Later, in inetsvc, we create a name "unknown" and create a entry @@ -51,44 +63,124 @@ LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH # kernel if /etc/nodename does not exist, as is expected on an initial boot. # +set_gz_hostname() { + hostname=${CONFIG_hostname} + if [ -z "$hostname" ] || [ "$hostname" == "unknown" ]; then + hostname=$SYSINFO_Bootparam_hostname + fi + + if [ -n "$hostname" ] && [ "$hostname" != "unknown" ]; then + return + fi + + # $headnode is set by load_sdc_config() + if [ "$headnode" == "true" ]; then + hostname="headnode" + return + fi + + if [[ -n ${SYSINFO_NIC_admin} ]]; then + eval "admin_mac=\${SYSINFO_Network_Interface_${SYSINFO_NIC_admin}_MAC_Address}" + if [[ -n ${admin_mac} ]]; then + hostname=$(echo "${admin_mac}" | tr ':' '-') + return + fi + fi + + fallback_mac=$(set | grep "^SYSINFO_Network_Interface_.*_MAC_Address" | head -n1 | cut -d'=' -f2) + if [[ -n ${fallback_mac} ]]; then + hostname=$(echo "${fallback_mac}" | tr ':' '-') + return + fi + + hostname="unknown" +} + + smf_netstrategy case "$_INIT_NET_STRATEGY" in - "dhcp") hostname=`/sbin/dhcpinfo Hostname` ;; - "rarp") hostname=`/sbin/hostconfig -h -p bootparams` - trap 'intr=1' 2 3 - while [ -z "$hostname" -a ! -f /etc/.UNCONFIGURED -a \ - -z "$intr" ]; do - echo "re-trying host configuration..." - # Restrict this to IPv4 interfaces. - /sbin/ifconfig -adD4 auto-revarp up - hostname=`/sbin/hostconfig -h -p bootparams` - done - trap 2 3 ;; - "none") hostname="`shcat /etc/nodename 2>/dev/null`" - if [ -z "$hostname" ]; then - if smf_is_globalzone; then - hostname=`/sbin/hostconfig -h -p bootparams` - else - hostname=`/sbin/uname -n` - fi - fi ;; + "dhcp") hostname=`/sbin/dhcpinfo Hostname` ;; + "rarp") hostname=`/sbin/hostconfig -h -p bootparams` + trap 'intr=1' 2 3 + while [ -z "$hostname" -a ! -f /etc/.UNCONFIGURED -a \ + -z "$intr" ]; do + echo "re-trying host configuration..." + # Restrict this to IPv4 interfaces. + /sbin/ifconfig -adD4 auto-revarp up + hostname=`/sbin/hostconfig -h -p bootparams` + done + trap 2 3 ;; + # /etc/nodename defaults to "unknown" on SmartOS + "none") hostname="`shcat /etc/nodename 2>/dev/null`" + if [ -z "$hostname" ]; then + if smf_is_globalzone; then + hostname=`/sbin/hostconfig -h -p bootparams` + else + hostname=`/sbin/uname -n` + fi + fi ;; esac +# Load sysinfo variables with SYSINFO_ prefix and config variables with +# CONFIG_ prefix. +# Note: since we're still starting up, "soft" values like network IP and such could +# not be set yet. + +if smf_is_globalzone; then + . /lib/sdc/config.sh + + load_sdc_sysinfo + + if boot_file_config_enabled; then + load_boot_file_config + else + load_sdc_config + fi +fi + # # If the netstrategy was unsuccessful and we haven't got a locally configured # name, default to "unknown" # -if [ -z "$hostname" ]; then - hostname="`shcat /etc/nodename 2>/dev/null`" - if [ -z "$hostname" ]; then - hostname="unknown" - fi +if [ -z "$hostname" ] || [ "$hostname" == "unknown" ]; then + hostname="`shcat /etc/nodename 2>/dev/null`" + if [ -z "$hostname" ] || [ "$hostname" == "unknown" ]; then + if smf_is_globalzone; then + set_gz_hostname + else + hostname="unknown" + fi + fi +fi + +if smf_is_globalzone; then + echo "$hostname" > /etc/nodename fi /sbin/uname -S $hostname -echo "Hostname: `/sbin/uname -n`" > /dev/msglog +# Reloading sysinfo here serves two purposes: +# - getting the IP info (which should exist now) +# - updating the host info (which we just set) +eval $(/usr/bin/sysinfo -f -p | sed -e "s/^/SYSINFO_/") + +# Try to add the /etc/hosts entry if we can find an IP +if [[ -n ${SYSINFO_NIC_admin} ]]; then + eval "ipaddr=\${SYSINFO_Network_Interface_${SYSINFO_NIC_admin}_IPv4_Address}" +fi +if [[ -z ${ipaddr} ]]; then + ipaddr=$(set | grep "^SYSINFO_Network_Interface_.*_IPv4_Address" | head -n1 | cut -d'=' -f2) +fi +if [[ -n ${ipaddr} ]]; then + fullname="" + + if [ -n "$CONFIG_dns_domain" ]; then + fullname=" ${hostname}.${CONFIG_dns_domain}" + fi + + printf "${ipaddr}\t${hostname}${fullname}\n" >> /etc/hosts +fi # Reset the library path now that we are past the critical stage unset LD_LIBRARY_PATH diff --git a/usr/src/cmd/svc/milestone/joyent-fs.xml b/usr/src/cmd/svc/milestone/joyent-fs.xml new file mode 100644 index 0000000000..f21eae27d8 --- /dev/null +++ b/usr/src/cmd/svc/milestone/joyent-fs.xml @@ -0,0 +1,93 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2005 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License, Version 1.0 only + (the "License"). You may not use this file except in compliance + with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END +--> + +<service_bundle type='manifest' name='SUNWcsr:filesystem-joyent'> + +<service + name='system/filesystem/smartdc' + type='service' + version='1'> + + <create_default_instance enabled='true' /> + + <single_instance/> + + <dependency + name='usr' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/filesystem/usr' /> + </dependency> + + <dependency + name='kbmd' + grouping='optional_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/kbmd:default' /> + </dependency> + + <!-- + Start method timeout is infinite to handle potentially unbounded + fsck times. + --> + <exec_method + type='method' + name='start' + exec='/lib/svc/method/fs-joyent' + timeout_seconds='0' /> + + <exec_method + type='method' + name='stop' + exec=':true' + timeout_seconds='0' /> + + <property_group name='startd' type='framework'> + <propval name='duration' type='astring' value='transient' /> + </property_group> + <property_group name='joyentfs' type='application'> + <stability value='Evolving'/> + <propval name='debug' type='boolean' value='false'/> + <propval name='usb_copy_path' type='astring' value='/usbkey'/> + <propval name='usb_mountpoint' type='astring' value='usbkey'/> + </property_group> + + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + Joyent file system mounts + </loctext> + </common_name> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/make-console-login-xml b/usr/src/cmd/svc/milestone/make-console-login-xml index a8e516fe9f..2b78164e78 100644 --- a/usr/src/cmd/svc/milestone/make-console-login-xml +++ b/usr/src/cmd/svc/milestone/make-console-login-xml @@ -24,6 +24,8 @@ # Copyright 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # +# Copyright 2019 Joyent, Inc. +# cat >console-login.xml <<EOF <?xml version="1.0"?> @@ -31,6 +33,8 @@ cat >console-login.xml <<EOF Copyright 2008 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. + Copyright 2015 Joyent, Inc. + NOTE: This service manifest is not editable; its contents will be overwritten by package or patch operations, including operating system upgrade. Make customizations in a different @@ -125,14 +129,14 @@ cat >console-login.xml <<EOF <property_group name='ttymon' type='application'> <propval name='value_authorization' type='astring' value='solaris.smf.value.vt' /> - <propval name='device' type='astring' value='/dev/console' /> + <propval name='device' type='astring' value='/dev/wscons' /> <propval name='label' type='astring' value='console' /> <propval name='timeout' type='count' value='0' /> <propval name='nohangup' type='boolean' value='true' /> <propval name='modules' type='astring' value='ldterm,ttcompat' /> <propval name='prompt' type='astring' - value='\`uname -n\` console login:' /> + value='\`uname -n\` wscons login:' /> <propval name='terminal_type' type='astring' value='' /> </property_group> @@ -140,8 +144,39 @@ cat >console-login.xml <<EOF <instance name='default' enabled='true'> </instance> +EOF + +for tty in a b c d; do + cat >>console-login.xml <<EOF +<instance name='tty$tty' enabled='true'> + + <dependency + name='system-console' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/console-login:default' /> + </dependency> + + <!-- these are passed to ttymon in the method script --> + <property_group name='ttymon' type='application'> + <propval name='value_authorization' type='astring' + value='solaris.smf.value.vt' /> + <propval name='device' type='astring' value='/dev/term/$tty' /> + <propval name='label' type='astring' value='115200' /> + <propval name='timeout' type='count' value='0' /> + <propval name='nohangup' type='boolean' value='true' /> + <propval name='modules' type='astring' + value='ldterm,ttcompat' /> + <propval name='prompt' type='astring' + value='\`uname -n\` tty$tty login:' /> + <propval name='terminal_type' type='astring' + value='xterm' /> + </property_group> +</instance> EOF +done # Note that this script file is normally parsed during build by sh(1). # When the parser encounters an EOF token (like the one above), it @@ -328,7 +363,7 @@ Sets the initial value of the TERM environment variable <visibility value='readwrite'/> <cardinality min='1' max='1'/> </prop_pattern> - </pg_pattern> + </pg_pattern> </template> </service> diff --git a/usr/src/cmd/svc/milestone/manifest-import b/usr/src/cmd/svc/milestone/manifest-import index 81cd780583..43fb677b92 100644 --- a/usr/src/cmd/svc/milestone/manifest-import +++ b/usr/src/cmd/svc/milestone/manifest-import @@ -74,13 +74,17 @@ function svccfg_apply { } # -# If the smf repository has file entries that are missing +# If the smf/manifest table has file entries that are missing # then there is work to be done by the cleanup process. # function cleanup_needwork { - smfmfiles=`svcprop -p manifestfiles '*' 2>/dev/null | - nawk -v early="$early" '$2 == "astring" && - (early != "true" || $3 ~ "^/lib/") { print $3 }'` + if [ "$early" == true ]; then + smfmfiles=`/usr/bin/svcprop smf/manifest | \ + awk '(/^lib_/ && /\/manifestfile /) {print $3}'` + else + smfmfiles=`/usr/bin/svcprop smf/manifest | \ + awk '/\/manifestfile / {print $3}'` + fi nw=`/lib/svc/bin/mfstscan $smfmfiles 2>&1 1>/dev/null` [ "$nw" ] && return 1 @@ -201,8 +205,13 @@ function import_manifests { rm -f $logf - nonsite_dirs=`/usr/bin/find $basedir/* -name site \ - -prune -o -type d -print -prune` + if [ "${basedir}" == "/opt/custom/smf" ]; then + # Special case where we will just import from the root, not subdirs + nonsite_dirs=${basedir} + else + nonsite_dirs=`/usr/bin/find $basedir/* -name site \ + -prune -o -type d -print -prune` + fi if [ -n "$_MFST_DEBUG" ]; then nonsite_manifests=`/lib/svc/bin/mfstscan $nonsite_dirs` @@ -454,6 +463,10 @@ else import_manifests "/lib/svc/manifest" true import_manifests "/var/svc/manifest" true + if [ -d "/opt/custom/smf" ]; then + import_manifests "/opt/custom/smf" true + fi + # # Apply profiles # @@ -472,9 +485,9 @@ fi # 6. Final actions. # -if $activity; then - /usr/sbin/svcadm _smf_backup "manifest_import" || true -fi +#if $activity; then +# /usr/sbin/svcadm _smf_backup "manifest_import" || true +#fi # # If the filesystem is NOT read only then move the repo back to perm diff --git a/usr/src/cmd/svc/milestone/mdata-execute b/usr/src/cmd/svc/milestone/mdata-execute new file mode 100755 index 0000000000..fca08ffbc7 --- /dev/null +++ b/usr/src/cmd/svc/milestone/mdata-execute @@ -0,0 +1,53 @@ +#!/usr/bin/bash +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at http://smartos.org/CDDL +# +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file. +# +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2012, Joyent, Inc. All rights reserved. +# + +set -o xtrace + +. /lib/svc/share/smf_include.sh +smf_is_globalzone && exit ${SMF_EXIT_OK} + +# If we got as far as running the user-script the 'provision' was a success +# from here out a failure will leave the zone running. +if [ -f /var/svc/provisioning ]; then + mv /var/svc/provision{ing,_success} +fi + +if [[ -x /var/svc/mdata-operator-script ]]; then + /var/svc/mdata-operator-script + operator_script_exit=$? + if [[ ${operator_script_exit} -gt 0 ]]; then + echo "WARNING: operator-script failed: exited ${operator_script_exit}" \ + >&2 + fi +fi + +user_script_exit=${SMF_EXIT_OK} +if [ -x /var/svc/mdata-user-script ]; then + /var/svc/mdata-user-script + [ $? -gt 0 ] && user_script_exit=${SMF_EXIT_ERR_FATAL} +fi + +exit ${user_script_exit} diff --git a/usr/src/cmd/svc/milestone/mdata-fetch b/usr/src/cmd/svc/milestone/mdata-fetch new file mode 100755 index 0000000000..cb7222f445 --- /dev/null +++ b/usr/src/cmd/svc/milestone/mdata-fetch @@ -0,0 +1,477 @@ +#!/usr/bin/bash +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at http://smartos.org/CDDL +# +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file. +# +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2017, Joyent, Inc. All rights reserved. +# + +export PS4='[\D{%FT%TZ}] ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' +export PATH=/usr/bin:/usr/sbin:$PATH + +set -o xtrace + +. /lib/svc/share/smf_include.sh +smf_is_globalzone && exit ${SMF_EXIT_OK} + +if [ ! -x /usr/sbin/mdata-get ]; then + echo "Metadata mdata-get tool not found." + exit ${SMF_EXIT_ERR_FATAL} +fi + +function fatal() { + if [[ -n $1 ]]; then + echo "FATAL: $*" >&2 + fi + exit ${SMF_EXIT_ERR_FATAL} +} + +# Test if an address looks like an IPv4 address. +function isIPv4() { + [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]] +} + +# Test if an address looks like an IPv4 address + CIDR. +function isIPv4AndCIDR() { + [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$ ]] +} + +# Test if an address looks like an IPv6 address. +function isIPv6() { + [[ "$1" =~ ^[0-9a-f]*:[0-9a-f]*:[:0-9a-f]*$ ]] +} + +# Test if an address looks like an IPv6 address + CIDR. +function isIPv6AndCIDR() { + [[ "$1" =~ ^[0-9a-f]*:[0-9a-f]*:[:0-9a-f]*/[0-9]{1,3}$ ]] +} + +# For old zones that were created prior to OS-2253 and bumping the mdata:fetch +# start timeout, we need to fix this otherwise we could timeout waiting for the +# socket. +cur_timeout=$(svcprop -p start/timeout_seconds svc:/smartdc/mdata:fetch) +if [[ -z ${cur_timeout} || ${cur_timeout} -lt 1800 ]]; then + # The current manifest has an old timeout value, fix in case we timeout + # here. XXX we can still hit OS-2296 here where smf will forget that we + # set this. + svccfg -s svc:/smartdc/mdata:fetch 'setprop start/timeout_seconds = 1800' + svcadm refresh svc:/smartdc/mdata:fetch +fi + +# This waits until /.zonecontrol/metadata.sock exists then exits 0 +/usr/vm/sbin/filewait /.zonecontrol/metadata.sock + +if [[ ! -e /.zonecontrol/metadata.sock ]]; then + # this is a bug since filewait should not have returned until file existed. + fatal "missing /.zonecontrol/metadata.sock, Unable to start mdata:fetch" +fi + +# Update sysinfo to ensure values that come from metadata are populated. +/usr/bin/sysinfo -fu + +echo "Retrieving metadata user-data" +/usr/sbin/mdata-get user-data >/var/db/mdata-user-data.new +case $? in + 0) + echo "Metadata user-data successfuly retrieved." + mv /var/db/mdata-user-data{.new,} + ;; + 1) + echo "Metadata user-data not defined." + rm -f /var/db/mdata-user-data{,.new} + ;; + *) + echo "Metadata couldn't be retrieved." + exit ${SMF_EXIT_ERR_FATAL} + ;; +esac + +echo "Retrieving metadata user-script..." +/usr/sbin/mdata-get user-script >/var/svc/mdata-user-script.new +case $? in + 0) + echo "Metadata user-script successfuly retrieved." + mv /var/svc/mdata-user-script{.new,} + chmod +x /var/svc/mdata-user-script + ;; + 1) + echo "Metadata user-script not defined." + rm -f /var/svc/mdata-user-script{,.new} + ;; + *) + echo "Metadata couldn't be retrieved." + exit ${SMF_EXIT_ERR_FATAL} + ;; +esac + +echo "Retrieving metadata operator-script..." +/usr/sbin/mdata-get sdc:operator-script >/var/svc/mdata-operator-script.new +case $? in + 0) + echo "Metadata operator-script successfuly retrieved." + mv /var/svc/mdata-operator-script{.new,} + chmod +x /var/svc/mdata-operator-script + ;; + 1) + echo "Metadata operator-script not defined." + rm -f /var/svc/mdata-operator-script{,.new} + ;; + *) + echo "Metadata couldn't be retrieved." + exit ${SMF_EXIT_ERR_FATAL} + ;; +esac + +echo "Retrieving tmpfs value..." +tmpfs=$(/usr/sbin/mdata-get sdc:tmpfs) +if [[ $? == 0 && -n ${tmpfs} && -f /etc/vfstab ]]; then + check="swap - /tmp tmpfs"; + + if [[ ${tmpfs} == "0" ]]; then + # When tmpfs is set 0, we remove any entry from /etc/vfstab but cannot + # adjust the "live" value as current /tmp will be in-use. On reboot the + # new value will take effect. + + grep -v "^${check}" /etc/vfstab > /etc/vfstab.new \ + && mv /etc/vfstab.new /etc/vfstab + else + new="swap - /tmp tmpfs - yes size=${tmpfs}m"; + if ! /usr/bin/grep "^${new}" /etc/vfstab; then + if ! /usr/bin/grep "^${check}" /etc/vfstab; then + # no tmpfs line. add it. + echo "${new}" >> /etc/vfstab + else + # existing tmpfs line, but wrong value. fix it. + /usr/bin/sed -i "" -e "s|^swap.*/tmp.*tmpfs.*$|${new}|" /etc/vfstab + echo $? + fi + + if mount | grep "^/tmp"; then + # Also fix current size, since /etc/vfstab didn't have our correct line + # but only if we have /tmp mounted at all. If not, we'll have to wait + # until the next reboot since /tmp will be in-use. + /usr/sbin/mount -F tmpfs -o remount,size=${tmpfs}m /tmp + fi + + fi + fi +fi + +# +# If we have NFS volumes, we'll add them to vfstab, and enable the nfs/client +# service so that will mount the volumes for us. +# +echo "Retrieving volume metadata..." + +volumes_added=0 +while IFS="|" read -r nfsvolume mountpoint name mode type; do + + cat >&2 <<EOF +*** VOLUME *** +NFSVOLUME: ${nfsvolume} +MOUNTPOINT: ${mountpoint} +MODE: ${mode} +NAME: ${name} +TYPE: ${type} +EOF + + if [[ -n ${type} && ${type} != "tritonnfs" ]]; then + fatal "unsupported volume type: ${type}" + fi + + # if we don't have volume and mountpoint, we can't add vfstab lines + if [[ -z ${nfsvolume} || -z ${mountpoint} ]]; then + fatal "invalid volume specification (need both volume & mountpoint)" + fi + + if ! grep "^${nfsvolume}[ ]" /etc/vfstab; then + if [[ -z ${mode} ]]; then + mode="rw" + fi + + if [[ ${mode} != "rw" && ${mode} != "ro" ]]; then + fatal "invalid volume mode: '${mode}'" + fi + + line=$(printf "%s - %s nfs - yes %s\n" "${nfsvolume}" "${mountpoint}" "${mode}") + volumes_added=$((${volumes_added}+1)) + + mkdir -p ${mountpoint} + echo "${line}" >> /etc/vfstab + fi +done < <(/usr/sbin/mdata-get sdc:volumes \ + | /usr/bin/json \ + -d '|' \ + -a nfsvolume mountpoint name mode type) + +if [[ ${volumes_added} -gt 0 ]]; then + for svc in \ + svc:/network/nfs/nlockmgr:default \ + svc:/network/nfs/status:default \ + svc:/network/rpc/bind:default \ + svc:/network/nfs/client:default \ + ; do + + svcadm enable ${svc} || fatal "Unable to enable ${svc}" + done +fi + +# We use the special sdc:nics value here though this is not an interface for +# use elsewhere. If this is changed please also update agent.js in the metadata +# agent. +# +# We run this every startup in case nics have changed since last boot. As +# network/physical has an optional_all dependency on this service, we'll have +# had our chance to write these files out before networking comes up. This +# might eventually be replaced by network/physical grabbing data directly. +echo "Retrieving nics data..." +while IFS="|" read -r iface ip ips netmask primary gateway gateways; do + + # if we don't have interface name, we don't know what files to write out. + if [[ -z ${iface} ]]; then + continue; + fi + + # A VM created on an older platform may not have the "ips" or "gateways" + # properties, so we will need to grab the older "ip" and "gateway" versions + # instead. + [[ -z $ips ]] && ips=$ip + [[ -z $gateways ]] && gateways=$gateway + + # We remove the hostname.netX file first, so we can append to a clean file + rm -f /etc/hostname.${iface} + + # remove any existing DHCP or addrconf configuration, since we'll create one + # if it belongs later + rm -f /etc/dhcp.${iface} + rm -f /etc/addrconf.${iface} + + OLDIFS=$IFS + IFS=$',' + for ip in $ips; do + # so it shows up in the logs + echo "iface[${iface}] ip[${ip}] netmask[${netmask}]" \ + "primary[${primary}] gateway[${gateway}]" + + [[ -z ${ip} ]] && continue; + + if [[ ${ip} == "dhcp" ]]; then + touch /etc/dhcp.${iface} + if hostname=`mdata-get sdc:hostname`; then + echo "inet ${hostname}" >> /etc/hostname.${iface} + fi + elif [[ ${ip} == "addrconf" ]]; then + touch /etc/addrconf.${iface} + elif isIPv4 ${ip} && [[ -n ${netmask} ]]; then + # We're using an older configuration where the routing prefix is + # specified using a mask instead of CIDR notation. We'll need to + # invoke ifconfig differently. + echo "${ip} netmask ${netmask} up" >> /etc/hostname.${iface} + elif isIPv4AndCIDR ${ip} \ + || isIPv6AndCIDR ${ip} \ + || isIPv6 ${ip}; then + # Either a routing prefix was specified, or, in the case of IPv6, we + # won't specify one and fall back on NDP to find it when we configure + # our interfaces. + echo "${ip} up" >> /etc/hostname.${iface} + fi + done + + if [[ ${primary} == "true" ]]; then + rm -f /etc/defaultrouter + for gateway in $gateways; do + if [[ -n ${gateway} ]] && isIPv4 ${gateway}; then + echo "${gateway}" >> /etc/defaultrouter + fi + done + fi + IFS=$OLDIFS + + # XXX we leave old hostname.netX files around and just replace when we have + # one next. +done < <(/usr/sbin/mdata-get sdc:nics \ + | /usr/bin/json \ + -d '|' \ + -e 'this.ips = this.ips ? this.ips.join(",") : ""' \ + -e 'this.gateways = this.gateways ? this.gateways.join(",") : ""' \ + -a interface ip ips netmask primary gateway gateways) + +# rebuild resolv.conf +resolvers=$(mdata-get sdc:resolvers) +resolvers_result=$? + +# Determine if resolv.conf is managed for us +maintain_resolvers=$(mdata-get sdc:maintain_resolvers) +maintain_result=$? +if [[ ${maintain_result} != 0 ]]; then + echo "Error getting maintain_resolvers, code: ${maintain_result}" + maintain_resolvers="false" +fi + +# If this is our first boot, write an initial set of resolvers +if [[ -f /var/svc/provisioning ]]; then + echo "First boot: writing resolvers" + maintain_resolvers="true" +fi + +if [[ ${resolvers_result} == 0 && ${maintain_resolvers} == "true" ]]; then + + # if dns_domain is missing or broken, we still write out, just w/o search + search="search $(mdata-get sdc:dns_domain)" + if [[ $? != 0 ]]; then + search= + fi + + if [[ ${resolvers} == "[]" ]]; then + nameservers= + else + nameservers=$(echo ${resolvers} | json -a | sed -e "s/^/nameserver /") + fi + + rm -f /etc/.resolv.conf.tmp + if [[ -n ${search} ]]; then + echo "${search}" > /etc/.resolv.conf.tmp + fi + if [[ -n ${nameservers} ]]; then + echo "${nameservers}" >> /etc/.resolv.conf.tmp + fi + + cp /etc/.resolv.conf.tmp /etc/resolv.conf \ + && cat /etc/.resolv.conf.tmp >&2 \ + && rm -f /etc/.resolv.conf.tmp +else + if [[ ${resolvers_result} == 0 ]]; then + echo "Error getting resolvers, code: ${resolvers_result}" + fi + + if [[ ${maintain_resolvers} == "true" ]]; then + echo "Not setting resolvers, maintain_resolvers=${maintain_resolvers}" + fi +fi + + +# Fetch routes + +# It is possible to specify the same route in several different ways using +# route(1m). We therefore use route(1m) itself to manage adding, deleting +# and determining duplicates. +zone_routes_file=/etc/inet/static_routes +vmadm_routes_file=/etc/inet/static_routes.vmadm +tmpdir=$(mktemp -d /tmp/mdata.XXXXXX) + +if [ -z $tmpdir ]; then + echo "Error creating temporary directory." + exit ${SMF_EXIT_ERR_FATAL} +fi + +# directory structure for the new copy of static_routes.vmadm (the one that +# will replace the current static_routes.vmadm once all of the adds and +# deletes have been applied): +new_root=${tmpdir}/new-routes +new_inet=${new_root}/etc/inet +# directory structure for the previous copy of static_routes.vmadm (used to +# determine routes that have been removed since the last time mdata-fetch +# was run): +old_root=${tmpdir}/old-routes +old_inet=${old_root}/etc/inet +# directory structure for the zone's persistent routes - those not created +# by vmadm (used to determine if vmadm routes are duplicates): +zone_root=${tmpdir}/zone-routes +zone_inet=${zone_root}/etc/inet + +mkdir -p $new_inet +mkdir -p ${old_root}/etc/inet +mkdir -p ${zone_root}/etc/inet + +if [[ -f ${vmadm_routes_file} ]]; then + cp $vmadm_routes_file ${old_inet}/static_routes +fi + +function route_in_per_zone_file() +{ + cp $zone_routes_file $zone_inet + output=$(route -pR $zone_root add $* 2>&1) + [[ $output =~ "entry exists" ]] +} + +# If re-running this script after initial boot, network/physical and +# network/routing-setup are already enabled, so apply routing adds and +# deletes manually +if [[ $(/usr/bin/svcs -H -o state network/routing-setup) == "online" ]]; then + routing_up="true" +fi + +while IFS="|" read -r gateway dst linklocal; do + echo "route: gateway[${gateway}] dst[${dst}] linklocal[${linklocal}]" + route_type="" + if [[ ${linklocal} == "true" ]]; then + route_type="-interface " + fi + route_str="${route_type}${dst} ${gateway}" + + if route_in_per_zone_file $route_str; then + echo "not adding duplicate route: ${route_str}" + # the zone has also defined this route; do nothing + else + echo "adding route to file: ${route_str}" + route -pR $new_root add $route_str + if [[ -n "${routing_up}" ]]; then + route add $route_str + fi + fi + + route -pR $old_root delete $route_str +done < <(/usr/sbin/mdata-get sdc:routes \ + | /usr/bin/json -d '|' -a gateway dst linklocal) + + +# Anything left in the old static_routes file is a delete. Don't delete the +# route from the routing tables if there's a duplicate route in the zone's +# static_routes file +if [[ -f ${old_inet}/static_routes ]]; then + egrep -v "^(#|$)" ${old_inet}/static_routes | while read -r route_str; do + if [[ "${route_str}" == "" ]]; then + continue + fi + + if route_in_per_zone_file "$route_str"; then + echo "not deleting duplicate route: ${route_str}" + else + if [[ -n "${routing_up}" ]]; then + route delete $route_str + fi + fi + done +fi + +if [[ -f ${new_inet}/static_routes ]]; then + cp ${new_inet}/static_routes ${vmadm_routes_file} +else + rm ${vmadm_routes_file} +fi +rm -rf $tmpdir + + +# Unconditionally enable mdata:execute, so that the last provisioning step +# is always taken (regardless of whether user-script exists or not) +svcadm enable smartdc/mdata:execute + +exit ${SMF_EXIT_OK} diff --git a/usr/src/cmd/svc/milestone/mdata.xml b/usr/src/cmd/svc/milestone/mdata.xml new file mode 100644 index 0000000000..152a7cb485 --- /dev/null +++ b/usr/src/cmd/svc/milestone/mdata.xml @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<service_bundle type="manifest" name="mdata"> + <service name="smartdc/mdata" type="service" version="1"> + <dependency name="filesystem" grouping="require_all" restart_on="error" type="service"> + <service_fmri value="svc:/system/filesystem/minimal" /> + </dependency> + <property_group name="startd" type="framework"> + <propval name="duration" type="astring" value="transient" /> + <propval name="ignore_error" type="astring" value="core,signal" /> + </property_group> + <instance name="fetch" enabled="true"> + <dependency name="boot-file" grouping="exclude_all" restart_on="refresh" type="path"> + <service_fmri value="file://localhost/tmp/.FIRST_REBOOT_NOT_YET_COMPLETE"/> + </dependency> + <dependency name="rmtmpfiles" grouping="optional_all" restart_on="error" type="service"> + <service_fmri value="svc:/system/rmtmpfiles" /> + </dependency> + <exec_method type="method" name="start" exec="/lib/svc/method/mdata-fetch" timeout_seconds="1800" /> + <exec_method type="method" name="stop" exec=":true" timeout_seconds="60" /> + </instance> + <instance name="execute" enabled="false"> + <dependency name="network" grouping="require_all" restart_on="error" type="service"> + <service_fmri value="svc:/milestone/multi-user:default" /> + </dependency> + <dependency name="mdata" grouping="require_all" restart_on="error" type="service"> + <service_fmri value="svc:/smartdc/mdata:fetch" /> + </dependency> + <exec_method type="method" name="start" exec="/lib/svc/method/mdata-execute" timeout_seconds="300" /> + <exec_method type="method" name="stop" exec=":true" timeout_seconds="60" /> + </instance> + <stability value="Evolving" /> + <template> + <common_name> + <loctext xml:lang="C">Joyent SDC metadata handler</loctext> + </common_name> + </template> + </service> +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/minimal-fs.xml b/usr/src/cmd/svc/milestone/minimal-fs.xml index b7af22bfcd..844760baa1 100644 --- a/usr/src/cmd/svc/milestone/minimal-fs.xml +++ b/usr/src/cmd/svc/milestone/minimal-fs.xml @@ -44,11 +44,11 @@ <single_instance/> <dependency - name='usr' + name='joyent' grouping='require_all' restart_on='none' type='service'> - <service_fmri value='svc:/system/filesystem/usr' /> + <service_fmri value='svc:/system/filesystem/smartdc' /> </dependency> <dependency diff --git a/usr/src/cmd/svc/milestone/net-early-admin b/usr/src/cmd/svc/milestone/net-early-admin new file mode 100644 index 0000000000..cda730f81e --- /dev/null +++ b/usr/src/cmd/svc/milestone/net-early-admin @@ -0,0 +1,237 @@ +#!/bin/ksh93 +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2019 Joyent, Inc. +# + +# Traditionally, when a Triton compute node boots, the network is not +# configured until after the local filesystems are mounted (in fact +# network/physical:default depends upon /system/filesystem/smartdc). Most +# obviously, in the case of a head node or a standalone SmartOS install, the +# network configuration is stored on the local zpool, so the network cannot +# be configured until this happens. +# +# For Triton compute nodes with encrypted zpools, we must enable the admin +# network before the local zpool filesytems are online -- we have to be able +# to communicate to the head node services to obtain the pin to unlock the +# local zpool. For PXE booted Triton compute nodes, we therefore configure +# the admin network sooner via the network/early-admin:default service. When +# network/physical:default runs, it will skip the configuration of the admin +# network and configuring the remaining interfaces. +# +# If we are not a Triton compute node, we exit successfully almost immediately +# without configuring the admin network. When the network/physical:default +# service runs, it will configure all the network interfaces as it traditionally +# has + +PATH=/bin:/usr/bin:/sbin:/usr/sbin + +. /lib/svc/share/smf_include.sh +. /lib/sdc/config.sh +. /lib/sdc/network.sh + +PS4='+ [$LINENO] ' + +set -o xtrace + +if ! smf_is_globalzone; then + echo "Non-global zone; no action required; exiting" + exit $SMT_EXIT_OK +fi + +if ! boot_file_config_enabled; then + echo "Boot-time networking files not present; exiting" + exit $SMF_EXIT_OK +fi + +function fatal +{ + # XXX: For SMF methods, does it matter/better to redirect to stderr? + echo "Error: $*" >&2 + exit $SMF_EXIT_ERR_FATAL +} + +if ! boot_file_config_valid; then + echo "ERROR: boot-time network config file incorrect" >&2 + exit $SMF_EXIT_ERR_CONFIG +fi + +unset aggrs +unset tags +typeset -A tagv +typeset -A aggr_links aggr_mode +typeset -A ip ip6 netmask gateway gateway6 mtu mac +/usr/lib/sdc/net-boot-config | while IFS="=" read var value; do + if [[ "$var" =~ _nic$ ]]; then + name=${var%_nic} + tags+=("${name}") + tagv[$name]="$value" + elif [[ "$var" =~ _aggr$ ]]; then + name=${var%_aggr} + aggrs+=(${name}) + aggr_links[$name]="${value//,/ }" + elif [[ "$var" =~ _lacp_mode$ ]]; then + name=${var%_lacp_mode} + aggr_mode[$name]="$value" + elif [[ "$var" =~ _mtu$ ]]; then + (( value < 1500 || value > 9000 )) && + fatal "ERROR: $var MTU value \'$value\' is not in" \ + "range [1500, 9000]" + + name=${var%_mtu} + mtu[$name]="$value" + elif [[ "$var" =~ _ip$ ]]; then + name=${var%_ip} + ip[$name]="$value" + elif [[ "$var" =~ _ip6$ ]]; then + name=${var%_ip6} + ip6[$name]="$value" + elif [[ "$var" =~ _netmask$ ]]; then + name=${var%_netmask} + netmask[$name]="$value" + elif [[ "$var" =~ _gateway$ ]]; then + name=${var%_gateway} + gateway[$name]="$value" + elif [[ "$var" =~ _gateway6$ ]]; then + name=${var%_gateway6} + gateway6[$name]="$value" + elif [[ "$var" =~ _mac$ ]]; then + name=${var%_mac} + mac[$name]="$value" + elif [[ "$var" == "dns_resolvers" ]]; then + dns_resolvers=(${value//,/ }) + fi + + eval "CONFIG_$var"="$value" +done + +# This must happen befor any other dladm commands (which includes log_if_state) +# or else dladm commands can fail +dladm init-phys + +log_if_state before + +typeset -A mac_to_link +out=$(dladm show-phys -mpo link,address) +(( $? == 0 )) || fatal "dladm show-phys failed" +while IFS=: read link addr; do + macaddr=$(normalize_mac $addr) + mac_to_link["$macaddr"]="$link" +done <<< "$out" + +ADMIN_NIC_TAG=${CONFIG_admin_tag:-"admin"} +[[ -n "${tagv[$ADMIN_NIC_TAG]}" ]] || \ + fatal "ERROR: admin nic tag '$ADMIN_NIC_TAG' not present" + +nic="${tagv[$ADMIN_NIC_TAG]}" + +if [[ -n "${aggr_links[$nic]}" ]]; then + mode=${aggr_mode[$nic]:-"off"} + + for l in ${aggr_links[$nic]}; do + [[ -n "${mac_to_link[$l]}" ]] || fatal "MAC '$l' not present" + links+="${mac_to_link[$l]} " + done + links="${links% }" + + echo "Creating aggr: $nic (mode=$mode, links=${links})" + dladm create-aggr -l ${links// / -l } -L $mode $nic +elif valid_mac "$nic"; then + [[ -n "${mac_to_link[$nic]}" ]] || \ + fatal "ERROR: admin mac address $nic not found on system" + nic=${mac_to_link[$nic]} +else + fatal "ERROR: Invalid value of ${ADMIN_NIC_TAG}_nic ($nic)" +fi + +# If there are other nic tags configured on the same link as +# the admin tag, find the largest MTU to use to set the +# datalink MTU +dlmtu="${mtu[$ADMIN_NIC_TAG]}" +for tag in ${tags[@]}; do + tagmac="${tagv[$tag]}" + + # If the 'mac' for the nic tag is actually an aggr, the + # tag will appear in $aggr_links (and we want to use that as + # the link name). If it is not an aggr, we need to map the + # MAC address to the link name so we can check if $tag + # resides on the same link as the admin interface + if [[ -n "${aggr_links[$tagmac]}" ]]; then + link="$tagmac" + else + link="${mac_to_link[$tagmac]}" + fi + + if [[ "$link" == "$nic" && $dlmtu -lt ${mtu[$tag]} ]]; then + dlmtu=${mtu[$tag]} + fi +done + +if [[ -n "$dlmtu" ]]; then + dladm set-linkprop -p mtu=$dlmtu $nic || \ + fatal "ERROR: Failed to set mtu on aggr $nic to $dlmtu" +fi + +driver=${nic%%[0-9]*} +get_link_state $nic +if [[ "$link_state" == "down" ]]; then + echo "admin nic '${nic}' is down: unplumbing" + /sbin/ifconfig $nic down unplumb + wait_for_nic_state $nic "unknown" +fi + +# There's some sort of race condition in the bnx driver: if the plumb +# command comes too soon after the unplumb, the interface can come up +# in a state where it never fires interrupts for link state changes. +if [[ "$driver" == "bnx" ]]; then + sleep 5 +fi + +/sbin/ifconfig $nic plumb mtu ${mtu[$ADMIN_NIC_TAG]} +wait_for_nic_state $nic "up" + +if [[ -n "${ip[$ADMIN_NIC_TAG]}" ]]; then + /sbin/ifconfig $nic inet ${ip[$ADMIN_NIC_TAG]} \ + netmask ${netmask[$ADMIN_NIC_TAG]:-"+"} up + [[ -n "${gateway[$ADMIN_NIC_TAG]}" ]] && \ + /usr/sbin/route add default ${gateway[$ADMIN_NIC_TAG]} +fi + +if [[ -n "${ip6[$ADMIN_NIC_TAG]}" ]]; then + /sbin/ifconfig $nic inet6 plumb mtu ${mtu[$ADMIN_NIC_TAG]} + [[ "${ip6[$ADMIN_NIC_TAG]}" != "addrconf" ]] && \ + /sbin/ifconfig $nic inet6 addif ${ip6[$ADMIN_NIC_TAG]} preferred up + [[ -n "${gateway6[$ADMIN_NIC_TAG]}" ]] && \ + /usr/sbin/route add -inet6 default ${gateway6[$ADMIN_NIC_TAG]} +fi + +# Add just the routes reachable through the admin network -- usually these are +# only present with rack aware networking (RAN) +/usr/lib/sdc/net-boot-config --routes | while read dst gw; do + if ! ip_in_net $gw ${ip[$ADMIN_NIC_TAG]} ${netmask[$ADMIN_NIC_TAG]}; then + continue + fi + route add "$dst" "$gw" +done + +if [[ -n "${CONFIG_dns_domain}" && -n ${dns_resolvers[0]} ]]; then + echo "search ${CONFIG_dns_domain}" > /etc/resolv.conf + for serv in ${dns_resolvers[@]}; do + echo "nameserver $serv" >> /etc/resolv.conf + done +fi + +touch /etc/svc/volatile/.early_admin_setup + +log_if_state after diff --git a/usr/src/cmd/svc/milestone/net-physical b/usr/src/cmd/svc/milestone/net-physical index 89784dcbbc..90f6f7aff5 100644 --- a/usr/src/cmd/svc/milestone/net-physical +++ b/usr/src/cmd/svc/milestone/net-physical @@ -1,4 +1,4 @@ -#!/sbin/sh +#!/usr/bin/ksh93 # # CDDL HEADER START # @@ -19,527 +19,839 @@ # # CDDL HEADER END # -# # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2012 Milan Jurik. All rights reserved. +# Copyright 2019 Joyent, Inc. # # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T. # All rights reserved. # . /lib/svc/share/smf_include.sh -. /lib/svc/share/net_include.sh +. /lib/sdc/config.sh +. /lib/sdc/network.sh + +set -o errexit +set -o xtrace # # In a shared-IP zone we need this service to be up, but all of the work -# it tries to do is irrelevant (and will actually lead to the service -# failing if we try to do it), so just bail out. +# it tries to do is irrelevant (and will actually lead to the service +# failing if we try to do it), so just bail out. # In the global zone and exclusive-IP zones we proceed. # -smf_configure_ip || exit $SMF_EXIT_OK - +smf_configure_ip || exit ${SMF_EXIT_OK} # Make sure that the libraries essential to this stage of booting can be found. LD_LIBRARY_PATH=/lib; export LD_LIBRARY_PATH +# Time (in seconds) to wait for admin NIC to get DHCP address before continuing. +ADMIN_DHCP_TIMEOUT=300 +ActiveAggrLinks= +typeset -A ActiveAggrLinks + smf_netstrategy -if smf_is_globalzone; then - net_reconfigure || exit $SMF_EXIT_ERR_CONFIG - - # Update PVID on interfaces configured with VLAN 1 - update_pvid - - # - # Upgrade handling. The upgrade file consists of a series of dladm(1M) - # commands. Note that after we are done, we cannot rename the upgrade - # script file as the file system is still read-only at this point. - # Defer this to the manifest-import service. - # - upgrade_script=/var/svc/profile/upgrade_datalink - if [ -f "${upgrade_script}" ]; then - . "${upgrade_script}" - fi - - # - # Upgrade handling for ibd: - # After we are done with the upgrade handling, we can not set the - # ibd/ibd_upgraded property to "true" as the file system is - # read-only at this point. It will be done later by ibd-post-upgrade - # service. - # - if [ -x /sbin/ibd_upgrade ]; then - ibd_upgraded=`/bin/svcprop -c -p ibd/ibd_upgraded \ - $SMF_FMRI 2> /dev/null` - if [ "$ibd_upgraded" != "true" ]; then - /sbin/ibd_upgrade -v - fi - fi - - # - # Bring up simnets, link aggregations and initialize security objects. - # Note that link property initialization is deferred until after - # IP interfaces are plumbed to ensure that the links will not - # be unloaded (and the property settings lost). We should bring - # up simnets prior to VLANs/Aggrs to enable creation of VLANs/Aggrs - # over simnets. - # - /sbin/dladm up-simnet - /sbin/dladm up-aggr - /sbin/dladm up-vlan - /sbin/dladm up-part - /sbin/dladm init-secobj - # - # Bring up VNICs - # - /sbin/dladm up-vnic - # - # Create flows via flowadm. - # - /sbin/flowadm init-flow -fi +function add_active_aggr_links +{ + set -o xtrace + typeset alink -# -# If the system was net booted by DHCP, hand DHCP management off to the -# DHCP agent (ifconfig communicates to the DHCP agent through the -# loopback interface). -# -if [ -n "$_INIT_NET_IF" -a "$_INIT_NET_STRATEGY" = "dhcp" ]; then - /sbin/dhcpagent -a -fi + for alink in ${2//,/ }; do + ActiveAggrLinks[$alink]=$1 + done +} -# -# The network initialization is done early to support diskless and -# dataless configurations. For IPv4 interfaces that were configured by -# the kernel (e.g. those on diskless machines) and not configured by -# DHCP, reset the netmask using the local "/etc/netmasks" file if one -# exists, and then reset the broadcast address based on the netmask. -# -/sbin/ifconfig -auD4 netmask + broadcast + +# Waits for up to 10 seconds for the link state to change to the given value +function wait_for_admin_nic_state +{ + wait_for_nic_state "${SYSINFO_NIC_admin}" "$1" +} -is_iptun () +# Plumbs the admin interface, and attempts to work around poorly-behaved +# drivers that can't handle plumb commands too quickly after one another +function plumb_admin { - intf=$1 - # Is this a persistent IP tunnel link? - /sbin/dladm show-iptun -P $intf > /dev/null 2>&1 - if [ $? -eq 0 ]; then - return 0 - fi - # Is this an implicit IP tunnel (i.e., ip.tun0) - ORIGIFS="$IFS" - IFS="$IFS." - set -- $intf - IFS="$ORIGIFS" - if [ $# -eq 2 -a \( "$1" = "ip" -o "$1" = "ip6" \) ]; then - # - # It looks like one, but another type of link might be - # using a name that looks like an implicit IP tunnel. - # If dladm show-link -P finds it, then it's not an IP - # tunnel. - # - /sbin/dladm show-link -Pp $intf > /dev/null 2>&1 - if [ $? -eq 0 ]; then - return 1 - else - return 0 - fi - fi - return 1 + set -o xtrace + driver=${SYSINFO_NIC_admin%%[0-9]*} + get_link_state ${SYSINFO_NIC_admin} + if [[ "$link_state" == "down" ]]; then + echo "admin nic '${SYSINFO_NIC_admin}' is down: unplumbing" + /sbin/ifconfig ${SYSINFO_NIC_admin} down unplumb + wait_for_admin_nic_state "unknown" + fi + + # There's some sort of race condition in the bnx driver: if the plumb + # command comes too soon after the unplumb, the interface can come up + # in a state where it never fires interrupts for link state changes. + if [[ "$driver" == "bnx" ]]; then + sleep 5 + fi + /sbin/ifconfig ${SYSINFO_NIC_admin} plumb mtu ${CONFIG_admin_mtu:-1500} + wait_for_admin_nic_state "up" } -# -# All the IPv4 and IPv6 interfaces are plumbed before doing any -# interface configuration. This prevents errors from plumb failures -# getting mixed in with the configured interface lists that the script -# outputs. -# +# Creates, plumbs and brings up a vnic with the specified inet parameters +function vnic_up +{ + set -o xtrace + + typeset link="$1" + typeset iface="$2" + typeset ip="$3" + typeset netmask="$4" + typeset vlan_id="$5" + typeset mac_addr="$6" + typeset dhcp_primary="$7" + typeset mtu="$8" + typeset details= + typeset vlan_opt= + typeset mac_addr_opt= + typeset prop_opt= + + details="link='${link}', iface='${iface}', ip='${ip}'" + details="${details}, netmask='${netmask}, vlan_id='${vlan_id}'" + + if [[ -z ${link} ]] || [[ -z ${iface} ]] || \ + [[ -z ${ip} ]] || ([[ ${ip} != "dhcp" ]] && [[ -z ${netmask} ]]); then + echo "WARNING: not bringing up nic (insufficient configuration): " \ + "$details" + return + fi + + eval "vnic_already_up=\${vnic_${iface}_up}" + if [[ -n "${vnic_already_up}" ]]; then + echo "vnic already up: $details" + return + fi + + if [[ -n ${mac_addr} ]] && [[ -n ${ActiveAggrLinks[${mac_addr}]} ]]; then + echo "WARNING: trying to assign MAC address \"${mac_addr}\" to vnic," \ + " but it already belongs to link aggr " \ + "\"${ActiveAggrLinks[${mac_addr}]}\"" + return + fi + + echo "Bringing up nic: $details" + + if [[ -n ${vlan_id} ]] && [[ ${vlan_id} != 0 ]]; then + vlan_opt="-v ${vlan_id}" + fi + + if [[ -n ${mac_addr} ]]; then + mac_addr_opt="-m ${mac_addr}" + fi + + if [[ -n ${mtu} ]]; then + valid_mtu ${iface} ${mtu} + prop_opt="-p mtu=${mtu}" + fi + + /usr/sbin/dladm create-vnic -t -l ${link} ${prop_opt} ${vlan_opt} \ + ${mac_addr_opt} ${iface} + if [[ $? -ne 0 ]]; then + echo "Failed to create VNIC ${iface}" + exit $SMF_EXIT_ERR_FATAL + fi + + /sbin/ifconfig ${iface} plumb + if [[ $? -ne 0 ]]; then + echo "Failed to plumb ${iface}" + exit $SMF_EXIT_ERR_FATAL + fi + + if [[ ${ip} == "dhcp" ]]; then + # We ignore errors here because the most common one is that DHCP + # is already running. + + if [[ -n ${dhcp_primary} ]]; then + /sbin/ifconfig ${iface} dhcp primary || /bin/true + else + /sbin/ifconfig ${iface} dhcp || /bin/true + fi + else + /sbin/ifconfig ${iface} inet ${ip} netmask ${netmask} up + fi + eval "vnic_${iface}_up=true" +} -# -# First deal with /etc/hostname -# -# Get the list of IPv4 interfaces to configure by breaking -# /etc/hostname.* into separate args by using "." as a shell separator -# character. -# -interface_names="`echo /etc/hostname.*[0-9] 2>/dev/null`" -if [ "$interface_names" != "/etc/hostname.*[0-9]" ]; then - ORIGIFS="$IFS" - IFS="$IFS." - set -- $interface_names - IFS="$ORIGIFS" - while [ $# -ge 2 ]; do - shift - intf_name=$1 - while [ $# -gt 1 -a "$2" != "/etc/hostname" ]; do - intf_name="$intf_name.$2" - shift - done - shift - - # skip IP tunnel interfaces plumbed by net-iptun. - if is_iptun $intf_name; then - continue - fi - - read one rest < /etc/hostname.$intf_name - if [ "$one" = ipmp ]; then - ipmp_list="$ipmp_list $intf_name" - else - inet_list="$inet_list $intf_name" - fi - done -fi +# Creates, plumbs and brings up a vnic with the specified inet6 parameters +function vnic_up6 +{ + set -o xtrace + + typeset link="$1" + typeset iface="$2" + typeset ip="$3" + typeset vlan_id="$4" + typeset mac_addr="$5" + typeset mtu="$6" + typeset details= + typeset vlan_opt= + typeset mac_addr_opt= + typeset prop_opt= + + details="link='${link}', iface='${iface}', ip6='${ip}'" + details="${details}, vlan_id='${vlan_id}'" + + if [[ -z ${link} ]] || [[ -z ${iface} ]] || [[ -z ${ip} ]]; then + echo "WARNING: not bringing up nic (insufficient configuration): " \ + "$details" + return + fi + + if [[ -n ${mac_addr} ]] && [[ -n ${ActiveAggrLinks[${mac_addr}]} ]]; then + echo "WARNING: trying to assign MAC address \"${mac_addr}\" to vnic," \ + " but it already belongs to link aggr " \ + "\"${ActiveAggrLinks[${mac_addr}]}\"" + return + fi + + # only bring up nic if not already up + eval "vnic_already_up=\${vnic_${iface}_up}" + if [[ -z "${vnic_already_up}" ]]; then + echo "Bringing up nic: $details" + + if [[ -n ${vlan_id} ]] && [[ ${vlan_id} != 0 ]]; then + vlan_opt="-v ${vlan_id}" + fi + + if [[ -n ${mac_addr} ]]; then + mac_addr_opt="-m ${mac_addr}" + fi + + if [[ -n ${mtu} ]]; then + valid_mtu ${iface} ${mtu} + prop_opt="-p mtu=${mtu}" + fi + + /usr/sbin/dladm create-vnic -t -l ${link} ${prop_opt} ${vlan_opt} \ + ${mac_addr_opt} ${iface} + if [[ $? -ne 0 ]]; then + echo "Failed to create VNIC ${iface}" + exit $SMF_EXIT_ERR_FATAL + fi + fi + + /sbin/ifconfig ${iface} inet6 plumb + if [[ $? -ne 0 ]]; then + echo "Failed to plumb ${iface}" + exit $SMF_EXIT_ERR_FATAL + fi + + if [[ -n ${ip} ]]; then + /sbin/ifconfig ${iface} inet6 up + fi + if [[ ${ip} != "addrconf" ]]; then + /sbin/ifconfig ${iface} inet6 \ + addif ${ip} preferred up + fi + eval "vnic_${iface}_up=true" +} -# -# Get the list of IPv6 interfaces to configure by breaking -# /etc/hostname6.* into separate args by using "." as a shell separator -# character. -# -interface_names="`echo /etc/hostname6.*[0-9] 2>/dev/null`" -if [ "$interface_names" != "/etc/hostname6.*[0-9]" ]; then - ORIGIFS="$IFS" - IFS="$IFS." - set -- $interface_names - IFS="$ORIGIFS" - while [ $# -ge 2 ]; do - shift - intf_name=$1 - while [ $# -gt 1 -a "$2" != "/etc/hostname6" ]; do - intf_name="$intf_name.$2" - shift - done - shift - - # skip IP tunnel interfaces plumbed by net-iptun. - if is_iptun $intf_name; then - continue - fi - - read one rest < /etc/hostname6.$intf_name - if [ "$one" = ipmp ]; then - ipmp6_list="$ipmp6_list $intf_name" - else - inet6_list="$inet6_list $intf_name" - fi - done -fi +# If there are aggregations in sysinfo, set them up. +function create_aggrs +{ + set -o xtrace + typeset links macs mode mtu + if [[ -z "${SYSINFO_Aggregations}" ]]; then + return 0 + fi + + aggrs=(${SYSINFO_Aggregations//,/ }) + for aggr in "${aggrs[@]}"; do + eval "links=\${SYSINFO_Aggregation_${aggr}_Interfaces}" + eval "macs=\${SYSINFO_Aggregation_${aggr}_MACs}" + eval "mode=\${SYSINFO_Aggregation_${aggr}_LACP_mode}" + eval "mtu=\${CONFIG_${aggr}_mtu}" + [[ -z "$mode" ]] && mode="off" + + echo "Creating aggr: ${aggr} (mode=${mode}, links=${links})" + dladm create-aggr -l ${links//,/ -l } -L ${mode} ${aggr} + if [[ $? -eq 0 ]]; then + add_active_aggr_links ${aggr} ${macs} + fi + + if [[ -n "$mtu" ]]; then + dladm set-linkprop -p mtu=${mtu} ${aggr} + if [[ $? -ne 0 ]]; then + echo "Failed to set mtu on aggr ${aggr} to ${mtu}" + exit $SMF_EXIT_ERR_FATAL + fi + fi + done + + # Creating the aggregations may affect the nic tags in sysinfo, so update: + /usr/bin/sysinfo -u + load_sdc_sysinfo +} # -# Create all of the IPv4 IPMP interfaces. +# Try various config parameters to set the default route # -if [ -n "$ipmp_list" ]; then - set -- $ipmp_list - while [ $# -gt 0 ]; do - if /sbin/ifconfig $1 ipmp; then - ipmp_created="$ipmp_created $1" - else - ipmp_failed="$ipmp_failed $1" - fi - shift - done - [ -n "$ipmp_failed" ] && warn_failed_ifs "create IPv4 IPMP" \ - "$ipmp_failed" -fi +function set_default_route +{ + set -o xtrace + typeset default_gw -# -# Step through the IPv4 interface list and try to plumb every interface. -# Generate list of plumbed and failed IPv4 interfaces. -# -if [ -n "$inet_list" ]; then - set -- $inet_list - while [ $# -gt 0 ]; do - /sbin/ifconfig $1 plumb - if /sbin/ifconfig $1 inet >/dev/null 2>&1; then - inet_plumbed="$inet_plumbed $1" - else - inet_failed="$inet_failed $1" - fi - shift - done - [ -n "$inet_failed" ] && warn_failed_ifs "plumb IPv4" "$inet_failed" -fi + if [[ -n "${CONFIG_headnode_default_gateway}" ]]; then + default_gw="${CONFIG_headnode_default_gateway}" -# Run autoconf to connect to a WLAN if the interface is a wireless one -if [ -x /sbin/wificonfig -a -n "$inet_plumbed" ]; then - set -- $inet_plumbed - while [ $# -gt 0 ]; do - if [ -r /dev/wifi/$1 ]; then - /sbin/wificonfig -i $1 startconf >/dev/null - fi - shift - done -fi + elif [[ -n ${CONFIG_admin_gateway} ]]; then + default_gw="${CONFIG_admin_gateway}" -# -# Step through the IPv6 interface list and plumb every interface. -# Generate list of plumbed and failed IPv6 interfaces. Each plumbed -# interface will be brought up later, after processing any contents of -# the /etc/hostname6.* file. -# -if [ -n "$inet6_list" ]; then - set -- $inet6_list - while [ $# -gt 0 ]; do - /sbin/ifconfig $1 inet6 plumb - if /sbin/ifconfig $1 inet6 >/dev/null 2>&1; then - inet6_plumbed="$inet6_plumbed $1" - else - inet6_failed="$inet6_failed $1" - fi - shift - done - [ -n "$inet6_failed" ] && warn_failed_ifs "plumb IPv6" "$inet6_failed" -fi + elif [[ -n ${BOOT_admin_gateway} ]]; then + default_gw=${BOOT_admin_gateway} -# -# Create all of the IPv6 IPMP interfaces. -# -if [ -n "$ipmp6_list" ]; then - set -- $ipmp6_list - while [ $# -gt 0 ]; do - if /sbin/ifconfig $1 inet6 ipmp; then - ipmp6_created="$ipmp6_created $1" - else - ipmp6_failed="$ipmp6_failed $1" - fi - shift - done - [ -n "$ipmp6_failed" ] && warn_failed_ifs "create IPv6 IPMP" \ - "$ipmp6_failed" -fi + elif [[ -n ${CONFIG_external_gateway} ]]; then + default_gw=${CONFIG_external_gateway} + fi -# -# Finally configure interfaces set up with ipadm. Any /etc/hostname*.intf -# files take precedence over ipadm defined configurations except when -# we are in a non-global zone and Layer-3 protection of IP addresses is -# enforced on the interface by the global zone. -# -for showif_output in `/sbin/ipadm show-if -p -o ifname,state,current`; do - intf=`echo $showif_output | /usr/bin/cut -f1 -d:` - state=`echo $showif_output | /usr/bin/cut -f2 -d:` - current=`echo $showif_output | /usr/bin/cut -f3 -d:` - if [[ "$state" != "disabled" && $current != *Z* ]]; then - # - # skip if not a persistent interface, or if it should get IP - # configuration from the global zone ('Z' flag is set) - # - continue; - elif is_iptun $intf; then - # skip IP tunnel interfaces plumbed by net-iptun - continue; - elif [ -f /etc/hostname.$intf ] || [ -f /etc/hostname6.$intf ]; then - if [[ $current != *Z* ]]; then - echo "found /etc/hostname.$intf "\ - "or /etc/hostname6.$intf, "\ - "ignoring ipadm configuration" > /dev/msglog - continue; - else - echo "Ignoring /etc/hostname*.$intf" > /dev/msglog - /sbin/ifconfig $intf unplumb > /dev/null 2>&1 - /sbin/ifconfig $intf inet6 unplumb > /dev/null 2>&1 - fi - fi - - # Enable the interface managed by ipadm - /sbin/ipadm enable-if -t $intf -done + if [[ -n ${default_gw} ]]; then + echo "${default_gw}" > /etc/defaultrouter + fi -# -# Process the /etc/hostname[6].* files for IPMP interfaces. Processing these -# before non-IPMP interfaces avoids accidental implicit IPMP group creation. -# -[ -n "$ipmp_created" ] && if_configure inet "IPMP" $ipmp_created -[ -n "$ipmp6_created" ] && if_configure inet6 "IPMP" $ipmp6_created + if [[ -n ${CONFIG_admin_gateway6} ]]; then + default_gw6="${CONFIG_admin_gateway6}" -# -# Process the /etc/hostname[6].* files for non-IPMP interfaces. -# -[ -n "$inet_plumbed" ] && if_configure inet "" $inet_plumbed -[ -n "$inet6_plumbed" ] && if_configure inet6 "" $inet6_plumbed + elif [[ -n ${BOOT_admin_gateway6} ]]; then + default_gw6=${BOOT_admin_gateway6} + + elif [[ -n ${CONFIG_external_gateway6} ]]; then + default_gw6=${CONFIG_external_gateway6} + fi + + if [[ -n ${default_gw6} ]]; then + # add static route + /usr/sbin/route add -inet6 default ${default_gw6} + fi +} # -# For the IPv4 and IPv6 interfaces that failed to plumb, find (or create) -# IPMP meta-interfaces to host their data addresses. +# Go through and set up the MTU for all of the various nic tags # -[ -n "$inet_failed" ] && move_addresses inet -[ -n "$inet6_failed" ] && move_addresses inet6 - -# Run DHCP if requested. Skip boot-configured interface. -interface_names="`echo /etc/dhcp.*[0-9] 2>/dev/null`" -if [ "$interface_names" != '/etc/dhcp.*[0-9]' ]; then - # - # First find the primary interface. Default to the first - # interface if not specified. First primary interface found - # "wins". Use care not to "reconfigure" a net-booted interface - # configured using DHCP. Run through the list of interfaces - # again, this time trying DHCP. - # - i4d_fail= - firstif= - primary= - ORIGIFS="$IFS" - IFS="${IFS}." - set -- $interface_names - - while [ $# -ge 2 ]; do - shift - [ -z "$firstif" ] && firstif=$1 - - for i in `shcat /etc/dhcp\.$1`; do - if [ "$i" = primary ]; then - primary=$1 - break - fi - done - - [ -n "$primary" ] && break - shift - done - - [ -z "$primary" ] && primary="$firstif" - cmdline=`shcat /etc/dhcp\.${primary}` - - if [ "$_INIT_NET_IF" != "$primary" ]; then - echo "starting DHCP on primary interface $primary" - /sbin/ifconfig $primary auto-dhcp primary $cmdline - # Exit code 4 means ifconfig timed out waiting for dhcpagent - [ $? != 0 ] && [ $? != 4 ] && i4d_fail="$i4d_fail $primary" - fi - - set -- $interface_names - - while [ $# -ge 2 ]; do - shift - cmdline=`shcat /etc/dhcp\.$1` - if [ "$1" != "$primary" -a \ - "$1" != "$_INIT_NET_IF" ]; then - echo "starting DHCP on interface $1" - /sbin/ifconfig $1 dhcp start wait 0 $cmdline - # Exit code can't be timeout when wait is 0 - [ $? != 0 ] && i4d_fail="$i4d_fail $1" - fi - shift - done - IFS="$ORIGIFS" - unset ORIGIFS - [ -n "$i4d_fail" ] && warn_failed_ifs "configure IPv4 DHCP" "$i4d_fail" -fi +function setup_mtu +{ + set -o xtrace + typeset tag oldifs val mac link curmtu + typeset -A mtus + typeset -A tagmap + + set -o xtrace + oldifs=$IFS + IFS=, + for tag in ${SYSINFO_Nic_Tags}; do + eval "val=\${CONFIG_${tag}_mtu}" + eval "mac=\${CONFIG_${tag}_nic}" + [[ -z "$val" ]] && continue + + valid_mtu ${tag} $val + + # + # Note, it doesn't matter what tag we use for a given mac + # address, because we'll always get the same link name later on. + # + if [[ -z "${tagmap[$mac]}" ]]; then + tagmap[$mac]=$tag + fi + + if [[ -z "${mtus[$mac]}" ]]; then + mtus[$mac]=$val + elif [[ "${mtus[$mac]}" -lt $val ]]; then + mtus[$mac]=$val + fi + done + IFS=$oldifs + + for mac in ${!mtus[@]}; do + tag=${tagmap[$mac]} + eval "link=\${SYSINFO_NIC_${tag}}" + if [[ -z "${link}" ]]; then + echo "/usbkey/config error: Missing link name for ${tag}" + exit $SMF_EXIT_ERR_FATAL + fi + + # + # Check the current MTU of the device. To help out devices which + # don't support the setting of the MTU (here's looking at you + # bnx), if the MTU is identical to its default, don't do + # anything and save the poor folks stuck with bnx some grief. + # + curmtu=$(/usr/sbin/dladm show-linkprop -c -o value -p mtu ${link}) + [[ $? -eq 0 ]] && [[ "$curmtu" -eq "${mtus[$mac]}" ]] && continue + + if ! /usr/sbin/dladm set-linkprop -p mtu=${mtus[$mac]} ${link}; then + echo "Failed to set mtu to ${mtus[$mac]} for link ${link}" + exit $SMF_EXIT_ERR_FATAL + fi + done +} -# In order to avoid bringing up the interfaces that have -# intentionally been left down, perform RARP only if the system -# has no configured hostname in /etc/nodename -hostname="`shcat /etc/nodename 2>/dev/null`" -if [ "$_INIT_NET_STRATEGY" = "rarp" -o -z "$hostname" ]; then - /sbin/ifconfig -adD4 auto-revarp netmask + broadcast + up + +# Helper function for plumbing an interface for an address family once +typeset -A plumbedifs +function plumbif +{ + iface=$1 + inet=$2 + addrtype=$3 + if [[ -z ${plumbedifs[${iface},${inet}]} ]]; then + plumbedifs[${iface},${inet}]="true" + /sbin/ifconfig ${iface} ${inet} plumb + if [[ ! ${addrtype} =~ "^vrrp" ]]; then + /sbin/ifconfig ${iface} ${inet} up + fi + fi +} + +if smf_is_globalzone; then + EARLY_ADMIN= + [[ -f /etc/svc/volatile/.early_admin_setup ]] && EARLY_ADMIN=1 + [[ -n "$EARLY_ADMIN" ]] || /usr/sbin/dladm init-phys + + # The next command is for logging purposes only + log_if_state before + + # Load sysinfo variables with SYSINFO_ prefix: we primarily care about + # the NIC_variables, which contain the actual interface name for a nic + # tag (it has mapped the foo_nic=<MAC address> variables to interface + # names for us). + load_sdc_sysinfo + + if boot_file_config_enabled; then + # We have a boot-time networking file present - use its values rather + # than ones from the config file or bootparams + if ! boot_file_config_valid; then + echo "ERROR: boot-time network config file incorrect" + exit ${SMF_EXIT_ERR_CONFIG} + fi + + load_boot_file_config + + # NOTE: some of the routes boot_file_config_init tries to add may + # fail if they are admin network routes added by the + # network/early-admin service. This is expected and not a problem. + boot_file_config_init + else + # Load config variables with CONFIG_ prefix, + # and sets the headnode variable + load_sdc_config + # Load boot params with BOOT_ prefix + load_sdc_bootparams + fi + + # Set up etherstubs + for stub in $(echo "${CONFIG_etherstub}" | sed -e "s/,/ /g"); do + /usr/sbin/dladm create-etherstub -t $stub || echo "ERROR: could not create etherstub ${stub}." + done + + # Create aggregations + create_aggrs + + # Make any mtu adjustments that may be necessary + setup_mtu + + # Setup admin NIC + ADMIN_NIC_TAG=${CONFIG_admin_tag:-"admin"} + + # If there is no NIC with the admin tag, and the config has + # admin_nic_autoselect=true, designate the first NIC reported + # by dladm for admin use. This is useful in environments where + # the NICs are known to change beneath us. + if [[ "${BOOT_smartos}" == "true" ]] && \ + [[ "${CONFIG_admin_nic_autoselect}" == "true" ]] && \ + ! nictagadm exists $ADMIN_NIC_TAG ; then + autoselected_admin_nic=$(dladm show-phys -m -p -o address | head -n1) + if [[ -z ${autoselected_admin_nic} ]] ; then + echo "ERROR: no NICs found, unable to autoselect admin NIC." + exit ${SMF_EXIT_ERR_CONFIG} + fi + + nictagadm add $ADMIN_NIC_TAG "${autoselected_admin_nic}" + if [[ $? -ne 0 ]] ; then + echo "ERROR: unable to add admin tag to NIC ${autoselected_admin_nic}" + exit ${SMF_EXIT_ERR_FATAL} + fi + + SYSINFO_NIC_admin=$(dladm show-phys -m -p -o link | head -n1) + if [[ -n ${SYSINFO_NIC_admin} ]] ; then + echo "Autoselected ${SYSINFO_NIC_admin} for use as admin NIC." + fi + + nictagadm list + elif [[ -v CONFIG_admin_tag ]]; then + # + # This handles the case when the 'admin_tag' property is set to + # override the default admin nic tag. + # + eval SYSINFO_NIC_admin='$'SYSINFO_NIC_${CONFIG_admin_tag} + + eval CONFIG_admin_ip='$'CONFIG_${CONFIG_admin_tag}_ip + eval CONFIG_admin_ip6='$'CONFIG_${CONFIG_admin_tag}_ip6 + eval CONFIG_admin_netmask='$'CONFIG_${CONFIG_admin_tag}_netmask + eval CONFIG_admin_mtu='$'CONFIG_${CONFIG_admin_tag}_mtu + eval CONFIG_admin_gateway='$'CONFIG_${CONFIG_admin_tag}_gateway + eval CONFIG_admin_gateway6='$'CONFIG_${CONFIG_admin_tag}_gateway6 + fi + + if [[ -z "${SYSINFO_NIC_admin}" ]]; then + echo "ERROR: admin NIC not found, unable to bring up admin network." + exit ${SMF_EXIT_ERR_CONFIG} + fi + + [[ -n "$EARLY_ADMIN" ]] || plumb_admin + + # If we performed early configuration of the admin network and + # the admin interface is on a link aggregation, the aggregation + # has already been configured. Add it to the list of active aggrs + # so the checks in vnic_up[6] are aware of it. + if [[ -n "$EARLY_ADMIN" && "${SYSINFO_NIC_admin}" =~ aggr[0-9]+$ ]]; then + eval "macs=\${SYSINFO_Aggregation_${SYSINFO_NIC_admin}_MACs}" + + add_active_aggr_links ${SYSINFO_NIC_admin} $macs + fi + + # Prefer the config file for admin nic values, but use + # bootparams if present + admin_ip=${CONFIG_admin_ip} + if [[ -z "$admin_ip" ]]; then + admin_ip=${BOOT_admin_ip} + fi + + admin_netmask=${CONFIG_admin_netmask} + if [[ -z "$admin_netmask" ]]; then + admin_netmask=${BOOT_admin_netmask} + fi + + admin_ip6=${CONFIG_admin_ip6} + if [[ -z "$admin_ip6" ]]; then + admin_ip6=${BOOT_admin_ip6} + fi + + if [[ $admin_ip == 'none' ]]; then + echo 'INFO: not configuring IP on admin interface (admin_ip=none)' + elif [[ -n "$EARLY_ADMIN" ]]; then + echo 'INFO: admin interface already configured (early setup)' + ADMIN_NIC_UP=true + elif [[ -n $admin_ip ]] && [[ -n $admin_netmask ]]; then + /sbin/ifconfig ${SYSINFO_NIC_admin} inet ${admin_ip} \ + netmask ${admin_netmask} up + ADMIN_NIC_UP=true + + # also setup resolv.conf if we can + if [[ -n ${CONFIG_dns_domain} ]] && [[ -n ${CONFIG_dns_resolvers} ]]; then + echo "search ${CONFIG_dns_domain}" > /etc/resolv.conf + if [[ -n ${CONFIG_binder_admin_ips} ]]; then + for serv in $(echo "${CONFIG_binder_admin_ips}" | sed -e "s/,/ /g"); do + echo "nameserver ${serv}" >> /etc/resolv.conf + done + fi + for serv in $(echo "${CONFIG_dns_resolvers}" | sed -e "s/,/ /g"); do + echo "nameserver ${serv}" >> /etc/resolv.conf + done + fi + else + if [[ ${headnode} == "true" ]]; then + echo "ERROR: headnode but no admin_{ip,netmask} in config, not bringing up admin network." + # Set a flag, but try to plumb the other interfaces anyway + ADMIN_NIC_MISCONFIGURED=true + else + # We ignore errors here because the most common one is that DHCP is + # already running. + /sbin/ifconfig ${SYSINFO_NIC_admin} dhcp || /bin/true + + # Wait for DHCP + timeout=${ADMIN_DHCP_TIMEOUT} + dhcp_admin_ip=$(/sbin/ifconfig ${SYSINFO_NIC_admin} | grep inet | awk '{ print $2 }') + while [[ (-z ${dhcp_admin_ip} || ${dhcp_admin_ip} == "0.0.0.0") && ${timeout} -gt 0 ]]; do + dhcp_admin_ip=$(/sbin/ifconfig ${SYSINFO_NIC_admin} | grep inet | awk '{ print $2 }') + timeout=$((${timeout} - 1)) + sleep 1 + done + + ADMIN_NIC_UP=true + fi + fi + + if [[ -n ${admin_ip6} && -z "$EARLY_ADMIN" ]]; then + # Plumb interface for inet6 + ifconfig ${SYSINFO_NIC_admin} inet6 \ + plumb mtu ${CONFIG_admin_mtu:-1500} up + + # Autodiscovery IPv6 using SLAAC + # NOTE: in.ndpd will be started later, due to plumbing the interface. + # this means autodiscovery also happens when configuring a + # static address. + # + # in.ndpd also sets a default route and this can't be disabled. + + # Configure static IPv6 + if [[ ${admin_ip6} != "addrconf" ]]; then + /sbin/ifconfig ${SYSINFO_NIC_admin} inet6 \ + addif ${admin_ip6} preferred up + fi + + ADMIN_NIC_UP=true + + # don't setup resolv.conf, IPv6 addresses already work + fi + + # If on Parallels or VirtualBox, create a bridge which + # allows traffic to flow correctly to the host-only network + if [[ "${ADMIN_NIC_UP}" == "true" ]] \ + && [[ ${SYSINFO_Product} == "Parallels Virtual Platform" \ + || ${SYSINFO_Product} == "VirtualBox" ]] \ + && [[ -z $(/usr/sbin/dladm show-bridge -p vmwarebr) ]]; then + /usr/sbin/dladm create-bridge -l ${SYSINFO_NIC_admin} vmwarebr + fi + + # Setup the external NIC. The installer may have already set up external0, + # so, if it exists, we're not going to try and set up the vnic again. + if [[ -n ${SYSINFO_NIC_external} ]] \ + && ! dladm show-vnic external0 > /dev/null; then + + if [[ -n "${CONFIG_external_ip}" ]]; then + vnic_up "${SYSINFO_NIC_external}" "external0" \ + "${CONFIG_external_ip}" "${CONFIG_external_netmask}" \ + "${CONFIG_external_vlan_id}" "${CONFIG_external_mac}" \ + "primary" "${CONFIG_external_mtu}" + fi + + if [[ -n "${CONFIG_external_ip6}" ]]; then + vnic_up6 "${SYSINFO_NIC_external}" "external0" \ + "${CONFIG_external_ip6}" \ + "${CONFIG_external_vlan_id}" "${CONFIG_external_mac}" \ + "${CONFIG_external_mtu}" + fi + fi + + set_default_route + + # Setup extra nics, if specified in the config file + nic_tags="${SYSINFO_Nic_Tags}" + if [[ -n "${nic_tags}" ]]; then + tags=(${nic_tags//,/ }) + + if boot_file_config_enabled; then + bootparam_ip_keys="" + bootparam_ip6_keys="" + config_ip_keys=${CONFIG_bootfile_ip_keys//,/ } + config_ip6_keys=${CONFIG_bootfile_ip6_keys//,/ } + else + bootparam_ip_keys=$(sdc_bootparams_keys | grep -- "-ip$" || true) + bootparam_ip6_keys=$(sdc_bootparams_keys | grep -- "-ip6$" || true) + config_ip_keys=$(sdc_config_keys | grep "_ip$" || true) + config_ip6_keys=$(sdc_config_keys | grep "_ip6$" || true) + fi + + for tag in "${tags[@]}"; do + + eval "link=\${SYSINFO_NIC_${tag}}" + if [[ -z "${link}" ]]; then + echo "WARNING: No link found with tag '${tag}'" + continue + fi + + for key in ${config_ip_keys}; do + if [[ ${key} == ${tag}[0-9]_ip ]] || [[ ${key} == ${tag}[0-9][0-9]_ip ]]; then + iface=${key//_ip/} + #echo " iface=$iface" + eval "ip=\${CONFIG_${iface}_ip}" + eval "netmask=\${CONFIG_${iface}_netmask}" + eval "vlan=\${CONFIG_${iface}_vlan_id}" + eval "macaddr=\${CONFIG_${iface}_mac}" + eval "mtu=\${CONFIG_${iface}_mtu}" + + echo vnic_up "${link}" "${iface}" "${ip}" "${netmask}" "${vlan}" "${macaddr}" "${mtu}" + vnic_up "${link}" "${iface}" "${ip}" "${netmask}" \ + "${vlan}" "${macaddr}" "" "${mtu}" + fi + done + + for key in ${bootparam_ip_keys}; do + if [[ ${key} == ${tag}[0-9]-ip ]] || [[ ${key} == ${tag}[0-9][0-9]-ip ]]; then + iface=${key//-ip/} + eval "ip=\${BOOT_${iface}_ip}" + eval "netmask=\${BOOT_${iface}_netmask}" + eval "vlan=\${BOOT_${iface}_vlan_id}" + eval "macaddr=\${BOOT_${iface}_mac}" + eval "mtu=\${CONFIG_${iface}_mtu}" + echo vnic_up "${link}" "${iface}" "${ip}" "${netmask}" "${vlan}" "${macaddr}" "${mtu}" + vnic_up "${link}" "${iface}" "${ip}" "${netmask}" \ + "${vlan}" "${macaddr}" "" "${mtu}" + fi + done + + for key in ${config_ip6_keys}; do + if [[ ${key} == ${tag}[0-9]_ip6 ]] || [[ ${key} == ${tag}[0-9][0-9]_ip6 ]]; then + iface=${key//_ip6/} + #echo " iface=$iface" + eval "ip=\${CONFIG_${iface}_ip6}" + eval "vlan=\${CONFIG_${iface}_vlan_id}" + eval "macaddr=\${CONFIG_${iface}_mac}" + eval "mtu=\${CONFIG_${iface}_mtu}" + + echo vnic_up6 "${link}" "${iface}" "${ip}" "${vlan}" "${macaddr}" "${mtu}" + vnic_up6 "${link}" "${iface}" "${ip}" \ + "${vlan}" "${macaddr}" "${mtu}" + fi + done + + for key in ${bootparam_ip6_keys}; do + if [[ ${key} == ${tag}[0-9]-ip6 ]] || [[ ${key} == ${tag}[0-9][0-9]-ip6 ]]; then + iface=${key//-ip6/} + eval "ip=\${BOOT_${iface}_ip6}" + eval "vlan=\${BOOT_${iface}_vlan_id}" + eval "macaddr=\${BOOT_${iface}_mac}" + eval "mtu=\${CONFIG_${iface}_mtu}" + echo vnic_up6 "${link}" "${iface}" "${ip}" "${vlan}" "${macaddr}" "${mtu}" + vnic_up6 "${link}" "${iface}" "${ip}" \ + "${vlan}" "${macaddr}" "${mtu}" + fi + done + + done + fi +else + # Non-global zones + + # Bring up statically assigned interfaces, and find the primary DHCP + # interface, if it exists + while IFS=: read -r iface addrtype; do + # Keep track of whether or not we've configured our first IPv4 + # address on this interface + first_ipv4_configured="" + iface_configured="" + + if [[ -f /etc/dhcp.${iface} ]]; then + plumbif ${iface} inet ${addrtype} + if [[ -z "${primary}" ]]; then + /sbin/ifconfig ${iface} auto-dhcp primary + primary=${iface} + else + /sbin/ifconfig ${iface} auto-dhcp + fi + first_ipv4_configured="true" + iface_configured="true" + fi + + if [[ -f /etc/hostname.${iface} ]]; then + while read ifparams; do + # For IPv4, we need to set the address on the first logical + # interface. For IPv6, the address on the first interface is + # the link-local address, and can't be changed. + # + # Lines starting with inet indicate the hostname for that + # interface, and are used by dhcpagent. Skip over them. + if [[ -f /etc/dhcp.${iface} && "${ifparams}" == inet* ]]; then + continue + elif [[ "${ifparams}" == {3}({1,3}(\d).){1,3}(\d)* ]]; then + plumbif ${iface} inet ${addrtype} + if [[ -z "${first_ipv4_configured}" ]]; then + first_ipv4_configured="true" + ifcommand="inet" + else + ifcommand="inet addif" + fi + else + plumbif ${iface} inet6 ${addrtype} + ifcommand="inet6 addif" + fi + + # vrrp interfaces can't be brought up with ifconfig: vrrpadm + # handles that instead + if [[ "${addrtype}" =~ "^vrrp" ]]; then + /sbin/ifconfig ${iface} ${ifcommand} \ + `printf "%s" "${ifparams}" | sed -e 's/ up//'` + else + /sbin/ifconfig ${iface} ${ifcommand} ${ifparams} up + fi + + iface_configured="true" + done < /etc/hostname.${iface} + fi + + if [[ -f /etc/addrconf.${iface} ]]; then + # Enable the NDP daemon, so that once this script finishes, we'll + # be able to pick up router advertisments and finish configuring + # the network interface. We then just need to plumb the interface, + # and let in.ndpd take care of configuring addresses. + svcadm enable svc:/network/routing/ndp:default + plumbif ${iface} inet6 ${addrtype} + iface_configured="true" + fi + + # If we didn't configure the device at all, mark it for DHCP if we + # don't do DHCP on anyone else. + if [[ -z ${iface_configured} && -z "${first_iface}" ]]; then + first_iface=${iface} + fi + done < <(/usr/sbin/dladm show-vnic -p -o link,macaddrtype 2>/dev/null) + + if [[ -z "${primary}" && -n "${first_iface}" ]]; then + first_iface_type=$(dladm show-vnic ${first_iface} -p -o macaddrtype) + plumbif ${first_iface} inet ${first_iface_type} + /sbin/ifconfig ${first_iface} auto-dhcp + primary=${first_iface} + fi + + while IFS=: read -r iface addrtype; do + if [[ "${iface}" == "${primary}" + || -f /etc/hostname.${iface} + || -f /etc/dhcp.${iface} + || -f /etc/addrconf.${iface} + || "${addrtype}" =~ "^vrrp" ]]; then + continue + fi + + plumbif ${iface} inet ${addrtype} + /sbin/ifconfig ${iface} auto-dhcp start wait 0 + done < <(/usr/sbin/dladm show-vnic -p -o link,macaddrtype 2>/dev/null) fi -# -# If the /etc/defaultrouter file exists, process it now so that the next -# stage of booting will have access to NFS. -# -if [ -f /etc/defaultrouter ]; then - while read router rubbish; do - case "$router" in - '#'* | '') ;; # Ignore comments, empty lines - *) /sbin/route -n add default -gateway $router ;; - esac - done </etc/defaultrouter +log_if_state after + +# Since we hopefully made networking changes here, update the sysinfo cache +if smf_is_globalzone; then + /usr/bin/sysinfo -u fi -# -# If we get here and were not asked to plumb any IPv4 interfaces, look -# for boot properties that direct us. -# -# - The "network-interface" property is required and indicates the -# interface name. -# - The "xpv-hcp" property, if present, is used by the hypervisor -# tools to indicate how the specified interface should be configured. -# Permitted values are "dhcp" and "off", where "off" indicates static -# IP configuration. -# -# In the case where "xpv-hcp" is set to "dhcp", no further properties -# are required or examined. -# -# In the case where "xpv-hcp" is not present or set to "off", the -# "host-ip" and "subnet-mask" properties are used to configure -# the specified interface. The "router-ip" property, if present, -# is used to add a default route. -# -nic="`/sbin/devprop network-interface`" -if smf_is_globalzone && [ -z "$inet_list" ] && [ -n "$nic" ]; then - hcp="`/sbin/devprop xpv-hcp`" - case "$hcp" in - "dhcp") - /sbin/ifconfig $nic plumb 2>/dev/null - [ -n "`/sbin/ifconfig $nic 2>/dev/null`" ] && ( - # The interface is successfully plumbed, so - # modify "inet_list" to force the exit code - # checks to work. - inet_list=$nic; - # Given that this is the only IPv4 interface, - # we assert that it is primary. - echo "starting DHCP on primary interface $primary"; - /sbin/ifconfig $nic auto-dhcp primary; - # Exit code 4 means ifconfig timed out waiting - # for dhcpagent - [ $? != 0 ] && [ $? != 4 ] && \ - i4d_fail="$i4d_fail $nic"; - ) - ;; - - "off"|"") - /sbin/devprop host-ip subnet-mask router-ip | ( - read ip; - read mask; - read router; - [ -n "$ip" ] && [ -n "$mask" ] && \ - /sbin/ifconfig $nic plumb 2>/dev/null - [ -n "`/sbin/ifconfig $nic 2>/dev/null`" ] && ( - # The interface is successfully - # plumbed, so modify "inet_list" to - # force the exit code checks to work. - inet_list=$nic; - /sbin/ifconfig $nic inet $ip \ - netmask $mask broadcast + up 2>/dev/null; - [ -n "$router" ] && route add \ - default $router 2>/dev/null; - ) - ) - ;; - esac +# Enable symmetric routing: when there are multiple nics configured, always +# take into account the interface a packet is being sent over when +# selecting a route. This prevents packets being sent with another nic's +# source IP. +/usr/sbin/ndd -set /dev/ip ip_strict_src_multihoming 1 + +# If the admin nic was missing config options, exit with a config error +if [[ -n "${ADMIN_NIC_MISCONFIGURED}" ]]; then + exit ${SMF_EXIT_ERR_CONFIG} fi -# -# We tell smf this service is online if any of the following is true: -# - no interfaces were configured for plumbing and no DHCP failures -# - any non-loopback IPv4 interfaces are up and have a non-zero address -# - there are any DHCP interfaces started -# - any non-loopback IPv6 interfaces are up -# -# If we weren't asked to configure any interfaces, exit -if [ -z "$inet_list" ] && [ -z "$inet6_list" ]; then - # Config error if DHCP was attempted without plumbed interfaces - [ -n "$i4d_fail" ] && exit $SMF_EXIT_ERR_CONFIG - exit $SMF_EXIT_OK +if [[ $admin_ip == 'none' ]]; then + # + # We're done, even if there are not any usable IP addresses. + # + exit $SMF_EXIT_OK fi # Any non-loopback IPv4 interfaces with usable addresses up? -if [ -n "`/sbin/ifconfig -a4u`" ]; then - /sbin/ifconfig -a4u | while read intf addr rest; do - [ $intf = inet ] && [ $addr != 127.0.0.1 ] && - [ $addr != 0.0.0.0 ] && exit $SMF_EXIT_OK - done && exit $SMF_EXIT_OK +if [[ -n "`/sbin/ifconfig -a4u`" ]]; then + /sbin/ifconfig -a4u | while read intf addr rest; do + [[ ${intf} == "inet" ]] && [[ ${addr} != "127.0.0.1" ]] && + [[ ${addr} != "0.0.0.0" ]] && exit ${SMF_EXIT_OK} + done && exit ${SMF_EXIT_OK} fi # Any DHCP interfaces started? -[ -n "`/sbin/ifconfig -a4 dhcp status 2>/dev/null`" ] && exit $SMF_EXIT_OK +[[ -n "`/sbin/ifconfig -a4 dhcp status 2>/dev/null`" ]] && exit ${SMF_EXIT_OK} # Any non-loopback IPv6 interfaces up? -if [ -n "`/sbin/ifconfig -au6`" ]; then - /sbin/ifconfig -au6 | while read intf addr rest; do - [ $intf = inet6 ] && [ $addr != ::1/128 ] && exit $SMF_EXIT_OK - done && exit $SMF_EXIT_OK +if [[ -n "`/sbin/ifconfig -au6`" ]]; then + /sbin/ifconfig -au6 | while read intf addr rest; do + [[ ${intf} = "inet6" ]] && [[ ${addr} != "::1/128" ]] && exit ${SMF_EXIT_OK} + done && exit ${SMF_EXIT_OK} fi # This service was supposed to configure something yet didn't. Exit # with config error. -exit $SMF_EXIT_ERR_CONFIG +exit ${SMF_EXIT_ERR_CONFIG} diff --git a/usr/src/cmd/svc/milestone/net-routing-setup b/usr/src/cmd/svc/milestone/net-routing-setup index 6ab1a6c7f0..0b294c2630 100644 --- a/usr/src/cmd/svc/milestone/net-routing-setup +++ b/usr/src/cmd/svc/milestone/net-routing-setup @@ -21,11 +21,15 @@ # # # Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. +# +# Copyright (c) 2012 Joyent, Inc. All rights reserved. # This script configures IP routing. . /lib/svc/share/smf_include.sh +set -o xtrace + # # In a shared-IP zone we need this service to be up, but all of the work # it tries to do is irrelevant (and will actually lead to the service @@ -80,6 +84,17 @@ fi # smf_netstrategy +# +# Read /etc/inet/static_routes.vmadm and add each link-local route. +# +if [ -f /etc/inet/static_routes.vmadm ]; then + echo "Adding vmadm persistent link-local routes:" + /usr/bin/egrep -v "^(#|$)" /etc/inet/static_routes.vmadm | + /usr/bin/grep -- "-interface " | while read line; do + /usr/sbin/route add $line + done +fi + if [ "$_INIT_NET_STRATEGY" = "dhcp" ] && \ [ -n "`/sbin/dhcpinfo Router`" ]; then defrouters=`/sbin/dhcpinfo Router` @@ -156,7 +171,8 @@ fi # however, as persistent daemon state is now controlled by SMF. # ipv4_routing_set=`/usr/bin/svcprop -p routeadm/ipv4-routing-set $SMF_FMRI` -if [ -z "$defrouters" ]; then +smartos_param=`/usr/bin/bootparams | grep "^smartos"` +if [ -z "$defrouters" ] && [ "$smartos_param" != "" ]; then # # Set default value for ipv4-routing to enabled. If routeadm -e/-d # has not yet been run by the administrator, we apply this default. @@ -210,5 +226,22 @@ if [ -f /etc/inet/static_routes ]; then done fi +# +# Read /etc/inet/static_routes.vmadm and add each non-link-local route. +# +if [ -f /etc/inet/static_routes.vmadm ]; then + echo "Adding vmadm persistent routes:" + /usr/bin/egrep -v "^(#|$)" /etc/inet/static_routes.vmadm | + /usr/bin/grep -v -- "-interface " | while read line; do + /usr/sbin/route add $line + done +fi + +# +# Log the result +# +echo "Routing setup complete:" +/usr/bin/netstat -rn + # Clear exit status. exit $SMF_EXIT_OK diff --git a/usr/src/cmd/svc/milestone/network-early-admin.xml b/usr/src/cmd/svc/milestone/network-early-admin.xml new file mode 100644 index 0000000000..6566f31bff --- /dev/null +++ b/usr/src/cmd/svc/milestone/network-early-admin.xml @@ -0,0 +1,73 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2019, Joyent, Inc. + + This file and its contents are supplied under the terms of the + Common Development and Distribution License ("CDDL"), version 1.0. + You may only use this file in accordance with the terms of version + 1.0 of the CDDL. + + A full copy of the text of the CDDL should have accompanied this + source. A copy of the CDDL is also available via the Internet at + http://www.illumos.org/license/CDDL. + +--> + +<service_bundle type='manifest' name='network-early-admin'> + +<service + name='network/early-admin' + type='service' + version='1'> + + <!-- ifconfig needs loopback for IPC with dhcpagent --> + <dependency + name='network' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/network/loopback' /> + <service_fmri value='svc:/network/datalink-management' /> + <service_fmri value='svc:/network/ip-interface-management' /> + </dependency> + + <instance name='default' enabled='true'> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/net-early-admin' + timeout_seconds='600' /> + + <exec_method + type='method' + name='stop' + exec=':true' + timeout_seconds='3' /> + + <property_group name='startd' type='framework'> + <propval name='duration' type='astring' value='transient' /> + </property_group> + + <template> + <common_name> + <loctext xml:lang='C'> + Triton admin network on compute nodes + </loctext> + </common_name> + <documentation> + <manpage title='ifconfig' section='1M' + manpath='/usr/share/man' /> + <manpage title='dladm' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> + + </instance> + + <stability value='Unstable' /> + +</service> + +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/network-location.xml b/usr/src/cmd/svc/milestone/network-location.xml index aad337f42f..5e23d92481 100644 --- a/usr/src/cmd/svc/milestone/network-location.xml +++ b/usr/src/cmd/svc/milestone/network-location.xml @@ -81,14 +81,6 @@ </dependency> <dependency - name='location_netcfg' - grouping='require_all' - restart_on='none' - type='service'> - <service_fmri value='svc:/network/netcfg:default' /> - </dependency> - - <dependency name='filesystem' grouping='require_all' restart_on='none' diff --git a/usr/src/cmd/svc/milestone/network-physical.xml b/usr/src/cmd/svc/milestone/network-physical.xml index fdc9e4bdf8..f26c9647a1 100644 --- a/usr/src/cmd/svc/milestone/network-physical.xml +++ b/usr/src/cmd/svc/milestone/network-physical.xml @@ -47,7 +47,13 @@ <service_fmri value='svc:/network/loopback' /> </dependency> - <instance name='default' enabled='true'> + <dependency + name='joyent' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/filesystem/smartdc' /> + </dependency> <!-- physical:default and physical:nwam are mutually exclusive. @@ -55,13 +61,25 @@ does not work. --> <dependency - name='physical_nwam' - grouping='exclude_all' + name='mdata-fetch' + grouping='optional_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/smartdc/mdata:fetch' /> + </dependency> + + <!-- Prevent this and network/early-admin from trying to configure the + admin interface at the same time --> + <dependency + name='early-admin' + grouping='optional_all' restart_on='none' type='service'> - <service_fmri value='svc:/network/physical:nwam' /> + <service_fmri value='svc:/network/early-admin:default' /> </dependency> + <instance name='default' enabled='true'> + <exec_method type='method' name='start' @@ -92,82 +110,6 @@ </instance> - <instance name='nwam' enabled='false'> - - <exec_method - type='method' - name='start' - exec='/lib/svc/method/net-nwam start' - timeout_seconds='120' > - <method_context> - <method_credential user='root' group='root' - supp_groups='netadm' privileges='zone' /> - </method_context> - </exec_method> - - <exec_method - type='method' - name='stop' - exec='/lib/svc/method/net-nwam stop' - timeout_seconds='60' > - <method_context> - <method_credential user='root' group='root' - supp_groups='netadm' privileges='zone' /> - </method_context> - </exec_method> - - <exec_method - type='method' - name='refresh' - exec='/lib/svc/method/net-nwam refresh' - timeout_seconds='60' > - <method_context> - <method_credential user='root' group='root' - supp_groups='netadm' privileges='zone' /> - </method_context> - </exec_method> - - <property_group name='general' type='framework'> - <!-- to start/stop NWAM services --> - <propval name='action_authorization' type='astring' - value='solaris.smf.manage.nwam' /> - <propval name='value_authorization' type='astring' - value='solaris.smf.manage.nwam' /> - </property_group> - - <property_group name='nwamd' type='application'> - <stability value='Unstable' /> - <propval name='debug' type='boolean' value='false' /> - <propval name='autoconf' type='boolean' value='false' /> - <propval name='ncu_wait_time' type='count' value='60' /> - <propval name='condition_check_interval' type='count' - value='120' /> - <propval name='scan_interval' type='count' value='120' /> - <propval name='scan_level' type='astring' value='weak' /> - <propval name='strict_bssid' type='boolean' value='false' /> - <propval name='active_ncp' type='astring' value='Automatic' /> - <propval name='value_authorization' type='astring' - value='solaris.smf.value.nwam' /> - </property_group> - - <template> - <common_name> - <loctext xml:lang='C'> - physical network interface autoconfiguration - </loctext> - </common_name> - <documentation> - <manpage title='nwamd' section='1M' - manpath='/usr/share/man' /> - <doc_link - name='Network Auto-Magic OpenSolaris Project Page' - uri='http://hub.opensolaris.org/bin/view/Project+nwam/' - /> - </documentation> - </template> - - </instance> - <stability value='Unstable' /> </service> diff --git a/usr/src/cmd/svc/milestone/network-routing-setup.xml b/usr/src/cmd/svc/milestone/network-routing-setup.xml index b34d578e2a..85a74756da 100644 --- a/usr/src/cmd/svc/milestone/network-routing-setup.xml +++ b/usr/src/cmd/svc/milestone/network-routing-setup.xml @@ -40,11 +40,19 @@ <!-- loopback/physical network configuration is required --> <dependency - name='network' - grouping='optional_all' + name='loopback' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/network/loopback' /> + </dependency> + + <dependency + name='physical' + grouping='require_all' restart_on='none' type='service'> - <service_fmri value='svc:/milestone/network' /> + <service_fmri value='svc:/network/physical' /> </dependency> <!-- usr filesystem required to run routing-related commands --> diff --git a/usr/src/cmd/svc/milestone/network.xml b/usr/src/cmd/svc/milestone/network.xml index 75b5578f44..48386ebf73 100644 --- a/usr/src/cmd/svc/milestone/network.xml +++ b/usr/src/cmd/svc/milestone/network.xml @@ -54,6 +54,14 @@ <service_fmri value='svc:/network/physical' /> </dependency> + <dependency + name='routing-setup' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/network/routing-setup' /> + </dependency> + <exec_method type='method' name='start' diff --git a/usr/src/cmd/svc/milestone/single-user.xml b/usr/src/cmd/svc/milestone/single-user.xml index 8797f13c47..579ecb5ddd 100644 --- a/usr/src/cmd/svc/milestone/single-user.xml +++ b/usr/src/cmd/svc/milestone/single-user.xml @@ -68,7 +68,7 @@ <dependency name='manifests' - grouping='require_all' + grouping='optional_all' restart_on='none' type='service'> <service_fmri value='svc:/system/manifest-import' /> diff --git a/usr/src/cmd/svc/milestone/smartdc-config b/usr/src/cmd/svc/milestone/smartdc-config new file mode 100755 index 0000000000..510c93add6 --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-config @@ -0,0 +1,211 @@ +#!/bin/bash +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2019 Joyent, Inc. +# + +# +# Despite its "smartdc/config" name, this service is used both for SmartOS and +# Triton. It has two jobs: +# +# During an initial setup, this runs through the initial (possibly interactive) +# configuration. +# +# During normal operation, it does some miscellaneous configuration based on +# /usbkey/config (which, under Triton, will have already been updated from the +# USB key by svc:/system/filesystem/smartdc:default). +# + +set -o errexit +set -o xtrace + +. /lib/svc/share/smf_include.sh +. /lib/sdc/config.sh + +export PATH="/usr/sbin:/sbin:/usr/bin" + +set_root_password() { + enc_password=$1 + + sed -e "s|^root:[^\:]*:|root:${enc_password}:|" /etc/shadow > /etc/shadow.new \ + && chmod 400 /etc/shadow.new \ + && mv /etc/shadow.new /etc/shadow +} + +case "$1" in +'start') + + # If we're a headnode, see if we have to do interactive configuration. + if /bin/bootparams | grep "^headnode=true" > /dev/null 2>&1; then + USB_PATH=/mnt/`svcprop -p "joyentfs/usb_mountpoint" svc:/system/filesystem/smartdc:default` + + # Check for config and run interactive if it doesn't exist. + if [[ ! -f ${USB_PATH}/config ]]; then + if /bin/bootparams | grep "^noimport=true" >/dev/null 2>&1; then + # Skipping interactive config, bypass rest of script. + exit $SMF_EXIT_OK + fi + + /smartdc/lib/sdc-on-tty -d /dev/console \ + ${USB_PATH}/scripts/prompt-config.sh "${USB_PATH}" + + # If user quit from interactive configuration then we're done. + [[ ! -f ${USB_PATH}/config ]] && exit $SMF_EXIT_OK + fi + elif /bin/bootparams | grep "^smartos=true" > /dev/null 2>&1; then + USB_PATH=/`svcprop -p "joyentfs/usb_copy_path" svc:/system/filesystem/smartdc:default` + + # Check for config and run interactive if it doesn't exist. + if [[ ! -f ${USB_PATH}/config ]]; then + if /bin/bootparams | grep "^noimport=true" >/dev/null 2>&1; then + # Skipping interactive config, bypass rest of script. + exit $SMF_EXIT_OK + fi + + /smartdc/lib/sdc-on-tty -d /dev/console \ + /smartdc/lib/smartos_prompt_config.sh "${USB_PATH}" + + # If user quit from interactive configuration then we're done. + [[ ! -f ${USB_PATH}/config ]] && exit $SMF_EXIT_OK + fi + fi + + # This puts config vars in CONFIG_ + load_sdc_config + load_sdc_sysinfo + + # Write the info about this datacenter to /.dcinfo so we can use in the GZ + echo "SDC_DATACENTER_NAME='${CONFIG_datacenter_name}'" > /.dcinfo + + if [[ -n "${SYSINFO_Bootparam_smartos}" && -f /usbkey/shadow ]]; then + echo "setting root password from /usbkey/shadow" + # Boot parameter takes precidence over config + elif [[ -n "${SYSINFO_Bootparam_root_shadow}" ]]; then + set_root_password "${SYSINFO_Bootparam_root_shadow}" + echo "Set root password boot parameters." + elif [[ -n "${CONFIG_root_shadow}" ]]; then + set_root_password "${CONFIG_root_shadow}" + echo "Set root password from config." + else + echo "No root shadow entry in the config, cannot set." + fi + + # Set authorized_keys for root + if [[ -n "${CONFIG_root_authorized_keys_file}" ]] \ + && [[ -n "${CONFIG_config_inc_dir}" ]] \ + && [[ -d "${CONFIG_config_inc_dir}" ]] \ + && [[ -f "${CONFIG_config_inc_dir}/${CONFIG_root_authorized_keys_file}" ]]; then + + mkdir -p /root/.ssh + cp "${CONFIG_config_inc_dir}/${CONFIG_root_authorized_keys_file}" /root/.ssh/authorized_keys + chmod 0600 /root/.ssh/authorized_keys + chmod 0700 /root/.ssh + fi + + if [[ -n "${CONFIG_ntp_conf_file}" ]] \ + && [[ -n "${CONFIG_config_inc_dir}" ]] \ + && [[ -d "${CONFIG_config_inc_dir}" ]] \ + && [[ -f "${CONFIG_config_inc_dir}/${CONFIG_ntp_conf_file}" ]]; then + # + # We were given a valid NTP configuration file, so use it without + # modification: + # + cp "${CONFIG_config_inc_dir}/${CONFIG_ntp_conf_file}" \ + /etc/inet/ntp.conf + echo "Copied NTP configuration." + else + # + # If we have an admin network defined, allow time service from this + # network: + # + ntp_aflag= + if [[ -n ${CONFIG_admin_network} && -n ${CONFIG_admin_netmask} ]]; then + if [[ "${CONFIG_admin_network}" != "..." ]]; then + ntp_aflag="-a ${CONFIG_admin_network}/${CONFIG_admin_netmask}" + fi + fi + + # + # If we were given a list of servers, use it: + # + ntp_hosts="${CONFIG_ntp_hosts}" + if [[ -z "${ntp_hosts}" ]]; then + # + # Otherwise, use the default SmartOS vendor pool from the NTP + # Pool Project: + # + ntp_hosts='0.smartos.pool.ntp.org' + fi + + # + # Generate NTP configuration: + # + if ! /smartdc/lib/ntp_config -f /etc/inet/ntp.conf \ + -s "${ntp_hosts}" ${ntp_aflag}; then + echo "FATAL: could not configure NTP" >&2 + exit ${SMF_EXIT_ERR_CONFIG} + fi + echo "Generated NTP configuration." + fi + + # set the keymap. For dvorak for instance + if [[ -n ${CONFIG_default_keymap} ]]; then + /usr/bin/loadkeys ${CONFIG_default_keymap} + fi + + # + # In SmartOS, disabling SMT via the boot option is a pain, so we support a + # config option in /usbkey/config, the official mechanism for permanent + # configuration. We'd like to do this earlier but we have to wait for + # /usbkey to be mounted first. This does imply we've potentially handed out + # "too many" interrupts for the set of CPUs remaining online. + # + if [[ -n $(/bin/bootparams | grep '^smartos=true') ]]; then + if [[ "$CONFIG_smt_enabled" = "false" ]]; then + psradm -aS || exit $SMF_EXIT_ERR_FATAL + fi + fi + + # Enable virtual terminals to support interactive installation + vtdaemon="svc:/system/vtdaemon" + svccfg -s ${vtdaemon} setprop options/hotkeys=true + svcadm refresh ${vtdaemon} + svcadm enable ${vtdaemon} + svcadm enable svc:/system/console-login:vt2 + svcadm enable svc:/system/console-login:vt3 + svcadm enable svc:/system/console-login:vt4 + svcadm enable svc:/system/console-login:vt5 + svcadm enable svc:/system/console-login:vt6 + + # force update of sysinfo (and dump to stdout so we have in the log) + sysinfo -f + + ;; + +'stop') + ;; + +*) + echo "Usage: $0 { start | stop }" + exit $SMF_EXIT_ERR_FATAL + ;; +esac +exit $SMF_EXIT_OK diff --git a/usr/src/cmd/svc/milestone/smartdc-config.xml b/usr/src/cmd/svc/milestone/smartdc-config.xml new file mode 100644 index 0000000000..bf84d1991b --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-config.xml @@ -0,0 +1,145 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2011 Joyent, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. +--> + +<service_bundle type='manifest' name='smartdc:config'> + +<!-- + This service applies inital configuration for the GZ in smartdc. +--> +<service + name='system/smartdc/config' + type='service' + version='1'> + + <create_default_instance enabled='true' /> + + <single_instance /> + + <!-- + dependency/dependent info + We need to wait for fs-joyent to be done so we can access the USB key + We need to run before sysconfig so interactive configuration can talk + on the console + (sysconfig) + We don't want anything else running which spews on console + (system/identy) + We don't want manifest import scribbling on the console while running + (manifest-import) + network/physical can't run without a config file + (network/physical) + --> + <dependency + name='filesystem-joyent' + type='service' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/system/filesystem/smartdc' /> + </dependency> + + <!-- + We don't want ssh starting if we haven't updated the passwords/keys + --> + <dependent + name='ssh_multi-user-server' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/network/ssh' /> + </dependent> + + <!-- Make ntp wait so we can setup the config. --> + <dependent + name='ntp' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/network/ntp' /> + </dependent> + + <dependent + name='headnode_config' + grouping='optional_all' + restart_on='none'> + <service_fmri value='svc:/milestone/sysconfig' /> + </dependent> + + <dependent + name='identity' + grouping='optional_all' + restart_on='none'> + <service_fmri value='svc:/system/identity:node' /> + </dependent> + + <dependent + name='headnode_no_output' + grouping='optional_all' + restart_on='none'> + <service_fmri value='svc:/system/manifest-import' /> + </dependent> + + <dependent + name='network-physical' + grouping='optional_all' + restart_on='none'> + <service_fmri value='svc:/network/physical:default' /> + </dependent> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/smartdc-config %m' + timeout_seconds='0'> + </exec_method> + + <exec_method + type='method' + name='stop' + exec='/lib/svc/method/smartdc-config %m' + timeout_seconds='60'> + </exec_method> + + <property_group name='startd' type='framework'> + <propval name='duration' type='astring' value='transient' /> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + SmartDC live-image config management service + </loctext> + </common_name> + <documentation> + <manpage title='smartdc' section='5' manpath='/usr/share/man' /> + </documentation> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/smartdc-init b/usr/src/cmd/svc/milestone/smartdc-init new file mode 100755 index 0000000000..1b2a550095 --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-init @@ -0,0 +1,246 @@ +#!/bin/bash +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2018, Joyent, Inc. +# + +export PS4='[\D{%FT%TZ}] ${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' +set -o xtrace + +. /lib/svc/share/smf_include.sh +. /lib/sdc/config.sh + +# Make sure working directory is / to prevent unmounting problems. +cd / +PATH=/usr/sbin:/usr/bin; export PATH + +wait_and_clear() +{ + while [ true ]; do + # It seems like jobs -p can miscount if we don't run jobs first + jobs >/dev/null + local cnt=`jobs -p | wc -l` + [ $cnt -eq 0 ] && break + for s in `svcs -x | nawk '{ + if ($1 ~ /^svc:/) nm=$1 + if ($1 == "State:" && $2 == "maintenance") print nm + }'` + do + svcadm clear $s + done + sleep 1 + done +} + +# Sets the default firewall rules for a node (unless they're already set) +set_default_fw_rules() { + local fw_default_v + if [[ -f /var/fw/.default_rules_setup ]]; then + read fw_default_v < /var/fw/.default_rules_setup + else + fw_default_v=0 + fi + + # Handle empty files from before we started versioning default rules + if [[ -z $fw_default_v ]]; then + fw_default_v=1 + fi + + if [[ $fw_default_v -lt 1 ]]; then + /usr/sbin/fwadm add -f - <<RULES +{ + "rules": [ + { + "description": "allow all ICMPv4 types", + "rule": "FROM any TO all vms ALLOW icmp type all", + "enabled": true, + "global": true + } + ] +} +RULES + [[ $? -ne 0 ]] && return 1 + echo 1 > /var/fw/.default_rules_setup + fi + + if [[ $fw_default_v -lt 2 ]]; then + /usr/sbin/fwadm add -f - <<RULES +{ + "rules": [ + { + "description": "allow all ICMPv6 types", + "rule": "FROM any TO all vms ALLOW icmp6 type all", + "enabled": true, + "global": true + } + ] +} +RULES + [[ $? -ne 0 ]] && return 1 + echo 2 > /var/fw/.default_rules_setup + fi +} + +configure_fwadm() +{ + if [[ ! -d /var/log/fw/logs ]]; then + mkdir -p /var/log/fw/logs + mv /var/log/fw/*-*.log /var/log/fw/logs + fi + + # See also OS-2635 + if [[ -f /var/log/fw/fw.log.0 ]]; then + for file in /var/log/fw/fw.log.[0-9]*; do + mv ${file} "/var/log/fw/fwadm_$(uname -n)_$(stat -c "%y" ${file} | cut -d'.' -f1 | tr ' ' 'T').log" + done + fi + if [[ -f /var/log/fw/fw.log ]]; then + mv /var/log/fw/fw.log /var/log/fw/fwadm.log + fi + + if [[ ! -e /var/log/fw/fwadm.log ]]; then + touch /var/log/fw/fwadm.log + fi +} + +configure_vmadm() +{ + # ensure /var/log/vm exists for VM.log logs + mkdir -p /var/log/vm/logs + + # See also OS-2635 + if [[ -f /var/log/vm/vm.log.0 ]]; then + for file in /var/log/vm/vm.log.[0-9]*; do + mv ${file} "/var/log/vm/vmadm_$(uname -n)_$(stat -c "%y" ${file} | cut -d'.' -f1 | tr ' ' 'T').log" + done + fi + if [[ -f /var/log/vm/vm.log ]]; then + mv /var/log/vm/vm.log /var/log/vm/vmadm.log + fi + + # need to create this file so rotation works + if [[ ! -e /var/log/vm/vmadm.log ]]; then + touch /var/log/vm/vmadm.log + fi +} + +update_root_password() +{ + + enc_password=`nawk -F= '{ + if ($1 == "root_shadow") + print substr($2, 2, length($2) - 2) + }' /opt/smartdc/config/node.config` + + [[ -z "$enc_password" ]] && return 0 + + sed -e "s|^root:[^\:]*:|root:${enc_password}:|" /etc/shadow \ + >/etc/shadow.new \ + && chmod 400 /etc/shadow.new \ + && mv /etc/shadow.new /etc/shadow +} + +# Loads config file for the node. These are the config values from the headnode +# plus authorized keys and anything else we want. +# This function is only invoked on a compute node. +install_config() +{ + # On standalone machines we don't do this update + [[ -n $(/usr/bin/bootparams | grep "^standalone=true") ]] && return 0 + + load_sdc_config + + curl -k -o /tmp/node.config --silent \ + "http://${CONFIG_assets_admin_ip}/extra/joysetup/node.config" + + [[ ! -f /tmp/node.config ]] && return 0 + grep datacenter_name /tmp/node.config >/dev/null 2>&1 + if [ $? != 0 ]; then + # There is no valid config file served by the assets zone + rm -f /tmp/node.config + return 0 + fi + + # Install the file if the local copy is different + diff /tmp/node.config /opt/smartdc/config/node.config >/dev/null 2>&1 + if [ $? != 0 ]; then + printf "Updating config file\n" >/dev/console + mkdir -p /opt/smartdc/config + mv /tmp/node.config /opt/smartdc/config + update_root_password + else + rm -f /tmp/node.config + fi +} + +case "$1" in +'start') + + # Always setup socket filter no matter what happens next. + /sbin/soconfig -F datafilt datafilt prog '2:2:0,2:2:6,26:2:0,26:2:6' + + USBMOUNT= + + # If we're not importing the pools, we shouldn't try to setup as a headnode + # (since there'll be no zpool) + if /bin/bootparams | grep "^noimport=true" > /dev/null 2>&1; then + exit $SMF_EXIT_OK + fi + + # If we're a headnode, we'll not have AMQP args on the cmdline, and we want + # to run an initial_script first anyway. + if /bin/bootparams | grep "^headnode=true" > /dev/null 2>&1; then + USBMOUNT=/mnt/`svcprop -p joyentfs/usb_mountpoint svc:/system/filesystem/smartdc:default` + + # No config file (e.g. user quit during interactive configuration), so + # treat as if "noimport=true". + [[ ! -f $USBMOUNT/config ]] && exit $SMF_EXIT_OK + + initial_script=${USBMOUNT}/$(grep "^initial_script=" $USBMOUNT/config.inc/generic 2>/dev/null | cut -d'=' -f2-) + if [ -n ${initial_script} ] && [ -e ${initial_script} ]; then + # Execute the script + ${initial_script} + result=$? + if [ ${result} -eq 2 ]; then + # we're rebooting, no need to start ur + echo "REBOOTING!" >> /dev/console + enable_ur="false" + elif [ ${result} -ne 0 ]; then + echo "WARNING: initial_script failed with exit code [${result}]." + exit $SMF_EXIT_ERR_FATAL + fi + fi + elif /bin/bootparams | grep "^smartos=true" > /dev/null 2>&1; then + set_default_fw_rules + else + install_config + fi + + configure_fwadm + configure_vmadm + + if /bin/bootparams | grep "^headnode=true" > /dev/null 2>&1; then + /usr/sbin/umount $USBMOUNT + fi + + ;; + +'stop') + ;; + +*) + echo "Usage: $0 { start | stop }" + exit $SMF_EXIT_ERR_FATAL + ;; +esac +exit $SMF_EXIT_OK diff --git a/usr/src/cmd/svc/milestone/smartdc-init.xml b/usr/src/cmd/svc/milestone/smartdc-init.xml new file mode 100644 index 0000000000..266aac53df --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-init.xml @@ -0,0 +1,124 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2012 Joyent, Inc. All rights reserved. + Use is subject to license terms. + + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. +--> + +<service_bundle type='manifest' name='Joyent:joyent'> + +<!-- + This service imports the zone's zpool on bootup, before zones try to + start. It also sets up configuration data on that zpool, if necessary. +--> +<service + name='system/smartdc/init' + type='service' + version='1'> + + <create_default_instance enabled='true' /> + + <single_instance /> + + <!-- + dependency info + This depends on the system/zones svc which seems weird since that + svc runs late in boot because it depends on milestone/multi-user-server. + Our svc does the initial setup of the infrastructure zones on the + headnode so we need the system/zones svc to run first so that we can + install and boot the infrastructure zones. The reason this seems + weird is that this svc also sets up the zpool when we first boot, + before we reboot to install the infrastructure zones, so it might seem + like it should run early in boot, but it can't because of the zone + issue described above. + + The dependency on network/physical is needed since all of the earlier + svcs which have a dependency on network/physical are using optional_all. + That makes sense for those svcs since they can come up without a + network, but for SmartOS, by this point we require the network to be + there. + --> + <dependency + name='zones' + type='service' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/system/zones' /> + </dependency> + + <dependency + name='net-physical' + type='service' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/network/physical' /> + </dependency> + + <exec_method + type='method' + name='start' + exec='/lib/svc/method/smartdc-init %m' + timeout_seconds='0'> + </exec_method> + + <exec_method + type='method' + name='stop' + exec='/lib/svc/method/smartdc-init %m' + timeout_seconds='60'> + </exec_method> + + <property_group name='startd' type='framework'> + <propval name='duration' type='astring' value='transient' /> + </property_group> + + <!-- + The zpool holding the zones (this must be imported). + --> + <property_group name='config' type='application'> + <propval name='zpool' type='astring' value='zones' /> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> + Joyent live-image management service + </loctext> + </common_name> + <documentation> + <manpage title='zones' section='5' manpath='/usr/share/man' /> + <manpage + title='zpool' + section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> +</service> + +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/smartdc-ur b/usr/src/cmd/svc/milestone/smartdc-ur new file mode 100755 index 0000000000..2c32482368 --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-ur @@ -0,0 +1,79 @@ +#!/bin/bash +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 2010-2011 Joyent, Inc. All rights reserved. +# Use is subject to license terms. + +set -o xtrace + +. /lib/svc/share/smf_include.sh +. /lib/sdc/config.sh + +PATH=/usr/sbin:/usr/bin; export PATH + +if [[ -n "$(/bin/bootparams | grep "^standalone=true")" || + -n "$(/bin/bootparams | grep "^smartos=true")" ]]; then + # Standalone and SmartOS systems do not need ur + svcadm disable "${SMF_FMRI}" + exit $SMF_EXIT_OK +fi + +case "$1" in +'start') + # + # Grab AMQP parameters from the kernel command line or headnode config + # + + load_sdc_config + load_sdc_sysinfo + + if [[ -n ${SYSINFO_Bootparam_rabbitmq} ]]; then + rabbit=${SYSINFO_Bootparam_rabbitmq} + fi + if [[ -z ${rabbit} ]] && [[ -n ${CONFIG_rabbitmq} ]]; then + rabbit=${CONFIG_rabbitmq} + fi + + if [[ -z $rabbit ]]; then + echo "unable to find AMQP parameters!" + exit $SMF_EXIT_ERR_FATAL + fi + + export AMQP_LOGIN=$(echo $rabbit | cut -d: -f1) + export AMQP_PASSWORD=$(echo $rabbit | cut -d: -f2) + export AMQP_HOST=$(echo $rabbit | cut -d: -f3) + export AMQP_PORT=$(echo $rabbit | cut -d: -f4) + export NODE_PATH=/usr/node/node_modules + + /usr/bin/ctrun -l child -o noorphan /smartdc/ur-agent/ur-agent 2>&1 & + + ;; + +'stop') + ;; + +*) + echo "Usage: $0 { start | stop }" + exit $SMF_EXIT_ERR_FATAL + ;; +esac +exit $SMF_EXIT_OK diff --git a/usr/src/cmd/svc/milestone/smartdc-ur.xml b/usr/src/cmd/svc/milestone/smartdc-ur.xml new file mode 100644 index 0000000000..7ee78dcb02 --- /dev/null +++ b/usr/src/cmd/svc/milestone/smartdc-ur.xml @@ -0,0 +1,62 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. +--> + +<!-- + Copyright (c) 2014, Joyent, Inc. +--> + +<service_bundle type="manifest" name="ur"> + <service name="smartdc/agent/ur" type="service" version="0.0.1"> + + <create_default_instance enabled="true"/> + <single_instance/> + + <dependency name="smartdc-init" grouping="require_all" restart_on="error" type="service"> + <service_fmri value="svc:/milestone/single-user"/> + </dependency> + + <exec_method + type="method" + name="start" + exec="/lib/svc/method/smartdc-ur %m" + timeout_seconds="60"> + <method_context> + <method_credential user="root" group="staff"/> + </method_context> + </exec_method> + + <exec_method type="method" name="restart" exec=":kill" timeout_seconds="60"> + <method_context> + <method_credential user="root" group="staff"/> + </method_context> + </exec_method> + + <exec_method type="method" name="stop" exec=":kill" timeout_seconds="60"> + <method_context> + <method_credential user="root" group="staff"/> + </method_context> + </exec_method> + + <property_group name="startd" type="framework"> + <propval name="ignore_error" type="astring" value="core,signal"/> + </property_group> + + <property_group name="application" type="application"> + </property_group> + + <stability value="Evolving"/> + + <template> + <common_name> + <loctext xml:lang="C">Ur Agent (node)</loctext> + </common_name> + </template> + + </service> + +</service_bundle> diff --git a/usr/src/cmd/svc/milestone/sysidtool-net b/usr/src/cmd/svc/milestone/sysidtool-net new file mode 100755 index 0000000000..538d17bba1 --- /dev/null +++ b/usr/src/cmd/svc/milestone/sysidtool-net @@ -0,0 +1,5 @@ +#!/bin/sh + +# This exists solely to support the service on older SmartOS zone images. + +exit 0 diff --git a/usr/src/cmd/svc/milestone/sysidtool-system b/usr/src/cmd/svc/milestone/sysidtool-system new file mode 100755 index 0000000000..538d17bba1 --- /dev/null +++ b/usr/src/cmd/svc/milestone/sysidtool-system @@ -0,0 +1,5 @@ +#!/bin/sh + +# This exists solely to support the service on older SmartOS zone images. + +exit 0 diff --git a/usr/src/cmd/svc/profile/Makefile b/usr/src/cmd/svc/profile/Makefile index 2b00542413..8c44470471 100644 --- a/usr/src/cmd/svc/profile/Makefile +++ b/usr/src/cmd/svc/profile/Makefile @@ -23,6 +23,7 @@ # Copyright 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # Copyright 2019 Peter Tribble. +# Copyright 2019 Joyent, Inc. # include ../../Makefile.cmd @@ -32,6 +33,7 @@ FILEMODE = 0444 ROOTPROFILE = $(ROOT)/etc/svc/profile PROFILESRCS = \ + generic.xml \ generic_open.xml \ generic_limited_net.xml \ inetd_generic.xml \ @@ -60,7 +62,7 @@ TEST = /usr/bin/test LISTSVCS = listsvcs.pl install: all $(PROFILES) - $(RM) $(ROOTPROFILE)/platform.xml + $(CP) platform_none.xml $(ROOTPROFILE)/platform.xml # SUNW,Sun-Fire-V890 $(RM) $(ROOTPROFILE)/platform_SUNW,Sun-Fire-V890.xml $(LN) $(ROOTPROFILE)/platform_SUNW,Sun-Fire-880.xml \ @@ -87,7 +89,7 @@ $(CHECK_OPEN) $(CHECK_LMTD): \ @$(COMM) -23 $@.enabled $@.all | $(TEE) $@.notcovered @$(TEST) ! -s $@.notcovered && $(TOUCH) $@ -lint _msg: +_msg: clobber clean: $(RM) $(CHECK_OPEN)* $(CHECK_LMTD)* diff --git a/usr/src/cmd/svc/profile/generic.xml b/usr/src/cmd/svc/profile/generic.xml new file mode 100644 index 0000000000..61232545dd --- /dev/null +++ b/usr/src/cmd/svc/profile/generic.xml @@ -0,0 +1,397 @@ +<?xml version='1.0'?> +<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'> +<!-- + CDDL HEADER START + + The contents of this file are subject to the terms of the + Common Development and Distribution License (the "License"). + You may not use this file except in compliance with the License. + + You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + or http://www.opensolaris.org/os/licensing. + See the License for the specific language governing permissions + and limitations under the License. + + When distributing Covered Code, include this CDDL HEADER in each + file and include the License file at usr/src/OPENSOLARIS.LICENSE. + If applicable, add the following below this CDDL HEADER, with the + fields enclosed by brackets "[]" replaced with your own identifying + information: Portions Copyright [yyyy] [name of copyright owner] + + CDDL HEADER END + + Copyright 2010 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + Copyright 2019 Joyent, Inc. + + The purpose of the limited_net profile is to provide a set of + active services that allow one to connect to the machine via ssh + (requires sshd). The services which are deactivated here are those + that are at odds with this goal. Those which are activated are + explicit requirements for the goal's satisfaction. + + NOTE: Service profiles delivered by this package are not editable, + and their contents will be overwritten by package or patch + operations, including operating system upgrade. Make customizations + in a distinct file. The paths, /etc/svc/profile/site.xml and + /var/svc/profile/site.xml, are distinguished locations for site-specific + service profile, treated otherwise equivalently to this file. +--> +<service_bundle type='profile' name='generic_limited_net' + xmlns:xi='http://www.w3.org/2003/XInclude' > + + <!-- + svc.startd(1M) services + --> + <service name='system/cryptosvc' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/coreadm' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/metainit' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='system/cron' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/dbus' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/extended-accounting' version='1' type='service'> + <instance name='flow' enabled='false'/> + <instance name='process' enabled='false'/> + <instance name='task' enabled='false'/> + <instance name='net' enabled='false'/> + </service> + <service name='system/identity' version='1' type='service'> + <instance name='domain' enabled='true'/> + </service> + <service name='system/intrd' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/keymap' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/picl' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/sac' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='system/scheduler' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/system-log' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/utmp' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/zones' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/rcap' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/hotplug' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='network/rpc/bind' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='system/name-service-cache' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='network/netmask' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/status' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/nlockmgr' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/client' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/server' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/rquota' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/cbd' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/nfs/mapid' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/smb/client' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='network/ssh' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='network/smtp' version='1' type='service'> + <instance name='sendmail' enabled='false'/> + </service> + <service name='network/sendmail-client' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/inetd' version='1' type='restarter'> + <instance name='default' enabled='true'/> + </service> + <service name='system/filesystem/autofs' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='system/filesystem/rmvolmgr' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='system/power' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + + <service name='network/dns/multicast' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + <service name='network/dhcp-server' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/ntp' version='1' type='service'> + <instance name='default' enabled='true' /> + </service> + <service name='network/rarp' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/slp' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/security/kadmin' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/security/krb5_prop' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/security/krb5kdc' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + + <service name='application/management/net-snmp' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/management/seaport' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/management/snmpdx' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/management/wbem' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/print/ipp-listener' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/print/ppd-cache-update' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/print/rfc1179' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/cups/in-lpd' version='1' type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='application/stosreg' version='1' type='service'> + <instance name='default' enabled='true' /> + </service> + + <!-- + default inetd(1M) services + --> + <service name='network/finger' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/ftp' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/login' version='1' type='service'> + <instance name='rlogin' enabled='false'/> + <instance name='klogin' enabled='false'/> + <instance name='eklogin' enabled='false'/> + </service> + <service name='network/shell' version='1' type='service'> + <instance name='default' enabled='false'/> + <instance name='kshell' enabled='false'/> + </service> + <service name='network/telnet' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + non-default inetd(1M) services + --> + <service name='network/uucp' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/chargen' version='1' type='service'> + <instance name='stream' enabled='false'/> + <instance name='dgram' enabled='false'/> + </service> + <service name='network/daytime' version='1' type='service'> + <instance name='stream' enabled='false'/> + <instance name='dgram' enabled='false'/> + </service> + <service name='network/discard' version='1' type='service'> + <instance name='stream' enabled='false'/> + <instance name='dgram' enabled='false'/> + </service> + <service name='network/echo' version='1' type='service'> + <instance name='stream' enabled='false'/> + <instance name='dgram' enabled='false'/> + </service> + <service name='network/time' version='1' type='service'> + <instance name='stream' enabled='false'/> + <instance name='dgram' enabled='false'/> + </service> + <service name='network/comsat' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rexec' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/talk' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/stdiscover' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/stlisten' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + default inetd(1M) RPC services enabled + --> + <service name='network/rpc/gss' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/mdcomm' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/smserver' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/security/ktkt_warn' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + default inetd(1M) RPC services disabled + --> + <service name='network/rpc/rstat' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/rusers' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/meta' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/metamed' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/metamh' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + non-default inetd(1M) RPC services disabled + --> + <service name='network/rpc/rex' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/spray' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + <service name='network/rpc/wall' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + Disable Avahi mDNS bridge service + --> + <service name='system/avahi-bridge-dsd' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + Enable CDE/ToolTalk/GDM services. + --> + <service name='network/rpc/cde-ttdbserver' version='1' type='service'> + <instance name='tcp' enabled='false' /> + </service> + <service name='application/graphical-login/gdm' version='1' + type='service'> + <instance name='default' enabled='false' /> + </service> + <service name='network/rpc/cde-calendar-manager' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + Disable X11 services. + --> + <service name='application/x11/xfs' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + pkg.depotd service + --> + <service name='system/pkgserv' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <!-- + Enable VNC config service for xVM + --> + <service name='system/xvm/vnc-config' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='system/xvm/ipagent' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='application/print/service-selector' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='system/dumpadm' version='1' type='service'> + <instance name='default' enabled='true'/> + </service> + + <service name='system/metasync' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='application/font/fc-cache' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='system/consolekit' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='application/opengl/ogl-select' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + <service name='system/hal' version='1' type='service'> + <instance name='default' enabled='false'/> + </service> + + +</service_bundle> diff --git a/usr/src/cmd/svc/shell/smf_include.sh b/usr/src/cmd/svc/shell/smf_include.sh index 4acbed7d90..a5e3431d5b 100644 --- a/usr/src/cmd/svc/shell/smf_include.sh +++ b/usr/src/cmd/svc/shell/smf_include.sh @@ -22,6 +22,7 @@ # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2012 Joyent, Inc. All rights reserved. # Copyright 2015 Nexenta Systems, Inc. All rights reserved. # Copyright 2012 Joyent, Inc. All rights reserved. # diff --git a/usr/src/cmd/svc/startd/graph.c b/usr/src/cmd/svc/startd/graph.c index f63be7f941..d514b19c38 100644 --- a/usr/src/cmd/svc/startd/graph.c +++ b/usr/src/cmd/svc/startd/graph.c @@ -145,6 +145,8 @@ #include <assert.h> #include <errno.h> #include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> #include <fm/libfmevent.h> #include <libscf.h> #include <libscf_priv.h> @@ -4846,6 +4848,20 @@ vertex_subgraph_dependencies_shutdown(scf_handle_t *h, graph_vertex_t *v, was_up = up_state(old_state); now_up = up_state(v->gv_state); + if (halting != -1 && old_state == RESTARTER_STATE_DISABLED && + v->gv_state != RESTARTER_STATE_DISABLED) { + /* + * We're halting and we have a svc which is transitioning to + * offline in parallel. This leads to a race condition where + * gt_enter_offline might re-enable the svc after we disabled + * it. Since we're halting, we want to ensure no svc ever + * transitions out of the disabled state. In this case, modify + * the flags to keep us on the halting path. + */ + was_up = 0; + now_up = 0; + } + if (!was_up && now_up) { ++non_subgraph_svcs; } else if (was_up && !now_up) { @@ -6785,6 +6801,9 @@ repository_event_thread(void *unused) char *fmri = startd_alloc(max_scf_fmri_size); char *pg_name = startd_alloc(max_scf_value_size); int r; + int fd; + + (void) pthread_setname_np(pthread_self(), "repository_event"); (void) pthread_setname_np(pthread_self(), "repository_event"); @@ -6809,6 +6828,14 @@ retry: goto retry; } + if ((fd = open("/etc/svc/volatile/startd.ready", O_RDONLY | O_CREAT, + S_IRUSR)) < 0) { + log_error(LOG_WARNING, "Couldn't create startd.ready file\n", + SCF_GROUP_FRAMEWORK, scf_strerror(scf_error())); + } else { + (void) close(fd); + } + /*CONSTCOND*/ while (1) { ssize_t res; diff --git a/usr/src/cmd/svc/startd/method.c b/usr/src/cmd/svc/startd/method.c index 1c5549179c..2629b74210 100644 --- a/usr/src/cmd/svc/startd/method.c +++ b/usr/src/cmd/svc/startd/method.c @@ -100,34 +100,18 @@ static uint_t method_events[] = { * method_record_start(restarter_inst_t *) * Record a service start for rate limiting. Place the current time * in the circular array of instance starts. + * + * Save the critical_failure_period and critical_failure_allowed with either + * the defaults or the svc properties startd/critical_failure_count and + * startd/critical_failure_period. + * ri_crit_fail_allowed is capped at RINST_START_TIMES. */ static void method_record_start(restarter_inst_t *inst) { - int index = inst->ri_start_index++ % RINST_START_TIMES; - - inst->ri_start_time[index] = gethrtime(); -} - -/* - * method_rate_critical(restarter_inst_t *) - * Return true if the average start interval is less than the permitted - * interval. The implicit interval defaults to RINST_FAILURE_RATE_NS and - * RINST_START_TIMES but may be overridden with the svc properties - * startd/critical_failure_count and startd/critical_failure_period - * which represent the number of failures to consider and the amount of - * time in seconds in which that number may occur, respectively. Note that - * this time is measured as of the transition to 'enabled' rather than wall - * clock time. - * Implicit success if insufficient measurements for an average exist. - */ -int -method_rate_critical(restarter_inst_t *inst) -{ + int index; + uint_t critical_failure_allowed = RINST_START_TIMES; hrtime_t critical_failure_period; - uint_t critical_failure_count = RINST_START_TIMES; - uint_t n = inst->ri_start_index; - hrtime_t avg_ns = 0; uint64_t scf_fr, scf_st; scf_propvec_t *prop = NULL; scf_propvec_t restart_critical[] = { @@ -151,17 +135,48 @@ method_rate_critical(restarter_inst_t *inst) * in seconds but tracked in ns */ critical_failure_period = (hrtime_t)scf_fr * NANOSEC; - critical_failure_count = (uint_t)scf_st; + critical_failure_allowed = (uint_t)scf_st; + + if (critical_failure_allowed > RINST_START_TIMES) + critical_failure_allowed = RINST_START_TIMES; + if (critical_failure_allowed < 1) + critical_failure_allowed = 1; + } - if (inst->ri_start_index < critical_failure_count) + + inst->ri_crit_fail_allowed = critical_failure_allowed; + inst->ri_crit_fail_period = critical_failure_period; + + index = inst->ri_start_index++ % critical_failure_allowed; + inst->ri_start_time[index] = gethrtime(); +} + +/* + * method_rate_critical(restarter_inst_t *) + * Return true if the number of failures within the interval + * ri_crit_fail_period exceeds ri_crit_fail_allowed. The allowed failure + * count defaults to RINST_START_TIMES and the implicit interval defaults + * to RINST_FAILURE_RATE_NS but may be overridden with the svc properties + * startd/critical_failure_count and startd/critical_failure_period which + * represent the acceptable number of failures and the amount of time in + * seconds in which that number may occur, respectively. Note that this time + * is measured as of the transition to 'enabled' rather than wall clock + * time. Implicitly not critical if insufficient failures have occured. + */ +int +method_rate_critical(restarter_inst_t *inst) +{ + uint_t n = inst->ri_start_index; + uint_t fail_allowed = inst->ri_crit_fail_allowed; + hrtime_t diff_ns; + + if (n < fail_allowed) return (0); - avg_ns = - (inst->ri_start_time[(n - 1) % critical_failure_count] - - inst->ri_start_time[n % critical_failure_count]) / - (critical_failure_count - 1); + diff_ns = inst->ri_start_time[(n - 1) % fail_allowed] - + inst->ri_start_time[n % fail_allowed]; - return (avg_ns < critical_failure_period); + return (diff_ns < inst->ri_crit_fail_period); } /* diff --git a/usr/src/cmd/svc/startd/startd.h b/usr/src/cmd/svc/startd/startd.h index d230ed1490..df3e98a27b 100644 --- a/usr/src/cmd/svc/startd/startd.h +++ b/usr/src/cmd/svc/startd/startd.h @@ -398,7 +398,7 @@ typedef enum { #define RINST_RETAKE_MASK 0x0f000000 -#define RINST_START_TIMES 5 /* failures to consider */ +#define RINST_START_TIMES 10 /* up to 10 fails to consider */ #define RINST_FAILURE_RATE_NS 600000000000LL /* 1 failure/10 minutes */ #define RINST_WT_SVC_FAILURE_RATE_NS NANOSEC /* 1 failure/second */ @@ -420,6 +420,8 @@ typedef struct restarter_inst { hrtime_t ri_start_time[RINST_START_TIMES]; uint_t ri_start_index; /* times started */ + uint_t ri_crit_fail_allowed; + hrtime_t ri_crit_fail_period; uu_list_node_t ri_link; pthread_mutex_t ri_lock; diff --git a/usr/src/cmd/svc/svcadm/Makefile b/usr/src/cmd/svc/svcadm/Makefile index 07d22a2226..fae2142dac 100644 --- a/usr/src/cmd/svc/svcadm/Makefile +++ b/usr/src/cmd/svc/svcadm/Makefile @@ -21,6 +21,7 @@ # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2012, Joyent, Inc. All rights reserved. # PROG = svcadm @@ -49,7 +50,11 @@ $(PROG): $(OBJS) $(POFILE): $(POFILES) cat $(POFILES) > $(POFILE) -install: all $(ROOTUSRSBINPROG) +install: all $(ROOTSBINPROG) $(ROOTUSRSBINPROG) + +$(ROOTUSRSBINPROG): + -$(RM) $@ + -$(SYMLINK) ../../sbin/$(PROG) $@ clean: $(RM) $(OBJS) diff --git a/usr/src/cmd/svc/svccfg/Makefile b/usr/src/cmd/svc/svccfg/Makefile index 16312b56f7..ece4cb765e 100644 --- a/usr/src/cmd/svc/svccfg/Makefile +++ b/usr/src/cmd/svc/svccfg/Makefile @@ -107,6 +107,7 @@ lint := SVCCFG_EXTRA_LIBS = -lscf -ll -luutil -lumem -lmd5 -lnvpair LDLIBS += $(SVCCFG_EXTRA_LIBS) +$(NATIVE_BUILD)NATIVE_LIBS += libl.so libumem.so libmd5.so libnvpair.so libc.so $(NATIVE_BUILD)CC = $(NATIVECC) $(NATIVE_BUILD)LD = $(NATIVELD) $(NATIVE_BUILD)CFLAGS = $(NATIVE_CFLAGS) diff --git a/usr/src/cmd/svc/svcs/Makefile b/usr/src/cmd/svc/svcs/Makefile index aac8f82e0a..10cbbe2127 100644 --- a/usr/src/cmd/svc/svcs/Makefile +++ b/usr/src/cmd/svc/svcs/Makefile @@ -34,7 +34,7 @@ include ../../Makefile.cmd include ../../Makefile.ctf POFILE = $(PROG)_all.po -LDLIBS += -lcontract -lscf -luutil -lumem -lnvpair -lzonecfg -lproc +LDLIBS += -lcontract -lscf -luutil -lumem -lnvpair -lzonecfg -lsasl -lproc CPPFLAGS += -I ../common lint := LINTFLAGS = -mux diff --git a/usr/src/cmd/svc/svcs/explain.c b/usr/src/cmd/svc/svcs/explain.c index 331a5375fd..8a8dfa043b 100644 --- a/usr/src/cmd/svc/svcs/explain.c +++ b/usr/src/cmd/svc/svcs/explain.c @@ -196,6 +196,7 @@ static char *emsg_invalid_dep; extern scf_handle_t *h; extern char *g_zonename; +extern char *g_zonealias; /* ARGSUSED */ static int @@ -2017,6 +2018,9 @@ print_service(inst_t *svcp, int verbose) if (g_zonename != NULL) (void) printf(gettext(" Zone: %s\n"), g_zonename); + if (g_zonealias != NULL) + (void) printf(gettext(" Alias: %s\n"), g_zonealias); + stime = svcp->stime.tv_sec; tmp = localtime(&stime); diff --git a/usr/src/cmd/svc/svcs/svcs.c b/usr/src/cmd/svc/svcs/svcs.c index b6cf4f7fd1..402b40f4ca 100644 --- a/usr/src/cmd/svc/svcs/svcs.c +++ b/usr/src/cmd/svc/svcs/svcs.c @@ -60,6 +60,7 @@ #include <sys/ctfs.h> #include <sys/stat.h> +#include <sasl/saslutil.h> #include <assert.h> #include <errno.h> #include <fcntl.h> @@ -138,6 +139,9 @@ static int first_paragraph = 1; /* For -l mode. */ static char *common_name_buf; /* Sized for maximal length value. */ char *locale; /* Current locale. */ char *g_zonename; /* zone being operated upon */ +char *g_zonealias; /* alias for zone, if any */ +static char g_aliasdec[MAXPATHLEN / 4 * 3]; /* decoded zone alias buffer */ +static char g_aliasbuf[MAXPATHLEN]; /* base64 encoded zone alias buffer */ /* * Pathname storage for path generated from the fmri. @@ -244,7 +248,25 @@ ht_free(void) static void ht_init(void) { - assert(ht_buckets == NULL); + if (ht_buckets != NULL) { + /* + * If we already have a hash table (e.g., because we are + * processing multiple zones), destroy it before creating + * a new one. + */ + struct ht_elem *elem, *next; + int i; + + for (i = 0; i < ht_buckets_num; i++) { + for (elem = ht_buckets[i]; elem != NULL; elem = next) { + next = elem->next; + free((char *)elem->fmri); + free(elem); + } + } + + free(ht_buckets); + } ht_buckets_num = 8; ht_buckets = safe_malloc(sizeof (*ht_buckets) * ht_buckets_num); @@ -3684,6 +3706,24 @@ again: assert(opt_zone == NULL || zids == NULL); if (opt_zone == NULL) { + zone_status_t status; + + if (zone_getattr(zids[zent], ZONE_ATTR_STATUS, + &status, sizeof (status)) < 0 || + status != ZONE_IS_RUNNING) { + /* + * If this zone is not running or we cannot + * get its status, we do not want to attempt + * to bind an SCF handle to it, lest we + * accidentally interfere with a zone that + * is not yet running by looking up a door + * to its svc.configd (which could potentially + * block a mount with an EBUSY). + */ + zent++; + goto nextzone; + } + if (getzonenamebyid(zids[zent++], zonename, sizeof (zonename)) < 0) { uu_warn(gettext("could not get name for " @@ -3706,18 +3746,46 @@ again: uu_die(gettext("invalid zone '%s'\n"), g_zonename); scf_value_destroy(zone); + + /* + * On SmartOS, there may be a base64-encoded string attribute + * named 'alias' associated with this zone. This alias is + * useful, so we attempt to make it available when we are + * displaying -xZ output. If it's not available or not + * decodable, we just ignore it. + */ + if (g_zonename != NULL) { + unsigned len; + struct zone_attrtab zattrs; + zone_dochandle_t zhdl = zonecfg_init_handle(); + + bzero(&zattrs, sizeof (zattrs)); + (void) strcpy(zattrs.zone_attr_name, "alias"); + + if (zhdl != NULL && + zonecfg_get_handle(g_zonename, zhdl) == Z_OK && + zonecfg_lookup_attr(zhdl, &zattrs) == Z_OK && + zonecfg_get_attr_string(&zattrs, g_aliasbuf, + sizeof (g_aliasbuf)) == Z_OK && + sasl_decode64(g_aliasbuf, strlen(g_aliasbuf), + g_aliasdec, sizeof (g_aliasdec), &len) == SASL_OK) { + g_aliasdec[len] = '\0'; + g_zonealias = g_aliasdec; + } else { + g_zonealias = NULL; + } + zonecfg_fini_handle(zhdl); + } } if (scf_handle_bind(h) == -1) { if (g_zonename != NULL) { - uu_warn(gettext("Could not bind to repository " + if (show_zones) + goto nextzone; + + uu_die(gettext("Could not bind to repository " "server for zone %s: %s\n"), g_zonename, scf_strerror(scf_error())); - - if (!show_zones) - return (UU_EXIT_FATAL); - - goto nextzone; } uu_die(gettext("Could not bind to repository server: %s. " @@ -3776,7 +3844,7 @@ again: if (opt_mode == 'L') { if ((err = scf_walk_fmri(h, argc, argv, SCF_WALK_MULTIPLE, - print_log, NULL, &exit_status, uu_warn)) != 0) { + print_log, NULL, errarg, errfunc)) != 0) { uu_warn(gettext("failed to iterate over " "instances: %s\n"), scf_strerror(err)); exit_status = UU_EXIT_FATAL; diff --git a/usr/src/cmd/syslogd/syslog.conf b/usr/src/cmd/syslogd/syslog.conf index 1ed7a40d39..a894b3ed90 100644 --- a/usr/src/cmd/syslogd/syslog.conf +++ b/usr/src/cmd/syslogd/syslog.conf @@ -54,3 +54,7 @@ user.err /var/adm/messages user.alert `root, operator' user.emerg * ) + +auth.info /var/log/auth.log +mail.info /var/log/postfix.log +#local0.info /var/log/courier.log diff --git a/usr/src/cmd/syslogd/system-log b/usr/src/cmd/syslogd/system-log index b7608219fc..7f7ad92d96 100644 --- a/usr/src/cmd/syslogd/system-log +++ b/usr/src/cmd/syslogd/system-log @@ -21,10 +21,12 @@ # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. +# Copyright 2016, Joyent, Inc. All rights reserved. # -# ident "%Z%%M% %I% %E% SMI" OLD_CONF=/etc/default/syslogd +DFLT_SUM="7c87d765805cc45a0dad9fb5c86cc4f2" +CONF_SUM="$DFLT_SUM" . /lib/svc/share/smf_include.sh @@ -52,14 +54,72 @@ convert() rm -f ${OLD_CONF}.new } -if [ ! -f /etc/syslog.conf ]; then - echo "/etc/syslog.conf is missing. Exiting." - exit $SMF_EXIT_ERR_CONFIG +# +# If the rsyslog.conf file doesn't exist and the syslog.conf file has not +# been customized, generate the default rsyslog.conf. +# +generate_rsyslog_conf() +{ + if [ ! -f /etc/syslog.conf ]; then + return + fi + + CONF_SUM=`nawk '{ + if (substr($1, 1, 1) != "#" && NF != 0) print $0 + }' /etc/syslog.conf | sum -x md5` + + if [ "$CONF_SUM" != "$DFLT_SUM" ]; then + return + fi + + cat <<-DONE >/etc/rsyslog.conf + # + # Sample rsyslog configuration file + # + + $ModLoad immark + $ModLoad imsolaris + $ModLoad imtcp + $ModLoad imudp + + *.err;kern.notice;auth.notice /dev/sysmsg + *.err;kern.debug;daemon.notice;mail.crit /var/adm/messages + + *.alert;kern.err;daemon.err operator + *.alert root + + *.emerg * + + mail.debug /var/log/syslog + + auth.info /var/log/auth.log + mail.info /var/log/postfix.log + DONE +} + +if [ ! -f /etc/rsyslog.conf -a -x /usr/sbin/rsyslogd ]; then + generate_rsyslog_conf fi -if [ ! -x /usr/sbin/syslogd ]; then - echo "Executable /usr/sbin/syslogd not found. Exiting" - exit $SMF_EXIT_ERR_CONFIG +if [ -f /etc/rsyslog.conf ]; then + if [ ! -x /usr/sbin/rsyslogd ]; then + echo "Executable /usr/sbin/rsyslogd not found or not executable. Exiting." + exit $SMF_EXIT_ERR_CONFIG + fi +else + # + # Fall back to old syslogd + # + echo "/etc/rsyslog.conf is missing. Using syslogd instead." + if [ ! -f /etc/syslog.conf ]; then + echo "/etc/syslog.conf is missing. Exiting." + exit $SMF_EXIT_ERR_CONFIG + fi + + if [ ! -x /usr/sbin/syslogd ]; then + echo "Executable /usr/sbin/syslogd not found or not executable. Exiting." + exit $SMF_EXIT_ERR_CONFIG + fi fi if smf_is_globalzone; then @@ -80,6 +140,27 @@ if [ ! -f /var/adm/messages ]; then /usr/bin/cp /dev/null /var/adm/messages /usr/bin/chmod 0644 /var/adm/messages fi + +if [ -f /etc/rsyslog.conf ]; then + # + # If we have a config file for the old syslogd, only run rsyslogd + # if there are no customizations in the old syslog.conf file. + # + if [ -f /etc/syslog.conf ]; then + CONF_SUM=`nawk '{ + if (substr($1, 1, 1) != "#" && NF != 0) print $0 + }' /etc/syslog.conf | sum -x md5` + fi + + if [ "$CONF_SUM" == "$DFLT_SUM" ]; then + /usr/sbin/rsyslogd -c5 -n & + exit $SMF_EXIT_OK + fi +fi + +# +# Run the old syslogd +# remote=`awk -F= ' /^LOG_FROM_REMOTE=[yY][Ee][Ss]/ {print "true"} /^LOG_FROM_REMOTE=[Nn][Oo]/ {print "false"}' < ${OLD_CONF}` @@ -96,3 +177,10 @@ case ${remote} in esac /usr/sbin/syslogd >/dev/msglog 2>&1 & + +if [ -f /etc/rsyslog.conf ]; then + logger -p daemon.err \ + "Not using rsyslogd because there is a custom /etc/syslog.conf file" +fi + +exit $SMF_EXIT_OK diff --git a/usr/src/cmd/tail/Makefile b/usr/src/cmd/tail/Makefile index 6f50c71fb7..7afc25b187 100644 --- a/usr/src/cmd/tail/Makefile +++ b/usr/src/cmd/tail/Makefile @@ -22,6 +22,7 @@ OBJS= forward.o misc.o read.o reverse.o tail.o SRCS= $(OBJS:%.o=%.c) include ../Makefile.cmd +include ../Makefile.ctf CLOBBERFILES= $(PROG) CPPFLAGS += -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 @@ -46,6 +47,10 @@ $(PROG): $(OBJS) $(LINK.c) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) +%.o: %.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + install: all .WAIT $(ROOTPROG) $(ROOTXPG4PROG) $(ROOTXPG4PROG): diff --git a/usr/src/cmd/truss/print.c b/usr/src/cmd/truss/print.c index 6a77a9d9ce..c679988bd5 100644 --- a/usr/src/cmd/truss/print.c +++ b/usr/src/cmd/truss/print.c @@ -874,7 +874,9 @@ prt_mc4(private_t *pri, int raw, long val) /* print memcntl() (4th) argument */ return; case MC_SYNC: - if ((val & ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE)) == 0) { + if ((val & + ~(MS_SYNC|MS_ASYNC|MS_INVALIDATE|MS_INVALCURPROC)) + == 0) { *(s = pri->code_buf) = '\0'; if (val & MS_SYNC) (void) strlcat(s, "|MS_SYNC", CBSIZE); @@ -883,6 +885,9 @@ prt_mc4(private_t *pri, int raw, long val) /* print memcntl() (4th) argument */ if (val & MS_INVALIDATE) (void) strlcat(s, "|MS_INVALIDATE", CBSIZE); + if (val & MS_INVALCURPROC) + (void) strlcat(s, "|MS_INVALCURPROC", + CBSIZE); } break; @@ -2097,6 +2102,8 @@ udp_optname(private_t *pri, long val) case UDP_EXCLBIND: return ("UDP_EXCLBIND"); case UDP_RCVHDR: return ("UDP_RCVHDR"); case UDP_NAT_T_ENDPOINT: return ("UDP_NAT_T_ENDPOINT"); + case UDP_SRCPORT_HASH: return ("UDP_SRCPORT_HASH"); + case UDP_SND_TO_CONNECTED: return ("UDP_SND_TO_CONNECTED"); default: (void) snprintf(pri->code_buf, sizeof (pri->code_buf), "0x%lx", @@ -2545,7 +2552,7 @@ prt_zga(private_t *pri, int raw, long val) case ZONE_ATTR_BOOTARGS: s = "ZONE_ATTR_BOOTARGS"; break; case ZONE_ATTR_BRAND: s = "ZONE_ATTR_BRAND"; break; case ZONE_ATTR_FLAGS: s = "ZONE_ATTR_FLAGS"; break; - case ZONE_ATTR_PHYS_MCAP: s = "ZONE_ATTR_PHYS_MCAP"; break; + case ZONE_ATTR_DID: s = "ZONE_ATTR_DID"; break; } } diff --git a/usr/src/cmd/truss/systable.c b/usr/src/cmd/truss/systable.c index 343d18a120..9e4d1780f4 100644 --- a/usr/src/cmd/truss/systable.c +++ b/usr/src/cmd/truss/systable.c @@ -29,6 +29,9 @@ /* Copyright (c) 2013, OmniTI Computer Consulting, Inc. All rights reserved. */ +/* + * Copyright (c) 2014, Joyent, Inc. All rights reserved. + */ #include <stdio.h> #include <stdlib.h> #include <fcntl.h> @@ -1716,9 +1719,10 @@ const char * const afcodes[] = { "POLICY", /* 29 */ "RDS", /* 30 */ "TRILL", /* 31 */ - "PACKET" /* 32 */ + "PACKET", /* 32 */ + "LX_NETLINK" /* 33 */ }; -#if MAX_AFCODES != 33 +#if MAX_AFCODES != 34 #error Need to update address-family table #endif diff --git a/usr/src/cmd/varpd/Makefile b/usr/src/cmd/varpd/Makefile new file mode 100644 index 0000000000..ea37ac6f71 --- /dev/null +++ b/usr/src/cmd/varpd/Makefile @@ -0,0 +1,75 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2018 Joyent, Inc. +# + +PROG= varpd +OBJS = varpd.o +SRCS = $(OBJS:%.o=../%.c) +MANIFEST = varpd.xml +ROOTLIBVARPD = $(ROOTLIB)/varpd +ROOTLIBVARPDPROG= $(PROG:%=$(ROOTLIBVARPD)/%) + + +include ../Makefile.cmd +include ../Makefile.ctf + +ROOTMANIFESTDIR= $(ROOTSVCNETWORK) + +CLEANFILES += $(OBJS) +CPPFLAGS += -D_REENTRANT +CFLAGS += $(CCVERBOSE) +LDLIBS += -lvarpd -lumem -lscf +$(NOT_RELEASE_BUILD)CPPFLAGS += -DDEBUG + +# +# Our debug only umem related functions cause lint to get confused. +# +LINTFLAGS += -erroff=E_NAME_DEF_NOT_USED2 + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +clean: + -$(RM) $(CLEANFILES) + +lint: lint_PROG + +%.o: ../%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +check: $(CHKMANIFEST) + +clobber: clean + $(RM) $(PROG) + +install: $(PROG) $(ROOTLIBVARPDPROG) $(ROOTMANIFEST) + +$(ROOTLIBVARPD): + $(INS.dir) + +$(ROOTLIBVARPD)/%: % $(ROOTLIBVARPD) + $(INS.file) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/cmd/varpd/varpd.c b/usr/src/cmd/varpd/varpd.c new file mode 100644 index 0000000000..896f39733a --- /dev/null +++ b/usr/src/cmd/varpd/varpd.c @@ -0,0 +1,526 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2018 Joyent, Inc. + */ + +/* + * virtual arp daemon -- varpd + * + * The virtual arp daemon is the user land counterpart to the overlay driver. To + * truly understand its purpose and how it fits into things, you should read the + * overlay big theory statement in uts/common/io/overlay/overlay.c. + * + * varod's purpose it to provide a means for looking up the destination on the + * underlay network for a host on an overlay network and to also be a door + * server such that dladm(1M) via libdladm can configure and get useful status + * information. The heavy lifting is all done by libvarpd and the various lookup + * plugins. + * + * When varpd first starts up, we take of chdiring into /var/run/varpd, which is + * also where we create /var/run/varpd.door, our door server. After that we + * daemonize and only after we daemonize do we go ahead and load plugins. The + * reason that we don't load plugins before daemonizing is that they could very + * well be creating threads and thus lose them all. In general, we want to make + * things easier on our children and not require them to be fork safe. + * + * Once it's spun up, the main varpd thread sits in sigsuspend and really just + * hangs out waiting for something, libvarpd handles everything else. + */ + +#include <libvarpd.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <libgen.h> +#include <stdarg.h> +#include <stdlib.h> +#include <paths.h> +#include <limits.h> +#include <sys/corectl.h> +#include <signal.h> +#include <strings.h> +#include <sys/wait.h> +#include <unistd.h> +#include <thread.h> +#include <priv.h> +#include <libscf.h> + +#define VARPD_EXIT_REQUESTED SMF_EXIT_OK +#define VARPD_EXIT_FATAL SMF_EXIT_ERR_FATAL +#define VARPD_EXIT_USAGE SMF_EXIT_ERR_CONFIG + +#define VARPD_RUNDIR "/var/run/varpd" +#define VARPD_DEFAULT_DOOR "/var/run/varpd/varpd.door" + +#define VARPD_PG "varpd" +#define VARPD_PROP_INC "include_path" + +static varpd_handle_t *varpd_handle; +static const char *varpd_pname; +static volatile boolean_t varpd_exit = B_FALSE; + +/* + * Debug builds are automatically wired up for umem debugging. + */ +#ifdef DEBUG +const char * +_umem_debug_init() +{ + return ("default,verbose"); +} + +const char * +_umem_logging_init(void) +{ + return ("fail,contents"); +} +#endif /* DEBUG */ + +static void +varpd_vwarn(FILE *out, const char *fmt, va_list ap) +{ + int error = errno; + + (void) fprintf(out, "%s: ", varpd_pname); + (void) vfprintf(out, fmt, ap); + + if (fmt[strlen(fmt) - 1] != '\n') + (void) fprintf(out, ": %s\n", strerror(error)); +} + +static void +varpd_fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + varpd_vwarn(stderr, fmt, ap); + va_end(ap); + + exit(VARPD_EXIT_FATAL); +} + +static void +varpd_dfatal(int dfd, const char *fmt, ...) +{ + int status = VARPD_EXIT_FATAL; + va_list ap; + + va_start(ap, fmt); + varpd_vwarn(stdout, fmt, ap); + va_end(ap); + + /* Take a single shot at this */ + (void) write(dfd, &status, sizeof (status)); + exit(status); +} + +/* ARGSUSED */ +static int +varpd_plugin_walk_cb(varpd_handle_t *vph, const char *name, void *unused) +{ + (void) printf("loaded %s!\n", name); + return (0); +} + +static int +varpd_dir_setup(void) +{ + int fd; + + if (mkdir(VARPD_RUNDIR, 0700) != 0) { + if (errno != EEXIST) + varpd_fatal("failed to create %s: %s", VARPD_RUNDIR, + strerror(errno)); + } + + fd = open(VARPD_RUNDIR, O_RDONLY); + if (fd < 0) + varpd_fatal("failed to open %s: %s", VARPD_RUNDIR, + strerror(errno)); + + if (fchown(fd, UID_NETADM, GID_NETADM) != 0) + varpd_fatal("failed to chown %s: %s\n", VARPD_RUNDIR, + strerror(errno)); + + return (fd); +} + +/* + * Because varpd is generally run under SMF, we opt to keep its stdout and + * stderr to be whatever our parent set them up to be. + */ +static void +varpd_fd_setup(void) +{ + int dupfd; + + closefrom(STDERR_FILENO + 1); + dupfd = open(_PATH_DEVNULL, O_RDONLY); + if (dupfd < 0) + varpd_fatal("failed to open %s: %s", _PATH_DEVNULL, + strerror(errno)); + if (dup2(dupfd, STDIN_FILENO) == -1) + varpd_fatal("failed to dup out stdin: %s", strerror(errno)); +} + +/* + * We borrow fmd's daemonization style. Basically, the parent waits for the + * child to successfully set up a door and recover all of the old configurations + * before we say that we're good to go. + */ +static int +varpd_daemonize(int dirfd) +{ + char path[PATH_MAX]; + struct rlimit rlim; + sigset_t set, oset; + int estatus, pfds[2]; + pid_t child; + priv_set_t *pset; + + /* + * Set a per-process core path to be inside of /var/run/varpd. Make sure + * that we aren't limited in our dump size. + */ + (void) snprintf(path, sizeof (path), + "/var/run/varpd/core.%s.%%p", varpd_pname); + (void) core_set_process_path(path, strlen(path) + 1, getpid()); + + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + (void) setrlimit(RLIMIT_CORE, &rlim); + + /* + * Claim as many file descriptors as the system will let us. + */ + if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { + rlim.rlim_cur = rlim.rlim_max; + (void) setrlimit(RLIMIT_NOFILE, &rlim); + } + + /* + * chdir /var/run/varpd + */ + if (fchdir(dirfd) != 0) + varpd_fatal("failed to chdir to %s", VARPD_RUNDIR); + + + /* + * At this point block all signals going in so we don't have the parent + * mistakingly exit when the child is running, but never block SIGABRT. + */ + if (sigfillset(&set) != 0) + abort(); + if (sigdelset(&set, SIGABRT) != 0) + abort(); + if (sigprocmask(SIG_BLOCK, &set, &oset) != 0) + abort(); + + /* + * Do the fork+setsid dance. + */ + if (pipe(pfds) != 0) + varpd_fatal("failed to create pipe for daemonizing"); + + if ((child = fork()) == -1) + varpd_fatal("failed to fork for daemonizing"); + + if (child != 0) { + /* We'll be exiting shortly, so allow for silent failure */ + (void) close(pfds[1]); + if (read(pfds[0], &estatus, sizeof (estatus)) == + sizeof (estatus)) + _exit(estatus); + + if (waitpid(child, &estatus, 0) == child && WIFEXITED(estatus)) + _exit(WEXITSTATUS(estatus)); + + _exit(VARPD_EXIT_FATAL); + } + + /* + * Drop privileges here. + * + * We should make sure we keep around PRIV_NET_PRIVADDR and + * PRIV_SYS_DLCONFIG, but drop everything else; however, keep basic + * privs and have our child drop them. + * + * We should also run as netadm:netadm and drop all of our groups. + */ + if (setgroups(0, NULL) != 0) + abort(); + if (setgid(GID_NETADM) == -1 || seteuid(UID_NETADM) == -1) + abort(); + if ((pset = priv_allocset()) == NULL) + abort(); + priv_basicset(pset); + if (priv_delset(pset, PRIV_PROC_EXEC) == -1 || + priv_delset(pset, PRIV_PROC_INFO) == -1 || + priv_delset(pset, PRIV_PROC_FORK) == -1 || + priv_delset(pset, PRIV_PROC_SESSION) == -1 || + priv_delset(pset, PRIV_FILE_LINK_ANY) == -1 || + priv_addset(pset, PRIV_SYS_DL_CONFIG) == -1 || + priv_addset(pset, PRIV_NET_PRIVADDR) == -1) { + abort(); + } + /* + * Remove privs from the permitted set. That will cause them to be + * removed from the effective set. We want to make sure that in the case + * of a vulnerability, something can't get back in here and wreak more + * havoc. But if we want non-basic privs in the effective set, we have + * to request them explicitly. + */ + if (setppriv(PRIV_SET, PRIV_PERMITTED, pset) == -1) + abort(); + if (setppriv(PRIV_SET, PRIV_EFFECTIVE, pset) == -1) + abort(); + + priv_freeset(pset); + + if (close(pfds[0]) != 0) + abort(); + if (setsid() == -1) + abort(); + if (sigprocmask(SIG_SETMASK, &oset, NULL) != 0) + abort(); + (void) umask(0022); + + return (pfds[1]); +} + +static int +varpd_setup_lookup_threads(void) +{ + int ret; + long i, ncpus = sysconf(_SC_NPROCESSORS_ONLN) * 2 + 1; + + if (ncpus <= 0) + abort(); + for (i = 0; i < ncpus; i++) { + thread_t thr; + + ret = thr_create(NULL, 0, + (void *(*)(void *))libvarpd_overlay_lookup_run, + varpd_handle, THR_DETACHED | THR_DAEMON, &thr); + if (ret != 0) + return (ret); + } + + return (0); +} + +static void +varpd_cleanup(void) +{ + varpd_exit = B_TRUE; +} + +/* + * Load default information from SMF and apply any of if necessary. We recognize + * the following properties: + * + * varpd/include_path Treat these as a series of -i options. + * + * If we're not under SMF, just move on. + */ +static void +varpd_load_smf(int dfd) +{ + char *fmri, *inc; + scf_simple_prop_t *prop; + + if ((fmri = getenv("SMF_FMRI")) == NULL) + return; + + if ((prop = scf_simple_prop_get(NULL, fmri, VARPD_PG, + VARPD_PROP_INC)) == NULL) + return; + + while ((inc = scf_simple_prop_next_astring(prop)) != NULL) { + int err = libvarpd_plugin_load(varpd_handle, inc); + if (err != 0) { + varpd_dfatal(dfd, "failed to load from %s: %s\n", + inc, strerror(err)); + } + } + + scf_simple_prop_free(prop); +} + +/* + * There are a bunch of things we need to do to be a proper daemon here. + * + * o Ensure that /var/run/varpd exists or create it + * o make stdin /dev/null (stdout?) + * o Ensure any other fds that we somehow inherited are closed, eg. + * closefrom() + * o Properly daemonize + * o Mask all signals except sigabrt before creating our first door -- all + * other doors will inherit from that. + * o Have the main thread sigsuspend looking for most things that are + * actionable... + */ +int +main(int argc, char *argv[]) +{ + int err, c, dirfd, dfd, i; + const char *doorpath = VARPD_DEFAULT_DOOR; + sigset_t set; + struct sigaction act; + int nincpath = 0, nextincpath = 0; + char **incpath = NULL; + + varpd_pname = basename(argv[0]); + + /* + * We want to clean up our file descriptors before we do anything else + * as we can't assume that libvarpd won't open file descriptors, etc. + */ + varpd_fd_setup(); + + if ((err = libvarpd_create(&varpd_handle)) != 0) { + varpd_fatal("failed to open a libvarpd handle"); + return (1); + } + + while ((c = getopt(argc, argv, ":i:d:")) != -1) { + switch (c) { + case 'i': + if (nextincpath == nincpath) { + if (nincpath == 0) + nincpath = 16; + else + nincpath *= 2; + incpath = realloc(incpath, sizeof (char *) * + nincpath); + if (incpath == NULL) { + (void) fprintf(stderr, "failed to " + "allocate memory for the %dth " + "-I option: %s\n", nextincpath + 1, + strerror(errno)); + } + + } + incpath[nextincpath] = optarg; + nextincpath++; + break; + case 'd': + doorpath = optarg; + break; + default: + (void) fprintf(stderr, "unknown option: %c\n", c); + return (1); + } + } + + dirfd = varpd_dir_setup(); + + (void) libvarpd_plugin_walk(varpd_handle, varpd_plugin_walk_cb, NULL); + + dfd = varpd_daemonize(dirfd); + + /* + * Now that we're in the child, go ahead and load all of our plug-ins. + * We do this, in part, because these plug-ins may need threads of their + * own and fork won't preserve those and we'd rather the plug-ins don't + * have to learn about fork-handlers. + */ + for (i = 0; i < nextincpath; i++) { + err = libvarpd_plugin_load(varpd_handle, incpath[i]); + if (err != 0) { + varpd_dfatal(dfd, "failed to load from %s: %s\n", + incpath[i], strerror(err)); + } + } + + varpd_load_smf(dfd); + + if ((err = libvarpd_persist_enable(varpd_handle, VARPD_RUNDIR)) != 0) + varpd_dfatal(dfd, "failed to enable varpd persistence: %s\n", + strerror(err)); + + if ((err = libvarpd_persist_restore(varpd_handle)) != 0) + varpd_dfatal(dfd, "failed to enable varpd persistence: %s\n", + strerror(err)); + + /* + * The ur-door thread will inherit from this signal mask. So set it to + * what we want before doing anything else. In addition, so will our + * threads that handle varpd lookups. + */ + if (sigfillset(&set) != 0) + varpd_dfatal(dfd, "failed to fill a signal set..."); + + if (sigdelset(&set, SIGABRT) != 0) + varpd_dfatal(dfd, "failed to unmask SIGABRT"); + + if (sigprocmask(SIG_BLOCK, &set, NULL) != 0) + varpd_dfatal(dfd, "failed to set our door signal mask"); + + if ((err = varpd_setup_lookup_threads()) != 0) + varpd_dfatal(dfd, "failed to create lookup threads: %s\n", + strerror(err)); + + if ((err = libvarpd_door_server_create(varpd_handle, doorpath)) != 0) + varpd_dfatal(dfd, "failed to create door server at %s: %s\n", + doorpath, strerror(err)); + + /* + * At this point, finish up signal intialization and finally go ahead, + * notify the parent that we're okay, and enter the sigsuspend loop. + */ + bzero(&act, sizeof (struct sigaction)); + act.sa_handler = varpd_cleanup; + if (sigfillset(&act.sa_mask) != 0) + varpd_dfatal(dfd, "failed to fill sigaction mask"); + act.sa_flags = 0; + if (sigaction(SIGHUP, &act, NULL) != 0) + varpd_dfatal(dfd, "failed to register HUP handler"); + if (sigdelset(&set, SIGHUP) != 0) + varpd_dfatal(dfd, "failed to remove HUP from mask"); + if (sigaction(SIGQUIT, &act, NULL) != 0) + varpd_dfatal(dfd, "failed to register QUIT handler"); + if (sigdelset(&set, SIGQUIT) != 0) + varpd_dfatal(dfd, "failed to remove QUIT from mask"); + if (sigaction(SIGINT, &act, NULL) != 0) + varpd_dfatal(dfd, "failed to register INT handler"); + if (sigdelset(&set, SIGINT) != 0) + varpd_dfatal(dfd, "failed to remove INT from mask"); + if (sigaction(SIGTERM, &act, NULL) != 0) + varpd_dfatal(dfd, "failed to register TERM handler"); + if (sigdelset(&set, SIGTERM) != 0) + varpd_dfatal(dfd, "failed to remove TERM from mask"); + + err = 0; + (void) write(dfd, &err, sizeof (err)); + (void) close(dfd); + + for (;;) { + if (sigsuspend(&set) == -1) + if (errno == EFAULT) + abort(); + if (varpd_exit == B_TRUE) + break; + } + + libvarpd_door_server_destroy(varpd_handle); + libvarpd_destroy(varpd_handle); + + return (VARPD_EXIT_REQUESTED); +} diff --git a/usr/src/cmd/varpd/varpd.xml b/usr/src/cmd/varpd/varpd.xml new file mode 100644 index 0000000000..df7015a3d6 --- /dev/null +++ b/usr/src/cmd/varpd/varpd.xml @@ -0,0 +1,67 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- +This file and its contents are supplied under the terms of the +Common Development and Distribution License ("CDDL"), version 1.0. +You may only use this file in accordance with the terms of version +1.0 of the CDDL. + +A full copy of the text of the CDDL should have accompanied this +source. A copy of the CDDL is also available via the Internet at +http://www.illumos.org/license/CDDL. + +Copyright 2018, Joyent, Inc. +--> + +<service_bundle type="manifest" name="illumos:varpd" > + + <service name="network/varpd" type="service" version="1" > + + <create_default_instance enabled="true" /> + + <single_instance/> + + <dependency name="varpd-network-physical" + grouping="require_all" + restart_on="none" + type="service"> + <service_fmri value="svc:/network/physical:default" /> + </dependency> + + <dependency name="varpd-device-local" + grouping="require_all" + restart_on="none" + type="service"> + <service_fmri value="svc:/system/device/local:default" /> + </dependency> + + <exec_method + type="method" + name="start" + exec="/usr/lib/varpd/varpd" + timeout_seconds="60" /> + + <exec_method + type="method" + name="stop" + exec=":kill" + timeout_seconds="10" /> + + <property_group name='varpd' type='application'> + <property name='include_path' type='astring'> + <astring_list> + <value_node value='/usr/lib/varpd'/> + </astring_list> + </property> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang="C">virtual ARP daemon + </loctext> + </common_name> + </template> + </service> +</service_bundle> diff --git a/usr/src/cmd/vi/port/Makefile b/usr/src/cmd/vi/port/Makefile index f85351d89a..bd35a08e69 100644 --- a/usr/src/cmd/vi/port/Makefile +++ b/usr/src/cmd/vi/port/Makefile @@ -79,6 +79,9 @@ $(XPG6) := CFLAGS += -DXPG4 -DXPG6 -I$(SRC)/lib/libc/inc CPPFLAGS += -DUSG -DSTDIO -DVMUNIX -DTABS=8 -DSINGLE -DTAG_STACK +# vi intentionally uses foo[-1] as a sentinal value to q*column() +$(__GNUC4)CERRWARN += -_gcc=-Wno-array-bounds + # vi maintains its own versions of various routines from libc and libcurses, # so localize all symbols to avoid name space collisions. LDFLAGS += $(MAPFILE.NGB:%=-M%) diff --git a/usr/src/cmd/vi/port/ex_cmdsub.c b/usr/src/cmd/vi/port/ex_cmdsub.c index 9004ac7271..7e2cd4740d 100644 --- a/usr/src/cmd/vi/port/ex_cmdsub.c +++ b/usr/src/cmd/vi/port/ex_cmdsub.c @@ -1740,7 +1740,7 @@ char *prompt; /* In ex mode, let the system hassle with setting no echo */ if (!inopen) - return (unsigned char *)getpass(prompt); + return (unsigned char *)getpass((const char *)prompt); viprintf("%s", prompt); flush(); for (p=pbuf; (c = getkey())!='\n' && c!=EOF && c!='\r';) { if (p < &pbuf[8]) diff --git a/usr/src/cmd/vndadm/Makefile b/usr/src/cmd/vndadm/Makefile new file mode 100644 index 0000000000..2b9ca6c3c1 --- /dev/null +++ b/usr/src/cmd/vndadm/Makefile @@ -0,0 +1,67 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2018 Joyent, Inc. All rights reserved. +# + +PROG= vndadm +OBJS = vndadm.o +SRCS = $(OBJS:%.o=../%.c) + + +include ../Makefile.cmd +include ../Makefile.ctf + +CLEANFILES += $(OBJS) +CFLAGS += $(CCVERBOSE) +LDLIBS += -lvnd +LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2 +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +all := TARGET += all +clean := TARGET += clean +clobber := TARGET += clobber +install := TARGET += install +lint := TARGET += lint + +SUBDIRS = test + +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +clean: $(SUBDIRS) + -$(RM) $(CLEANFILES) + +lint: lint_PROG $(SUBDIRS) + +%.o: ../%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +clobber: clean $(SUBDIRS) + $(RM) $(PROG) + +install: $(PROG) $(ROOTUSRSBINPROG) $(SUBDIRS) + + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/Makefile b/usr/src/cmd/vndadm/test/Makefile new file mode 100644 index 0000000000..12ef2c3a3c --- /dev/null +++ b/usr/src/cmd/vndadm/test/Makefile @@ -0,0 +1,19 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +SUBDIRS = scripts tst + +include Makefile.subdirs +include Makefile.com diff --git a/usr/src/cmd/vndadm/test/Makefile.com b/usr/src/cmd/vndadm/test/Makefile.com new file mode 100644 index 0000000000..cb096952ca --- /dev/null +++ b/usr/src/cmd/vndadm/test/Makefile.com @@ -0,0 +1,43 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include $(SRC)/Makefile.master +include $(SRC)/cmd/Makefile.cmd + +# +# Force c99 for everything +# +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +# +# Deal with odd lint bits. +# +LINTFLAGS += -xerroff=E_NAME_DEF_NOT_USED2 + +# +# Install related definitions +# +ROOTOPTPKG = $(ROOT)/opt/vndtest +ROOTBIN = $(ROOTOPTPKG)/bin +ROOTTST = $(ROOTOPTPKG)/tst +ROOTTSTDIR = $(ROOTTST)/$(TSTDIR) +ROOTTSTEXES = $(EXETESTS:%=$(ROOTTSTDIR)/%) +ROOTTSTSH = $(SHTESTS:%=$(ROOTTSTDIR)/%) +ROOTOUT = $(OUTFILES:%=$(ROOTTSTDIR)/%) +ROOTTESTS = $(ROOTTSTEXES) $(ROOTTSTSH) $(ROOTOUT) +FILEMODE = 0555 +LDLIBS = $(LDLIBS.cmd) +LINTEXE = $(EXETESTS:%.exe=%.exe.ln) diff --git a/usr/src/cmd/vndadm/test/Makefile.subdirs b/usr/src/cmd/vndadm/test/Makefile.subdirs new file mode 100644 index 0000000000..957448c23b --- /dev/null +++ b/usr/src/cmd/vndadm/test/Makefile.subdirs @@ -0,0 +1,29 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +.KEEP_STATE: + +all := TARGET += all +clean := TARGET += clean +clobber := TARGET += clobber +install := TARGET += install +lint := TARGET += lint + +all clean clobber install lint: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/cmd/vndadm/test/Makefile.targ b/usr/src/cmd/vndadm/test/Makefile.targ new file mode 100644 index 0000000000..bcbd3c8f35 --- /dev/null +++ b/usr/src/cmd/vndadm/test/Makefile.targ @@ -0,0 +1,59 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +$(ROOTOPTPKG): + $(INS.dir) + +$(ROOTBIN): $(ROOTOPTPKG) + $(INS.dir) + +$(ROOTBIN)/%: %.ksh $(ROOTBIN) + $(INS.rename) + +$(ROOTTST): $(ROOTOPTPKG) + $(INS.dir) + +$(ROOTTSTDIR): $(ROOTTST) + $(INS.dir) + +$(ROOTTSTDIR)/%.ksh: %.ksh $(ROOTTSTDIR) + $(INS.file) + +$(ROOTTSTDIR)/%.out: %.out $(ROOTTSTDIR) + $(INS.file) + +%.o: %.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +%.exe: %.o $(SUPOBJS) + $(LINK.c) -o $@ $< $(SUPOBJS) $(LDLIBS) + $(POST_PROCESS) + +$(ROOTTSTDIR)/%.exe: %.exe $(ROOTTSTDIR) + $(INS.file) + +all: install + +%.exe.ln: %.c $(SUPOBJS) + $(LINT.c) $< $(LDLIBS) + +lint: $(LINTEXE) + +clean: + -$(RM) *.o $(CLEANFILES) + +clobber: clean + -$(RM) $(CLOBBERFILES) diff --git a/usr/src/cmd/vndadm/test/scripts/Makefile b/usr/src/cmd/vndadm/test/scripts/Makefile new file mode 100644 index 0000000000..d0f58918f9 --- /dev/null +++ b/usr/src/cmd/vndadm/test/scripts/Makefile @@ -0,0 +1,28 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +include ../Makefile.com + +SRCS = vndtest +SCRIPTS = $(SRCS:%=$(ROOTBIN)/%) + +SCRIPTS := FILEMODE = 0555 +CLOBBERFILES = $(SCRIPTS) + +install: $(SCRIPTS) + +lint: + +include ../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/scripts/vndtest.ksh b/usr/src/cmd/vndadm/test/scripts/vndtest.ksh new file mode 100755 index 0000000000..224306ffe9 --- /dev/null +++ b/usr/src/cmd/vndadm/test/scripts/vndtest.ksh @@ -0,0 +1,298 @@ +#!/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright 2019 Joyent, Inc. +# + +# +# vnd test suite driver +# +unalias -a + +vt_arg0=$(basename $0) +vt_root="$(dirname $0)/.." +vt_ksh="/usr/bin/ksh" +vt_outdir= +vt_keep= +vt_all= +vt_tests= +vt_stub= +vt_vnics="vndtest1 vndtest2 vndtest3 vndtest4 vndtest5" +vt_tnum=0 +vt_tfail=0 +vt_tsuc=0 + +function usage +{ + typeset msg="$*" + [[ -z "$msg" ]] || echo "$msg" 2>&1 + cat <<USAGE >&2 +Usage: $vt_arg0 [ -o dir ] [ -k ] [ -a | test ... ] + + -o dir Sets 'dir' as the output directory + -a Runs all tests, ignores tests passed in + -k Keep output from all tests, not just failures + -m mdb binary to test +USAGE + exit 2 +} + +function fatal +{ + typeset msg="$*" + [[ -z "$msg" ]] && msg="failed" + echo "$vt_arg0: $msg" >&2 + exit 1 +} + +function setup_outdir +{ + vt_outdir="$vt_outdir/$vt_arg0.$$" + mkdir -p $vt_outdir || fatal "failed to make output dir $vt_outdir" +} + +function setup_etherstub +{ + vt_ether="vndstub$$" + + dladm create-etherstub -t $vt_ether || \ + fatal "failed to create etherstub" +} + +function cleanup_vnd +{ + typeset over=$1 + typeset vnddevs vn + + vnddevs=$(vndadm list -p -d: -o datalink,name) + [[ $? -eq 0 ]] || fatal "failed to list vnics" + for v in $vnddevs; do + vn=$(echo $v | awk 'BEGIN{ FS=":"} + { if ($1 == targ) { print $2 } }' targ=$over) + [[ -z "$vn" ]] && continue + vndadm destroy $vn || fatal "failed to destroy $vn" + done +} + +function create_vnics +{ + for n in $vt_vnics; do + dladm create-vnic -t -l $vt_ether $n || fatal \ + "failed to create vnic $n over $vt_ether" + done +} + +function cleanup_vnics +{ + typeset nics vn + + nics=$(dladm show-vnic -p -o over,link) + [[ $? -eq 0 ]] || fatal "failed to list vnics" + for n in $nics; do + vn=$(echo $n | awk 'BEGIN{ FS=":"} + { if ($1 == targ) { print $2 } }' targ=$vt_ether ) + [[ -z "$vn" ]] && continue + cleanup_vnd $vn + # + # There may or may not be an IP device on our nics... + # + ifconfig $vn down unplumb 2>/dev/null || /bin/true + dladm delete-vnic $vn || fatal "failed to delete vnic $n" + done + +} + +function cleanup_etherstub +{ + cleanup_vnics + dladm delete-etherstub -t $vt_ether || \ + fatal "failed to delete etherstub" +} + +function run_single +{ + typeset name=$1 + typeset expect base ext exe command odir res reason + typeset iserr + + [[ -z "$name" ]] && fail "missing test to run" + base=${name##*/} + ext=${base##*.} + expect=${base%%.*} + odir="$vt_outdir/current" + [[ -z "$ext" ]] && fatal "found test without ext: $name" + [[ -z "$expect" ]] && fatal "found test without prefix: $name" + + [[ "$expect" == "create" || "$expect" == "ecreate" ]] && create_vnics + if [[ "$expect" == "err" || "$expect" == "ecreate" ]]; then + iserr="yup" + else + iserr="" + fi + + case "$ext" in + "ksh") + command="$vt_ksh ./$base" + ;; + "exe") + command="./$base" + ;; + "out") + # + # This is the file format for checking output against. + # + return 0 + ;; + *) + echo "skipping test $name (unknown extensino)" + return 0 + ;; + esac + + echo "Executing test $name ... \c" + mkdir -p "$odir" >/dev/null || fatal "can't make output directory" + cd $(dirname $name) || fatal "failed to enter test directory" + $command $vt_vnics > "$odir/stdout" 2>"$odir/stderr" + res=$? + cd - > /dev/null || fatal "failed to leave test directory" + + if [[ -f "$name.out" ]] && \ + ! diff "$name.out" "$odir/stdout" >/dev/null; then + cp $name.out $odir/$base.out + reason="stdout mismatch" + elif [[ -n "$iserr" && $res -eq 0 ]]; then + reason="test exited $res, not non-zero" + elif [[ -z "$iserr" && $res -ne 0 ]]; then + reason="test exited $res, not zero" + fi + + if [[ -n "$reason" ]]; then + echo "$reason" + ((vt_tfail++)) + mv "$odir" "$vt_outdir/failure.$vt_tfail" || fatal \ + "failed to move test output directory" + cp "$name" "$vt_outdir/failure.$vt_tfail/$(basename $name)" || \ + fatal "failed to copy test into output directory" + else + echo "passed" + ((vt_tsuc++)) + mv "$odir" "$vt_outdir/success.$vt_tsuc" || fatal \ + "failed to move test directory" + fi + + [[ "$expect" == "create" || "$expect" == "ecreate" ]] && cleanup_vnics + + ((vt_tnum++)) +} + +function run_all +{ + typeset tests t dir + + tests=$(ls -1 $vt_root/*/*/@(ecreate|create|tst|err).*.@(ksh|exe)) + for t in $tests; do + run_single $t + done +} + +function welcome +{ + cat <<WELCOME +Starting tests... +output directory: $vt_outdir +WELCOME +} + +function cleanup +{ + [[ -n "$vt_keep" ]] && return + rm -rf "$vt_outdir"/success.* || fatal \ + "failed to remove successful test cases" + if [[ $vt_tfail -eq 0 ]]; then + rmdir "$vt_outdir" || fatal \ + "failed to remove test output directory" + fi +} + +function goodbye +{ + cat <<EOF + +------------- +Results +------------- + +Tests passed: $vt_tsuc +Tests failed: $vt_tfail +Tests ran: $vt_tnum + +EOF + if [[ $vt_tfail -eq 0 ]]; then + echo "Congrats, vnd isn't completely broken, the tests pass". + else + echo "Some tests failed, you have some work to do." + fi +} + +while getopts ":ahko:m:" c $@; do + case "$c" in + a) + vt_all="y" + ;; + k) + vt_keep="y" + ;; + o) + vt_outdir="$OPTARG" + ;; + h) + usage + ;; + :) + usage "option requires an argument -- $OPTARG" + ;; + *) + usage "invalid option -- $OPTARG" + ;; + esac +done + +shift $((OPTIND-1)) + +[[ $(zonename) != "global" ]] && fatal "vndtest only runs in the global zone" + +[[ -z "$vt_all" && $# == 0 ]] && usage "no tests to run" + +[[ -z "$vt_outdir" ]] && vt_outdir="$PWD" + +setup_outdir +setup_etherstub +welcome + +if [[ ! -z "$vt_all" ]]; then + run_all +else + for t in $@; do + [[ -f $t ]] || fatal "cannot find test $t" + run_single $t + done +fi + +cleanup_etherstub +goodbye +cleanup + +# +# Exit 1 if we have tests that return non-zero +# +[[ $vt_tfai -eq 0 ]] diff --git a/usr/src/cmd/vndadm/test/tst/Makefile b/usr/src/cmd/vndadm/test/tst/Makefile new file mode 100644 index 0000000000..9b1ba29429 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/Makefile @@ -0,0 +1,18 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +SUBDIRS = cmd dld ioctl lib + +include ../Makefile.subdirs diff --git a/usr/src/cmd/vndadm/test/tst/cmd/Makefile b/usr/src/cmd/vndadm/test/tst/cmd/Makefile new file mode 100644 index 0000000000..1ca20bf749 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/Makefile @@ -0,0 +1,34 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +TSTDIR = cmd +COMMONSH = cmd.common.ksh +SHTESTS = $(COMMONSH) \ + create.list.ksh \ + create.sdev.ksh \ + create.setbuf.ksh \ + ecreate.destroy.ksh \ + ecreate.setbadprop.ksh \ + ecreate.setbadvalue.ksh \ + ecreate.setbuftoobig.ksh \ + ecreate.setrdonlyprop.ksh + +OUTFILES = create.list.ksh.out + +include ../../Makefile.com + +install: $(ROOTTESTS) + +include ../../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/tst/cmd/cmd.common.ksh b/usr/src/cmd/vndadm/test/tst/cmd/cmd.common.ksh new file mode 100644 index 0000000000..31e4e8bf5c --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/cmd.common.ksh @@ -0,0 +1,33 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Common ksh-based utilities +# + +vt_arg0=$(basename $0) + +function fatal +{ + typeset msg="$*" + [[ -z "$msg" ]] && msg="failed" + echo "$vt_arg0: $msg" >&2 + exit 1 +} + +[[ -z "$1" ]] && fatal "missing required vnic" +[[ -z "$2" ]] && fatal "missing required vnic" +[[ -z "$3" ]] && fatal "missing required vnic" diff --git a/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh b/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh new file mode 100644 index 0000000000..fdec9a85be --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh @@ -0,0 +1,30 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Basic device listing +# + +. ./cmd.common.ksh + +# +# Use what we hope is a relatively unique name +# +cl_name="triforceofcourage0" +vndadm create -l $1 $cl_name || fatal "failed to create vnd device" +vndadm list -p -o name,zone $cl_name +vndadm list -p -d: -o zone,name $cl_name +vndadm destroy $cl_name || fatal "failed to destroy vnd device" diff --git a/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh.out b/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh.out new file mode 100644 index 0000000000..d208b38aab --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/create.list.ksh.out @@ -0,0 +1,2 @@ +triforceofcourage0 global +global:triforceofcourage0 diff --git a/usr/src/cmd/vndadm/test/tst/cmd/create.sdev.ksh b/usr/src/cmd/vndadm/test/tst/cmd/create.sdev.ksh new file mode 100644 index 0000000000..b816ade1de --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/create.sdev.ksh @@ -0,0 +1,25 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Verify that our sdev links exist +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd" +[[ -c /dev/vnd/$1 ]] || fatal "missing link" +[[ -c /dev/vnd/zone/$(zonename)/$1 ]] || fatal "missing per-zone link" diff --git a/usr/src/cmd/vndadm/test/tst/cmd/create.setbuf.ksh b/usr/src/cmd/vndadm/test/tst/cmd/create.setbuf.ksh new file mode 100644 index 0000000000..d50edbead4 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/create.setbuf.ksh @@ -0,0 +1,34 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Set and validate the buffer size properties. Valiate that we can set +# the value using the various number analogues, eg. 1024K, etc. +# +set -o pipefail + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm set $1 rxbuf=1M +cur=$(vndadm get -p $1 rxbuf | nawk '{ print $4 }') +[[ $? -eq 0 ]] || fatal "failed to get rxbuf" +[[ $cur -eq 1048576 ]] || fatal "rxbuf is $cur, not 1M" + +vndadm set $1 txbuf=1024K +cur=$(vndadm get -p $1 rxbuf | nawk '{ print $4 }') +[[ $? -eq 0 ]] || fatal "failed to get txbuf" +[[ $cur -eq 1048576 ]] || fatal "txbuf is $cur, not 1M" diff --git a/usr/src/cmd/vndadm/test/tst/cmd/ecreate.destroy.ksh b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.destroy.ksh new file mode 100644 index 0000000000..e3c4931018 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.destroy.ksh @@ -0,0 +1,25 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that destroy on a previously destroyed link fails +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm destroy $1 || fatal "failed to destroy vnd device" +vndadm destroy $1 diff --git a/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadprop.ksh b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadprop.ksh new file mode 100644 index 0000000000..30c27575b1 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadprop.ksh @@ -0,0 +1,24 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that we can't set a non-existant proprety +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm set $1 ganon=ganondorf diff --git a/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadvalue.ksh b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadvalue.ksh new file mode 100644 index 0000000000..056b24a817 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbadvalue.ksh @@ -0,0 +1,24 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that we can't set something to a garbage value +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm set $1 rxbuf=hello diff --git a/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbuftoobig.ksh b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbuftoobig.ksh new file mode 100644 index 0000000000..551e20461c --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setbuftoobig.ksh @@ -0,0 +1,24 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that we can't set a buffer value to a ridiculous size +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm set $1 rxsize=1T diff --git a/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setrdonlyprop.ksh b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setrdonlyprop.ksh new file mode 100644 index 0000000000..4beb53e227 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/cmd/ecreate.setrdonlyprop.ksh @@ -0,0 +1,24 @@ +#!/usr/bin/ksh +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that we can't set a read only property. +# + +. ./cmd.common.ksh + +vndadm create $1 || fatal "failed to bring up vnd device" +vndadm set $1 mintu=100 diff --git a/usr/src/cmd/vndadm/test/tst/dld/Makefile b/usr/src/cmd/vndadm/test/tst/dld/Makefile new file mode 100644 index 0000000000..3088812630 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/dld/Makefile @@ -0,0 +1,27 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +TSTDIR = dld +COMMONSH = dld.common.ksh +SHTESTS = $(COMMONSH) \ + ecreate.ipfirst.ksh \ + ecreate.vndfirst.ksh \ + create.reuse.ksh + +include ../../Makefile.com + +install: $(ROOTTESTS) + +include ../../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/tst/dld/create.reuse.ksh b/usr/src/cmd/vndadm/test/tst/dld/create.reuse.ksh new file mode 100644 index 0000000000..bc2ffde7f6 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/dld/create.reuse.ksh @@ -0,0 +1,31 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure that we can reuse a data link +# + +. ./dld.common.ksh + +dld_nic=$1 +[[ -z "$1" ]] && fatal "missing required vnic" + +vndadm create $dld_nic || fatal "failed to bring up vnd" +vndadm destroy $dld_nic || fatal "failed to bring down vnd" +ifconfig $dld_nic plumb up || fatal "failed to bring up IP" +ifconfig $dld_nic down unplumb || fatal "failed to bring down IP" +vndadm create $dld_nic || fatal "failed to bring up vnd" +vndadm destroy $dld_nic || fatal "failed to bring down vnd" diff --git a/usr/src/cmd/vndadm/test/tst/dld/dld.common.ksh b/usr/src/cmd/vndadm/test/tst/dld/dld.common.ksh new file mode 100644 index 0000000000..7a2e0a8e2b --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/dld/dld.common.ksh @@ -0,0 +1,29 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Common ksh-based utilities +# + +vt_arg0=$(basename $0) + +function fatal +{ + typeset msg="$*" + [[ -z "$msg" ]] && msg="failed" + echo "$vt_arg0: $msg" >&2 + exit 1 +} diff --git a/usr/src/cmd/vndadm/test/tst/dld/ecreate.ipfirst.ksh b/usr/src/cmd/vndadm/test/tst/dld/ecreate.ipfirst.ksh new file mode 100644 index 0000000000..e6409781cb --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/dld/ecreate.ipfirst.ksh @@ -0,0 +1,27 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure vnd fails to come up when IP is up +# + +. ./dld.common.ksh + +dld_nic=$1 +[[ -z "$1" ]] && fatal "missing required vnic" + +ifconfig $dld_nic plumb up || fatal "failed to bring up IP" +vndadm create $dld_nic diff --git a/usr/src/cmd/vndadm/test/tst/dld/ecreate.vndfirst.ksh b/usr/src/cmd/vndadm/test/tst/dld/ecreate.vndfirst.ksh new file mode 100644 index 0000000000..ee7a13c09c --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/dld/ecreate.vndfirst.ksh @@ -0,0 +1,27 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Make sure IP fails to come up when vnd is up +# + +. ./dld.common.ksh + +dld_nic=$1 +[[ -z "$1" ]] && fatal "missing required vnic" + +vndadm create $dld_nic || fatal "failed to bring up vnd" +ifconfig $dld_nic plumb up diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/Makefile b/usr/src/cmd/vndadm/test/tst/ioctl/Makefile new file mode 100644 index 0000000000..fe074f32b0 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/Makefile @@ -0,0 +1,49 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +TSTDIR = ioctl +EXETESTS = \ + create.attach.exe \ + create.attachnolink.exe \ + create.badlinkname.exe \ + create.doublelink.exe \ + create.gioctlattach.exe \ + create.link.exe \ + create.linkexists.exe \ + create.ngioctlfault.exe \ + create.nopriv1.exe \ + create.nopriv2.exe \ + create.nopriv3.exe \ + create.nopriv4.exe \ + create.olink.exe \ + create.olinknopriv.exe \ + create.rmenolink.exe \ + tst.attachrdonly.exe \ + tst.basicopenctl.exe \ + tst.badioctl.exe \ + tst.gioctlfault.exe \ + tst.gioctlnattach.exe \ + tst.openctlbadflags.exe +SHTESTS = \ + tst.iocsize.ksh +SUPBOBJS = + +CLOBBERFILES = $(EXETESTS) + +include ../../Makefile.com + +install: $(ROOTTESTS) + +include ../../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.attach.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.attach.c new file mode 100644 index 0000000000..d7bca5cce3 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.attach.c @@ -0,0 +1,63 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Simply attach a nic + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.attachnolink.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.attachnolink.c new file mode 100644 index 0000000000..43c6c99af5 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.attachnolink.c @@ -0,0 +1,67 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Try to attach to a non-existant vnic + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + /* + * All datalink names have numbers, so we can pick a datalink which + * doesn't exist by not using numbers... + */ + (void) strlcpy(via.via_name, "enolink", VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(via.via_errno == VND_E_NODATALINK); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.badlinkname.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.badlinkname.c new file mode 100644 index 0000000000..e3a067d5ce --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.badlinkname.c @@ -0,0 +1,119 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Test that we can't link a nic with invalid names + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +static const char *names[] = { + /* Reserved names */ + "ctl", + "zone", + /* Invalid characters */ + "The fight of the century", + "Link/Ganon", + "happens@7pm", + "#testing", + "asdf!!", + "power&courage&wisdom", + "over9000?", + "you're", + "100$", + "(function", + "x)", + "2^128", + "1++", + "No.", + "99%", + "*****", + "r|m", + "=0", + "`p0", + "goodbye~", + "however;", + "\"hesaid", + "shesaid\'", + /* emoji pile of poo */ + "\xF0\x9F\x92\xA9", + NULL +}; + +int +main(int argc, const char *argv[]) +{ + int fd, ret, i; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + for (i = 0; names[i] != NULL; i++) { + (void) strlcpy(vil.vil_name, names[i], VND_NAMELEN); + (void) fprintf(stderr, "Trying to create [%s]\n", names[i]); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == -1); + assert(vil.vil_errno == VND_E_BADNAME); + } + + /* Finally, the missing null terminator */ + for (i = 0; i < VND_NAMELEN; i++) + vil.vil_name[i] = 'a'; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == -1); + assert(vil.vil_errno == VND_E_BADNAME); + + viu.viu_errno = 0; + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == -1); + assert(viu.viu_errno == VND_E_NOTLINKED); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.doublelink.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.doublelink.c new file mode 100644 index 0000000000..dcf4f311e9 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.doublelink.c @@ -0,0 +1,82 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Link a nic, first should work, second will fail. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + (void) strlcpy(vil.vil_name, "dup", VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == -1); + assert(vil.vil_errno == VND_E_LINKED); + viu.viu_errno = 0; + + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == 0); + assert(viu.viu_errno == 0); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.gioctlattach.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.gioctlattach.c new file mode 100644 index 0000000000..3d6f43377b --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.gioctlattach.c @@ -0,0 +1,69 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Ensure that we can't run global ioctls on an attached handle + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + via.via_name[0] = 'a'; + via.via_name[1] = '\0'; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(via.via_errno == VND_E_ATTACHED); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.link.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.link.c new file mode 100644 index 0000000000..16569d58cd --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.link.c @@ -0,0 +1,76 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Link a nic + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + viu.viu_errno = 0; + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == 0); + assert(viu.viu_errno == 0); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.linkexists.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.linkexists.c new file mode 100644 index 0000000000..4e3be0db5d --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.linkexists.c @@ -0,0 +1,90 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Try to create two devices with the same link name. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, fd2, ret; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + + if (argc < 3) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + fd2 = open(VND_PATH, O_RDWR); + assert(fd2 > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(via.via_name, argv[2], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd2, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, "dup", VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + (void) strlcpy(vil.vil_name, "dup", VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd2, VND_IOC_LINK, &vil); + assert(ret == -1); + assert(vil.vil_errno == VND_E_LINKEXISTS); + + viu.viu_errno = 0; + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == 0); + assert(viu.viu_errno == 0); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.ngioctlfault.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.ngioctlfault.c new file mode 100644 index 0000000000..bf174f1a8f --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.ngioctlfault.c @@ -0,0 +1,96 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Pass bad addresses to all of our non-global ioctls + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +static int requests[] = { + VND_IOC_LINK, + VND_IOC_UNLINK, + VND_IOC_GETRXBUF, + VND_IOC_SETRXBUF, + VND_IOC_GETTXBUF, + VND_IOC_SETTXBUF, + VND_IOC_GETMINTU, + VND_IOC_GETMAXTU, + VND_IOC_GETMAXBUF, + -1 +}; + +int +main(int argc, const char *argv[]) +{ + int fd, ret, i; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + for (i = 0; requests[i] != -1; i++) { + ret = ioctl(fd, requests[i], (void *)(uintptr_t)i); + assert(ret == -1); + assert(errno == EFAULT); + } + + + viu.viu_errno = 0; + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == 0); + assert(viu.viu_errno == 0); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv1.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv1.c new file mode 100644 index 0000000000..6d5ad0eec2 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv1.c @@ -0,0 +1,69 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to attach a device without PRIV_NET_CONFIG + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <priv.h> +#include <string.h> +#include <unistd.h> +#include <stropts.h> +#include <stdio.h> +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + priv_set_t *ps; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_SYS_NET_CONFIG) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + fd = open(VND_PATH, O_RDWR); + assert(fd >= 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(errno == EPERM); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv2.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv2.c new file mode 100644 index 0000000000..6b38f159a0 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv2.c @@ -0,0 +1,69 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to attach a device without PRIV_NET_RAWACCESS + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <priv.h> +#include <unistd.h> +#include <stropts.h> +#include <string.h> +#include <stdio.h> +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + priv_set_t *ps; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_NET_RAWACCESS) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + fd = open(VND_PATH, O_RDWR); + assert(fd >= 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(errno == EPERM); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv3.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv3.c new file mode 100644 index 0000000000..a8c43fc46d --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv3.c @@ -0,0 +1,70 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to attach a device without PRIV_NET_CONFIG and PRIV_NET_RAWACCESS + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <priv.h> +#include <unistd.h> +#include <stropts.h> +#include <string.h> +#include <stdio.h> +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + priv_set_t *ps; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_SYS_NET_CONFIG) == 0); + assert(priv_addset(ps, PRIV_NET_RAWACCESS) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + fd = open(VND_PATH, O_RDWR); + assert(fd >= 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(errno == EPERM); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv4.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv4.c new file mode 100644 index 0000000000..aed0204544 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv4.c @@ -0,0 +1,75 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to link a device without PRIV_NET_CONFIG + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <priv.h> +#include <unistd.h> +#include <stropts.h> +#include <string.h> +#include <stdio.h> +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + priv_set_t *ps; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd >= 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_SYS_NET_CONFIG) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == -1); + assert(errno == EPERM); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv5.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv5.c new file mode 100644 index 0000000000..2db8ecc95f --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.nopriv5.c @@ -0,0 +1,77 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to open a device without PRIV_NET_RAWACCESS + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <assert.h> +#include <priv.h> +#include <unistd.h> +#include <stropts.h> +#include <string.h> +#include <stdio.h> +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, fd2, ret; + priv_set_t *ps; + char *path; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd >= 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_SYS_NET_RAWACCESS) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + (void) asprintf(&path, "/dev/vnd/%s", argv[1]); + assert(path != NULL); + fd2 = open(path, O_RDWR); + assert(fd2 == -1); + assert(errno == EPERM); + + free(path); + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.olink.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.olink.c new file mode 100644 index 0000000000..0f9292bbae --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.olink.c @@ -0,0 +1,77 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Open a /dev/vnd/%s link + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + char *path; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + ret = asprintf(&path, "/dev/vnd/%s", argv[1]); + assert(ret != -1); + + ret = open(path, O_RDONLY); + assert(ret > 0); + assert(close(ret) == 0); + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.olinknopriv.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.olinknopriv.c new file mode 100644 index 0000000000..338218e751 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.olinknopriv.c @@ -0,0 +1,83 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to open a /dev/vnd/%s without PRIV_NET_RAWACCESS + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> +#include <priv.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + char *path; + priv_set_t *ps; + vnd_ioc_attach_t via; + vnd_ioc_link_t vil; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + (void) strlcpy(vil.vil_name, argv[1], VND_NAMELEN); + vil.vil_errno = 0; + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(ret == 0); + assert(vil.vil_errno == 0); + + ret = asprintf(&path, "/dev/vnd/%s", argv[1]); + assert(ret != -1); + + ps = priv_allocset(); + assert(ps != NULL); + assert(priv_addset(ps, PRIV_NET_RAWACCESS) == 0); + assert(setppriv(PRIV_OFF, PRIV_PERMITTED, ps) == 0); + + ret = open(path, O_RDWR); + assert(ret == -1); + assert(errno == EPERM); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/create.rmenolink.c b/usr/src/cmd/vndadm/test/tst/ioctl/create.rmenolink.c new file mode 100644 index 0000000000..d44e6512a7 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/create.rmenolink.c @@ -0,0 +1,69 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Verify that unlink fails when we're not linked. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + vnd_ioc_unlink_t viu; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == 0); + assert(via.via_errno == 0); + + viu.viu_errno = 0; + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(ret == -1); + assert(viu.viu_errno == VND_E_NOTLINKED); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.attachrdonly.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.attachrdonly.c new file mode 100644 index 0000000000..29def6182d --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.attachrdonly.c @@ -0,0 +1,63 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Fail to attach when /dev/vnd/ctl is opened read only. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <strings.h> +#include <assert.h> +#include <stdio.h> +#include <unistd.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(int argc, const char *argv[]) +{ + int fd, ret; + vnd_ioc_attach_t via; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= VND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open(VND_PATH, O_RDONLY); + assert(fd > 0); + + (void) strlcpy(via.via_name, argv[1], VND_NAMELEN); + via.via_zoneid = 0; + via.via_errno = 0; + + ret = ioctl(fd, VND_IOC_ATTACH, &via); + assert(ret == -1); + assert(errno == EBADF); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.badioctl.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.badioctl.c new file mode 100644 index 0000000000..f26722f035 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.badioctl.c @@ -0,0 +1,79 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Throw a bunch of bad ioctls at us and make sure that we get ENOTTY. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <unistd.h> +#include <stropts.h> +#include <limits.h> +#include <assert.h> + +/* + * We're including a bunch of bad header files that have ioctl numbers that we + * know we shouldn't. + */ +#include <sys/ipd.h> +#include <sys/dtrace.h> + +#define VND_PATH "/dev/vnd/ctl" + +/* + * A series of bad requests + */ +static int requests[] = { + 0, + 1, + 42, + 169, + 4096, + INT_MAX, + IPDIOC_CORRUPT, + IPDIOC_REMOVE, + DTRACEIOC_CONF, + DTRACEIOC_REPLICATE, + -1 +}; + +int +main(void) +{ + int fd, i; + + fd = open(VND_PATH, O_RDONLY); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s read only: %s\n", + VND_PATH, strerror(errno)); + return (1); + } + + for (i = 0; requests[i] != -1; i++) { + int ret; + ret = ioctl(fd, requests[i], NULL); + assert(ret == -1); + assert(errno == ENOTTY); + } + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.basicopenctl.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.basicopenctl.c new file mode 100644 index 0000000000..852ad5550f --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.basicopenctl.c @@ -0,0 +1,76 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Ensure that we can do a basic open of the device for read, write, and + * read/write. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <unistd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(void) +{ + int fd; + + fd = open(VND_PATH, O_RDONLY); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s read only: %s\n", + VND_PATH, strerror(errno)); + return (1); + } + + if (close(fd) != 0) { + (void) fprintf(stderr, "failed to close vnd fd: %s\n", + strerror(errno)); + return (1); + } + + fd = open(VND_PATH, O_RDWR); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s read/write: %s\n", + VND_PATH, strerror(errno)); + return (1); + } + + if (close(fd) != 0) { + (void) fprintf(stderr, "failed to close vnd fd: %s\n", + strerror(errno)); + return (1); + } + + fd = open(VND_PATH, O_WRONLY); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s write only: %s\n", + VND_PATH, strerror(errno)); + return (1); + } + + if (close(fd) != 0) { + (void) fprintf(stderr, "failed to close vnd fd: %s\n", + strerror(errno)); + return (1); + } + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlfault.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlfault.c new file mode 100644 index 0000000000..b581b5dd4c --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlfault.c @@ -0,0 +1,78 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Pass pointers to arbitrary addresses and make sure we properly get EFAULT for + * all the global ioctls. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <unistd.h> +#include <stropts.h> +#include <limits.h> +#include <assert.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(void) +{ + int fd, ret; + vnd_ioc_attach_t *via; + vnd_ioc_list_t *vil; + vnd_ioc_buf_t *vib; + + fd = open(VND_PATH, O_RDWR); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s r/w: %s\n", VND_PATH, + strerror(errno)); + return (1); + } + + via = (vnd_ioc_attach_t *)(uintptr_t)23; + vil = (vnd_ioc_list_t *)(uintptr_t)42; + vib = (vnd_ioc_buf_t *)(uintptr_t)169; + + ret = ioctl(fd, VND_IOC_ATTACH, NULL); + assert(ret == -1); + assert(errno == EFAULT); + ret = ioctl(fd, VND_IOC_LIST, NULL); + assert(ret == -1); + assert(errno == EFAULT); + ret = ioctl(fd, VND_IOC_GETMAXBUF, NULL); + assert(ret == -1); + assert(errno == EFAULT); + + ret = ioctl(fd, VND_IOC_ATTACH, via); + assert(ret == -1); + assert(errno == EFAULT); + ret = ioctl(fd, VND_IOC_LIST, vil); + assert(ret == -1); + assert(errno == EFAULT); + ret = ioctl(fd, VND_IOC_GETMAXBUF, vib); + assert(ret == -1); + assert(errno == EFAULT); + + assert(close(fd) == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlnattach.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlnattach.c new file mode 100644 index 0000000000..98acffa194 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.gioctlnattach.c @@ -0,0 +1,100 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Here we test that all the ioctls which require us to be on a local device + * fail to work. Specifically, the errno should be VND_E_NOTATTACHED + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <unistd.h> +#include <stropts.h> +#include <limits.h> +#include <assert.h> +#include <stdlib.h> + +#include <sys/vnd.h> + +#define VND_PATH "/dev/vnd/ctl" + +static int vib_ioc[] = { + VND_IOC_GETRXBUF, + VND_IOC_SETRXBUF, + VND_IOC_GETTXBUF, + VND_IOC_SETTXBUF, + VND_IOC_GETMINTU, + VND_IOC_GETMAXTU, + -1 +}; + +int +main(void) +{ + int fd, ret, i; + vnd_ioc_link_t vil; + vnd_ioc_unlink_t viu; + vnd_ioc_buf_t vib; + frameio_t *fio; + char buf[1]; + + fd = open(VND_PATH, O_RDWR); + if (fd < 0) { + (void) fprintf(stderr, "failed to open %s r/w: %s\n", VND_PATH, + strerror(errno)); + return (1); + } + + bzero(&vil, sizeof (vnd_ioc_link_t)); + vil.vil_name[0] = 'a'; + bzero(&viu, sizeof (vnd_ioc_unlink_t)); + bzero(&vib, sizeof (vnd_ioc_buf_t)); + fio = malloc(sizeof (frameio_t) + sizeof (framevec_t)); + assert(fio != NULL); + fio->fio_version = FRAMEIO_CURRENT_VERSION; + fio->fio_nvpf = 1; + fio->fio_nvecs = 1; + fio->fio_vecs[0].fv_buf = buf; + fio->fio_vecs[0].fv_buflen = 1; + + ret = ioctl(fd, VND_IOC_LINK, &vil); + assert(vil.vil_errno == VND_E_NOTATTACHED); + ret = ioctl(fd, VND_IOC_UNLINK, &viu); + assert(viu.viu_errno == VND_E_NOTLINKED); + + for (i = 0; vib_ioc[i] != -1; i++) { + bzero(&vib, sizeof (vib)); + ret = ioctl(fd, vib_ioc[i], &vib); + assert(vib.vib_errno == VND_E_NOTATTACHED); + } + + /* The frameio ioctls only use standard errnos */ + ret = ioctl(fd, VND_IOC_FRAMEIO_READ, fio); + assert(ret == -1); + assert(errno == ENXIO); + ret = ioctl(fd, VND_IOC_FRAMEIO_WRITE, fio); + assert(ret == -1); + assert(errno == ENXIO); + + free(fio); + assert(close(fd) == 0); + + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.iocsize.ksh b/usr/src/cmd/vndadm/test/tst/ioctl/tst.iocsize.ksh new file mode 100644 index 0000000000..9b30043d47 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.iocsize.ksh @@ -0,0 +1,54 @@ +# +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +# +# Ensure structure sizes for both ILP32 and LP64 are the same +# + +vt_arg0=$(basename $0) +vt_structs="vnd_ioc_attach_t vnd_ioc_link_t vnd_ioc_unlink_t" +vt_structs="$vt_structs vnd_ioc_nonblock_t vnd_ioc_buf_t vnd_ioc_info_t" + +vt_t32="/tmp/vnd.iocsize.32.$$" +vt_t64="/tmp/vnd.iocsize.64.$$" + +function fatal +{ + typeset msg="$*" + [[ -z "$msg" ]] && msg="failed" + echo "$vt_arg0: $msg" >&2 + exit 1 +} + +function dump_types +{ + typeset file=$1 + typeset lib=$2 + typeset t + + for t in $vn_structs; do + mdb -e \'::print -at $t\' $lib >> $file || fatal \ + "failed to dump type $t from $lib" + done +} + +rm -f $vt_t32 $vt_t64 || fatal "failed to cleanup old temp files" +touch $vt_t32 $vt_t64 || fatal "failed to create temp files" + +dump_types $vt_t32 /usr/lib/libvnd.so.1 +dump_types $vt_t64 /usr/lib/64/libvnd.so.1 + +diff $vt_t32 $vt_t64 diff --git a/usr/src/cmd/vndadm/test/tst/ioctl/tst.openctlbadflags.c b/usr/src/cmd/vndadm/test/tst/ioctl/tst.openctlbadflags.c new file mode 100644 index 0000000000..65e48029b7 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/ioctl/tst.openctlbadflags.c @@ -0,0 +1,88 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure that we can't open the vnd control device with invalid flags. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <stdio.h> + +#define VND_PATH "/dev/vnd/ctl" + +int +main(void) +{ + int fd; + + fd = open(VND_PATH, O_RDONLY | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_EXCL!"); + return (1); + } + + fd = open(VND_PATH, O_RDWR | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_EXCL!"); + return (1); + } + + fd = open(VND_PATH, O_WRONLY | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_EXCL!"); + return (1); + } + + fd = open(VND_PATH, O_RDONLY | O_NDELAY); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY!"); + return (1); + } + + fd = open(VND_PATH, O_RDWR | O_NDELAY); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY!"); + return (1); + } + + fd = open(VND_PATH, O_WRONLY | O_NDELAY); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY!"); + return (1); + } + + fd = open(VND_PATH, O_RDONLY | O_NDELAY | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY | O_EXCL!"); + return (1); + } + + fd = open(VND_PATH, O_RDWR | O_NDELAY | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY | O_EXCL!"); + return (1); + } + + fd = open(VND_PATH, O_WRONLY | O_NDELAY | O_EXCL); + if (fd != -1) { + (void) fprintf(stderr, "somehow opened vnd O_NDELAY | O_EXCL!"); + return (1); + } + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/Makefile b/usr/src/cmd/vndadm/test/tst/lib/Makefile new file mode 100644 index 0000000000..d7a1ed8fa5 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/Makefile @@ -0,0 +1,44 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +TSTDIR = lib +EXETESTS = \ + create.basic.exe \ + create.badlink.exe \ + create.badpropid.exe \ + create.badpropsize.exe \ + create.badzone.exe \ + create.enomem.exe \ + create.frameioeagain.exe \ + create.open.exe \ + create.propiter.exe \ + create.proprdonly.exe \ + err.badclose.exe \ + tst.badopen.exe \ + tst.strerror.exe \ + tst.strsyserror.exe +OUTFILES = tst.strerror.exe.out +SHTESTS = +SUPBOBJS = + +CLOBBERFILES = $(EXETESTS) + +include ../../Makefile.com + +LDLIBS += -lvnd + +install: $(ROOTTESTS) + +include ../../Makefile.targ diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.badlink.c b/usr/src/cmd/vndadm/test/tst/lib/create.badlink.c new file mode 100644 index 0000000000..aefec3ed44 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.badlink.c @@ -0,0 +1,39 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure that we can't create something in the context of a datalink that + * doesn't exist. + */ + +#include <assert.h> +#include <stdio.h> +#include <libvnd.h> + +int +main(void) +{ + int syserr; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + vhp = vnd_create(NULL, "foobar", "foobar", &vnderr, &syserr); + (void) printf("%d, %d\n", vnderr, syserr); + assert(vhp == NULL); + assert(vnderr == VND_E_NODATALINK); + assert(syserr == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.badpropid.c b/usr/src/cmd/vndadm/test/tst/lib/create.badpropid.c new file mode 100644 index 0000000000..15334fa31c --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.badpropid.c @@ -0,0 +1,76 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure that we can't get and set nonexisting properties. + */ + +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr, ret; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + ret = vnd_prop_get(vhp, VND_PROP_MAX, NULL, 0); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROP); + assert(vnd_syserrno(vhp) == 0); + + ret = vnd_prop_get(vhp, VND_PROP_MAX + 5, NULL, 0); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROP); + assert(vnd_syserrno(vhp) == 0); + + ret = vnd_prop_set(vhp, VND_PROP_MAX, NULL, 0); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROP); + assert(vnd_syserrno(vhp) == 0); + + ret = vnd_prop_set(vhp, VND_PROP_MAX + 5, NULL, 0); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROP); + assert(vnd_syserrno(vhp) == 0); + + ret = vnd_prop_writeable(VND_PROP_MAX, NULL); + assert(ret == -1); + + ret = vnd_prop_writeable(VND_PROP_MAX + 5, NULL); + assert(ret == -1); + + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.badpropsize.c b/usr/src/cmd/vndadm/test/tst/lib/create.badpropsize.c new file mode 100644 index 0000000000..d5fefd3764 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.badpropsize.c @@ -0,0 +1,63 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Validate that we can't set properties with bogus sizes. + */ + +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <limits.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr, ret, i; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + for (i = 0; i < VND_PROP_MAX; i++) { + ret = vnd_prop_get(vhp, i, NULL, INT32_MAX); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROPSIZE); + assert(vnd_syserrno(vhp) == 0); + + ret = vnd_prop_set(vhp, i, NULL, INT32_MAX); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_BADPROPSIZE); + assert(vnd_syserrno(vhp) == 0); + } + + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.badzone.c b/usr/src/cmd/vndadm/test/tst/lib/create.badzone.c new file mode 100644 index 0000000000..30f9612963 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.badzone.c @@ -0,0 +1,43 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure that we can't create something in the context of a zone that + * doesn't exist. + */ + +#include <assert.h> +#include <sys/zone.h> +#include <string.h> +#include <libvnd.h> + +int +main(void) +{ + int syserr; + vnd_errno_t vnderr; + char zname[ZONENAME_MAX+4]; + vnd_handle_t *vhp; + + (void) memset(zname, 'a', sizeof (zname)); + zname[ZONENAME_MAX+3] = '\0'; + + vhp = vnd_create(zname, "foobar", "foobar", &vnderr, &syserr); + assert(vhp == NULL); + assert(vnderr == VND_E_NOZONE); + assert(syserr == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.basic.c b/usr/src/cmd/vndadm/test/tst/lib/create.basic.c new file mode 100644 index 0000000000..5335f8cbb4 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.basic.c @@ -0,0 +1,49 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Simple create and destroy. + */ + +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.enomem.c b/usr/src/cmd/vndadm/test/tst/lib/create.enomem.c new file mode 100644 index 0000000000..9203e369ae --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.enomem.c @@ -0,0 +1,91 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Verify that we can't allocate a handle when in an ENOMEM situation. + */ + +#include <procfs.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <sys/sysmacros.h> +#include <assert.h> +#include <strings.h> + +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int fd; + int syserr; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + pstatus_t status; + void *addr; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + fd = open("/proc/self/status", O_RDONLY); + if (fd < 0) + exit(1); + if (read(fd, &status, sizeof (status)) != sizeof (status)) + exit(1); + + addr = mmap((caddr_t)P2ROUNDUP(status.pr_brkbase + + status.pr_brksize, 0x1000), 0x1000, + PROT_READ, MAP_ANON | MAP_FIXED | MAP_PRIVATE, -1, 0); + if (addr == (void *)-1) { + perror("mmap"); + exit(1); + } + + /* malloc an approximate size of the vnd_handle_t */ + for (;;) { + void *buf; + + buf = malloc(8); + if (buf == NULL) + break; + } + + for (;;) { + void *buf; + + buf = malloc(4); + if (buf == NULL) + break; + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp == NULL); + assert(vnderr == VND_E_NOMEM); + assert(syserr == 0); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.frameioeagain.c b/usr/src/cmd/vndadm/test/tst/lib/create.frameioeagain.c new file mode 100644 index 0000000000..6cb14fb7df --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.frameioeagain.c @@ -0,0 +1,80 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Create a datalink, set it to non-blocking mode and ensure that we get EAGAIN + * from frame I/O calls. Note that if this test is not plumbed up over an + * etherstub, then it is likely that other traffic will appear on the device and + * this will fail. Note that the test suite always creates these devices over an + * etherstub. + */ + +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr, ret, fd; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + frameio_t *fio; + char buf[1520]; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + fd = vnd_pollfd(vhp); + ret = fcntl(fd, F_SETFL, O_NONBLOCK); + assert(ret == 0); + + fio = malloc(sizeof (frameio_t) + + sizeof (framevec_t)); + assert(fio != NULL); + fio->fio_version = FRAMEIO_CURRENT_VERSION; + fio->fio_nvpf = 1; + fio->fio_nvecs = 1; + + fio->fio_vecs[0].fv_buf = buf; + fio->fio_vecs[0].fv_buflen = sizeof (buf); + + ret = vnd_frameio_read(vhp, fio); + (void) printf("%d, %d\n", ret, errno); + assert(ret == -1); + assert(errno == EAGAIN); + + vnd_close(vhp); + free(fio); + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.open.c b/usr/src/cmd/vndadm/test/tst/lib/create.open.c new file mode 100644 index 0000000000..9cb1d7e40e --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.open.c @@ -0,0 +1,56 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure we can open a created datalink. + */ + +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr; + vnd_errno_t vnderr; + vnd_handle_t *vhp, *vhp2; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + vhp2 = vnd_open(NULL, argv[1], &vnderr, &syserr); + assert(vhp2 != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + vnd_close(vhp2); + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.propiter.c b/usr/src/cmd/vndadm/test/tst/lib/create.propiter.c new file mode 100644 index 0000000000..a0b46180f7 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.propiter.c @@ -0,0 +1,79 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Ensure that vnd_prop_iter sees all props; + */ + +#include <stdio.h> +#include <strings.h> +#include <stdlib.h> +#include <assert.h> +#include <libvnd.h> + +static boolean_t *g_props; + +/* ARGSUSED */ +static int +prop_cb(vnd_handle_t *vhp, vnd_prop_t prop, void *unused) +{ + assert(prop < VND_PROP_MAX); + g_props[prop] = B_TRUE; + + return (0); +} + +int +main(int argc, const char *argv[]) +{ + int syserr, i, ret; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + g_props = malloc(sizeof (boolean_t) * VND_PROP_MAX); + if (g_props == NULL) { + (void) fprintf(stderr, "failed to alloc memory for %d " + "boolean_t\n", VND_PROP_MAX); + return (1); + } + for (i = 0; i < VND_PROP_MAX; i++) + g_props[i] = B_FALSE; + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + ret = vnd_prop_iter(vhp, prop_cb, NULL); + assert(ret == 0); + + for (i = 0; i < VND_PROP_MAX; i++) + assert(g_props[i] == B_TRUE); + + free(g_props); + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/create.proprdonly.c b/usr/src/cmd/vndadm/test/tst/lib/create.proprdonly.c new file mode 100644 index 0000000000..18b1f7d58d --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/create.proprdonly.c @@ -0,0 +1,63 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Validate that we can't set read only properties + */ + +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <limits.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr, ret; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + vnd_prop_buf_t vpb; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_create(NULL, argv[1], argv[1], &vnderr, &syserr); + assert(vhp != NULL); + assert(vnderr == 0); + assert(syserr == 0); + + ret = vnd_prop_get(vhp, VND_PROP_MINTU, &vpb, + sizeof (vnd_prop_buf_t)); + assert(ret == 0); + + ret = vnd_prop_set(vhp, VND_PROP_MINTU, &vpb, + sizeof (vnd_prop_buf_t)); + assert(ret == -1); + assert(vnd_errno(vhp) == VND_E_PROPRDONLY); + assert(vnd_syserrno(vhp) == 0); + + vnd_close(vhp); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/err.badclose.c b/usr/src/cmd/vndadm/test/tst/lib/err.badclose.c new file mode 100644 index 0000000000..8c832506a0 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/err.badclose.c @@ -0,0 +1,33 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * This program should segfault. + */ + +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <libvnd.h> + +int +main(void) +{ + vnd_handle_t *vhp = (void *)0x42; + vnd_close(vhp); + /* This should not be reached */ + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/tst.badopen.c b/usr/src/cmd/vndadm/test/tst/lib/tst.badopen.c new file mode 100644 index 0000000000..4f67ce79ed --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/tst.badopen.c @@ -0,0 +1,49 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Make sure we can't open a vnd device that doesn't exist + */ + +#include <errno.h> +#include <stdio.h> +#include <strings.h> +#include <assert.h> +#include <libvnd.h> + +int +main(int argc, const char *argv[]) +{ + int syserr; + vnd_errno_t vnderr; + vnd_handle_t *vhp; + + if (argc < 2) { + (void) fprintf(stderr, "missing arguments...\n"); + return (1); + } + + if (strlen(argv[1]) >= LIBVND_NAMELEN) { + (void) fprintf(stderr, "vnic name too long...\n"); + return (1); + } + + vhp = vnd_open(NULL, argv[1], &vnderr, &syserr); + assert(vhp == NULL); + assert(vnderr == VND_E_SYS); + assert(syserr == ENOENT); + + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.c b/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.c new file mode 100644 index 0000000000..a99a9ecbf6 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.c @@ -0,0 +1,30 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Verify that all the error strings we care about match what we expect. + */ + +#include <stdio.h> +#include <libvnd.h> + +int +main(void) +{ + int i; + for (i = 0; i <= VND_E_UNKNOWN + 1; i++) + (void) printf("[%s]\n", vnd_strerror(i)); + return (0); +} diff --git a/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.exe.out b/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.exe.out new file mode 100644 index 0000000000..83dbcdfdb4 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/tst.strerror.exe.out @@ -0,0 +1,37 @@ +[no error] +[not enough memory available] +[no such datalink] +[datalink not of type DL_ETHER] +[unknown dlpi failure] +[DL_ATTACH_REQ failed] +[DL_BIND_REQ failed] +[DL_PROMISCON_REQ failed] +[DLD_CAPAB_DIRECT enable failed] +[bad datalink capability] +[bad datalink subcapability] +[bad dld version] +[failed to create kstats] +[no such vnd link] +[netstack doesn't exist] +[device already associated] +[device already attached] +[device already linked] +[invalid name] +[permission denied] +[no such zone] +[failed to initialize vnd stream module] +[device not attached] +[device not linked] +[another device has the same link name] +[failed to create minor node] +[requested buffer size is too large] +[requested buffer size is too small] +[unable to obtain exclusive access to dlpi link, link busy] +[DLD direct capability not supported over data link] +[invalid property size] +[invalid property] +[property is read only] +[unexpected system error] +[capabilities invalid, pass-through module detected] +[unknown error] +[unknown error] diff --git a/usr/src/cmd/vndadm/test/tst/lib/tst.strsyserror.c b/usr/src/cmd/vndadm/test/tst/lib/tst.strsyserror.c new file mode 100644 index 0000000000..b95e6372e4 --- /dev/null +++ b/usr/src/cmd/vndadm/test/tst/lib/tst.strsyserror.c @@ -0,0 +1,50 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +/* + * Verify that the error message from libvnd's strsyserrno is the same as the + * underlying strerror function's. It should be. We'll just check an assortment + * of errnos. + */ + +#include <stdio.h> +#include <string.h> +#include <libvnd.h> + +int +main(void) +{ + int i; + const char *vnd, *libc; + for (i = 0; i < 42; i++) { + vnd = vnd_strsyserror(i); + libc = strerror(i); + if ((vnd != NULL && libc == NULL) || + (vnd == NULL && libc != NULL)) { + (void) fprintf(stderr, "errno %d, vnd: %p, libc: %p", + i, (void *)vnd, (void *)libc); + return (1); + } + if (vnd != NULL && strcmp(vnd, libc) != 0) { + (void) fprintf(stderr, + "errno %d: libc and vnd disagree.\n", i); + (void) fprintf(stderr, "vnd: %s\n", vnd); + (void) fprintf(stderr, "libc: %s\n", libc); + return (1); + } + } + + return (0); +} diff --git a/usr/src/cmd/vndadm/vndadm.c b/usr/src/cmd/vndadm/vndadm.c new file mode 100644 index 0000000000..6811663696 --- /dev/null +++ b/usr/src/cmd/vndadm/vndadm.c @@ -0,0 +1,872 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014 Joyent, Inc. All rights reserved. + */ + +#include <errno.h> +#include <sys/types.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <assert.h> +#include <libgen.h> +#include <stdlib.h> +#include <unistd.h> +#include <zone.h> + +#include <libvnd.h> + +typedef int (*vndadm_print_t)(vnd_handle_t *, vnd_prop_t); +typedef int (*vndadm_parse_t)(char *, void **, size_t *); + +typedef struct vndadm_proptbl { + const char *vp_name; + vndadm_print_t vp_print; + vndadm_parse_t vp_parse; +} vndadm_proptbl_t; + +/* + * Forwards + */ +static int usage(const char *, ...); +static int vndadm_print_size(vnd_handle_t *, vnd_prop_t); +static int vndadm_print_number(vnd_handle_t *, vnd_prop_t); +static int vndadm_parse_size(char *, void **, size_t *); + +/* + * Globals + */ +static char *vnd_pname; + +static void +vnd_vwarn(vnd_errno_t verr, int syserr, const char *format, va_list alist) +{ + (void) fprintf(stderr, "%s: ", vnd_pname); + (void) vfprintf(stderr, format, alist); + if (strchr(format, '\n') == NULL) { + (void) fprintf(stderr, ": %s\n", verr != VND_E_SYS ? + vnd_strerror(verr) : vnd_strsyserror(syserr)); + } +} + +static void +vnd_libwarn(vnd_errno_t verr, int syserr, const char *format, ...) +{ + va_list alist; + + va_start(alist, format); + vnd_vwarn(verr, syserr, format, alist); + va_end(alist); +} + +static void +vnd_warn(const char *format, ...) +{ + va_list alist; + + va_start(alist, format); + vnd_vwarn(0, 0, format, alist); + va_end(alist); +} + +static vndadm_proptbl_t vndadm_propname_tbl[] = { + { "rxbuf", vndadm_print_size, + vndadm_parse_size }, /* VND_PROP_RXBUF */ + { "txbuf", vndadm_print_size, + vndadm_parse_size }, /* VND_PROP_TXBUF */ + { "maxsize", vndadm_print_size, NULL }, /* VND_PROP_MAXBUF */ + { "mintu", vndadm_print_number, NULL }, /* VND_PROP_MINTU */ + { "maxtu", vndadm_print_number, NULL }, /* VND_PROP_MAXTU */ + NULL /* VND_PROP_MAX */ +}; + +static const char * +vndadm_prop_to_name(vnd_prop_t prop) +{ + if (prop > VND_PROP_MAX) + return (NULL); + + return (vndadm_propname_tbl[prop].vp_name); +} + +static vnd_prop_t +vndadm_name_to_prop(const char *name) +{ + int i; + + for (i = 0; i < VND_PROP_MAX; i++) { + if (strcmp(name, vndadm_propname_tbl[i].vp_name) == 0) + return (i); + } + + return (VND_PROP_MAX); +} + +static int +vndadm_print_size(vnd_handle_t *vhp, vnd_prop_t prop) +{ + vnd_prop_buf_t buf; + + if (vnd_prop_get(vhp, prop, &buf, sizeof (buf)) != 0) { + vnd_libwarn(vnd_errno(vhp), vnd_syserrno(vhp), + "failed to get property %s", vndadm_prop_to_name(prop)); + return (1); + } + + (void) printf("%lld", buf.vpb_size); + return (0); +} + +static int +vndadm_print_number(vnd_handle_t *vhp, vnd_prop_t prop) +{ + vnd_prop_buf_t buf; + + if (vnd_prop_get(vhp, prop, &buf, sizeof (buf)) != 0) { + vnd_libwarn(vnd_errno(vhp), vnd_syserrno(vhp), + "failed to get property %s", vndadm_prop_to_name(prop)); + return (1); + } + + (void) printf("%lld", buf.vpb_size); + return (0); +} + +static int +vndadm_parse_size(char *str, void **bufp, size_t *sizep) +{ + char *end; + unsigned long long val, orig; + vnd_prop_buf_t *buf; + + errno = 0; + val = strtoull(str, &end, 10); + if (errno != 0) { + vnd_warn("%s: not a number\n", str); + return (1); + } + + orig = val; + switch (*end) { + case 'g': + case 'G': + val *= 1024; + if (val < orig) + goto overflow; + /*FALLTHRU*/ + case 'm': + case 'M': + val *= 1024; + if (val < orig) + goto overflow; + /*FALLTHRU*/ + case 'k': + case 'K': + val *= 1024; + if (val < orig) + goto overflow; + end++; + break; + default: + break; + } + + if (*end == 'b' || *end == 'B') + end++; + if (*end != '\0') { + vnd_warn("%s: not a number", str); + return (1); + } + + buf = malloc(sizeof (vnd_prop_buf_t)); + if (buf == NULL) { + vnd_warn("failed to allocate memory for setting a property"); + return (1); + } + + buf->vpb_size = val; + *bufp = buf; + *sizep = sizeof (vnd_prop_buf_t); + + return (0); + +overflow: + vnd_warn("value overflowed: %s\n", str); + return (1); +} + +static void +vndadm_create_usage(FILE *out) +{ + (void) fprintf(out, "\tcreate:\t\t[-z zonename] -l datalink name\n"); +} + +static int +vndadm_create(int argc, char *argv[]) +{ + int c, syserr; + vnd_errno_t vnderr; + const char *datalink = NULL; + const char *linkname = NULL; + const char *zonename = NULL; + vnd_handle_t *vhp; + + optind = 0; + while ((c = getopt(argc, argv, ":z:l:")) != -1) { + switch (c) { + case 'l': + datalink = optarg; + break; + case 'z': + zonename = optarg; + break; + case ':': + return (usage("-%c requires an operand\n", optopt)); + case '?': + return (usage("unknown option: -%c\n", optopt)); + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + return (usage("missing required link name\n")); + } else if (argc > 1) { + return (usage("create: too many arguments for link name, " + "pick one\n")); + } + linkname = argv[0]; + if (datalink == NULL) + datalink = linkname; + + vhp = vnd_create(zonename, datalink, linkname, &vnderr, &syserr); + if (vhp == NULL) { + vnd_libwarn(vnderr, syserr, + "failed to create datapath link %s", linkname); + return (1); + } + + vnd_close(vhp); + return (0); +} + +static void +vndadm_destroy_usage(FILE *out) +{ + (void) fprintf(out, "\tdestroy:\t[-z zonename] [link]...\n"); +} + +static int +vndadm_destroy(int argc, char *argv[]) +{ + vnd_handle_t *vhp; + int c, syserr; + vnd_errno_t vnderr; + const char *zonename = NULL; + + optind = 0; + while ((c = getopt(argc, argv, ":z:")) != -1) { + switch (c) { + case 'z': + zonename = optarg; + break; + case ':': + return (usage("-%c requires an operand\n", optopt)); + case '?': + return (usage("unknown option: -%c\n", optopt)); + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) { + return (usage("extraneous arguments\n")); + } + + vhp = vnd_open(zonename, argv[0], &vnderr, &syserr); + if (vhp == NULL) { + vnd_libwarn(vnderr, syserr, "failed to open link: %s", argv[0]); + return (1); + } + + if (vnd_unlink(vhp) != 0) { + vnd_libwarn(vnd_errno(vhp), vnd_syserrno(vhp), + "failed to destroy link %s", argv[0]); + return (1); + } + + vnd_close(vhp); + return (0); +} + +static void +vndadm_list_usage(FILE *out) +{ + (void) fprintf(out, "\tlist:\t\t[-p] [-d delim] [-o field,...] " + "[-z zonename] [link]...\n"); +} + +#define VNDADM_LIST_NFIELDS 3 + +typedef struct vndadm_list_cb { + int vsc_argc; + char **vsc_argv; + int vsc_found; + boolean_t vsc_parse; + const char *vsc_delim; + int vsc_order[VNDADM_LIST_NFIELDS]; + int vsc_last; + zoneid_t vsc_zid; +} vndadm_list_cb_t; + +typedef struct vndadm_list_field { + const char *vlf_name; + const char *vlf_header; + int vlf_size; + void (*vlf_print)(struct vndadm_list_field *, vnd_info_t *, boolean_t); + void (*vlf_parse)(struct vndadm_list_field *, vnd_info_t *, boolean_t); +} vndadm_list_field_t; + +static void +vlf_print_link(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + if (last == B_TRUE) { + (void) printf("%s", viip->vi_name); + } else { + (void) printf("%-*s", vlfp->vlf_size, viip->vi_name); + } +} + +/* ARGSUSED */ +static void +vlf_parse_link(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + (void) printf("%s", viip->vi_name); +} + +static void +vlf_print_datalink(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + if (last == B_TRUE) { + (void) printf("%s", viip->vi_datalink); + } else { + (void) printf("%-*s", vlfp->vlf_size, viip->vi_datalink); + } +} + +/* ARGSUSED */ +static void +vlf_parse_datalink(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + (void) printf("%s", viip->vi_datalink); +} + +static void +vlf_print_zone(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + char buf[ZONENAME_MAX]; + + if (getzonenamebyid(viip->vi_zone, buf, sizeof (buf)) <= 0) + (void) strlcpy(buf, "<unknown>", sizeof (buf)); + + if (last == B_TRUE) { + (void) printf("%s", buf); + } else { + (void) printf("%-*s", vlfp->vlf_size, buf); + } +} + +/* ARGSUSED */ +static void +vlf_parse_zone(vndadm_list_field_t *vlfp, vnd_info_t *viip, + boolean_t last) +{ + char buf[ZONENAME_MAX]; + + if (getzonenamebyid(viip->vi_zone, buf, sizeof (buf)) <= 0) + (void) strlcpy(buf, "<unknown>", sizeof (buf)); + + (void) printf("%s", buf); +} + +static vndadm_list_field_t vlf_tbl[] = { + { "name", "NAME", 16, vlf_print_link, vlf_parse_link }, + { "datalink", "DATALINK", 16, vlf_print_datalink, vlf_parse_datalink }, + { "zone", "ZONENAME", 32, vlf_print_zone, vlf_parse_zone }, + { NULL } +}; + + +static int +vndadm_list_f(vnd_info_t *viip, void *arg) +{ + int i; + boolean_t found; + vndadm_list_cb_t *vscp = arg; + + if (vscp->vsc_zid != ALL_ZONES && vscp->vsc_zid != viip->vi_zone) + return (0); + + if (vscp->vsc_argc != 0) { + found = B_FALSE; + for (i = 0; i < vscp->vsc_argc; i++) { + if (strcmp(viip->vi_name, vscp->vsc_argv[i]) == 0) { + found = B_TRUE; + break; + } + } + if (found == B_FALSE) + return (0); + vscp->vsc_found++; + } + + for (i = 0; i < VNDADM_LIST_NFIELDS && vscp->vsc_order[i] != -1; i++) { + boolean_t last = i == vscp->vsc_last; + if (vscp->vsc_parse == B_TRUE) + vlf_tbl[vscp->vsc_order[i]].vlf_parse( + &vlf_tbl[vscp->vsc_order[i]], viip, last); + else + vlf_tbl[vscp->vsc_order[i]].vlf_print( + &vlf_tbl[vscp->vsc_order[i]], viip, last); + + if (last == B_FALSE) + (void) printf("%s", vscp->vsc_delim); + } + (void) printf("\n"); + + return (0); +} + +static int +vndadm_list(int argc, char *argv[]) +{ + int c, i, syserr; + vnd_errno_t vnderr; + boolean_t parse = B_FALSE; + const char *zonename = NULL, *delim = NULL; + char *fields = NULL; + vndadm_list_cb_t vsc; + + optind = 0; + while ((c = getopt(argc, argv, ":pd:o:z:")) != -1) { + switch (c) { + case 'p': + parse = B_TRUE; + break; + case 'd': + delim = optarg; + break; + case 'o': + fields = optarg; + break; + case 'z': + zonename = optarg; + break; + case ':': + return (usage("-%c requires an operand\n", optopt)); + case '?': + return (usage("unknown option: -%c\n", optopt)); + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + vsc.vsc_argc = argc; + vsc.vsc_argv = argv; + vsc.vsc_found = 0; + if (zonename != NULL) { + vsc.vsc_zid = getzoneidbyname(zonename); + if (vsc.vsc_zid == -1) { + vnd_warn("no such zone: %s\n", zonename); + return (1); + } + } else { + vsc.vsc_zid = ALL_ZONES; + } + + /* Sanity check parseable related stuff */ + if (delim != NULL && parse == B_FALSE) { + return (usage("-d cannot be used without -p\n")); + } + + if (parse == B_TRUE && fields == NULL) { + return (usage("-p cannot be used without -o\n")); + } + + /* validate our fields, if any */ + if (fields != NULL) { + char *c, *n; + int floc = 0; + + c = fields; + for (;;) { + if (floc >= VNDADM_LIST_NFIELDS) { + return (usage("too many fields specified " + "for -o\n")); + } + + n = strchr(c, ','); + if (n != NULL) + *n = '\0'; + + for (i = 0; i < VNDADM_LIST_NFIELDS; i++) { + if (strcasecmp(c, vlf_tbl[i].vlf_name) == 0) + break; + } + if (i == VNDADM_LIST_NFIELDS) { + vnd_warn("invalid field for -o: %s\nvalid " + "fields are:", c); + for (i = 0; i < VNDADM_LIST_NFIELDS; i++) + vnd_warn(" %s", vlf_tbl[i].vlf_name); + vnd_warn("\n"); + return (usage(NULL)); + } + vsc.vsc_order[floc] = i; + floc++; + + if (n == NULL) + break; + c = n + 1; + } + + vsc.vsc_last = floc - 1; + while (floc < VNDADM_LIST_NFIELDS) + vsc.vsc_order[floc++] = -1; + } else { + vsc.vsc_order[0] = 0; + vsc.vsc_order[1] = 1; + vsc.vsc_order[2] = 2; + } + + vsc.vsc_parse = parse; + vsc.vsc_delim = delim; + if (vsc.vsc_delim == NULL) + vsc.vsc_delim = " "; + + if (vsc.vsc_parse != B_TRUE) { + for (i = 0; i < VNDADM_LIST_NFIELDS && vsc.vsc_order[i] != -1; + i++) { + if (i + 1 == VNDADM_LIST_NFIELDS) { + (void) printf("%s\n", + vlf_tbl[vsc.vsc_order[i]].vlf_header); + continue; + } + (void) printf("%-*s ", + vlf_tbl[vsc.vsc_order[i]].vlf_size, + vlf_tbl[vsc.vsc_order[i]].vlf_header); + } + } + + if (vnd_walk(vndadm_list_f, &vsc, &vnderr, &syserr) != 0) { + vnd_libwarn(vnderr, syserr, "failed to walk vnd links"); + return (1); + } + + if (argc > 0 && vsc.vsc_found == 0) { + vnd_warn("no links matched requested names\n"); + return (1); + } + + return (0); +} + +typedef struct vndadm_get { + boolean_t vg_parse; + const char *vg_delim; + const char *vg_link; + int vg_argc; + char **vg_argv; +} vndadm_get_t; + +static int +vndadm_get_cb(vnd_handle_t *vhp, vnd_prop_t prop, void *arg) +{ + boolean_t writeable; + const char *perm; + vndadm_get_t *vgp = arg; + const char *name = vndadm_prop_to_name(prop); + + /* Verify if this is a prop we're supposed to print */ + if (vgp->vg_argc > 0) { + int i; + boolean_t found = B_FALSE; + for (i = 0; i < vgp->vg_argc; i++) { + if (strcmp(name, vgp->vg_argv[i]) == 0) { + found = B_TRUE; + break; + } + } + if (found == B_FALSE) + return (0); + } + + if (vnd_prop_writeable(prop, &writeable) != 0) + abort(); + + perm = writeable ? "rw" : "r-"; + + if (vgp->vg_parse == B_TRUE) { + (void) printf("%s%s%s%s%s%s", vgp->vg_link, vgp->vg_delim, + name, vgp->vg_delim, perm, vgp->vg_delim); + } else { + (void) printf("%-13s %-16s %-5s ", vgp->vg_link, name, perm); + } + + if (vndadm_propname_tbl[prop].vp_print != NULL) { + if (vndadm_propname_tbl[prop].vp_print(vhp, prop) != 0) + return (1); + } else { + (void) printf("-"); + } + (void) printf("\n"); + return (0); +} + +static int +vndadm_get(int argc, char *argv[]) +{ + vnd_handle_t *vhp; + boolean_t parse = B_FALSE; + vndadm_get_t vg; + int c, syserr; + vnd_errno_t vnderr; + const char *zonename = NULL, *delim = NULL; + + if (argc <= 0) { + return (usage("get requires a link name\n")); + } + + optind = 0; + while ((c = getopt(argc, argv, ":pd:z:")) != -1) { + switch (c) { + case 'p': + parse = B_TRUE; + break; + case 'd': + delim = optarg; + break; + case 'z': + zonename = optarg; + break; + case ':': + return (usage("-%c requires an operand\n", optopt)); + case '?': + return (usage("unknown option: -%c\n", optopt)); + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + return (usage("missing required link\n")); + } + + vhp = vnd_open(zonename, argv[0], &vnderr, &syserr); + if (vhp == NULL) { + vnd_libwarn(vnderr, syserr, "failed to open link: %s", argv[0]); + return (1); + } + + vg.vg_argc = argc - 1; + vg.vg_argv = argv + 1; + vg.vg_link = argv[0]; + vg.vg_parse = parse; + vg.vg_delim = delim != NULL ? delim : " "; + if (vg.vg_parse == B_FALSE) + (void) printf("%-13s %-16s %-5s %s\n", "LINK", "PROPERTY", + "PERM", "VALUE"); + + if (vnd_prop_iter(vhp, vndadm_get_cb, &vg) != 0) + return (1); + + return (0); +} + +static void +vndadm_get_usage(FILE *out) +{ + (void) fprintf(out, + "\tget:\t\t[-p] [-d delim] [-z zonename] link [prop]...\n"); +} + +static int +vndadm_set(int argc, char *argv[]) +{ + vnd_handle_t *vhp; + int c, i, syserr; + vnd_errno_t vnderr; + const char *zonename = NULL; + + optind = 0; + while ((c = getopt(argc, argv, ":z:")) != -1) { + switch (c) { + case 'z': + zonename = optarg; + break; + case ':': + return (usage("-%c requires an operand\n", optopt)); + case '?': + return (usage("unknown option: -%c\n", optopt)); + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + if (argc < 2) { + return (usage("missing arguments to set\n")); + } + + vhp = vnd_open(zonename, argv[0], &vnderr, &syserr); + if (vhp == NULL) { + vnd_libwarn(vnderr, syserr, "failed to open link: %s", argv[0]); + return (1); + } + + for (i = 1; i < argc; i++) { + char *eq, *key, *value; + boolean_t writeable; + vnd_prop_t prop; + void *buf; + size_t psize; + int ret; + + key = argv[i]; + eq = strchr(key, '='); + if (eq == NULL) { + vnd_warn("invalid property name=value: %s\n", key); + return (1); + } + *eq = '\0'; + value = eq + 1; + if (*value == '\0') { + vnd_warn("property value required for %s\n", key); + return (1); + } + prop = vndadm_name_to_prop(key); + if (prop == VND_PROP_MAX) { + vnd_warn("unknown property: %s\n", key); + return (1); + } + + if (vnd_prop_writeable(prop, &writeable) != 0) + abort(); + if (writeable != B_TRUE) { + vnd_warn("property %s is read-only\n", key); + return (1); + } + assert(vndadm_propname_tbl[prop].vp_parse != NULL); + + /* + * vp_parse functions should say what explicitly is invalid. We + * should indicate that the property failed. + */ + ret = vndadm_propname_tbl[prop].vp_parse(value, &buf, &psize); + if (ret != 0) { + vnd_warn("failed to set property %s\n", key); + return (1); + } + + ret = vnd_prop_set(vhp, prop, buf, psize); + free(buf); + if (ret != 0) { + vnd_libwarn(vnd_errno(vhp), vnd_syserrno(vhp), + "failed to set property %s", key); + return (1); + } + } + + return (0); +} + +static void +vndadm_set_usage(FILE *out) +{ + (void) fprintf(out, "\tset:\t\t[-z zonename] link prop=val...\n"); +} + +typedef struct vnd_cmdtab { + const char *vc_name; + int (*vc_op)(int, char *[]); + void (*vc_usage)(FILE *); +} vnd_cmdtab_t; + +static vnd_cmdtab_t vnd_tab[] = { + { "create", vndadm_create, vndadm_create_usage }, + { "destroy", vndadm_destroy, vndadm_destroy_usage }, + { "list", vndadm_list, vndadm_list_usage }, + { "get", vndadm_get, vndadm_get_usage }, + { "set", vndadm_set, vndadm_set_usage }, + { NULL, NULL } +}; + +static int +usage(const char *format, ...) +{ + vnd_cmdtab_t *tab; + const char *help = "usage: %s <subcommand> <args> ...\n"; + + if (format != NULL) { + va_list alist; + + va_start(alist, format); + (void) fprintf(stderr, "%s: ", vnd_pname); + (void) vfprintf(stderr, format, alist); + va_end(alist); + } + (void) fprintf(stderr, help, vnd_pname); + for (tab = vnd_tab; tab->vc_name != NULL; tab++) + tab->vc_usage(stderr); + + return (2); +} + +int +main(int argc, char *argv[]) +{ + vnd_cmdtab_t *tab; + + vnd_pname = basename(argv[0]); + if (argc < 2) { + return (usage(NULL)); + } + + for (tab = vnd_tab; tab->vc_name != NULL; tab++) { + if (strcmp(argv[1], tab->vc_name) == 0) { + argc -= 2; argv += 2; + assert(argc >= 0); + return (tab->vc_op(argc, argv)); + } + } + + return (usage("unknown sub-command '%s'\n", argv[1])); +} diff --git a/usr/src/cmd/vndstat/Makefile b/usr/src/cmd/vndstat/Makefile new file mode 100644 index 0000000000..c77eef3887 --- /dev/null +++ b/usr/src/cmd/vndstat/Makefile @@ -0,0 +1,33 @@ +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2014 Joyent, Inc. All rights reserved. +# + +PROG= vndstat + +include ../Makefile.cmd + +LDLIBS += -lkstat + +.KEEP_STATE: + +all: $(PROG) + +install: all $(ROOTPROG) + +clean: + $(RM) $(PROG) + +lint: lint_PROG + +include ../Makefile.targ diff --git a/usr/src/cmd/vndstat/vndstat.c b/usr/src/cmd/vndstat/vndstat.c new file mode 100644 index 0000000000..6f6c76fc12 --- /dev/null +++ b/usr/src/cmd/vndstat/vndstat.c @@ -0,0 +1,542 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright (c) 2014, Joyent, Inc. All rights reserved. + */ + +#include <sys/kstat.h> +#include <kstat.h> +#include <stdlib.h> +#include <unistd.h> +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <alloca.h> +#include <signal.h> +#include <sys/varargs.h> +#include <sys/int_limits.h> +#include <sys/sysmacros.h> + +#define KSTAT_FIELD_USEINSTANCE 0x01 +#define KSTAT_FIELD_NODELTA 0x02 +#define KSTAT_FIELD_FILLER 0x04 +#define KSTAT_FIELD_STRING 0x08 +#define KSTAT_FIELD_UNIT 0x10 +#define KSTAT_FIELD_LJUST 0x20 + +typedef struct kstat_field { + char *ksf_header; /* header for field */ + char *ksf_name; /* name of stat, if any */ + int ksf_width; /* width for field in output line */ + uint32_t ksf_flags; /* flags for this field, if any */ + char *ksf_suffix; /* optional suffix for units */ + int ksf_hint; /* index hint for field in kstat */ +} kstat_field_t; + +typedef struct kstat_instance { + char ksi_name[KSTAT_STRLEN]; /* name of the underlying kstat */ + int ksi_instance; /* instance identifer of this kstat */ + kstat_t *ksi_ksp; /* pointer to the kstat */ + uint64_t *ksi_data[2]; /* pointer to two generations of data */ + hrtime_t ksi_snaptime[2]; /* hrtime for data generations */ + int ksi_gen; /* current generation */ + struct kstat_instance *ksi_next; /* next in instance list */ +} kstat_instance_t; + +const char *g_cmd = "vndstat"; + +static void +kstat_nicenum(uint64_t num, char *buf, size_t buflen) +{ + uint64_t n = num; + int index = 0; + char u; + + while (n >= 1024) { + n /= 1024; + index++; + } + + u = " KMGTPE"[index]; + + if (index == 0) { + (void) snprintf(buf, buflen, "%llu", n); + } else if ((num & ((1ULL << 10 * index) - 1)) == 0) { + /* + * If this is an even multiple of the base, always display + * without any decimal precision. + */ + (void) snprintf(buf, buflen, "%llu%c", n, u); + } else { + /* + * We want to choose a precision that reflects the best choice + * for fitting in 5 characters. This can get rather tricky when + * we have numbers that are very close to an order of magnitude. + * For example, when displaying 10239 (which is really 9.999K), + * we want only a single place of precision for 10.0K. We could + * develop some complex heuristics for this, but it's much + * easier just to try each combination in turn. + */ + int i; + for (i = 2; i >= 0; i--) { + if (snprintf(buf, buflen, "%.*f%c", i, + (double)num / (1ULL << 10 * index), u) <= 5) + break; + } + } +} + +static void +fatal(char *fmt, ...) +{ + va_list ap; + int error = errno; + + va_start(ap, fmt); + + (void) fprintf(stderr, "%s: ", g_cmd); + /*LINTED*/ + (void) vfprintf(stderr, fmt, ap); + + if (fmt[strlen(fmt) - 1] != '\n') + (void) fprintf(stderr, ": %s\n", strerror(error)); + + exit(EXIT_FAILURE); +} + +int +kstat_field_hint(kstat_t *ksp, kstat_field_t *field) +{ + kstat_named_t *nm = KSTAT_NAMED_PTR(ksp); + int i; + + assert(ksp->ks_type == KSTAT_TYPE_NAMED); + + for (i = 0; i < ksp->ks_ndata; i++) { + if (strcmp(field->ksf_name, nm[i].name) == 0) + return (field->ksf_hint = i); + } + + fatal("could not find field '%s' in %s:%d\n", + field->ksf_name, ksp->ks_name, ksp->ks_instance); + + return (0); +} + +int +kstat_instances_compare(const void *lhs, const void *rhs) +{ + kstat_instance_t *l = *((kstat_instance_t **)lhs); + kstat_instance_t *r = *((kstat_instance_t **)rhs); + int rval; + + if ((rval = strcmp(l->ksi_name, r->ksi_name)) != 0) + return (rval); + + if (l->ksi_instance < r->ksi_instance) + return (-1); + + if (l->ksi_instance > r->ksi_instance) + return (1); + + return (0); +} + +void +kstat_instances_update(kstat_ctl_t *kcp, kstat_instance_t **head, + boolean_t (*interested)(kstat_t *)) +{ + int ninstances = 0, i; + kstat_instance_t **sorted, *ksi, *next; + kstat_t *ksp; + kid_t kid; + + if ((kid = kstat_chain_update(kcp)) == 0 && *head != NULL) + return; + + if (kid == -1) + fatal("failed to update kstat chain"); + + for (ksi = *head; ksi != NULL; ksi = ksi->ksi_next) + ksi->ksi_ksp = NULL; + + for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) { + kstat_instance_t *last = NULL; + + if (!interested(ksp)) + continue; + + /* + * Now look to see if we have this instance and name. (Yes, + * this is a linear search; we're assuming that this list is + * modest in size.) + */ + for (ksi = *head; ksi != NULL; ksi = ksi->ksi_next) { + last = ksi; + + if (ksi->ksi_instance != ksp->ks_instance) + continue; + + if (strcmp(ksi->ksi_name, ksp->ks_name) != 0) + continue; + + ksi->ksi_ksp = ksp; + ninstances++; + break; + } + + if (ksi != NULL) + continue; + + if ((ksi = malloc(sizeof (kstat_instance_t))) == NULL) + fatal("could not allocate memory for stat instance"); + + bzero(ksi, sizeof (kstat_instance_t)); + (void) strlcpy(ksi->ksi_name, ksp->ks_name, KSTAT_STRLEN); + ksi->ksi_instance = ksp->ks_instance; + ksi->ksi_ksp = ksp; + ksi->ksi_next = NULL; + + if (last == NULL) { + assert(*head == NULL); + *head = ksi; + } else { + last->ksi_next = ksi; + } + + ninstances++; + } + + /* + * Now we know how many instances we have; iterate back over them, + * pruning the stale ones and adding the active ones to a holding + * array in which to sort them. + */ + sorted = (void *)alloca(ninstances * sizeof (kstat_instance_t *)); + ninstances = 0; + + for (ksi = *head; ksi != NULL; ksi = next) { + next = ksi->ksi_next; + + if (ksi->ksi_ksp == NULL) { + free(ksi); + } else { + sorted[ninstances++] = ksi; + } + } + + if (ninstances == 0) { + *head = NULL; + return; + } + + qsort(sorted, ninstances, sizeof (kstat_instance_t *), + kstat_instances_compare); + + *head = sorted[0]; + + for (i = 0; i < ninstances; i++) { + ksi = sorted[i]; + ksi->ksi_next = i < ninstances - 1 ? sorted[i + 1] : NULL; + } +} + +void +kstat_instances_read(kstat_ctl_t *kcp, kstat_instance_t *instances, + kstat_field_t *fields) +{ + kstat_instance_t *ksi; + int i, nfields; + + for (nfields = 0; fields[nfields].ksf_header != NULL; nfields++) + continue; + + for (ksi = instances; ksi != NULL; ksi = ksi->ksi_next) { + kstat_t *ksp = ksi->ksi_ksp; + + if (ksp == NULL) + continue; + + if (kstat_read(kcp, ksp, NULL) == -1) { + if (errno == ENXIO) { + /* + * Our kstat has been removed since the update; + * NULL it out to prevent us from trying to read + * it again (and to indicate that it should not + * be displayed) and drive on. + */ + ksi->ksi_ksp = NULL; + continue; + } + + fatal("failed to read kstat %s:%d", + ksi->ksi_name, ksi->ksi_instance); + } + + if (ksp->ks_type != KSTAT_TYPE_NAMED) { + fatal("%s:%d is not a named kstat", ksi->ksi_name, + ksi->ksi_instance); + } + + if (ksi->ksi_data[0] == NULL) { + size_t size = nfields * sizeof (uint64_t) * 2; + uint64_t *data; + + if ((data = malloc(size)) == NULL) + fatal("could not allocate memory"); + + bzero(data, size); + ksi->ksi_data[0] = data; + ksi->ksi_data[1] = &data[nfields]; + } + + for (i = 0; i < nfields; i++) { + kstat_named_t *nm = KSTAT_NAMED_PTR(ksp); + kstat_field_t *field = &fields[i]; + int hint = field->ksf_hint; + + if (field->ksf_name == NULL) + continue; + + if (hint < 0 || hint >= ksp->ks_ndata || + strcmp(field->ksf_name, nm[hint].name) != 0) { + hint = kstat_field_hint(ksp, field); + } + + if (field->ksf_flags & KSTAT_FIELD_STRING) + ksi->ksi_data[ksi->ksi_gen][i] = + (uint64_t)(uintptr_t) + nm[hint].value.str.addr.ptr; + else + ksi->ksi_data[ksi->ksi_gen][i] = + nm[hint].value.ui64; + } + + ksi->ksi_snaptime[ksi->ksi_gen] = ksp->ks_snaptime; + ksi->ksi_gen ^= 1; + } +} + +uint64_t +kstat_instances_delta(kstat_instance_t *ksi, int i) +{ + int gen = ksi->ksi_gen; + uint64_t delta = ksi->ksi_data[gen ^ 1][i] - ksi->ksi_data[gen][i]; + uint64_t tdelta = ksi->ksi_snaptime[gen ^ 1] - ksi->ksi_snaptime[gen]; + + return (((delta * (uint64_t)NANOSEC) + (tdelta / 2)) / tdelta); +} + +void +kstat_instances_print(kstat_instance_t *instances, kstat_field_t *fields, + boolean_t header) +{ + kstat_instance_t *ksi = instances; + int i, nfields; + + for (nfields = 0; fields[nfields].ksf_header != NULL; nfields++) + continue; + + if (header) { + for (i = 0; i < nfields; i++) { + if (fields[i].ksf_flags & KSTAT_FIELD_LJUST) { + (void) printf("%s%c", fields[i].ksf_header, + i < nfields - 1 ? ' ' : '\n'); + continue; + } + (void) printf("%*s%c", fields[i].ksf_width, + fields[i].ksf_header, i < nfields - 1 ? ' ' : '\n'); + } + } + + for (ksi = instances; ksi != NULL; ksi = ksi->ksi_next) { + if (ksi->ksi_snaptime[1] == 0 || ksi->ksi_ksp == NULL) + continue; + + for (i = 0; i < nfields; i++) { + char trailer = i < nfields - 1 ? ' ' : '\n'; + + if (fields[i].ksf_flags & KSTAT_FIELD_FILLER) { + (void) printf("%*s%c", fields[i].ksf_width, + fields[i].ksf_header, trailer); + continue; + } + + if (fields[i].ksf_flags & KSTAT_FIELD_STRING) { + (void) printf("%*s%c", fields[i].ksf_width, + (char *)(uintptr_t)ksi->ksi_data[ + ksi->ksi_gen ^ 1][i], + trailer); + continue; + } + + if (fields[i].ksf_flags & KSTAT_FIELD_UNIT) { + char buf[128]; + size_t flen = fields[i].ksf_width + 1; + const char *suffix = ""; + + if (fields[i].ksf_suffix != NULL) { + suffix = fields[i].ksf_suffix; + flen -= strlen(fields[i].ksf_suffix); + } + + kstat_nicenum(fields[i].ksf_flags & + KSTAT_FIELD_NODELTA ? + ksi->ksi_data[ksi->ksi_gen ^ 1][i] : + kstat_instances_delta(ksi, i), buf, + MIN(sizeof (buf), flen)); + (void) printf("%*s%s%c", flen - 1, buf, + suffix, trailer); + continue; + } + + (void) printf("%*lld%c", fields[i].ksf_width, + fields[i].ksf_flags & KSTAT_FIELD_USEINSTANCE ? + ksi->ksi_instance : + fields[i].ksf_flags & KSTAT_FIELD_NODELTA ? + ksi->ksi_data[ksi->ksi_gen ^ 1][i] : + kstat_instances_delta(ksi, i), trailer); + } + } +} + +static boolean_t +interested(kstat_t *ksp) +{ + const char *module = "vnd"; + const char *class = "net"; + + if (strcmp(ksp->ks_module, module) != 0) + return (B_FALSE); + + if (strcmp(ksp->ks_class, class) != 0) + return (B_FALSE); + + return (B_TRUE); +} + +/* BEGIN CSTYLED */ +char *g_usage = "Usage: vndstat [interval [count]]\n" + "\n" + " Displays statistics for active vnd devices, with one line per device.\n" + " All statistics are reported as per-second rates.\n" + "\n" + " The columns are as follows:\n" + "\n" + " zone => name of the zone with the device\n" + " name => name of the vnd device\n" + " rx => bytes received\n" + " tx => bytes transmitted\n" + " drops => number of dropped packets\n" + " txfc => number of transmit flow control events\n" + "\n"; +/* END CSTYLED */ + +void +usage() +{ + (void) fprintf(stderr, "%s", g_usage); + exit(EXIT_FAILURE); +} + +/*ARGSUSED*/ +void +intr(int sig) +{} + +/*ARGSUSED*/ +int +main(int argc, char **argv) +{ + kstat_ctl_t *kcp; + kstat_instance_t *instances = NULL; + int i = 0; + int interval = 1; + int count = INT32_MAX; + struct itimerval itimer; + struct sigaction act; + sigset_t set; + char *endp; + + kstat_field_t fields[] = { + { "name", "linkname", 6, KSTAT_FIELD_STRING }, + { "|", NULL, 1, KSTAT_FIELD_FILLER }, + { "rx B/s", "rbytes", 8, KSTAT_FIELD_UNIT, "B/s" }, + { "|", NULL, 1, KSTAT_FIELD_FILLER }, + { "tx B/s", "obytes", 8, KSTAT_FIELD_UNIT, "B/s" }, + { "|", NULL, 1, KSTAT_FIELD_FILLER }, + { "drops", "total_drops", 5 }, + { "txfc", "flowcontrol_events", 4 }, + { "|", NULL, 1, KSTAT_FIELD_FILLER }, + { "zone", "zonename", 36, + KSTAT_FIELD_STRING | KSTAT_FIELD_LJUST }, + { NULL } + }; + + if (argc > 1) { + interval = strtol(argv[1], &endp, 10); + + if (*endp != '\0' || interval <= 0) + usage(); + } + + if (argc > 2) { + count = strtol(argv[2], &endp, 10); + + if (*endp != '\0' || count <= 0) + usage(); + } + + if ((kcp = kstat_open()) == NULL) + fatal("could not open /dev/kstat"); + + (void) sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = intr; + (void) sigaction(SIGALRM, &act, NULL); + + (void) sigemptyset(&set); + (void) sigaddset(&set, SIGALRM); + (void) sigprocmask(SIG_BLOCK, &set, NULL); + + bzero(&itimer, sizeof (itimer)); + itimer.it_value.tv_sec = interval; + itimer.it_interval.tv_sec = interval; + + if (setitimer(ITIMER_REAL, &itimer, NULL) != 0) { + fatal("could not set timer to %d second%s", interval, + interval == 1 ? "" : "s"); + } + + (void) sigemptyset(&set); + + for (;;) { + kstat_instances_update(kcp, &instances, interested); + kstat_instances_read(kcp, instances, fields); + + if (i++ > 0) { + kstat_instances_print(instances, fields, + instances != NULL && instances->ksi_next == NULL ? + (((i - 2) % 20) == 0) : B_TRUE); + } + + if (i > count) + break; + + (void) sigsuspend(&set); + } + + /*NOTREACHED*/ + return (0); +} diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c index 695da7fcbc..c8c7c525ba 100644 --- a/usr/src/cmd/zfs/zfs_main.c +++ b/usr/src/cmd/zfs/zfs_main.c @@ -248,9 +248,9 @@ get_usage(zfs_help_t idx) return (gettext("\tclone [-p] [-o property=value] ... " "<snapshot> <filesystem|volume>\n")); case HELP_CREATE: - return (gettext("\tcreate [-p] [-o property=value] ... " + return (gettext("\tcreate [-Pnpv] [-o property=value] ... " "<filesystem>\n" - "\tcreate [-ps] [-b blocksize] [-o property=value] ... " + "\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... " "-V <size> <volume>\n")); case HELP_DESTROY: return (gettext("\tdestroy [-fnpRrv] <filesystem|volume>\n" @@ -258,7 +258,7 @@ get_usage(zfs_help_t idx) "<filesystem|volume>@<snap>[%<snap>][,...]\n" "\tdestroy <filesystem|volume>#<bookmark>\n")); case HELP_GET: - return (gettext("\tget [-rHp] [-d max] " + return (gettext("\tget [-crHp] [-d max] " "[-o \"all\" | field[,...]]\n" "\t [-t type[,...]] [-s source[,...]]\n" "\t <\"all\" | property[,...]> " @@ -704,7 +704,7 @@ should_auto_mount(zfs_handle_t *zhp) } /* - * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol> + * zfs clone [-Fp] [-o prop=value] ... <snap> <fs | vol> * * Given an existing dataset, create a writable copy whose initial contents * are the same as the source. The newly created dataset maintains a @@ -712,12 +712,18 @@ should_auto_mount(zfs_handle_t *zhp) * the clone exists. * * The '-p' flag creates all the non-existing ancestors of the target first. + * + * The '-F' flag retries the zfs_mount() operation as long as zfs_mount() is + * still returning EBUSY. Any callers which specify -F should be careful to + * ensure that no other process has a persistent hold on the mountpoint's + * directory. */ static int zfs_do_clone(int argc, char **argv) { zfs_handle_t *zhp = NULL; boolean_t parents = B_FALSE; + boolean_t keeptrying = B_FALSE; nvlist_t *props; int ret = 0; int c; @@ -726,8 +732,11 @@ zfs_do_clone(int argc, char **argv) nomem(); /* check options */ - while ((c = getopt(argc, argv, "o:p")) != -1) { + while ((c = getopt(argc, argv, "Fo:p")) != -1) { switch (c) { + case 'F': + keeptrying = B_TRUE; + break; case 'o': if (!parseprop(props, optarg)) { nvlist_free(props); @@ -796,11 +805,16 @@ zfs_do_clone(int argc, char **argv) * step. */ if (should_auto_mount(clone)) { - if ((ret = zfs_mount(clone, NULL, 0)) != 0) { - (void) fprintf(stderr, gettext("clone " - "successfully created, " - "but not mounted\n")); - } else if ((ret = zfs_share(clone)) != 0) { + while ((ret = zfs_mount(clone, NULL, 0)) != 0) { + if (!keeptrying || errno != EBUSY) { + (void) fprintf(stderr, + gettext("clone " + "successfully created, " + "but not mounted\n")); + break; + } + } + if (ret == 0 && (ret = zfs_share(clone)) != 0) { (void) fprintf(stderr, gettext("clone " "successfully created, " "but not shared\n")); @@ -824,8 +838,8 @@ usage: } /* - * zfs create [-p] [-o prop=value] ... fs - * zfs create [-ps] [-b blocksize] [-o prop=value] ... -V vol size + * zfs create [-Pnpv] [-o prop=value] ... fs + * zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size * * Create a new dataset. This command can be used to create filesystems * and volumes. Snapshot creation is handled by 'zfs snapshot'. @@ -837,17 +851,30 @@ usage: * SPA_VERSION_REFRESERVATION, we set a refreservation instead. * * The '-p' flag creates all the non-existing ancestors of the target first. + * + * The '-n' flag is no-op (dry run) mode. This will perform a user-space sanity + * check of arguments and properties, but does not check for permissions, + * available space, etc. + * + * The '-v' flag is for verbose output. + * + * The '-P' flag is used for parseable output. It implies '-v'. */ static int zfs_do_create(int argc, char **argv) { zfs_type_t type = ZFS_TYPE_FILESYSTEM; zfs_handle_t *zhp = NULL; + zpool_handle_t *zpool_handle = NULL; + nvlist_t *real_props = NULL; uint64_t volsize = 0; int c; boolean_t noreserve = B_FALSE; boolean_t bflag = B_FALSE; boolean_t parents = B_FALSE; + boolean_t dryrun = B_FALSE; + boolean_t verbose = B_FALSE; + boolean_t parseable = B_FALSE; int ret = 1; nvlist_t *props; uint64_t intval; @@ -856,7 +883,7 @@ zfs_do_create(int argc, char **argv) nomem(); /* check options */ - while ((c = getopt(argc, argv, ":V:b:so:p")) != -1) { + while ((c = getopt(argc, argv, ":PV:b:nso:pv")) != -1) { switch (c) { case 'V': type = ZFS_TYPE_VOLUME; @@ -872,6 +899,10 @@ zfs_do_create(int argc, char **argv) nomem(); volsize = intval; break; + case 'P': + verbose = B_TRUE; + parseable = B_TRUE; + break; case 'p': parents = B_TRUE; break; @@ -889,6 +920,9 @@ zfs_do_create(int argc, char **argv) intval) != 0) nomem(); break; + case 'n': + dryrun = B_TRUE; + break; case 'o': if (!parseprop(props, optarg)) goto error; @@ -896,6 +930,9 @@ zfs_do_create(int argc, char **argv) case 's': noreserve = B_TRUE; break; + case 'v': + verbose = B_TRUE; + break; case ':': (void) fprintf(stderr, gettext("missing size " "argument\n")); @@ -927,14 +964,9 @@ zfs_do_create(int argc, char **argv) goto badusage; } - if (type == ZFS_TYPE_VOLUME && !noreserve) { - zpool_handle_t *zpool_handle; - nvlist_t *real_props = NULL; - uint64_t spa_version; + if (dryrun || (type == ZFS_TYPE_VOLUME && !noreserve)) { + char msg[ZFS_MAX_DATASET_NAME_LEN * 2]; char *p; - zfs_prop_t resv_prop; - char *strval; - char msg[1024]; if ((p = strchr(argv[0], '/')) != NULL) *p = '\0'; @@ -943,25 +975,31 @@ zfs_do_create(int argc, char **argv) *p = '/'; if (zpool_handle == NULL) goto error; - spa_version = zpool_get_prop_int(zpool_handle, - ZPOOL_PROP_VERSION, NULL); - if (spa_version >= SPA_VERSION_REFRESERVATION) - resv_prop = ZFS_PROP_REFRESERVATION; - else - resv_prop = ZFS_PROP_RESERVATION; (void) snprintf(msg, sizeof (msg), + dryrun ? gettext("cannot verify '%s'") : gettext("cannot create '%s'"), argv[0]); if (props && (real_props = zfs_valid_proplist(g_zfs, type, props, 0, NULL, zpool_handle, B_TRUE, msg)) == NULL) { zpool_close(zpool_handle); goto error; } + } + + if (type == ZFS_TYPE_VOLUME && !noreserve) { + uint64_t spa_version; + zfs_prop_t resv_prop; + char *strval; + + spa_version = zpool_get_prop_int(zpool_handle, + ZPOOL_PROP_VERSION, NULL); + if (spa_version >= SPA_VERSION_REFRESERVATION) + resv_prop = ZFS_PROP_REFRESERVATION; + else + resv_prop = ZFS_PROP_RESERVATION; volsize = zvol_volsize_to_reservation(zpool_handle, volsize, real_props); - nvlist_free(real_props); - zpool_close(zpool_handle); if (nvlist_lookup_string(props, zfs_prop_to_name(resv_prop), &strval) != 0) { @@ -972,6 +1010,10 @@ zfs_do_create(int argc, char **argv) } } } + if (zpool_handle != NULL) { + zpool_close(zpool_handle); + nvlist_free(real_props); + } if (parents && zfs_name_valid(argv[0], type)) { /* @@ -983,8 +1025,50 @@ zfs_do_create(int argc, char **argv) ret = 0; goto error; } - if (zfs_create_ancestors(g_zfs, argv[0]) != 0) - goto error; + if (verbose) { + (void) printf(parseable ? "create_ancestors\t%s\n" : + dryrun ? "would create ancestors of %s\n" : + "create ancestors of %s\n", argv[0]); + } + if (!dryrun) { + if (zfs_create_ancestors(g_zfs, argv[0]) != 0) { + goto error; + } + } + } + + if (verbose) { + nvpair_t *nvp = NULL; + (void) printf(parseable ? "create\t%s\n" : + dryrun ? "would create %s\n" : "create %s\n", argv[0]); + while ((nvp = nvlist_next_nvpair(props, nvp)) != NULL) { + uint64_t uval; + char *sval; + + switch (nvpair_type(nvp)) { + case DATA_TYPE_UINT64: + VERIFY0(nvpair_value_uint64(nvp, &uval)); + (void) printf(parseable ? + "property\t%s\t%llu\n" : "\t%s=%llu\n", + nvpair_name(nvp), (u_longlong_t)uval); + break; + case DATA_TYPE_STRING: + VERIFY0(nvpair_value_string(nvp, &sval)); + (void) printf(parseable ? + "property\t%s\t%s\n" : "\t%s=%s\n", + nvpair_name(nvp), sval); + break; + default: + (void) fprintf(stderr, "property '%s' " + "has illegal type %d\n", + nvpair_name(nvp), nvpair_type(nvp)); + abort(); + } + } + } + if (dryrun) { + ret = 0; + goto error; } /* pass to libzfs */ @@ -1027,12 +1111,13 @@ badusage: } /* - * zfs destroy [-rRf] <fs, vol> + * zfs destroy [-rRfF] <fs, vol> * zfs destroy [-rRd] <snap> * * -r Recursively destroy all children * -R Recursively destroy all dependents, including clones * -f Force unmounting of any dependents + * -F Continue retrying on seeing EBUSY * -d If we can't destroy now, mark for deferred destruction * * Destroys the given dataset. By default, it will unmount any filesystems, @@ -1042,6 +1127,7 @@ badusage: typedef struct destroy_cbdata { boolean_t cb_first; boolean_t cb_force; + boolean_t cb_wait; boolean_t cb_recurse; boolean_t cb_error; boolean_t cb_doclones; @@ -1125,13 +1211,18 @@ out: static int destroy_callback(zfs_handle_t *zhp, void *data) { - destroy_cbdata_t *cb = data; + destroy_cbdata_t *cbp = data; + struct timespec ts; + int err = 0; + + ts.tv_sec = 0; + ts.tv_nsec = 500 * (NANOSEC / MILLISEC); const char *name = zfs_get_name(zhp); - if (cb->cb_verbose) { - if (cb->cb_parsable) { + if (cbp->cb_verbose) { + if (cbp->cb_parsable) { (void) printf("destroy\t%s\n", name); - } else if (cb->cb_dryrun) { + } else if (cbp->cb_dryrun) { (void) printf(gettext("would destroy %s\n"), name); } else { @@ -1146,13 +1237,10 @@ destroy_callback(zfs_handle_t *zhp, void *data) */ if (strchr(zfs_get_name(zhp), '/') == NULL && zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) { - zfs_close(zhp); - return (0); - } - if (cb->cb_dryrun) { - zfs_close(zhp); - return (0); + goto out; } + if (cbp->cb_dryrun) + goto out; /* * We batch up all contiguous snapshots (even of different @@ -1161,23 +1249,66 @@ destroy_callback(zfs_handle_t *zhp, void *data) * because we must delete a clone before its origin. */ if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) { - fnvlist_add_boolean(cb->cb_batchedsnaps, name); - } else { - int error = zfs_destroy_snaps_nvl(g_zfs, - cb->cb_batchedsnaps, B_FALSE); - fnvlist_free(cb->cb_batchedsnaps); - cb->cb_batchedsnaps = fnvlist_alloc(); - - if (error != 0 || - zfs_unmount(zhp, NULL, cb->cb_force ? MS_FORCE : 0) != 0 || - zfs_destroy(zhp, cb->cb_defer_destroy) != 0) { - zfs_close(zhp); - return (-1); + fnvlist_add_boolean(cbp->cb_batchedsnaps, name); + goto out; + } + + if (cbp->cb_wait) + libzfs_print_on_error(g_zfs, B_FALSE); + + /* + * Unless instructed to retry on EBUSY, bail out on the first error. + * When retrying, try every 500ms until either succeeding or seeing a + * non-EBUSY error code. + */ + while ((err = zfs_destroy_snaps_nvl(g_zfs, + cbp->cb_batchedsnaps, B_FALSE)) != 0) { + if (cbp->cb_wait && libzfs_errno(g_zfs) == EZFS_BUSY) { + (void) nanosleep(&ts, NULL); + continue; + } + (void) fprintf(stderr, "%s: %s\n", + libzfs_error_action(g_zfs), + libzfs_error_description(g_zfs)); + break; + } + + fnvlist_free(cbp->cb_batchedsnaps); + cbp->cb_batchedsnaps = fnvlist_alloc(); + + if (err != 0) + goto out; + + while ((err = zfs_unmount(zhp, NULL, + cbp->cb_force ? MS_FORCE : 0)) != 0) { + if (cbp->cb_wait && libzfs_errno(g_zfs) == EZFS_BUSY) { + (void) nanosleep(&ts, NULL); + continue; } + (void) fprintf(stderr, "%s: %s\n", + libzfs_error_action(g_zfs), + libzfs_error_description(g_zfs)); + break; } + if (err != 0) + goto out; + + while ((err = zfs_destroy(zhp, cbp->cb_defer_destroy)) != 0) { + if (cbp->cb_wait && libzfs_errno(g_zfs) == EZFS_BUSY) { + (void) nanosleep(&ts, NULL); + continue; + } + (void) fprintf(stderr, "%s: %s\n", + libzfs_error_action(g_zfs), + libzfs_error_description(g_zfs)); + break; + } + +out: + libzfs_print_on_error(g_zfs, B_TRUE); zfs_close(zhp); - return (0); + return (err); } static int @@ -1333,7 +1464,7 @@ zfs_do_destroy(int argc, char **argv) zfs_type_t type = ZFS_TYPE_DATASET; /* check options */ - while ((c = getopt(argc, argv, "vpndfrR")) != -1) { + while ((c = getopt(argc, argv, "vpndfFrR")) != -1) { switch (c) { case 'v': cb.cb_verbose = B_TRUE; @@ -1352,6 +1483,9 @@ zfs_do_destroy(int argc, char **argv) case 'f': cb.cb_force = B_TRUE; break; + case 'F': + cb.cb_wait = B_TRUE; + break; case 'r': cb.cb_recurse = B_TRUE; break; @@ -1722,8 +1856,11 @@ zfs_do_get(int argc, char **argv) cb.cb_type = ZFS_TYPE_DATASET; /* check options */ - while ((c = getopt(argc, argv, ":d:o:s:rt:Hp")) != -1) { + while ((c = getopt(argc, argv, ":d:o:s:rt:Hcp")) != -1) { switch (c) { + case 'c': + libzfs_set_cachedprops(g_zfs, B_TRUE); + break; case 'p': cb.cb_literal = B_TRUE; break; @@ -3244,6 +3381,7 @@ zfs_do_list(int argc, char **argv) int types = ZFS_TYPE_DATASET; boolean_t types_specified = B_FALSE; char *fields = NULL; + zprop_list_t *pl; list_cbdata_t cb = { 0 }; char *value; int limit = 0; @@ -3362,6 +3500,18 @@ zfs_do_list(int argc, char **argv) != 0) usage(B_FALSE); + /* + * The default set of properties contains only properties which can be + * retrieved from the set of cached properties. If any user-specfied + * properties cannot be retrieved from that set, unset the cachedprops + * flags on the ZFS handle. + */ + libzfs_set_cachedprops(g_zfs, B_TRUE); + for (pl = cb.cb_proplist; pl != NULL; pl = pl->pl_next) { + if (zfs_prop_cacheable(pl->pl_prop)) + libzfs_set_cachedprops(g_zfs, B_FALSE); + } + cb.cb_first = B_TRUE; ret = zfs_for_each(argc, argv, flags, types, sortcol, &cb.cb_proplist, diff --git a/usr/src/cmd/zlogin/zlogin.c b/usr/src/cmd/zlogin/zlogin.c index e942e66e71..fb219918c5 100644 --- a/usr/src/cmd/zlogin/zlogin.c +++ b/usr/src/cmd/zlogin/zlogin.c @@ -23,12 +23,12 @@ * Copyright 2013 DEY Storage Systems, Inc. * Copyright (c) 2014 Gary Mills * Copyright 2015 Nexenta Systems, Inc. All rights reserved. - * Copyright 2019 Joyent, Inc. + * Copyright 2020 Joyent, Inc. * Copyright 2020 OmniOS Community Edition (OmniOSce) Association. */ /* - * zlogin provides three types of login which allow users in the global + * zlogin provides five types of login which allow users in the global * zone to access non-global zones. * * - "interactive login" is similar to rlogin(1); for example, the user could @@ -44,12 +44,22 @@ * In this mode, zlogin sets up pipes as the communication channel, and * 'su' is used to do the login setup work. * + * - "interactive command" is a combination of the above two modes where + * a command is provide like the non-interactive case, but the -i option is + * also provided to make things interactive. For example, the user could + * issue 'zlogin -i my-zone /bin/sh'. In this mode neither 'login -c' nor + * 'su root -c' is prepended to the command invocation. Because of this + * there will be no wtmpx login record within the zone. + * * - "console login" is the equivalent to accessing the tip line for a * zone. For example, the user can issue 'zlogin -C my-zone'. * In this mode, zlogin contacts the zoneadmd process via unix domain * socket. If zoneadmd is not running, it starts it. This allows the * console to be available anytime the zone is installed, regardless of * whether it is running. + * + * - "standalone-processs interactive" is specified with -I and connects to + * the zone's stdin, stdout and stderr zfd(7D) devices. */ #include <sys/socket.h> @@ -94,7 +104,8 @@ #include <auth_attr.h> #include <secdb.h> -static int masterfd; +static int masterfd = -1; +static int ctlfd = -1; static struct termios save_termios; static struct termios effective_termios; static int save_fd; @@ -103,12 +114,13 @@ static volatile int dead; static volatile pid_t child_pid = -1; static int interactive = 0; static priv_set_t *dropprivs; +static unsigned int connect_flags = 0; static int nocmdchar = 0; static int failsafe = 0; -static int disconnect = 0; static char cmdchar = '~'; static int quiet = 0; +static char zonebrand[MAXNAMELEN]; static int pollerr = 0; @@ -125,10 +137,14 @@ static boolean_t forced_login = B_FALSE; #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ #endif -#define SUPATH "/usr/bin/su" +#define SUPATH1 "/usr/bin/su" +#define SUPATH2 "/bin/su" #define FAILSAFESHELL "/sbin/sh" #define DEFAULTSHELL "/sbin/sh" #define DEF_PATH "/usr/sbin:/usr/bin" +#define LX_DEF_PATH "/bin:/usr/sbin:/usr/bin" + +#define MAX_RETRY 30 #define CLUSTER_BRAND_NAME "cluster" @@ -155,7 +171,7 @@ static boolean_t forced_login = B_FALSE; static void usage(void) { - (void) fprintf(stderr, gettext("usage: %s [ -dnQCES ] [ -e cmdchar ] " + (void) fprintf(stderr, gettext("usage: %s [-dinCEINQS] [-e cmdchar] " "[-l user] zonename [command [args ...] ]\n"), pname); exit(2); } @@ -250,57 +266,60 @@ postfork_dropprivs() } } -/* - * Create the unix domain socket and call the zoneadmd server; handshake - * with it to determine whether it will allow us to connect. - */ static int -get_console_master(const char *zname) +connect_zone_sock(const char *zname, const char *suffix, boolean_t verbose) { int sockfd = -1; struct sockaddr_un servaddr; - char clientid[MAXPATHLEN]; - char handshake[MAXPATHLEN], c; - int msglen; - int i = 0, err = 0; if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { - zperror(gettext("could not create socket")); + if (verbose) + zperror(gettext("could not create socket")); return (-1); } bzero(&servaddr, sizeof (servaddr)); servaddr.sun_family = AF_UNIX; (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path), - "%s/%s.console_sock", ZONES_TMPDIR, zname); - + "%s/%s.%s", ZONES_TMPDIR, zname, suffix); if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof (servaddr)) == -1) { - zperror(gettext("Could not connect to zone console")); - goto bad; + if (verbose) + zperror(gettext("Could not connect to zone")); + close(sockfd); + return (-1); } - masterfd = sockfd; + return (sockfd); +} - msglen = snprintf(clientid, sizeof (clientid), "IDENT %lu %s %d\n", - getpid(), setlocale(LC_MESSAGES, NULL), disconnect); + +static int +handshake_zone_sock(int sockfd, unsigned int flags) +{ + char clientid[MAXPATHLEN]; + char handshake[MAXPATHLEN], c; + int msglen; + int i = 0, err = 0; + + msglen = snprintf(clientid, sizeof (clientid), "IDENT %s %u\n", + setlocale(LC_MESSAGES, NULL), flags); if (msglen >= sizeof (clientid) || msglen < 0) { zerror("protocol error"); - goto bad; + return (-1); } - if (write(masterfd, clientid, msglen) != msglen) { + if (write(sockfd, clientid, msglen) != msglen) { zerror("protocol error"); - goto bad; + return (-1); } - bzero(handshake, sizeof (handshake)); - /* * Take care not to accumulate more than our fill, and leave room for * the NUL at the end. */ - while ((err = read(masterfd, &c, 1)) == 1) { + bzero(handshake, sizeof (handshake)); + while ((err = read(sockfd, &c, 1)) == 1) { if (i >= (sizeof (handshake) - 1)) break; if (c == '\n') @@ -310,26 +329,48 @@ get_console_master(const char *zname) } /* - * If something went wrong during the handshake we bail; perhaps - * the server died off. + * If something went wrong during the handshake we bail. + * Perhaps the server died off. */ if (err == -1) { - zperror(gettext("Could not connect to zone console")); - goto bad; + zperror(gettext("Could not connect to zone")); + return (-1); } - if (strncmp(handshake, "OK", sizeof (handshake)) == 0) - return (0); + if (strncmp(handshake, "OK", sizeof (handshake)) != 0) { + zerror(gettext("Zone is already in use by process ID %s."), + handshake); + return (-1); + } - zerror(gettext("Console is already in use by process ID %s."), - handshake); -bad: - (void) close(sockfd); - masterfd = -1; - return (-1); + return (0); } - +static int +send_ctl_sock(const char *buf, size_t len) +{ + char rbuf[BUFSIZ]; + int i; + if (ctlfd == -1) { + return (-1); + } + if (write(ctlfd, buf, len) != len) { + return (-1); + } + /* read the response */ + for (i = 0; i < (BUFSIZ - 1); i++) { + char c; + if (read(ctlfd, &c, 1) != 1 || c == '\n' || c == '\0') { + break; + } + rbuf[i] = c; + } + rbuf[i+1] = '\0'; + if (strncmp("OK", rbuf, BUFSIZ) != 0) { + return (-1); + } + return (0); +} /* * Routines to handle pty creation upon zone entry and to shuttle I/O back * and forth between the two terminals. We also compute and store the @@ -518,8 +559,32 @@ sigwinch(int s) { struct winsize ws; - if (ioctl(0, TIOCGWINSZ, &ws) == 0) - (void) ioctl(masterfd, TIOCSWINSZ, &ws); + if (ioctl(0, TIOCGWINSZ, &ws) == 0) { + if (ctlfd != -1) { + char buf[BUFSIZ]; + snprintf(buf, sizeof (buf), "TIOCSWINSZ %hu %hu\n", + ws.ws_row, ws.ws_col); + (void) send_ctl_sock(buf, strlen(buf)); + } else { + (void) ioctl(masterfd, TIOCSWINSZ, &ws); + } + } +} + +/* + * Toggle zfd EOF mode and notify zoneadmd + */ +/*ARGSUSED*/ +static void +sigusr1(int s) +{ + connect_flags ^= ZLOGIN_ZFD_EOF; + if (ctlfd != -1) { + char buf[BUFSIZ]; + snprintf(buf, sizeof (buf), "SETFLAGS %u\n", + connect_flags); + (void) send_ctl_sock(buf, strlen(buf)); + } } static volatile int close_on_sig = -1; @@ -863,28 +928,34 @@ doio(int stdin_fd, int appin_fd, int stdout_fd, int stderr_fd, int sig_fd, break; } - /* event from master side stdout */ - if (pollfds[0].revents) { - if (pollfds[0].revents & + /* event from master side stderr */ + if (pollfds[1].revents) { + if (pollfds[1].revents & POLLHUP) + break; + + if (pollfds[1].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { - if (process_output(stdout_fd, STDOUT_FILENO) + if (process_output(stderr_fd, STDERR_FILENO) != 0) break; } else { - pollerr = pollfds[0].revents; + pollerr = pollfds[1].revents; break; } } - /* event from master side stderr */ - if (pollfds[1].revents) { - if (pollfds[1].revents & + /* event from master side stdout */ + if (pollfds[0].revents) { + if (pollfds[0].revents & POLLHUP) + break; + + if (pollfds[0].revents & (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { - if (process_output(stderr_fd, STDERR_FILENO) + if (process_output(stdout_fd, STDOUT_FILENO) != 0) break; } else { - pollerr = pollfds[1].revents; + pollerr = pollfds[0].revents; break; } } @@ -1054,7 +1125,7 @@ zone_login_cmd(brand_handle_t bh, const char *login) * but we're going to be very simplistic about it and break stuff * up based on spaces. We're not even going to support any kind * of quoting or escape characters. It's truly amazing that - * there is no library function in OpenSolaris to do this for us. + * there is no library function in Illumos to do this for us. */ /* @@ -1093,69 +1164,155 @@ zone_login_cmd(brand_handle_t bh, const char *login) } /* - * Prepare argv array for exec'd process; if we're passing commands to the - * new process, then use su(1M) to do the invocation. Otherwise, use + * Prepare argv array for exec'd process. If commands are passed to the new + * process and su(1M) is avalable, use it for the invocation. Otherwise, use * 'login -z <from_zonename> -f' (-z is an undocumented option which tells * login that we're coming from another zone, and to disregard its CONSOLE * checks). */ static char ** -prep_args(brand_handle_t bh, const char *login, char **argv) +prep_args(brand_handle_t bh, char *zonename, const char *login, char **argv) { - int argc = 0, a = 0, i, n = -1; - char **new_argv; + int argc = 0, i; + size_t subshell_len = 1; + char *subshell = NULL, *supath = NULL; + char **new_argv = NULL; - if (argv != NULL) { - size_t subshell_len = 1; - char *subshell; + if (argv == NULL) { + if (failsafe) { + if ((new_argv = malloc(sizeof (char *) * 2)) == NULL) + return (NULL); + new_argv[0] = FAILSAFESHELL; + new_argv[1] = NULL; + } else { + new_argv = zone_login_cmd(bh, login); + } + return (new_argv); + } - while (argv[argc] != NULL) - argc++; + /* + * Attempt to locate a 'su' binary if not using the failsafe shell. + */ + if (!failsafe) { + struct stat sb; + char zonepath[MAXPATHLEN]; + char supath_check[MAXPATHLEN]; + + if (zone_get_zonepath(zonename, zonepath, + sizeof (zonepath)) != Z_OK) { + zerror(gettext("unable to determine zone " + "path")); + return (NULL); + } - for (i = 0; i < argc; i++) { - subshell_len += strlen(argv[i]) + 1; + (void) snprintf(supath_check, sizeof (supath), "%s/root/%s", + zonepath, SUPATH1); + if (stat(supath_check, &sb) == 0) { + supath = SUPATH1; + } else { + (void) snprintf(supath_check, sizeof (supath_check), + "%s/root/%s", zonepath, SUPATH2); + if (stat(supath_check, &sb) == 0) { + supath = SUPATH2; + } } - if ((subshell = calloc(1, subshell_len)) == NULL) + } + + /* + * With no failsafe shell or supath to wrap the incoming command, the + * arguments are passed straight through. + */ + if (!failsafe && supath == NULL) { + /* + * Such an outcome is not acceptable, however, if the caller + * expressed a desire to switch users. + */ + if (strcmp(login, "root") != 0) { + zerror(gettext("unable to find 'su' command")); return (NULL); + } + return (argv); + } - for (i = 0; i < argc; i++) { - (void) strcat(subshell, argv[i]); + /* + * Inventory arguments and allocate a buffer to escape them for the + * subshell. + */ + while (argv[argc] != NULL) { + /* + * Allocate enough space for the delimiter and 2 + * quotes which might be needed. + */ + subshell_len += strlen(argv[argc]) + 3; + argc++; + } + if ((subshell = calloc(1, subshell_len)) == NULL) { + return (NULL); + } + + /* + * The handling of quotes in the following block may seem unusual, but + * it is done this way for backward compatibility. + * When running a command, zlogin is documented as: + * zlogin zonename command args + * However, some code has come to depend on the following usage: + * zlogin zonename 'command args' + * This relied on the fact that the single argument would be re-parsed + * within the zone and excuted as a command with an argument. To remain + * compatible with this (incorrect) usage, if there is only a single + * argument, it is not quoted, even if it has embedded spaces. + * + * Here are two examples which both need to work: + * 1) zlogin foo 'echo hello' + * This has a single argv member with a space in it but will not be + * quoted on the command passed into the zone. + * 2) zlogin foo bash -c 'echo hello' + * This has 3 argv members. The 3rd arg has a space and must be + * quoted on the command passed into the zone. + */ + for (i = 0; i < argc; i++) { + if (i > 0) (void) strcat(subshell, " "); + + if (argc > 1 && (strchr(argv[i], ' ') != NULL || + strchr(argv[i], '\t') != NULL)) { + (void) strcat(subshell, "'"); + (void) strcat(subshell, argv[i]); + (void) strcat(subshell, "'"); + } else { + (void) strcat(subshell, argv[i]); } + } - if (failsafe) { - n = 4; - if ((new_argv = malloc(sizeof (char *) * n)) == NULL) - return (NULL); + if (failsafe) { + int a = 0, n = 4; - new_argv[a++] = FAILSAFESHELL; - } else { - n = 5; - if ((new_argv = malloc(sizeof (char *) * n)) == NULL) - return (NULL); + if ((new_argv = malloc(sizeof (char *) * n)) == NULL) + return (NULL); - new_argv[a++] = SUPATH; - if (strcmp(login, "root") != 0) { - new_argv[a++] = "-"; - n++; - } - new_argv[a++] = (char *)login; - } + new_argv[a++] = FAILSAFESHELL; new_argv[a++] = "-c"; new_argv[a++] = subshell; new_argv[a++] = NULL; assert(a == n); } else { - if (failsafe) { - n = 2; - if ((new_argv = malloc(sizeof (char *) * n)) == NULL) - return (NULL); - new_argv[a++] = FAILSAFESHELL; - new_argv[a++] = NULL; - assert(n == a); + int a = 0, n = 6; + + assert(supath != NULL); + if ((new_argv = malloc(sizeof (char *) * n)) == NULL) + return (NULL); + + new_argv[a++] = supath; + if (strcmp(login, "root") != 0) { + new_argv[a++] = "-"; } else { - new_argv = zone_login_cmd(bh, login); + n--; } + new_argv[a++] = (char *)login; + new_argv[a++] = "-c"; + new_argv[a++] = subshell; + new_argv[a++] = NULL; + assert(a == n); } return (new_argv); @@ -1186,6 +1343,7 @@ prep_env() int e = 0, size = 1; char **new_env, *estr; char *term = getenv("TERM"); + char *path; size++; /* for $PATH */ if (term != NULL) @@ -1202,7 +1360,12 @@ prep_env() if ((new_env = malloc(sizeof (char *) * size)) == NULL) return (NULL); - if ((estr = add_env("PATH", DEF_PATH)) == NULL) + if (strcmp(zonebrand, "lx") == 0) + path = LX_DEF_PATH; + else + path = DEF_PATH; + + if ((estr = add_env("PATH", path)) == NULL) return (NULL); new_env[e++] = estr; @@ -1724,24 +1887,61 @@ get_username() return (nptr->pw_name); } +static boolean_t +zlog_mode_logging(char *zonename, boolean_t *found) +{ + boolean_t lm = B_FALSE; + zone_dochandle_t handle; + struct zone_attrtab attr; + + *found = B_FALSE; + if ((handle = zonecfg_init_handle()) == NULL) + return (lm); + + if (zonecfg_get_handle(zonename, handle) != Z_OK) + goto done; + + if (zonecfg_setattrent(handle) != Z_OK) + goto done; + while (zonecfg_getattrent(handle, &attr) == Z_OK) { + if (strcmp("zlog-mode", attr.zone_attr_name) == 0) { + int len = strlen(attr.zone_attr_value); + + *found = B_TRUE; + if (strncmp("log", attr.zone_attr_value, 3) == 0 || + strncmp("nolog", attr.zone_attr_value, 5) == 0 || + (len >= 3 && attr.zone_attr_value[len - 2] == '-')) + lm = B_TRUE; + break; + } + } + (void) zonecfg_endattrent(handle); + +done: + zonecfg_fini_handle(handle); + return (lm); +} + int main(int argc, char **argv) { - int arg, console = 0; + int arg, console = 0, imode = 0; + int estatus = 0; zoneid_t zoneid; zone_state_t st; char *login = "root"; + int iflag = 0; int lflag = 0; int nflag = 0; char *zonename = NULL; char **proc_args = NULL; char **new_args, **new_env; sigset_t block_cld; + siginfo_t si; char devroot[MAXPATHLEN]; char *slavename, slaveshortname[MAXPATHLEN]; priv_set_t *privset; int tmpl_fd; - char zonebrand[MAXNAMELEN]; char default_brand[MAXNAMELEN]; struct stat sb; char kernzone[ZONENAME_MAX]; @@ -1755,7 +1955,7 @@ main(int argc, char **argv) (void) getpname(argv[0]); username = get_username(); - while ((arg = getopt(argc, argv, "dnECR:Se:l:Q")) != EOF) { + while ((arg = getopt(argc, argv, "diNnECIR:Se:l:Q")) != EOF) { switch (arg) { case 'C': console = 1; @@ -1763,6 +1963,16 @@ main(int argc, char **argv) case 'E': nocmdchar = 1; break; + case 'I': + /* + * interactive mode is just a slight variation on the + * console mode. + */ + console = 1; + imode = 1; + /* The default is HUP, disconnect on EOF */ + connect_flags ^= ZLOGIN_ZFD_EOF; + break; case 'R': /* undocumented */ if (*optarg != '/') { zerror(gettext("root path must be absolute.")); @@ -1782,15 +1992,22 @@ main(int argc, char **argv) failsafe = 1; break; case 'd': - disconnect = 1; + connect_flags |= ZLOGIN_DISCONNECT; break; case 'e': set_cmdchar(optarg); break; + case 'i': + iflag = 1; + break; case 'l': login = optarg; lflag = 1; break; + case 'N': + /* NOHUP - do not send EOF */ + connect_flags ^= ZLOGIN_ZFD_EOF; + break; case 'n': nflag = 1; break; @@ -1801,6 +2018,12 @@ main(int argc, char **argv) if (console != 0) { + /* + * The only connect option in console mode is ZLOGIN_DISCONNECT + */ + if (imode == 0) + connect_flags &= ZLOGIN_DISCONNECT; + if (lflag != 0) { zerror(gettext( "-l may not be specified for console login")); @@ -1827,17 +2050,27 @@ main(int argc, char **argv) } + if (iflag != 0 && nflag != 0) { + zerror(gettext("-i and -n flags are incompatible")); + usage(); + } + if (failsafe != 0 && lflag != 0) { zerror(gettext("-l may not be specified for failsafe login")); usage(); } - if (!console && disconnect != 0) { + if (!console && (connect_flags & ZLOGIN_DISCONNECT) != 0) { zerror(gettext( "-d may only be specified with console login")); usage(); } + if (imode == 0 && (connect_flags & ZLOGIN_ZFD_EOF) != 0) { + zerror(gettext("-N may only be specified with -I")); + usage(); + } + if (optind == (argc - 1)) { /* * zone name, no process name; this should be an interactive @@ -1860,7 +2093,8 @@ main(int argc, char **argv) /* zone name and process name, and possibly some args */ zonename = argv[optind]; proc_args = &argv[optind + 1]; - interactive = 0; + if (iflag && isatty(STDIN_FILENO)) + interactive = 1; } else { usage(); } @@ -1946,10 +2180,31 @@ main(int argc, char **argv) } /* - * The console is a separate case from the rest of the code; handle - * it first. + * The console (or standalong interactive mode) is a separate case from + * the rest of the code; handle it first. */ if (console) { + int gz_stderr_fd = -1; + int retry; + boolean_t set_raw = B_TRUE; + + if (imode) { + boolean_t has_zfd_config; + + if (zlog_mode_logging(zonename, &has_zfd_config)) + set_raw = B_FALSE; + + /* + * Asked for standalone interactive mode but the + * zlog-mode attribute is not configured on the zone. + */ + if (!has_zfd_config) { + zerror(gettext("'%s' is not configured on " + "the zone"), "zlog-mode"); + return (1); + } + } + /* * Ensure that zoneadmd for this zone is running. */ @@ -1958,16 +2213,56 @@ main(int argc, char **argv) /* * Make contact with zoneadmd. + * + * Handshake with the control socket first. We handle retries + * here since the relevant thread in zoneadmd might not have + * finished setting up yet. */ - if (get_console_master(zonename) == -1) + for (retry = 0; retry < MAX_RETRY; retry++) { + masterfd = connect_zone_sock(zonename, + (imode ? "server_ctl" : "console_sock"), B_FALSE); + if (masterfd != -1) + break; + sleep(1); + } + + if (retry == MAX_RETRY) { + zerror(gettext("unable to connect for %d seconds"), + MAX_RETRY); return (1); + } - if (!quiet) - (void) printf( - gettext("[Connected to zone '%s' console]\n"), - zonename); + if (handshake_zone_sock(masterfd, connect_flags) != 0) { + (void) close(masterfd); + return (1); + } + + if (imode) { + ctlfd = masterfd; + + /* Now open the io-related sockets */ + masterfd = connect_zone_sock(zonename, "server_out", + B_TRUE); + gz_stderr_fd = connect_zone_sock(zonename, + "server_err", B_TRUE); + if (masterfd == -1 || gz_stderr_fd == -1) { + (void) close(ctlfd); + (void) close(masterfd); + (void) close(gz_stderr_fd); + return (1); + } + } + + if (!quiet) { + if (imode) + (void) printf(gettext("[Connected to zone '%s' " + "interactively]\n"), zonename); + else + (void) printf(gettext("[Connected to zone '%s' " + "console]\n"), zonename); + } - if (set_tty_rawmode(STDIN_FILENO) == -1) { + if (set_raw && set_tty_rawmode(STDIN_FILENO) == -1) { reset_tty(); zperror(gettext("failed to set stdin pty to raw mode")); return (1); @@ -1976,15 +2271,25 @@ main(int argc, char **argv) (void) sigset(SIGWINCH, sigwinch); (void) sigwinch(0); + if (imode) { + /* Allow EOF mode toggling via SIGUSR1 */ + (void) sigset(SIGUSR1, sigusr1); + } + /* * Run the I/O loop until we get disconnected. */ - doio(masterfd, -1, masterfd, -1, -1, B_FALSE); + doio(masterfd, -1, masterfd, gz_stderr_fd, -1, B_FALSE); reset_tty(); - if (!quiet) - (void) printf( - gettext("\n[Connection to zone '%s' console " - "closed]\n"), zonename); + if (!quiet) { + if (imode) + (void) printf(gettext("\n[Interactive " + "connection to zone '%s' closed]\n"), + zonename); + else + (void) printf(gettext("\n[Connection to zone " + "'%s' console closed]\n"), zonename); + } return (0); } @@ -2052,11 +2357,23 @@ main(int argc, char **argv) return (1); } - if ((new_args = prep_args(bh, login, proc_args)) == NULL) { - zperror(gettext("could not assemble new arguments")); - brand_close(bh); - return (1); + /* + * The 'interactive' parameter (-i option) indicates that we're running + * a command interactively. In this case we skip prep_args so that we + * don't prepend the 'su root -c' preamble to the command invocation + * since the 'su' command typically will execute a setpgrp which will + * disassociate the actual command from the controlling terminal that + * we (zlogin) setup. + */ + if (!iflag) { + if ((new_args = prep_args(bh, zonename, login, proc_args)) + == NULL) { + zperror(gettext("could not assemble new arguments")); + brand_close(bh); + return (1); + } } + /* * Get the brand specific user_cmd. This command is used to get * a passwd(4) entry for login. @@ -2202,6 +2519,8 @@ main(int argc, char **argv) return (1); } + /* Note: we're now inside the zone, can't use gettext anymore */ + if (slavefd != STDERR_FILENO) (void) close(STDERR_FILENO); @@ -2243,8 +2562,18 @@ main(int argc, char **argv) /* * In failsafe mode, we don't use login(1), so don't try * setting up a utmpx entry. + * + * A branded zone may have very different utmpx semantics. + * At the moment, we only have two brand types: + * Illumos-like (native, sn1) and Linux. In the Illumos + * case, we know exactly how to do the necessary utmpx + * setup. Fortunately for us, the Linux /bin/login is + * prepared to deal with a non-initialized utmpx entry, so + * we can simply skip it. If future brands don't fall into + * either category, we'll have to add a per-brand utmpx + * setup hook. */ - if (!failsafe) + if (!failsafe && (strcmp(zonebrand, "lx") != 0)) if (setup_utmpx(slaveshortname) == -1) return (1); @@ -2253,13 +2582,17 @@ main(int argc, char **argv) * execute the brand's login program. */ if (setuid(0) == -1) { - zperror(gettext("insufficient privilege")); + zperror("insufficient privilege"); return (1); } - (void) execve(new_args[0], new_args, new_env); - zperror(gettext("exec failure")); - return (1); + if (iflag) { + (void) execve(proc_args[0], proc_args, new_env); + } else { + (void) execve(new_args[0], new_args, new_env); + } + zperror("exec failure"); + return (ENOEXEC); } (void) ct_tmpl_clear(tmpl_fd); @@ -2284,8 +2617,19 @@ main(int argc, char **argv) if (pollerr != 0) { (void) fprintf(stderr, gettext("Error: connection closed due " "to unexpected pollevents=0x%x.\n"), pollerr); - return (1); + return (EPIPE); } - return (0); + /* reap child and get its status */ + if (waitid(P_PID, child_pid, &si, WEXITED | WNOHANG) == -1) { + estatus = errno; + } else if (si.si_pid == 0) { + estatus = ECHILD; + } else if (si.si_code == CLD_EXITED) { + estatus = si.si_status; + } else { + estatus = ECONNABORTED; + } + + return (estatus); } diff --git a/usr/src/cmd/zoneadm/Makefile b/usr/src/cmd/zoneadm/Makefile index 5dea2ace60..4581c14af9 100644 --- a/usr/src/cmd/zoneadm/Makefile +++ b/usr/src/cmd/zoneadm/Makefile @@ -21,14 +21,18 @@ # # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2011, Joyent, Inc. All rights reserved. # PROG= zoneadm +SCRIPTS= MANIFEST= zones.xml resource-mgmt.xml SVCMETHOD= svc-zones svc-resource-mgmt include ../Makefile.cmd +include ../Makefile.ctf +ROOTUSRSBINSCRIPTS= $(SCRIPTS:%=$(ROOTUSRSBIN)/%) ROOTMANIFESTDIR= $(ROOTSVCSYSTEM) OBJS= zoneadm.o zfs.o @@ -44,13 +48,14 @@ CERRWARN += $(CNOWARN_UNINIT) .KEEP_STATE: -all: $(PROG) +all: $(PROG) $(SCRIPTS) $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) -install: all $(ROOTUSRSBINPROG) $(ROOTMANIFEST) $(ROOTSVCMETHOD) +install: all $(ROOTUSRSBINPROG) $(ROOTUSRSBINSCRIPTS) $(ROOTMANIFEST) \ + $(ROOTSVCMETHOD) check: $(PROG).c $(CHKMANIFEST) $(CSTYLE) -pP $(SRCS:%=%) @@ -60,7 +65,7 @@ $(POFILE): $(POFILES) $(CAT) $(POFILES) > $@ clean: - $(RM) $(OBJS) $(POFILES) + $(RM) $(OBJS) $(POFILES) $(SCRIPTS) lint: lint_SRCS diff --git a/usr/src/cmd/zoneadm/svc-resource-mgmt b/usr/src/cmd/zoneadm/svc-resource-mgmt index 762de4c0d8..57fd21c3d9 100644 --- a/usr/src/cmd/zoneadm/svc-resource-mgmt +++ b/usr/src/cmd/zoneadm/svc-resource-mgmt @@ -22,25 +22,19 @@ # # Copyright 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. - -# ident "%Z%%M% %I% %E% SMI" -# +# Copyright 2012 Joyent, Inc. All rights reserved. . /lib/svc/share/smf_include.sh -[ ! -f /etc/zones/global.xml ] && exit $SMF_EXIT_OK # No global zone - # resource mgmt. - # configuration - -[ ! -x /usr/sbin/zoneadm ] && exit $SMF_EXIT_OK # SUNWzoneu not installed - # Make sure working directory is / to prevent unmounting problems. cd / PATH=/usr/sbin:/usr/bin; export PATH +smf_is_globalzone || exit $SMF_EXIT_OK + case "$1" in 'start') - zoneadm -z global apply + prctl -r -n zone.cpu-shares -v 65535 -t priv -i zone global if [ $? -ne 0 ]; then exit $SMF_EXIT_ERR_FATAL fi diff --git a/usr/src/cmd/zoneadm/svc-zones b/usr/src/cmd/zoneadm/svc-zones index 9d307835bd..41f6b3325f 100644 --- a/usr/src/cmd/zoneadm/svc-zones +++ b/usr/src/cmd/zoneadm/svc-zones @@ -22,9 +22,128 @@ # # Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# Copyright (c) 2012, Joyent Inc. All rights reserved. + +set -o xtrace . /lib/svc/share/smf_include.sh +ZPOOL=`svcprop -p config/zpool svc:/system/smartdc/init:default 2>/dev/null` +ZPOOL=${ZPOOL:-zones} + +MAN_SRCDIR=/lib/svc/manifest +MAN_DESTDIR=/$ZPOOL/manifests + +MAN_DIRS="\ + application \ + milestone \ + network \ + network/dns \ + network/ipsec \ + network/ldap \ + network/loadbalancer \ + network/nfs \ + network/nis \ + network/routing \ + network/rpc \ + network/security \ + network/shares \ + network/smb \ + network/ssl \ + site \ + system \ + system/device \ + system/filesystem \ + system/fm \ + system/install \ + system/security \ + system/svc" + +cp_brand_manifests() +{ + brand=$1 + + # Create the dir hierarchy under the dest dir for SMF manifests. + mkdir -m755 $MAN_DESTDIR/$brand + + for dir in $MAN_DIRS + do + mkdir -m755 $MAN_DESTDIR/$brand/$dir + done + + # + # Process the SMF SVC configuration list to setup the brand-specific + # SMF svcs. + # + nawk -v base=$MAN_SRCDIR -v dest=$MAN_DESTDIR/$brand '{ + # Ignore comments and empty lines. + if (substr($1, 1, 1) == "#" || length($1) == 0) + next + + # entry format is: name status + proc_file($1, $2); + } + + # + # Copy the manifest from the global zone to the brand-specific + # manifest area. At the same time, update any manifests whose + # status needs to be changed, based on what our configuration + # file indicates. + # + function proc_file(fname, status) + { + f = base "/" fname + of = dest "/" fname + + while (getline <f > 0) { + # Fix up the console svc to work with zones. + if (fname == "system/console-login.xml") + sub("wscons", "console") + + if (($1 == "<instance" && + $2 == "name=\047default\047") || + $1 == "<create_default_instance") { + if (status == "enabled") + n=sub("\047false\047", "\047true\047") + else + n=sub("\047true\047", "\047false\047") + + if (n > 0) + printf("update svc state: %s %s\n", + fname, $0) + } + + print $0 >of + } + close(f) + close(of) + }' /usr/lib/brand/$brand/manifests +} + +# +# If we're running off of a live-image, setup a zone-specific collection of +# manifest files which are mounted in a zone's /lib/svc/manifest directory. +# +# Regenerate the manifest data each time this service starts, so that it's +# always in sync with the running platform (and any fixes included there). +# +setup_manifests() +{ + echo "Initializing manifest dir." + + rm -rf $MAN_DESTDIR + mkdir -m755 -p $MAN_DESTDIR + + for i in /usr/lib/brand/* + do + brand=`basename $i` + [[ ! -f /usr/lib/brand/$brand/manifests ]] && continue + # joyent-minimal uses /zones/manifests/joyent too + [[ "$brand" == "joyent-minimal" ]] && continue + cp_brand_manifests $brand + done +} + # # Return a list of running, non-global zones for which a shutdown via # "/sbin/init 0" may work (typically only Solaris zones.) @@ -51,6 +170,16 @@ PATH=/usr/sbin:/usr/bin; export PATH case "$1" in 'start') + # + # Generate the manifest, even if no zones, since zones could be + # provisioned later. + # + zfs list -H -o name $ZPOOL >/dev/null 2>&1 + [ $? -eq 0 ] && setup_manifests + + # Create directory for zone sockets + mkdir -m755 -p /var/zonecontrol + egrep -vs '^#|^global:' /etc/zones/index || exit 0 # no local zones # @@ -68,6 +197,14 @@ case "$1" in [ -z "$ZONES" ] && echo "Booting zones:\c" ZONES=yes echo " $zone\c" + + # + # Make sure a site dir exists, it wasn't initially + # being created. + # + zonepath=`zonecfg -z $zone info zonepath | cut -d: -f2` + [ ! -d $zonepath/site ] && mkdir -m755 $zonepath/site + # # zoneadmd puts itself into its own contract so # this service will lose sight of it. We don't @@ -110,7 +247,7 @@ case "$1" in for zone in $zonelist; do echo " $zone\c" - zoneadm -z $zone shutdown & + zlogin -S $zone /sbin/init 0 < /dev/null >&0 2>&0 & SHUTDOWN=1 done diff --git a/usr/src/cmd/zoneadm/zfs.c b/usr/src/cmd/zoneadm/zfs.c index 15be33ddab..214340d0ce 100644 --- a/usr/src/cmd/zoneadm/zfs.c +++ b/usr/src/cmd/zoneadm/zfs.c @@ -21,8 +21,8 @@ /* * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, Joyent, Inc. All rights reserved. * Copyright (c) 2012, 2015 by Delphix. All rights reserved. - * Copyright (c) 2012, Joyent, Inc. All rights reserved. * Copyright (c) 2016 Martin Matuska. All rights reserved. */ @@ -968,6 +968,7 @@ create_zfs_zonepath(char *zonepath) zfs_handle_t *zhp; char zfs_name[MAXPATHLEN]; nvlist_t *props = NULL; + int i; if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK) return; @@ -1003,9 +1004,20 @@ create_zfs_zonepath(char *zonepath) nvlist_free(props); - if (zfs_mount(zhp, NULL, 0) != 0) { + /* + * A monitoring tool might race with us and touch the mountpoint just + * as we're trying to mount, blocking the mount. We wait and retry a + * few times to workaround this race. + */ + for (i = 0; i < 5; i++) { + if (zfs_mount(zhp, NULL, 0) == 0) + break; (void) fprintf(stderr, gettext("cannot mount ZFS dataset %s: " "%s\n"), zfs_name, libzfs_error_description(g_zfs)); + (void) sleep(1); + } + + if (i >= 5) { (void) zfs_destroy(zhp, B_FALSE); } else { if (chmod(zonepath, S_IRWXU) != 0) { diff --git a/usr/src/cmd/zoneadm/zoneadm.c b/usr/src/cmd/zoneadm/zoneadm.c index e5b1bc4a07..3487dcc7e6 100644 --- a/usr/src/cmd/zoneadm/zoneadm.c +++ b/usr/src/cmd/zoneadm/zoneadm.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2015, Joyent Inc. All rights reserved. * Copyright (c) 2015 by Delphix. All rights reserved. */ @@ -101,13 +102,11 @@ typedef struct zone_entry { char zroot[MAXPATHLEN]; char zuuid[UUID_PRINTABLE_STRING_LENGTH]; zone_iptype_t ziptype; + zoneid_t zdid; } zone_entry_t; #define CLUSTER_BRAND_NAME "cluster" -static zone_entry_t *zents; -static size_t nzents; - #define LOOPBACK_IF "lo0" #define SOCKET_AF(af) (((af) == AF_UNSPEC) ? AF_INET : (af)) @@ -406,19 +405,6 @@ zerror(const char *fmt, ...) va_end(alist); } -static void * -safe_calloc(size_t nelem, size_t elsize) -{ - void *r = calloc(nelem, elsize); - - if (r == NULL) { - zerror(gettext("failed to allocate %lu bytes: %s"), - (ulong_t)nelem * elsize, strerror(errno)); - exit(Z_ERR); - } - return (r); -} - static void zone_print(zone_entry_t *zent, boolean_t verbose, boolean_t parsable) { @@ -443,6 +429,7 @@ zone_print(zone_entry_t *zent, boolean_t verbose, boolean_t parsable) } if (!verbose) { char *cp, *clim; + char zdid[80]; if (!parsable) { (void) printf("%s\n", zent->zname); @@ -458,8 +445,12 @@ zone_print(zone_entry_t *zent, boolean_t verbose, boolean_t parsable) (void) printf("%.*s\\:", clim - cp, cp); cp = clim + 1; } - (void) printf("%s:%s:%s:%s\n", cp, zent->zuuid, zent->zbrand, - ip_type_str); + if (zent->zdid == -1) + zdid[0] = '\0'; + else + (void) snprintf(zdid, sizeof (zdid), "%d", zent->zdid); + (void) printf("%s:%s:%s:%s:%s\n", cp, zent->zuuid, zent->zbrand, + ip_type_str, zdid); return; } if (zent->zstate_str != NULL) { @@ -485,6 +476,9 @@ lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent) (void) strlcpy(zent->zbrand, "???", sizeof (zent->zbrand)); zent->zstate_str = "???"; + if (strcmp(zone_name, GLOBAL_ZONENAME) == 0) + zid = zent->zdid = GLOBAL_ZONEID; + zent->zid = zid; if (zonecfg_get_uuid(zone_name, uuid) == Z_OK && @@ -529,8 +523,8 @@ lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent) zent->zstate_str = zone_state_str(zent->zstate_num); /* - * A zone's brand is only available in the .xml file describing it, - * which is only visible to the global zone. This causes + * A zone's brand might only be available in the .xml file describing + * it, which is only visible to the global zone. This causes * zone_get_brand() to fail when called from within a non-global * zone. Fortunately we only do this on labeled systems, where we * know all zones are native. @@ -554,6 +548,22 @@ lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent) return (Z_OK); } + if ((handle = zonecfg_init_handle()) == NULL) { + zperror2(zent->zname, gettext("could not init handle")); + return (Z_ERR); + } + if ((err = zonecfg_get_handle(zent->zname, handle)) != Z_OK) { + zperror2(zent->zname, gettext("could not get handle")); + zonecfg_fini_handle(handle); + return (Z_ERR); + } + + if ((err = zonecfg_get_iptype(handle, &zent->ziptype)) != Z_OK) { + zperror2(zent->zname, gettext("could not get ip-type")); + zonecfg_fini_handle(handle); + return (Z_ERR); + } + /* * There is a race condition where the zone could boot while * we're walking the index file. In this case the zone state @@ -574,193 +584,76 @@ lookup_zone_info(const char *zone_name, zoneid_t zid, zone_entry_t *zent) zent->ziptype = ZS_EXCLUSIVE; else zent->ziptype = ZS_SHARED; - return (Z_OK); } } - if ((handle = zonecfg_init_handle()) == NULL) { - zperror2(zent->zname, gettext("could not init handle")); - return (Z_ERR); - } - if ((err = zonecfg_get_handle(zent->zname, handle)) != Z_OK) { - zperror2(zent->zname, gettext("could not get handle")); - zonecfg_fini_handle(handle); - return (Z_ERR); - } + zent->zdid = zonecfg_get_did(handle); - if ((err = zonecfg_get_iptype(handle, &zent->ziptype)) != Z_OK) { - zperror2(zent->zname, gettext("could not get ip-type")); - zonecfg_fini_handle(handle); - return (Z_ERR); - } zonecfg_fini_handle(handle); return (Z_OK); } -/* - * fetch_zents() calls zone_list(2) to find out how many zones are running - * (which is stored in the global nzents), then calls zone_list(2) again - * to fetch the list of running zones (stored in the global zents). This - * function may be called multiple times, so if zents is already set, we - * return immediately to save work. - * - * Note that the data about running zones can change while this function - * is running, so its possible that the list of zones will have empty slots - * at the end. - */ - -static int -fetch_zents(void) -{ - zoneid_t *zids = NULL; - uint_t nzents_saved; - int i, retv; - FILE *fp; - boolean_t inaltroot; - zone_entry_t *zentp; - const char *altroot; - - if (nzents > 0) - return (Z_OK); - - if (zone_list(NULL, &nzents) != 0) { - zperror(gettext("failed to get zoneid list"), B_FALSE); - return (Z_ERR); - } - -again: - if (nzents == 0) - return (Z_OK); - - zids = safe_calloc(nzents, sizeof (zoneid_t)); - nzents_saved = nzents; - - if (zone_list(zids, &nzents) != 0) { - zperror(gettext("failed to get zone list"), B_FALSE); - free(zids); - return (Z_ERR); - } - if (nzents != nzents_saved) { - /* list changed, try again */ - free(zids); - goto again; - } - - zents = safe_calloc(nzents, sizeof (zone_entry_t)); - - inaltroot = zonecfg_in_alt_root(); - if (inaltroot) { - fp = zonecfg_open_scratch("", B_FALSE); - altroot = zonecfg_get_root(); - } else { - fp = NULL; - } - zentp = zents; - retv = Z_OK; - for (i = 0; i < nzents; i++) { - char name[ZONENAME_MAX]; - char altname[ZONENAME_MAX]; - char rev_altroot[MAXPATHLEN]; - - if (getzonenamebyid(zids[i], name, sizeof (name)) < 0) { - /* - * There is a race condition where the zone may have - * shutdown since we retrieved the number of running - * zones above. This is not an error, there will be - * an empty slot at the end of the list. - */ - continue; - } - if (zonecfg_is_scratch(name)) { - /* Ignore scratch zones by default */ - if (!inaltroot) - continue; - if (fp == NULL || - zonecfg_reverse_scratch(fp, name, altname, - sizeof (altname), rev_altroot, - sizeof (rev_altroot)) == -1) { - zerror(gettext("could not resolve scratch " - "zone %s"), name); - retv = Z_ERR; - continue; - } - /* Ignore zones in other alternate roots */ - if (strcmp(rev_altroot, altroot) != 0) - continue; - (void) strcpy(name, altname); - } else { - /* Ignore non-scratch when in an alternate root */ - if (inaltroot && strcmp(name, GLOBAL_ZONENAME) != 0) - continue; - } - if (lookup_zone_info(name, zids[i], zentp) != Z_OK) { - /* - * There is a race condition where the zone may have - * shutdown since we retrieved the number of running - * zones above. This is not an error, there will be - * an empty slot at the end of the list. - */ - continue; - } - zentp++; - } - nzents = zentp - zents; - if (fp != NULL) - zonecfg_close_scratch(fp); - - free(zids); - return (retv); -} - static int zone_print_list(zone_state_t min_state, boolean_t verbose, boolean_t parsable, boolean_t exclude_global) { - int i; zone_entry_t zent; FILE *cookie; - char *name; + struct zoneent *ze; /* - * First get the list of running zones from the kernel and print them. - * If that is all we need, then return. - */ - if ((i = fetch_zents()) != Z_OK) { - /* - * No need for error messages; fetch_zents() has already taken - * care of this. - */ - return (i); - } - for (i = 0; i < nzents; i++) { - if (exclude_global && zents[i].zid == GLOBAL_ZONEID) - continue; - zone_print(&zents[i], verbose, parsable); - } - if (min_state >= ZONE_STATE_RUNNING) - return (Z_OK); - /* - * Next, get the full list of zones from the configuration, skipping - * any we have already printed. + * Get the full list of zones from the configuration. */ cookie = setzoneent(); - while ((name = getzoneent(cookie)) != NULL) { - for (i = 0; i < nzents; i++) { - if (strcmp(zents[i].zname, name) == 0) - break; - } - if (i < nzents) { - free(name); - continue; - } - if (lookup_zone_info(name, ZONE_ID_UNDEFINED, &zent) != Z_OK) { - free(name); + while ((ze = getzoneent_private(cookie)) != NULL) { + char *name = ze->zone_name; + zoneid_t zid; + + zid = getzoneidbyname(name); + if (exclude_global && zid == GLOBAL_ZONEID) continue; + + if (ze->zone_brand[0] == '\0') { + /* old, incomplete index entry */ + if (lookup_zone_info(name, zid, &zent) != Z_OK) { + free(ze); + continue; + } + } else { + /* new, full index entry */ + (void) strlcpy(zent.zname, name, sizeof (zent.zname)); + (void) strlcpy(zent.zroot, ze->zone_path, + sizeof (zent.zroot)); + uuid_unparse(ze->zone_uuid, zent.zuuid); + (void) strlcpy(zent.zbrand, ze->zone_brand, + sizeof (zent.zbrand)); + zent.ziptype = ze->zone_iptype; + zent.zdid = ze->zone_did; + zent.zid = zid; + + if (zid != -1) { + int err; + + err = zone_get_state(name, + (zone_state_t *)&ze->zone_state); + if (err != Z_OK) { + errno = err; + zperror2(name, gettext("could not get " + "state")); + free(ze); + continue; + } + } + + zent.zstate_num = ze->zone_state; + zent.zstate_str = zone_state_str(zent.zstate_num); } - free(name); + if (zent.zstate_num >= min_state) zone_print(&zent, verbose, parsable); + + free(ze); } endzoneent(cookie); return (Z_OK); @@ -770,18 +663,22 @@ zone_print_list(zone_state_t min_state, boolean_t verbose, boolean_t parsable, * Retrieve a zone entry by name. Returns NULL if no such zone exists. */ static zone_entry_t * -lookup_running_zone(const char *str) +lookup_running_zone(const char *name) { - int i; + zoneid_t zid; + zone_entry_t *zent; + + if ((zid = getzoneidbyname(name)) == -1) + return (NULL); - if (fetch_zents() != Z_OK) + if ((zent = malloc(sizeof (zone_entry_t))) == NULL) return (NULL); - for (i = 0; i < nzents; i++) { - if (strcmp(str, zents[i].zname) == 0) - return (&zents[i]); + if (lookup_zone_info(name, zid, zent) != Z_OK) { + free(zent); + return (NULL); } - return (NULL); + return (zent); } /* @@ -1017,8 +914,12 @@ validate_zonepath(char *path, int cmd_num) (void) printf(gettext("WARNING: %s is on a temporary " "file system.\n"), rpath); } - if (crosscheck_zonepaths(rpath) != Z_OK) - return (Z_ERR); + if (cmd_num != CMD_BOOT && cmd_num != CMD_REBOOT && + cmd_num != CMD_READY) { + /* we checked when we installed, no need to check each boot */ + if (crosscheck_zonepaths(rpath) != Z_OK) + return (Z_ERR); + } /* * Try to collect and report as many minor errors as possible * before returning, so the user can learn everything that needs @@ -1205,6 +1106,7 @@ static int ready_func(int argc, char *argv[]) { zone_cmd_arg_t zarg; + boolean_t debug = B_FALSE; int arg; if (zonecfg_in_alt_root()) { @@ -1213,11 +1115,14 @@ ready_func(int argc, char *argv[]) } optind = 0; - if ((arg = getopt(argc, argv, "?")) != EOF) { + if ((arg = getopt(argc, argv, "?X")) != EOF) { switch (arg) { case '?': sub_usage(SHELP_READY, CMD_READY); return (optopt == '?' ? Z_OK : Z_USAGE); + case 'X': + debug = B_TRUE; + break; default: sub_usage(SHELP_READY, CMD_READY); return (Z_USAGE); @@ -1234,6 +1139,7 @@ ready_func(int argc, char *argv[]) return (Z_ERR); zarg.cmd = Z_READY; + zarg.debug = debug; if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { zerror(gettext("call to %s failed"), "zoneadmd"); return (Z_ERR); @@ -1246,6 +1152,7 @@ boot_func(int argc, char *argv[]) { zone_cmd_arg_t zarg; boolean_t force = B_FALSE; + boolean_t debug = B_FALSE; int arg; if (zonecfg_in_alt_root()) { @@ -1272,7 +1179,7 @@ boot_func(int argc, char *argv[]) * zoneadm -z myzone boot -- -s -v -m verbose. */ optind = 0; - while ((arg = getopt(argc, argv, "?fs")) != EOF) { + while ((arg = getopt(argc, argv, "?fsX")) != EOF) { switch (arg) { case '?': sub_usage(SHELP_BOOT, CMD_BOOT); @@ -1284,6 +1191,9 @@ boot_func(int argc, char *argv[]) case 'f': force = B_TRUE; break; + case 'X': + debug = B_TRUE; + break; default: sub_usage(SHELP_BOOT, CMD_BOOT); return (Z_USAGE); @@ -1309,6 +1219,7 @@ boot_func(int argc, char *argv[]) if (verify_details(CMD_BOOT, argv) != Z_OK) return (Z_ERR); zarg.cmd = force ? Z_FORCEBOOT : Z_BOOT; + zarg.debug = debug; if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { zerror(gettext("call to %s failed"), "zoneadmd"); return (Z_ERR); @@ -1623,10 +1534,10 @@ auth_check(char *user, char *zone, int cmd_num) * not already running (or ready). */ static int -sanity_check(char *zone, int cmd_num, boolean_t running, +sanity_check(char *zone, int cmd_num, boolean_t need_running, boolean_t unsafe_when_running, boolean_t force) { - zone_entry_t *zent; + boolean_t is_running = B_FALSE; priv_set_t *privset; zone_state_t state, min_state; char kernzone[ZONENAME_MAX]; @@ -1697,51 +1608,54 @@ sanity_check(char *zone, int cmd_num, boolean_t running, } if (!zonecfg_in_alt_root()) { - zent = lookup_running_zone(zone); - } else if ((fp = zonecfg_open_scratch("", B_FALSE)) == NULL) { - zent = NULL; - } else { - if (zonecfg_find_scratch(fp, zone, zonecfg_get_root(), - kernzone, sizeof (kernzone)) == 0) - zent = lookup_running_zone(kernzone); - else - zent = NULL; + /* Avoid the xml read overhead of lookup_running_zone */ + if (getzoneidbyname(zone) != -1) + is_running = B_TRUE; + + } else if ((fp = zonecfg_open_scratch("", B_FALSE)) != NULL) { + if (zonecfg_find_scratch(fp, zone, zonecfg_get_root(), kernzone, + sizeof (kernzone)) == 0 && getzoneidbyname(kernzone) != -1) + is_running = B_TRUE; + zonecfg_close_scratch(fp); } /* * Look up from the kernel for 'running' zones. */ - if (running && !force) { - if (zent == NULL) { + if (need_running && !force) { + if (!is_running) { zerror(gettext("not running")); return (Z_ERR); } } else { int err; - if (unsafe_when_running && zent != NULL) { + err = zone_get_state(zone, &state); + + if (unsafe_when_running && is_running) { /* check whether the zone is ready or running */ - if ((err = zone_get_state(zent->zname, - &zent->zstate_num)) != Z_OK) { + char *zstate_str; + + if (err != Z_OK) { errno = err; - zperror2(zent->zname, - gettext("could not get state")); + zperror2(zone, gettext("could not get state")); /* can't tell, so hedge */ - zent->zstate_str = "ready/running"; + zstate_str = "ready/running"; } else { - zent->zstate_str = - zone_state_str(zent->zstate_num); + zstate_str = zone_state_str(state); } zerror(gettext("%s operation is invalid for %s zones."), - cmd_to_str(cmd_num), zent->zstate_str); + cmd_to_str(cmd_num), zstate_str); return (Z_ERR); } - if ((err = zone_get_state(zone, &state)) != Z_OK) { + + if (err != Z_OK) { errno = err; zperror2(zone, gettext("could not get state")); return (Z_ERR); } + switch (cmd_num) { case CMD_UNINSTALL: if (state == ZONE_STATE_CONFIGURED) { @@ -1829,6 +1743,7 @@ static int halt_func(int argc, char *argv[]) { zone_cmd_arg_t zarg; + boolean_t debug = B_FALSE; int arg; if (zonecfg_in_alt_root()) { @@ -1837,11 +1752,14 @@ halt_func(int argc, char *argv[]) } optind = 0; - if ((arg = getopt(argc, argv, "?")) != EOF) { + if ((arg = getopt(argc, argv, "?X")) != EOF) { switch (arg) { case '?': sub_usage(SHELP_HALT, CMD_HALT); return (optopt == '?' ? Z_OK : Z_USAGE); + case 'X': + debug = B_TRUE; + break; default: sub_usage(SHELP_HALT, CMD_HALT); return (Z_USAGE); @@ -1867,6 +1785,7 @@ halt_func(int argc, char *argv[]) return (Z_ERR); zarg.cmd = Z_HALT; + zarg.debug = debug; return ((zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) == 0) ? Z_OK : Z_ERR); } @@ -1944,6 +1863,7 @@ static int reboot_func(int argc, char *argv[]) { zone_cmd_arg_t zarg; + boolean_t debug = B_FALSE; int arg; if (zonecfg_in_alt_root()) { @@ -1952,11 +1872,14 @@ reboot_func(int argc, char *argv[]) } optind = 0; - if ((arg = getopt(argc, argv, "?")) != EOF) { + if ((arg = getopt(argc, argv, "?X")) != EOF) { switch (arg) { case '?': sub_usage(SHELP_REBOOT, CMD_REBOOT); return (optopt == '?' ? Z_OK : Z_USAGE); + case 'X': + debug = B_TRUE; + break; default: sub_usage(SHELP_REBOOT, CMD_REBOOT); return (Z_USAGE); @@ -1991,6 +1914,7 @@ reboot_func(int argc, char *argv[]) return (Z_ERR); zarg.cmd = Z_REBOOT; + zarg.debug = debug; return ((zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) == 0) ? Z_OK : Z_ERR); } @@ -2218,6 +2142,10 @@ verify_fs_special(struct zone_fstab *fstab) if (strcmp(fstab->zone_fs_type, MNTTYPE_ZFS) == 0) return (verify_fs_zfs(fstab)); + if (strcmp(fstab->zone_fs_type, MNTTYPE_HYPRLOFS) == 0 && + strcmp(fstab->zone_fs_special, "swap") == 0) + return (Z_OK); + if (stat64(fstab->zone_fs_special, &st) != 0) { (void) fprintf(stderr, gettext("could not verify fs " "%s: could not access %s: %s\n"), fstab->zone_fs_dir, @@ -2621,7 +2549,6 @@ verify_handle(int cmd_num, zone_dochandle_t handle, char *argv[]) dladm_handle_t dh; dladm_status_t status; datalink_id_t linkid; - char errmsg[DLADM_STRSIZE]; in_alt_root = zonecfg_in_alt_root(); if (in_alt_root) @@ -2704,11 +2631,6 @@ verify_handle(int cmd_num, zone_dochandle_t handle, char *argv[]) dladm_close(dh); } if (status != DLADM_STATUS_OK) { - (void) fprintf(stderr, - gettext("WARNING: skipping network " - "interface '%s': %s\n"), - nwiftab.zone_nwif_physical, - dladm_status2str(status, errmsg)); break; } dl_owner_zid = ALL_ZONES; @@ -2792,6 +2714,74 @@ no_net: return (return_code); } +/* + * Called when readying or booting a zone. We double check that the zone's + * debug ID is set and is unique. This covers the case of pre-existing zones + * with no ID. Also, its possible that a zone was migrated to this host + * and as a result it has a duplicate ID. In this case we preserve the ID + * of the first zone we match on in the index file (since it was there before + * the current zone) and we assign a new unique ID to the current zone. + * Return true if we assigned a new ID, indicating that the zone configuration + * needs to be saved. + */ +static boolean_t +verify_fix_did(zone_dochandle_t handle) +{ + zoneid_t mydid; + struct zoneent *ze; + FILE *cookie; + boolean_t fix = B_FALSE; + + mydid = zonecfg_get_did(handle); + if (mydid == -1) { + zonecfg_set_did(handle); + return (B_TRUE); + } + + /* Get the full list of zones from the configuration. */ + cookie = setzoneent(); + while ((ze = getzoneent_private(cookie)) != NULL) { + char *name; + zoneid_t did; + + name = ze->zone_name; + if (strcmp(name, GLOBAL_ZONENAME) == 0 || + strcmp(name, target_zone) == 0) { + free(ze); + continue; + } + + if (ze->zone_brand[0] == '\0') { + /* old, incomplete index entry */ + zone_entry_t zent; + + if (lookup_zone_info(name, ZONE_ID_UNDEFINED, + &zent) != Z_OK) { + free(ze); + continue; + } + did = zent.zdid; + } else { + /* new, full index entry */ + did = ze->zone_did; + } + free(ze); + + if (did == mydid) { + fix = B_TRUE; + break; + } + } + endzoneent(cookie); + + if (fix) { + zonecfg_set_did(handle); + return (B_TRUE); + } + + return (B_FALSE); +} + static int verify_details(int cmd_num, char *argv[]) { @@ -2851,6 +2841,18 @@ verify_details(int cmd_num, char *argv[]) if (verify_handle(cmd_num, handle, argv) != Z_OK) return_code = Z_ERR; + if (cmd_num == CMD_READY || cmd_num == CMD_BOOT) { + int vcommit = 0, obscommit = 0; + + vcommit = verify_fix_did(handle); + obscommit = zonecfg_fix_obsolete(handle); + + if (vcommit || obscommit) + if (zonecfg_save(handle) != Z_OK) + (void) fprintf(stderr, gettext("Could not save " + "updated configuration.\n")); + } + zonecfg_fini_handle(handle); if (return_code == Z_ERR) (void) fprintf(stderr, @@ -2936,6 +2938,7 @@ install_func(int argc, char *argv[]) int status; boolean_t do_postinstall = B_FALSE; boolean_t brand_help = B_FALSE; + boolean_t do_dataset = B_TRUE; char opts[128]; if (target_zone == NULL) { @@ -3011,6 +3014,12 @@ install_func(int argc, char *argv[]) } /* Ignore unknown options - may be brand specific. */ break; + case 'x': + if (strcmp(optarg, "nodataset") == 0) { + do_dataset = B_FALSE; + continue; /* internal arg, don't pass thru */ + } + break; default: /* Ignore unknown options - may be brand specific. */ break; @@ -3063,7 +3072,8 @@ install_func(int argc, char *argv[]) goto done; } - create_zfs_zonepath(zonepath); + if (do_dataset) + create_zfs_zonepath(zonepath); } status = do_subproc(cmdbuf); @@ -3874,10 +3884,10 @@ cleanup_zonepath(char *zonepath, boolean_t all) * exist if the zone was force-attached after a * migration. */ - char *std_entries[] = {"dev", "lu", "root", - "SUNWattached.xml", NULL}; - /* (MAXPATHLEN * 3) is for the 3 std_entries dirs */ - char cmdbuf[sizeof (RMCOMMAND) + (MAXPATHLEN * 3) + 64]; + char *std_entries[] = {"dev", "lastexited", "logs", "lu", + "root", "SUNWattached.xml", NULL}; + /* (MAXPATHLEN * 5) is for the 5 std_entries dirs */ + char cmdbuf[sizeof (RMCOMMAND) + (MAXPATHLEN * 5) + 64]; /* * We shouldn't need these checks but lets be paranoid since we @@ -5027,6 +5037,7 @@ uninstall_func(int argc, char *argv[]) if (zonecfg_ping_zoneadmd(target_zone) == Z_OK) { zone_cmd_arg_t zarg; zarg.cmd = Z_NOTE_UNINSTALLING; + zarg.debug = B_FALSE; /* we don't care too much if this fails, just plow on */ (void) zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE); @@ -5142,6 +5153,7 @@ mount_func(int argc, char *argv[]) return (Z_ERR); zarg.cmd = force ? Z_FORCEMOUNT : Z_MOUNT; + zarg.debug = B_FALSE; zarg.bootbuf[0] = '\0'; if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { zerror(gettext("call to %s failed"), "zoneadmd"); @@ -5163,6 +5175,7 @@ unmount_func(int argc, char *argv[]) return (Z_ERR); zarg.cmd = Z_UNMOUNT; + zarg.debug = B_FALSE; if (zonecfg_call_zoneadmd(target_zone, &zarg, locale, B_TRUE) != 0) { zerror(gettext("call to %s failed"), "zoneadmd"); return (Z_ERR); @@ -5384,7 +5397,7 @@ apply_func(int argc, char *argv[]) priv_set_t *privset; zoneid_t zoneid; zone_dochandle_t handle; - struct zone_mcaptab mcap; + uint64_t mcap; char pool_err[128]; zoneid = getzoneid(); @@ -5475,19 +5488,12 @@ apply_func(int argc, char *argv[]) } /* - * If a memory cap is configured, set the cap in the kernel using - * zone_setattr() and make sure the rcapd SMF service is enabled. + * If a memory cap is configured, make sure the rcapd SMF service is + * enabled. */ - if (zonecfg_getmcapent(handle, &mcap) == Z_OK) { - uint64_t num; + if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &mcap) == Z_OK) { char smf_err[128]; - num = (uint64_t)strtoll(mcap.zone_physmem_cap, NULL, 10); - if (zone_setattr(zoneid, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) { - zerror(gettext("could not set zone memory cap")); - res = Z_ERR; - } - if (zonecfg_enable_rcapd(smf_err, sizeof (smf_err)) != Z_OK) { zerror(gettext("enabling system/rcap service failed: " "%s"), smf_err); diff --git a/usr/src/cmd/zoneadm/zones.xml b/usr/src/cmd/zoneadm/zones.xml index 9c8e305f89..b094bc660b 100644 --- a/usr/src/cmd/zoneadm/zones.xml +++ b/usr/src/cmd/zoneadm/zones.xml @@ -54,11 +54,32 @@ <service_fmri value='svc:/milestone/multi-user-server' /> </dependency> + <!-- + Until overlay device creation is moved out of the zone + state-change script, zones must be dependent on varpd's + successful launch. + --> + <dependency + name='varpd' + type='service' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/network/varpd' /> + </dependency> + + <dependency + name='metadata' + type='service' + grouping='require_all' + restart_on='none'> + <service_fmri value='svc:/system/smartdc/metadata' /> + </dependency> + <exec_method type='method' name='start' exec='/lib/svc/method/svc-zones %m' - timeout_seconds='60'> + timeout_seconds='0'> </exec_method> <!-- diff --git a/usr/src/cmd/zoneadmd/Makefile b/usr/src/cmd/zoneadmd/Makefile index 04b3ab3078..aa41acfc8b 100644 --- a/usr/src/cmd/zoneadmd/Makefile +++ b/usr/src/cmd/zoneadmd/Makefile @@ -18,57 +18,59 @@ # # CDDL HEADER END - -# - # # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. # Copyright 2014 Nexenta Systems, Inc. All rights reserved. +# Copyright (c) 2011, Joyent, Inc. All rights reserved. # PROG= zoneadmd include ../Makefile.cmd +include ../Makefile.ctf -ROOTCMDDIR= $(ROOTLIB)/zones +$(64ONLY)SUBDIRS= $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) -OBJS= zoneadmd.o zcons.o vplat.o -SRCS = $(OBJS:.o=.c) -POFILE=zoneadmd_all.po -POFILES= $(OBJS:%.o=%.po) +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +lint := TARGET = lint CFLAGS += $(CCVERBOSE) CERRWARN += -_gcc=-Wno-switch CERRWARN += -_gcc=-Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) -LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair \ - -lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol \ - -linetutil -lscf XGETFLAGS += -a -x zoneadmd.xcl +ROOTUSRLIBZONES = $(ROOT)/usr/lib/zones + .KEEP_STATE: .PARALLEL: -all: $(PROG) +all: $(SUBDIRS) $(PROG): $(OBJS) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) -install: all $(ROOTCMD) +install: $(SUBDIRS) + -$(RM) $(ROOTUSRLIBZONES)/$(PROG) + -$(LN) $(ISAEXEC) $(ROOTUSRLIBZONES)/$(PROG) -$(POFILE): $(POFILES) - $(RM) $@ - $(CAT) $(POFILES) > $@ +$(POFILE): -clean: - $(RM) $(OBJS) - -lint: lint_SRCS +clean clobebr lint: $(SUBDIRS) check: - $(CSTYLE) -p -P $(SRCS:%=%) + $(CSTYLE) -p -P *.c + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: include ../Makefile.targ diff --git a/usr/src/cmd/zoneadmd/Makefile.com b/usr/src/cmd/zoneadmd/Makefile.com new file mode 100644 index 0000000000..aaf21c7f5b --- /dev/null +++ b/usr/src/cmd/zoneadmd/Makefile.com @@ -0,0 +1,72 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END + +# +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright 2020 Joyent, Inc. +# + +PROG= zoneadmd + +include ../../Makefile.cmd +include ../../Makefile.ctf + +ROOTCMDDIR= $(ROOTLIB)/zones + +OBJS= zoneadmd.o zcons.o zfd.o vplat.o log.o + +CFLAGS += $(CCVERBOSE) +LDLIBS += -lsocket -lzonecfg -lnsl -ldevinfo -ldevice -lnvpair \ + -lgen -lbsm -lcontract -lzfs -luuid -lbrand -ldladm -ltsnet -ltsol \ + -linetutil -lscf -lppt -lcustr + +CSTD= $(CSTD_GNU99) + +.KEEP_STATE: + +%.o: ../%.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + +ROOTUSRLIBZONES = $(ROOT)/usr/lib/zones +ROOTUSRLIBZONES32 = $(ROOTUSRLIBZONES)/$(MACH32) +ROOTUSRLIBZONES64 = $(ROOTUSRLIBZONES)/$(MACH64) +ROOTUSRLIBZONESPROG32 = $(ROOTUSRLIBZONES32)/$(PROG) +ROOTUSRLIBZONESPROG64 = $(ROOTUSRLIBZONES64)/$(PROG) +$(ROOTUSRLIBZONES32)/%: $(ROOTUSRLIBZONES32) % + $(INS.file) +$(ROOTUSRLIBZONES64)/%: $(ROOTUSRLIBZONES64) % + $(INS.file) +$(ROOTUSRLIBZONES32): + $(INS.dir) + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +clean: + $(RM) $(OBJS) + +lint: + $(LINT.c) ../*.c $(LDLIBS) + +include ../../Makefile.targ diff --git a/usr/src/cmd/zoneadmd/amd64/Makefile b/usr/src/cmd/zoneadmd/amd64/Makefile new file mode 100644 index 0000000000..75ac51db32 --- /dev/null +++ b/usr/src/cmd/zoneadmd/amd64/Makefile @@ -0,0 +1,31 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +.KEEP_STATE: + +include ../Makefile.com +include ../../Makefile.cmd.64 + +install: all $(ROOTUSRLIBZONES64) $(ROOTUSRLIBZONESPROG64) diff --git a/usr/src/cmd/zoneadmd/i386/Makefile b/usr/src/cmd/zoneadmd/i386/Makefile new file mode 100644 index 0000000000..a8764e0638 --- /dev/null +++ b/usr/src/cmd/zoneadmd/i386/Makefile @@ -0,0 +1,30 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (the "License"). You may not use this file except in compliance +# with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright (c) 2011, Joyent, Inc. All rights reserved. +# + +.KEEP_STATE: + +include ../Makefile.com + +install: all $(ROOTUSRLIBZONES32) $(ROOTUSRLIBZONESPROG32) diff --git a/usr/src/cmd/zoneadmd/log.c b/usr/src/cmd/zoneadmd/log.c new file mode 100644 index 0000000000..a4ecc3e1e8 --- /dev/null +++ b/usr/src/cmd/zoneadmd/log.c @@ -0,0 +1,1027 @@ +/* + * This file and its contents are supplied under the terms of the + * Common Development and Distribution License ("CDDL"), version 1.0. + * You may only use this file in accordance with the terms of version + * 1.0 of the CDDL. + * + * A full copy of the text of the CDDL should have accompanied this + * source. A copy of the CDDL is also available via the Internet at + * http://www.illumos.org/license/CDDL. + */ + +/* + * Copyright 2020 Joyent, Inc. + */ + +/* + * zoneadmd logging + * + * zoneadmd logs to log files under <zonepath>/logs. Each log entry is a json + * structure of the form: + * + * { + * "log": "some message\n", + * "stream": "stderr", + * "time": "2018-03-28T13:25:02.670423000Z" + * } + * + * Unlike the example above, the entries in the log file are not pretty-printed. + * Messages are processed so that they have the proper json escapes for + * problematic characters. Excessively long messages may be truncated. + * + * To use these interfaces: + * + * int logid; + * + * logstream_init(zlogp); + * + * logid = logstream_open("stdio.log", "stdout", flags); + * ... + * logstream_write(logid, buf, len); + * ... + * logstream_close(logid); + * + * logstream_init() needs to be called only once. + * + * logstream_open() opens a log file (if not already open) and associates the + * specified stream with it. + * + * The following flag is supported: + * + * LS_LINE_BUFFERED Buffer writes until a newline is encountered or the + * buffer fills. This should only be used with streams + * that are written to by a single thread. The timestamp + * on log messages are the time that the log entry was + * written to the log file. This means the timestamp is + * the time when the console user hits enter, not the time + * that the prompt was printed. + * + * Line buffering is particularly useful for bhyve console logging because + * bhyve's UART emulation causes read() calls in zcons.c to return far fewer + * than 10 characters at a time. Without line buffering, a small number of + * logged characters are accompanied by about 64 characters of timestamp and + * other overhead. Line buffering saves quite a lot of space and makes the log + * much easier to read. + * + * + * Log rotation + * + * Two attributes, zlog-max-size and zlog-keep-rotated are used for automatic + * log rotation. zlog-max-size is the approximate maximum size of a log before + * it is automatically rotated. Rotated logs are renamed as + * <log>.<iso-8601-stamp>. If zlog-keep-rotated is specified and is an integer + * greater than zero, only that number of rotated logs will be retained. + * + * If zlog-max-size is not specified, log rotation will not happen + * automatically. An external log rotation program may rename the log file(s), + * then send SIGHUP to zoneadmd. + * + * Log rotation can be forced with SIGUSR1. In this case, the log will be + * rotated as though it hit the maximum size and will be subject to retention + * rules described above. + * + * + * Locking strategy + * + * Callers need not worry about locking. In the interest of simplicity, a + * single global lock is used to protect the state of the log files and the + * associated streams. Locking is necessary because reboots and log rotations + * can cause various state changes. Without locking, races could cause log + * entries to be directed to the wrong file descriptors. + * + * The simplistic global lock complicates error reporting within logging + * routines. zerror() must not be called while holding logging_lock. Rather, + * logstream_err() should be used to log via syslog. + */ + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <glob.h> +#include <libcustr.h> +#include <netdb.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <strings.h> +#include <synch.h> +#include <syslog.h> +#include <time.h> +#include <thread.h> +#include <unistd.h> + +#include <sys/debug.h> +#include <sys/stat.h> +#include <sys/sysmacros.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/varargs.h> + +#include "zoneadmd.h" + +/* + * Currently we only expect stdout, stderr, zoneadmd, and console. Increase + * MAX_ZLOG_STREAMS if more streams are added. If the count increases + * significantly, logfile_t and logstream_t elements should be dynamically + * allocated and the algorithms associated with opening and closing them should + * become more efficient. + */ +#define MAX_LOG_STREAMS 4 + +#define ZLOG_MAXSZ "zlog-max-size" /* zonecfg attr */ +#define ZLOG_MAXSZ_MIN (1024 * 1024) /* min size for autorotate */ +#define ZLOG_KEEP "zlog-keep-rotated" /* zonecfg attr */ +#define ZLOG_KEEP_MAX 1000 /* number of log files */ + +/* + * While we could get the numeric value of BUNYAN_LOG_INFO from bunyan.h, + * the log version is internal to the library, so we just define the values + * we need here. + */ +#define BUNYAN_VERSION 0 +#define BUNYAN_LOG_LEVEL 30 /* info */ + +typedef struct logfile { + char lf_path[MAXPATHLEN]; /* log file name (absolute path) */ + char lf_name[MAXNAMELEN]; /* tail of log file name */ + char lf_buf[BUFSIZ]; /* Buffer for event messages */ + custr_t *lf_cus; /* custr_t wrapper for lf_buf */ + int lf_fd; /* file descriptor */ + size_t lf_size; /* Current size */ + boolean_t lf_write_err; /* Avoid spamming console via logsys */ + boolean_t lf_closing; /* Avoid rotation recursion */ +} logfile_t; + +/* Large enough to hold BUFSIZ bytes with some escaping */ +#define LS_BUFSZ (BUFSIZ * 2) + +/* Large enough to hold LS_BUF contents + bunyan mandatory properties */ +#define LS_OBUFSZ (LS_BUFSZ + MAXNAMELEN + 128) + +typedef struct logstream { + char ls_stream[MAXNAMELEN]; /* stdout, stderr, etc. */ + char ls_buf[LS_BUFSZ]; /* Not-yet written data, json */ + char ls_obuf[LS_OBUFSZ]; /* Buffer to form output json */ + custr_t *ls_cusbuf; /* custr_t wrapper to ls_buf */ + custr_t *ls_cusobuf; /* custr_t wrapper to ls_ofbuf */ + logstream_flags_t ls_flags; + logfile_t *ls_logfile; /* N streams per log file */ +} logstream_t; + +typedef struct jsonpair { + const char *jp_key; + const char *jp_val; +} jsonpair_t; + +boolean_t logging_poisoned = B_FALSE; + +/* + * MAX_LOG_STREAMS is a small number so we allocate in the simplest way. + */ +static logstream_t streams[MAX_LOG_STREAMS]; +static logfile_t logfiles[MAX_LOG_STREAMS]; + +static char host[MAXHOSTNAMELEN]; +static char pidstr[10]; + +static boolean_t logging_initialized = B_FALSE; +static uint64_t logging_rot_size; /* See ZLOG_MAXSZ */ +static uint64_t logging_rot_keep; /* See ZLOG_KEEP */ +static int logging_pending_sig = 0; /* Signal recvd while logging */ +static mutex_t logging_lock = ERRORCHECKMUTEX; /* The global logging lock */ + +static void logstream_flush_all(logfile_t *); +static void logstream_sighandler(int); +static void rotate_log(logfile_t *); +static size_t make_json(jsonpair_t *, size_t, custr_t *); +static void logfile_write(logfile_t *, custr_t *); + +/* + * If errors are encountered while logging_lock is held, we can't use zerror(). + */ +static void +logstream_err(boolean_t use_strerror, const char *fmt, ...) +{ + va_list alist; + char buf[MAXPATHLEN * 2]; + char *bp; + int saved_errno = errno; + + (void) snprintf(buf, sizeof (buf), "[zone %s] ", zone_name); + + bp = &buf[strlen(buf)]; + + va_start(alist, fmt); + (void) vsnprintf(bp, sizeof (buf) - (bp - buf), fmt, alist); + va_end(alist); + + if (use_strerror) { + bp = &buf[strlen(buf)]; + (void) snprintf(bp, sizeof (buf) - (bp - buf), ": %s", + strerror(saved_errno)); + } + syslog(LOG_ERR, "%s", buf); + + errno = saved_errno; +} + +static void +logstream_lock(void) +{ + VERIFY(logging_initialized); + VERIFY(!logging_poisoned); + + mutex_enter(&logging_lock); +} + +static void +logstream_unlock(void) +{ + int sig = logging_pending_sig; + + logging_pending_sig = 0; + mutex_exit(&logging_lock); + + /* + * If a signal arrived while this thread was holding the lock, call the + * handler. + */ + if (sig != 0) { + logstream_sighandler(sig); + } +} + +static void +logfile_write_event(logfile_t *lfp, const char *stream, const char *event) +{ + size_t len; + jsonpair_t pairs[] = { + { "stream", stream }, + { "msg", event } + }; + + len = make_json(pairs, ARRAY_SIZE(pairs), lfp->lf_cus); + if (len >= sizeof (lfp->lf_buf)) { + logstream_err(B_FALSE, "%s: buffer too small. Need %zu bytes, " + "have %zu bytes", __func__, len + 1, sizeof (lfp->lf_buf)); + return; + } + + logfile_write(lfp, lfp->lf_cus); +} + +static void +close_log(logfile_t *lfp, const char *why, boolean_t ign_err) +{ + int err; + + VERIFY(MUTEX_HELD(&logging_lock)); + + /* + * Something may have gone wrong during log rotation, leading to a + * zombie log. + */ + if (lfp->lf_fd == -1) { + return; + } + + lfp->lf_closing = B_TRUE; + + logstream_flush_all(lfp); + + logfile_write_event(lfp, "logfile", why); + + err = close(lfp->lf_fd); + if (!ign_err) + VERIFY0(err); + + lfp->lf_size = 0; + lfp->lf_fd = -1; +} + +static void +open_log(logfile_t *lfp, const char *why) +{ + struct stat64 sb; + + VERIFY(MUTEX_HELD(&logging_lock)); + VERIFY3S(lfp->lf_fd, ==, -1); + + lfp->lf_fd = open(lfp->lf_path, + O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, 0600); + if (lfp->lf_fd == -1) { + logstream_err(B_TRUE, "Cannot open log file %s", + lfp->lf_path); + lfp->lf_write_err = B_TRUE; + return; + } + + VERIFY0(fstat64(lfp->lf_fd, &sb)); + lfp->lf_size = sb.st_size; + lfp->lf_write_err = B_FALSE; + lfp->lf_closing = B_FALSE; + + logfile_write_event(lfp, "logfile", why); +} + +static void +logstream_sighandler(int sig) +{ + int i; + + /* + * Protect against recursive mutex enters when a signal comes during + * logging. This will cause this function to be called again just after + * this thread drops the lock. + */ + if (MUTEX_HELD(&logging_lock)) { + logging_pending_sig = sig; + return; + } + + logstream_lock(); + if (logging_poisoned) { + logstream_unlock(); + return; + } + + for (i = 0; i < ARRAY_SIZE(logfiles); i++) { + /* Inactive logfile slot */ + if (logfiles[i].lf_name[0] == '\0') { + continue; + } + + switch (sig) { + case SIGHUP: + close_log(&logfiles[i], "close-rotate", B_FALSE); + open_log(&logfiles[i], "open-rotate"); + break; + case SIGUSR1: + rotate_log(&logfiles[i]); + break; + default: + logstream_err(B_FALSE, "unhandled signal %d", sig); + } + } + + logstream_unlock(); +} + +static void +get_attr_uint64(zlog_t *zlogp, zone_dochandle_t handle, const char *name, + uint64_t max, uint64_t *valp) +{ + struct zone_attrtab tab = { 0 }; + char *p; + uint64_t val; + + ASSERT(!MUTEX_HELD(&logging_lock)); + + (void) strlcpy(tab.zone_attr_name, name, sizeof (tab.zone_attr_name)); + if (zonecfg_lookup_attr(handle, &tab) != Z_OK) { + return; + } + + errno = 0; + val = strtol(tab.zone_attr_value, &p, 10); + if (errno != 0 && *p == '\0') { + zerror(zlogp, errno != 0, "Bad value '%s' for 'attr name=%s'", + tab.zone_attr_value, tab.zone_attr_name); + return; + } + if (val > max) { + zerror(zlogp, B_FALSE, "Value of attr '%s' is too large. " + "Reducing to %llu", name, max); + val = max; + } + + *valp = val; +} + +static void +logstream_atfork_prepare(void) +{ + logstream_lock(); +} + +static void +logstream_atfork_parent(void) +{ + logstream_unlock(); +} + +/* + * logstream_*() should never be called in a child process, so we make sure this + * code is never called there. + * + * zerror() in a child process is still safe: it knows to check for poisoning, + * and in such a case will redirect its output to stderr on the presumption it + * is a pipe to the parent. + */ +static void +logstream_atfork_child(void) +{ + logging_poisoned = B_TRUE; + logging_pending_sig = 0; + (void) snprintf(pidstr, sizeof (pidstr), "%d", getpid()); + logstream_unlock(); +} + +void +logstream_init(zlog_t *zlogp) +{ + zone_dochandle_t handle; + int i; + + VERIFY(!logging_initialized); + + VERIFY0(gethostname(host, sizeof (host))); + (void) snprintf(pidstr, sizeof (pidstr), "%d", getpid()); + + for (i = 0; i < ARRAY_SIZE(logfiles); i++) { + logfile_t *lfp = &logfiles[i]; + + lfp->lf_fd = -1; + if (custr_alloc_buf(&lfp->lf_cus, lfp->lf_buf, + sizeof (lfp->lf_buf)) != 0) { + (void) fprintf(stderr, "failed to allocate custr_t for " + "log file\n"); + abort(); + } + } + + for (i = 0; i < ARRAY_SIZE(streams); i++) { + logstream_t *lsp = &streams[i]; + + if (custr_alloc_buf(&lsp->ls_cusbuf, lsp->ls_buf, + sizeof (lsp->ls_buf)) != 0 || + custr_alloc_buf(&lsp->ls_cusobuf, lsp->ls_obuf, + sizeof (lsp->ls_obuf)) != 0) { + (void) fprintf(stderr, "failed to allocate custr_t for " + "log stream\n"); + abort(); + } + } + + VERIFY0(pthread_atfork(logstream_atfork_prepare, + logstream_atfork_parent, logstream_atfork_child)); + + logging_initialized = B_TRUE; + + /* Now it is safe to use zlogp */ + + if ((handle = zonecfg_init_handle()) == NULL || + zonecfg_get_handle(zone_name, handle) != Z_OK) { + zerror(zlogp, B_FALSE, "failed to open zone configuration " + "while initializing logging"); + } else { + get_attr_uint64(zlogp, handle, ZLOG_MAXSZ, UINT64_MAX, + &logging_rot_size); + if (logging_rot_size != 0 && + logging_rot_size < ZLOG_MAXSZ_MIN) { + zerror(zlogp, B_FALSE, "%s value %llu is too small. " + "Setting to %d", ZLOG_MAXSZ, logging_rot_size, + ZLOG_MAXSZ_MIN); + logging_rot_size = ZLOG_MAXSZ_MIN; + } + get_attr_uint64(zlogp, handle, ZLOG_KEEP, ZLOG_KEEP_MAX, + &logging_rot_keep); + } + + zonecfg_fini_handle(handle); + + /* + * This thread should receive SIGHUP so that it can close the log + * file and reopen it during log rotation. SIGUSR1 can be used to force + * a log rotation. + */ + (void) sigset(SIGHUP, logstream_sighandler); + (void) sigset(SIGUSR1, logstream_sighandler); +} + +/* + * Rotate a single log file. The global lock must be held while this is called. + */ +static void +rotate_log(logfile_t *lfp) +{ + time_t t; + struct tm gtm; + char path[MAXPATHLEN]; + int64_t i; + size_t len; + glob_t glb = { 0 }; + int err; + + VERIFY(MUTEX_HELD(&logging_lock)); + + if (lfp->lf_closing) { + return; + } + + if ((t = time(NULL)) == (time_t)-1 || gmtime_r(&t, >m) == NULL) { + logstream_err(B_TRUE, "failed to format time"); + return; + } + + (void) snprintf(path, sizeof (path), "%s.%04d%02d%02dT%02d%02d%02dZ", + lfp->lf_path, gtm.tm_year + 1900, gtm.tm_mon + 1, gtm.tm_mday, + gtm.tm_hour, gtm.tm_min, gtm.tm_sec); + + if (rename(lfp->lf_path, path) != 0) { + logstream_err(B_TRUE, "failed to rotate log file " + "'%s' to '%s'", lfp->lf_path, path); + } + + close_log(lfp, "close-rotate", B_FALSE); + open_log(lfp, "open-rotate"); + + if (logging_rot_keep == 0) { + return; + } + + /* + * Remove old logs. + */ + len = snprintf(path, sizeof (path), + /* <lf_path>.YYYYmmdd */ + "%s.[12][0-9][0-9][0-9][01][0-9][0-3][0-9]" + /* THHMMSSZ */ + "T[012][0-9][0-5][0-9][0-6][0-9]Z", lfp->lf_path); + if (len >= sizeof (path)) { + logstream_err(B_FALSE, "log rotation glob too long"); + return; + } + + if ((err = glob(path, GLOB_LIMIT, NULL, &glb)) != 0) { + if (err != GLOB_NOMATCH) { + logstream_err(B_TRUE, "glob terminated with error %d", + err); + } + globfree(&glb); + return; + } + + if (glb.gl_pathc <= logging_rot_keep) { + globfree(&glb); + return; + } + + for (i = glb.gl_pathc - logging_rot_keep - 1; i >= 0; i--) { + if (unlink(glb.gl_pathv[i]) != 0) { + logstream_err(B_TRUE, "log rotation could not remove " + "%s", glb.gl_pathv[i]); + } + } + globfree(&glb); +} + +/* + * Modify the input string with json escapes. Since the destination can thus + * be larger than the source, multiple calls may be required to fully convert + * sbuf to json. + * + * sbuf, slen Source buffer and the number of bytes in it to process + * dest Destination custr_t containing escaped JSON. + * scntp On return, *scntp stores number of scnt bytes consumed + * flushp If non-NULL, line-buffered mode is enabled. Processing + * will stop at the first newline or when dest is full and + * *flushp will be set to B_TRUE. + * + * This function makes no attempt to handle wide characters properly because + * the messages that come in may be using any character encoding. Since + * characters other than 7-bit ASCII are not directly readable in the log + * anyway, it is better to log the raw data and leave it to specialized log + * readers to interpret non-ASCII data. + */ +static void +escape_json(const char *sbuf, size_t slen, custr_t *dest, size_t *scntp, + boolean_t *flushp) +{ + char c; + const char *save_sbuf = sbuf; + const char *sbuf_end = sbuf + slen - 1; + char append_buf[7]; /* "\\u0000\0" */ + const char *append; + int len; + + if (slen == 0) { + *scntp = 0; + return; + } + + if (flushp != NULL) { + *flushp = B_FALSE; + } + + while (sbuf <= sbuf_end) { + c = sbuf[0]; + + switch (c) { + case '\\': + append = "\\\\"; + break; + + case '"': + append = "\\\""; + break; + + case '\b': + append = "\\b"; + break; + + case '\f': + append = "\\f"; + break; + + case '\n': + append = "\\n"; + if (flushp != NULL) { + *flushp = B_TRUE; + } + break; + + case '\r': + append = "\\r"; + break; + + case '\t': + append = "\\t"; + break; + + default: + if (c >= 0x20 && c < 0x7f) { + append_buf[0] = c; + append_buf[1] = '\0'; + } else { + len = snprintf(append_buf, sizeof (append_buf), + "\\u%04x", (int)(0xff & c)); + VERIFY3S(len, <, sizeof (append_buf)); + } + append = append_buf; + break; + } + + if (custr_append(dest, append) != 0) { + VERIFY3S(errno, ==, EOVERFLOW); + if (flushp != NULL) { + *flushp = B_TRUE; + } + break; + } + + sbuf++; + + if (flushp != NULL && *flushp) { + break; + } + } + + *scntp = sbuf - save_sbuf; + + VERIFY3U(*scntp, <=, slen); +} + +/* + * Like write(2), but to a logfile_t and with retries on short writes. + */ +static void +logfile_write(logfile_t *lfp, custr_t *cus) +{ + const char *buf = custr_cstr(cus); + size_t buflen = custr_len(cus); + ssize_t wlen; + size_t wanted = buflen; + + while (buflen > 0) { + wlen = write(lfp->lf_fd, buf, buflen); + if (wlen == -1) { + if (lfp->lf_write_err) { + lfp->lf_write_err = B_TRUE; + logstream_err(B_TRUE, "log file fd %d '%s': " + "failed to write %llu of %llu bytes", + lfp->lf_fd, lfp->lf_path, buflen, wanted); + } + return; + } + buf += wlen; + buflen -= wlen; + lfp->lf_size += wlen; + + lfp->lf_write_err = B_FALSE; + } + + if (logging_rot_size != 0 && lfp->lf_size > logging_rot_size) { + rotate_log(lfp); + } +} + +static void +add_bunyan_preamble(custr_t *cus) +{ + struct tm gtm; + struct timeval tv; + /* Large enough for YYYY-MM-DDTHH:MM:SS.000000000Z + NUL */ + char timestr[32] = { 0 }; + size_t len; + + if (gettimeofday(&tv, NULL) != 0 || + gmtime_r(&tv.tv_sec, >m) == NULL) { + logstream_err(B_TRUE, "failed to get time of day"); + abort(); + } + + len = strftime(timestr, sizeof (timestr) - 1, "%FT%T", >m); + VERIFY3U(len, >, 0); + VERIFY3U(len, <, sizeof (timestr) - 1); + + VERIFY0(custr_append_printf(cus, "\"time\": \"%s.%09ldZ\", ", + timestr, tv.tv_usec * 1000)); + VERIFY0(custr_append_printf(cus, "\"v\": %d, ", BUNYAN_VERSION)); + VERIFY0(custr_append_printf(cus, "\"hostname\": \"%s\", ", host)); + VERIFY0(custr_append(cus, "\"name\": \"zoneadmd\",")); + VERIFY0(custr_append_printf(cus, "\"pid\": %s, ", pidstr)); + VERIFY0(custr_append_printf(cus, "\"level\": %d", BUNYAN_LOG_LEVEL)); +} + +/* + * Convert the json pairs into a json object. The properties required for + * bunyan-formatted json objects are added to every object. + * Returns the number of bytes that would have been written to + * buf if bufsz had buf been sufficiently large (excluding the terminating null + * byte). Like snprintf(). + */ +static size_t +make_json(jsonpair_t *pairs, size_t npairs, custr_t *cus) +{ + int i; + const char *key, *val; + const char *start = ", "; + + VERIFY3S(npairs, >, 0); + + custr_reset(cus); + + VERIFY0(custr_append(cus, "{ ")); + + add_bunyan_preamble(cus); + + for (i = 0; i < npairs; i++) { + size_t len; + + key = pairs[i].jp_key; + val = pairs[i].jp_val; + + /* The total number of bytes we're adding to cus */ + len = 3 + strlen(key) + 3 + strlen(val) + 1; + if (custr_append_printf(cus, "%s\"%s\":\"%s\"", + start, key, val) != 0) { + VERIFY3S(errno, ==, EOVERFLOW); + return (custr_len(cus) + len); + } + } + + if (custr_append(cus, " }\n") != 0) { + return (custr_len(cus) + 3); + } + + return (custr_len(cus)); +} + +static void +logstream_write_json(logstream_t *lsp) +{ + size_t len; + jsonpair_t pairs[] = { + { "msg", lsp->ls_buf }, + { "stream", lsp->ls_stream }, + }; + + if (custr_len(lsp->ls_cusbuf) == 0) { + return; + } + + len = make_json(pairs, ARRAY_SIZE(pairs), lsp->ls_cusobuf); + + custr_reset(lsp->ls_cusbuf); + if (len >= sizeof (lsp->ls_obuf)) { + logstream_err(B_FALSE, "%s: buffer too small. Need %llu bytes, " + "have %llu bytes", __func__, len + 1, + sizeof (lsp->ls_obuf)); + return; + } + + logfile_write(lsp->ls_logfile, lsp->ls_cusobuf); +} + +/* + * We output to the log file as json. + * ex. for string 'msg\n' on the zone's stdout: + * {"log":"msg\n","stream":"stdout","time":"2014-10-24T20:12:11.101973117Z"} + * + * We use ns in the last field of the timestamp for compatibility. + * + * We keep track of the size of the log file and rotate it when we exceed + * the log size limit (if one is set). + */ +void +logstream_write(int ls, char *buf, int len) +{ + logstream_t *lsp; + size_t scnt; + boolean_t newline; + boolean_t buffered; + + if (ls == -1 || len == 0) { + return; + } + VERIFY3S(ls, >=, 0); + VERIFY3S(ls, <, ARRAY_SIZE(streams)); + + logstream_lock(); + + lsp = &streams[ls]; + if (lsp->ls_stream[0] == '\0' || lsp->ls_logfile == NULL) { + logstream_unlock(); + return; + } + + buffered = !!(lsp->ls_flags & LS_LINE_BUFFERED); + + do { + escape_json(buf, len, lsp->ls_cusbuf, &scnt, + buffered ? &newline : NULL); + + buf += scnt; + len -= scnt; + + if (!buffered || newline) { + logstream_write_json(lsp); + } + } while (len > 0 && (!buffered || newline)); + + logstream_unlock(); +} + +static void +logstream_flush(int ls) +{ + logstream_t *lsp; + + VERIFY(MUTEX_HELD(&logging_lock)); + + lsp = &streams[ls]; + if (lsp->ls_stream[0] == '\0' || lsp->ls_logfile == NULL) { + return; + } + logstream_write_json(lsp); +} + +static void +logstream_flush_all(logfile_t *lfp) +{ + int i; + + VERIFY(MUTEX_HELD(&logging_lock)); + + for (i = 0; i < ARRAY_SIZE(streams); i++) { + if (streams[i].ls_logfile == lfp) { + logstream_flush(i); + } + } +} + +int +logstream_open(const char *logname, const char *stream, logstream_flags_t flags) +{ + int ls = -1; + int i; + logstream_t *lsp; + logfile_t *lfp = NULL; + + VERIFY3U(strlen(logname), <, sizeof (lfp->lf_name)); + VERIFY3U(strlen(stream), <, sizeof (lsp->ls_stream)); + + logstream_lock(); + + /* + * Find an empty logstream_t and verify that the stream is not already + * open. + */ + for (i = 0; i < ARRAY_SIZE(streams); i++) { + if (ls == -1 && streams[i].ls_stream[0] == '\0') { + VERIFY3P(streams[i].ls_logfile, ==, NULL); + ls = i; + continue; + } + if (strcmp(stream, streams[i].ls_stream) == 0) { + logstream_unlock(); + logstream_err(B_FALSE, "log stream %s already open", + stream); + return (-1); + } + } + VERIFY3S(ls, !=, -1); + + /* Find an existing or available logfile_t */ + for (i = 0; i < ARRAY_SIZE(logfiles); i++) { + if (lfp == NULL && logfiles[i].lf_name[0] == '\0') { + lfp = &logfiles[i]; + } + if (strcmp(logname, logfiles[i].lf_name) == 0) { + lfp = &logfiles[i]; + break; + } + } + if (lfp->lf_name[0] == '\0') { + (void) strlcpy(lfp->lf_name, logname, sizeof (lfp->lf_name)); + (void) snprintf(lfp->lf_path, sizeof (lfp->lf_path), "%s/logs", + zonepath); + (void) mkdir(lfp->lf_path, 0700); + + (void) snprintf(lfp->lf_path, sizeof (lfp->lf_path), + "%s/logs/%s", zonepath, logname); + + open_log(lfp, "open"); + if (lfp->lf_fd == -1) { + logstream_unlock(); + return (-1); + } + } + + lsp = &streams[ls]; + (void) strlcpy(lsp->ls_stream, stream, sizeof (lsp->ls_stream)); + + lsp->ls_flags = flags; + lsp->ls_logfile = lfp; + + logstream_unlock(); + + return (ls); +} + +static void +logstream_reset(logstream_t *lsp) +{ + custr_t *buf = lsp->ls_cusbuf; + custr_t *obuf = lsp->ls_cusobuf; + + (void) memset(lsp, 0, sizeof (*lsp)); + lsp->ls_cusbuf = buf; + lsp->ls_cusobuf = obuf; + + custr_reset(buf); + custr_reset(obuf); +} + +static void +logfile_reset(logfile_t *lfp) +{ + custr_t *buf = lfp->lf_cus; + + (void) memset(lfp, 0, sizeof (*lfp)); + lfp->lf_cus = buf; + lfp->lf_fd = -1; + + custr_reset(buf); +} + +void +logstream_close(int ls, boolean_t abrupt) +{ + logstream_t *lsp; + logfile_t *lfp; + int i; + + if (ls == -1) { + return; + } + VERIFY3S(ls, >=, 0); + VERIFY3S(ls, <, ARRAY_SIZE(streams)); + + logstream_lock(); + logstream_flush(ls); + + lsp = &streams[ls]; + lfp = lsp->ls_logfile; + + VERIFY(lsp->ls_stream[0] != '\0'); + VERIFY3P(lfp, !=, NULL); + + logstream_reset(lsp); + + for (i = 0; i < ARRAY_SIZE(streams); i++) { + if (streams[i].ls_logfile == lfp) { + logstream_unlock(); + return; + } + } + + /* No more streams using this log file so return to initial state */ + + close_log(lfp, "close", abrupt); + + logfile_reset(lfp); + + logstream_unlock(); +} diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c index 4a2d94605b..01332d43e8 100644 --- a/usr/src/cmd/zoneadmd/vplat.c +++ b/usr/src/cmd/zoneadmd/vplat.c @@ -21,7 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 2013, Joyent Inc. All rights reserved. + * Copyright 2018, Joyent Inc. * Copyright (c) 2015, 2016 by Delphix. All rights reserved. * Copyright 2019 OmniOS Community Edition (OmniOSce) Association. */ @@ -79,10 +79,12 @@ #include <sys/conf.h> #include <sys/systeminfo.h> #include <sys/secflags.h> +#include <sys/vnic.h> #include <libdlpi.h> #include <libdllink.h> #include <libdlvlan.h> +#include <libdlvnic.h> #include <inet/tcp.h> #include <arpa/inet.h> @@ -138,6 +140,9 @@ #define DFSTYPES "/etc/dfs/fstypes" #define MAXTNZLEN 2048 +/* Number of times to retry unmounting if it fails */ +#define UMOUNT_RETRIES 30 + #define ALT_MOUNT(mount_cmd) ((mount_cmd) != Z_MNT_BOOT) /* a reasonable estimate for the number of lwps per process */ @@ -163,11 +168,25 @@ static priv_set_t *zprivs = NULL; static const char *DFLT_FS_ALLOWED = "hsfs,smbfs,nfs,nfs3,nfs4,nfsdyn"; +typedef struct zone_proj_rctl_map { + char *zpr_zone_rctl; + char *zpr_project_rctl; +} zone_proj_rctl_map_t; + +static zone_proj_rctl_map_t zone_proj_rctl_map[] = { + {"zone.max-msg-ids", "project.max-msg-ids"}, + {"zone.max-sem-ids", "project.max-sem-ids"}, + {"zone.max-shm-ids", "project.max-shm-ids"}, + {"zone.max-shm-memory", "project.max-shm-memory"}, + {NULL, NULL} +}; + /* from libsocket, not in any header file */ extern int getnetmaskbyaddr(struct in_addr, struct in_addr *); /* from zoneadmd */ extern char query_hook[]; +extern char post_statechg_hook[]; /* * For each "net" resource configured in zonecfg, we track a zone_addr_list_t @@ -204,7 +223,7 @@ autofs_cleanup(zoneid_t zoneid) /* * Ask autofs to unmount all trigger nodes in the given zone. */ - return (_autofssys(AUTOFS_UNMOUNTALL, (void *)zoneid)); + return (_autofssys(AUTOFS_UNMOUNTALL, (void *)((uintptr_t)zoneid))); } static void @@ -595,6 +614,24 @@ root_to_lu(zlog_t *zlogp, char *zroot, size_t zrootlen, boolean_t isresolved) } /* + * Perform brand-specific cleanup if we are unable to unmount a FS. + */ +static void +brand_umount_cleanup(zlog_t *zlogp, char *path) +{ + char cmdbuf[2 * MAXPATHLEN]; + + if (post_statechg_hook[0] == '\0') + return; + + if (snprintf(cmdbuf, sizeof (cmdbuf), "%s %d %d %s", post_statechg_hook, + ZONE_STATE_DOWN, Z_UNMOUNT, path) > sizeof (cmdbuf)) + return; + + (void) do_subproc(zlogp, cmdbuf, NULL, B_FALSE); +} + +/* * The general strategy for unmounting filesystems is as follows: * * - Remote filesystems may be dead, and attempting to contact them as @@ -627,6 +664,7 @@ static int unmount_filesystems(zlog_t *zlogp, zoneid_t zoneid, boolean_t unmount_cmd) { int error = 0; + int fail = 0; FILE *mnttab; struct mnttab *mnts; uint_t nmnt; @@ -714,18 +752,39 @@ unmount_filesystems(zlog_t *zlogp, zoneid_t zoneid, boolean_t unmount_cmd) if (umount2(path, MS_FORCE) == 0) { unmounted = B_TRUE; stuck = B_FALSE; + fail = 0; } else { /* - * The first failure indicates a - * mount we won't be able to get - * rid of automatically, so we - * bail. + * We may hit a failure here if there + * is an app in the GZ with an open + * pipe into the zone (commonly into + * the zone's /var/run). This type + * of app will notice the closed + * connection and cleanup, but it may + * take a while and we have no easy + * way to notice that. To deal with + * this case, we will wait and retry + * a few times before we give up. */ - error++; - zerror(zlogp, B_FALSE, - "unable to unmount '%s'", path); - free_mnttable(mnts, nmnt); - goto out; + fail++; + if (fail < (UMOUNT_RETRIES - 1)) { + zerror(zlogp, B_FALSE, + "unable to unmount '%s', " + "retrying in 2 seconds", + path); + (void) sleep(2); + } else if (fail > UMOUNT_RETRIES) { + error++; + zerror(zlogp, B_FALSE, + "unmount of '%s' failed", + path); + free_mnttable(mnts, nmnt); + goto out; + } else { + /* Try the hook 2 times */ + brand_umount_cleanup(zlogp, + path); + } } } /* @@ -1063,23 +1122,10 @@ mount_one_dev_symlink_cb(void *arg, const char *source, const char *target) int vplat_get_iptype(zlog_t *zlogp, zone_iptype_t *iptypep) { - zone_dochandle_t handle; - - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - if (zonecfg_get_iptype(handle, iptypep) != Z_OK) { + if (zonecfg_get_iptype(snap_hndl, iptypep) != Z_OK) { zerror(zlogp, B_FALSE, "invalid ip-type configuration"); - zonecfg_fini_handle(handle); return (-1); } - zonecfg_fini_handle(handle); return (0); } @@ -1092,14 +1138,13 @@ static int mount_one_dev(zlog_t *zlogp, char *devpath, zone_mnt_t mount_cmd) { char brand[MAXNAMELEN]; - zone_dochandle_t handle = NULL; brand_handle_t bh = NULL; struct zone_devtab ztab; di_prof_t prof = NULL; int err; int retval = -1; zone_iptype_t iptype; - const char *curr_iptype; + const char *curr_iptype = NULL; if (di_prof_init(devpath, &prof)) { zerror(zlogp, B_TRUE, "failed to initialize profile"); @@ -1134,6 +1179,8 @@ mount_one_dev(zlog_t *zlogp, char *devpath, zone_mnt_t mount_cmd) curr_iptype = "exclusive"; break; } + if (curr_iptype == NULL) + abort(); if (brand_platform_iter_devices(bh, zone_name, mount_one_dev_device_cb, prof, curr_iptype) != 0) { @@ -1148,28 +1195,25 @@ mount_one_dev(zlog_t *zlogp, char *devpath, zone_mnt_t mount_cmd) } /* Add user-specified devices and directories */ - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_FALSE, "can't initialize zone handle"); - goto cleanup; - } - if (err = zonecfg_get_handle(zone_name, handle)) { - zerror(zlogp, B_FALSE, "can't get handle for zone " - "%s: %s", zone_name, zonecfg_strerror(err)); - goto cleanup; - } - if (err = zonecfg_setdevent(handle)) { + if ((err = zonecfg_setdevent(snap_hndl)) != 0) { zerror(zlogp, B_FALSE, "%s: %s", zone_name, zonecfg_strerror(err)); goto cleanup; } - while (zonecfg_getdevent(handle, &ztab) == Z_OK) { - if (di_prof_add_dev(prof, ztab.zone_dev_match)) { + while (zonecfg_getdevent(snap_hndl, &ztab) == Z_OK) { + char path[MAXPATHLEN]; + + if ((err = resolve_device_match(zlogp, &ztab, + path, sizeof (path))) != Z_OK) + goto cleanup; + + if (di_prof_add_dev(prof, path)) { zerror(zlogp, B_TRUE, "failed to add " - "user-specified device"); + "user-specified device '%s'", path); goto cleanup; } } - (void) zonecfg_enddevent(handle); + (void) zonecfg_enddevent(snap_hndl); /* Send profile to kernel */ if (di_prof_commit(prof)) { @@ -1182,8 +1226,6 @@ mount_one_dev(zlog_t *zlogp, char *devpath, zone_mnt_t mount_cmd) cleanup: if (bh != NULL) brand_close(bh); - if (handle != NULL) - zonecfg_fini_handle(handle); if (prof) di_prof_fini(prof); return (retval); @@ -1674,12 +1716,10 @@ static int mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) { char rootpath[MAXPATHLEN]; - char zonepath[MAXPATHLEN]; char brand[MAXNAMELEN]; char luroot[MAXPATHLEN]; int i, num_fs = 0; struct zone_fstab *fs_ptr = NULL; - zone_dochandle_t handle = NULL; zone_state_t zstate; brand_handle_t bh; plat_gmount_cb_data_t cb; @@ -1693,22 +1733,12 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) goto bad; } - if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) { - zerror(zlogp, B_TRUE, "unable to determine zone path"); - goto bad; - } - if (zone_get_rootpath(zone_name, rootpath, sizeof (rootpath)) != Z_OK) { zerror(zlogp, B_TRUE, "unable to determine zone root"); goto bad; } - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - goto bad; - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK || - zonecfg_setfsent(handle) != Z_OK) { + if (zonecfg_setfsent(snap_hndl) != Z_OK) { zerror(zlogp, B_FALSE, "invalid configuration"); goto bad; } @@ -1726,7 +1756,6 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) /* Get a handle to the brand info for this zone */ if ((bh = brand_open(brand)) == NULL) { zerror(zlogp, B_FALSE, "unable to determine zone brand"); - zonecfg_fini_handle(handle); return (-1); } @@ -1741,7 +1770,6 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) plat_gmount_cb, &cb) != 0) { zerror(zlogp, B_FALSE, "unable to mount filesystems"); brand_close(bh); - zonecfg_fini_handle(handle); return (-1); } brand_close(bh); @@ -1752,13 +1780,10 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) * higher level directories (e.g., /usr) get mounted before * any beneath them (e.g., /usr/local). */ - if (mount_filesystems_fsent(handle, zlogp, &fs_ptr, &num_fs, + if (mount_filesystems_fsent(snap_hndl, zlogp, &fs_ptr, &num_fs, mount_cmd) != 0) goto bad; - zonecfg_fini_handle(handle); - handle = NULL; - /* * Normally when we mount a zone all the zone filesystems * get mounted relative to rootpath, which is usually @@ -1798,23 +1823,40 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) qsort(fs_ptr, num_fs, sizeof (*fs_ptr), fs_compare); for (i = 0; i < num_fs; i++) { - if (ALT_MOUNT(mount_cmd) && - strcmp(fs_ptr[i].zone_fs_dir, "/dev") == 0) { - size_t slen = strlen(rootpath) - 2; + if (ALT_MOUNT(mount_cmd)) { + if (strcmp(fs_ptr[i].zone_fs_dir, "/dev") == 0) { + size_t slen = strlen(rootpath) - 2; - /* - * By default we'll try to mount /dev as /a/dev - * but /dev is special and always goes at the top - * so strip the trailing '/a' from the rootpath. - */ - assert(strcmp(&rootpath[slen], "/a") == 0); - rootpath[slen] = '\0'; - if (mount_one(zlogp, &fs_ptr[i], rootpath, mount_cmd) - != 0) - goto bad; - rootpath[slen] = '/'; - continue; + /* + * By default we'll try to mount /dev + * as /a/dev but /dev is special and + * always goes at the top so strip the + * trailing '/a' from the rootpath. + */ + assert(strcmp(&rootpath[slen], "/a") == 0); + rootpath[slen] = '\0'; + if (mount_one(zlogp, &fs_ptr[i], rootpath, + mount_cmd) != 0) + goto bad; + rootpath[slen] = '/'; + continue; + } else if (strcmp(brand_name, default_brand) != 0) { + /* + * If mounting non-native brand, skip + * mounting global mounts and + * filesystem entries since they are + * only needed for native pkg upgrade + * tools. + * + * The only exception right now is + * /dev (handled above), which is + * needed in the luroot in order to + * zlogin -S into the zone. + */ + continue; + } } + if (mount_one(zlogp, &fs_ptr[i], rootpath, mount_cmd) != 0) goto bad; } @@ -1837,8 +1879,6 @@ mount_filesystems(zlog_t *zlogp, zone_mnt_t mount_cmd) return (0); bad: - if (handle != NULL) - zonecfg_fini_handle(handle); free_fs_data(fs_ptr, num_fs); return (-1); } @@ -2194,13 +2234,7 @@ configure_one_interface(zlog_t *zlogp, zoneid_t zone_id, if (ioctl(s, SIOCLIFADDIF, (caddr_t)&lifr) < 0) { /* * Here, we know that the interface can't be brought up. - * A similar warning message was already printed out to - * the console by zoneadm(1M) so instead we log the - * message to syslog and continue. */ - zerror(&logsys, B_TRUE, "WARNING: skipping network interface " - "'%s' which may not be present/plumbed in the " - "global zone.", lifr.lifr_name); (void) close(s); return (Z_OK); } @@ -2413,7 +2447,6 @@ bad: static int configure_shared_network_interfaces(zlog_t *zlogp) { - zone_dochandle_t handle; struct zone_nwiftab nwiftab, loopback_iftab; zoneid_t zoneid; @@ -2422,29 +2455,19 @@ configure_shared_network_interfaces(zlog_t *zlogp) return (-1); } - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - if (zonecfg_setnwifent(handle) == Z_OK) { + if (zonecfg_setnwifent(snap_hndl) == Z_OK) { for (;;) { - if (zonecfg_getnwifent(handle, &nwiftab) != Z_OK) + if (zonecfg_getnwifent(snap_hndl, &nwiftab) != Z_OK) break; + nwifent_free_attrs(&nwiftab); if (configure_one_interface(zlogp, zoneid, &nwiftab) != Z_OK) { - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); + (void) zonecfg_endnwifent(snap_hndl); return (-1); } } - (void) zonecfg_endnwifent(handle); + (void) zonecfg_endnwifent(snap_hndl); } - zonecfg_fini_handle(handle); if (is_system_labeled()) { /* * Labeled zones share the loopback interface @@ -2898,7 +2921,6 @@ free_ip_interface(zone_addr_list_t *zalist) static int configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) { - zone_dochandle_t handle; struct zone_nwiftab nwiftab; char rootpath[MAXPATHLEN]; char path[MAXPATHLEN]; @@ -2907,30 +2929,18 @@ configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) boolean_t added = B_FALSE; zone_addr_list_t *zalist = NULL, *new; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - - if (zonecfg_setnwifent(handle) != Z_OK) { - zonecfg_fini_handle(handle); + if (zonecfg_setnwifent(snap_hndl) != Z_OK) return (0); - } for (;;) { - if (zonecfg_getnwifent(handle, &nwiftab) != Z_OK) + if (zonecfg_getnwifent(snap_hndl, &nwiftab) != Z_OK) break; + nwifent_free_attrs(&nwiftab); if (prof == NULL) { if (zone_get_devroot(zone_name, rootpath, sizeof (rootpath)) != Z_OK) { - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); + (void) zonecfg_endnwifent(snap_hndl); zerror(zlogp, B_TRUE, "unable to determine dev root"); return (-1); @@ -2938,8 +2948,7 @@ configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) (void) snprintf(path, sizeof (path), "%s%s", rootpath, "/dev"); if (di_prof_init(path, &prof) != 0) { - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); + (void) zonecfg_endnwifent(snap_hndl); zerror(zlogp, B_TRUE, "failed to initialize profile"); return (-1); @@ -2963,17 +2972,17 @@ configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) nwiftab.zone_nwif_physical) == 0) { added = B_TRUE; } else { - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); - zerror(zlogp, B_TRUE, "failed to add network device"); - return (-1); + /* + * Failed to add network device, but the brand hook + * might be doing this for us, so keep silent. + */ + continue; } /* set up the new IP interface, and add them all later */ new = malloc(sizeof (*new)); if (new == NULL) { zerror(zlogp, B_TRUE, "no memory for %s", nwiftab.zone_nwif_physical); - zonecfg_fini_handle(handle); free_ip_interface(zalist); } bzero(new, sizeof (*new)); @@ -2983,16 +2992,14 @@ configure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) } if (zalist != NULL) { if ((errno = add_net(zlogp, zoneid, zalist)) != 0) { - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); + (void) zonecfg_endnwifent(snap_hndl); zerror(zlogp, B_TRUE, "failed to add address"); free_ip_interface(zalist); return (-1); } free_ip_interface(zalist); } - (void) zonecfg_endnwifent(handle); - zonecfg_fini_handle(handle); + (void) zonecfg_endnwifent(snap_hndl); if (prof != NULL && added) { if (di_prof_commit(prof) != 0) { @@ -3128,48 +3135,23 @@ remove_datalink_protect(zlog_t *zlogp, zoneid_t zoneid) /* datalink does not belong to the GZ */ continue; } - if (dlstatus != DLADM_STATUS_OK) { + if (dlstatus != DLADM_STATUS_OK) zerror(zlogp, B_FALSE, + "clear 'protection' link property: %s", dladm_status2str(dlstatus, dlerr)); - free(dllinks); - return (-1); - } + dlstatus = dladm_set_linkprop(dld_handle, *dllink, "allowed-ips", NULL, 0, DLADM_OPT_ACTIVE); - if (dlstatus != DLADM_STATUS_OK) { + if (dlstatus != DLADM_STATUS_OK) zerror(zlogp, B_FALSE, + "clear 'allowed-ips' link property: %s", dladm_status2str(dlstatus, dlerr)); - free(dllinks); - return (-1); - } } free(dllinks); return (0); } static int -unconfigure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid) -{ - int dlnum = 0; - - /* - * The kernel shutdown callback for the dls module should have removed - * all datalinks from this zone. If any remain, then there's a - * problem. - */ - if (zone_list_datalink(zoneid, &dlnum, NULL) != 0) { - zerror(zlogp, B_TRUE, "unable to list network interfaces"); - return (-1); - } - if (dlnum != 0) { - zerror(zlogp, B_FALSE, - "datalinks remain in zone after shutdown"); - return (-1); - } - return (0); -} - -static int tcp_abort_conn(zlog_t *zlogp, zoneid_t zoneid, const struct sockaddr_storage *local, const struct sockaddr_storage *remote) { @@ -3251,26 +3233,14 @@ static int get_privset(zlog_t *zlogp, priv_set_t *privs, zone_mnt_t mount_cmd) { int error = -1; - zone_dochandle_t handle; char *privname = NULL; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - if (ALT_MOUNT(mount_cmd)) { zone_iptype_t iptype; - const char *curr_iptype; + const char *curr_iptype = NULL; - if (zonecfg_get_iptype(handle, &iptype) != Z_OK) { + if (zonecfg_get_iptype(snap_hndl, &iptype) != Z_OK) { zerror(zlogp, B_TRUE, "unable to determine ip-type"); - zonecfg_fini_handle(handle); return (-1); } @@ -3283,17 +3253,15 @@ get_privset(zlog_t *zlogp, priv_set_t *privs, zone_mnt_t mount_cmd) break; } - if (zonecfg_default_privset(privs, curr_iptype) == Z_OK) { - zonecfg_fini_handle(handle); + if (zonecfg_default_privset(privs, curr_iptype) == Z_OK) return (0); - } + zerror(zlogp, B_FALSE, "failed to determine the zone's default privilege set"); - zonecfg_fini_handle(handle); return (-1); } - switch (zonecfg_get_privset(handle, privs, &privname)) { + switch (zonecfg_get_privset(snap_hndl, privs, &privname)) { case Z_OK: error = 0; break; @@ -3316,10 +3284,22 @@ get_privset(zlog_t *zlogp, priv_set_t *privs, zone_mnt_t mount_cmd) } free(privname); - zonecfg_fini_handle(handle); return (error); } +static char * +zone_proj_rctl(const char *name) +{ + int i; + + for (i = 0; zone_proj_rctl_map[i].zpr_zone_rctl != NULL; i++) { + if (strcmp(name, zone_proj_rctl_map[i].zpr_zone_rctl) == 0) { + return (zone_proj_rctl_map[i].zpr_project_rctl); + } + } + return (NULL); +} + static int get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) { @@ -3329,25 +3309,15 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) nvlist_t **nvlv = NULL; int rctlcount = 0; int error = -1; - zone_dochandle_t handle; struct zone_rctltab rctltab; rctlblk_t *rctlblk = NULL; uint64_t maxlwps; uint64_t maxprocs; + int rproc, rlwp; *bufp = NULL; *bufsizep = 0; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - rctltab.zone_rctl_valptr = NULL; if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) { zerror(zlogp, B_TRUE, "%s failed", "nvlist_alloc"); @@ -3356,22 +3326,31 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) /* * Allow the administrator to control both the maximum number of - * process table slots and the maximum number of lwps with just the - * max-processes property. If only the max-processes property is set, - * we add a max-lwps property with a limit derived from max-processes. + * process table slots, and the maximum number of lwps, with a single + * max-processes or max-lwps property. If only the max-processes + * property is set, we add a max-lwps property with a limit derived + * from max-processes. If only the max-lwps property is set, we add a + * max-processes property with the same limit as max-lwps. */ - if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXPROCS, &maxprocs) - == Z_OK && - zonecfg_get_aliased_rctl(handle, ALIAS_MAXLWPS, &maxlwps) - == Z_NO_ENTRY) { - if (zonecfg_set_aliased_rctl(handle, ALIAS_MAXLWPS, + rproc = zonecfg_get_aliased_rctl(snap_hndl, ALIAS_MAXPROCS, &maxprocs); + rlwp = zonecfg_get_aliased_rctl(snap_hndl, ALIAS_MAXLWPS, &maxlwps); + if (rproc == Z_OK && rlwp == Z_NO_ENTRY) { + if (zonecfg_set_aliased_rctl(snap_hndl, ALIAS_MAXLWPS, maxprocs * LWPS_PER_PROCESS) != Z_OK) { zerror(zlogp, B_FALSE, "unable to set max-lwps alias"); goto out; } + } else if (rlwp == Z_OK && rproc == Z_NO_ENTRY) { + /* no scaling for max-proc value */ + if (zonecfg_set_aliased_rctl(snap_hndl, ALIAS_MAXPROCS, + maxlwps) != Z_OK) { + zerror(zlogp, B_FALSE, + "unable to set max-processes alias"); + goto out; + } } - if (zonecfg_setrctlent(handle) != Z_OK) { + if (zonecfg_setrctlent(snap_hndl) != Z_OK) { zerror(zlogp, B_FALSE, "%s failed", "zonecfg_setrctlent"); goto out; } @@ -3380,10 +3359,11 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) zerror(zlogp, B_TRUE, "memory allocation failed"); goto out; } - while (zonecfg_getrctlent(handle, &rctltab) == Z_OK) { + while (zonecfg_getrctlent(snap_hndl, &rctltab) == Z_OK) { struct zone_rctlvaltab *rctlval; uint_t i, count; const char *name = rctltab.zone_rctl_name; + char *proj_nm; /* zoneadm should have already warned about unknown rctls. */ if (!zonecfg_is_rctl(name)) { @@ -3450,6 +3430,26 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) } zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); rctltab.zone_rctl_valptr = NULL; + + /* + * With no action on our part we will start zsched with the + * project rctl values for our (zoneadmd) current project. For + * brands running a variant of Illumos, that's not a problem + * since they will setup their own projects, but for a + * non-native brand like lx, where there are no projects, we + * want to start things up with the same project rctls as the + * corresponding zone rctls, since nothing within the zone will + * ever change the project rctls. + */ + if ((proj_nm = zone_proj_rctl(name)) != NULL) { + if (nvlist_add_nvlist_array(nvl, proj_nm, nvlv, count) + != 0) { + zerror(zlogp, B_FALSE, + "nvlist_add_nvlist_arrays failed"); + goto out; + } + } + if (nvlist_add_nvlist_array(nvl, (char *)name, nvlv, count) != 0) { zerror(zlogp, B_FALSE, "%s failed", @@ -3462,7 +3462,7 @@ get_rctls(zlog_t *zlogp, char **bufp, size_t *bufsizep) nvlv = NULL; rctlcount++; } - (void) zonecfg_endrctlent(handle); + (void) zonecfg_endrctlent(snap_hndl); if (rctlcount == 0) { error = 0; @@ -3486,8 +3486,6 @@ out: nvlist_free(nvl); if (nvlv != NULL) free(nvlv); - if (handle != NULL) - zonecfg_fini_handle(handle); return (error); } @@ -3503,7 +3501,7 @@ get_implicit_datasets(zlog_t *zlogp, char **retstr) > sizeof (cmdbuf)) return (-1); - if (do_subproc(zlogp, cmdbuf, retstr) != 0) + if (do_subproc(zlogp, cmdbuf, retstr, B_FALSE) != 0) return (-1); return (0); @@ -3512,7 +3510,6 @@ get_implicit_datasets(zlog_t *zlogp, char **retstr) static int get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) { - zone_dochandle_t handle; struct zone_dstab dstab; size_t total, offset, len; int error = -1; @@ -3523,30 +3520,20 @@ get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) *bufp = NULL; *bufsizep = 0; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - if (get_implicit_datasets(zlogp, &implicit_datasets) != 0) { zerror(zlogp, B_FALSE, "getting implicit datasets failed"); goto out; } - if (zonecfg_setdsent(handle) != Z_OK) { + if (zonecfg_setdsent(snap_hndl) != Z_OK) { zerror(zlogp, B_FALSE, "%s failed", "zonecfg_setdsent"); goto out; } total = 0; - while (zonecfg_getdsent(handle, &dstab) == Z_OK) + while (zonecfg_getdsent(snap_hndl, &dstab) == Z_OK) total += strlen(dstab.zone_dataset_name) + 1; - (void) zonecfg_enddsent(handle); + (void) zonecfg_enddsent(snap_hndl); if (implicit_datasets != NULL) implicit_len = strlen(implicit_datasets); @@ -3563,12 +3550,12 @@ get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) goto out; } - if (zonecfg_setdsent(handle) != Z_OK) { + if (zonecfg_setdsent(snap_hndl) != Z_OK) { zerror(zlogp, B_FALSE, "%s failed", "zonecfg_setdsent"); goto out; } offset = 0; - while (zonecfg_getdsent(handle, &dstab) == Z_OK) { + while (zonecfg_getdsent(snap_hndl, &dstab) == Z_OK) { len = strlen(dstab.zone_dataset_name); (void) strlcpy(str + offset, dstab.zone_dataset_name, total - offset); @@ -3576,7 +3563,7 @@ get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) if (offset < total - 1) str[offset++] = ','; } - (void) zonecfg_enddsent(handle); + (void) zonecfg_enddsent(snap_hndl); if (implicit_len > 0) (void) strlcpy(str + offset, implicit_datasets, total - offset); @@ -3588,8 +3575,6 @@ get_datasets(zlog_t *zlogp, char **bufp, size_t *bufsizep) out: if (error != 0 && str != NULL) free(str); - if (handle != NULL) - zonecfg_fini_handle(handle); if (implicit_datasets != NULL) free(implicit_datasets); @@ -3599,40 +3584,26 @@ out: static int validate_datasets(zlog_t *zlogp) { - zone_dochandle_t handle; struct zone_dstab dstab; zfs_handle_t *zhp; libzfs_handle_t *hdl; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (-1); - } - if (zonecfg_get_snapshot_handle(zone_name, handle) != Z_OK) { + if (zonecfg_setdsent(snap_hndl) != Z_OK) { zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (-1); - } - - if (zonecfg_setdsent(handle) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); return (-1); } if ((hdl = libzfs_init()) == NULL) { zerror(zlogp, B_FALSE, "opening ZFS library"); - zonecfg_fini_handle(handle); return (-1); } - while (zonecfg_getdsent(handle, &dstab) == Z_OK) { + while (zonecfg_getdsent(snap_hndl, &dstab) == Z_OK) { if ((zhp = zfs_open(hdl, dstab.zone_dataset_name, ZFS_TYPE_FILESYSTEM)) == NULL) { zerror(zlogp, B_FALSE, "cannot open ZFS dataset '%s'", dstab.zone_dataset_name); - zonecfg_fini_handle(handle); libzfs_fini(hdl); return (-1); } @@ -3647,7 +3618,6 @@ validate_datasets(zlog_t *zlogp) zerror(zlogp, B_FALSE, "cannot set 'zoned' " "property for ZFS dataset '%s'\n", dstab.zone_dataset_name); - zonecfg_fini_handle(handle); zfs_close(zhp); libzfs_fini(hdl); return (-1); @@ -3655,9 +3625,8 @@ validate_datasets(zlog_t *zlogp) zfs_close(zhp); } - (void) zonecfg_enddsent(handle); + (void) zonecfg_enddsent(snap_hndl); - zonecfg_fini_handle(handle); libzfs_fini(hdl); return (0); @@ -3711,17 +3680,11 @@ validate_rootds_label(zlog_t *zlogp, char *rootpath, m_label_t *zone_sl) zfs_handle_t *zhp; libzfs_handle_t *hdl; m_label_t ds_sl; - char zonepath[MAXPATHLEN]; char ds_hexsl[MAXNAMELEN]; if (!is_system_labeled()) return (0); - if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) { - zerror(zlogp, B_TRUE, "unable to determine zone path"); - return (-1); - } - if (!is_zonepath_zfs(zonepath)) return (0); @@ -4392,62 +4355,52 @@ duplicate_reachable_path(zlog_t *zlogp, const char *rootpath) } /* - * Set memory cap and pool info for the zone's resource management - * configuration. + * Set pool info for the zone's resource management configuration. */ static int setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) { int res; uint64_t tmp; - struct zone_mcaptab mcap; char sched[MAXNAMELEN]; - zone_dochandle_t handle = NULL; char pool_err[128]; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (Z_BAD_HANDLE); - } - - if ((res = zonecfg_get_snapshot_handle(zone_name, handle)) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - zonecfg_fini_handle(handle); - return (res); - } - - /* - * If a memory cap is configured, set the cap in the kernel using - * zone_setattr() and make sure the rcapd SMF service is enabled. - */ - if (zonecfg_getmcapent(handle, &mcap) == Z_OK) { - uint64_t num; - char smf_err[128]; - - num = (uint64_t)strtoull(mcap.zone_physmem_cap, NULL, 10); - if (zone_setattr(zoneid, ZONE_ATTR_PHYS_MCAP, &num, 0) == -1) { - zerror(zlogp, B_TRUE, "could not set zone memory cap"); - zonecfg_fini_handle(handle); - return (Z_INVAL); - } - - if (zonecfg_enable_rcapd(smf_err, sizeof (smf_err)) != Z_OK) { - zerror(zlogp, B_FALSE, "enabling system/rcap service " - "failed: %s", smf_err); - zonecfg_fini_handle(handle); - return (Z_INVAL); - } - } - /* Get the scheduling class set in the zone configuration. */ - if (zonecfg_get_sched_class(handle, sched, sizeof (sched)) == Z_OK && + if (zonecfg_get_sched_class(snap_hndl, sched, sizeof (sched)) == Z_OK && strlen(sched) > 0) { if (zone_setattr(zoneid, ZONE_ATTR_SCHED_CLASS, sched, strlen(sched)) == -1) zerror(zlogp, B_TRUE, "WARNING: unable to set the " "default scheduling class"); - } else if (zonecfg_get_aliased_rctl(handle, ALIAS_SHARES, &tmp) + if (strcmp(sched, "FX") == 0) { + /* + * When FX is specified then by default all processes + * will start at the lowest priority level (0) and + * stay there. We support an optional attr which + * indicates that all the processes should be "high + * priority". We set this on the zone so that starting + * init will set the priority high. + */ + struct zone_attrtab a; + + bzero(&a, sizeof (a)); + (void) strlcpy(a.zone_attr_name, "fixed-hi-prio", + sizeof (a.zone_attr_name)); + + if (zonecfg_lookup_attr(snap_hndl, &a) == Z_OK && + strcmp(a.zone_attr_value, "true") == 0) { + boolean_t hi = B_TRUE; + + if (zone_setattr(zoneid, + ZONE_ATTR_SCHED_FIXEDHI, (void *)hi, + sizeof (hi)) == -1) + zerror(zlogp, B_TRUE, "WARNING: unable " + "to set high priority"); + } + } + + } else if (zonecfg_get_aliased_rctl(snap_hndl, ALIAS_SHARES, &tmp) == Z_OK) { /* * If the zone has the zone.cpu-shares rctl set then we want to @@ -4458,7 +4411,7 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) */ char class_name[PC_CLNMSZ]; - if (zonecfg_get_dflt_sched_class(handle, class_name, + if (zonecfg_get_dflt_sched_class(snap_hndl, class_name, sizeof (class_name)) != Z_OK) { zerror(zlogp, B_FALSE, "WARNING: unable to determine " "the zone's scheduling class"); @@ -4491,7 +4444,7 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) * right thing in all cases (reuse or create) based on the current * zonecfg. */ - if ((res = zonecfg_bind_tmp_pool(handle, zoneid, pool_err, + if ((res = zonecfg_bind_tmp_pool(snap_hndl, zoneid, pool_err, sizeof (pool_err))) != Z_OK) { if (res == Z_POOL || res == Z_POOL_CREATE || res == Z_POOL_BIND) zerror(zlogp, B_FALSE, "%s: %s\ndedicated-cpu setting " @@ -4500,14 +4453,13 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) else zerror(zlogp, B_FALSE, "could not bind zone to " "temporary pool: %s", zonecfg_strerror(res)); - zonecfg_fini_handle(handle); return (Z_POOL_BIND); } /* * Check if we need to warn about poold not being enabled. */ - if (zonecfg_warn_poold(handle)) { + if (zonecfg_warn_poold(snap_hndl)) { zerror(zlogp, B_FALSE, "WARNING: A range of dedicated-cpus has " "been specified\nbut the dynamic pool service is not " "enabled.\nThe system will not dynamically adjust the\n" @@ -4517,7 +4469,7 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) } /* The following is a warning, not an error. */ - if ((res = zonecfg_bind_pool(handle, zoneid, pool_err, + if ((res = zonecfg_bind_pool(snap_hndl, zoneid, pool_err, sizeof (pool_err))) != Z_OK) { if (res == Z_POOL_BIND) zerror(zlogp, B_FALSE, "WARNING: unable to bind to " @@ -4531,10 +4483,9 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid) } /* Update saved pool name in case it has changed */ - (void) zonecfg_get_poolname(handle, zone_name, pool_name, + (void) zonecfg_get_poolname(snap_hndl, zone_name, pool_name, sizeof (pool_name)); - zonecfg_fini_handle(handle); return (Z_OK); } @@ -4725,36 +4676,31 @@ setup_zone_fs_allowed(zone_dochandle_t handle, zlog_t *zlogp, zoneid_t zoneid) } static int -setup_zone_attrs(zlog_t *zlogp, char *zone_namep, zoneid_t zoneid) +setup_zone_attrs(zlog_t *zlogp, zoneid_t zoneid) { - zone_dochandle_t handle; int res = Z_OK; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, "getting zone configuration handle"); - return (Z_BAD_HANDLE); - } - if ((res = zonecfg_get_snapshot_handle(zone_namep, handle)) != Z_OK) { - zerror(zlogp, B_FALSE, "invalid configuration"); - goto out; - } - - if ((res = setup_zone_hostid(handle, zlogp, zoneid)) != Z_OK) + if ((res = setup_zone_hostid(snap_hndl, zlogp, zoneid)) != Z_OK) goto out; - if ((res = setup_zone_fs_allowed(handle, zlogp, zoneid)) != Z_OK) + if ((res = setup_zone_fs_allowed(snap_hndl, zlogp, zoneid)) != Z_OK) goto out; - if ((res = setup_zone_secflags(handle, zlogp, zoneid)) != Z_OK) + if ((res = setup_zone_secflags(snap_hndl, zlogp, zoneid)) != Z_OK) goto out; out: - zonecfg_fini_handle(handle); return (res); } +/* + * The zone_did is a persistent debug ID. Each zone should have a unique ID + * in the kernel. This is used for things like DTrace which want to monitor + * zones across reboots. They can't use the zoneid since that changes on + * each boot. + */ zoneid_t -vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd) +vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zone_did) { zoneid_t rval = -1; priv_set_t *privs; @@ -4770,7 +4716,7 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd) tsol_zcent_t *zcent = NULL; int match = 0; int doi = 0; - int flags; + int flags = -1; zone_iptype_t iptype; if (zone_get_rootpath(zone_name, rootpath, sizeof (rootpath)) != Z_OK) { @@ -4792,6 +4738,8 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd) flags = ZCF_NET_EXCL; break; } + if (flags == -1) + abort(); if ((privs = priv_allocset()) == NULL) { zerror(zlogp, B_TRUE, "%s failed", "priv_allocset"); @@ -4895,7 +4843,7 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd) xerr = 0; if ((zoneid = zone_create(kzone, rootpath, privs, rctlbuf, rctlbufsz, zfsbuf, zfsbufsz, &xerr, match, doi, zlabel, - flags)) == -1) { + flags, zone_did)) == -1) { if (xerr == ZE_AREMOUNTS) { if (zonecfg_find_mounts(rootpath, NULL, NULL) < 1) { zerror(zlogp, B_FALSE, @@ -4941,7 +4889,7 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd) struct brand_attr attr; char modname[MAXPATHLEN]; - if (setup_zone_attrs(zlogp, zone_name, zoneid) != Z_OK) + if (setup_zone_attrs(zlogp, zoneid) != Z_OK) goto error; if ((bh = brand_open(brand_name)) == NULL) { @@ -4999,6 +4947,8 @@ error: } if (rctlbuf != NULL) free(rctlbuf); + if (zfsbuf != NULL) + free(zfsbuf); priv_freeset(privs); if (fp != NULL) zonecfg_close_scratch(fp); @@ -5087,7 +5037,7 @@ write_index_file(zoneid_t zoneid) int vplat_bringup(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zoneid) { - char zonepath[MAXPATHLEN]; + char zpath[MAXPATHLEN]; if (mount_cmd == Z_MNT_BOOT && validate_datasets(zlogp) != 0) { lofs_discard_mnttab(); @@ -5098,15 +5048,11 @@ vplat_bringup(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zoneid) * Before we try to mount filesystems we need to create the * attribute backing store for /dev */ - if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) { - lofs_discard_mnttab(); - return (-1); - } - resolve_lofs(zlogp, zonepath, sizeof (zonepath)); + (void) strlcpy(zpath, zonepath, sizeof (zpath)); + resolve_lofs(zlogp, zpath, sizeof (zpath)); /* Make /dev directory owned by root, grouped sys */ - if (make_one_dir(zlogp, zonepath, "/dev", DEFAULT_DIR_MODE, - 0, 3) != 0) { + if (make_one_dir(zlogp, zpath, "/dev", DEFAULT_DIR_MODE, 0, 3) != 0) { lofs_discard_mnttab(); return (-1); } @@ -5141,6 +5087,8 @@ vplat_bringup(zlog_t *zlogp, zone_mnt_t mount_cmd, zoneid_t zoneid) return (-1); } break; + default: + abort(); } } @@ -5215,14 +5163,88 @@ unmounted: } } +/* + * Delete all transient VNICs belonging to this zone. A transient VNIC + * is one that is created and destroyed along with the lifetime of the + * zone. Non-transient VNICs, ones that are assigned from the GZ to a + * NGZ, are reassigned to the GZ in zone_shutdown() via the + * zone-specific data (zsd) callbacks. + */ +static int +delete_transient_vnics(zlog_t *zlogp, zoneid_t zoneid) +{ + dladm_status_t status; + int num_links = 0; + datalink_id_t *links, link; + uint32_t link_flags; + datalink_class_t link_class; + char link_name[MAXLINKNAMELEN]; + + if (zone_list_datalink(zoneid, &num_links, NULL) != 0) { + zerror(zlogp, B_TRUE, "unable to determine " + "number of network interfaces"); + return (-1); + } + + if (num_links == 0) + return (0); + + links = malloc(num_links * sizeof (datalink_id_t)); + + if (links == NULL) { + zerror(zlogp, B_TRUE, "failed to delete " + "network interfaces because of alloc fail"); + return (-1); + } + + if (zone_list_datalink(zoneid, &num_links, links) != 0) { + zerror(zlogp, B_TRUE, "failed to delete " + "network interfaces because of failure " + "to list them"); + return (-1); + } + + for (int i = 0; i < num_links; i++) { + char dlerr[DLADM_STRSIZE]; + link = links[i]; + + status = dladm_datalink_id2info(dld_handle, link, &link_flags, + &link_class, NULL, link_name, sizeof (link_name)); + + if (status != DLADM_STATUS_OK) { + zerror(zlogp, B_FALSE, "failed to " + "delete network interface (%u)" + "due to failure to get link info: %s", + link, + dladm_status2str(status, dlerr)); + return (-1); + } + + if (link_flags & DLADM_OPT_TRANSIENT) { + assert(link_class & DATALINK_CLASS_VNIC); + status = dladm_vnic_delete(dld_handle, link, + DLADM_OPT_ACTIVE); + + if (status != DLADM_STATUS_OK) { + zerror(zlogp, B_TRUE, "failed to delete link " + "with id %d: %s", link, + dladm_status2str(status, dlerr)); + return (-1); + } + } + } + + return (0); +} + int -vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) +vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting, + boolean_t debug) { char *kzone; zoneid_t zoneid; int res; char pool_err[128]; - char zpath[MAXPATHLEN]; char cmdbuf[MAXPATHLEN]; brand_handle_t bh = NULL; dladm_status_t status; @@ -5255,16 +5277,12 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) goto error; } - if (remove_datalink_pool(zlogp, zoneid) != 0) { + if (remove_datalink_pool(zlogp, zoneid) != 0) zerror(zlogp, B_FALSE, "unable clear datalink pool property"); - goto error; - } - if (remove_datalink_protect(zlogp, zoneid) != 0) { + if (remove_datalink_protect(zlogp, zoneid) != 0) zerror(zlogp, B_FALSE, "unable clear datalink protect property"); - goto error; - } /* * The datalinks assigned to the zone will be removed from the NGZ as @@ -5278,12 +5296,6 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) goto error; } - /* Get the zonepath of this zone */ - if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) { - zerror(zlogp, B_FALSE, "unable to determine zone path"); - goto error; - } - /* Get a handle to the brand info for this zone */ if ((bh = brand_open(brand_name)) == NULL) { zerror(zlogp, B_FALSE, "unable to determine zone brand"); @@ -5294,7 +5306,7 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) * brand a chance to cleanup any custom configuration. */ (void) strcpy(cmdbuf, EXEC_PREFIX); - if (brand_get_halt(bh, zone_name, zpath, cmdbuf + EXEC_LEN, + if (brand_get_halt(bh, zone_name, zonepath, cmdbuf + EXEC_LEN, sizeof (cmdbuf) - EXEC_LEN) < 0) { brand_close(bh); zerror(zlogp, B_FALSE, "unable to determine branded zone's " @@ -5304,7 +5316,7 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) brand_close(bh); if ((strlen(cmdbuf) > EXEC_LEN) && - (do_subproc(zlogp, cmdbuf, NULL) != Z_OK)) { + (do_subproc(zlogp, cmdbuf, NULL, debug) != Z_OK)) { zerror(zlogp, B_FALSE, "%s failed", cmdbuf); goto error; } @@ -5336,17 +5348,18 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) } break; case ZS_EXCLUSIVE: - if (unconfigure_exclusive_network_interfaces(zlogp, - zoneid) != 0) { - zerror(zlogp, B_FALSE, "unable to unconfigure " - "network interfaces in zone"); + if (delete_transient_vnics(zlogp, zoneid) != 0) { + zerror(zlogp, B_FALSE, "unable to delete " + "transient vnics in zone"); goto error; } + status = dladm_zone_halt(dld_handle, zoneid); if (status != DLADM_STATUS_OK) { zerror(zlogp, B_FALSE, "unable to notify " "dlmgmtd of zone halt: %s", dladm_status2str(status, errmsg)); + goto error; } break; } @@ -5378,14 +5391,9 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting) if (rebooting) { struct zone_psettab pset_tab; - zone_dochandle_t handle; - if ((handle = zonecfg_init_handle()) != NULL && - zonecfg_get_handle(zone_name, handle) == Z_OK && - zonecfg_lookup_pset(handle, &pset_tab) == Z_OK) + if (zonecfg_lookup_pset(snap_hndl, &pset_tab) == Z_OK) destroy_tmp_pool = B_FALSE; - - zonecfg_fini_handle(handle); } if (destroy_tmp_pool) { diff --git a/usr/src/cmd/zoneadmd/zcons.c b/usr/src/cmd/zoneadmd/zcons.c index 5f6fc4973c..5f415b0210 100644 --- a/usr/src/cmd/zoneadmd/zcons.c +++ b/usr/src/cmd/zoneadmd/zcons.c @@ -22,7 +22,7 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. - * Copyright 2012 Joyent, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. * Copyright 2015 Nexenta Systems, Inc. All rights reserved. */ @@ -40,10 +40,10 @@ * * Global Zone | Non-Global Zone * .--------------. | - * .-----------. | zoneadmd -z | | .--------. .---------. - * | zlogin -C | | myzone | | | ttymon | | syslogd | - * `-----------' `--------------' | `--------' `---------' - * | | | | | | | + * .-----------. | zoneadmd -z |--. | .--------. .---------. + * | zlogin -C | | myzone | | | | ttymon | | syslogd | + * `-----------' `--------------' V | `--------' `---------' + * | | | | console.log | | | * User | | | | | V V * - - - - - - - - -|- - - -|- - - -|-|- - - - - - -|- - /dev/zconsole - - - * Kernel V V | | | @@ -81,6 +81,8 @@ * functions as a two-way proxy for console I/O, relaying user input * to the master side of the console, and relaying output from the * zone to the user. + * + * - Logging output to <zonepath>/logs/console.log. */ #include <sys/types.h> @@ -118,9 +120,10 @@ #define CONSOLE_SOCKPATH ZONES_TMPDIR "/%s.console_sock" +#define ZCONS_RETRY 10 + static int serverfd = -1; /* console server unix domain socket fd */ char boot_args[BOOTARGS_MAX]; -char bad_boot_arg[BOOTARGS_MAX]; /* * The eventstream is a simple one-directional flow of messages from the @@ -130,7 +133,10 @@ char bad_boot_arg[BOOTARGS_MAX]; */ static int eventstream[2]; - +/* flag used to cope with race creating master zcons devlink */ +static boolean_t master_zcons_failed = B_FALSE; +/* flag to track if we've seen a state change when there is no master zcons */ +static boolean_t state_changed = B_FALSE; int eventstream_init() @@ -322,7 +328,7 @@ destroy_console_devs(zlog_t *zlogp) * interfaces to instantiate a new zone console node. We do a lot of * sanity checking, and are careful to reuse a console if one exists. * - * Once the device is in the device tree, we kick devfsadm via di_init_devs() + * Once the device is in the device tree, we kick devfsadm via di_devlink_init() * to ensure that the appropriate symlinks (to the master and slave console * devices) are placed in /dev in the global zone. */ @@ -408,43 +414,63 @@ devlinks: * Open the master side of the console and issue the ZC_HOLDSLAVE ioctl, * which will cause the master to retain a reference to the slave. * This prevents ttymon from blowing through the slave's STREAMS anchor. + * + * In very rare cases the open returns ENOENT if devfs doesn't have + * everything setup yet due to heavy zone startup load. Wait for + * 1 sec. and retry a few times. Even if we can't setup the zone's + * console, we still go ahead and boot the zone. */ (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", zone_name, ZCONS_MASTER_NAME); - if ((masterfd = open(conspath, O_RDWR | O_NOCTTY)) == -1) { + for (i = 0; i < ZCONS_RETRY; i++) { + masterfd = open(conspath, O_RDWR | O_NOCTTY); + if (masterfd >= 0 || errno != ENOENT) + break; + (void) sleep(1); + } + if (masterfd == -1) { zerror(zlogp, B_TRUE, "ERROR: could not open master side of " "zone console for %s to acquire slave handle", zone_name); - goto error; + master_zcons_failed = B_TRUE; } + (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", zone_name, ZCONS_SLAVE_NAME); - if ((slavefd = open(conspath, O_RDWR | O_NOCTTY)) == -1) { + for (i = 0; i < ZCONS_RETRY; i++) { + slavefd = open(conspath, O_RDWR | O_NOCTTY); + if (slavefd >= 0 || errno != ENOENT) + break; + (void) sleep(1); + } + if (slavefd == -1) zerror(zlogp, B_TRUE, "ERROR: could not open slave side of zone" " console for %s to acquire slave handle", zone_name); - (void) close(masterfd); - goto error; - } + /* * This ioctl can occasionally return ENXIO if devfs doesn't have * everything plumbed up yet due to heavy zone startup load. Wait for * 1 sec. and retry a few times before we fail to boot the zone. */ - for (i = 0; i < 5; i++) { - if (ioctl(masterfd, ZC_HOLDSLAVE, (caddr_t)(intptr_t)slavefd) - == 0) { - rv = 0; - break; - } else if (errno != ENXIO) { - break; + if (masterfd != -1 && slavefd != -1) { + for (i = 0; i < ZCONS_RETRY; i++) { + if (ioctl(masterfd, ZC_HOLDSLAVE, + (caddr_t)(intptr_t)slavefd) == 0) { + rv = 0; + break; + } else if (errno != ENXIO) { + break; + } + (void) sleep(1); } - (void) sleep(1); + if (rv != 0) + zerror(zlogp, B_TRUE, "ERROR: error while acquiring " + "slave handle of zone console for %s", zone_name); } - if (rv != 0) - zerror(zlogp, B_TRUE, "ERROR: error while acquiring slave " - "handle of zone console for %s", zone_name); - (void) close(slavefd); - (void) close(masterfd); + if (slavefd != -1) + (void) close(slavefd); + if (masterfd != -1) + (void) close(masterfd); error: if (ddef_hdl) @@ -517,6 +543,7 @@ get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len, size_t buflen = sizeof (buf); char c = '\0'; int i = 0, r; + ucred_t *cred = NULL; /* "eat up the ident string" case, for simplicity */ if (pid == NULL) { @@ -550,18 +577,22 @@ get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len, break; } + if (getpeerucred(clifd, &cred) == 0) { + *pid = ucred_getpid((const ucred_t *)cred); + ucred_free(cred); + } else { + return (-1); + } + /* * Parse buffer for message of the form: - * IDENT <pid> <locale> <disconnect flag> + * IDENT <locale> <disconnect flag> */ bufp = buf; if (strncmp(bufp, "IDENT ", 6) != 0) return (-1); bufp += 6; errno = 0; - *pid = strtoll(bufp, &bufp, 10); - if (errno != 0) - return (-1); while (*bufp != '\0' && isspace(*bufp)) bufp++; @@ -667,14 +698,6 @@ event_message(int clifd, char *clilocale, zone_evt_t evt, int dflag) else str = "NOTICE: Zone boot failed"; break; - case Z_EVT_ZONE_BADARGS: - /*LINTED*/ - (void) snprintf(lmsg, sizeof (lmsg), - localize_msg(clilocale, - "WARNING: Ignoring invalid boot arguments: %s"), - bad_boot_arg); - lstr = lmsg; - break; default: return; } @@ -713,7 +736,7 @@ test_client(int clifd) * messages) can be output in the user's locale. */ static void -do_console_io(zlog_t *zlogp, int consfd, int servfd) +do_console_io(zlog_t *zlogp, int consfd, int servfd, int conslog) { struct pollfd pollfds[4]; char ibuf[BUFSIZ]; @@ -759,14 +782,21 @@ do_console_io(zlog_t *zlogp, int consfd, int servfd) (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { errno = 0; cc = read(consfd, ibuf, BUFSIZ); - if (cc <= 0 && (errno != EINTR) && - (errno != EAGAIN)) - break; - /* - * Lose I/O if no one is listening - */ - if (clifd != -1 && cc > 0) - (void) write(clifd, ibuf, cc); + if (cc <= 0) { + if (errno != EINTR && + errno != EAGAIN) { + break; + } + } else { + logstream_write(conslog, ibuf, cc); + + /* + * Lose I/O if no one is listening + */ + if (clifd != -1) { + (void) write(clifd, ibuf, cc); + } + } } else { pollerr = pollfds[0].revents; zerror(zlogp, B_FALSE, @@ -878,7 +908,6 @@ init_console(zlog_t *zlogp) if (init_console_dev(zlogp) == -1) { zerror(zlogp, B_FALSE, "console setup: device initialization failed"); - return (-1); } if ((serverfd = init_console_sock(zlogp)) == -1) { @@ -890,6 +919,17 @@ init_console(zlog_t *zlogp) } /* + * Maintain a simple flag that tracks if we have seen at least one state + * change. This is currently only used to handle the special case where we are + * running without a console device, which is what normally drives shutdown. + */ +void +zcons_statechanged() +{ + state_changed = B_TRUE; +} + +/* * serve_console() is the master loop for driving console I/O. It is also the * routine which is ultimately responsible for "pulling the plug" on zoneadmd * when it realizes that the daemon should shut down. @@ -907,6 +947,10 @@ serve_console(zlog_t *zlogp) int masterfd; zone_state_t zstate; char conspath[MAXPATHLEN]; + static boolean_t cons_warned = B_FALSE; + int conslog; + + conslog = logstream_open("console.log", "console", LS_LINE_BUFFERED); (void) snprintf(conspath, sizeof (conspath), "/dev/zcons/%s/%s", zone_name, ZCONS_MASTER_NAME); @@ -914,6 +958,46 @@ serve_console(zlog_t *zlogp) for (;;) { masterfd = open(conspath, O_RDWR|O_NONBLOCK|O_NOCTTY); if (masterfd == -1) { + if (master_zcons_failed) { + /* + * If we don't have a console and the zone is + * not shutting down, there may have been a + * race/failure with devfs while creating the + * console. In this case we want to leave the + * zone up, even without a console, so + * periodically recheck. + */ + int i; + + /* + * In the normal flow of this loop, we use + * do_console_io to give things a chance to get + * going first. However, in this case we can't + * use that, so we have to wait for at least + * one state change before checking the state. + */ + for (i = 0; i < 60; i++) { + if (state_changed) + break; + (void) sleep(1); + } + + if (i < 60 && zone_get_state(zone_name, + &zstate) == Z_OK && + (zstate == ZONE_STATE_READY || + zstate == ZONE_STATE_RUNNING)) { + if (!cons_warned) { + zerror(zlogp, B_FALSE, + "WARNING: missing zone " + "console for %s", + zone_name); + cons_warned = B_TRUE; + } + (void) sleep(ZCONS_RETRY); + continue; + } + } + zerror(zlogp, B_TRUE, "failed to open console master"); (void) mutex_lock(&lock); goto death; @@ -933,7 +1017,7 @@ serve_console(zlog_t *zlogp) goto death; } - do_console_io(zlogp, masterfd, serverfd); + do_console_io(zlogp, masterfd, serverfd, conslog); /* * We would prefer not to do this, but hostile zone processes @@ -974,4 +1058,6 @@ death: destroy_console_sock(serverfd); (void) destroy_console_devs(zlogp); + + logstream_close(conslog, B_FALSE); } diff --git a/usr/src/cmd/zoneadmd/zfd.c b/usr/src/cmd/zoneadmd/zfd.c new file mode 100644 index 0000000000..307d916154 --- /dev/null +++ b/usr/src/cmd/zoneadmd/zfd.c @@ -0,0 +1,1237 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * Copyright 2019 Joyent, Inc. + */ + +/* + * Zone file descriptor support is used as a mechanism for a process inside the + * zone to log messages to the GZ zoneadmd and also as a way to interact + * directly with the process (via zlogin -I). The zfd thread is modeled on + * the zcons thread so see the comment header in zcons.c for a general overview. + * Unlike with zcons, which has a single endpoint within the zone and a single + * endpoint used by zoneadmd, we setup multiple endpoints within the zone. + * + * The mode, which is controlled by the zone attribute "zlog-mode" is somewhat + * of a misnomer since its purpose has evolved. The attribute currently + * can have six values which are used to control: + * - how the zfd devices are used inside the zone + * - if the output on the device(s) is also teed into another stream within + * the zone + * - if we do logging in the GZ + * See the comment on get_mode_logmax() in this file, and the comment in + * uts/common/io/zfd.c for more details. + * + * Internally the zfd_mode_t struct holds the number of stdio devs (1 or 3), + * the number of additional devs corresponding to the zone attr value and the + * GZ logging flag. + * + * Note that although the mode indicates the number of devices needed, we always + * create all possible zfd devices for simplicity. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/termios.h> +#include <sys/zfd.h> +#include <sys/mkdev.h> + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <stropts.h> +#include <thread.h> +#include <ucred.h> +#include <unistd.h> +#include <zone.h> +#include <signal.h> +#include <wchar.h> + +#include <libdevinfo.h> +#include <libdevice.h> +#include <libzonecfg.h> + +#include <syslog.h> +#include <sys/modctl.h> + +#include "zoneadmd.h" + +static int shutting_down = 0; +static thread_t logger_tid; +static char log_name[MAXNAMELEN] = "stdio.log"; + +/* + * The eventstream is a simple one-directional flow of messages implemented + * with a pipe. It is used to wake up the poller when it needs to shutdown. + */ +static int eventstream[2] = {-1, -1}; + +#define ZLOG_MODE "zlog-mode" +#define ZLOG_NAME "zlog-name" +#define ZFDNEX_DEVTREEPATH "/pseudo/zfdnex@2" +#define ZFDNEX_FILEPATH "/devices/pseudo/zfdnex@2" +#define SERVER_SOCKPATH ZONES_TMPDIR "/%s.server_%s" +#define ZTTY_RETRY 5 + +#define NUM_ZFD_DEVS 5 + +typedef struct zfd_mode { + uint_t zmode_n_stddevs; + uint_t zmode_n_addl_devs; + boolean_t zmode_gzlogging; +} zfd_mode_t; +static zfd_mode_t mode; + +/* + * cb_data is only used by destroy_cb. + */ +struct cb_data { + zlog_t *zlogp; + int killed; +}; + +/* + * destroy_zfd_devs() and its helper destroy_cb() tears down any zfd instances + * associated with this zone. If things went very wrong, we might have an + * incorrect number of instances hanging around. This routine hunts down and + * tries to remove all of them. Of course, if the fd is open, the instance will + * not detach, which is a potential issue. + */ +static int +destroy_cb(di_node_t node, void *arg) +{ + struct cb_data *cb = (struct cb_data *)arg; + char *prop_data; + char *tmp; + char devpath[MAXPATHLEN]; + devctl_hdl_t hdl; + + if (di_prop_lookup_strings(DDI_DEV_T_ANY, node, "zfd_zname", + &prop_data) == -1) + return (DI_WALK_CONTINUE); + + assert(prop_data != NULL); + if (strcmp(prop_data, zone_name) != 0) { + /* this is a zfd for a different zone */ + return (DI_WALK_CONTINUE); + } + + tmp = di_devfs_path(node); + (void) snprintf(devpath, sizeof (devpath), "/devices/%s", tmp); + di_devfs_path_free(tmp); + + if ((hdl = devctl_device_acquire(devpath, 0)) == NULL) { + zerror(cb->zlogp, B_TRUE, "WARNING: zfd %s found, " + "but it could not be controlled.", devpath); + return (DI_WALK_CONTINUE); + } + if (devctl_device_remove(hdl) == 0) { + cb->killed++; + } else { + zerror(cb->zlogp, B_TRUE, "WARNING: zfd %s found, " + "but it could not be removed.", devpath); + } + devctl_release(hdl); + return (DI_WALK_CONTINUE); +} + +static int +destroy_zfd_devs(zlog_t *zlogp) +{ + di_node_t root; + struct cb_data cb; + + bzero(&cb, sizeof (cb)); + cb.zlogp = zlogp; + + if ((root = di_init(ZFDNEX_DEVTREEPATH, DINFOCPYALL)) == DI_NODE_NIL) { + zerror(zlogp, B_TRUE, "di_init failed"); + return (-1); + } + + (void) di_walk_node(root, DI_WALK_CLDFIRST, (void *)&cb, destroy_cb); + + di_fini(root); + return (0); +} + +static void +make_tty(zlog_t *zlogp, int id) +{ + int i; + int fd = -1; + char stdpath[MAXPATHLEN]; + + /* + * Open the master side of the dev and issue the ZFD_MAKETTY ioctl, + * which will cause the the various tty-related streams modules to be + * pushed when the slave opens the device. + * + * In very rare cases the open returns ENOENT if devfs doesn't have + * everything setup yet due to heavy zone startup load. Wait for + * 1 sec. and retry a few times. Even if we can't setup tty mode + * we still move on. + */ + (void) snprintf(stdpath, sizeof (stdpath), "/dev/zfd/%s/master/%d", + zone_name, id); + + for (i = 0; !shutting_down && i < ZTTY_RETRY; i++) { + fd = open(stdpath, O_RDWR | O_NOCTTY); + if (fd >= 0 || errno != ENOENT) + break; + (void) sleep(1); + } + if (fd == -1) { + zerror(zlogp, B_TRUE, "ERROR: could not open zfd %d for " + "zone %s to set tty mode", id, zone_name); + } else { + /* + * This ioctl can occasionally return ENXIO if devfs doesn't + * have everything plumbed up yet due to heavy zone startup + * load. Wait for 1 sec. and retry a few times before we give + * up. + */ + for (i = 0; !shutting_down && i < ZTTY_RETRY; i++) { + if (ioctl(fd, ZFD_MAKETTY) == 0) { + break; + } else if (errno != ENXIO) { + break; + } + (void) sleep(1); + } + } + + if (fd != -1) + (void) close(fd); +} + +/* + * init_zfd_devs() drives the device-tree configuration of the zone fd devices. + * The general strategy is to use the libdevice (devctl) interfaces to + * instantiate all of new zone fd nodes. We do a lot of sanity checking, and + * are careful to reuse a dev if one exists. + * + * Once the devices are in the device tree, we kick devfsadm via + * di_devlink_init() to ensure that the appropriate symlinks (to the master and + * slave fd devices) are placed in /dev in the global zone. + */ +static int +init_zfd_dev(zlog_t *zlogp, devctl_hdl_t bus_hdl, int id) +{ + int rv = -1; + devctl_ddef_t ddef_hdl = NULL; + devctl_hdl_t dev_hdl = NULL; + + if ((ddef_hdl = devctl_ddef_alloc("zfd", 0)) == NULL) { + zerror(zlogp, B_TRUE, "failed to allocate ddef handle"); + goto error; + } + + /* + * Set four properties on this node; the name of the zone, the dev name + * seen inside the zone, a flag which lets pseudo know that it is OK to + * automatically allocate an instance # for this device, and the last + * one tells the device framework not to auto-detach this node - we + * need the node to still be there when we ask devfsadmd to make links, + * and when we need to open it. + */ + if (devctl_ddef_string(ddef_hdl, "zfd_zname", zone_name) == -1) { + zerror(zlogp, B_TRUE, "failed to create zfd_zname property"); + goto error; + } + if (devctl_ddef_int(ddef_hdl, "zfd_id", id) == -1) { + zerror(zlogp, B_TRUE, "failed to create zfd_id property"); + goto error; + } + if (devctl_ddef_int(ddef_hdl, "auto-assign-instance", 1) == -1) { + zerror(zlogp, B_TRUE, "failed to create auto-assign-instance " + "property"); + goto error; + } + if (devctl_ddef_int(ddef_hdl, "ddi-no-autodetach", 1) == -1) { + zerror(zlogp, B_TRUE, "failed to create ddi-no-auto-detach " + "property"); + goto error; + } + if (devctl_bus_dev_create(bus_hdl, ddef_hdl, 0, &dev_hdl) == -1) { + zerror(zlogp, B_TRUE, "failed to create zfd node"); + goto error; + } + rv = 0; + +error: + if (ddef_hdl) + devctl_ddef_free(ddef_hdl); + if (dev_hdl) + devctl_release(dev_hdl); + return (rv); +} + +static int +init_zfd_devs(zlog_t *zlogp, zfd_mode_t *mode) +{ + devctl_hdl_t bus_hdl = NULL; + di_devlink_handle_t dl = NULL; + int rv = -1; + int i; + + /* + * Time to make the devices. + */ + if ((bus_hdl = devctl_bus_acquire(ZFDNEX_FILEPATH, 0)) == NULL) { + zerror(zlogp, B_TRUE, "devctl_bus_acquire failed"); + goto error; + } + + for (i = 0; i < NUM_ZFD_DEVS; i++) { + if (init_zfd_dev(zlogp, bus_hdl, i) != 0) + goto error; + } + + if ((dl = di_devlink_init("zfd", DI_MAKE_LINK)) == NULL) { + zerror(zlogp, B_TRUE, "failed to create devlinks"); + goto error; + } + + (void) di_devlink_fini(&dl); + rv = 0; + + if (mode->zmode_n_stddevs == 1) { + /* We want the primary stream to look like a tty. */ + make_tty(zlogp, 0); + } + +error: + if (bus_hdl) + devctl_release(bus_hdl); + return (rv); +} + +static int +init_server_sock(int *servfd, char *nm) +{ + int resfd = -1; + struct sockaddr_un servaddr; + + bzero(&servaddr, sizeof (servaddr)); + servaddr.sun_family = AF_UNIX; + (void) snprintf(servaddr.sun_path, sizeof (servaddr.sun_path), + SERVER_SOCKPATH, zone_name, nm); + + if ((resfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + zerror(&logplat, B_TRUE, + "server setup: could not create socket"); + goto err; + } + (void) unlink(servaddr.sun_path); + + if (bind(resfd, (struct sockaddr *)&servaddr, sizeof (servaddr)) + == -1) { + zerror(&logplat, B_TRUE, + "server setup: could not bind to socket"); + goto err; + } + + if (listen(resfd, 4) == -1) { + zerror(&logplat, B_TRUE, + "server setup: could not listen on socket"); + goto err; + } + + *servfd = resfd; + return (0); + +err: + (void) unlink(servaddr.sun_path); + if (resfd != -1) + (void) close(resfd); + return (-1); +} + +static void +destroy_server_sock(int servfd, char *nm) +{ + char path[MAXPATHLEN]; + + (void) snprintf(path, sizeof (path), SERVER_SOCKPATH, zone_name, nm); + (void) unlink(path); + (void) shutdown(servfd, SHUT_RDWR); + (void) close(servfd); +} + +/* + * Read the "ident" string from the client's descriptor; this routine also + * tolerates being called with pid=NULL, for times when you want to "eat" + * the ident string from a client without saving it. + */ +static int +get_client_ident(int clifd, pid_t *pid, char *locale, size_t locale_len, + uint_t *flagsp) +{ + char buf[BUFSIZ], *bufp; + size_t buflen = sizeof (buf); + char c = '\0'; + int i = 0, r; + ucred_t *cred = NULL; + + /* "eat up the ident string" case, for simplicity */ + if (pid == NULL) { + assert(locale == NULL && locale_len == 0); + while (read(clifd, &c, 1) == 1) { + if (c == '\n') + return (0); + } + } + + bzero(buf, sizeof (buf)); + while ((buflen > 1) && (r = read(clifd, &c, 1)) == 1) { + buflen--; + if (c == '\n') + break; + + buf[i] = c; + i++; + } + if (r == -1) + return (-1); + + /* + * We've filled the buffer, but still haven't seen \n. Keep eating + * until we find it; we don't expect this to happen, but this is + * defensive. + */ + if (c != '\n') { + while ((r = read(clifd, &c, sizeof (c))) > 0) + if (c == '\n') + break; + } + + /* + * Parse buffer for message of the form: + * IDENT <locale> <flags> + */ + bufp = buf; + if (strncmp(bufp, "IDENT ", 6) != 0) + return (-1); + bufp += 6; + + if (getpeerucred(clifd, &cred) == 0) { + *pid = ucred_getpid((const ucred_t *)cred); + ucred_free(cred); + } else { + return (-1); + } + + while (*bufp != '\0' && isspace(*bufp)) + bufp++; + buflen = strlen(bufp) - 1; + bufp[buflen - 1] = '\0'; + (void) strlcpy(locale, bufp, locale_len); + + *flagsp = atoi(&bufp[buflen]); + + return (0); +} + +static int +accept_client(int servfd, pid_t *pid, char *locale, size_t locale_len, + uint_t *flagsp) +{ + int connfd; + struct sockaddr_un cliaddr; + socklen_t clilen; + int flags; + + clilen = sizeof (cliaddr); + connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen); + if (connfd == -1) + return (-1); + if (pid != NULL) { + if (get_client_ident(connfd, pid, locale, locale_len, flagsp) + == -1) { + (void) shutdown(connfd, SHUT_RDWR); + (void) close(connfd); + return (-1); + } + (void) write(connfd, "OK\n", 3); + } + + flags = fcntl(connfd, F_GETFL, 0); + if (flags != -1) + (void) fcntl(connfd, F_SETFL, flags | O_NONBLOCK | FD_CLOEXEC); + + return (connfd); +} + +static void +reject_client(int servfd, pid_t clientpid) +{ + int connfd; + struct sockaddr_un cliaddr; + socklen_t clilen; + char nak[MAXPATHLEN]; + + clilen = sizeof (cliaddr); + connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen); + + /* + * After getting its ident string, tell client to get lost. + */ + if (get_client_ident(connfd, NULL, NULL, 0, NULL) == 0) { + (void) snprintf(nak, sizeof (nak), "%lu\n", + clientpid); + (void) write(connfd, nak, strlen(nak)); + } + (void) shutdown(connfd, SHUT_RDWR); + (void) close(connfd); +} + +static int +accept_socket(int servfd, pid_t verpid) +{ + int connfd; + struct sockaddr_un cliaddr; + socklen_t clilen = sizeof (cliaddr); + ucred_t *cred = NULL; + pid_t rpid = -1; + int flags; + + connfd = accept(servfd, (struct sockaddr *)&cliaddr, &clilen); + if (connfd == -1) + return (-1); + + /* Confirm connecting process is who we expect */ + if (getpeerucred(connfd, &cred) == 0) { + rpid = ucred_getpid((const ucred_t *)cred); + ucred_free(cred); + } + if (rpid == -1 || rpid != verpid) { + (void) shutdown(connfd, SHUT_RDWR); + (void) close(connfd); + return (-1); + } + + flags = fcntl(connfd, F_GETFL, 0); + if (flags != -1) + (void) fcntl(connfd, F_SETFL, flags | O_NONBLOCK | FD_CLOEXEC); + + return (connfd); +} + +static void +ctlcmd_process(int sockfd, int stdoutfd, unsigned int *flags) +{ + char buf[BUFSIZ]; + int i; + for (i = 0; i < BUFSIZ-1; i++) { + char c; + if (read(sockfd, &c, 1) != 1 || + c == '\n' || c == '\0') { + break; + } + buf[i] = c; + } + if (i == 0) { + goto fail; + } + buf[i] = '\0'; + + if (strncmp(buf, "TIOCSWINSZ ", 11) == 0) { + char *next = buf + 11; + struct winsize ws; + errno = 0; + ws.ws_row = strtol(next, &next, 10); + if (errno == EINVAL) { + goto fail; + } + ws.ws_col = strtol(next + 1, &next, 10); + if (errno == EINVAL) { + goto fail; + } + if (ioctl(stdoutfd, TIOCSWINSZ, &ws) == 0) { + (void) write(sockfd, "OK\n", 3); + return; + } + } + if (strncmp(buf, "SETFLAGS ", 9) == 0) { + char *next = buf + 9; + unsigned int result; + errno = 0; + result = strtoul(next, &next, 10); + if (errno == EINVAL) { + goto fail; + } + *flags = result; + (void) write(sockfd, "OK\n", 3); + return; + } +fail: + (void) write(sockfd, "FAIL\n", 5); +} + +/* + * Check to see if the client at the other end of the socket is still alive; we + * know it is not if it throws EPIPE at us when we try to write an otherwise + * harmless 0-length message to it. + */ +static int +test_client(int clifd) +{ + if ((write(clifd, "", 0) == -1) && errno == EPIPE) + return (-1); + return (0); +} + +/* + * We want to sleep for a little while but need to be responsive if the zone is + * halting. We poll/sleep on the event stream so we can notice if we're halting. + * Return true if halting, otherwise false. + */ +static boolean_t +halt_sleep(int slptime) +{ + struct pollfd evfd[1]; + + evfd[0].fd = eventstream[1]; + evfd[0].events = POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI; + + if (poll(evfd, 1, slptime) > 0) { + /* zone halting */ + return (B_TRUE); + } + return (B_FALSE); +} + +/* + * This routine drives the logging and interactive I/O loop. It polls for + * input from the zone side of the fd (output to stdout/stderr), and from the + * client (input to the zone's stdin). Additionally, it polls on the server + * fd, and disconnects any clients that might try to hook up with the zone + * while the fd's are in use. + * + * Data from the zone's stdout and stderr is formatted in json and written to + * the log file whether an interactive client is connected or not. + * + * When the client first calls us up, it is expected to send a line giving its + * "identity"; this consists of the string 'IDENT <pid> <locale>'. This is so + * that we can report that the fd's are busy, along with some diagnostics + * about who has them busy; the locale is ignore here but kept for compatability + * with the zlogin code when running on the zone's console. + * + * We need to handle the case where there is no server within the zone (or + * the server gets stuck) and data that we're writing to the zone server's + * stdin fills the pipe. Because of the way the zfd device works writes can + * flow into the stream and simply be dropped, if there is no server, or writes + * could return -1 with EAGAIN if the server is stuck. Since we ignore errors + * on the write to stdin, we won't get blocked in that case but we'd like to + * avoid dropping initial input if the server within the zone hasn't started + * yet. To handle this we wait to read initial input until we detect that there + * is a server inside the zone. We have to poll for this so that we can + * re-run the ioctl to notice when a server shows up. This poll/wait is handled + * by halt_sleep() so that we can be responsive if the zone wants to halt. + * We only do this check to avoid dropping initial input so it is possible for + * the server within the zone to go away later. At that point zfd will just + * drop any new input flowing into the stream. + */ +static void +do_zfd_io(int gzctlfd, int gzservfd, int gzerrfd, int stdinfd, int stdoutfd, + int stderrfd, int logout, int logerr) +{ + struct pollfd pollfds[8]; + char ibuf[BUFSIZ + 1]; + int cc, ret; + int ctlfd = -1; + int clifd = -1; + int clierrfd = -1; + int pollerr = 0; + char clilocale[MAXPATHLEN]; + pid_t clipid = 0; + uint_t flags = 0; + boolean_t stdin_ready = B_FALSE; + int slptime = 250; /* initial poll sleep time in ms */ + + /* client control socket, watch for read events */ + pollfds[0].fd = ctlfd; + pollfds[0].events = POLLIN | POLLRDNORM | POLLRDBAND | + POLLPRI | POLLERR | POLLHUP | POLLNVAL; + + /* client socket, watch for read events */ + pollfds[1].fd = clifd; + pollfds[1].events = pollfds[0].events; + + /* stdout, watch for read events */ + pollfds[2].fd = stdoutfd; + pollfds[2].events = pollfds[0].events; + + /* stderr, watch for read events */ + pollfds[3].fd = stderrfd; + pollfds[3].events = pollfds[0].events; + + /* the server control socket; watch for new connections */ + pollfds[4].fd = gzctlfd; + pollfds[4].events = POLLIN | POLLRDNORM; + + /* the server stdin/out socket; watch for new connections */ + pollfds[5].fd = gzservfd; + pollfds[5].events = POLLIN | POLLRDNORM; + + /* the server stderr socket; watch for new connections */ + pollfds[6].fd = gzerrfd; + pollfds[6].events = POLLIN | POLLRDNORM; + + /* the eventstream; any input means the zone is halting */ + pollfds[7].fd = eventstream[1]; + pollfds[7].events = pollfds[0].events; + + while (!shutting_down) { + pollfds[0].revents = pollfds[1].revents = 0; + pollfds[2].revents = pollfds[3].revents = 0; + pollfds[4].revents = pollfds[5].revents = 0; + pollfds[6].revents = pollfds[7].revents = 0; + + ret = poll(pollfds, 8, -1); + if (ret == -1 && errno != EINTR) { + zerror(&logplat, B_TRUE, "poll failed"); + /* we are hosed, close connection */ + break; + } + + /* control events from client */ + if (pollfds[0].revents & + (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { + /* process control message */ + ctlcmd_process(ctlfd, stdoutfd, &flags); + } else if (pollfds[0].revents) { + /* bail if any error occurs */ + pollerr = pollfds[0].revents; + zerror(&logplat, B_FALSE, "closing connection " + "with control channel, pollerr %d\n", pollerr); + break; + } + + /* event from client side */ + if (pollfds[1].revents) { + if (stdin_ready) { + if (pollfds[1].revents & (POLLIN | + POLLRDNORM | POLLRDBAND | POLLPRI)) { + errno = 0; + cc = read(clifd, ibuf, BUFSIZ); + if (cc > 0) { + /* + * See comment for this + * function on what happens if + * there is no reader in the + * zone. EOF is handled below. + */ + (void) write(stdinfd, ibuf, cc); + } + } else if (pollfds[1].revents & (POLLERR | + POLLNVAL)) { + pollerr = pollfds[1].revents; + zerror(&logplat, B_FALSE, + "closing connection " + "with client, pollerr %d\n", + pollerr); + break; + } + + if (pollfds[1].revents & POLLHUP) { + if (flags & ZLOGIN_ZFD_EOF) { + /* + * Let the client know. We've + * already serviced any pending + * regular input. Let the + * stream clear since the EOF + * ioctl jumps to the head. + */ + (void) ioctl(stdinfd, I_FLUSH); + if (halt_sleep(250)) + break; + (void) ioctl(stdinfd, ZFD_EOF); + } + break; + } + } else { + if (ioctl(stdinfd, ZFD_HAS_SLAVE) == 0) { + stdin_ready = B_TRUE; + } else { + /* + * There is nothing in the zone to read + * our input. Presumably the user + * providing input expects something to + * show up, but that is no guarantee. + * Since we haven't serviced the pending + * input poll yet, we don't want to + * immediately loop around but we also + * need to be responsive if the zone is + * halting. + */ + if (halt_sleep(slptime)) + break; + + if (slptime < 5000) + slptime += 250; + } + } + } + + /* event from the zone's stdout */ + if (pollfds[2].revents) { + if (pollfds[2].revents & + (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { + errno = 0; + cc = read(stdoutfd, ibuf, BUFSIZ); + /* zfd is a stream, so ignore 0 length read */ + if (cc < 0 && (errno != EINTR) && + (errno != EAGAIN)) + break; + if (cc > 0) { + logstream_write(logout, ibuf, cc); + + /* + * Lose output if no one is listening, + * otherwise pass it on. + */ + if (clifd != -1) + (void) write(clifd, ibuf, cc); + } + } else { + pollerr = pollfds[2].revents; + zerror(&logplat, B_FALSE, + "closing connection with stdout zfd, " + "pollerr %d\n", pollerr); + break; + } + } + + /* event from the zone's stderr */ + if (pollfds[3].revents) { + if (pollfds[3].revents & + (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI)) { + errno = 0; + cc = read(stderrfd, ibuf, BUFSIZ); + /* zfd is a stream, so ignore 0 length read */ + if (cc < 0 && (errno != EINTR) && + (errno != EAGAIN)) + break; + if (cc > 0) { + logstream_write(logerr, ibuf, cc); + + /* + * Lose output if no one is listening, + * otherwise pass it on. + */ + if (clierrfd != -1) + (void) write(clierrfd, ibuf, + cc); + } + } else { + pollerr = pollfds[3].revents; + zerror(&logplat, B_FALSE, + "closing connection with stderr zfd, " + "pollerr %d\n", pollerr); + break; + } + } + + /* connect event from server control socket */ + if (pollfds[4].revents) { + if (ctlfd != -1) { + /* + * Test the client to see if it is really + * still alive. If it has died but we + * haven't yet detected that, we might + * deny a legitimate connect attempt. If it + * is dead, we break out; once we tear down + * the old connection, the new connection + * will happen. + */ + if (test_client(ctlfd) == -1) { + break; + } + /* we're already handling a client */ + reject_client(gzctlfd, clipid); + } else { + ctlfd = accept_client(gzctlfd, &clipid, + clilocale, sizeof (clilocale), &flags); + if (ctlfd != -1) { + pollfds[0].fd = ctlfd; + } else { + break; + } + } + } + + /* connect event from server stdin/out socket */ + if (pollfds[5].revents) { + if (ctlfd == -1) { + /* + * This shouldn't happen since the client is + * expected to connect on the control socket + * first. If we see this, tear everything down + * and start over. + */ + zerror(&logplat, B_FALSE, "GZ zfd stdin/stdout " + "connection attempt with no GZ control\n"); + break; + } + assert(clifd == -1); + if ((clifd = accept_socket(gzservfd, clipid)) != -1) { + /* No need to watch for other new connections */ + pollfds[5].fd = -1; + /* Client input is of interest, though */ + pollfds[1].fd = clifd; + } else { + break; + } + } + + /* connection event from server stderr socket */ + if (pollfds[6].revents) { + if (ctlfd == -1) { + /* + * Same conditions apply to stderr as stdin/out. + */ + zerror(&logplat, B_FALSE, "GZ zfd stderr " + "connection attempt with no GZ control\n"); + break; + } + assert(clierrfd == -1); + if ((clierrfd = accept_socket(gzerrfd, clipid)) != -1) { + /* No need to watch for other new connections */ + pollfds[6].fd = -1; + } else { + break; + } + } + + /* + * Watch for events on the eventstream. This is how we get + * notified of the zone halting, etc. It provides us a + * "wakeup" from poll when important things happen, which + * is good. + */ + if (pollfds[7].revents) { + break; + } + } + + if (clifd != -1) { + (void) shutdown(clifd, SHUT_RDWR); + (void) close(clifd); + } + + if (clierrfd != -1) { + (void) shutdown(clierrfd, SHUT_RDWR); + (void) close(clierrfd); + } +} + +static int +open_fd(int id, int rw) +{ + int fd; + int flag = O_NONBLOCK | O_NOCTTY | O_CLOEXEC; + int retried = 0; + char stdpath[MAXPATHLEN]; + + (void) snprintf(stdpath, sizeof (stdpath), "/dev/zfd/%s/master/%d", + zone_name, id); + flag |= rw; + + while (!shutting_down) { + if ((fd = open(stdpath, flag)) != -1) { + /* + * Setting RPROTDIS on the stream means that the + * control portion of messages received (which we don't + * care about) will be discarded by the stream head. If + * we allowed such messages, we wouldn't be able to use + * read(2), as it fails (EBADMSG) when a message with a + * control element is received. + */ + if (ioctl(fd, I_SRDOPT, RNORM|RPROTDIS) == -1) { + zerror(&logplat, B_TRUE, + "failed to set options on zfd"); + return (-1); + } + return (fd); + } + + if (retried++ > 60) + break; + + (void) sleep(1); + } + + zerror(&logplat, B_TRUE, "failed to open zfd"); + return (-1); +} + +/* + * Body of the worker thread to log the zfd's stdout and stderr to a log file + * and to perform interactive IO to the stdin, stdout and stderr zfd's. + * + * The stdin, stdout and stderr are from the perspective of the process inside + * the zone, so the zoneadmd view is opposite (i.e. we write to the stdin fd + * and read from the stdout/stderr fds). + */ +static void +srvr(void *modearg) +{ + zfd_mode_t *mode = (zfd_mode_t *)modearg; + int gzctlfd = -1; + int gzoutfd = -1; + int stdinfd = -1; + int stdoutfd = -1; + int gzerrfd = -1; + int stderrfd = -1; + int flags; + int len; + char ibuf[BUFSIZ + 1]; + int logout = -1; + int logerr = -1; + + if (!shutting_down && mode->zmode_gzlogging) { + logout = logstream_open(log_name, "stdout", 0); + logerr = logstream_open(log_name, "stderr", 0); + } + + if (!shutting_down) { + if (pipe(eventstream) != 0) { + zerror(&logplat, B_TRUE, "failed to open logger " + "control pipe"); + return; + } + } + + while (!shutting_down) { + if (init_server_sock(&gzctlfd, "ctl") == -1) { + zerror(&logplat, B_FALSE, + "server setup: control socket init failed"); + goto death; + } + if (init_server_sock(&gzoutfd, "out") == -1) { + zerror(&logplat, B_FALSE, + "server setup: stdout socket init failed"); + goto death; + } + if (init_server_sock(&gzerrfd, "err") == -1) { + zerror(&logplat, B_FALSE, + "server setup: stderr socket init failed"); + goto death; + } + + if (mode->zmode_n_stddevs == 1) { + if ((stdinfd = open_fd(0, O_RDWR)) == -1) { + goto death; + } + stdoutfd = stdinfd; + } else { + if ((stdinfd = open_fd(0, O_WRONLY)) == -1 || + (stdoutfd = open_fd(1, O_RDONLY)) == -1 || + (stderrfd = open_fd(2, O_RDONLY)) == -1) { + goto death; + } + } + + do_zfd_io(gzctlfd, gzoutfd, gzerrfd, stdinfd, stdoutfd, + stderrfd, logout, logerr); +death: + destroy_server_sock(gzctlfd, "ctl"); + destroy_server_sock(gzoutfd, "out"); + destroy_server_sock(gzerrfd, "err"); + + /* when shutting down, leave open until drained */ + if (!shutting_down) { + (void) close(stdinfd); + if (mode->zmode_n_stddevs == 3) { + (void) close(stdoutfd); + (void) close(stderrfd); + } + } + } + + /* + * Attempt to drain remaining log output from the zone prior to closing + * the file descriptors. This helps ensure that complete logs are + * captured during shutdown. + */ + flags = fcntl(stdoutfd, F_GETFL, 0); + if (fcntl(stdoutfd, F_SETFL, flags | O_NONBLOCK) != -1) { + while ((len = read(stdoutfd, ibuf, BUFSIZ)) > 0) { + logstream_write(logout, ibuf, len); + } + } + (void) close(stdoutfd); + + if (mode->zmode_n_stddevs > 1) { + (void) close(stdinfd); + flags = fcntl(stderrfd, F_GETFL, 0); + if (fcntl(stderrfd, F_SETFL, flags | O_NONBLOCK) != -1) { + while ((len = read(stderrfd, ibuf, BUFSIZ)) > 0) { + logstream_write(logerr, ibuf, len); + } + } + (void) close(stderrfd); + } + + + (void) close(eventstream[0]); + eventstream[0] = -1; + (void) close(eventstream[1]); + eventstream[1] = -1; + logstream_close(logout, B_FALSE); + logstream_close(logerr, B_FALSE); +} + +/* + * The meaning of the original legacy values for the zlog-mode evolved over + * time, to the point where the old names no longer made sense. The current + * values are simply positional letters used to indicate various capabilities. + * The following table shows the meaning of the mode values, along with the + * legacy name which we continue to support for compatability. Any future + * capability can add a letter to the left and '-' is implied for existing + * strings. + * + * zlog-mode gz log - tty - ngz log + * --------- ------ --- ------- + * gt- (int) y y n + * g-- (log) y n n + * gtn (nlint) y y y + * g-n (nolog) y n y + * -t- n y n + * --- n n n + * + * This function also obtains any custom name for stdio.log while it is reading + * the zone configuration. + */ +static void +get_mode_logmax(zfd_mode_t *mode) +{ + zone_dochandle_t handle; + struct zone_attrtab attr; + + bzero(mode, sizeof (zfd_mode_t)); + + if ((handle = zonecfg_init_handle()) == NULL) + return; + + if (zonecfg_get_handle(zone_name, handle) != Z_OK) + goto done; + + if (zonecfg_setattrent(handle) != Z_OK) + goto done; + while (zonecfg_getattrent(handle, &attr) == Z_OK) { + if (strcmp(ZLOG_MODE, attr.zone_attr_name) == 0) { + if (strcmp("g--", attr.zone_attr_value) == 0 || + strncmp("log", attr.zone_attr_value, 3) == 0) { + mode->zmode_gzlogging = B_TRUE; + mode->zmode_n_stddevs = 3; + mode->zmode_n_addl_devs = 0; + } else if (strcmp("g-n", attr.zone_attr_value) == 0 || + strncmp("nolog", attr.zone_attr_value, 5) == 0) { + mode->zmode_gzlogging = B_TRUE; + mode->zmode_n_stddevs = 3; + mode->zmode_n_addl_devs = 2; + } else if (strcmp("gt-", attr.zone_attr_value) == 0 || + strncmp("int", attr.zone_attr_value, 3) == 0) { + mode->zmode_gzlogging = B_TRUE; + mode->zmode_n_stddevs = 1; + mode->zmode_n_addl_devs = 0; + } else if (strcmp("gtn", attr.zone_attr_value) == 0 || + strncmp("nlint", attr.zone_attr_value, 5) == 0) { + mode->zmode_gzlogging = B_TRUE; + mode->zmode_n_stddevs = 1; + mode->zmode_n_addl_devs = 1; + } else if (strcmp("-t-", attr.zone_attr_value) == 0) { + mode->zmode_gzlogging = B_FALSE; + mode->zmode_n_stddevs = 1; + mode->zmode_n_addl_devs = 0; + } else if (strcmp("---", attr.zone_attr_value) == 0) { + mode->zmode_gzlogging = B_FALSE; + mode->zmode_n_stddevs = 3; + mode->zmode_n_addl_devs = 0; + } + continue; + } + + if (strcmp(ZLOG_NAME, attr.zone_attr_name) == 0) { + (void) strlcpy(log_name, attr.zone_attr_value, + sizeof (log_name)); + continue; + } + } + (void) zonecfg_endattrent(handle); + +done: + zonecfg_fini_handle(handle); +} + +void +create_log_thread(zlog_t *zlogp) +{ + int res; + + shutting_down = 0; + + get_mode_logmax(&mode); + if (mode.zmode_n_stddevs == 0) + return; + + if (init_zfd_devs(zlogp, &mode) == -1) { + zerror(zlogp, B_FALSE, + "zfd setup: device initialization failed"); + return; + } + + res = thr_create(NULL, 0, (void * (*)(void *))srvr, (void *)&mode, 0, + &logger_tid); + if (res != 0) { + zerror(zlogp, B_FALSE, "error %d creating logger thread", res); + logger_tid = 0; + } +} + +void +destroy_log_thread(zlog_t *zlogp) +{ + if (logger_tid != 0) { + int stop = 1; + + shutting_down = 1; + /* break out of poll to shutdown */ + if (eventstream[0] != -1) + (void) write(eventstream[0], &stop, sizeof (stop)); + (void) thr_join(logger_tid, NULL, NULL); + logger_tid = 0; + } + + (void) destroy_zfd_devs(zlogp); +} diff --git a/usr/src/cmd/zoneadmd/zoneadmd.c b/usr/src/cmd/zoneadmd/zoneadmd.c index b901d6055f..7c8d2a2bbc 100644 --- a/usr/src/cmd/zoneadmd/zoneadmd.c +++ b/usr/src/cmd/zoneadmd/zoneadmd.c @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. * Copyright (c) 2016 by Delphix. All rights reserved. */ @@ -69,6 +70,7 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/sysmacros.h> +#include <sys/time.h> #include <bsm/adt.h> #include <bsm/adt_event.h> @@ -102,6 +104,8 @@ #include <libdladm.h> #include <sys/dls_mgmt.h> #include <libscf.h> +#include <uuid/uuid.h> +#include <libppt.h> #include <libzonecfg.h> #include <zonestat_impl.h> @@ -109,6 +113,8 @@ static char *progname; char *zone_name; /* zone which we are managing */ +zone_dochandle_t snap_hndl; /* handle for snapshot created when ready */ +char zonepath[MAXNAMELEN]; char pool_name[MAXNAMELEN]; char default_brand[MAXNAMELEN]; char brand_name[MAXNAMELEN]; @@ -117,13 +123,15 @@ boolean_t zone_iscluster; boolean_t zone_islabeled; boolean_t shutdown_in_progress; static zoneid_t zone_id; +static zoneid_t zone_did = 0; dladm_handle_t dld_handle = NULL; -static char pre_statechg_hook[2 * MAXPATHLEN]; -static char post_statechg_hook[2 * MAXPATHLEN]; +char pre_statechg_hook[2 * MAXPATHLEN]; +char post_statechg_hook[2 * MAXPATHLEN]; char query_hook[2 * MAXPATHLEN]; -zlog_t logsys; +zlog_t logsys; /* log to syslog */ +zlog_t logplat; /* log to platform.log */ mutex_t lock = DEFAULTMUTEX; /* to serialize stuff */ mutex_t msglock = DEFAULTMUTEX; /* for calling setlocale() */ @@ -136,12 +144,17 @@ static int zone_door = -1; boolean_t in_death_throes = B_FALSE; /* daemon is dying */ boolean_t bringup_failure_recovery = B_FALSE; /* ignore certain failures */ +static int platloghdl = -1; /* Handle for <zonepath>/logs/platform.log */ + #if !defined(TEXT_DOMAIN) /* should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ #endif #define DEFAULT_LOCALE "C" +#define RSRC_NET "net" +#define RSRC_DEV "device" + static const char * z_cmd_name(zone_cmd_t zcmd) { @@ -215,17 +228,14 @@ zerror(zlog_t *zlogp, boolean_t use_strerror, const char *fmt, ...) { va_list alist; char buf[MAXPATHLEN * 2]; /* enough space for err msg with a path */ - char *bp; + char *bp, *bp_nozone; int saved_errno = errno; - if (zlogp == NULL) - return; if (zlogp == &logsys) - (void) snprintf(buf, sizeof (buf), "[zone '%s'] ", - zone_name); + (void) snprintf(buf, sizeof (buf), "[zone '%s'] ", zone_name); else buf[0] = '\0'; - bp = &(buf[strlen(buf)]); + bp = bp_nozone = &(buf[strlen(buf)]); /* * In theory, the locale pointer should be set to either "C" or a @@ -242,15 +252,38 @@ zerror(zlog_t *zlogp, boolean_t use_strerror, const char *fmt, ...) if (use_strerror) (void) snprintf(bp, sizeof (buf) - (bp - buf), ": %s", strerror(saved_errno)); + + (void) strlcat(buf, "\n", sizeof (buf)); + + /* + * If we don't have the platform log, we are in a child process, and + * should log to stderr (which is a pipe) instead of the file. + */ + if (logging_poisoned) { + (void) fprintf(stderr, "%s", buf); + + if (zlogp != &logsys && zlogp->logfile == stderr) + return; + } else { + logstream_write(platloghdl, bp_nozone, strlen(bp_nozone)); + + if (zlogp == &logplat) + return; + } + if (zlogp == &logsys) { + bp = strrchr(buf, '\n'); + if (bp != NULL && bp[1] == '\0') { + *bp = '\0'; + } (void) syslog(LOG_ERR, "%s", buf); } else if (zlogp->logfile != NULL) { - (void) fprintf(zlogp->logfile, "%s\n", buf); + (void) fprintf(zlogp->logfile, "%s", buf); } else { size_t buflen; size_t copylen; - buflen = snprintf(zlogp->log, zlogp->loglen, "%s\n", buf); + buflen = snprintf(zlogp->log, zlogp->loglen, "%s", buf); copylen = MIN(buflen, zlogp->loglen); zlogp->log += copylen; zlogp->loglen -= copylen; @@ -258,34 +291,43 @@ zerror(zlog_t *zlogp, boolean_t use_strerror, const char *fmt, ...) } /* - * Emit a warning for any boot arguments which are unrecognized. Since - * Solaris boot arguments are getopt(3c) compatible (see kernel(1m)), we + * Append src to dest, modifying dest in the process. Prefix src with + * a space character if dest is a non-empty string. + */ +static void +strnappend(char *dest, size_t n, const char *src) +{ + (void) snprintf(dest, n, "%s%s%s", dest, + dest[0] == '\0' ? "" : " ", src); +} + +/* + * Since illumos boot arguments are getopt(3c) compatible (see kernel(1m)), we * put the arguments into an argv style array, use getopt to process them, - * and put the resultant argument string back into outargs. + * and put the resultant argument string back into outargs. Non-native brands + * may support alternate forms of boot arguments so we must handle that as well. * * During the filtering, we pull out any arguments which are truly "boot" * arguments, leaving only those which are to be passed intact to the * progenitor process. The one we support at the moment is -i, which * indicates to the kernel which program should be launched as 'init'. * - * A return of Z_INVAL indicates specifically that the arguments are - * not valid; this is a non-fatal error. Except for Z_OK, all other return - * values are treated as fatal. + * Except for Z_OK, all other return values are treated as fatal. */ static int filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs, - char *init_file, char *badarg) + char *init_file) { int argc = 0, argc_save; int i; - int err; + int err = Z_OK; char *arg, *lasts, **argv = NULL, **argv_save; char zonecfg_args[BOOTARGS_MAX]; char scratchargs[BOOTARGS_MAX], *sargs; + char scratchopt[3]; char c; bzero(outargs, BOOTARGS_MAX); - bzero(badarg, BOOTARGS_MAX); /* * If the user didn't specify transient boot arguments, check @@ -293,25 +335,10 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs, * and use them if applicable. */ if (inargs == NULL || inargs[0] == '\0') { - zone_dochandle_t handle; - if ((handle = zonecfg_init_handle()) == NULL) { - zerror(zlogp, B_TRUE, - "getting zone configuration handle"); - return (Z_BAD_HANDLE); - } - err = zonecfg_get_snapshot_handle(zone_name, handle); - if (err != Z_OK) { - zerror(zlogp, B_FALSE, - "invalid configuration snapshot"); - zonecfg_fini_handle(handle); - return (Z_BAD_HANDLE); - } - bzero(zonecfg_args, sizeof (zonecfg_args)); - (void) zonecfg_get_bootargs(handle, zonecfg_args, + (void) zonecfg_get_bootargs(snap_hndl, zonecfg_args, sizeof (zonecfg_args)); inargs = zonecfg_args; - zonecfg_fini_handle(handle); } if (strlen(inargs) >= BOOTARGS_MAX) { @@ -348,14 +375,22 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs, } /* - * We preserve compatibility with the Solaris system boot behavior, + * We preserve compatibility with the illumos system boot behavior, * which allows: * * # reboot kernel/unix -s -m verbose * - * In this example, kernel/unix tells the booter what file to - * boot. We don't want reboot in a zone to be gratuitously different, - * so we silently ignore the boot file, if necessary. + * In this example, kernel/unix tells the booter what file to boot. The + * original intent of this was that we didn't want reboot in a zone to + * be gratuitously different, so we would silently ignore the boot + * file, if necessary. However, this usage is archaic and has never + * been common, since it is impossible to boot a zone onto a different + * kernel. Ignoring the first argument breaks for non-native brands + * which pass boot arguments in a different style. e.g. + * systemd.log_level=debug + * Thus, for backward compatibility we only ignore the first argument + * if it appears to be in the illumos form and attempting to specify a + * kernel. */ if (argv[0] == NULL) goto done; @@ -363,7 +398,7 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs, assert(argv[0][0] != ' '); assert(argv[0][0] != '\t'); - if (argv[0][0] != '-' && argv[0][0] != '\0') { + if (strncmp(argv[0], "kernel/", 7) == 0) { argv = &argv[1]; argc--; } @@ -386,41 +421,35 @@ filter_bootargs(zlog_t *zlogp, const char *inargs, char *outargs, case 'm': case 's': /* These pass through unmolested */ - (void) snprintf(outargs, BOOTARGS_MAX, - "%s -%c %s ", outargs, c, optarg ? optarg : ""); + (void) snprintf(scratchopt, sizeof (scratchopt), + "-%c", c); + strnappend(outargs, BOOTARGS_MAX, scratchopt); + if (optarg != NULL) + strnappend(outargs, BOOTARGS_MAX, optarg); break; case '?': /* - * We warn about unknown arguments but pass them - * along anyway-- if someone wants to develop their - * own init replacement, they can pass it whatever - * args they want. + * If a brand has its own init, we need to pass along + * whatever the user provides. We use the entire + * unknown string here so that we correctly handle + * unknown long options (e.g. --debug). */ - err = Z_INVAL; - (void) snprintf(outargs, BOOTARGS_MAX, - "%s -%c", outargs, optopt); - (void) snprintf(badarg, BOOTARGS_MAX, - "%s -%c", badarg, optopt); + strnappend(outargs, BOOTARGS_MAX, argv[optind - 1]); break; } } /* - * For Solaris Zones we warn about and discard non-option arguments. - * Hence 'boot foo bar baz gub' --> 'boot'. However, to be similar - * to the kernel, we concat up all the other remaining boot args. - * and warn on them as a group. + * We need to pass along everything else since we don't know what + * the brand's init is expecting. For example, an argument list like: + * --confdir /foo --debug + * will cause the getopt parsing to stop at '/foo' but we need to pass + * that on, along with the '--debug'. This does mean that we require + * any of our known options (-ifms) to preceed the brand-specific ones. */ - if (optind < argc) { - err = Z_INVAL; - while (optind < argc) { - (void) snprintf(badarg, BOOTARGS_MAX, "%s%s%s", - badarg, strlen(badarg) > 0 ? " " : "", - argv[optind]); - optind++; - } - zerror(zlogp, B_FALSE, "WARNING: Unused or invalid boot " - "arguments `%s'.", badarg); + while (optind < argc) { + strnappend(outargs, BOOTARGS_MAX, argv[optind]); + optind++; } done: @@ -459,7 +488,7 @@ mkzonedir(zlog_t *zlogp) * Run the brand's pre-state change callback, if it exists. */ static int -brand_prestatechg(zlog_t *zlogp, int state, int cmd) +brand_prestatechg(zlog_t *zlogp, int state, int cmd, boolean_t debug) { char cmdbuf[2 * MAXPATHLEN]; const char *altroot; @@ -472,7 +501,7 @@ brand_prestatechg(zlog_t *zlogp, int state, int cmd) state, cmd, altroot) > sizeof (cmdbuf)) return (-1); - if (do_subproc(zlogp, cmdbuf, NULL) != 0) + if (do_subproc(zlogp, cmdbuf, NULL, debug) != 0) return (-1); return (0); @@ -482,7 +511,7 @@ brand_prestatechg(zlog_t *zlogp, int state, int cmd) * Run the brand's post-state change callback, if it exists. */ static int -brand_poststatechg(zlog_t *zlogp, int state, int cmd) +brand_poststatechg(zlog_t *zlogp, int state, int cmd, boolean_t debug) { char cmdbuf[2 * MAXPATHLEN]; const char *altroot; @@ -495,7 +524,7 @@ brand_poststatechg(zlog_t *zlogp, int state, int cmd) state, cmd, altroot) > sizeof (cmdbuf)) return (-1); - if (do_subproc(zlogp, cmdbuf, NULL) != 0) + if (do_subproc(zlogp, cmdbuf, NULL, debug) != 0) return (-1); return (0); @@ -532,37 +561,51 @@ notify_zonestatd(zoneid_t zoneid) * Bring a zone up to the pre-boot "ready" stage. The mount_cmd argument is * 'true' if this is being invoked as part of the processing for the "mount" * subcommand. + * + * If a scratch zone mount (ALT_MOUNT) is being performed then do not + * call the state change hooks. */ static int -zone_ready(zlog_t *zlogp, zone_mnt_t mount_cmd, int zstate) +zone_ready(zlog_t *zlogp, zone_mnt_t mount_cmd, int zstate, boolean_t debug) { int err; + boolean_t snapped = B_FALSE; - if (brand_prestatechg(zlogp, zstate, Z_READY) != 0) - return (-1); - + if ((snap_hndl = zonecfg_init_handle()) == NULL) { + zerror(zlogp, B_TRUE, "getting zone configuration handle"); + goto bad; + } if ((err = zonecfg_create_snapshot(zone_name)) != Z_OK) { zerror(zlogp, B_FALSE, "unable to create snapshot: %s", zonecfg_strerror(err)); goto bad; } + snapped = B_TRUE; - if ((zone_id = vplat_create(zlogp, mount_cmd)) == -1) { - if ((err = zonecfg_destroy_snapshot(zone_name)) != Z_OK) - zerror(zlogp, B_FALSE, "destroying snapshot: %s", - zonecfg_strerror(err)); + if (zonecfg_get_snapshot_handle(zone_name, snap_hndl) != Z_OK) { + zerror(zlogp, B_FALSE, "invalid configuration snapshot"); goto bad; } + + if (zone_did == 0) + zone_did = zone_get_did(zone_name); + + if (!ALT_MOUNT(mount_cmd) && + brand_prestatechg(zlogp, zstate, Z_READY, debug) != 0) + goto bad; + + if ((zone_id = vplat_create(zlogp, mount_cmd, zone_did)) == -1) + goto bad; + if (vplat_bringup(zlogp, mount_cmd, zone_id) != 0) { bringup_failure_recovery = B_TRUE; - (void) vplat_teardown(NULL, (mount_cmd != Z_MNT_BOOT), B_FALSE); - if ((err = zonecfg_destroy_snapshot(zone_name)) != Z_OK) - zerror(zlogp, B_FALSE, "destroying snapshot: %s", - zonecfg_strerror(err)); + (void) vplat_teardown(NULL, (mount_cmd != Z_MNT_BOOT), B_FALSE, + debug); goto bad; } - if (brand_poststatechg(zlogp, zstate, Z_READY) != 0) + if (!ALT_MOUNT(mount_cmd) && + brand_poststatechg(zlogp, zstate, Z_READY, debug) != 0) goto bad; return (0); @@ -572,7 +615,16 @@ bad: * If something goes wrong, we up the zones's state to the target * state, READY, and then invoke the hook as if we're halting. */ - (void) brand_poststatechg(zlogp, ZONE_STATE_READY, Z_HALT); + if (!ALT_MOUNT(mount_cmd)) + (void) brand_poststatechg(zlogp, ZONE_STATE_READY, Z_HALT, + debug); + + if (snapped) + if ((err = zonecfg_destroy_snapshot(zone_name)) != Z_OK) + zerror(zlogp, B_FALSE, "destroying snapshot: %s", + zonecfg_strerror(err)); + zonecfg_fini_handle(snap_hndl); + snap_hndl = NULL; return (-1); } @@ -624,15 +676,8 @@ mount_early_fs(void *data, const char *spec, const char *dir, /* determine the zone rootpath */ if (mount_cmd) { - char zonepath[MAXPATHLEN]; char luroot[MAXPATHLEN]; - if (zone_get_zonepath(zone_name, - zonepath, sizeof (zonepath)) != Z_OK) { - zerror(zlogp, B_FALSE, "unable to determine zone path"); - return (-1); - } - (void) snprintf(luroot, sizeof (luroot), "%s/lu", zonepath); resolve_lofs(zlogp, luroot, sizeof (luroot)); (void) strlcpy(rootpath, luroot, sizeof (rootpath)); @@ -687,6 +732,8 @@ mount_early_fs(void *data, const char *spec, const char *dir, char opt_buf[MAX_MNTOPT_STR]; int optlen = 0; int mflag = MS_DATA; + int i; + int ret; (void) ct_tmpl_clear(tmpl_fd); /* @@ -714,9 +761,26 @@ mount_early_fs(void *data, const char *spec, const char *dir, optlen = MAX_MNTOPT_STR; mflag = MS_OPTIONSTR; } - if (mount(spec, dir, mflag, fstype, NULL, 0, opt, optlen) != 0) - _exit(errno); - _exit(0); + + /* + * There is an obscure race condition which can cause mount + * to return EBUSY. This happens for example on the mount + * of the zone's /etc/svc/volatile file system if there is + * a GZ process running svcs -Z, which will touch the + * mountpoint, just as we're trying to do the mount. To cope + * with this, we retry up to 3 times to let this transient + * process get out of the way. + */ + for (i = 0; i < 3; i++) { + ret = 0; + if (mount(spec, dir, mflag, fstype, NULL, 0, opt, + optlen) != 0) + ret = errno; + if (ret != EBUSY) + break; + (void) sleep(1); + } + _exit(ret); } /* parent */ @@ -740,18 +804,275 @@ mount_early_fs(void *data, const char *spec, const char *dir, } /* + * Replace characters other than [A-Za-z0-9_] with '_' so that the string is a + * valid environment variable name. + */ +static void +sanitize_env_var_name(char *var) +{ + for (char *p = var; *p != '\0'; p++) { + if (!isalnum(*p)) { + *p = '_'; + } + } +} + +/* + * env variable name format + * _ZONECFG_{resource name}_{identifying attr. name}_{property name} + * Any dashes (-) in the property names are replaced with underscore (_). + */ +static void +set_zonecfg_env(char *rsrc, char *attr, char *name, char *val) +{ + /* Enough for maximal name, rsrc + attr, & slop for ZONECFG & _'s */ + char nm[2 * MAXNAMELEN + 32]; + + if (attr == NULL) + (void) snprintf(nm, sizeof (nm), "_ZONECFG_%s_%s", rsrc, + name); + else + (void) snprintf(nm, sizeof (nm), "_ZONECFG_%s_%s_%s", rsrc, + attr, name); + + sanitize_env_var_name(nm); + + (void) setenv(nm, val, 1); +} + +/* + * Resolve a device:match value to a path. This is only different for PPT + * devices, where we expect the match property to be a /devices/... path, and + * configured for PPT already. + */ +int +resolve_device_match(zlog_t *zlogp, struct zone_devtab *dtab, + char *path, size_t len) +{ + struct zone_res_attrtab *rap; + + for (rap = dtab->zone_dev_attrp; rap != NULL; + rap = rap->zone_res_attr_next) { + if (strcmp(rap->zone_res_attr_name, "model") == 0 && + strcmp(rap->zone_res_attr_value, "passthru") == 0) + break; + } + + if (rap == NULL) { + if (strlcpy(path, dtab->zone_dev_match, len) >= len) + return (Z_INVAL); + return (Z_OK); + } + + if (strncmp(dtab->zone_dev_match, "/devices", + strlen("/devices")) != 0) { + zerror(zlogp, B_FALSE, "invalid passthru match value '%s'", + dtab->zone_dev_match); + return (Z_INVAL); + } + + if (ppt_devpath_to_dev(dtab->zone_dev_match, path, len) != 0) { + zerror(zlogp, B_TRUE, "failed to resolve passthru device %s", + dtab->zone_dev_match); + return (Z_INVAL); + } + + return (Z_OK); +} + +/* + * Export various zonecfg properties into environment for the boot and state + * change hooks. + * + * If debug is true, _ZONEADMD_brand_debug is set to 1, else it is set to an + * empty string. Brand hooks consider any non-empty string as an indication + * that debug output is requested. + * + * We could export more of the config in the future, as necessary. A better + * solution would be to make it so brand-specific behavior is handled by + * brand-specific callbacks written in C. Then the normal libzonecfg interfaces + * can be used for accessing any parts of the configuration that are needed. + * + * All of the environment variables set by this function are specific to + * SmartOS. + */ +static int +setup_subproc_env(zlog_t *zlogp, boolean_t debug) +{ + int res; + struct zone_nwiftab ntab; + struct zone_devtab dtab; + struct zone_attrtab atab; + char net_resources[MAXNAMELEN * 2]; + char dev_resources[MAXNAMELEN * 2]; + char didstr[16]; + char uuidstr[UUID_PRINTABLE_STRING_LENGTH]; + uuid_t uuid; + + /* snap_hndl is null when called through the set_brand_env code path */ + if (snap_hndl == NULL) + return (Z_OK); + + if ((res = zonecfg_get_uuid(zone_name, uuid)) != Z_OK) + return (res); + + uuid_unparse(uuid, uuidstr); + (void) setenv("_ZONECFG_uuid", uuidstr, 1); + + (void) snprintf(didstr, sizeof (didstr), "%d", zone_did); + (void) setenv("_ZONECFG_did", didstr, 1); + + /* + * "net" resources are exported because zoneadmd does not handle + * automatic configuration of vnics and so that the bhyve boot hook + * can generate the argument list for the brand's init program. At such + * a time as vnic creation is handled in zoneadmd and brand callbacks + * can be executed as part of the zoneadmd process this should be + * removed. + */ + net_resources[0] = '\0'; + if ((res = zonecfg_setnwifent(snap_hndl)) != Z_OK) + goto done; + + while (zonecfg_getnwifent(snap_hndl, &ntab) == Z_OK) { + struct zone_res_attrtab *rap; + char *phys; + + phys = ntab.zone_nwif_physical; + + (void) strlcat(net_resources, phys, sizeof (net_resources)); + (void) strlcat(net_resources, " ", sizeof (net_resources)); + + set_zonecfg_env(RSRC_NET, phys, "physical", phys); + + set_zonecfg_env(RSRC_NET, phys, "address", + ntab.zone_nwif_address); + set_zonecfg_env(RSRC_NET, phys, "allowed-address", + ntab.zone_nwif_allowed_address); + set_zonecfg_env(RSRC_NET, phys, "defrouter", + ntab.zone_nwif_defrouter); + set_zonecfg_env(RSRC_NET, phys, "global-nic", + ntab.zone_nwif_gnic); + set_zonecfg_env(RSRC_NET, phys, "mac-addr", ntab.zone_nwif_mac); + set_zonecfg_env(RSRC_NET, phys, "vlan-id", + ntab.zone_nwif_vlan_id); + + for (rap = ntab.zone_nwif_attrp; rap != NULL; + rap = rap->zone_res_attr_next) + set_zonecfg_env(RSRC_NET, phys, rap->zone_res_attr_name, + rap->zone_res_attr_value); + nwifent_free_attrs(&ntab); + } + + (void) setenv("_ZONECFG_net_resources", net_resources, 1); + + (void) zonecfg_endnwifent(snap_hndl); + + /* + * "device" resources are exported because the bhyve boot brand callback + * needs them to generate the argument list for the brand's init + * program. At such a time as brand callbacks can be executed as part + * of the zoneadmd process, this should be removed. + * + * The bhyve brand only supports disk-like and ppt devices and does not + * support regular expressions. + */ + if ((res = zonecfg_setdevent(snap_hndl)) != Z_OK) + goto done; + + dev_resources[0] = '\0'; + while (zonecfg_getdevent(snap_hndl, &dtab) == Z_OK) { + char *match = dtab.zone_dev_match; + struct zone_res_attrtab *rap; + char path[MAXPATHLEN]; + + res = resolve_device_match(zlogp, &dtab, path, sizeof (path)); + if (res != Z_OK) + goto done; + + /* + * Even if not modified, the match path will be mangled in the + * environment variable name, so we always store the value here. + */ + set_zonecfg_env(RSRC_DEV, match, "path", path); + + for (rap = dtab.zone_dev_attrp; rap != NULL; + rap = rap->zone_res_attr_next) { + set_zonecfg_env(RSRC_DEV, match, + rap->zone_res_attr_name, rap->zone_res_attr_value); + } + + /* + * _ZONECFG_device_resources will contain a space separated list + * of devices that have _ZONECFG_device_<device>* environment + * variables. So that each element of the list matches up with + * <device>, each list item needs to be sanitized in the same + * way that environment variable names are sanitized. + */ + sanitize_env_var_name(match); + (void) strlcat(dev_resources, match, sizeof (dev_resources)); + (void) strlcat(dev_resources, " ", sizeof (dev_resources)); + } + (void) zonecfg_enddevent(snap_hndl); + + (void) setenv("_ZONECFG_device_resources", dev_resources, 1); + + /* + * "attr" resources are exported because the bhyve brand's boot hook + * needs access to the "ram", "cpu", "bootrom", etc. to form the + * argument list for the brand's init program. Once the bhyve brand is + * configured via proper resources and properties, this should be + * removed. + */ + if ((res = zonecfg_setattrent(snap_hndl)) != Z_OK) + goto done; + + while (zonecfg_getattrent(snap_hndl, &atab) == Z_OK) { + set_zonecfg_env("attr", NULL, atab.zone_attr_name, + atab.zone_attr_value); + } + + (void) zonecfg_endattrent(snap_hndl); + + if (debug) + (void) setenv("_ZONEADMD_brand_debug", "1", 1); + else + (void) setenv("_ZONEADMD_brand_debug", "", 1); + + res = Z_OK; + +done: + return (res); +} + +void +nwifent_free_attrs(struct zone_nwiftab *np) +{ + struct zone_res_attrtab *rap; + + for (rap = np->zone_nwif_attrp; rap != NULL; ) { + struct zone_res_attrtab *tp = rap; + + rap = rap->zone_res_attr_next; + free(tp); + } +} + +/* * If retstr is not NULL, the output of the subproc is returned in the str, * otherwise it is output using zerror(). Any memory allocated for retstr * should be freed by the caller. */ int -do_subproc(zlog_t *zlogp, char *cmdbuf, char **retstr) +do_subproc(zlog_t *zlogp, char *cmdbuf, char **retstr, boolean_t debug) { char buf[1024]; /* arbitrary large amount */ char *inbuf; FILE *file; int status; int rd_cnt; + int fds[2]; + pid_t child; if (retstr != NULL) { if ((*retstr = malloc(1024)) == NULL) { @@ -764,31 +1085,104 @@ do_subproc(zlog_t *zlogp, char *cmdbuf, char **retstr) inbuf = buf; } - file = popen(cmdbuf, "r"); - if (file == NULL) { - zerror(zlogp, B_TRUE, "could not launch: %s", cmdbuf); + if (pipe(fds) != 0) { + zerror(zlogp, B_TRUE, "failed to create pipe for subprocess"); + return (-1); + } + + if ((child = fork()) == 0) { + int in; + + /* + * SIGINT is currently ignored. It probably shouldn't be so + * hard to kill errant children, so we revert to SIG_DFL. + * SIGHUP and SIGUSR1 are used to perform log rotation. We + * leave those as-is because we don't want a 'pkill -HUP + * zoneadmd' to kill this child process before exec(). On + * exec(), SIGHUP and SIGUSR1 will become SIG_DFL. + */ + (void) sigset(SIGINT, SIG_DFL); + + /* + * Set up a pipe for the child to log to. + */ + if (dup2(fds[1], STDERR_FILENO) == -1) { + (void) snprintf(buf, sizeof (buf), + "subprocess failed to dup2(STDERR_FILENO): %s\n", + strerror(errno)); + (void) write(fds[1], buf, strlen(buf)); + _exit(127); + } + if (dup2(fds[1], STDOUT_FILENO) == -1) { + perror("subprocess failed to dup2(STDOUT_FILENO)"); + _exit(127); + } + /* + * Some naughty children may try to read from stdin. Be sure + * that the first file that a child opens doesn't get stdin's + * file descriptor. + */ + if ((in = open("/dev/null", O_RDONLY)) == -1 || + dup2(in, STDIN_FILENO) == -1) { + zerror(zlogp, B_TRUE, + "subprocess failed to set up STDIN_FILENO"); + _exit(127); + } + closefrom(STDERR_FILENO + 1); + + if (setup_subproc_env(zlogp, debug) != Z_OK) { + zerror(zlogp, B_FALSE, "failed to setup environment"); + _exit(127); + } + + (void) execl("/bin/sh", "sh", "-c", cmdbuf, NULL); + + zerror(zlogp, B_TRUE, "subprocess execl failed"); + _exit(127); + } else if (child == -1) { + zerror(zlogp, B_TRUE, "failed to create subprocess for '%s'", + cmdbuf); + (void) close(fds[0]); + (void) close(fds[1]); return (-1); } + (void) close(fds[1]); + + file = fdopen(fds[0], "r"); while (fgets(inbuf, 1024, file) != NULL) { if (retstr == NULL) { - if (zlogp != &logsys) + if (zlogp != &logsys) { + int last = strlen(inbuf) - 1; + + if (inbuf[last] == '\n') + inbuf[last] = '\0'; zerror(zlogp, B_FALSE, "%s", inbuf); + } } else { char *p; rd_cnt += 1024 - 1; if ((p = realloc(*retstr, rd_cnt + 1024)) == NULL) { zerror(zlogp, B_FALSE, "out of memory"); - (void) pclose(file); - return (-1); + break; } *retstr = p; inbuf = *retstr + rd_cnt; } } - status = pclose(file); + + while (fclose(file) != 0) { + assert(errno == EINTR); + } + while (waitpid(child, &status, 0) == -1) { + if (errno != EINTR) { + zerror(zlogp, B_TRUE, + "failed to get exit status of '%s'", cmdbuf); + return (-1); + } + } if (WIFSIGNALED(status)) { zerror(zlogp, B_FALSE, "%s unexpectedly terminated due to " @@ -803,24 +1197,91 @@ do_subproc(zlog_t *zlogp, char *cmdbuf, char **retstr) return (WEXITSTATUS(status)); } +/* + * Get the path for this zone's init(1M) (or equivalent) process. First look + * for a zone-specific init-name attr, then get it from the brand. + */ +static int +get_initname(brand_handle_t bh, char *initname, int len) +{ + struct zone_attrtab a; + + bzero(&a, sizeof (a)); + (void) strlcpy(a.zone_attr_name, "init-name", + sizeof (a.zone_attr_name)); + + if (zonecfg_lookup_attr(snap_hndl, &a) == Z_OK) { + (void) strlcpy(initname, a.zone_attr_value, len); + return (0); + } + + return (brand_get_initname(bh, initname, len)); +} + +/* + * Get the restart-init flag for this zone's init(1M) (or equivalent) process. + * First look for a zone-specific restart-init attr, then get it from the brand. + */ +static boolean_t +restartinit(brand_handle_t bh) +{ + struct zone_attrtab a; + + bzero(&a, sizeof (a)); + (void) strlcpy(a.zone_attr_name, "restart-init", + sizeof (a.zone_attr_name)); + + if (zonecfg_lookup_attr(snap_hndl, &a) == Z_OK) { + if (strcmp(a.zone_attr_value, "false") == 0) + return (B_FALSE); + return (B_TRUE); + } + + return (brand_restartinit(bh)); +} + +/* + * Get the app-svc-dependent flag for this zone's init process. This is a + * zone-specific attr which controls the type of contract we create for the + * zone's init. When true, the contract will include CT_PR_EV_EXIT in the fatal + * set, so that when any service which is in the same contract exits, the init + * application will be terminated. + */ +static boolean_t +is_app_svc_dep(void) +{ + struct zone_attrtab a; + + bzero(&a, sizeof (a)); + (void) strlcpy(a.zone_attr_name, "app-svc-dependent", + sizeof (a.zone_attr_name)); + + if (zonecfg_lookup_attr(snap_hndl, &a) == Z_OK && + strcmp(a.zone_attr_value, "true") == 0) { + return (B_TRUE); + } + + return (B_FALSE); +} + static int -zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) +zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate, boolean_t debug) { zoneid_t zoneid; struct stat st; - char zpath[MAXPATHLEN], initpath[MAXPATHLEN], init_file[MAXPATHLEN]; + char rpath[MAXPATHLEN], initpath[MAXPATHLEN], init_file[MAXPATHLEN]; char nbootargs[BOOTARGS_MAX]; char cmdbuf[MAXPATHLEN]; fs_callback_t cb; brand_handle_t bh; zone_iptype_t iptype; - boolean_t links_loaded = B_FALSE; dladm_status_t status; char errmsg[DLADM_STRSIZE]; int err; boolean_t restart_init; + boolean_t app_svc_dep; - if (brand_prestatechg(zlogp, zstate, Z_BOOT) != 0) + if (brand_prestatechg(zlogp, zstate, Z_BOOT, debug) != 0) return (-1); if ((zoneid = getzoneidbyname(zone_name)) == -1) { @@ -853,13 +1314,8 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) /* * Get the brand's boot callback if it exists. */ - if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) { - zerror(zlogp, B_FALSE, "unable to determine zone path"); - brand_close(bh); - goto bad; - } (void) strcpy(cmdbuf, EXEC_PREFIX); - if (brand_get_boot(bh, zone_name, zpath, cmdbuf + EXEC_LEN, + if (brand_get_boot(bh, zone_name, zonepath, cmdbuf + EXEC_LEN, sizeof (cmdbuf) - EXEC_LEN) != 0) { zerror(zlogp, B_FALSE, "unable to determine branded zone's boot callback"); @@ -868,41 +1324,50 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) } /* Get the path for this zone's init(1M) (or equivalent) process. */ - if (brand_get_initname(bh, init_file, MAXPATHLEN) != 0) { + if (get_initname(bh, init_file, MAXPATHLEN) != 0) { zerror(zlogp, B_FALSE, "unable to determine zone's init(1M) location"); brand_close(bh); goto bad; } - /* See if this zone's brand should restart init if it dies. */ - restart_init = brand_restartinit(bh); + /* See if we should restart init if it dies. */ + restart_init = restartinit(bh); + + /* + * See if we need to setup contract dependencies between the zone's + * primary application and any of its services. + */ + app_svc_dep = is_app_svc_dep(); brand_close(bh); - err = filter_bootargs(zlogp, bootargs, nbootargs, init_file, - bad_boot_arg); - if (err == Z_INVAL) - eventstream_write(Z_EVT_ZONE_BADARGS); - else if (err != Z_OK) + err = filter_bootargs(zlogp, bootargs, nbootargs, init_file); + if (err != Z_OK) goto bad; assert(init_file[0] != '\0'); - /* Try to anticipate possible problems: Make sure init is executable. */ - if (zone_get_rootpath(zone_name, zpath, sizeof (zpath)) != Z_OK) { + /* + * Try to anticipate possible problems: If possible, make sure init is + * executable. + */ + if (zone_get_rootpath(zone_name, rpath, sizeof (rpath)) != Z_OK) { zerror(zlogp, B_FALSE, "unable to determine zone root"); goto bad; } - (void) snprintf(initpath, sizeof (initpath), "%s%s", zpath, init_file); + (void) snprintf(initpath, sizeof (initpath), "%s%s", rpath, init_file); - if (stat(initpath, &st) == -1) { + if (lstat(initpath, &st) == -1) { zerror(zlogp, B_TRUE, "could not stat %s", initpath); goto bad; } - if ((st.st_mode & S_IXUSR) == 0) { + /* LINTED: E_NOP_IF_STMT */ + if ((st.st_mode & S_IFMT) == S_IFLNK) { + /* symlink, we'll have to wait and resolve when we boot */ + } else if ((st.st_mode & S_IXUSR) == 0) { zerror(zlogp, B_FALSE, "%s is not executable", initpath); goto bad; } @@ -920,7 +1385,6 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) " %s", dladm_status2str(status, errmsg)); goto bad; } - links_loaded = B_TRUE; } /* @@ -929,7 +1393,7 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) * is booted. */ if ((strlen(cmdbuf) > EXEC_LEN) && - (do_subproc(zlogp, cmdbuf, NULL) != Z_OK)) { + (do_subproc(zlogp, cmdbuf, NULL, debug) != Z_OK)) { zerror(zlogp, B_FALSE, "%s failed", cmdbuf); goto bad; } @@ -950,19 +1414,31 @@ zone_bootup(zlog_t *zlogp, const char *bootargs, int zstate) goto bad; } + if (app_svc_dep && zone_setattr(zoneid, ZONE_ATTR_APP_SVC_CT, + (void *)B_TRUE, sizeof (boolean_t)) == -1) { + zerror(zlogp, B_TRUE, "could not set zone app-die"); + goto bad; + } + /* * Inform zonestatd of a new zone so that it can install a door for * the zone to contact it. */ notify_zonestatd(zone_id); + /* Startup a thread to perform zfd logging/tty svc for the zone. */ + create_log_thread(zlogp); + if (zone_boot(zoneid) == -1) { zerror(zlogp, B_TRUE, "unable to boot zone"); + destroy_log_thread(zlogp); goto bad; } - if (brand_poststatechg(zlogp, zstate, Z_BOOT) != 0) + if (brand_poststatechg(zlogp, zstate, Z_BOOT, debug) != 0) { + destroy_log_thread(zlogp); goto bad; + } return (0); @@ -971,32 +1447,45 @@ bad: * If something goes wrong, we up the zones's state to the target * state, RUNNING, and then invoke the hook as if we're halting. */ - (void) brand_poststatechg(zlogp, ZONE_STATE_RUNNING, Z_HALT); - if (links_loaded) - (void) dladm_zone_halt(dld_handle, zoneid); + (void) brand_poststatechg(zlogp, ZONE_STATE_RUNNING, Z_HALT, debug); + return (-1); } static int -zone_halt(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting, int zstate) +zone_halt(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting, int zstate, + boolean_t debug) { int err; - if (brand_prestatechg(zlogp, zstate, Z_HALT) != 0) + /* + * If performing a scratch zone unmount then do not call the + * state change hooks. + */ + if (unmount_cmd == B_FALSE && + brand_prestatechg(zlogp, zstate, Z_HALT, debug) != 0) return (-1); - if (vplat_teardown(zlogp, unmount_cmd, rebooting) != 0) { + if (vplat_teardown(zlogp, unmount_cmd, rebooting, debug) != 0) { if (!bringup_failure_recovery) zerror(zlogp, B_FALSE, "unable to destroy zone"); + destroy_log_thread(zlogp); return (-1); } + /* Shut down is done, stop the log thread */ + destroy_log_thread(zlogp); + + if (unmount_cmd == B_FALSE && + brand_poststatechg(zlogp, zstate, Z_HALT, debug) != 0) + return (-1); + if ((err = zonecfg_destroy_snapshot(zone_name)) != Z_OK) zerror(zlogp, B_FALSE, "destroying snapshot: %s", zonecfg_strerror(err)); - if (brand_poststatechg(zlogp, zstate, Z_HALT) != 0) - return (-1); + zonecfg_fini_handle(snap_hndl); + snap_hndl = NULL; return (0); } @@ -1008,7 +1497,6 @@ zone_graceful_shutdown(zlog_t *zlogp) pid_t child; char cmdbuf[MAXPATHLEN]; brand_handle_t bh = NULL; - char zpath[MAXPATHLEN]; ctid_t ct; int tmpl_fd; int child_status; @@ -1029,18 +1517,12 @@ zone_graceful_shutdown(zlog_t *zlogp) return (-1); } - if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) { - zerror(zlogp, B_FALSE, "unable to determine zone path"); - brand_close(bh); - return (-1); - } - /* * If there is a brand 'shutdown' callback, execute it now to give the * brand a chance to cleanup any custom configuration. */ (void) strcpy(cmdbuf, EXEC_PREFIX); - if (brand_get_shutdown(bh, zone_name, zpath, cmdbuf + EXEC_LEN, + if (brand_get_shutdown(bh, zone_name, zonepath, cmdbuf + EXEC_LEN, sizeof (cmdbuf) - EXEC_LEN) != 0 || strlen(cmdbuf) <= EXEC_LEN) { (void) strcat(cmdbuf, SHUTDOWN_DEFAULT); } @@ -1178,6 +1660,36 @@ audit_put_record(zlog_t *zlogp, ucred_t *uc, int return_val, } /* + * Log the exit time and status of the zone's init process into + * {zonepath}/lastexited. If the zone shutdown normally, the exit status will + * be -1, otherwise it will be the exit status as described in wait.3c. + * If the zone is configured to restart init, then nothing will be logged if + * init exits unexpectedly (the kernel will never upcall in this case). + */ +static void +log_init_exit(int status) +{ + char p[MAXPATHLEN]; + char buf[128]; + struct timeval t; + int fd; + + if (snprintf(p, sizeof (p), "%s/lastexited", zonepath) > sizeof (p)) + return; + if (gettimeofday(&t, NULL) != 0) + return; + if (snprintf(buf, sizeof (buf), "%ld.%ld %d\n", t.tv_sec, t.tv_usec, + status) > sizeof (buf)) + return; + if ((fd = open(p, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) + return; + + (void) write(fd, buf, strlen(buf)); + + (void) close(fd); +} + +/* * The main routine for the door server that deals with zone state transitions. */ /* ARGSUSED */ @@ -1190,9 +1702,11 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, zone_state_t zstate; zone_cmd_t cmd; + boolean_t debug; + int init_status; zone_cmd_arg_t *zargp; - boolean_t kernelcall; + boolean_t kernelcall = B_TRUE; int rval = -1; uint64_t uniqid; @@ -1213,6 +1727,8 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, * it is time for us to shut down zoneadmd. */ if (zargp == DOOR_UNREF_DATA) { + logstream_close(platloghdl, B_TRUE); + /* * See comment at end of main() for info on the last rites. */ @@ -1242,6 +1758,8 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, goto out; } cmd = zargp->cmd; + debug = zargp->debug; + init_status = zargp->status; if (door_ucred(&uc) != 0) { zerror(&logsys, B_TRUE, "door_ucred"); @@ -1322,7 +1840,7 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, rval = -1; goto out; } - zlogp = &logsys; /* Log errors to syslog */ + zlogp = &logplat; /* Log errors to platform.log */ } /* @@ -1348,23 +1866,25 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, case ZONE_STATE_INSTALLED: switch (cmd) { case Z_READY: - rval = zone_ready(zlogp, Z_MNT_BOOT, zstate); + rval = zone_ready(zlogp, Z_MNT_BOOT, zstate, debug); if (rval == 0) eventstream_write(Z_EVT_ZONE_READIED); + zcons_statechanged(); break; case Z_BOOT: case Z_FORCEBOOT: eventstream_write(Z_EVT_ZONE_BOOTING); - if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate)) - == 0) { + if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate, + debug)) == 0) { rval = zone_bootup(zlogp, zargp->bootbuf, - zstate); + zstate, debug); } audit_put_record(zlogp, uc, rval, "boot"); + zcons_statechanged(); if (rval != 0) { bringup_failure_recovery = B_TRUE; (void) zone_halt(zlogp, B_FALSE, B_FALSE, - zstate); + zstate, debug); eventstream_write(Z_EVT_ZONE_BOOTFAILED); } break; @@ -1416,7 +1936,7 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, rval = zone_ready(zlogp, strcmp(zargp->bootbuf, "-U") == 0 ? - Z_MNT_UPDATE : Z_MNT_SCRATCH, zstate); + Z_MNT_UPDATE : Z_MNT_SCRATCH, zstate, debug); if (rval != 0) break; @@ -1478,15 +1998,18 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, rval = 0; break; case Z_BOOT: + case Z_FORCEBOOT: (void) strlcpy(boot_args, zargp->bootbuf, sizeof (boot_args)); eventstream_write(Z_EVT_ZONE_BOOTING); - rval = zone_bootup(zlogp, zargp->bootbuf, zstate); + rval = zone_bootup(zlogp, zargp->bootbuf, zstate, + debug); audit_put_record(zlogp, uc, rval, "boot"); + zcons_statechanged(); if (rval != 0) { bringup_failure_recovery = B_TRUE; (void) zone_halt(zlogp, B_FALSE, B_TRUE, - zstate); + zstate, debug); eventstream_write(Z_EVT_ZONE_BOOTFAILED); } boot_args[0] = '\0'; @@ -1494,15 +2017,17 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, case Z_HALT: if (kernelcall) /* Invalid; can't happen */ abort(); - if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate)) - != 0) + if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate, + debug)) != 0) break; + zcons_statechanged(); eventstream_write(Z_EVT_ZONE_HALTED); break; case Z_SHUTDOWN: case Z_REBOOT: case Z_NOTE_UNINSTALLING: case Z_MOUNT: + case Z_FORCEMOUNT: case Z_UNMOUNT: if (kernelcall) /* Invalid; can't happen */ abort(); @@ -1519,7 +2044,7 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, case Z_UNMOUNT: if (kernelcall) /* Invalid; can't happen */ abort(); - rval = zone_halt(zlogp, B_TRUE, B_FALSE, zstate); + rval = zone_halt(zlogp, B_TRUE, B_FALSE, zstate, debug); if (rval == 0) { eventstream_write(Z_EVT_ZONE_HALTED); (void) sema_post(&scratch_sem); @@ -1541,15 +2066,18 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, case ZONE_STATE_DOWN: switch (cmd) { case Z_READY: - if ((rval = zone_halt(zlogp, B_FALSE, B_TRUE, zstate)) - != 0) + if ((rval = zone_halt(zlogp, B_FALSE, B_TRUE, zstate, + debug)) != 0) break; - if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate)) == 0) + zcons_statechanged(); + if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate, + debug)) == 0) eventstream_write(Z_EVT_ZONE_READIED); else eventstream_write(Z_EVT_ZONE_HALTED); break; case Z_BOOT: + case Z_FORCEBOOT: /* * We could have two clients racing to boot this * zone; the second client loses, but its request @@ -1560,32 +2088,40 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, rval = 0; break; case Z_HALT: - if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate)) - != 0) + if (kernelcall) { + log_init_exit(init_status); + } else { + log_init_exit(-1); + } + if ((rval = zone_halt(zlogp, B_FALSE, B_FALSE, zstate, + debug)) != 0) break; eventstream_write(Z_EVT_ZONE_HALTED); + zcons_statechanged(); break; case Z_REBOOT: (void) strlcpy(boot_args, zargp->bootbuf, sizeof (boot_args)); eventstream_write(Z_EVT_ZONE_REBOOTING); - if ((rval = zone_halt(zlogp, B_FALSE, B_TRUE, zstate)) - != 0) { + if ((rval = zone_halt(zlogp, B_FALSE, B_TRUE, zstate, + debug)) != 0) { eventstream_write(Z_EVT_ZONE_BOOTFAILED); boot_args[0] = '\0'; break; } - if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate)) - != 0) { + zcons_statechanged(); + if ((rval = zone_ready(zlogp, Z_MNT_BOOT, zstate, + debug)) != 0) { eventstream_write(Z_EVT_ZONE_BOOTFAILED); boot_args[0] = '\0'; break; } - rval = zone_bootup(zlogp, zargp->bootbuf, zstate); + rval = zone_bootup(zlogp, zargp->bootbuf, zstate, + debug); audit_put_record(zlogp, uc, rval, "reboot"); if (rval != 0) { (void) zone_halt(zlogp, B_FALSE, B_TRUE, - zstate); + zstate, debug); eventstream_write(Z_EVT_ZONE_BOOTFAILED); } boot_args[0] = '\0'; @@ -1597,6 +2133,7 @@ server(void *cookie, char *args, size_t alen, door_desc_t *dp, break; case Z_NOTE_UNINSTALLING: case Z_MOUNT: + case Z_FORCEMOUNT: case Z_UNMOUNT: zerror(zlogp, B_FALSE, "%s operation is invalid " "for zones in state '%s'", z_cmd_name(cmd), @@ -1765,6 +2302,29 @@ top: "zoneadmd does not appear to be available; " "restarted zoneadmd to recover.", zone_name, zone_state_str(zstate)); + + /* + * Startup a thread to perform the zfd logging/tty svc + * for the zone. zlogp won't be valid for much longer + * so use logplat. + */ + if (getzoneidbyname(zone_name) != -1) { + create_log_thread(&logplat); + } + + /* recover the global configuration snapshot */ + if (snap_hndl == NULL) { + if ((snap_hndl = zonecfg_init_handle()) + == NULL || + zonecfg_create_snapshot(zone_name) + != Z_OK || + zonecfg_get_snapshot_handle(zone_name, + snap_hndl) != Z_OK) { + zerror(zlogp, B_FALSE, "recovering " + "zone configuration handle"); + goto out; + } + } } (void) fdetach(zone_door_path); @@ -1778,21 +2338,62 @@ out: } /* - * Setup the brand's pre and post state change callbacks, as well as the - * query callback, if any of these exist. + * Run the query hook with the 'env' parameter. It should return a + * string of tab-delimited key-value pairs, each of which should be set + * in the environment. + * + * Because the env_vars string values become part of the environment, the + * string is static and we don't free it. + * + * This function is always called before zoneadmd forks and makes itself + * exclusive, so it is possible there could more than one instance of zoneadmd + * running in parallel at this point. Thus, we have no zonecfg snapshot and + * shouldn't take one yet (i.e. snap_hndl is NULL). Thats ok, since we don't + * need any zonecfg info to query for a brand-specific env value. */ static int -brand_callback_init(brand_handle_t bh, char *zone_name) +set_brand_env(zlog_t *zlogp) { - char zpath[MAXPATHLEN]; + int ret = 0; + static char *env_vars = NULL; + char buf[2 * MAXPATHLEN]; + + if (query_hook[0] == '\0' || env_vars != NULL) + return (0); + + if (snprintf(buf, sizeof (buf), "%s env", query_hook) > sizeof (buf)) + return (-1); - if (zone_get_zonepath(zone_name, zpath, sizeof (zpath)) != Z_OK) + if (do_subproc(zlogp, buf, &env_vars, B_FALSE) != 0) return (-1); + if (env_vars != NULL) { + char *sp; + + sp = strtok(env_vars, "\t"); + while (sp != NULL) { + if (putenv(sp) != 0) { + ret = -1; + break; + } + sp = strtok(NULL, "\t"); + } + } + + return (ret); +} + +/* + * Setup the brand's pre and post state change callbacks, as well as the + * query callback, if any of these exist. + */ +static int +brand_callback_init(brand_handle_t bh, char *zone_name) +{ (void) strlcpy(pre_statechg_hook, EXEC_PREFIX, sizeof (pre_statechg_hook)); - if (brand_get_prestatechange(bh, zone_name, zpath, + if (brand_get_prestatechange(bh, zone_name, zonepath, pre_statechg_hook + EXEC_LEN, sizeof (pre_statechg_hook) - EXEC_LEN) != 0) return (-1); @@ -1803,7 +2404,7 @@ brand_callback_init(brand_handle_t bh, char *zone_name) (void) strlcpy(post_statechg_hook, EXEC_PREFIX, sizeof (post_statechg_hook)); - if (brand_get_poststatechange(bh, zone_name, zpath, + if (brand_get_poststatechange(bh, zone_name, zonepath, post_statechg_hook + EXEC_LEN, sizeof (post_statechg_hook) - EXEC_LEN) != 0) return (-1); @@ -1814,7 +2415,7 @@ brand_callback_init(brand_handle_t bh, char *zone_name) (void) strlcpy(query_hook, EXEC_PREFIX, sizeof (query_hook)); - if (brand_get_query(bh, zone_name, zpath, query_hook + EXEC_LEN, + if (brand_get_query(bh, zone_name, zonepath, query_hook + EXEC_LEN, sizeof (query_hook) - EXEC_LEN) != 0) return (-1); @@ -1942,6 +2543,11 @@ main(int argc, char *argv[]) return (1); } + if (zone_get_zonepath(zone_name, zonepath, sizeof (zonepath)) != Z_OK) { + zerror(zlogp, B_FALSE, "unable to determine zone path"); + return (-1); + } + if (zonecfg_default_brand(default_brand, sizeof (default_brand)) != Z_OK) { zerror(zlogp, B_FALSE, "unable to determine default brand"); @@ -2013,6 +2619,11 @@ main(int argc, char *argv[]) } priv_freeset(privset); + if (set_brand_env(zlogp) != 0) { + zerror(zlogp, B_FALSE, "Unable to setup brand's environment"); + return (1); + } + if (mkzonedir(zlogp) != 0) return (1); @@ -2139,6 +2750,15 @@ main(int argc, char *argv[]) openlog("zoneadmd", LOG_PID, LOG_DAEMON); /* + * Allow logging to <zonepath>/logs/<file>. + */ + logstream_init(zlogp); + platloghdl = logstream_open("platform.log", "zoneadmd", 0); + + /* logplat looks the same as logsys, but logs to platform.log */ + logplat = logsys; + + /* * The eventstream is used to publish state changes in the zone * from the door threads to the console I/O poller. */ @@ -2157,7 +2777,6 @@ main(int argc, char *argv[]) if (make_daemon_exclusive(zlogp) == -1) goto child_out; - /* * Create/join a new session; we need to be careful of what we do with * the console from now on so we don't end up being the session leader @@ -2167,9 +2786,13 @@ main(int argc, char *argv[]) /* * This thread shouldn't be receiving any signals; in particular, - * SIGCHLD should be received by the thread doing the fork(). + * SIGCHLD should be received by the thread doing the fork(). The + * exceptions are SIGHUP and SIGUSR1 for log rotation, set up by + * logstream_init(). */ (void) sigfillset(&blockset); + (void) sigdelset(&blockset, SIGHUP); + (void) sigdelset(&blockset, SIGUSR1); (void) thr_sigsetmask(SIG_BLOCK, &blockset, NULL); /* diff --git a/usr/src/cmd/zoneadmd/zoneadmd.h b/usr/src/cmd/zoneadmd/zoneadmd.h index d784a303b3..06353cbe61 100644 --- a/usr/src/cmd/zoneadmd/zoneadmd.h +++ b/usr/src/cmd/zoneadmd/zoneadmd.h @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2019 Joyent, Inc. */ #ifndef _ZONEADMD_H @@ -32,6 +33,9 @@ extern "C" { #endif #include <libdladm.h> +#include <libzonecfg.h> +#include <thread.h> +#include <synch.h> /* * Multi-threaded programs should avoid MT-unsafe library calls (i.e., any- @@ -69,6 +73,7 @@ extern "C" { #define DEFAULT_DIR_USER -1 /* user ID for chown: -1 means don't change */ #define DEFAULT_DIR_GROUP -1 /* grp ID for chown: -1 means don't change */ +#define ALT_MOUNT(mount_cmd) ((mount_cmd) != Z_MNT_BOOT) typedef struct zlog { FILE *logfile; /* file to log to */ @@ -83,24 +88,27 @@ typedef struct zlog { char *locale; /* locale to use for gettext() */ } zlog_t; -extern zlog_t logsys; +extern zlog_t logsys; /* syslog */ +extern zlog_t logplat; /* platform.log */ extern mutex_t lock; extern mutex_t msglock; extern boolean_t in_death_throes; extern boolean_t bringup_failure_recovery; extern char *zone_name; +extern char zonepath[MAXNAMELEN]; +extern zone_dochandle_t snap_hndl; extern char pool_name[MAXNAMELEN]; extern char brand_name[MAXNAMELEN]; extern char default_brand[MAXNAMELEN]; extern char boot_args[BOOTARGS_MAX]; -extern char bad_boot_arg[BOOTARGS_MAX]; extern boolean_t zone_isnative; extern boolean_t zone_iscluster; extern dladm_handle_t dld_handle; extern void zerror(zlog_t *, boolean_t, const char *, ...); extern char *localize_msg(char *locale, const char *msg); +extern void nwifent_free_attrs(struct zone_nwiftab *); /* * Eventstream interfaces. @@ -112,8 +120,7 @@ typedef enum { Z_EVT_ZONE_HALTED, Z_EVT_ZONE_READIED, Z_EVT_ZONE_UNINSTALLING, - Z_EVT_ZONE_BOOTFAILED, - Z_EVT_ZONE_BADARGS + Z_EVT_ZONE_BOOTFAILED } zone_evt_t; extern int eventstream_init(); @@ -135,9 +142,9 @@ typedef enum { /* * Virtual platform interfaces. */ -extern zoneid_t vplat_create(zlog_t *, zone_mnt_t); +extern zoneid_t vplat_create(zlog_t *, zone_mnt_t, zoneid_t); extern int vplat_bringup(zlog_t *, zone_mnt_t, zoneid_t); -extern int vplat_teardown(zlog_t *, boolean_t, boolean_t); +extern int vplat_teardown(zlog_t *, boolean_t, boolean_t, boolean_t); extern int vplat_get_iptype(zlog_t *, zone_iptype_t *); /* @@ -154,6 +161,23 @@ extern void resolve_lofs(zlog_t *zlogp, char *path, size_t pathlen); */ extern int init_console(zlog_t *); extern void serve_console(zlog_t *); +extern void zcons_statechanged(); + +/* + * Logging routines + */ +typedef enum { + LS_LINE_BUFFERED = 0x1 /* Write when \n found or full buffer */ +} logstream_flags_t; + +extern boolean_t logging_poisoned; + +extern void create_log_thread(zlog_t *); +extern void destroy_log_thread(zlog_t *); +extern void logstream_init(zlog_t *); +extern int logstream_open(const char *, const char *, logstream_flags_t); +extern void logstream_write(int, char *, int); +extern void logstream_close(int, boolean_t); /* * Contract handling. @@ -163,7 +187,13 @@ extern int init_template(void); /* * Routine to manage child processes. */ -extern int do_subproc(zlog_t *, char *, char **); +extern int do_subproc(zlog_t *, char *, char **, boolean_t); + +/* + * Resource handling. + */ +extern int resolve_device_match(zlog_t *, struct zone_devtab *, + char *, size_t); #ifdef __cplusplus } diff --git a/usr/src/cmd/zonecfg/Makefile b/usr/src/cmd/zonecfg/Makefile index c5d042f48f..c551d7073a 100644 --- a/usr/src/cmd/zonecfg/Makefile +++ b/usr/src/cmd/zonecfg/Makefile @@ -29,6 +29,7 @@ PROG= zonecfg OBJS= zonecfg.o zonecfg_lex.o zonecfg_grammar.tab.o include ../Makefile.cmd +include ../Makefile.ctf # zonecfg has a name clash with main() and libl.so.1. However, zonecfg must # still export a number of "yy*" (libl) interfaces. Reduce all other symbols @@ -38,7 +39,8 @@ MAPOPTS = $(MAPFILES:%=-M%) LFLAGS = -t YFLAGS = -d -b zonecfg_grammar -LDLIBS += -lzonecfg -ll -lnsl -ltecla -lzfs -lbrand -ldladm -linetutil +LDLIBS += -lzonecfg -ll -lnsl -ltecla -lzfs -lbrand -ldladm -linetutil -luuid +CFLAGS += -DYYLMAX=2048 CPPFLAGS += -I. LDFLAGS += $(MAPOPTS) CLEANFILES += zonecfg_lex.c zonecfg_grammar.tab.c zonecfg_grammar.tab.h diff --git a/usr/src/cmd/zonecfg/zonecfg.c b/usr/src/cmd/zonecfg/zonecfg.c index d1aaec81db..20f30e6e0c 100644 --- a/usr/src/cmd/zonecfg/zonecfg.c +++ b/usr/src/cmd/zonecfg/zonecfg.c @@ -81,6 +81,7 @@ #include <libinetutil.h> #include <pwd.h> #include <inet/ip.h> +#include <uuid/uuid.h> #include <libzonecfg.h> #include "zonecfg.h" @@ -128,7 +129,7 @@ extern int lex_lineno; #define SHELP_REMOVE "remove [-F] <resource-type> " \ "[ <property-name>=<property-value> ]*\n" \ "\t(global scope)\n" \ - "remove <property-name> <property-value>\n" \ + "remove [-F] <property-name> <property-value>\n" \ "\t(resource scope)" #define SHELP_REVERT "revert [-F]" #define SHELP_SELECT "select <resource-type> { <property-name>=" \ @@ -189,6 +190,8 @@ char *res_types[] = { "admin", "fs-allowed", ALIAS_MAXPROCS, + ALIAS_ZFSPRI, + "uuid", "security-flags", NULL }; @@ -237,6 +240,12 @@ char *prop_types[] = { "fs-allowed", ALIAS_MAXPROCS, "allowed-address", + ALIAS_ZFSPRI, + "mac-addr", + "vlan-id", + "global-nic", + "property", + "uuid", "default", "lower", "upper", @@ -306,6 +315,7 @@ static const char *clear_cmds[] = { "clear " ALIAS_MAXSEMIDS, "clear " ALIAS_SHARES, "clear " ALIAS_MAXPROCS, + "clear " ALIAS_ZFSPRI, NULL }; @@ -358,6 +368,8 @@ static const char *set_cmds[] = { "set hostid=", "set fs-allowed=", "set " ALIAS_MAXPROCS "=", + "set " ALIAS_ZFSPRI "=", + "set uuid=", NULL }; @@ -391,6 +403,7 @@ static const char *info_cmds[] = { "info admin", "info fs-allowed", "info max-processes", + "info uuid", NULL }; @@ -416,10 +429,20 @@ static const char *net_res_scope_cmds[] = { "exit", "help", "info", + "add property ", + "clear allowed-address", + "clear defrouter", + "clear global-nic", + "clear mac-addr", + "clear vlan-id", + "remove property ", "set address=", "set allowed-address=", - "set physical=", "set defrouter=", + "set global-nic=", + "set mac-addr=", + "set physical=", + "set vlan-id=", NULL }; @@ -429,6 +452,7 @@ static const char *device_res_scope_cmds[] = { "exit", "help", "info", + "add property ", "set match=", NULL }; @@ -546,12 +570,14 @@ static zone_dochandle_t handle; /* used all over the place */ static char zone[ZONENAME_MAX]; static char revert_zone[ZONENAME_MAX]; +static char new_uuid[UUID_PRINTABLE_STRING_LENGTH]; /* global brand operations */ static brand_handle_t brand; /* set in modifying functions, checked in read_input() */ static boolean_t need_to_commit = B_FALSE; +static boolean_t is_create = B_FALSE; boolean_t saw_error; /* set in yacc parser, checked in read_input() */ @@ -600,7 +626,6 @@ static struct zone_rctltab old_rctltab, in_progress_rctltab; static struct zone_attrtab old_attrtab, in_progress_attrtab; static struct zone_dstab old_dstab, in_progress_dstab; static struct zone_psettab old_psettab, in_progress_psettab; -static struct zone_mcaptab old_mcaptab, in_progress_mcaptab; static struct zone_admintab old_admintab, in_progress_admintab; static struct zone_secflagstab old_secflagstab, in_progress_secflagstab; @@ -1106,11 +1131,20 @@ usage(boolean_t verbose, uint_t flags) (void) fprintf(fp, gettext("Valid commands:\n")); (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS), gettext("<IP-address>")); + (void) fprintf(fp, "\t%s %s (%s=<value>,%s=<value>)\n", + cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), pt_to_str(PT_VALUE)); (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), pt_to_str(PT_ALLOWED_ADDRESS), gettext("<IP-address>")); (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), pt_to_str(PT_PHYSICAL), gettext("<interface>")); + (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), + pt_to_str(PT_MAC), gettext("<mac-address>")); + (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), + pt_to_str(PT_GNIC), gettext("<global zone NIC>")); + (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), + pt_to_str(PT_VLANID), gettext("<vlan ID>")); (void) fprintf(fp, gettext("See ifconfig(1M) for " "details of the <interface> string.\n")); (void) fprintf(fp, gettext("%s %s is valid " @@ -1118,10 +1152,12 @@ usage(boolean_t verbose, uint_t flags) "must not be set.\n"), cmd_to_str(CMD_SET), pt_to_str(PT_ADDRESS), pt_to_str(PT_IPTYPE), gettext("shared")); - (void) fprintf(fp, gettext("%s %s is valid " - "if the %s property is set to %s, otherwise it " - "must not be set.\n"), - cmd_to_str(CMD_SET), pt_to_str(PT_ALLOWED_ADDRESS), + (void) fprintf(fp, gettext("%s (%s, %s, %s, %s) are " + "valid if the %s property is set to %s, otherwise " + "they must not be set.\n"), + cmd_to_str(CMD_SET), + pt_to_str(PT_ALLOWED_ADDRESS), pt_to_str(PT_MAC), + pt_to_str(PT_VLANID), pt_to_str(PT_GNIC), pt_to_str(PT_IPTYPE), gettext("exclusive")); (void) fprintf(fp, gettext("\t%s %s=%s\n%s %s " "is valid if the %s or %s property is set, " @@ -1137,6 +1173,9 @@ usage(boolean_t verbose, uint_t flags) "used to configure a device node.\n"), rt_to_str(resource_scope)); (void) fprintf(fp, gettext("Valid commands:\n")); + (void) fprintf(fp, "\t%s %s (%s=<value>,%s=<value>)\n", + cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), pt_to_str(PT_VALUE)); (void) fprintf(fp, "\t%s %s=%s\n", cmd_to_str(CMD_SET), pt_to_str(PT_MATCH), gettext("<device-path>")); break; @@ -1283,10 +1322,12 @@ usage(boolean_t verbose, uint_t flags) if (flags & HELP_USAGE) { (void) fprintf(fp, "%s:\t%s %s\n", gettext("usage"), execname, cmd_to_str(CMD_HELP)); - (void) fprintf(fp, "\t%s -z <zone>\t\t\t(%s)\n", + (void) fprintf(fp, "\t%s {-z <zone>|-u <uuid>}\t\t\t(%s)\n", execname, gettext("interactive")); - (void) fprintf(fp, "\t%s -z <zone> <command>\n", execname); - (void) fprintf(fp, "\t%s -z <zone> -f <command-file>\n", + (void) fprintf(fp, "\t%s {-z <zone>|-u <uuid>} <command>\n", + execname); + (void) fprintf(fp, + "\t%s {-z <zone>|-u <uuid>} -f <command-file>\n", execname); } if (flags & HELP_SUBCMDS) { @@ -1375,15 +1416,22 @@ usage(boolean_t verbose, uint_t flags) pt_to_str(PT_MAXSEMIDS)); (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), pt_to_str(PT_SHARES)); + (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), + pt_to_str(PT_UUID)); + (void) fprintf(fp, "\t%s\t%s\n", gettext("(global)"), + pt_to_str(PT_ZFSPRI)); (void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s, %s\n", rt_to_str(RT_FS), pt_to_str(PT_DIR), pt_to_str(PT_SPECIAL), pt_to_str(PT_RAW), pt_to_str(PT_TYPE), pt_to_str(PT_OPTIONS)); - (void) fprintf(fp, "\t%s\t\t%s, %s, %s|%s\n", rt_to_str(RT_NET), + (void) fprintf(fp, "\t%s\t\t%s, %s, %s, %s, %s, %s, %s %s\n", + rt_to_str(RT_NET), pt_to_str(PT_ADDRESS), pt_to_str(PT_ALLOWED_ADDRESS), - pt_to_str(PT_PHYSICAL), pt_to_str(PT_DEFROUTER)); - (void) fprintf(fp, "\t%s\t\t%s\n", rt_to_str(RT_DEVICE), - pt_to_str(PT_MATCH)); + pt_to_str(PT_GNIC), pt_to_str(PT_MAC), + pt_to_str(PT_PHYSICAL), pt_to_str(PT_NPROP), + pt_to_str(PT_VLANID), pt_to_str(PT_DEFROUTER)); + (void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_DEVICE), + pt_to_str(PT_MATCH), pt_to_str(PT_NPROP)); (void) fprintf(fp, "\t%s\t\t%s, %s\n", rt_to_str(RT_RCTL), pt_to_str(PT_NAME), pt_to_str(PT_VALUE)); (void) fprintf(fp, "\t%s\t\t%s, %s, %s\n", rt_to_str(RT_ATTR), @@ -1441,6 +1489,9 @@ initialize(boolean_t handle_expected) if (zonecfg_check_handle(handle) != Z_OK) { if ((err = zonecfg_get_handle(zone, handle)) == Z_OK) { got_handle = B_TRUE; + + (void) zonecfg_fix_obsolete(handle); + if (zonecfg_get_brand(handle, brandname, sizeof (brandname)) != Z_OK) { zerr("Zone %s is inconsistent: missing " @@ -1708,6 +1759,7 @@ create_func(cmd_t *cmd) boolean_t force = B_FALSE; boolean_t attach = B_FALSE; boolean_t arg_err = B_FALSE; + uuid_t uuid; assert(cmd != NULL); @@ -1715,7 +1767,7 @@ create_func(cmd_t *cmd) (void) strlcpy(zone_template, "SUNWdefault", sizeof (zone_template)); optind = 0; - while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?a:bFt:")) + while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "?a:bFt:X")) != EOF) { switch (arg) { case '?': @@ -1741,6 +1793,17 @@ create_func(cmd_t *cmd) (void) strlcpy(zone_template, optarg, sizeof (zone_template)); break; + case 'X': + (void) snprintf(zone_template, sizeof (zone_template), + "%s/%s.xml", ZONE_CONFIG_ROOT, zone); + err = zonecfg_get_xml_handle(zone_template, handle); + if (err != Z_OK) { + zone_perror(execname, err, B_TRUE); + exit(Z_ERR); + } + got_handle = B_TRUE; + need_to_commit = B_TRUE; + return; default: short_usage(CMD_CREATE); arg_err = B_TRUE; @@ -1794,9 +1857,14 @@ create_func(cmd_t *cmd) } need_to_commit = B_TRUE; + is_create = B_TRUE; zonecfg_fini_handle(handle); handle = tmphandle; got_handle = B_TRUE; + + /* Allocate a new uuid for this new zone */ + uuid_generate(uuid); + uuid_unparse(uuid, new_uuid); } /* @@ -1845,8 +1913,8 @@ export_func(cmd_t *cmd) struct zone_rctltab rctltab; struct zone_dstab dstab; struct zone_psettab psettab; - struct zone_mcaptab mcaptab; struct zone_rctlvaltab *valptr; + struct zone_res_attrtab *rap; struct zone_admintab admintab; struct zone_secflagstab secflagstab; int err, arg; @@ -1860,6 +1928,7 @@ export_func(cmd_t *cmd) FILE *of; boolean_t autoboot; zone_iptype_t iptype; + uuid_t uuid; boolean_t need_to_close = B_FALSE; boolean_t arg_err = B_FALSE; @@ -1970,6 +2039,14 @@ export_func(cmd_t *cmd) pt_to_str(PT_FS_ALLOWED), fsallowedp); } + if (zonecfg_get_uuid(zone, uuid) == Z_OK && !uuid_is_null(uuid)) { + char suuid[UUID_PRINTABLE_STRING_LENGTH]; + + uuid_unparse(uuid, suuid); + (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), + pt_to_str(PT_UUID), suuid); + } + if ((err = zonecfg_setfsent(handle)) != Z_OK) { zone_perror(zone, err, B_FALSE); goto done; @@ -2017,7 +2094,17 @@ export_func(cmd_t *cmd) export_prop(of, PT_ALLOWED_ADDRESS, nwiftab.zone_nwif_allowed_address); export_prop(of, PT_PHYSICAL, nwiftab.zone_nwif_physical); + export_prop(of, PT_MAC, nwiftab.zone_nwif_mac); + export_prop(of, PT_VLANID, nwiftab.zone_nwif_vlan_id); + export_prop(of, PT_GNIC, nwiftab.zone_nwif_gnic); export_prop(of, PT_DEFROUTER, nwiftab.zone_nwif_defrouter); + for (rap = nwiftab.zone_nwif_attrp; rap != NULL; + rap = rap->zone_res_attr_next) { + fprintf(of, "%s %s (%s=%s,%s=\"%s\")\n", + cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), rap->zone_res_attr_name, + pt_to_str(PT_VALUE), rap->zone_res_attr_value); + } (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); } (void) zonecfg_endnwifent(handle); @@ -2030,21 +2117,17 @@ export_func(cmd_t *cmd) (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), rt_to_str(RT_DEVICE)); export_prop(of, PT_MATCH, devtab.zone_dev_match); + for (rap = devtab.zone_dev_attrp; rap != NULL; + rap = rap->zone_res_attr_next) { + fprintf(of, "%s %s (%s=%s,%s=\"%s\")\n", + cmd_to_str(CMD_ADD), pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), rap->zone_res_attr_name, + pt_to_str(PT_VALUE), rap->zone_res_attr_value); + } (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); } (void) zonecfg_enddevent(handle); - if (zonecfg_getmcapent(handle, &mcaptab) == Z_OK) { - char buf[128]; - - (void) fprintf(of, "%s %s\n", cmd_to_str(CMD_ADD), - rt_to_str(RT_MCAP)); - bytes_to_units(mcaptab.zone_physmem_cap, buf, sizeof (buf)); - (void) fprintf(of, "%s %s=%s\n", cmd_to_str(CMD_SET), - pt_to_str(PT_PHYSICAL), buf); - (void) fprintf(of, "%s\n", cmd_to_str(CMD_END)); - } - if ((err = zonecfg_setrctlent(handle)) != Z_OK) { zone_perror(zone, err, B_FALSE); goto done; @@ -2208,7 +2291,6 @@ add_resource(cmd_t *cmd) { int type; struct zone_psettab tmp_psettab; - struct zone_mcaptab tmp_mcaptab; struct zone_secflagstab tmp_secflagstab; uint64_t tmp; uint64_t tmp_mcap; @@ -2301,9 +2383,10 @@ add_resource(cmd_t *cmd) * Make sure there isn't already a mem-cap entry or max-swap * or max-locked rctl. */ - if (zonecfg_lookup_mcap(handle, &tmp_mcaptab) == Z_OK || - zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp_mcap) - == Z_OK || + if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, + &tmp_mcap) == Z_OK || + zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, + &tmp_mcap) == Z_OK || zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &tmp_mcap) == Z_OK) { zerr(gettext("The %s resource or a related resource " @@ -2316,7 +2399,6 @@ add_resource(cmd_t *cmd) "to even the root user; " "this could render the system impossible\n" "to administer. Please use caution.")); - bzero(&in_progress_mcaptab, sizeof (in_progress_mcaptab)); return; case RT_ADMIN: bzero(&in_progress_admintab, sizeof (in_progress_admintab)); @@ -2427,6 +2509,79 @@ bad: zonecfg_free_rctl_value_list(rctlvaltab); } +/* + * Resource attribute ("property" resource embedded on net or dev resource) + */ +static void +do_res_attr(struct zone_res_attrtab **headp, complex_property_ptr_t cpp) +{ + complex_property_ptr_t cp; + struct zone_res_attrtab *np; + int err; + boolean_t seen_name = B_FALSE, seen_value = B_FALSE; + + if ((np = calloc(1, sizeof (struct zone_res_attrtab))) == NULL) { + zone_perror(zone, Z_NOMEM, B_TRUE); + exit(Z_ERR); + } + + for (cp = cpp; cp != NULL; cp = cp->cp_next) { + switch (cp->cp_type) { + case PT_NAME: + if (seen_name) { + zerr(gettext("%s already specified"), + pt_to_str(PT_NAME)); + goto bad; + } + if (strlcpy(np->zone_res_attr_name, cp->cp_value, + sizeof (np->zone_res_attr_name)) >= + sizeof (np->zone_res_attr_name)) { + zerr(gettext("Input for %s is too long"), + pt_to_str(PT_NAME)); + goto bad; + } + seen_name = B_TRUE; + break; + case PT_VALUE: + if (seen_value) { + zerr(gettext("%s already specified"), + pt_to_str(PT_VALUE)); + goto bad; + } + if (strlcpy(np->zone_res_attr_value, cp->cp_value, + sizeof (np->zone_res_attr_value)) >= + sizeof (np->zone_res_attr_value)) { + zerr(gettext("Input for %s is too long"), + pt_to_str(PT_VALUE)); + goto bad; + } + + seen_value = B_TRUE; + break; + default: + zone_perror(pt_to_str(PT_NPROP), Z_NO_PROPERTY_TYPE, + B_TRUE); + long_usage(CMD_ADD, B_TRUE); + usage(B_FALSE, HELP_PROPS); + zonecfg_free_res_attr_list(np); + return; + } + } + + if (!seen_name) + zerr(gettext("%s not specified"), pt_to_str(PT_NAME)); + if (!seen_value) + zerr(gettext("%s not specified"), pt_to_str(PT_VALUE)); + + err = zonecfg_add_res_attr(headp, np); + if (err != Z_OK) + zone_perror(pt_to_str(PT_NPROP), err, B_TRUE); + return; + +bad: + zonecfg_free_res_attr_list(np); +} + static void add_property(cmd_t *cmd) { @@ -2494,6 +2649,44 @@ add_property(cmd_t *cmd) } } return; + case RT_NET: + if (prop_type != PT_NPROP) { + zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, + B_TRUE); + long_usage(CMD_ADD, B_TRUE); + usage(B_FALSE, HELP_PROPS); + return; + } + pp = cmd->cmd_property_ptr[0]; + if (pp->pv_type != PROP_VAL_COMPLEX) { + zerr(gettext("A %s value was expected here."), + pvt_to_str(PROP_VAL_COMPLEX)); + saw_error = B_TRUE; + return; + } + + do_res_attr(&(in_progress_nwiftab.zone_nwif_attrp), + pp->pv_complex); + return; + case RT_DEVICE: + if (prop_type != PT_NPROP) { + zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, + B_TRUE); + long_usage(CMD_ADD, B_TRUE); + usage(B_FALSE, HELP_PROPS); + return; + } + pp = cmd->cmd_property_ptr[0]; + if (pp->pv_type != PROP_VAL_COMPLEX) { + zerr(gettext("A %s value was expected here."), + pvt_to_str(PROP_VAL_COMPLEX)); + saw_error = B_TRUE; + return; + } + + do_res_attr(&(in_progress_devtab.zone_dev_attrp), + pp->pv_complex); + return; case RT_RCTL: if (prop_type != PT_VALUE) { zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, @@ -2538,7 +2731,7 @@ static boolean_t gz_invalid_rt_property(int type) { return (global_zone && (type == RT_ZONENAME || type == RT_ZONEPATH || - type == RT_AUTOBOOT || type == RT_LIMITPRIV || + type == RT_AUTOBOOT || type == RT_LIMITPRIV || type == RT_UUID || type == RT_BOOTARGS || type == RT_BRAND || type == RT_SCHED || type == RT_IPTYPE || type == RT_HOSTID || type == RT_FS_ALLOWED)); } @@ -2547,7 +2740,7 @@ static boolean_t gz_invalid_property(int type) { return (global_zone && (type == PT_ZONENAME || type == PT_ZONEPATH || - type == PT_AUTOBOOT || type == PT_LIMITPRIV || + type == PT_AUTOBOOT || type == PT_LIMITPRIV || type == PT_UUID || type == PT_BOOTARGS || type == PT_BRAND || type == PT_SCHED || type == PT_IPTYPE || type == PT_HOSTID || type == PT_FS_ALLOWED)); } @@ -2598,8 +2791,9 @@ add_func(cmd_t *cmd) resource_scope = cmd->cmd_res_type; end_op = CMD_ADD; add_resource(cmd); - } else + } else { add_property(cmd); + } } /* @@ -2764,6 +2958,32 @@ fill_in_fstab(cmd_t *cmd, struct zone_fstab *fstab, boolean_t fill_in_only) return (zonecfg_lookup_filesystem(handle, fstab)); } +/* + * Turn an addr that looks like f:2:0:44:5:6C into 0f:02:00:44:05:6c + * We're expecting a dst of at least MAXMACADDRLEN size here. + */ +static void +normalize_mac_addr(char *dst, const char *src, int len) +{ + char *p, *e, *sep = ""; + long n; + char buf[MAXMACADDRLEN], tmp[4]; + + *dst = '\0'; + (void) strlcpy(buf, src, sizeof (buf)); + p = strtok(buf, ":"); + while (p != NULL) { + n = strtol(p, &e, 16); + if (*e != '\0' || n > 0xff) + return; + (void) snprintf(tmp, sizeof (tmp), "%s%02x", sep, n); + (void) strlcat(dst, tmp, len); + + sep = ":"; + p = strtok(NULL, ":"); + } +} + static int fill_in_nwiftab(cmd_t *cmd, struct zone_nwiftab *nwiftab, boolean_t fill_in_only) @@ -2797,6 +3017,21 @@ fill_in_nwiftab(cmd_t *cmd, struct zone_nwiftab *nwiftab, pp->pv_simple, sizeof (nwiftab->zone_nwif_physical)); break; + case PT_MAC: + normalize_mac_addr(nwiftab->zone_nwif_mac, + pp->pv_simple, + sizeof (nwiftab->zone_nwif_mac)); + break; + case PT_VLANID: + (void) strlcpy(nwiftab->zone_nwif_vlan_id, + pp->pv_simple, + sizeof (nwiftab->zone_nwif_vlan_id)); + break; + case PT_GNIC: + (void) strlcpy(nwiftab->zone_nwif_gnic, + pp->pv_simple, + sizeof (nwiftab->zone_nwif_gnic)); + break; case PT_DEFROUTER: (void) strlcpy(nwiftab->zone_nwif_defrouter, pp->pv_simple, @@ -3095,6 +3330,8 @@ prompt_remove_resource(cmd_t *cmd, char *rsrc) num = zonecfg_num_resources(handle, rsrc); if (num == 0) { + if (force) + return (B_TRUE); z_cmd_rt_perror(CMD_REMOVE, cmd->cmd_res_type, Z_NO_ENTRY, B_TRUE); return (B_FALSE); @@ -3123,7 +3360,7 @@ prompt_remove_resource(cmd_t *cmd, char *rsrc) } static void -remove_fs(cmd_t *cmd) +remove_fs(cmd_t *cmd, boolean_t force) { int err; @@ -3132,13 +3369,16 @@ remove_fs(cmd_t *cmd) struct zone_fstab fstab; if ((err = fill_in_fstab(cmd, &fstab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, B_TRUE); return; } - if ((err = zonecfg_delete_filesystem(handle, &fstab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, B_TRUE); - else + if ((err = zonecfg_delete_filesystem(handle, &fstab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_FS, err, B_TRUE); + } else { need_to_commit = B_TRUE; + } zonecfg_free_fs_option_list(fstab.zone_fs_options); return; } @@ -3157,7 +3397,7 @@ remove_fs(cmd_t *cmd) } static void -remove_net(cmd_t *cmd) +remove_net(cmd_t *cmd, boolean_t force) { int err; @@ -3166,13 +3406,18 @@ remove_net(cmd_t *cmd) struct zone_nwiftab nwiftab; if ((err = fill_in_nwiftab(cmd, &nwiftab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, + B_TRUE); return; } - if ((err = zonecfg_delete_nwif(handle, &nwiftab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, B_TRUE); - else + if ((err = zonecfg_delete_nwif(handle, &nwiftab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_NET, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } return; } @@ -3190,7 +3435,7 @@ remove_net(cmd_t *cmd) } static void -remove_device(cmd_t *cmd) +remove_device(cmd_t *cmd, boolean_t force) { int err; @@ -3199,13 +3444,18 @@ remove_device(cmd_t *cmd) struct zone_devtab devtab; if ((err = fill_in_devtab(cmd, &devtab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, + B_TRUE); return; } - if ((err = zonecfg_delete_dev(handle, &devtab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, B_TRUE); - else + if ((err = zonecfg_delete_dev(handle, &devtab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DEVICE, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } return; } @@ -3223,7 +3473,7 @@ remove_device(cmd_t *cmd) } static void -remove_attr(cmd_t *cmd) +remove_attr(cmd_t *cmd, boolean_t force) { int err; @@ -3232,13 +3482,18 @@ remove_attr(cmd_t *cmd) struct zone_attrtab attrtab; if ((err = fill_in_attrtab(cmd, &attrtab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, + B_TRUE); return; } - if ((err = zonecfg_delete_attr(handle, &attrtab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, B_TRUE); - else + if ((err = zonecfg_delete_attr(handle, &attrtab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_ATTR, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } return; } @@ -3256,7 +3511,7 @@ remove_attr(cmd_t *cmd) } static void -remove_dataset(cmd_t *cmd) +remove_dataset(cmd_t *cmd, boolean_t force) { int err; @@ -3265,13 +3520,18 @@ remove_dataset(cmd_t *cmd) struct zone_dstab dstab; if ((err = fill_in_dstab(cmd, &dstab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, + B_TRUE); return; } - if ((err = zonecfg_delete_ds(handle, &dstab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, B_TRUE); - else + if ((err = zonecfg_delete_ds(handle, &dstab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DATASET, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } return; } @@ -3289,7 +3549,7 @@ remove_dataset(cmd_t *cmd) } static void -remove_rctl(cmd_t *cmd) +remove_rctl(cmd_t *cmd, boolean_t force) { int err; @@ -3298,13 +3558,18 @@ remove_rctl(cmd_t *cmd) struct zone_rctltab rctltab; if ((err = fill_in_rctltab(cmd, &rctltab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, + B_TRUE); return; } - if ((err = zonecfg_delete_rctl(handle, &rctltab)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, B_TRUE); - else + if ((err = zonecfg_delete_rctl(handle, &rctltab)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_RCTL, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } zonecfg_free_rctl_value_list(rctltab.zone_rctl_valptr); return; } @@ -3323,72 +3588,90 @@ remove_rctl(cmd_t *cmd) } static void -remove_pset() +remove_pset(boolean_t force) { int err; struct zone_psettab psettab; if ((err = zonecfg_lookup_pset(handle, &psettab)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_DCPU, err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DCPU, err, B_TRUE); return; } - if ((err = zonecfg_delete_pset(handle)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_DCPU, err, B_TRUE); - else + if ((err = zonecfg_delete_pset(handle)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_DCPU, err, B_TRUE); + } else { need_to_commit = B_TRUE; + } } static void -remove_pcap() +remove_pcap(boolean_t force) { int err; uint64_t tmp; if (zonecfg_get_aliased_rctl(handle, ALIAS_CPUCAP, &tmp) != Z_OK) { - zerr("%s %s: %s", cmd_to_str(CMD_REMOVE), rt_to_str(RT_PCAP), - zonecfg_strerror(Z_NO_RESOURCE_TYPE)); - saw_error = B_TRUE; + if (!force) { + zerr("%s %s: %s", cmd_to_str(CMD_REMOVE), + rt_to_str(RT_PCAP), + zonecfg_strerror(Z_NO_RESOURCE_TYPE)); + saw_error = B_TRUE; + } return; } - if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_CPUCAP)) != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_PCAP, err, B_TRUE); - else + if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_CPUCAP)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_PCAP, err, B_TRUE); + } else { need_to_commit = B_TRUE; + } } static void -remove_mcap() +remove_mcap(boolean_t force) { int err, res1, res2, res3; uint64_t tmp; - struct zone_mcaptab mcaptab; boolean_t revert = B_FALSE; - res1 = zonecfg_lookup_mcap(handle, &mcaptab); + res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &tmp); res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &tmp); res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &tmp); /* if none of these exist, there is no resource to remove */ if (res1 != Z_OK && res2 != Z_OK && res3 != Z_OK) { - zerr("%s %s: %s", cmd_to_str(CMD_REMOVE), rt_to_str(RT_MCAP), - zonecfg_strerror(Z_NO_RESOURCE_TYPE)); - saw_error = B_TRUE; + if (!force) { + zerr("%s %s: %s", cmd_to_str(CMD_REMOVE), + rt_to_str(RT_MCAP), + zonecfg_strerror(Z_NO_RESOURCE_TYPE)); + saw_error = B_TRUE; + } return; } if (res1 == Z_OK) { - if ((err = zonecfg_delete_mcap(handle)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, B_TRUE); - revert = B_TRUE; + if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXPHYSMEM)) + != Z_OK) { + if (!force) { + z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, + B_TRUE); + revert = B_TRUE; + } } else { need_to_commit = B_TRUE; } } + if (res2 == Z_OK) { if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXSWAP)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, B_TRUE); - revert = B_TRUE; + if (!force) { + z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, + B_TRUE); + revert = B_TRUE; + } } else { need_to_commit = B_TRUE; } @@ -3396,8 +3679,11 @@ remove_mcap() if (res3 == Z_OK) { if ((err = zonecfg_rm_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, B_TRUE); - revert = B_TRUE; + if (!force) { + z_cmd_rt_perror(CMD_REMOVE, RT_MCAP, err, + B_TRUE); + revert = B_TRUE; + } } else { need_to_commit = B_TRUE; } @@ -3408,7 +3694,7 @@ remove_mcap() } static void -remove_admin(cmd_t *cmd) +remove_admin(cmd_t *cmd, boolean_t force) { int err; @@ -3417,34 +3703,33 @@ remove_admin(cmd_t *cmd) struct zone_admintab admintab; if ((err = fill_in_admintab(cmd, &admintab, B_FALSE)) != Z_OK) { - z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, - err, B_TRUE); + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, err, + B_TRUE); return; } if ((err = zonecfg_delete_admin(handle, &admintab, - zone)) - != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, - err, B_TRUE); - else + zone)) != Z_OK) { + if (!force) + z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, err, + B_TRUE); + } else { need_to_commit = B_TRUE; + } return; - } else { - /* - * unqualified admin removal. - * remove all admins but prompt if more - * than one. - */ - if (!prompt_remove_resource(cmd, "admin")) - return; - - if ((err = zonecfg_delete_admins(handle, zone)) - != Z_OK) - z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, - err, B_TRUE); - else - need_to_commit = B_TRUE; } + + /* + * unqualified admin removal. + * remove all admins but prompt if more than one. + */ + if (!prompt_remove_resource(cmd, "admin")) + return; + + if ((err = zonecfg_delete_admins(handle, zone)) != Z_OK) + z_cmd_rt_perror(CMD_REMOVE, RT_ADMIN, err, B_TRUE); + else + need_to_commit = B_TRUE; } static void @@ -3474,6 +3759,7 @@ remove_resource(cmd_t *cmd) int type; int arg; boolean_t arg_err = B_FALSE; + boolean_t force = B_FALSE; if ((type = cmd->cmd_res_type) == RT_UNKNOWN) { long_usage(CMD_REMOVE, B_TRUE); @@ -3488,6 +3774,7 @@ remove_resource(cmd_t *cmd) arg_err = B_TRUE; break; case 'F': + force = B_TRUE; break; default: short_usage(CMD_REMOVE); @@ -3503,34 +3790,34 @@ remove_resource(cmd_t *cmd) switch (type) { case RT_FS: - remove_fs(cmd); + remove_fs(cmd, force); return; case RT_NET: - remove_net(cmd); + remove_net(cmd, force); return; case RT_DEVICE: - remove_device(cmd); + remove_device(cmd, force); return; case RT_RCTL: - remove_rctl(cmd); + remove_rctl(cmd, force); return; case RT_ATTR: - remove_attr(cmd); + remove_attr(cmd, force); return; case RT_DATASET: - remove_dataset(cmd); + remove_dataset(cmd, force); return; case RT_DCPU: - remove_pset(); + remove_pset(force); return; case RT_PCAP: - remove_pcap(); + remove_pcap(force); return; case RT_MCAP: - remove_mcap(); + remove_mcap(force); return; case RT_ADMIN: - remove_admin(cmd); + remove_admin(cmd, force); return; case RT_SECFLAGS: remove_secflags(); @@ -3550,7 +3837,27 @@ remove_property(cmd_t *cmd) int err, res_type, prop_type; property_value_ptr_t pp; struct zone_rctlvaltab *rctlvaltab; + struct zone_res_attrtab *np; complex_property_ptr_t cx; + int arg; + boolean_t force = B_FALSE; + boolean_t arg_err = B_FALSE; + + optind = 0; + while ((arg = getopt(cmd->cmd_argc, cmd->cmd_argv, "F")) != EOF) { + switch (arg) { + case 'F': + force = B_TRUE; + break; + default: + arg_err = B_TRUE; + break; + } + } + if (arg_err) { + saw_error = B_TRUE; + return; + } res_type = resource_scope; prop_type = cmd->cmd_prop_name[0]; @@ -3592,7 +3899,7 @@ remove_property(cmd_t *cmd) prop_id = pp->pv_simple; err = zonecfg_remove_fs_option(&in_progress_fstab, prop_id); - if (err != Z_OK) + if (err != Z_OK && !force) zone_perror(pt_to_str(prop_type), err, B_TRUE); } else { list_property_ptr_t list; @@ -3604,12 +3911,62 @@ remove_property(cmd_t *cmd) break; err = zonecfg_remove_fs_option( &in_progress_fstab, prop_id); - if (err != Z_OK) + if (err != Z_OK && !force) zone_perror(pt_to_str(prop_type), err, B_TRUE); } } return; + case RT_NET: /* FALLTHRU */ + case RT_DEVICE: + if (prop_type != PT_NPROP) { + zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, + B_TRUE); + long_usage(CMD_REMOVE, B_TRUE); + usage(B_FALSE, HELP_PROPS); + return; + } + pp = cmd->cmd_property_ptr[0]; + if (pp->pv_type != PROP_VAL_COMPLEX) { + zerr(gettext("A %s value was expected here."), + pvt_to_str(PROP_VAL_COMPLEX)); + saw_error = B_TRUE; + return; + } + + np = alloca(sizeof (struct zone_res_attrtab)); + for (cx = pp->pv_complex; cx != NULL; cx = cx->cp_next) { + switch (cx->cp_type) { + case PT_NAME: + (void) strlcpy(np->zone_res_attr_name, + cx->cp_value, + sizeof (np->zone_res_attr_name)); + break; + case PT_VALUE: + (void) strlcpy(np->zone_res_attr_value, + cx->cp_value, + sizeof (np->zone_res_attr_value)); + break; + default: + zone_perror(pt_to_str(prop_type), + Z_NO_PROPERTY_TYPE, B_TRUE); + long_usage(CMD_REMOVE, B_TRUE); + usage(B_FALSE, HELP_PROPS); + return; + } + } + np->zone_res_attr_next = NULL; + + if (res_type == RT_NET) { + err = zonecfg_remove_res_attr( + &(in_progress_nwiftab.zone_nwif_attrp), np); + } else { /* RT_DEVICE */ + err = zonecfg_remove_res_attr( + &(in_progress_devtab.zone_dev_attrp), np); + } + if (err != Z_OK && !force) + zone_perror(pt_to_str(prop_type), err, B_TRUE); + return; case RT_RCTL: if (prop_type != PT_VALUE) { zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, @@ -3658,22 +4015,10 @@ remove_property(cmd_t *cmd) rctlvaltab->zone_rctlval_next = NULL; err = zonecfg_remove_rctl_value(&in_progress_rctltab, rctlvaltab); - if (err != Z_OK) + if (err != Z_OK && !force) zone_perror(pt_to_str(prop_type), err, B_TRUE); zonecfg_free_rctl_value_list(rctlvaltab); return; - case RT_NET: - if (prop_type != PT_DEFROUTER) { - zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, - B_TRUE); - long_usage(CMD_REMOVE, B_TRUE); - usage(B_FALSE, HELP_PROPS); - return; - } else { - bzero(&in_progress_nwiftab.zone_nwif_defrouter, - sizeof (in_progress_nwiftab.zone_nwif_defrouter)); - return; - } default: zone_perror(rt_to_str(res_type), Z_NO_RESOURCE_TYPE, B_TRUE); long_usage(CMD_REMOVE, B_TRUE); @@ -3736,8 +4081,7 @@ clear_property(cmd_t *cmd) case RT_MCAP: switch (prop_type) { case PT_PHYSICAL: - in_progress_mcaptab.zone_physmem_cap[0] = '\0'; - need_to_commit = B_TRUE; + remove_aliased_rctl(PT_PHYSICAL, ALIAS_MAXPHYSMEM); return; case PT_SWAP: remove_aliased_rctl(PT_SWAP, ALIAS_MAXSWAP); @@ -3747,6 +4091,30 @@ clear_property(cmd_t *cmd) return; } break; + case RT_NET: + switch (prop_type) { + case PT_ALLOWED_ADDRESS: + in_progress_nwiftab.zone_nwif_allowed_address[0] = '\0'; + need_to_commit = B_TRUE; + return; + case PT_DEFROUTER: + in_progress_nwiftab.zone_nwif_defrouter[0] = '\0'; + need_to_commit = B_TRUE; + return; + case PT_GNIC: + in_progress_nwiftab.zone_nwif_gnic[0] = '\0'; + need_to_commit = B_TRUE; + return; + case PT_MAC: + in_progress_nwiftab.zone_nwif_mac[0] = '\0'; + need_to_commit = B_TRUE; + return; + case PT_VLANID: + in_progress_nwiftab.zone_nwif_vlan_id[0] = '\0'; + need_to_commit = B_TRUE; + return; + } + break; case RT_SECFLAGS: switch (prop_type) { case PT_LOWER: @@ -3788,6 +4156,8 @@ clear_global(cmd_t *cmd) /* FALLTHRU */ case PT_ZONEPATH: /* FALLTHRU */ + case PT_UUID: + /* FALLTHRU */ case PT_BRAND: zone_perror(pt_to_str(type), Z_CLEAR_DISALLOW, B_TRUE); return; @@ -3850,6 +4220,9 @@ clear_global(cmd_t *cmd) case PT_SHARES: remove_aliased_rctl(PT_SHARES, ALIAS_SHARES); return; + case PT_ZFSPRI: + remove_aliased_rctl(PT_ZFSPRI, ALIAS_ZFSPRI); + return; case PT_HOSTID: if ((err = zonecfg_set_hostid(handle, NULL)) != Z_OK) z_cmd_rt_perror(CMD_CLEAR, RT_HOSTID, err, B_TRUE); @@ -3895,7 +4268,7 @@ clear_func(cmd_t *cmd) void select_func(cmd_t *cmd) { - int type, err, res; + int type, err; uint64_t limit; uint64_t tmp; @@ -3990,7 +4363,8 @@ select_func(cmd_t *cmd) return; case RT_MCAP: /* if none of these exist, there is no resource to select */ - if ((res = zonecfg_lookup_mcap(handle, &old_mcaptab)) != Z_OK && + if (zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &limit) + != Z_OK && zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &limit) != Z_OK && zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &limit) @@ -3999,12 +4373,6 @@ select_func(cmd_t *cmd) B_TRUE); global_scope = B_TRUE; } - if (res == Z_OK) - bcopy(&old_mcaptab, &in_progress_mcaptab, - sizeof (struct zone_mcaptab)); - else - bzero(&in_progress_mcaptab, - sizeof (in_progress_mcaptab)); return; case RT_ADMIN: if ((err = fill_in_admintab(cmd, &old_admintab, B_FALSE)) @@ -4281,9 +4649,8 @@ set_func(cmd_t *cmd) boolean_t autoboot; zone_iptype_t iptype; boolean_t force_set = B_FALSE; - size_t physmem_size = sizeof (in_progress_mcaptab.zone_physmem_cap); uint64_t mem_cap, mem_limit; - float cap; + double cap; char *unitp; struct zone_psettab tmp_psettab; boolean_t arg_err = B_FALSE; @@ -4356,6 +4723,10 @@ set_func(cmd_t *cmd) res_type = RT_HOSTID; } else if (prop_type == PT_FS_ALLOWED) { res_type = RT_FS_ALLOWED; + } else if (prop_type == PT_ZFSPRI) { + res_type = RT_ZFSPRI; + } else if (prop_type == PT_UUID) { + res_type = RT_UUID; } else { zerr(gettext("Cannot set a resource-specific property " "from the global scope.")); @@ -4385,10 +4756,12 @@ set_func(cmd_t *cmd) * A nasty expression but not that complicated: * 1. fs options are simple or list (tested below) * 2. rctl value's are complex or list (tested below) + * 3. net attr's are complex (tested below) * Anything else should be simple. */ if (!(res_type == RT_FS && prop_type == PT_OPTIONS) && !(res_type == RT_RCTL && prop_type == PT_VALUE) && + !(res_type == RT_NET && prop_type == PT_NPROP) && (pp->pv_type != PROP_VAL_SIMPLE || (prop_id = pp->pv_simple) == NULL)) { zerr(gettext("A %s value was expected here."), @@ -4561,6 +4934,9 @@ set_func(cmd_t *cmd) case RT_SHARES: set_aliased_rctl(ALIAS_SHARES, prop_type, prop_id); return; + case RT_ZFSPRI: + set_aliased_rctl(ALIAS_ZFSPRI, prop_type, prop_id); + return; case RT_HOSTID: if ((err = zonecfg_set_hostid(handle, prop_id)) != Z_OK) { if (err == Z_TOO_BIG) { @@ -4574,6 +4950,15 @@ set_func(cmd_t *cmd) } need_to_commit = B_TRUE; return; + case RT_UUID: + /* + * We can't set here. We have to wait until commit since the + * uuid will be updating the index file and we may not have + * created the zone yet. + */ + (void) strlcpy(new_uuid, prop_id, sizeof (new_uuid)); + need_to_commit = B_TRUE; + return; case RT_FS_ALLOWED: if ((err = zonecfg_set_fs_allowed(handle, prop_id)) != Z_OK) zone_perror(zone, err, B_TRUE); @@ -4648,6 +5033,21 @@ set_func(cmd_t *cmd) prop_id, sizeof (in_progress_nwiftab.zone_nwif_physical)); break; + case PT_MAC: + normalize_mac_addr(in_progress_nwiftab.zone_nwif_mac, + prop_id, + sizeof (in_progress_nwiftab.zone_nwif_mac)); + break; + case PT_VLANID: + (void) strlcpy(in_progress_nwiftab.zone_nwif_vlan_id, + prop_id, + sizeof (in_progress_nwiftab.zone_nwif_vlan_id)); + break; + case PT_GNIC: + (void) strlcpy(in_progress_nwiftab.zone_nwif_gnic, + prop_id, + sizeof (in_progress_nwiftab.zone_nwif_gnic)); + break; case PT_DEFROUTER: if (validate_net_address_syntax(prop_id, B_TRUE) != Z_OK) { @@ -4658,6 +5058,20 @@ set_func(cmd_t *cmd) prop_id, sizeof (in_progress_nwiftab.zone_nwif_defrouter)); break; + case PT_NPROP: + if (pp->pv_type != PROP_VAL_COMPLEX) { + zerr(gettext("A %s value was expected here."), + pvt_to_str(PROP_VAL_COMPLEX)); + saw_error = B_TRUE; + return; + } + zonecfg_free_res_attr_list( + in_progress_nwiftab.zone_nwif_attrp); + in_progress_nwiftab.zone_nwif_attrp = NULL; + if (!(pp->pv_type == PROP_VAL_LIST && + pp->pv_list == NULL)) + add_property(cmd); + break; default: zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, B_TRUE); @@ -4673,6 +5087,20 @@ set_func(cmd_t *cmd) prop_id, sizeof (in_progress_devtab.zone_dev_match)); break; + case PT_NPROP: + if (pp->pv_type != PROP_VAL_COMPLEX) { + zerr(gettext("A %s value was expected here."), + pvt_to_str(PROP_VAL_COMPLEX)); + saw_error = B_TRUE; + return; + } + zonecfg_free_res_attr_list( + in_progress_devtab.zone_dev_attrp); + in_progress_devtab.zone_dev_attrp = NULL; + if (!(pp->pv_type == PROP_VAL_LIST && + pp->pv_list == NULL)) + add_property(cmd); + break; default: zone_perror(pt_to_str(prop_type), Z_NO_PROPERTY_TYPE, B_TRUE); @@ -4816,35 +5244,50 @@ set_func(cmd_t *cmd) * the add_resource() function. */ - if ((cap = strtof(prop_id, &unitp)) <= 0 || *unitp != '\0' || - (int)(cap * 100) < 1) { + if ((cap = strtod(prop_id, &unitp)) <= 0 || *unitp != '\0' || + (cap * 100.0) < 1) { zerr(gettext("%s property is out of range."), pt_to_str(PT_NCPUS)); saw_error = B_TRUE; return; } + cap *= 100.0; + /* To avoid rounding issues add .5 to force correct value. */ if ((err = zonecfg_set_aliased_rctl(handle, ALIAS_CPUCAP, - (int)(cap * 100))) != Z_OK) + (uint_t)(cap + 0.5))) != Z_OK) { zone_perror(zone, err, B_TRUE); - else + } else { need_to_commit = B_TRUE; + } return; case RT_MCAP: switch (prop_type) { case PT_PHYSICAL: + /* + * We have to check if an rctl is allowed here since + * there might already be a rctl defined that blocks + * the alias. + */ + if (!zonecfg_aliased_rctl_ok(handle, + ALIAS_MAXPHYSMEM)) { + zone_perror(pt_to_str(PT_LOCKED), + Z_ALIAS_DISALLOW, B_FALSE); + saw_error = B_TRUE; + return; + } + if (!zonecfg_valid_memlimit(prop_id, &mem_cap)) { - zerr(gettext("A positive number with a " + zerr(gettext("A non-negative number with a " "required scale suffix (K, M, G or T) was " - "expected here.")); - saw_error = B_TRUE; - } else if (mem_cap < ONE_MB) { - zerr(gettext("%s value is too small. It must " - "be at least 1M."), pt_to_str(PT_PHYSICAL)); + "expected\nhere.")); saw_error = B_TRUE; } else { - snprintf(in_progress_mcaptab.zone_physmem_cap, - physmem_size, "%llu", mem_cap); + if ((err = zonecfg_set_aliased_rctl(handle, + ALIAS_MAXPHYSMEM, mem_cap)) != Z_OK) + zone_perror(zone, err, B_TRUE); + else + need_to_commit = B_TRUE; } break; case PT_SWAP: @@ -5139,6 +5582,23 @@ info_hostid(zone_dochandle_t handle, FILE *fp) } static void +info_uuid(FILE *fp) +{ + uuid_t uuid; + char suuid[UUID_PRINTABLE_STRING_LENGTH]; + + if (new_uuid[0] != '\0') { + (void) fprintf(fp, "%s: %s\n", pt_to_str(PT_UUID), new_uuid); + } else if (zonecfg_get_uuid(zone, uuid) == Z_OK && + !uuid_is_null(uuid)) { + uuid_unparse(uuid, suuid); + (void) fprintf(fp, "%s: %s\n", pt_to_str(PT_UUID), suuid); + } else { + (void) fprintf(fp, "%s:\n", pt_to_str(PT_UUID)); + } +} + +static void info_fs_allowed(zone_dochandle_t handle, FILE *fp) { char fsallowedp[ZONE_FS_ALLOWED_MAX]; @@ -5220,12 +5680,25 @@ loopend: static void output_net(FILE *fp, struct zone_nwiftab *nwiftab) { + struct zone_res_attrtab *np; + (void) fprintf(fp, "%s:\n", rt_to_str(RT_NET)); output_prop(fp, PT_ADDRESS, nwiftab->zone_nwif_address, B_TRUE); output_prop(fp, PT_ALLOWED_ADDRESS, nwiftab->zone_nwif_allowed_address, B_TRUE); - output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE); output_prop(fp, PT_DEFROUTER, nwiftab->zone_nwif_defrouter, B_TRUE); + output_prop(fp, PT_GNIC, nwiftab->zone_nwif_gnic, B_TRUE); + output_prop(fp, PT_MAC, nwiftab->zone_nwif_mac, B_TRUE); + output_prop(fp, PT_PHYSICAL, nwiftab->zone_nwif_physical, B_TRUE); + output_prop(fp, PT_VLANID, nwiftab->zone_nwif_vlan_id, B_TRUE); + + for (np = nwiftab->zone_nwif_attrp; np != NULL; + np = np->zone_res_attr_next) { + fprintf(fp, "\t%s: (%s=%s,%s=\"%s\")\n", + pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), np->zone_res_attr_name, + pt_to_str(PT_VALUE), np->zone_res_attr_value); + } } static void @@ -5268,8 +5741,18 @@ info_net(zone_dochandle_t handle, FILE *fp, cmd_t *cmd) static void output_dev(FILE *fp, struct zone_devtab *devtab) { + struct zone_res_attrtab *np; + (void) fprintf(fp, "%s:\n", rt_to_str(RT_DEVICE)); output_prop(fp, PT_MATCH, devtab->zone_dev_match, B_TRUE); + + for (np = devtab->zone_dev_attrp; np != NULL; + np = np->zone_res_attr_next) { + fprintf(fp, "\t%s: (%s=%s,%s=\"%s\")\n", + pt_to_str(PT_NPROP), + pt_to_str(PT_NAME), np->zone_res_attr_name, + pt_to_str(PT_VALUE), np->zone_res_attr_value); + } } static void @@ -5528,15 +6011,18 @@ bytes_to_units(char *str, char *buf, int bufsize) } static void -output_mcap(FILE *fp, struct zone_mcaptab *mcaptab, int showswap, +output_mcap(FILE *fp, int showphys, uint64_t maxphys, int showswap, uint64_t maxswap, int showlocked, uint64_t maxlocked) { char buf[128]; (void) fprintf(fp, "%s:\n", rt_to_str(RT_MCAP)); - if (mcaptab->zone_physmem_cap[0] != '\0') { - bytes_to_units(mcaptab->zone_physmem_cap, buf, sizeof (buf)); - output_prop(fp, PT_PHYSICAL, buf, B_TRUE); + + if (showphys == Z_OK) { + (void) snprintf(buf, sizeof (buf), "%llu", maxphys); + bytes_to_units(buf, buf, sizeof (buf)); + /* Print directly since "physical" also is a net property. */ + (void) fprintf(fp, "\t[%s: %s]\n", pt_to_str(PT_PHYSICAL), buf); } if (showswap == Z_OK) { @@ -5558,16 +6044,16 @@ info_mcap(zone_dochandle_t handle, FILE *fp) int res1, res2, res3; uint64_t swap_limit; uint64_t locked_limit; - struct zone_mcaptab lookup; + uint64_t phys_limit; - bzero(&lookup, sizeof (lookup)); - res1 = zonecfg_getmcapent(handle, &lookup); + res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, &phys_limit); res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &swap_limit); res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &locked_limit); if (res1 == Z_OK || res2 == Z_OK || res3 == Z_OK) - output_mcap(fp, &lookup, res2, swap_limit, res3, locked_limit); + output_mcap(fp, res1, phys_limit, res2, swap_limit, + res3, locked_limit); } static void @@ -5637,9 +6123,11 @@ info_func(cmd_t *cmd) FILE *fp = stdout; boolean_t need_to_close = B_FALSE; int type; - int res1, res2; + int res1, res2, res3; uint64_t swap_limit; uint64_t locked_limit; + uint64_t phys_limit; + struct stat statbuf; assert(cmd != NULL); @@ -5687,7 +6175,9 @@ info_func(cmd_t *cmd) &swap_limit); res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, &locked_limit); - output_mcap(fp, &in_progress_mcaptab, res1, swap_limit, + res3 = zonecfg_get_aliased_rctl(handle, + ALIAS_MAXPHYSMEM, &phys_limit); + output_mcap(fp, res3, phys_limit, res1, swap_limit, res2, locked_limit); break; case RT_ADMIN: @@ -5730,6 +6220,7 @@ info_func(cmd_t *cmd) info_iptype(handle, fp); info_hostid(handle, fp); info_fs_allowed(handle, fp); + info_uuid(fp); } info_aliased_rctl(handle, fp, ALIAS_MAXLWPS); info_aliased_rctl(handle, fp, ALIAS_MAXPROCS); @@ -5738,6 +6229,7 @@ info_func(cmd_t *cmd) info_aliased_rctl(handle, fp, ALIAS_MAXMSGIDS); info_aliased_rctl(handle, fp, ALIAS_MAXSEMIDS); info_aliased_rctl(handle, fp, ALIAS_SHARES); + info_aliased_rctl(handle, fp, ALIAS_ZFSPRI); if (!global_zone) { info_fs(handle, fp, cmd); info_net(handle, fp, cmd); @@ -5802,6 +6294,9 @@ info_func(cmd_t *cmd) case RT_SHARES: info_aliased_rctl(handle, fp, ALIAS_SHARES); break; + case RT_ZFSPRI: + info_aliased_rctl(handle, fp, ALIAS_ZFSPRI); + break; case RT_FS: info_fs(handle, fp, cmd); break; @@ -5832,6 +6327,9 @@ info_func(cmd_t *cmd) case RT_HOSTID: info_hostid(handle, fp); break; + case RT_UUID: + info_uuid(fp); + break; case RT_ADMIN: info_auth(handle, fp, cmd); break; @@ -6410,10 +6908,33 @@ verify_func(cmd_t *cmd) if (save) { if (ret_val == Z_OK) { + /* + * If the zone doesn't yet have a debug ID, set one now. + */ + if (zonecfg_get_did(handle) == -1) + zonecfg_set_did(handle); + if ((ret_val = zonecfg_save(handle)) == Z_OK) { need_to_commit = B_FALSE; (void) strlcpy(revert_zone, zone, sizeof (revert_zone)); + + if (is_create) { + zonecfg_notify_create(handle); + is_create = B_FALSE; + } + } + + /* + * Commit a new uuid at this point since we now know the + * zone index entry will exist. + */ + if (new_uuid[0] != '\0') { + if ((err = zonecfg_set_uuid(zone, zonepath, + new_uuid)) != Z_OK) + zone_perror(zone, err, B_FALSE); + else + new_uuid[0] = '\0'; } } else { zerr(gettext("Zone %s failed to verify"), zone); @@ -6584,6 +7105,7 @@ end_func(cmd_t *cmd) int err, arg, res1, res2, res3; uint64_t swap_limit; uint64_t locked_limit; + uint64_t phys_limit; uint64_t proc_cap; assert(cmd != NULL); @@ -6887,8 +7409,8 @@ end_func(cmd_t *cmd) break; case RT_MCAP: /* Make sure everything was filled in. */ - res1 = strlen(in_progress_mcaptab.zone_physmem_cap) == 0 ? - Z_ERR : Z_OK; + res1 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXPHYSMEM, + &phys_limit); res2 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXSWAP, &swap_limit); res3 = zonecfg_get_aliased_rctl(handle, ALIAS_MAXLOCKEDMEM, @@ -6904,11 +7426,6 @@ end_func(cmd_t *cmd) /* if phys & locked are both set, verify locked <= phys */ if (res1 == Z_OK && res3 == Z_OK) { - uint64_t phys_limit; - char *endp; - - phys_limit = strtoull( - in_progress_mcaptab.zone_physmem_cap, &endp, 10); if (phys_limit < locked_limit) { zerr(gettext("The %s cap must be less than or " "equal to the %s cap."), @@ -6920,23 +7437,6 @@ end_func(cmd_t *cmd) } err = Z_OK; - if (res1 == Z_OK) { - /* - * We could be ending from either an add operation - * or a select operation. Since all of the properties - * within this resource are optional, we always use - * modify on the mcap entry. zonecfg_modify_mcap() - * will handle both adding and modifying a memory cap. - */ - err = zonecfg_modify_mcap(handle, &in_progress_mcaptab); - } else if (end_op == CMD_SELECT) { - /* - * If we're ending from a select and the physical - * memory cap is empty then the user could have cleared - * the physical cap value, so try to delete the entry. - */ - (void) zonecfg_delete_mcap(handle); - } break; case RT_ADMIN: /* First make sure everything was filled in. */ @@ -7499,8 +7999,10 @@ get_execbasename(char *execfullname) int main(int argc, char *argv[]) { - int err, arg; + int err, arg, uflag = 0, zflag = 0; struct stat st; + uuid_t uuidin; + char zonename[ZONENAME_MAX + 1]; /* This must be before anything goes to stdout. */ setbuf(stdout, NULL); @@ -7527,7 +8029,7 @@ main(int argc, char *argv[]) exit(Z_OK); } - while ((arg = getopt(argc, argv, "?f:R:z:")) != EOF) { + while ((arg = getopt(argc, argv, "?f:R:z:u:")) != EOF) { switch (arg) { case '?': if (optopt == '?') @@ -7554,6 +8056,21 @@ main(int argc, char *argv[]) } zonecfg_set_root(optarg); break; + case 'u': + if (uuid_parse((char *)optarg, uuidin) == -1) + return (Z_INVALID_PROPERTY); + + if (zonecfg_get_name_by_uuid(uuidin, zonename, + ZONENAME_MAX) != Z_OK) { + zone_perror(optarg, Z_BOGUS_ZONE_NAME, B_TRUE); + usage(B_FALSE, HELP_SYNTAX); + exit(Z_USAGE); + } + + (void) strlcpy(zone, zonename, sizeof (zone)); + (void) strlcpy(revert_zone, zonename, sizeof (zone)); + uflag = 1; + break; case 'z': if (strcmp(optarg, GLOBAL_ZONENAME) == 0) { global_zone = B_TRUE; @@ -7564,6 +8081,7 @@ main(int argc, char *argv[]) } (void) strlcpy(zone, optarg, sizeof (zone)); (void) strlcpy(revert_zone, optarg, sizeof (zone)); + zflag = 1; break; default: usage(B_FALSE, HELP_USAGE); @@ -7571,7 +8089,7 @@ main(int argc, char *argv[]) } } - if (optind > argc || strcmp(zone, "") == 0) { + if (optind > argc || strcmp(zone, "") == 0 || (uflag && zflag)) { usage(B_FALSE, HELP_USAGE); exit(Z_USAGE); } diff --git a/usr/src/cmd/zonecfg/zonecfg.h b/usr/src/cmd/zonecfg/zonecfg.h index 108d0ce507..e4ae4d4d61 100644 --- a/usr/src/cmd/zonecfg/zonecfg.h +++ b/usr/src/cmd/zonecfg/zonecfg.h @@ -21,6 +21,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent Inc. All rights reserved. */ #ifndef _ZONECFG_H @@ -90,7 +91,9 @@ extern "C" { #define RT_ADMIN 26 #define RT_FS_ALLOWED 27 #define RT_MAXPROCS 28 /* really a rctl alias property, but for info */ -#define RT_SECFLAGS 29 +#define RT_ZFSPRI 29 /* really a rctl alias property, but for info */ +#define RT_UUID 30 /* really a property, but for info */ +#define RT_SECFLAGS 31 #define RT_MIN RT_UNKNOWN #define RT_MAX RT_SECFLAGS @@ -138,9 +141,15 @@ extern "C" { #define PT_FS_ALLOWED 39 #define PT_MAXPROCS 40 #define PT_ALLOWED_ADDRESS 41 -#define PT_DEFAULT 42 -#define PT_LOWER 43 -#define PT_UPPER 44 +#define PT_ZFSPRI 42 +#define PT_MAC 43 +#define PT_VLANID 44 +#define PT_GNIC 45 +#define PT_NPROP 46 +#define PT_UUID 47 +#define PT_DEFAULT 48 +#define PT_LOWER 49 +#define PT_UPPER 50 #define PT_MIN PT_UNKNOWN #define PT_MAX PT_UPPER diff --git a/usr/src/cmd/zonecfg/zonecfg_grammar.y b/usr/src/cmd/zonecfg/zonecfg_grammar.y index 2e950512ec..d8b2fadb2f 100644 --- a/usr/src/cmd/zonecfg/zonecfg_grammar.y +++ b/usr/src/cmd/zonecfg/zonecfg_grammar.y @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2013, Joyent Inc. All rights reserved. */ /* @@ -135,7 +136,8 @@ complex_piece_func(int cp_type, const char *str, complex_property_ptr_t cp_next) %token NAME MATCH PRIV LIMIT ACTION VALUE EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET %token OPEN_PAREN CLOSE_PAREN COMMA DATASET LIMITPRIV BOOTARGS BRAND PSET PCAP %token MCAP NCPUS IMPORTANCE SHARES MAXLWPS MAXSHMMEM MAXSHMIDS MAXMSGIDS -%token MAXSEMIDS LOCKED SWAP SCHED CLEAR DEFROUTER ADMIN SECFLAGS USER AUTHS MAXPROCS +%token MAXSEMIDS LOCKED SWAP SCHED CLEAR DEFROUTER ADMIN USER AUTHS MAXPROCS +%token ZFSPRI MAC VLANID GNIC NPROP UUID SECFLAGS %token DEFAULT UPPER LOWER %type <strval> TOKEN EQUAL OPEN_SQ_BRACKET CLOSE_SQ_BRACKET @@ -146,7 +148,7 @@ complex_piece_func(int cp_type, const char *str, complex_property_ptr_t cp_next) %type <ival> property_name SPECIAL RAW DIR OPTIONS TYPE ADDRESS PHYSICAL NAME MATCH ZONENAME ZONEPATH AUTOBOOT POOL LIMITPRIV BOOTARGS VALUE PRIV LIMIT ACTION BRAND SCHED IPTYPE DEFROUTER HOSTID USER AUTHS FS_ALLOWED - ALLOWED_ADDRESS DEFAULT UPPER LOWER + ALLOWED_ADDRESS MAC VLANID GNIC NPROP UUID DEFAULT UPPER LOWER %type <cmd> command %type <cmd> add_command ADD %type <cmd> cancel_command CANCEL @@ -651,6 +653,24 @@ info_command: INFO $$->cmd_res_type = RT_FS_ALLOWED; $$->cmd_prop_nv_pairs = 0; } + | INFO UUID + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &info_func; + $$->cmd_res_type = RT_UUID; + $$->cmd_prop_nv_pairs = 0; + } + | INFO ZFSPRI + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &info_func; + $$->cmd_res_type = RT_ZFSPRI; + $$->cmd_prop_nv_pairs = 0; + } | INFO resource_type property_name EQUAL property_value { if (($$ = alloc_cmd()) == NULL) @@ -735,6 +755,19 @@ remove_command: REMOVE $$->cmd_prop_name[0] = $2; $$->cmd_property_ptr[0] = &property[0]; } + | REMOVE TOKEN property_name property_value + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &remove_func; + $$->cmd_argc = 1; + $$->cmd_argv[0] = claim_token($2); + $$->cmd_argv[1] = NULL; + $$->cmd_prop_nv_pairs = 1; + $$->cmd_prop_name[0] = $3; + $$->cmd_property_ptr[0] = &property[0]; + } | REMOVE resource_type property_name EQUAL property_value { if (($$ = alloc_cmd()) == NULL) @@ -746,6 +779,20 @@ remove_command: REMOVE $$->cmd_prop_name[0] = $3; $$->cmd_property_ptr[0] = &property[0]; } + | REMOVE TOKEN resource_type property_name EQUAL property_value + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &remove_func; + $$->cmd_res_type = $3; + $$->cmd_argc = 1; + $$->cmd_argv[0] = claim_token($2); + $$->cmd_argv[1] = NULL; + $$->cmd_prop_nv_pairs = 1; + $$->cmd_prop_name[0] = $4; + $$->cmd_property_ptr[0] = &property[0]; + } | REMOVE resource_type property_name EQUAL property_value property_name EQUAL property_value { if (($$ = alloc_cmd()) == NULL) @@ -759,6 +806,22 @@ remove_command: REMOVE $$->cmd_prop_name[1] = $6; $$->cmd_property_ptr[1] = &property[1]; } + | REMOVE TOKEN resource_type property_name EQUAL property_value property_name EQUAL property_value + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &remove_func; + $$->cmd_res_type = $3; + $$->cmd_argc = 1; + $$->cmd_argv[0] = claim_token($2); + $$->cmd_argv[1] = NULL; + $$->cmd_prop_nv_pairs = 2; + $$->cmd_prop_name[0] = $4; + $$->cmd_property_ptr[0] = &property[0]; + $$->cmd_prop_name[1] = $7; + $$->cmd_property_ptr[1] = &property[1]; + } | REMOVE resource_type property_name EQUAL property_value property_name EQUAL property_value property_name EQUAL property_value { if (($$ = alloc_cmd()) == NULL) @@ -774,6 +837,24 @@ remove_command: REMOVE $$->cmd_prop_name[2] = $9; $$->cmd_property_ptr[2] = &property[2]; } + | REMOVE TOKEN resource_type property_name EQUAL property_value property_name EQUAL property_value property_name EQUAL property_value + { + if (($$ = alloc_cmd()) == NULL) + YYERROR; + cmd = $$; + $$->cmd_handler = &remove_func; + $$->cmd_res_type = $3; + $$->cmd_argc = 1; + $$->cmd_argv[0] = claim_token($2); + $$->cmd_argv[1] = NULL; + $$->cmd_prop_nv_pairs = 3; + $$->cmd_prop_name[0] = $4; + $$->cmd_property_ptr[0] = &property[0]; + $$->cmd_prop_name[1] = $7; + $$->cmd_property_ptr[1] = &property[1]; + $$->cmd_prop_name[2] = $10; + $$->cmd_property_ptr[2] = &property[2]; + } revert_command: REVERT { @@ -978,6 +1059,10 @@ property_name: SPECIAL { $$ = PT_SPECIAL; } | ALLOWED_ADDRESS { $$ = PT_ALLOWED_ADDRESS; } | PHYSICAL { $$ = PT_PHYSICAL; } | DEFROUTER { $$ = PT_DEFROUTER; } + | MAC { $$ = PT_MAC; } + | VLANID { $$ = PT_VLANID; } + | GNIC { $$ = PT_GNIC; } + | NPROP { $$ = PT_NPROP; } | NAME { $$ = PT_NAME; } | VALUE { $$ = PT_VALUE; } | MATCH { $$ = PT_MATCH; } @@ -1001,6 +1086,8 @@ property_name: SPECIAL { $$ = PT_SPECIAL; } | USER { $$ = PT_USER; } | AUTHS { $$ = PT_AUTHS; } | FS_ALLOWED { $$ = PT_FS_ALLOWED; } + | UUID { $$ = PT_UUID; } + | ZFSPRI { $$ = PT_ZFSPRI; } | DEFAULT { $$ = PT_DEFAULT; } | UPPER { $$ = PT_UPPER; } | LOWER { $$ = PT_LOWER; } diff --git a/usr/src/cmd/zonecfg/zonecfg_lex.l b/usr/src/cmd/zonecfg/zonecfg_lex.l index 8714c55499..05b41df48b 100644 --- a/usr/src/cmd/zonecfg/zonecfg_lex.l +++ b/usr/src/cmd/zonecfg/zonecfg_lex.l @@ -22,6 +22,7 @@ /* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent Inc. All rights reserved. */ #include <assert.h> @@ -57,10 +58,11 @@ extern void yyerror(char *s); static char *create_token(char *s); %} -%a 7000 -%p 5000 +%a 8000 +%p 5500 %e 2000 %n 1000 +%o 14000 %{ /* @@ -238,6 +240,18 @@ static char *create_token(char *s); <TSTATE>defrouter { return DEFROUTER; } <CSTATE>defrouter { return DEFROUTER; } +<TSTATE>mac-addr { return MAC; } +<CSTATE>mac-addr { return MAC; } + +<TSTATE>vlan-id { return VLANID; } +<CSTATE>vlan-id { return VLANID; } + +<TSTATE>global-nic { return GNIC; } +<CSTATE>global-nic { return GNIC; } + +<TSTATE>property { return NPROP; } +<CSTATE>property { return NPROP; } + <TSTATE>dir { return DIR; } <CSTATE>dir { return DIR; } @@ -310,6 +324,12 @@ static char *create_token(char *s); <TSTATE>fs-allowed { return FS_ALLOWED; } <CSTATE>fs-allowed { return FS_ALLOWED; } +<TSTATE>uuid { return UUID; } +<CSTATE>uuid { return UUID; } + +<TSTATE>zfs-io-priority { return ZFSPRI; } +<CSTATE>zfs-io-priority { return ZFSPRI; } + <TSTATE>default { return DEFAULT; } <CSTATE>default { return DEFAULT; } @@ -368,6 +388,13 @@ static char *create_token(char *s); return TOKEN; } +<CSTATE>\"[^\"\n]*[\"\n] { + yylval.strval = create_token(yytext + 1); + if (yylval.strval[yyleng - 2] == '"') + yylval.strval[yyleng - 2] = 0; + return TOKEN; + } + <TSTATE>\"[^\"\n]*[\"\n] { yylval.strval = create_token(yytext + 1); if (yylval.strval[yyleng - 2] == '"') diff --git a/usr/src/cmd/zonename/Makefile b/usr/src/cmd/zonename/Makefile index 566e893a67..3a51952455 100644 --- a/usr/src/cmd/zonename/Makefile +++ b/usr/src/cmd/zonename/Makefile @@ -28,8 +28,10 @@ # PROG= zonename +OBJS= zonename.o include ../Makefile.cmd +include ../Makefile.ctf LDLIBS += -lzonecfg @@ -37,6 +39,10 @@ LDLIBS += -lzonecfg all: $(PROG) +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + install: all $(ROOTSBINPROG) $(RM) $(ROOTPROG) $(SYMLINK) ../../sbin/$(PROG) $(ROOTPROG) @@ -44,6 +50,10 @@ install: all $(ROOTSBINPROG) check: $(PROG).c $(CSTYLE) -pP $(PROG).c +%.o: %.c + $(COMPILE.c) $< + $(POST_PROCESS_O) + clean: lint: lint_PROG diff --git a/usr/src/cmd/zonestat/zonestatd/zonestatd.c b/usr/src/cmd/zonestat/zonestatd/zonestatd.c index 026d188fda..10b73fd26e 100644 --- a/usr/src/cmd/zonestat/zonestatd/zonestatd.c +++ b/usr/src/cmd/zonestat/zonestatd/zonestatd.c @@ -21,6 +21,7 @@ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, Joyent, Inc. All rights reserved. */ #include <alloca.h> #include <assert.h> @@ -2190,7 +2191,7 @@ zsd_get_zone_rctl_usage(char *name) return (rctlblk_get_value(rblk)); } -#define ZSD_NUM_RCTL_VALS 19 +#define ZSD_NUM_RCTL_VALS 20 /* * Fetch the limit information for a zone. This uses zone_enter() as the @@ -2237,12 +2238,6 @@ zsd_get_zone_caps(zsd_ctl_t *ctl, zsd_zone_t *zone, uint64_t *cpu_shares, *msgids = 0; *lofi = 0; - /* Get the ram cap first since it is a zone attr */ - ret = zone_getattr(zone->zsz_id, ZONE_ATTR_PHYS_MCAP, - ram_cap, sizeof (*ram_cap)); - if (ret < 0 || *ram_cap == 0) - *ram_cap = ZS_LIMIT_NONE; - /* Get the zone's default scheduling class */ ret = zone_getattr(zone->zsz_id, ZONE_ATTR_SCHED_CLASS, class, sizeof (class)); @@ -2298,6 +2293,7 @@ zsd_get_zone_caps(zsd_ctl_t *ctl, zsd_zone_t *zone, uint64_t *cpu_shares, vals[i++] = zsd_get_zone_rctl_usage("zone.max-msg-ids"); vals[i++] = zsd_get_zone_rctl_limit("zone.max-lofi"); vals[i++] = zsd_get_zone_rctl_usage("zone.max-lofi"); + vals[i++] = zsd_get_zone_rctl_usage("zone.max-physical-memory"); if (write(p[1], vals, ZSD_NUM_RCTL_VALS * sizeof (uint64_t)) != ZSD_NUM_RCTL_VALS * sizeof (uint64_t)) { @@ -2342,6 +2338,7 @@ zsd_get_zone_caps(zsd_ctl_t *ctl, zsd_zone_t *zone, uint64_t *cpu_shares, *msgids = vals[i++]; *lofi_cap = vals[i++]; *lofi = vals[i++]; + *ram_cap = vals[i++]; /* Interpret maximum values as no cap */ if (*cpu_cap == UINT32_MAX || *cpu_cap == 0) diff --git a/usr/src/cmd/zpool/zpool_main.c b/usr/src/cmd/zpool/zpool_main.c index 39cdf276c8..cb13102874 100644 --- a/usr/src/cmd/zpool/zpool_main.c +++ b/usr/src/cmd/zpool/zpool_main.c @@ -24,6 +24,7 @@ * Copyright (c) 2011, 2018 by Delphix. All rights reserved. * Copyright (c) 2012 by Frederik Wessels. All rights reserved. * Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved. + * Copyright (c) 2013 Joyent, Inc. All rights reserved. * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>. * Copyright 2016 Nexenta Systems, Inc. * Copyright (c) 2017 Datto Inc. @@ -2790,6 +2791,8 @@ zpool_do_checkpoint(int argc, char **argv) * * -F Attempt rewind if necessary. * + * -m Allow import with a missing log device. + * * -n See if rewind would work, but don't actually rewind. * * -N Import the pool but don't mount datasets. |