Index: stable/11/bin/sh/input.c =================================================================== --- stable/11/bin/sh/input.c (revision 359753) +++ stable/11/bin/sh/input.c (revision 359754) @@ -1,518 +1,516 @@ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include /* defines BUFSIZ */ #include #include #include #include #include /* * This file implements the input routines used by the parser. */ #include "shell.h" #include "redir.h" #include "syntax.h" #include "input.h" #include "output.h" #include "options.h" #include "memalloc.h" #include "error.h" #include "alias.h" #include "parser.h" #include "myhistedit.h" #include "trap.h" #define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ struct strpush { struct strpush *prev; /* preceding string on stack */ const char *prevstring; int prevnleft; int prevlleft; struct alias *ap; /* if push was associated with an alias */ }; /* * The parsefile structure pointed to by the global variable parsefile * contains information about the current file being read. */ struct parsefile { struct parsefile *prev; /* preceding file on stack */ int linno; /* current line */ int fd; /* file descriptor (or -1 if string) */ int nleft; /* number of chars left in this line */ int lleft; /* number of lines left in this buffer */ const char *nextc; /* next char in buffer */ char *buf; /* input buffer */ struct strpush *strpush; /* for pushing strings at this level */ struct strpush basestrpush; /* so pushing one is fast */ }; int plinno = 1; /* input line number */ int parsenleft; /* copy of parsefile->nleft */ static int parselleft; /* copy of parsefile->lleft */ const char *parsenextc; /* copy of parsefile->nextc */ static char basebuf[BUFSIZ + 1];/* buffer for top level input file */ static struct parsefile basepf = { /* top level input file */ .nextc = basebuf, .buf = basebuf }; static struct parsefile *parsefile = &basepf; /* current input file */ int whichprompt; /* 1 == PS1, 2 == PS2 */ -EditLine *el; /* cookie for editline package */ - static void pushfile(void); static int preadfd(void); static void popstring(void); void resetinput(void) { popallfiles(); parselleft = parsenleft = 0; /* clear input buffer */ } /* * Read a character from the script, returning PEOF on end of file. * Nul characters in the input are silently discarded. */ int pgetc(void) { return pgetc_macro(); } static int preadfd(void) { int nr; parsenextc = parsefile->buf; retry: #ifndef NO_HISTORY if (parsefile->fd == 0 && el) { static const char *rl_cp; static int el_len; if (rl_cp == NULL) { el_resize(el); rl_cp = el_gets(el, &el_len); } if (rl_cp == NULL) nr = el_len == 0 ? 0 : -1; else { nr = el_len; if (nr > BUFSIZ) nr = BUFSIZ; memcpy(parsefile->buf, rl_cp, nr); if (nr != el_len) { el_len -= nr; rl_cp += nr; } else rl_cp = NULL; } } else #endif nr = read(parsefile->fd, parsefile->buf, BUFSIZ); if (nr <= 0) { if (nr < 0) { if (errno == EINTR) goto retry; if (parsefile->fd == 0 && errno == EWOULDBLOCK) { int flags = fcntl(0, F_GETFL, 0); if (flags >= 0 && flags & O_NONBLOCK) { flags &=~ O_NONBLOCK; if (fcntl(0, F_SETFL, flags) >= 0) { out2fmt_flush("sh: turning off NDELAY mode\n"); goto retry; } } } } nr = -1; } return nr; } /* * Refill the input buffer and return the next input character: * * 1) If a string was pushed back on the input, pop it; * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading * from a string so we can't refill the buffer, return EOF. * 3) If there is more in this buffer, use it else call read to fill it. * 4) Process input up to the next newline, deleting nul characters. */ int preadbuffer(void) { char *p, *q, *r, *end; char savec; while (parsefile->strpush) { /* * Add a space to the end of an alias to ensure that the * alias remains in use while parsing its last word. * This avoids alias recursions. */ if (parsenleft == -1 && parsefile->strpush->ap != NULL) return ' '; popstring(); if (--parsenleft >= 0) return (*parsenextc++); } if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) return PEOF; again: if (parselleft <= 0) { if ((parselleft = preadfd()) == -1) { parselleft = parsenleft = EOF_NLEFT; return PEOF; } } p = parsefile->buf + (parsenextc - parsefile->buf); end = p + parselleft; *end = '\0'; q = strchrnul(p, '\n'); if (q != end && *q == '\0') { /* delete nul characters */ for (r = q; q != end; q++) { if (*q != '\0') *r++ = *q; } parselleft -= end - r; if (parselleft == 0) goto again; end = p + parselleft; *end = '\0'; q = strchrnul(p, '\n'); } if (q == end) { parsenleft = parselleft; parselleft = 0; } else /* *q == '\n' */ { q++; parsenleft = q - parsenextc; parselleft -= parsenleft; } parsenleft--; savec = *q; *q = '\0'; #ifndef NO_HISTORY if (parsefile->fd == 0 && hist && parsenextc[strspn(parsenextc, " \t\n")] != '\0') { HistEvent he; INTOFF; history(hist, &he, whichprompt == 1 ? H_ENTER : H_ADD, parsenextc); INTON; } #endif if (vflag) { out2str(parsenextc); flushout(out2); } *q = savec; return *parsenextc++; } /* * Returns if we are certain we are at EOF. Does not cause any more input * to be read from the outside world. */ int preadateof(void) { if (parsenleft > 0) return 0; if (parsefile->strpush) return 0; if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) return 1; return 0; } /* * Undo the last call to pgetc. Only one character may be pushed back. * PEOF may be pushed back. */ void pungetc(void) { parsenleft++; parsenextc--; } /* * Push a string back onto the input at this current parsefile level. * We handle aliases this way. */ void pushstring(const char *s, int len, struct alias *ap) { struct strpush *sp; INTOFF; /*out2fmt_flush("*** calling pushstring: %s, %d\n", s, len);*/ if (parsefile->strpush) { sp = ckmalloc(sizeof (struct strpush)); sp->prev = parsefile->strpush; parsefile->strpush = sp; } else sp = parsefile->strpush = &(parsefile->basestrpush); sp->prevstring = parsenextc; sp->prevnleft = parsenleft; sp->prevlleft = parselleft; sp->ap = ap; if (ap) ap->flag |= ALIASINUSE; parsenextc = s; parsenleft = len; INTON; } static void popstring(void) { struct strpush *sp = parsefile->strpush; INTOFF; if (sp->ap) { if (parsenextc != sp->ap->val && (parsenextc[-1] == ' ' || parsenextc[-1] == '\t')) forcealias(); sp->ap->flag &= ~ALIASINUSE; } parsenextc = sp->prevstring; parsenleft = sp->prevnleft; parselleft = sp->prevlleft; /*out2fmt_flush("*** calling popstring: restoring to '%s'\n", parsenextc);*/ parsefile->strpush = sp->prev; if (sp != &(parsefile->basestrpush)) ckfree(sp); INTON; } /* * Set the input to take input from a file. If push is set, push the * old input onto the stack first. */ void setinputfile(const char *fname, int push) { int fd; int fd2; INTOFF; if ((fd = open(fname, O_RDONLY | O_CLOEXEC)) < 0) error("cannot open %s: %s", fname, strerror(errno)); if (fd < 10) { fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 10); close(fd); if (fd2 < 0) error("Out of file descriptors"); fd = fd2; } setinputfd(fd, push); INTON; } /* * Like setinputfile, but takes an open file descriptor (which should have * its FD_CLOEXEC flag already set). Call this with interrupts off. */ void setinputfd(int fd, int push) { if (push) { pushfile(); parsefile->buf = ckmalloc(BUFSIZ + 1); } if (parsefile->fd > 0) close(parsefile->fd); parsefile->fd = fd; if (parsefile->buf == NULL) parsefile->buf = ckmalloc(BUFSIZ + 1); parselleft = parsenleft = 0; plinno = 1; } /* * Like setinputfile, but takes input from a string. */ void setinputstring(const char *string, int push) { INTOFF; if (push) pushfile(); parsenextc = string; parselleft = parsenleft = strlen(string); parsefile->buf = NULL; plinno = 1; INTON; } /* * To handle the "." command, a stack of input files is used. Pushfile * adds a new entry to the stack and popfile restores the previous level. */ static void pushfile(void) { struct parsefile *pf; parsefile->nleft = parsenleft; parsefile->lleft = parselleft; parsefile->nextc = parsenextc; parsefile->linno = plinno; pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); pf->prev = parsefile; pf->fd = -1; pf->strpush = NULL; pf->basestrpush.prev = NULL; parsefile = pf; } void popfile(void) { struct parsefile *pf = parsefile; INTOFF; if (pf->fd >= 0) close(pf->fd); if (pf->buf) ckfree(pf->buf); while (pf->strpush) popstring(); parsefile = pf->prev; ckfree(pf); parsenleft = parsefile->nleft; parselleft = parsefile->lleft; parsenextc = parsefile->nextc; plinno = parsefile->linno; INTON; } /* * Return current file (to go back to it later using popfilesupto()). */ struct parsefile * getcurrentfile(void) { return parsefile; } /* * Pop files until the given file is on top again. Useful for regular * builtins that read shell commands from files or strings. * If the given file is not an active file, an error is raised. */ void popfilesupto(struct parsefile *file) { while (parsefile != file && parsefile != &basepf) popfile(); if (parsefile != file) error("popfilesupto() misused"); } /* * Return to top level. */ void popallfiles(void) { while (parsefile != &basepf) popfile(); } /* * Close the file(s) that the shell is reading commands from. Called * after a fork is done. */ void closescript(void) { popallfiles(); if (parsefile->fd > 0) { close(parsefile->fd); parsefile->fd = 0; } } Index: stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs_util.h =================================================================== --- stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs_util.h (revision 359753) +++ stable/11/cddl/contrib/opensolaris/cmd/zfs/zfs_util.h (revision 359754) @@ -1,42 +1,42 @@ /* * 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) 2005, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef _ZFS_UTIL_H #define _ZFS_UTIL_H #include #ifdef __cplusplus extern "C" { #endif void * safe_malloc(size_t size); void nomem(void); -libzfs_handle_t *g_zfs; +extern libzfs_handle_t *g_zfs; #ifdef __cplusplus } #endif #endif /* _ZFS_UTIL_H */ Index: stable/11/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c =================================================================== --- stable/11/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c (revision 359753) +++ stable/11/cddl/contrib/opensolaris/cmd/zpool/zpool_main.c (revision 359754) @@ -1,6372 +1,6374 @@ /* * 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) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011, 2018 by Delphix. All rights reserved. * Copyright (c) 2012 by Frederik Wessels. All rights reserved. * Copyright (c) 2012 Martin Matuska . All rights reserved. * Copyright (c) 2013 by Prasad Joshi (sTec). All rights reserved. * Copyright 2016 Igor Kozhukhov . * Copyright 2016 Nexenta Systems, Inc. * Copyright (c) 2017 Datto Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "zpool_util.h" #include "zfs_comutil.h" #include "zfeature_common.h" #include "statcommon.h" +libzfs_handle_t *g_zfs; + static int zpool_do_create(int, char **); static int zpool_do_destroy(int, char **); static int zpool_do_add(int, char **); static int zpool_do_remove(int, char **); static int zpool_do_labelclear(int, char **); static int zpool_do_checkpoint(int, char **); static int zpool_do_list(int, char **); static int zpool_do_iostat(int, char **); static int zpool_do_status(int, char **); static int zpool_do_online(int, char **); static int zpool_do_offline(int, char **); static int zpool_do_clear(int, char **); static int zpool_do_reopen(int, char **); static int zpool_do_reguid(int, char **); static int zpool_do_attach(int, char **); static int zpool_do_detach(int, char **); static int zpool_do_replace(int, char **); static int zpool_do_split(int, char **); static int zpool_do_initialize(int, char **); static int zpool_do_scrub(int, char **); static int zpool_do_import(int, char **); static int zpool_do_export(int, char **); static int zpool_do_upgrade(int, char **); static int zpool_do_history(int, char **); static int zpool_do_get(int, char **); static int zpool_do_set(int, char **); /* * These libumem hooks provide a reasonable set of defaults for the allocator's * debugging facilities. */ #ifdef DEBUG const char * _umem_debug_init(void) { return ("default,verbose"); /* $UMEM_DEBUG setting */ } const char * _umem_logging_init(void) { return ("fail,contents"); /* $UMEM_LOGGING setting */ } #endif typedef enum { HELP_ADD, HELP_ATTACH, HELP_CLEAR, HELP_CREATE, HELP_CHECKPOINT, HELP_DESTROY, HELP_DETACH, HELP_EXPORT, HELP_HISTORY, HELP_IMPORT, HELP_IOSTAT, HELP_LABELCLEAR, HELP_LIST, HELP_OFFLINE, HELP_ONLINE, HELP_REPLACE, HELP_REMOVE, HELP_INITIALIZE, HELP_SCRUB, HELP_STATUS, HELP_UPGRADE, HELP_GET, HELP_SET, HELP_SPLIT, HELP_REGUID, HELP_REOPEN } zpool_help_t; typedef struct zpool_command { const char *name; int (*func)(int, char **); zpool_help_t usage; } zpool_command_t; /* * Master command table. Each ZFS command has a name, associated function, and * usage message. The usage messages need to be internationalized, so we have * to have a function to return the usage message based on a command index. * * These commands are organized according to how they are displayed in the usage * message. An empty command (one with a NULL name) indicates an empty line in * the generic usage message. */ static zpool_command_t command_table[] = { { "create", zpool_do_create, HELP_CREATE }, { "destroy", zpool_do_destroy, HELP_DESTROY }, { NULL }, { "add", zpool_do_add, HELP_ADD }, { "remove", zpool_do_remove, HELP_REMOVE }, { NULL }, { "labelclear", zpool_do_labelclear, HELP_LABELCLEAR }, { NULL }, { "checkpoint", zpool_do_checkpoint, HELP_CHECKPOINT }, { NULL }, { "list", zpool_do_list, HELP_LIST }, { "iostat", zpool_do_iostat, HELP_IOSTAT }, { "status", zpool_do_status, HELP_STATUS }, { NULL }, { "online", zpool_do_online, HELP_ONLINE }, { "offline", zpool_do_offline, HELP_OFFLINE }, { "clear", zpool_do_clear, HELP_CLEAR }, { "reopen", zpool_do_reopen, HELP_REOPEN }, { NULL }, { "attach", zpool_do_attach, HELP_ATTACH }, { "detach", zpool_do_detach, HELP_DETACH }, { "replace", zpool_do_replace, HELP_REPLACE }, { "split", zpool_do_split, HELP_SPLIT }, { NULL }, { "initialize", zpool_do_initialize, HELP_INITIALIZE }, { "scrub", zpool_do_scrub, HELP_SCRUB }, { NULL }, { "import", zpool_do_import, HELP_IMPORT }, { "export", zpool_do_export, HELP_EXPORT }, { "upgrade", zpool_do_upgrade, HELP_UPGRADE }, { "reguid", zpool_do_reguid, HELP_REGUID }, { NULL }, { "history", zpool_do_history, HELP_HISTORY }, { "get", zpool_do_get, HELP_GET }, { "set", zpool_do_set, HELP_SET }, }; #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) static zpool_command_t *current_command; static char history_str[HIS_MAX_RECORD_LEN]; static boolean_t log_history = B_TRUE; static uint_t timestamp_fmt = NODATE; static const char * get_usage(zpool_help_t idx) { switch (idx) { case HELP_ADD: return (gettext("\tadd [-fn] ...\n")); case HELP_ATTACH: return (gettext("\tattach [-f] " "\n")); case HELP_CLEAR: return (gettext("\tclear [-nF] [device]\n")); case HELP_CREATE: return (gettext("\tcreate [-fnd] [-B] " "[-o property=value] ... \n" "\t [-O file-system-property=value] ...\n" "\t [-m mountpoint] [-R root] [-t tempname] " " ...\n")); case HELP_CHECKPOINT: return (gettext("\tcheckpoint [--discard] ...\n")); case HELP_DESTROY: return (gettext("\tdestroy [-f] \n")); case HELP_DETACH: return (gettext("\tdetach \n")); case HELP_EXPORT: return (gettext("\texport [-f] ...\n")); case HELP_HISTORY: return (gettext("\thistory [-il] [] ...\n")); case HELP_IMPORT: return (gettext("\timport [-d dir] [-D]\n" "\timport [-o mntopts] [-o property=value] ... \n" "\t [-d dir | -c cachefile] [-D] [-f] [-m] [-N] " "[-R root] [-F [-n]] -a\n" "\timport [-o mntopts] [-o property=value] ... \n" "\t [-d dir | -c cachefile] [-D] [-f] [-m] [-N] " "[-R root] [-F [-n]] [-t]\n" "\t [--rewind-to-checkpoint] [newpool]\n")); case HELP_IOSTAT: return (gettext("\tiostat [-v] [-T d|u] [pool] ... [interval " "[count]]\n")); case HELP_LABELCLEAR: return (gettext("\tlabelclear [-f] \n")); case HELP_LIST: return (gettext("\tlist [-Hpv] [-o property[,...]] " "[-T d|u] [pool] ... [interval [count]]\n")); case HELP_OFFLINE: return (gettext("\toffline [-t] ...\n")); case HELP_ONLINE: return (gettext("\tonline [-e] ...\n")); case HELP_REPLACE: return (gettext("\treplace [-f] " "[new-device]\n")); case HELP_REMOVE: return (gettext("\tremove [-nps] ...\n")); case HELP_REOPEN: return (gettext("\treopen \n")); case HELP_INITIALIZE: return (gettext("\tinitialize [-cs] [ ...]\n")); case HELP_SCRUB: return (gettext("\tscrub [-s | -p] ...\n")); case HELP_STATUS: return (gettext("\tstatus [-vx] [-T d|u] [pool] ... [interval " "[count]]\n")); case HELP_UPGRADE: return (gettext("\tupgrade [-v]\n" "\tupgrade [-V version] <-a | pool ...>\n")); case HELP_GET: return (gettext("\tget [-Hp] [-o \"all\" | field[,...]] " "<\"all\" | property[,...]> ...\n")); case HELP_SET: return (gettext("\tset \n")); case HELP_SPLIT: return (gettext("\tsplit [-n] [-R altroot] [-o mntopts]\n" "\t [-o property=value] " "[ ...]\n")); case HELP_REGUID: return (gettext("\treguid \n")); } abort(); /* NOTREACHED */ } /* * Callback routine that will print out a pool property value. */ static int print_prop_cb(int prop, void *cb) { FILE *fp = cb; (void) fprintf(fp, "\t%-15s ", zpool_prop_to_name(prop)); if (zpool_prop_readonly(prop)) (void) fprintf(fp, " NO "); else (void) fprintf(fp, " YES "); if (zpool_prop_values(prop) == NULL) (void) fprintf(fp, "-\n"); else (void) fprintf(fp, "%s\n", zpool_prop_values(prop)); return (ZPROP_CONT); } /* * Display usage message. If we're inside a command, display only the usage for * that command. Otherwise, iterate over the entire command table and display * a complete usage message. */ void usage(boolean_t requested) { FILE *fp = requested ? stdout : stderr; if (current_command == NULL) { int i; (void) fprintf(fp, gettext("usage: zpool command args ...\n")); (void) fprintf(fp, gettext("where 'command' is one of the following:\n\n")); for (i = 0; i < NCOMMAND; i++) { if (command_table[i].name == NULL) (void) fprintf(fp, "\n"); else (void) fprintf(fp, "%s", get_usage(command_table[i].usage)); } } else { (void) fprintf(fp, gettext("usage:\n")); (void) fprintf(fp, "%s", get_usage(current_command->usage)); } if (current_command != NULL && ((strcmp(current_command->name, "set") == 0) || (strcmp(current_command->name, "get") == 0) || (strcmp(current_command->name, "list") == 0))) { (void) fprintf(fp, gettext("\nthe following properties are supported:\n")); (void) fprintf(fp, "\n\t%-15s %s %s\n\n", "PROPERTY", "EDIT", "VALUES"); /* Iterate over all properties */ (void) zprop_iter(print_prop_cb, fp, B_FALSE, B_TRUE, ZFS_TYPE_POOL); (void) fprintf(fp, "\t%-15s ", "feature@..."); (void) fprintf(fp, "YES disabled | enabled | active\n"); (void) fprintf(fp, gettext("\nThe feature@ properties must be " "appended with a feature name.\nSee zpool-features(7).\n")); } /* * See comments at end of main(). */ if (getenv("ZFS_ABORT") != NULL) { (void) printf("dumping core by request\n"); abort(); } exit(requested ? 0 : 2); } void print_vdev_tree(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int indent, boolean_t print_logs) { nvlist_t **child; uint_t c, children; char *vname; if (name != NULL) (void) printf("\t%*s%s\n", indent, "", name); if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) return; for (c = 0; c < children; c++) { uint64_t is_log = B_FALSE; (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, &is_log); if ((is_log && !print_logs) || (!is_log && print_logs)) continue; vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); print_vdev_tree(zhp, vname, child[c], indent + 2, B_FALSE); free(vname); } } static boolean_t prop_list_contains_feature(nvlist_t *proplist) { nvpair_t *nvp; for (nvp = nvlist_next_nvpair(proplist, NULL); NULL != nvp; nvp = nvlist_next_nvpair(proplist, nvp)) { if (zpool_prop_feature(nvpair_name(nvp))) return (B_TRUE); } return (B_FALSE); } /* * Add a property pair (name, string-value) into a property nvlist. */ static int add_prop_list(const char *propname, char *propval, nvlist_t **props, boolean_t poolprop) { zpool_prop_t prop = ZPROP_INVAL; zfs_prop_t fprop; nvlist_t *proplist; const char *normnm; char *strval; if (*props == NULL && nvlist_alloc(props, NV_UNIQUE_NAME, 0) != 0) { (void) fprintf(stderr, gettext("internal error: out of memory\n")); return (1); } proplist = *props; if (poolprop) { const char *vname = zpool_prop_to_name(ZPOOL_PROP_VERSION); if ((prop = zpool_name_to_prop(propname)) == ZPROP_INVAL && !zpool_prop_feature(propname)) { (void) fprintf(stderr, gettext("property '%s' is " "not a valid pool property\n"), propname); return (2); } /* * feature@ properties and version should not be specified * at the same time. */ if ((prop == ZPOOL_PROP_INVAL && zpool_prop_feature(propname) && nvlist_exists(proplist, vname)) || (prop == ZPOOL_PROP_VERSION && prop_list_contains_feature(proplist))) { (void) fprintf(stderr, gettext("'feature@' and " "'version' properties cannot be specified " "together\n")); return (2); } if (zpool_prop_feature(propname)) normnm = propname; else normnm = zpool_prop_to_name(prop); } else { if ((fprop = zfs_name_to_prop(propname)) != ZPROP_INVAL) { normnm = zfs_prop_to_name(fprop); } else { normnm = propname; } } if (nvlist_lookup_string(proplist, normnm, &strval) == 0 && prop != ZPOOL_PROP_CACHEFILE) { (void) fprintf(stderr, gettext("property '%s' " "specified multiple times\n"), propname); return (2); } if (nvlist_add_string(proplist, normnm, propval) != 0) { (void) fprintf(stderr, gettext("internal " "error: out of memory\n")); return (1); } return (0); } /* * Set a default property pair (name, string-value) in a property nvlist */ static int add_prop_list_default(const char *propname, char *propval, nvlist_t **props, boolean_t poolprop) { char *pval; if (nvlist_lookup_string(*props, propname, &pval) == 0) return (0); return (add_prop_list(propname, propval, props, poolprop)); } /* * zpool add [-fn] ... * * -f Force addition of devices, even if they appear in use * -n Do not add the devices, but display the resulting layout if * they were to be added. * * Adds the given vdevs to 'pool'. As with create, the bulk of this work is * handled by get_vdev_spec(), which constructs the nvlist needed to pass to * libzfs. */ int zpool_do_add(int argc, char **argv) { boolean_t force = B_FALSE; boolean_t dryrun = B_FALSE; int c; nvlist_t *nvroot; char *poolname; zpool_boot_label_t boot_type; uint64_t boot_size; int ret; zpool_handle_t *zhp; nvlist_t *config; /* check options */ while ((c = getopt(argc, argv, "fn")) != -1) { switch (c) { case 'f': force = B_TRUE; break; case 'n': dryrun = B_TRUE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; /* get pool name and check number of arguments */ if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name argument\n")); usage(B_FALSE); } if (argc < 2) { (void) fprintf(stderr, gettext("missing vdev specification\n")); usage(B_FALSE); } poolname = argv[0]; argc--; argv++; if ((zhp = zpool_open(g_zfs, poolname)) == NULL) return (1); if ((config = zpool_get_config(zhp, NULL)) == NULL) { (void) fprintf(stderr, gettext("pool '%s' is unavailable\n"), poolname); zpool_close(zhp); return (1); } if (zpool_is_bootable(zhp)) boot_type = ZPOOL_COPY_BOOT_LABEL; else boot_type = ZPOOL_NO_BOOT_LABEL; /* pass off to get_vdev_spec for processing */ boot_size = zpool_get_prop_int(zhp, ZPOOL_PROP_BOOTSIZE, NULL); nvroot = make_root_vdev(zhp, force, !force, B_FALSE, dryrun, boot_type, boot_size, argc, argv); if (nvroot == NULL) { zpool_close(zhp); return (1); } if (dryrun) { nvlist_t *poolnvroot; verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &poolnvroot) == 0); (void) printf(gettext("would update '%s' to the following " "configuration:\n"), zpool_get_name(zhp)); /* print original main pool and new tree */ print_vdev_tree(zhp, poolname, poolnvroot, 0, B_FALSE); print_vdev_tree(zhp, NULL, nvroot, 0, B_FALSE); /* Do the same for the logs */ if (num_logs(poolnvroot) > 0) { print_vdev_tree(zhp, "logs", poolnvroot, 0, B_TRUE); print_vdev_tree(zhp, NULL, nvroot, 0, B_TRUE); } else if (num_logs(nvroot) > 0) { print_vdev_tree(zhp, "logs", nvroot, 0, B_TRUE); } ret = 0; } else { ret = (zpool_add(zhp, nvroot) != 0); } nvlist_free(nvroot); zpool_close(zhp); return (ret); } /* * zpool remove ... * * Removes the given vdev from the pool. */ int zpool_do_remove(int argc, char **argv) { char *poolname; int i, ret = 0; zpool_handle_t *zhp; boolean_t stop = B_FALSE; boolean_t noop = B_FALSE; boolean_t parsable = B_FALSE; char c; /* check options */ while ((c = getopt(argc, argv, "nps")) != -1) { switch (c) { case 'n': noop = B_TRUE; break; case 'p': parsable = B_TRUE; break; case 's': stop = B_TRUE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; /* get pool name and check number of arguments */ if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name argument\n")); usage(B_FALSE); } poolname = argv[0]; if ((zhp = zpool_open(g_zfs, poolname)) == NULL) return (1); if (stop && noop) { (void) fprintf(stderr, gettext("stop request ignored\n")); return (0); } if (stop) { if (argc > 1) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } if (zpool_vdev_remove_cancel(zhp) != 0) ret = 1; } else { if (argc < 2) { (void) fprintf(stderr, gettext("missing device\n")); usage(B_FALSE); } for (i = 1; i < argc; i++) { if (noop) { uint64_t size; if (zpool_vdev_indirect_size(zhp, argv[i], &size) != 0) { ret = 1; break; } if (parsable) { (void) printf("%s %llu\n", argv[i], size); } else { char valstr[32]; zfs_nicenum(size, valstr, sizeof (valstr)); (void) printf("Memory that will be " "used after removing %s: %s\n", argv[i], valstr); } } else { if (zpool_vdev_remove(zhp, argv[i]) != 0) ret = 1; } } } return (ret); } /* * zpool labelclear [-f] * * -f Force clearing the label for the vdevs which are members of * the exported or foreign pools. * * Verifies that the vdev is not active and zeros out the label information * on the device. */ int zpool_do_labelclear(int argc, char **argv) { char vdev[MAXPATHLEN]; char *name = NULL; struct stat st; int c, fd, ret = 0; nvlist_t *config; pool_state_t state; boolean_t inuse = B_FALSE; boolean_t force = B_FALSE; /* check options */ while ((c = getopt(argc, argv, "f")) != -1) { switch (c) { case 'f': force = B_TRUE; break; default: (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; /* get vdev name */ if (argc < 1) { (void) fprintf(stderr, gettext("missing vdev name\n")); usage(B_FALSE); } if (argc > 1) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } /* * Check if we were given absolute path and use it as is. * Otherwise if the provided vdev name doesn't point to a file, * try prepending dsk path and appending s0. */ (void) strlcpy(vdev, argv[0], sizeof (vdev)); if (vdev[0] != '/' && stat(vdev, &st) != 0) { char *s; (void) snprintf(vdev, sizeof (vdev), "%s/%s", #ifdef illumos ZFS_DISK_ROOT, argv[0]); if ((s = strrchr(argv[0], 's')) == NULL || !isdigit(*(s + 1))) (void) strlcat(vdev, "s0", sizeof (vdev)); #else "/dev", argv[0]); #endif if (stat(vdev, &st) != 0) { (void) fprintf(stderr, gettext( "failed to find device %s, try specifying absolute " "path instead\n"), argv[0]); return (1); } } if ((fd = open(vdev, O_RDWR)) < 0) { (void) fprintf(stderr, gettext("failed to open %s: %s\n"), vdev, strerror(errno)); return (1); } if (zpool_read_label(fd, &config) != 0) { (void) fprintf(stderr, gettext("failed to read label from %s\n"), vdev); return (1); } nvlist_free(config); ret = zpool_in_use(g_zfs, fd, &state, &name, &inuse); if (ret != 0) { (void) fprintf(stderr, gettext("failed to check state for %s\n"), vdev); return (1); } if (!inuse) goto wipe_label; switch (state) { default: case POOL_STATE_ACTIVE: case POOL_STATE_SPARE: case POOL_STATE_L2CACHE: (void) fprintf(stderr, gettext( "%s is a member (%s) of pool \"%s\"\n"), vdev, zpool_pool_state_to_name(state), name); ret = 1; goto errout; case POOL_STATE_EXPORTED: if (force) break; (void) fprintf(stderr, gettext( "use '-f' to override the following error:\n" "%s is a member of exported pool \"%s\"\n"), vdev, name); ret = 1; goto errout; case POOL_STATE_POTENTIALLY_ACTIVE: if (force) break; (void) fprintf(stderr, gettext( "use '-f' to override the following error:\n" "%s is a member of potentially active pool \"%s\"\n"), vdev, name); ret = 1; goto errout; case POOL_STATE_DESTROYED: /* inuse should never be set for a destroyed pool */ assert(0); break; } wipe_label: ret = zpool_clear_label(fd); if (ret != 0) { (void) fprintf(stderr, gettext("failed to clear label for %s\n"), vdev); } errout: free(name); (void) close(fd); return (ret); } /* * zpool create [-fnd] [-B] [-o property=value] ... * [-O file-system-property=value] ... * [-R root] [-m mountpoint] [-t tempname] ... * * -B Create boot partition. * -f Force creation, even if devices appear in use * -n Do not create the pool, but display the resulting layout if it * were to be created. * -R Create a pool under an alternate root * -m Set default mountpoint for the root dataset. By default it's * '/' * -t Use the temporary name until the pool is exported. * -o Set property=value. * -d Don't automatically enable all supported pool features * (individual features can be enabled with -o). * -O Set fsproperty=value in the pool's root file system * * Creates the named pool according to the given vdev specification. The * bulk of the vdev processing is done in get_vdev_spec() in zpool_vdev.c. Once * we get the nvlist back from get_vdev_spec(), we either print out the contents * (if '-n' was specified), or pass it to libzfs to do the creation. */ #define SYSTEM256 (256 * 1024 * 1024) int zpool_do_create(int argc, char **argv) { boolean_t force = B_FALSE; boolean_t dryrun = B_FALSE; boolean_t enable_all_pool_feat = B_TRUE; zpool_boot_label_t boot_type = ZPOOL_NO_BOOT_LABEL; uint64_t boot_size = 0; int c; nvlist_t *nvroot = NULL; char *poolname; char *tname = NULL; int ret = 1; char *altroot = NULL; char *mountpoint = NULL; nvlist_t *fsprops = NULL; nvlist_t *props = NULL; char *propval; /* check options */ while ((c = getopt(argc, argv, ":fndBR:m:o:O:t:")) != -1) { switch (c) { case 'f': force = B_TRUE; break; case 'n': dryrun = B_TRUE; break; case 'd': enable_all_pool_feat = B_FALSE; break; case 'B': #ifdef illumos /* * We should create the system partition. * Also make sure the size is set. */ boot_type = ZPOOL_CREATE_BOOT_LABEL; if (boot_size == 0) boot_size = SYSTEM256; break; #else (void) fprintf(stderr, gettext("option '%c' is not supported\n"), optopt); goto badusage; #endif case 'R': altroot = optarg; if (add_prop_list(zpool_prop_to_name( ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE)) goto errout; if (add_prop_list_default(zpool_prop_to_name( ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) goto errout; break; case 'm': /* Equivalent to -O mountpoint=optarg */ mountpoint = optarg; break; case 'o': if ((propval = strchr(optarg, '=')) == NULL) { (void) fprintf(stderr, gettext("missing " "'=' for -o option\n")); goto errout; } *propval = '\0'; propval++; if (add_prop_list(optarg, propval, &props, B_TRUE)) goto errout; /* * Get bootsize value for make_root_vdev(). */ if (zpool_name_to_prop(optarg) == ZPOOL_PROP_BOOTSIZE) { if (zfs_nicestrtonum(g_zfs, propval, &boot_size) < 0 || boot_size == 0) { (void) fprintf(stderr, gettext("bad boot partition size " "'%s': %s\n"), propval, libzfs_error_description(g_zfs)); goto errout; } } /* * If the user is creating a pool that doesn't support * feature flags, don't enable any features. */ if (zpool_name_to_prop(optarg) == ZPOOL_PROP_VERSION) { char *end; u_longlong_t ver; ver = strtoull(propval, &end, 10); if (*end == '\0' && ver < SPA_VERSION_FEATURES) { enable_all_pool_feat = B_FALSE; } } if (zpool_name_to_prop(optarg) == ZPOOL_PROP_ALTROOT) altroot = propval; break; case 'O': if ((propval = strchr(optarg, '=')) == NULL) { (void) fprintf(stderr, gettext("missing " "'=' for -O option\n")); goto errout; } *propval = '\0'; propval++; /* * Mountpoints are checked and then added later. * Uniquely among properties, they can be specified * more than once, to avoid conflict with -m. */ if (0 == strcmp(optarg, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT))) { mountpoint = propval; } else if (add_prop_list(optarg, propval, &fsprops, B_FALSE)) { goto errout; } break; case 't': /* * Sanity check temporary pool name. */ if (strchr(optarg, '/') != NULL) { (void) fprintf(stderr, gettext("cannot create " "'%s': invalid character '/' in temporary " "name\n"), optarg); (void) fprintf(stderr, gettext("use 'zfs " "create' to create a dataset\n")); goto errout; } if (add_prop_list(zpool_prop_to_name( ZPOOL_PROP_TNAME), optarg, &props, B_TRUE)) goto errout; if (add_prop_list_default(zpool_prop_to_name( ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) goto errout; tname = optarg; break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); goto badusage; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); goto badusage; } } argc -= optind; argv += optind; /* get pool name and check number of arguments */ if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name argument\n")); goto badusage; } if (argc < 2) { (void) fprintf(stderr, gettext("missing vdev specification\n")); goto badusage; } poolname = argv[0]; /* * As a special case, check for use of '/' in the name, and direct the * user to use 'zfs create' instead. */ if (strchr(poolname, '/') != NULL) { (void) fprintf(stderr, gettext("cannot create '%s': invalid " "character '/' in pool name\n"), poolname); (void) fprintf(stderr, gettext("use 'zfs create' to " "create a dataset\n")); goto errout; } /* * Make sure the bootsize is set when ZPOOL_CREATE_BOOT_LABEL is used, * and not set otherwise. */ if (boot_type == ZPOOL_CREATE_BOOT_LABEL) { const char *propname; char *strptr, *buf = NULL; int rv; propname = zpool_prop_to_name(ZPOOL_PROP_BOOTSIZE); if (nvlist_lookup_string(props, propname, &strptr) != 0) { (void) asprintf(&buf, "%" PRIu64, boot_size); if (buf == NULL) { (void) fprintf(stderr, gettext("internal error: out of memory\n")); goto errout; } rv = add_prop_list(propname, buf, &props, B_TRUE); free(buf); if (rv != 0) goto errout; } } else { const char *propname; char *strptr; propname = zpool_prop_to_name(ZPOOL_PROP_BOOTSIZE); if (nvlist_lookup_string(props, propname, &strptr) == 0) { (void) fprintf(stderr, gettext("error: setting boot " "partition size requires option '-B'\n")); goto errout; } } /* pass off to get_vdev_spec for bulk processing */ nvroot = make_root_vdev(NULL, force, !force, B_FALSE, dryrun, boot_type, boot_size, argc - 1, argv + 1); if (nvroot == NULL) goto errout; /* make_root_vdev() allows 0 toplevel children if there are spares */ if (!zfs_allocatable_devs(nvroot)) { (void) fprintf(stderr, gettext("invalid vdev " "specification: at least one toplevel vdev must be " "specified\n")); goto errout; } if (altroot != NULL && altroot[0] != '/') { (void) fprintf(stderr, gettext("invalid alternate root '%s': " "must be an absolute path\n"), altroot); goto errout; } /* * Check the validity of the mountpoint and direct the user to use the * '-m' mountpoint option if it looks like its in use. * Ignore the checks if the '-f' option is given. */ if (!force && (mountpoint == NULL || (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && strcmp(mountpoint, ZFS_MOUNTPOINT_NONE) != 0))) { char buf[MAXPATHLEN]; DIR *dirp; if (mountpoint && mountpoint[0] != '/') { (void) fprintf(stderr, gettext("invalid mountpoint " "'%s': must be an absolute path, 'legacy', or " "'none'\n"), mountpoint); goto errout; } if (mountpoint == NULL) { if (altroot != NULL) (void) snprintf(buf, sizeof (buf), "%s/%s", altroot, poolname); else (void) snprintf(buf, sizeof (buf), "/%s", poolname); } else { if (altroot != NULL) (void) snprintf(buf, sizeof (buf), "%s%s", altroot, mountpoint); else (void) snprintf(buf, sizeof (buf), "%s", mountpoint); } if ((dirp = opendir(buf)) == NULL && errno != ENOENT) { (void) fprintf(stderr, gettext("mountpoint '%s' : " "%s\n"), buf, strerror(errno)); (void) fprintf(stderr, gettext("use '-m' " "option to provide a different default\n")); goto errout; } else if (dirp) { int count = 0; while (count < 3 && readdir(dirp) != NULL) count++; (void) closedir(dirp); if (count > 2) { (void) fprintf(stderr, gettext("mountpoint " "'%s' exists and is not empty\n"), buf); (void) fprintf(stderr, gettext("use '-m' " "option to provide a " "different default\n")); goto errout; } } } /* * Now that the mountpoint's validity has been checked, ensure that * the property is set appropriately prior to creating the pool. */ if (mountpoint != NULL) { ret = add_prop_list(zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), mountpoint, &fsprops, B_FALSE); if (ret != 0) goto errout; } ret = 1; if (dryrun) { /* * For a dry run invocation, print out a basic message and run * through all the vdevs in the list and print out in an * appropriate hierarchy. */ (void) printf(gettext("would create '%s' with the " "following layout:\n\n"), poolname); print_vdev_tree(NULL, poolname, nvroot, 0, B_FALSE); if (num_logs(nvroot) > 0) print_vdev_tree(NULL, "logs", nvroot, 0, B_TRUE); ret = 0; } else { /* * Hand off to libzfs. */ if (enable_all_pool_feat) { spa_feature_t i; for (i = 0; i < SPA_FEATURES; i++) { char propname[MAXPATHLEN]; zfeature_info_t *feat = &spa_feature_table[i]; (void) snprintf(propname, sizeof (propname), "feature@%s", feat->fi_uname); /* * Skip feature if user specified it manually * on the command line. */ if (nvlist_exists(props, propname)) continue; ret = add_prop_list(propname, ZFS_FEATURE_ENABLED, &props, B_TRUE); if (ret != 0) goto errout; } } ret = 1; if (zpool_create(g_zfs, poolname, nvroot, props, fsprops) == 0) { zfs_handle_t *pool = zfs_open(g_zfs, tname ? tname : poolname, ZFS_TYPE_FILESYSTEM); if (pool != NULL) { if (zfs_mount(pool, NULL, 0) == 0) ret = zfs_shareall(pool); zfs_close(pool); } } else if (libzfs_errno(g_zfs) == EZFS_INVALIDNAME) { (void) fprintf(stderr, gettext("pool name may have " "been omitted\n")); } } errout: nvlist_free(nvroot); nvlist_free(fsprops); nvlist_free(props); return (ret); badusage: nvlist_free(fsprops); nvlist_free(props); usage(B_FALSE); return (2); } /* * zpool destroy * * -f Forcefully unmount any datasets * * Destroy the given pool. Automatically unmounts any datasets in the pool. */ int zpool_do_destroy(int argc, char **argv) { boolean_t force = B_FALSE; int c; char *pool; zpool_handle_t *zhp; int ret; /* check options */ while ((c = getopt(argc, argv, "f")) != -1) { switch (c) { case 'f': force = B_TRUE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; /* check arguments */ if (argc < 1) { (void) fprintf(stderr, gettext("missing pool argument\n")); usage(B_FALSE); } if (argc > 1) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } pool = argv[0]; if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL) { /* * As a special case, check for use of '/' in the name, and * direct the user to use 'zfs destroy' instead. */ if (strchr(pool, '/') != NULL) (void) fprintf(stderr, gettext("use 'zfs destroy' to " "destroy a dataset\n")); return (1); } if (zpool_disable_datasets(zhp, force) != 0) { (void) fprintf(stderr, gettext("could not destroy '%s': " "could not unmount datasets\n"), zpool_get_name(zhp)); return (1); } /* The history must be logged as part of the export */ log_history = B_FALSE; ret = (zpool_destroy(zhp, history_str) != 0); zpool_close(zhp); return (ret); } /* * zpool export [-f] ... * * -f Forcefully unmount datasets * * Export the given pools. By default, the command will attempt to cleanly * unmount any active datasets within the pool. If the '-f' flag is specified, * then the datasets will be forcefully unmounted. */ int zpool_do_export(int argc, char **argv) { boolean_t force = B_FALSE; boolean_t hardforce = B_FALSE; int c; zpool_handle_t *zhp; int ret; int i; /* check options */ while ((c = getopt(argc, argv, "fF")) != -1) { switch (c) { case 'f': force = B_TRUE; break; case 'F': hardforce = B_TRUE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; /* check arguments */ if (argc < 1) { (void) fprintf(stderr, gettext("missing pool argument\n")); usage(B_FALSE); } ret = 0; for (i = 0; i < argc; i++) { if ((zhp = zpool_open_canfail(g_zfs, argv[i])) == NULL) { ret = 1; continue; } if (zpool_disable_datasets(zhp, force) != 0) { ret = 1; zpool_close(zhp); continue; } /* The history must be logged as part of the export */ log_history = B_FALSE; if (hardforce) { if (zpool_export_force(zhp, history_str) != 0) ret = 1; } else if (zpool_export(zhp, force, history_str) != 0) { ret = 1; } zpool_close(zhp); } return (ret); } /* * Given a vdev configuration, determine the maximum width needed for the device * name column. */ static int max_width(zpool_handle_t *zhp, nvlist_t *nv, int depth, int max) { char *name = zpool_vdev_name(g_zfs, zhp, nv, B_TRUE); nvlist_t **child; uint_t c, children; int ret; if (strlen(name) + depth > max) max = strlen(name) + depth; free(name); if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &child, &children) == 0) { for (c = 0; c < children; c++) if ((ret = max_width(zhp, child[c], depth + 2, max)) > max) max = ret; } if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, &child, &children) == 0) { for (c = 0; c < children; c++) if ((ret = max_width(zhp, child[c], depth + 2, max)) > max) max = ret; } if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, &children) == 0) { for (c = 0; c < children; c++) if ((ret = max_width(zhp, child[c], depth + 2, max)) > max) max = ret; } return (max); } typedef struct spare_cbdata { uint64_t cb_guid; zpool_handle_t *cb_zhp; } spare_cbdata_t; static boolean_t find_vdev(nvlist_t *nv, uint64_t search) { uint64_t guid; nvlist_t **child; uint_t c, children; if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0 && search == guid) return (B_TRUE); if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, &children) == 0) { for (c = 0; c < children; c++) if (find_vdev(child[c], search)) return (B_TRUE); } return (B_FALSE); } static int find_spare(zpool_handle_t *zhp, void *data) { spare_cbdata_t *cbp = data; nvlist_t *config, *nvroot; config = zpool_get_config(zhp, NULL); verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); if (find_vdev(nvroot, cbp->cb_guid)) { cbp->cb_zhp = zhp; return (1); } zpool_close(zhp); return (0); } /* * Print out configuration state as requested by status_callback. */ void print_status_config(zpool_handle_t *zhp, const char *name, nvlist_t *nv, int namewidth, int depth, boolean_t isspare) { nvlist_t **child; uint_t c, vsc, children; pool_scan_stat_t *ps = NULL; vdev_stat_t *vs; char rbuf[6], wbuf[6], cbuf[6]; char *vname; uint64_t notpresent; uint64_t ashift; spare_cbdata_t cb; const char *state; char *type; if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) children = 0; verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &vsc) == 0); verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0); if (strcmp(type, VDEV_TYPE_INDIRECT) == 0) return; state = zpool_state_to_name(vs->vs_state, vs->vs_aux); if (isspare) { /* * For hot spares, we use the terms 'INUSE' and 'AVAILABLE' for * online drives. */ if (vs->vs_aux == VDEV_AUX_SPARED) state = "INUSE"; else if (vs->vs_state == VDEV_STATE_HEALTHY) state = "AVAIL"; } (void) printf("\t%*s%-*s %-8s", depth, "", namewidth - depth, name, state); if (!isspare) { zfs_nicenum(vs->vs_read_errors, rbuf, sizeof (rbuf)); zfs_nicenum(vs->vs_write_errors, wbuf, sizeof (wbuf)); zfs_nicenum(vs->vs_checksum_errors, cbuf, sizeof (cbuf)); (void) printf(" %5s %5s %5s", rbuf, wbuf, cbuf); } if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT, ¬present) == 0 || vs->vs_state <= VDEV_STATE_CANT_OPEN) { char *path; if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) == 0) (void) printf(" was %s", path); } else if (vs->vs_aux != 0) { (void) printf(" "); switch (vs->vs_aux) { case VDEV_AUX_OPEN_FAILED: (void) printf(gettext("cannot open")); break; case VDEV_AUX_BAD_GUID_SUM: (void) printf(gettext("missing device")); break; case VDEV_AUX_NO_REPLICAS: (void) printf(gettext("insufficient replicas")); break; case VDEV_AUX_VERSION_NEWER: (void) printf(gettext("newer version")); break; case VDEV_AUX_UNSUP_FEAT: (void) printf(gettext("unsupported feature(s)")); break; case VDEV_AUX_ASHIFT_TOO_BIG: (void) printf(gettext("unsupported minimum blocksize")); break; case VDEV_AUX_SPARED: verify(nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &cb.cb_guid) == 0); if (zpool_iter(g_zfs, find_spare, &cb) == 1) { if (strcmp(zpool_get_name(cb.cb_zhp), zpool_get_name(zhp)) == 0) (void) printf(gettext("currently in " "use")); else (void) printf(gettext("in use by " "pool '%s'"), zpool_get_name(cb.cb_zhp)); zpool_close(cb.cb_zhp); } else { (void) printf(gettext("currently in use")); } break; case VDEV_AUX_ERR_EXCEEDED: (void) printf(gettext("too many errors")); break; case VDEV_AUX_IO_FAILURE: (void) printf(gettext("experienced I/O failures")); break; case VDEV_AUX_BAD_LOG: (void) printf(gettext("bad intent log")); break; case VDEV_AUX_EXTERNAL: (void) printf(gettext("external device fault")); break; case VDEV_AUX_SPLIT_POOL: (void) printf(gettext("split into new pool")); break; case VDEV_AUX_CHILDREN_OFFLINE: (void) printf(gettext("all children offline")); break; default: (void) printf(gettext("corrupted data")); break; } } else if (children == 0 && !isspare && VDEV_STAT_VALID(vs_physical_ashift, vsc) && vs->vs_configured_ashift < vs->vs_physical_ashift) { (void) printf( gettext(" block size: %dB configured, %dB native"), 1 << vs->vs_configured_ashift, 1 << vs->vs_physical_ashift); } (void) nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &c); if (ps != NULL && ps->pss_state == DSS_SCANNING && vs->vs_scan_processed != 0 && children == 0) { (void) printf(gettext(" (%s)"), (ps->pss_func == POOL_SCAN_RESILVER) ? "resilvering" : "repairing"); } if ((vs->vs_initialize_state == VDEV_INITIALIZE_ACTIVE || vs->vs_initialize_state == VDEV_INITIALIZE_SUSPENDED || vs->vs_initialize_state == VDEV_INITIALIZE_COMPLETE) && !vs->vs_scan_removing) { char zbuf[1024]; char tbuf[256]; struct tm zaction_ts; time_t t = vs->vs_initialize_action_time; int initialize_pct = 100; if (vs->vs_initialize_state != VDEV_INITIALIZE_COMPLETE) { initialize_pct = (vs->vs_initialize_bytes_done * 100 / (vs->vs_initialize_bytes_est + 1)); } (void) localtime_r(&t, &zaction_ts); (void) strftime(tbuf, sizeof (tbuf), "%c", &zaction_ts); switch (vs->vs_initialize_state) { case VDEV_INITIALIZE_SUSPENDED: (void) snprintf(zbuf, sizeof (zbuf), ", suspended, started at %s", tbuf); break; case VDEV_INITIALIZE_ACTIVE: (void) snprintf(zbuf, sizeof (zbuf), ", started at %s", tbuf); break; case VDEV_INITIALIZE_COMPLETE: (void) snprintf(zbuf, sizeof (zbuf), ", completed at %s", tbuf); break; } (void) printf(gettext(" (%d%% initialized%s)"), initialize_pct, zbuf); } (void) printf("\n"); for (c = 0; c < children; c++) { uint64_t islog = B_FALSE, ishole = B_FALSE; /* Don't print logs or holes here */ (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, &islog); (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_HOLE, &ishole); if (islog || ishole) continue; vname = zpool_vdev_name(g_zfs, zhp, child[c], B_TRUE); print_status_config(zhp, vname, child[c], namewidth, depth + 2, isspare); free(vname); } } /* * Print the configuration of an exported pool. Iterate over all vdevs in the * pool, printing out the name and status for each one. */ void print_import_config(const char *name, nvlist_t *nv, int namewidth, int depth) { nvlist_t **child; uint_t c, children; vdev_stat_t *vs; char *type, *vname; verify(nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type) == 0); if (strcmp(type, VDEV_TYPE_MISSING) == 0 || strcmp(type, VDEV_TYPE_HOLE) == 0) return; verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &c) == 0); (void) printf("\t%*s%-*s", depth, "", namewidth - depth, name); (void) printf(" %s", zpool_state_to_name(vs->vs_state, vs->vs_aux)); if (vs->vs_aux != 0) { (void) printf(" "); switch (vs->vs_aux) { case VDEV_AUX_OPEN_FAILED: (void) printf(gettext("cannot open")); break; case VDEV_AUX_BAD_GUID_SUM: (void) printf(gettext("missing device")); break; case VDEV_AUX_NO_REPLICAS: (void) printf(gettext("insufficient replicas")); break; case VDEV_AUX_VERSION_NEWER: (void) printf(gettext("newer version")); break; case VDEV_AUX_UNSUP_FEAT: (void) printf(gettext("unsupported feature(s)")); break; case VDEV_AUX_ERR_EXCEEDED: (void) printf(gettext("too many errors")); break; case VDEV_AUX_CHILDREN_OFFLINE: (void) printf(gettext("all children offline")); break; default: (void) printf(gettext("corrupted data")); break; } } (void) printf("\n"); if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) return; for (c = 0; c < children; c++) { uint64_t is_log = B_FALSE; (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, &is_log); if (is_log) continue; vname = zpool_vdev_name(g_zfs, NULL, child[c], B_TRUE); print_import_config(vname, child[c], namewidth, depth + 2); free(vname); } if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, &child, &children) == 0) { (void) printf(gettext("\tcache\n")); for (c = 0; c < children; c++) { vname = zpool_vdev_name(g_zfs, NULL, child[c], B_FALSE); (void) printf("\t %s\n", vname); free(vname); } } if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &child, &children) == 0) { (void) printf(gettext("\tspares\n")); for (c = 0; c < children; c++) { vname = zpool_vdev_name(g_zfs, NULL, child[c], B_FALSE); (void) printf("\t %s\n", vname); free(vname); } } } /* * Print log vdevs. * Logs are recorded as top level vdevs in the main pool child array * but with "is_log" set to 1. We use either print_status_config() or * print_import_config() to print the top level logs then any log * children (eg mirrored slogs) are printed recursively - which * works because only the top level vdev is marked "is_log" */ static void print_logs(zpool_handle_t *zhp, nvlist_t *nv, int namewidth, boolean_t verbose) { uint_t c, children; nvlist_t **child; if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) return; (void) printf(gettext("\tlogs\n")); for (c = 0; c < children; c++) { uint64_t is_log = B_FALSE; char *name; (void) nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, &is_log); if (!is_log) continue; name = zpool_vdev_name(g_zfs, zhp, child[c], B_TRUE); if (verbose) print_status_config(zhp, name, child[c], namewidth, 2, B_FALSE); else print_import_config(name, child[c], namewidth, 2); free(name); } } /* * Display the status for the given pool. */ static void show_import(nvlist_t *config) { uint64_t pool_state; vdev_stat_t *vs; char *name; uint64_t guid; char *msgid; nvlist_t *nvroot; int reason; const char *health; uint_t vsc; int namewidth; char *comment; verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &name) == 0); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) == 0); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, &pool_state) == 0); verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &vsc) == 0); health = zpool_state_to_name(vs->vs_state, vs->vs_aux); reason = zpool_import_status(config, &msgid); (void) printf(gettext(" pool: %s\n"), name); (void) printf(gettext(" id: %llu\n"), (u_longlong_t)guid); (void) printf(gettext(" state: %s"), health); if (pool_state == POOL_STATE_DESTROYED) (void) printf(gettext(" (DESTROYED)")); (void) printf("\n"); switch (reason) { case ZPOOL_STATUS_MISSING_DEV_R: case ZPOOL_STATUS_MISSING_DEV_NR: case ZPOOL_STATUS_BAD_GUID_SUM: (void) printf(gettext(" status: One or more devices are " "missing from the system.\n")); break; case ZPOOL_STATUS_CORRUPT_LABEL_R: case ZPOOL_STATUS_CORRUPT_LABEL_NR: (void) printf(gettext(" status: One or more devices contains " "corrupted data.\n")); break; case ZPOOL_STATUS_CORRUPT_DATA: (void) printf( gettext(" status: The pool data is corrupted.\n")); break; case ZPOOL_STATUS_OFFLINE_DEV: (void) printf(gettext(" status: One or more devices " "are offlined.\n")); break; case ZPOOL_STATUS_CORRUPT_POOL: (void) printf(gettext(" status: The pool metadata is " "corrupted.\n")); break; case ZPOOL_STATUS_VERSION_OLDER: (void) printf(gettext(" status: The pool is formatted using a " "legacy on-disk version.\n")); break; case ZPOOL_STATUS_VERSION_NEWER: (void) printf(gettext(" status: The pool is formatted using an " "incompatible version.\n")); break; case ZPOOL_STATUS_FEAT_DISABLED: (void) printf(gettext(" status: Some supported features are " "not enabled on the pool.\n")); break; case ZPOOL_STATUS_UNSUP_FEAT_READ: (void) printf(gettext("status: The pool uses the following " "feature(s) not supported on this system:\n")); zpool_print_unsup_feat(config); break; case ZPOOL_STATUS_UNSUP_FEAT_WRITE: (void) printf(gettext("status: The pool can only be accessed " "in read-only mode on this system. It\n\tcannot be " "accessed in read-write mode because it uses the " "following\n\tfeature(s) not supported on this system:\n")); zpool_print_unsup_feat(config); break; case ZPOOL_STATUS_HOSTID_MISMATCH: (void) printf(gettext(" status: The pool was last accessed by " "another system.\n")); break; case ZPOOL_STATUS_FAULTED_DEV_R: case ZPOOL_STATUS_FAULTED_DEV_NR: (void) printf(gettext(" status: One or more devices are " "faulted.\n")); break; case ZPOOL_STATUS_BAD_LOG: (void) printf(gettext(" status: An intent log record cannot be " "read.\n")); break; case ZPOOL_STATUS_RESILVERING: (void) printf(gettext(" status: One or more devices were being " "resilvered.\n")); break; case ZPOOL_STATUS_NON_NATIVE_ASHIFT: (void) printf(gettext("status: One or more devices were " "configured to use a non-native block size.\n" "\tExpect reduced performance.\n")); break; default: /* * No other status can be seen when importing pools. */ assert(reason == ZPOOL_STATUS_OK); } /* * Print out an action according to the overall state of the pool. */ if (vs->vs_state == VDEV_STATE_HEALTHY) { if (reason == ZPOOL_STATUS_VERSION_OLDER || reason == ZPOOL_STATUS_FEAT_DISABLED) { (void) printf(gettext(" action: The pool can be " "imported using its name or numeric identifier, " "though\n\tsome features will not be available " "without an explicit 'zpool upgrade'.\n")); } else if (reason == ZPOOL_STATUS_HOSTID_MISMATCH) { (void) printf(gettext(" action: The pool can be " "imported using its name or numeric " "identifier and\n\tthe '-f' flag.\n")); } else { (void) printf(gettext(" action: The pool can be " "imported using its name or numeric " "identifier.\n")); } } else if (vs->vs_state == VDEV_STATE_DEGRADED) { (void) printf(gettext(" action: The pool can be imported " "despite missing or damaged devices. The\n\tfault " "tolerance of the pool may be compromised if imported.\n")); } else { switch (reason) { case ZPOOL_STATUS_VERSION_NEWER: (void) printf(gettext(" action: The pool cannot be " "imported. Access the pool on a system running " "newer\n\tsoftware, or recreate the pool from " "backup.\n")); break; case ZPOOL_STATUS_UNSUP_FEAT_READ: (void) printf(gettext("action: The pool cannot be " "imported. Access the pool on a system that " "supports\n\tthe required feature(s), or recreate " "the pool from backup.\n")); break; case ZPOOL_STATUS_UNSUP_FEAT_WRITE: (void) printf(gettext("action: The pool cannot be " "imported in read-write mode. Import the pool " "with\n" "\t\"-o readonly=on\", access the pool on a system " "that supports the\n\trequired feature(s), or " "recreate the pool from backup.\n")); break; case ZPOOL_STATUS_MISSING_DEV_R: case ZPOOL_STATUS_MISSING_DEV_NR: case ZPOOL_STATUS_BAD_GUID_SUM: (void) printf(gettext(" action: The pool cannot be " "imported. Attach the missing\n\tdevices and try " "again.\n")); break; default: (void) printf(gettext(" action: The pool cannot be " "imported due to damaged devices or data.\n")); } } /* Print the comment attached to the pool. */ if (nvlist_lookup_string(config, ZPOOL_CONFIG_COMMENT, &comment) == 0) (void) printf(gettext("comment: %s\n"), comment); /* * If the state is "closed" or "can't open", and the aux state * is "corrupt data": */ if (((vs->vs_state == VDEV_STATE_CLOSED) || (vs->vs_state == VDEV_STATE_CANT_OPEN)) && (vs->vs_aux == VDEV_AUX_CORRUPT_DATA)) { if (pool_state == POOL_STATE_DESTROYED) (void) printf(gettext("\tThe pool was destroyed, " "but can be imported using the '-Df' flags.\n")); else if (pool_state != POOL_STATE_EXPORTED) (void) printf(gettext("\tThe pool may be active on " "another system, but can be imported using\n\t" "the '-f' flag.\n")); } if (msgid != NULL) (void) printf(gettext(" see: http://illumos.org/msg/%s\n"), msgid); (void) printf(gettext(" config:\n\n")); namewidth = max_width(NULL, nvroot, 0, 0); if (namewidth < 10) namewidth = 10; print_import_config(name, nvroot, namewidth, 0); if (num_logs(nvroot) > 0) print_logs(NULL, nvroot, namewidth, B_FALSE); if (reason == ZPOOL_STATUS_BAD_GUID_SUM) { (void) printf(gettext("\n\tAdditional devices are known to " "be part of this pool, though their\n\texact " "configuration cannot be determined.\n")); } } /* * Perform the import for the given configuration. This passes the heavy * lifting off to zpool_import_props(), and then mounts the datasets contained * within the pool. */ static int do_import(nvlist_t *config, const char *newname, const char *mntopts, nvlist_t *props, int flags) { zpool_handle_t *zhp; char *name; uint64_t state; uint64_t version; verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &name) == 0); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, &state) == 0); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &version) == 0); if (!SPA_VERSION_IS_SUPPORTED(version)) { (void) fprintf(stderr, gettext("cannot import '%s': pool " "is formatted using an unsupported ZFS version\n"), name); return (1); } else if (state != POOL_STATE_EXPORTED && !(flags & ZFS_IMPORT_ANY_HOST)) { uint64_t hostid; if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_HOSTID, &hostid) == 0) { if ((unsigned long)hostid != gethostid()) { char *hostname; uint64_t timestamp; time_t t; verify(nvlist_lookup_string(config, ZPOOL_CONFIG_HOSTNAME, &hostname) == 0); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_TIMESTAMP, ×tamp) == 0); t = timestamp; (void) fprintf(stderr, gettext("cannot import " "'%s': pool may be in use from other " "system, it was last accessed by %s " "(hostid: 0x%lx) on %s"), name, hostname, (unsigned long)hostid, asctime(localtime(&t))); (void) fprintf(stderr, gettext("use '-f' to " "import anyway\n")); return (1); } } else { (void) fprintf(stderr, gettext("cannot import '%s': " "pool may be in use from other system\n"), name); (void) fprintf(stderr, gettext("use '-f' to import " "anyway\n")); return (1); } } if (zpool_import_props(g_zfs, config, newname, props, flags) != 0) return (1); if (newname != NULL) name = (char *)newname; if ((zhp = zpool_open_canfail(g_zfs, name)) == NULL) return (1); if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL && !(flags & ZFS_IMPORT_ONLY) && zpool_enable_datasets(zhp, mntopts, 0) != 0) { zpool_close(zhp); return (1); } zpool_close(zhp); return (0); } /* * zpool checkpoint * checkpoint --discard * * -d Discard the checkpoint from a checkpointed * --discard pool. * * Checkpoints the specified pool, by taking a "snapshot" of its * current state. A pool can only have one checkpoint at a time. */ int zpool_do_checkpoint(int argc, char **argv) { boolean_t discard; char *pool; zpool_handle_t *zhp; int c, err; struct option long_options[] = { {"discard", no_argument, NULL, 'd'}, {0, 0, 0, 0} }; discard = B_FALSE; while ((c = getopt_long(argc, argv, ":d", long_options, NULL)) != -1) { switch (c) { case 'd': discard = B_TRUE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; if (argc < 1) { (void) fprintf(stderr, gettext("missing pool argument\n")); usage(B_FALSE); } if (argc > 1) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } pool = argv[0]; if ((zhp = zpool_open(g_zfs, pool)) == NULL) { /* As a special case, check for use of '/' in the name */ if (strchr(pool, '/') != NULL) (void) fprintf(stderr, gettext("'zpool checkpoint' " "doesn't work on datasets. To save the state " "of a dataset from a specific point in time " "please use 'zfs snapshot'\n")); return (1); } if (discard) err = (zpool_discard_checkpoint(zhp) != 0); else err = (zpool_checkpoint(zhp) != 0); zpool_close(zhp); return (err); } #define CHECKPOINT_OPT 1024 /* * zpool import [-d dir] [-D] * import [-o mntopts] [-o prop=value] ... [-R root] [-D] * [-d dir | -c cachefile] [-f] -a * import [-o mntopts] [-o prop=value] ... [-R root] [-D] * [-d dir | -c cachefile] [-f] [-n] [-F] [-t] * [newpool] * * -c Read pool information from a cachefile instead of searching * devices. * * -d Scan in a specific directory, other than /dev/dsk. More than * one directory can be specified using multiple '-d' options. * * -D Scan for previously destroyed pools or import all or only * specified destroyed pools. * * -R Temporarily import the pool, with all mountpoints relative to * the given root. The pool will remain exported when the machine * is rebooted. * * -V Import even in the presence of faulted vdevs. This is an * intentionally undocumented option for testing purposes, and * treats the pool configuration as complete, leaving any bad * vdevs in the FAULTED state. In other words, it does verbatim * import. * * -f Force import, even if it appears that the pool is active. * * -F Attempt rewind if necessary. * * -n See if rewind would work, but don't actually rewind. * * -N Import the pool but don't mount datasets. * * -t Use newpool as a temporary pool name instead of renaming * the pool. * * -T Specify a starting txg to use for import. This option is * intentionally undocumented option for testing purposes. * * -a Import all pools found. * * -o Set property=value and/or temporary mount options (without '='). * * --rewind-to-checkpoint * Import the pool and revert back to the checkpoint. * * The import command scans for pools to import, and import pools based on pool * name and GUID. The pool can also be renamed as part of the import process. */ int zpool_do_import(int argc, char **argv) { char **searchdirs = NULL; int nsearch = 0; int c; int err = 0; nvlist_t *pools = NULL; boolean_t do_all = B_FALSE; boolean_t do_destroyed = B_FALSE; char *mntopts = NULL; nvpair_t *elem; nvlist_t *config; uint64_t searchguid = 0; char *searchname = NULL; char *propval; nvlist_t *found_config; nvlist_t *policy = NULL; nvlist_t *props = NULL; boolean_t first; int flags = ZFS_IMPORT_NORMAL; uint32_t rewind_policy = ZPOOL_NO_REWIND; boolean_t dryrun = B_FALSE; boolean_t do_rewind = B_FALSE; boolean_t xtreme_rewind = B_FALSE; uint64_t pool_state, txg = -1ULL; char *cachefile = NULL; importargs_t idata = { 0 }; char *endptr; struct option long_options[] = { {"rewind-to-checkpoint", no_argument, NULL, CHECKPOINT_OPT}, {0, 0, 0, 0} }; /* check options */ while ((c = getopt_long(argc, argv, ":aCc:d:DEfFmnNo:rR:tT:VX", long_options, NULL)) != -1) { switch (c) { case 'a': do_all = B_TRUE; break; case 'c': cachefile = optarg; break; case 'd': if (searchdirs == NULL) { searchdirs = safe_malloc(sizeof (char *)); } else { char **tmp = safe_malloc((nsearch + 1) * sizeof (char *)); bcopy(searchdirs, tmp, nsearch * sizeof (char *)); free(searchdirs); searchdirs = tmp; } searchdirs[nsearch++] = optarg; break; case 'D': do_destroyed = B_TRUE; break; case 'f': flags |= ZFS_IMPORT_ANY_HOST; break; case 'F': do_rewind = B_TRUE; break; case 'm': flags |= ZFS_IMPORT_MISSING_LOG; break; case 'n': dryrun = B_TRUE; break; case 'N': flags |= ZFS_IMPORT_ONLY; break; case 'o': if ((propval = strchr(optarg, '=')) != NULL) { *propval = '\0'; propval++; if (add_prop_list(optarg, propval, &props, B_TRUE)) goto error; } else { mntopts = optarg; } break; case 'R': if (add_prop_list(zpool_prop_to_name( ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE)) goto error; if (add_prop_list_default(zpool_prop_to_name( ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) goto error; break; case 't': flags |= ZFS_IMPORT_TEMP_NAME; if (add_prop_list_default(zpool_prop_to_name( ZPOOL_PROP_CACHEFILE), "none", &props, B_TRUE)) goto error; break; case 'T': errno = 0; txg = strtoull(optarg, &endptr, 0); if (errno != 0 || *endptr != '\0') { (void) fprintf(stderr, gettext("invalid txg value\n")); usage(B_FALSE); } rewind_policy = ZPOOL_DO_REWIND | ZPOOL_EXTREME_REWIND; break; case 'V': flags |= ZFS_IMPORT_VERBATIM; break; case 'X': xtreme_rewind = B_TRUE; break; case CHECKPOINT_OPT: flags |= ZFS_IMPORT_CHECKPOINT; break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); usage(B_FALSE); break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; if (cachefile && nsearch != 0) { (void) fprintf(stderr, gettext("-c is incompatible with -d\n")); usage(B_FALSE); } if ((dryrun || xtreme_rewind) && !do_rewind) { (void) fprintf(stderr, gettext("-n or -X only meaningful with -F\n")); usage(B_FALSE); } if (dryrun) rewind_policy = ZPOOL_TRY_REWIND; else if (do_rewind) rewind_policy = ZPOOL_DO_REWIND; if (xtreme_rewind) rewind_policy |= ZPOOL_EXTREME_REWIND; /* In the future, we can capture further policy and include it here */ if (nvlist_alloc(&policy, NV_UNIQUE_NAME, 0) != 0 || nvlist_add_uint64(policy, ZPOOL_LOAD_REQUEST_TXG, txg) != 0 || nvlist_add_uint32(policy, ZPOOL_LOAD_REWIND_POLICY, rewind_policy) != 0) goto error; if (searchdirs == NULL) { searchdirs = safe_malloc(sizeof (char *)); searchdirs[0] = "/dev"; nsearch = 1; } /* check argument count */ if (do_all) { if (argc != 0) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } } else { if (argc > 2) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } /* * Check for the SYS_CONFIG privilege. We do this explicitly * here because otherwise any attempt to discover pools will * silently fail. */ if (argc == 0 && !priv_ineffect(PRIV_SYS_CONFIG)) { (void) fprintf(stderr, gettext("cannot " "discover pools: permission denied\n")); free(searchdirs); nvlist_free(policy); return (1); } } /* * Depending on the arguments given, we do one of the following: * * Iterate through all pools and display information about * each one. * * -a Iterate through all pools and try to import each one. * * Find the pool that corresponds to the given GUID/pool * name and import that one. * * -D Above options applies only to destroyed pools. */ if (argc != 0) { char *endptr; errno = 0; searchguid = strtoull(argv[0], &endptr, 10); if (errno != 0 || *endptr != '\0') { searchname = argv[0]; searchguid = 0; } found_config = NULL; /* * User specified a name or guid. Ensure it's unique. */ idata.unique = B_TRUE; } idata.path = searchdirs; idata.paths = nsearch; idata.poolname = searchname; idata.guid = searchguid; idata.cachefile = cachefile; idata.policy = policy; pools = zpool_search_import(g_zfs, &idata); if (pools != NULL && idata.exists && (argc == 1 || strcmp(argv[0], argv[1]) == 0)) { (void) fprintf(stderr, gettext("cannot import '%s': " "a pool with that name already exists\n"), argv[0]); (void) fprintf(stderr, gettext("use the form 'zpool import " "[-t] ' to give it a new temporary " "or permanent name\n")); err = 1; } else if (pools == NULL && idata.exists) { (void) fprintf(stderr, gettext("cannot import '%s': " "a pool with that name is already created/imported,\n"), argv[0]); (void) fprintf(stderr, gettext("and no additional pools " "with that name were found\n")); err = 1; } else if (pools == NULL) { if (argc != 0) { (void) fprintf(stderr, gettext("cannot import '%s': " "no such pool available\n"), argv[0]); } err = 1; } if (err == 1) { free(searchdirs); nvlist_free(policy); return (1); } /* * At this point we have a list of import candidate configs. Even if * we were searching by pool name or guid, we still need to * post-process the list to deal with pool state and possible * duplicate names. */ err = 0; elem = NULL; first = B_TRUE; while ((elem = nvlist_next_nvpair(pools, elem)) != NULL) { verify(nvpair_value_nvlist(elem, &config) == 0); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, &pool_state) == 0); if (!do_destroyed && pool_state == POOL_STATE_DESTROYED) continue; if (do_destroyed && pool_state != POOL_STATE_DESTROYED) continue; verify(nvlist_add_nvlist(config, ZPOOL_LOAD_POLICY, policy) == 0); if (argc == 0) { if (first) first = B_FALSE; else if (!do_all) (void) printf("\n"); if (do_all) { err |= do_import(config, NULL, mntopts, props, flags); } else { show_import(config); } } else if (searchname != NULL) { char *name; /* * We are searching for a pool based on name. */ verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &name) == 0); if (strcmp(name, searchname) == 0) { if (found_config != NULL) { (void) fprintf(stderr, gettext( "cannot import '%s': more than " "one matching pool\n"), searchname); (void) fprintf(stderr, gettext( "import by numeric ID instead\n")); err = B_TRUE; } found_config = config; } } else { uint64_t guid; /* * Search for a pool by guid. */ verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &guid) == 0); if (guid == searchguid) found_config = config; } } /* * If we were searching for a specific pool, verify that we found a * pool, and then do the import. */ if (argc != 0 && err == 0) { if (found_config == NULL) { (void) fprintf(stderr, gettext("cannot import '%s': " "no such pool available\n"), argv[0]); err = B_TRUE; } else { err |= do_import(found_config, argc == 1 ? NULL : argv[1], mntopts, props, flags); } } /* * If we were just looking for pools, report an error if none were * found. */ if (argc == 0 && first) (void) fprintf(stderr, gettext("no pools available to import\n")); error: nvlist_free(props); nvlist_free(pools); nvlist_free(policy); free(searchdirs); return (err ? 1 : 0); } typedef struct iostat_cbdata { boolean_t cb_verbose; int cb_namewidth; int cb_iteration; zpool_list_t *cb_list; } iostat_cbdata_t; static void print_iostat_separator(iostat_cbdata_t *cb) { int i = 0; for (i = 0; i < cb->cb_namewidth; i++) (void) printf("-"); (void) printf(" ----- ----- ----- ----- ----- -----\n"); } static void print_iostat_header(iostat_cbdata_t *cb) { (void) printf("%*s capacity operations bandwidth\n", cb->cb_namewidth, ""); (void) printf("%-*s alloc free read write read write\n", cb->cb_namewidth, "pool"); print_iostat_separator(cb); } /* * Display a single statistic. */ static void print_one_stat(uint64_t value) { char buf[64]; zfs_nicenum(value, buf, sizeof (buf)); (void) printf(" %5s", buf); } /* * Print out all the statistics for the given vdev. This can either be the * toplevel configuration, or called recursively. If 'name' is NULL, then this * is a verbose output, and we don't want to display the toplevel pool stats. */ void print_vdev_stats(zpool_handle_t *zhp, const char *name, nvlist_t *oldnv, nvlist_t *newnv, iostat_cbdata_t *cb, int depth) { nvlist_t **oldchild, **newchild; uint_t c, children; vdev_stat_t *oldvs, *newvs; vdev_stat_t zerovs = { 0 }; uint64_t tdelta; double scale; char *vname; if (strcmp(name, VDEV_TYPE_INDIRECT) == 0) return; if (oldnv != NULL) { verify(nvlist_lookup_uint64_array(oldnv, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&oldvs, &c) == 0); } else { oldvs = &zerovs; } verify(nvlist_lookup_uint64_array(newnv, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&newvs, &c) == 0); if (strlen(name) + depth > cb->cb_namewidth) (void) printf("%*s%s", depth, "", name); else (void) printf("%*s%s%*s", depth, "", name, (int)(cb->cb_namewidth - strlen(name) - depth), ""); tdelta = newvs->vs_timestamp - oldvs->vs_timestamp; if (tdelta == 0) scale = 1.0; else scale = (double)NANOSEC / tdelta; /* only toplevel vdevs have capacity stats */ if (newvs->vs_space == 0) { (void) printf(" - -"); } else { print_one_stat(newvs->vs_alloc); print_one_stat(newvs->vs_space - newvs->vs_alloc); } print_one_stat((uint64_t)(scale * (newvs->vs_ops[ZIO_TYPE_READ] - oldvs->vs_ops[ZIO_TYPE_READ]))); print_one_stat((uint64_t)(scale * (newvs->vs_ops[ZIO_TYPE_WRITE] - oldvs->vs_ops[ZIO_TYPE_WRITE]))); print_one_stat((uint64_t)(scale * (newvs->vs_bytes[ZIO_TYPE_READ] - oldvs->vs_bytes[ZIO_TYPE_READ]))); print_one_stat((uint64_t)(scale * (newvs->vs_bytes[ZIO_TYPE_WRITE] - oldvs->vs_bytes[ZIO_TYPE_WRITE]))); (void) printf("\n"); if (!cb->cb_verbose) return; if (nvlist_lookup_nvlist_array(newnv, ZPOOL_CONFIG_CHILDREN, &newchild, &children) != 0) return; if (oldnv && nvlist_lookup_nvlist_array(oldnv, ZPOOL_CONFIG_CHILDREN, &oldchild, &c) != 0) return; for (c = 0; c < children; c++) { uint64_t ishole = B_FALSE, islog = B_FALSE; (void) nvlist_lookup_uint64(newchild[c], ZPOOL_CONFIG_IS_HOLE, &ishole); (void) nvlist_lookup_uint64(newchild[c], ZPOOL_CONFIG_IS_LOG, &islog); if (ishole || islog) continue; vname = zpool_vdev_name(g_zfs, zhp, newchild[c], B_FALSE); print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, newchild[c], cb, depth + 2); free(vname); } /* * Log device section */ if (num_logs(newnv) > 0) { (void) printf("%-*s - - - - - " "-\n", cb->cb_namewidth, "logs"); for (c = 0; c < children; c++) { uint64_t islog = B_FALSE; (void) nvlist_lookup_uint64(newchild[c], ZPOOL_CONFIG_IS_LOG, &islog); if (islog) { vname = zpool_vdev_name(g_zfs, zhp, newchild[c], B_FALSE); print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, newchild[c], cb, depth + 2); free(vname); } } } /* * Include level 2 ARC devices in iostat output */ if (nvlist_lookup_nvlist_array(newnv, ZPOOL_CONFIG_L2CACHE, &newchild, &children) != 0) return; if (oldnv && nvlist_lookup_nvlist_array(oldnv, ZPOOL_CONFIG_L2CACHE, &oldchild, &c) != 0) return; if (children > 0) { (void) printf("%-*s - - - - - " "-\n", cb->cb_namewidth, "cache"); for (c = 0; c < children; c++) { vname = zpool_vdev_name(g_zfs, zhp, newchild[c], B_FALSE); print_vdev_stats(zhp, vname, oldnv ? oldchild[c] : NULL, newchild[c], cb, depth + 2); free(vname); } } } static int refresh_iostat(zpool_handle_t *zhp, void *data) { iostat_cbdata_t *cb = data; boolean_t missing; /* * If the pool has disappeared, remove it from the list and continue. */ if (zpool_refresh_stats(zhp, &missing) != 0) return (-1); if (missing) pool_list_remove(cb->cb_list, zhp); return (0); } /* * Callback to print out the iostats for the given pool. */ int print_iostat(zpool_handle_t *zhp, void *data) { iostat_cbdata_t *cb = data; nvlist_t *oldconfig, *newconfig; nvlist_t *oldnvroot, *newnvroot; newconfig = zpool_get_config(zhp, &oldconfig); if (cb->cb_iteration == 1) oldconfig = NULL; verify(nvlist_lookup_nvlist(newconfig, ZPOOL_CONFIG_VDEV_TREE, &newnvroot) == 0); if (oldconfig == NULL) oldnvroot = NULL; else verify(nvlist_lookup_nvlist(oldconfig, ZPOOL_CONFIG_VDEV_TREE, &oldnvroot) == 0); /* * Print out the statistics for the pool. */ print_vdev_stats(zhp, zpool_get_name(zhp), oldnvroot, newnvroot, cb, 0); if (cb->cb_verbose) print_iostat_separator(cb); return (0); } int get_namewidth(zpool_handle_t *zhp, void *data) { iostat_cbdata_t *cb = data; nvlist_t *config, *nvroot; if ((config = zpool_get_config(zhp, NULL)) != NULL) { verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); if (!cb->cb_verbose) cb->cb_namewidth = strlen(zpool_get_name(zhp)); else cb->cb_namewidth = max_width(zhp, nvroot, 0, cb->cb_namewidth); } /* * The width must fall into the range [10,38]. The upper limit is the * maximum we can have and still fit in 80 columns. */ if (cb->cb_namewidth < 10) cb->cb_namewidth = 10; if (cb->cb_namewidth > 38) cb->cb_namewidth = 38; return (0); } /* * Parse the input string, get the 'interval' and 'count' value if there is one. */ static void get_interval_count(int *argcp, char **argv, unsigned long *iv, unsigned long *cnt) { unsigned long interval = 0, count = 0; int argc = *argcp, errno; /* * Determine if the last argument is an integer or a pool name */ if (argc > 0 && isdigit(argv[argc - 1][0])) { char *end; errno = 0; interval = strtoul(argv[argc - 1], &end, 10); if (*end == '\0' && errno == 0) { if (interval == 0) { (void) fprintf(stderr, gettext("interval " "cannot be zero\n")); usage(B_FALSE); } /* * Ignore the last parameter */ argc--; } else { /* * If this is not a valid number, just plow on. The * user will get a more informative error message later * on. */ interval = 0; } } /* * If the last argument is also an integer, then we have both a count * and an interval. */ if (argc > 0 && isdigit(argv[argc - 1][0])) { char *end; errno = 0; count = interval; interval = strtoul(argv[argc - 1], &end, 10); if (*end == '\0' && errno == 0) { if (interval == 0) { (void) fprintf(stderr, gettext("interval " "cannot be zero\n")); usage(B_FALSE); } /* * Ignore the last parameter */ argc--; } else { interval = 0; } } *iv = interval; *cnt = count; *argcp = argc; } static void get_timestamp_arg(char c) { if (c == 'u') timestamp_fmt = UDATE; else if (c == 'd') timestamp_fmt = DDATE; else usage(B_FALSE); } /* * zpool iostat [-v] [-T d|u] [pool] ... [interval [count]] * * -v Display statistics for individual vdevs * -T Display a timestamp in date(1) or Unix format * * This command can be tricky because we want to be able to deal with pool * creation/destruction as well as vdev configuration changes. The bulk of this * processing is handled by the pool_list_* routines in zpool_iter.c. We rely * on pool_list_update() to detect the addition of new pools. Configuration * changes are all handled within libzfs. */ int zpool_do_iostat(int argc, char **argv) { int c; int ret; int npools; unsigned long interval = 0, count = 0; zpool_list_t *list; boolean_t verbose = B_FALSE; iostat_cbdata_t cb; /* check options */ while ((c = getopt(argc, argv, "T:v")) != -1) { switch (c) { case 'T': get_timestamp_arg(*optarg); break; case 'v': verbose = B_TRUE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; get_interval_count(&argc, argv, &interval, &count); /* * Construct the list of all interesting pools. */ ret = 0; if ((list = pool_list_get(argc, argv, NULL, &ret)) == NULL) return (1); if (pool_list_count(list) == 0 && argc != 0) { pool_list_free(list); return (1); } if (pool_list_count(list) == 0 && interval == 0) { pool_list_free(list); (void) fprintf(stderr, gettext("no pools available\n")); return (1); } /* * Enter the main iostat loop. */ cb.cb_list = list; cb.cb_verbose = verbose; cb.cb_iteration = 0; cb.cb_namewidth = 0; for (;;) { pool_list_update(list); if ((npools = pool_list_count(list)) == 0) break; /* * Refresh all statistics. This is done as an explicit step * before calculating the maximum name width, so that any * configuration changes are properly accounted for. */ (void) pool_list_iter(list, B_FALSE, refresh_iostat, &cb); /* * Iterate over all pools to determine the maximum width * for the pool / device name column across all pools. */ cb.cb_namewidth = 0; (void) pool_list_iter(list, B_FALSE, get_namewidth, &cb); if (timestamp_fmt != NODATE) print_timestamp(timestamp_fmt); /* * If it's the first time, or verbose mode, print the header. */ if (++cb.cb_iteration == 1 || verbose) print_iostat_header(&cb); (void) pool_list_iter(list, B_FALSE, print_iostat, &cb); /* * If there's more than one pool, and we're not in verbose mode * (which prints a separator for us), then print a separator. */ if (npools > 1 && !verbose) print_iostat_separator(&cb); if (verbose) (void) printf("\n"); /* * Flush the output so that redirection to a file isn't buffered * indefinitely. */ (void) fflush(stdout); if (interval == 0) break; if (count != 0 && --count == 0) break; (void) sleep(interval); } pool_list_free(list); return (ret); } typedef struct list_cbdata { boolean_t cb_verbose; int cb_namewidth; boolean_t cb_scripted; zprop_list_t *cb_proplist; boolean_t cb_literal; } list_cbdata_t; /* * Given a list of columns to display, output appropriate headers for each one. */ static void print_header(list_cbdata_t *cb) { zprop_list_t *pl = cb->cb_proplist; char headerbuf[ZPOOL_MAXPROPLEN]; const char *header; boolean_t first = B_TRUE; boolean_t right_justify; size_t width = 0; for (; pl != NULL; pl = pl->pl_next) { width = pl->pl_width; if (first && cb->cb_verbose) { /* * Reset the width to accommodate the verbose listing * of devices. */ width = cb->cb_namewidth; } if (!first) (void) printf(" "); else first = B_FALSE; right_justify = B_FALSE; if (pl->pl_prop != ZPROP_INVAL) { header = zpool_prop_column_name(pl->pl_prop); right_justify = zpool_prop_align_right(pl->pl_prop); } else { int i; for (i = 0; pl->pl_user_prop[i] != '\0'; i++) headerbuf[i] = toupper(pl->pl_user_prop[i]); headerbuf[i] = '\0'; header = headerbuf; } if (pl->pl_next == NULL && !right_justify) (void) printf("%s", header); else if (right_justify) (void) printf("%*s", width, header); else (void) printf("%-*s", width, header); } (void) printf("\n"); } /* * Given a pool and a list of properties, print out all the properties according * to the described layout. */ static void print_pool(zpool_handle_t *zhp, list_cbdata_t *cb) { zprop_list_t *pl = cb->cb_proplist; boolean_t first = B_TRUE; char property[ZPOOL_MAXPROPLEN]; char *propstr; boolean_t right_justify; size_t width; for (; pl != NULL; pl = pl->pl_next) { width = pl->pl_width; if (first && cb->cb_verbose) { /* * Reset the width to accommodate the verbose listing * of devices. */ width = cb->cb_namewidth; } if (!first) { if (cb->cb_scripted) (void) printf("\t"); else (void) printf(" "); } else { first = B_FALSE; } right_justify = B_FALSE; if (pl->pl_prop != ZPROP_INVAL) { if (zpool_get_prop(zhp, pl->pl_prop, property, sizeof (property), NULL, cb->cb_literal) != 0) propstr = "-"; else propstr = property; right_justify = zpool_prop_align_right(pl->pl_prop); } else if ((zpool_prop_feature(pl->pl_user_prop) || zpool_prop_unsupported(pl->pl_user_prop)) && zpool_prop_get_feature(zhp, pl->pl_user_prop, property, sizeof (property)) == 0) { propstr = property; } else { propstr = "-"; } /* * If this is being called in scripted mode, or if this is the * last column and it is left-justified, don't include a width * format specifier. */ if (cb->cb_scripted || (pl->pl_next == NULL && !right_justify)) (void) printf("%s", propstr); else if (right_justify) (void) printf("%*s", width, propstr); else (void) printf("%-*s", width, propstr); } (void) printf("\n"); } static void print_one_column(zpool_prop_t prop, uint64_t value, boolean_t scripted, boolean_t valid) { char propval[64]; boolean_t fixed; size_t width = zprop_width(prop, &fixed, ZFS_TYPE_POOL); switch (prop) { case ZPOOL_PROP_EXPANDSZ: case ZPOOL_PROP_CHECKPOINT: if (value == 0) (void) strlcpy(propval, "-", sizeof (propval)); else zfs_nicenum(value, propval, sizeof (propval)); break; case ZPOOL_PROP_FRAGMENTATION: if (value == ZFS_FRAG_INVALID) { (void) strlcpy(propval, "-", sizeof (propval)); } else { (void) snprintf(propval, sizeof (propval), "%llu%%", value); } break; case ZPOOL_PROP_CAPACITY: (void) snprintf(propval, sizeof (propval), "%llu%%", value); break; default: zfs_nicenum(value, propval, sizeof (propval)); } if (!valid) (void) strlcpy(propval, "-", sizeof (propval)); if (scripted) (void) printf("\t%s", propval); else (void) printf(" %*s", width, propval); } void print_list_stats(zpool_handle_t *zhp, const char *name, nvlist_t *nv, list_cbdata_t *cb, int depth) { nvlist_t **child; vdev_stat_t *vs; uint_t c, children; char *vname; boolean_t scripted = cb->cb_scripted; uint64_t islog = B_FALSE; boolean_t haslog = B_FALSE; char *dashes = "%-*s - - - - - -\n"; verify(nvlist_lookup_uint64_array(nv, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &c) == 0); if (name != NULL) { boolean_t toplevel = (vs->vs_space != 0); uint64_t cap; if (strcmp(name, VDEV_TYPE_INDIRECT) == 0) return; if (scripted) (void) printf("\t%s", name); else if (strlen(name) + depth > cb->cb_namewidth) (void) printf("%*s%s", depth, "", name); else (void) printf("%*s%s%*s", depth, "", name, (int)(cb->cb_namewidth - strlen(name) - depth), ""); /* * Print the properties for the individual vdevs. Some * properties are only applicable to toplevel vdevs. The * 'toplevel' boolean value is passed to the print_one_column() * to indicate that the value is valid. */ print_one_column(ZPOOL_PROP_SIZE, vs->vs_space, scripted, toplevel); print_one_column(ZPOOL_PROP_ALLOCATED, vs->vs_alloc, scripted, toplevel); print_one_column(ZPOOL_PROP_FREE, vs->vs_space - vs->vs_alloc, scripted, toplevel); print_one_column(ZPOOL_PROP_CHECKPOINT, vs->vs_checkpoint_space, scripted, toplevel); print_one_column(ZPOOL_PROP_EXPANDSZ, vs->vs_esize, scripted, B_TRUE); print_one_column(ZPOOL_PROP_FRAGMENTATION, vs->vs_fragmentation, scripted, (vs->vs_fragmentation != ZFS_FRAG_INVALID && toplevel)); cap = (vs->vs_space == 0) ? 0 : (vs->vs_alloc * 100 / vs->vs_space); print_one_column(ZPOOL_PROP_CAPACITY, cap, scripted, toplevel); (void) printf("\n"); } if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, &children) != 0) return; for (c = 0; c < children; c++) { uint64_t ishole = B_FALSE; if (nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_HOLE, &ishole) == 0 && ishole) continue; if (nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, &islog) == 0 && islog) { haslog = B_TRUE; continue; } vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); print_list_stats(zhp, vname, child[c], cb, depth + 2); free(vname); } if (haslog == B_TRUE) { /* LINTED E_SEC_PRINTF_VAR_FMT */ (void) printf(dashes, cb->cb_namewidth, "log"); for (c = 0; c < children; c++) { if (nvlist_lookup_uint64(child[c], ZPOOL_CONFIG_IS_LOG, &islog) != 0 || !islog) continue; vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); print_list_stats(zhp, vname, child[c], cb, depth + 2); free(vname); } } if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE, &child, &children) == 0 && children > 0) { /* LINTED E_SEC_PRINTF_VAR_FMT */ (void) printf(dashes, cb->cb_namewidth, "cache"); for (c = 0; c < children; c++) { vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); print_list_stats(zhp, vname, child[c], cb, depth + 2); free(vname); } } if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES, &child, &children) == 0 && children > 0) { /* LINTED E_SEC_PRINTF_VAR_FMT */ (void) printf(dashes, cb->cb_namewidth, "spare"); for (c = 0; c < children; c++) { vname = zpool_vdev_name(g_zfs, zhp, child[c], B_FALSE); print_list_stats(zhp, vname, child[c], cb, depth + 2); free(vname); } } } /* * Generic callback function to list a pool. */ int list_callback(zpool_handle_t *zhp, void *data) { list_cbdata_t *cbp = data; nvlist_t *config; nvlist_t *nvroot; config = zpool_get_config(zhp, NULL); print_pool(zhp, cbp); if (!cbp->cb_verbose) return (0); verify(nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0); print_list_stats(zhp, NULL, nvroot, cbp, 0); return (0); } /* * zpool list [-Hp] [-o prop[,prop]*] [-T d|u] [pool] ... [interval [count]] * * -H Scripted mode. Don't display headers, and separate properties * by a single tab. * -o List of properties to display. Defaults to * "name,size,allocated,free,expandsize,fragmentation,capacity," * "dedupratio,health,altroot" * -p Diplay values in parsable (exact) format. * -T Display a timestamp in date(1) or Unix format * * List all pools in the system, whether or not they're healthy. Output space * statistics for each one, as well as health status summary. */ int zpool_do_list(int argc, char **argv) { int c; int ret; list_cbdata_t cb = { 0 }; static char default_props[] = "name,size,allocated,free,checkpoint,expandsize,fragmentation," "capacity,dedupratio,health,altroot"; char *props = default_props; unsigned long interval = 0, count = 0; zpool_list_t *list; boolean_t first = B_TRUE; /* check options */ while ((c = getopt(argc, argv, ":Ho:pT:v")) != -1) { switch (c) { case 'H': cb.cb_scripted = B_TRUE; break; case 'o': props = optarg; break; case 'p': cb.cb_literal = B_TRUE; break; case 'T': get_timestamp_arg(*optarg); break; case 'v': cb.cb_verbose = B_TRUE; break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); usage(B_FALSE); break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; get_interval_count(&argc, argv, &interval, &count); if (zprop_get_list(g_zfs, props, &cb.cb_proplist, ZFS_TYPE_POOL) != 0) usage(B_FALSE); for (;;) { if ((list = pool_list_get(argc, argv, &cb.cb_proplist, &ret)) == NULL) return (1); if (pool_list_count(list) == 0) break; cb.cb_namewidth = 0; (void) pool_list_iter(list, B_FALSE, get_namewidth, &cb); if (timestamp_fmt != NODATE) print_timestamp(timestamp_fmt); if (!cb.cb_scripted && (first || cb.cb_verbose)) { print_header(&cb); first = B_FALSE; } ret = pool_list_iter(list, B_TRUE, list_callback, &cb); if (interval == 0) break; if (count != 0 && --count == 0) break; pool_list_free(list); (void) sleep(interval); } if (argc == 0 && !cb.cb_scripted && pool_list_count(list) == 0) { (void) printf(gettext("no pools available\n")); ret = 0; } pool_list_free(list); zprop_free_list(cb.cb_proplist); return (ret); } static int zpool_do_attach_or_replace(int argc, char **argv, int replacing) { boolean_t force = B_FALSE; int c; nvlist_t *nvroot; char *poolname, *old_disk, *new_disk; zpool_handle_t *zhp; zpool_boot_label_t boot_type; uint64_t boot_size; int ret; /* check options */ while ((c = getopt(argc, argv, "f")) != -1) { switch (c) { case 'f': force = B_TRUE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; /* get pool name and check number of arguments */ if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name argument\n")); usage(B_FALSE); } poolname = argv[0]; if (argc < 2) { (void) fprintf(stderr, gettext("missing specification\n")); usage(B_FALSE); } old_disk = argv[1]; if (argc < 3) { if (!replacing) { (void) fprintf(stderr, gettext("missing specification\n")); usage(B_FALSE); } new_disk = old_disk; argc -= 1; argv += 1; } else { new_disk = argv[2]; argc -= 2; argv += 2; } if (argc > 1) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } if ((zhp = zpool_open(g_zfs, poolname)) == NULL) return (1); if (zpool_get_config(zhp, NULL) == NULL) { (void) fprintf(stderr, gettext("pool '%s' is unavailable\n"), poolname); zpool_close(zhp); return (1); } if (zpool_is_bootable(zhp)) boot_type = ZPOOL_COPY_BOOT_LABEL; else boot_type = ZPOOL_NO_BOOT_LABEL; boot_size = zpool_get_prop_int(zhp, ZPOOL_PROP_BOOTSIZE, NULL); nvroot = make_root_vdev(zhp, force, B_FALSE, replacing, B_FALSE, boot_type, boot_size, argc, argv); if (nvroot == NULL) { zpool_close(zhp); return (1); } ret = zpool_vdev_attach(zhp, old_disk, new_disk, nvroot, replacing); nvlist_free(nvroot); zpool_close(zhp); return (ret); } /* * zpool replace [-f] * * -f Force attach, even if appears to be in use. * * Replace with . */ /* ARGSUSED */ int zpool_do_replace(int argc, char **argv) { return (zpool_do_attach_or_replace(argc, argv, B_TRUE)); } /* * zpool attach [-f] * * -f Force attach, even if appears to be in use. * * Attach to the mirror containing . If is not * part of a mirror, then will be transformed into a mirror of * and . In either case, will begin life * with a DTL of [0, now], and will immediately begin to resilver itself. */ int zpool_do_attach(int argc, char **argv) { return (zpool_do_attach_or_replace(argc, argv, B_FALSE)); } /* * zpool detach [-f] * * -f Force detach of , even if DTLs argue against it * (not supported yet) * * Detach a device from a mirror. The operation will be refused if * is the last device in the mirror, or if the DTLs indicate that this device * has the only valid copy of some data. */ /* ARGSUSED */ int zpool_do_detach(int argc, char **argv) { int c; char *poolname, *path; zpool_handle_t *zhp; int ret; /* check options */ while ((c = getopt(argc, argv, "f")) != -1) { switch (c) { case 'f': case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; /* get pool name and check number of arguments */ if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name argument\n")); usage(B_FALSE); } if (argc < 2) { (void) fprintf(stderr, gettext("missing specification\n")); usage(B_FALSE); } poolname = argv[0]; path = argv[1]; if ((zhp = zpool_open(g_zfs, poolname)) == NULL) return (1); ret = zpool_vdev_detach(zhp, path); zpool_close(zhp); return (ret); } /* * zpool split [-n] [-o prop=val] ... * [-o mntopt] ... * [-R altroot] [ ...] * * -n Do not split the pool, but display the resulting layout if * it were to be split. * -o Set property=value, or set mount options. * -R Mount the split-off pool under an alternate root. * * Splits the named pool and gives it the new pool name. Devices to be split * off may be listed, provided that no more than one device is specified * per top-level vdev mirror. The newly split pool is left in an exported * state unless -R is specified. * * Restrictions: the top-level of the pool pool must only be made up of * mirrors; all devices in the pool must be healthy; no device may be * undergoing a resilvering operation. */ int zpool_do_split(int argc, char **argv) { char *srcpool, *newpool, *propval; char *mntopts = NULL; splitflags_t flags; int c, ret = 0; zpool_handle_t *zhp; nvlist_t *config, *props = NULL; flags.dryrun = B_FALSE; flags.import = B_FALSE; /* check options */ while ((c = getopt(argc, argv, ":R:no:")) != -1) { switch (c) { case 'R': flags.import = B_TRUE; if (add_prop_list( zpool_prop_to_name(ZPOOL_PROP_ALTROOT), optarg, &props, B_TRUE) != 0) { nvlist_free(props); usage(B_FALSE); } break; case 'n': flags.dryrun = B_TRUE; break; case 'o': if ((propval = strchr(optarg, '=')) != NULL) { *propval = '\0'; propval++; if (add_prop_list(optarg, propval, &props, B_TRUE) != 0) { nvlist_free(props); usage(B_FALSE); } } else { mntopts = optarg; } break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); usage(B_FALSE); break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); break; } } if (!flags.import && mntopts != NULL) { (void) fprintf(stderr, gettext("setting mntopts is only " "valid when importing the pool\n")); usage(B_FALSE); } argc -= optind; argv += optind; if (argc < 1) { (void) fprintf(stderr, gettext("Missing pool name\n")); usage(B_FALSE); } if (argc < 2) { (void) fprintf(stderr, gettext("Missing new pool name\n")); usage(B_FALSE); } srcpool = argv[0]; newpool = argv[1]; argc -= 2; argv += 2; if ((zhp = zpool_open(g_zfs, srcpool)) == NULL) return (1); config = split_mirror_vdev(zhp, newpool, props, flags, argc, argv); if (config == NULL) { ret = 1; } else { if (flags.dryrun) { (void) printf(gettext("would create '%s' with the " "following layout:\n\n"), newpool); print_vdev_tree(NULL, newpool, config, 0, B_FALSE); } nvlist_free(config); } zpool_close(zhp); if (ret != 0 || flags.dryrun || !flags.import) return (ret); /* * The split was successful. Now we need to open the new * pool and import it. */ if ((zhp = zpool_open_canfail(g_zfs, newpool)) == NULL) return (1); if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL && zpool_enable_datasets(zhp, mntopts, 0) != 0) { ret = 1; (void) fprintf(stderr, gettext("Split was successful, but " "the datasets could not all be mounted\n")); (void) fprintf(stderr, gettext("Try doing '%s' with a " "different altroot\n"), "zpool import"); } zpool_close(zhp); return (ret); } /* * zpool online ... */ int zpool_do_online(int argc, char **argv) { int c, i; char *poolname; zpool_handle_t *zhp; int ret = 0; vdev_state_t newstate; int flags = 0; /* check options */ while ((c = getopt(argc, argv, "et")) != -1) { switch (c) { case 'e': flags |= ZFS_ONLINE_EXPAND; break; case 't': case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; /* get pool name and check number of arguments */ if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name\n")); usage(B_FALSE); } if (argc < 2) { (void) fprintf(stderr, gettext("missing device name\n")); usage(B_FALSE); } poolname = argv[0]; if ((zhp = zpool_open(g_zfs, poolname)) == NULL) return (1); for (i = 1; i < argc; i++) { if (zpool_vdev_online(zhp, argv[i], flags, &newstate) == 0) { if (newstate != VDEV_STATE_HEALTHY) { (void) printf(gettext("warning: device '%s' " "onlined, but remains in faulted state\n"), argv[i]); if (newstate == VDEV_STATE_FAULTED) (void) printf(gettext("use 'zpool " "clear' to restore a faulted " "device\n")); else (void) printf(gettext("use 'zpool " "replace' to replace devices " "that are no longer present\n")); } } else { ret = 1; } } zpool_close(zhp); return (ret); } /* * zpool offline [-ft] ... * * -f Force the device into the offline state, even if doing * so would appear to compromise pool availability. * (not supported yet) * * -t Only take the device off-line temporarily. The offline * state will not be persistent across reboots. */ /* ARGSUSED */ int zpool_do_offline(int argc, char **argv) { int c, i; char *poolname; zpool_handle_t *zhp; int ret = 0; boolean_t istmp = B_FALSE; /* check options */ while ((c = getopt(argc, argv, "ft")) != -1) { switch (c) { case 't': istmp = B_TRUE; break; case 'f': case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; /* get pool name and check number of arguments */ if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name\n")); usage(B_FALSE); } if (argc < 2) { (void) fprintf(stderr, gettext("missing device name\n")); usage(B_FALSE); } poolname = argv[0]; if ((zhp = zpool_open(g_zfs, poolname)) == NULL) return (1); for (i = 1; i < argc; i++) { if (zpool_vdev_offline(zhp, argv[i], istmp) != 0) ret = 1; } zpool_close(zhp); return (ret); } /* * zpool clear [device] * * Clear all errors associated with a pool or a particular device. */ int zpool_do_clear(int argc, char **argv) { int c; int ret = 0; boolean_t dryrun = B_FALSE; boolean_t do_rewind = B_FALSE; boolean_t xtreme_rewind = B_FALSE; uint32_t rewind_policy = ZPOOL_NO_REWIND; nvlist_t *policy = NULL; zpool_handle_t *zhp; char *pool, *device; /* check options */ while ((c = getopt(argc, argv, "FnX")) != -1) { switch (c) { case 'F': do_rewind = B_TRUE; break; case 'n': dryrun = B_TRUE; break; case 'X': xtreme_rewind = B_TRUE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name\n")); usage(B_FALSE); } if (argc > 2) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } if ((dryrun || xtreme_rewind) && !do_rewind) { (void) fprintf(stderr, gettext("-n or -X only meaningful with -F\n")); usage(B_FALSE); } if (dryrun) rewind_policy = ZPOOL_TRY_REWIND; else if (do_rewind) rewind_policy = ZPOOL_DO_REWIND; if (xtreme_rewind) rewind_policy |= ZPOOL_EXTREME_REWIND; /* In future, further rewind policy choices can be passed along here */ if (nvlist_alloc(&policy, NV_UNIQUE_NAME, 0) != 0 || nvlist_add_uint32(policy, ZPOOL_LOAD_REWIND_POLICY, rewind_policy) != 0) { return (1); } pool = argv[0]; device = argc == 2 ? argv[1] : NULL; if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL) { nvlist_free(policy); return (1); } if (zpool_clear(zhp, device, policy) != 0) ret = 1; zpool_close(zhp); nvlist_free(policy); return (ret); } /* * zpool reguid */ int zpool_do_reguid(int argc, char **argv) { int c; char *poolname; zpool_handle_t *zhp; int ret = 0; /* check options */ while ((c = getopt(argc, argv, "")) != -1) { switch (c) { case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; /* get pool name and check number of arguments */ if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name\n")); usage(B_FALSE); } if (argc > 1) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } poolname = argv[0]; if ((zhp = zpool_open(g_zfs, poolname)) == NULL) return (1); ret = zpool_reguid(zhp); zpool_close(zhp); return (ret); } /* * zpool reopen * * Reopen the pool so that the kernel can update the sizes of all vdevs. */ int zpool_do_reopen(int argc, char **argv) { int c; int ret = 0; zpool_handle_t *zhp; char *pool; /* check options */ while ((c = getopt(argc, argv, "")) != -1) { switch (c) { case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc--; argv++; if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name\n")); usage(B_FALSE); } if (argc > 1) { (void) fprintf(stderr, gettext("too many arguments\n")); usage(B_FALSE); } pool = argv[0]; if ((zhp = zpool_open_canfail(g_zfs, pool)) == NULL) return (1); ret = zpool_reopen(zhp); zpool_close(zhp); return (ret); } typedef struct scrub_cbdata { int cb_type; int cb_argc; char **cb_argv; pool_scrub_cmd_t cb_scrub_cmd; } scrub_cbdata_t; static boolean_t zpool_has_checkpoint(zpool_handle_t *zhp) { nvlist_t *config, *nvroot; config = zpool_get_config(zhp, NULL); if (config != NULL) { pool_checkpoint_stat_t *pcs = NULL; uint_t c; nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE); (void) nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_CHECKPOINT_STATS, (uint64_t **)&pcs, &c); if (pcs == NULL || pcs->pcs_state == CS_NONE) return (B_FALSE); assert(pcs->pcs_state == CS_CHECKPOINT_EXISTS || pcs->pcs_state == CS_CHECKPOINT_DISCARDING); return (B_TRUE); } return (B_FALSE); } int scrub_callback(zpool_handle_t *zhp, void *data) { scrub_cbdata_t *cb = data; int err; /* * Ignore faulted pools. */ if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { (void) fprintf(stderr, gettext("cannot scrub '%s': pool is " "currently unavailable\n"), zpool_get_name(zhp)); return (1); } err = zpool_scan(zhp, cb->cb_type, cb->cb_scrub_cmd); if (err == 0 && zpool_has_checkpoint(zhp) && cb->cb_type == POOL_SCAN_SCRUB) { (void) printf(gettext("warning: will not scrub state that " "belongs to the checkpoint of pool '%s'\n"), zpool_get_name(zhp)); } return (err != 0); } /* * zpool scrub [-s | -p] ... * * -s Stop. Stops any in-progress scrub. * -p Pause. Pause in-progress scrub. */ int zpool_do_scrub(int argc, char **argv) { int c; scrub_cbdata_t cb; cb.cb_type = POOL_SCAN_SCRUB; cb.cb_scrub_cmd = POOL_SCRUB_NORMAL; /* check options */ while ((c = getopt(argc, argv, "sp")) != -1) { switch (c) { case 's': cb.cb_type = POOL_SCAN_NONE; break; case 'p': cb.cb_scrub_cmd = POOL_SCRUB_PAUSE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } if (cb.cb_type == POOL_SCAN_NONE && cb.cb_scrub_cmd == POOL_SCRUB_PAUSE) { (void) fprintf(stderr, gettext("invalid option combination: " "-s and -p are mutually exclusive\n")); usage(B_FALSE); } cb.cb_argc = argc; cb.cb_argv = argv; argc -= optind; argv += optind; if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name argument\n")); usage(B_FALSE); } return (for_each_pool(argc, argv, B_TRUE, NULL, scrub_callback, &cb)); } static void zpool_collect_leaves(zpool_handle_t *zhp, nvlist_t *nvroot, nvlist_t *res) { uint_t children = 0; nvlist_t **child; uint_t i; (void) nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, &child, &children); if (children == 0) { char *path = zpool_vdev_name(g_zfs, zhp, nvroot, B_FALSE); fnvlist_add_boolean(res, path); free(path); return; } for (i = 0; i < children; i++) { zpool_collect_leaves(zhp, child[i], res); } } /* * zpool initialize [-cs] [ ...] * Initialize all unused blocks in the specified vdevs, or all vdevs in the pool * if none specified. * * -c Cancel. Ends active initializing. * -s Suspend. Initializing can then be restarted with no flags. */ int zpool_do_initialize(int argc, char **argv) { int c; char *poolname; zpool_handle_t *zhp; nvlist_t *vdevs; int err = 0; struct option long_options[] = { {"cancel", no_argument, NULL, 'c'}, {"suspend", no_argument, NULL, 's'}, {0, 0, 0, 0} }; pool_initialize_func_t cmd_type = POOL_INITIALIZE_DO; while ((c = getopt_long(argc, argv, "cs", long_options, NULL)) != -1) { switch (c) { case 'c': if (cmd_type != POOL_INITIALIZE_DO) { (void) fprintf(stderr, gettext("-c cannot be " "combined with other options\n")); usage(B_FALSE); } cmd_type = POOL_INITIALIZE_CANCEL; break; case 's': if (cmd_type != POOL_INITIALIZE_DO) { (void) fprintf(stderr, gettext("-s cannot be " "combined with other options\n")); usage(B_FALSE); } cmd_type = POOL_INITIALIZE_SUSPEND; break; case '?': if (optopt != 0) { (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); } else { (void) fprintf(stderr, gettext("invalid option '%s'\n"), argv[optind - 1]); } usage(B_FALSE); } } argc -= optind; argv += optind; if (argc < 1) { (void) fprintf(stderr, gettext("missing pool name argument\n")); usage(B_FALSE); return (-1); } poolname = argv[0]; zhp = zpool_open(g_zfs, poolname); if (zhp == NULL) return (-1); vdevs = fnvlist_alloc(); if (argc == 1) { /* no individual leaf vdevs specified, so add them all */ nvlist_t *config = zpool_get_config(zhp, NULL); nvlist_t *nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE); zpool_collect_leaves(zhp, nvroot, vdevs); } else { int i; for (i = 1; i < argc; i++) { fnvlist_add_boolean(vdevs, argv[i]); } } err = zpool_initialize(zhp, cmd_type, vdevs); fnvlist_free(vdevs); zpool_close(zhp); return (err); } typedef struct status_cbdata { int cb_count; boolean_t cb_allpools; boolean_t cb_verbose; boolean_t cb_explain; boolean_t cb_first; boolean_t cb_dedup_stats; } status_cbdata_t; /* * Print out detailed scrub status. */ static void print_scan_status(pool_scan_stat_t *ps) { time_t start, end, pause; uint64_t total_secs_left; uint64_t elapsed, secs_left, mins_left, hours_left, days_left; uint64_t pass_scanned, scanned, pass_issued, issued, total; uint_t scan_rate, issue_rate; double fraction_done; char processed_buf[7], scanned_buf[7], issued_buf[7], total_buf[7]; char srate_buf[7], irate_buf[7]; (void) printf(gettext(" scan: ")); /* If there's never been a scan, there's not much to say. */ if (ps == NULL || ps->pss_func == POOL_SCAN_NONE || ps->pss_func >= POOL_SCAN_FUNCS) { (void) printf(gettext("none requested\n")); return; } start = ps->pss_start_time; end = ps->pss_end_time; pause = ps->pss_pass_scrub_pause; zfs_nicenum(ps->pss_processed, processed_buf, sizeof (processed_buf)); assert(ps->pss_func == POOL_SCAN_SCRUB || ps->pss_func == POOL_SCAN_RESILVER); /* Scan is finished or canceled. */ if (ps->pss_state == DSS_FINISHED) { total_secs_left = end - start; days_left = total_secs_left / 60 / 60 / 24; hours_left = (total_secs_left / 60 / 60) % 24; mins_left = (total_secs_left / 60) % 60; secs_left = (total_secs_left % 60); if (ps->pss_func == POOL_SCAN_SCRUB) { (void) printf(gettext("scrub repaired %s " "in %llu days %02llu:%02llu:%02llu " "with %llu errors on %s"), processed_buf, (u_longlong_t)days_left, (u_longlong_t)hours_left, (u_longlong_t)mins_left, (u_longlong_t)secs_left, (u_longlong_t)ps->pss_errors, ctime(&end)); } else if (ps->pss_func == POOL_SCAN_RESILVER) { (void) printf(gettext("resilvered %s " "in %llu days %02llu:%02llu:%02llu " "with %llu errors on %s"), processed_buf, (u_longlong_t)days_left, (u_longlong_t)hours_left, (u_longlong_t)mins_left, (u_longlong_t)secs_left, (u_longlong_t)ps->pss_errors, ctime(&end)); } return; } else if (ps->pss_state == DSS_CANCELED) { if (ps->pss_func == POOL_SCAN_SCRUB) { (void) printf(gettext("scrub canceled on %s"), ctime(&end)); } else if (ps->pss_func == POOL_SCAN_RESILVER) { (void) printf(gettext("resilver canceled on %s"), ctime(&end)); } return; } assert(ps->pss_state == DSS_SCANNING); /* Scan is in progress. Resilvers can't be paused. */ if (ps->pss_func == POOL_SCAN_SCRUB) { if (pause == 0) { (void) printf(gettext("scrub in progress since %s"), ctime(&start)); } else { (void) printf(gettext("scrub paused since %s"), ctime(&pause)); (void) printf(gettext("\tscrub started on %s"), ctime(&start)); } } else if (ps->pss_func == POOL_SCAN_RESILVER) { (void) printf(gettext("resilver in progress since %s"), ctime(&start)); } scanned = ps->pss_examined; pass_scanned = ps->pss_pass_exam; issued = ps->pss_issued; pass_issued = ps->pss_pass_issued; total = ps->pss_to_examine; /* we are only done with a block once we have issued the IO for it */ fraction_done = (double)issued / total; /* elapsed time for this pass, rounding up to 1 if it's 0 */ elapsed = time(NULL) - ps->pss_pass_start; elapsed -= ps->pss_pass_scrub_spent_paused; elapsed = (elapsed != 0) ? elapsed : 1; scan_rate = pass_scanned / elapsed; issue_rate = pass_issued / elapsed; total_secs_left = (issue_rate != 0) ? ((total - issued) / issue_rate) : UINT64_MAX; days_left = total_secs_left / 60 / 60 / 24; hours_left = (total_secs_left / 60 / 60) % 24; mins_left = (total_secs_left / 60) % 60; secs_left = (total_secs_left % 60); /* format all of the numbers we will be reporting */ zfs_nicenum(scanned, scanned_buf, sizeof (scanned_buf)); zfs_nicenum(issued, issued_buf, sizeof (issued_buf)); zfs_nicenum(total, total_buf, sizeof (total_buf)); zfs_nicenum(scan_rate, srate_buf, sizeof (srate_buf)); zfs_nicenum(issue_rate, irate_buf, sizeof (irate_buf)); /* doo not print estimated time if we have a paused scrub */ if (pause == 0) { (void) printf(gettext("\t%s scanned at %s/s, " "%s issued at %s/s, %s total\n"), scanned_buf, srate_buf, issued_buf, irate_buf, total_buf); } else { (void) printf(gettext("\t%s scanned, %s issued, %s total\n"), scanned_buf, issued_buf, total_buf); } if (ps->pss_func == POOL_SCAN_RESILVER) { (void) printf(gettext("\t%s resilvered, %.2f%% done"), processed_buf, 100 * fraction_done); } else if (ps->pss_func == POOL_SCAN_SCRUB) { (void) printf(gettext("\t%s repaired, %.2f%% done"), processed_buf, 100 * fraction_done); } if (pause == 0) { if (issue_rate >= 10 * 1024 * 1024) { (void) printf(gettext(", %llu days " "%02llu:%02llu:%02llu to go\n"), (u_longlong_t)days_left, (u_longlong_t)hours_left, (u_longlong_t)mins_left, (u_longlong_t)secs_left); } else { (void) printf(gettext(", no estimated " "completion time\n")); } } else { (void) printf(gettext("\n")); } } /* * As we don't scrub checkpointed blocks, we want to warn the * user that we skipped scanning some blocks if a checkpoint exists * or existed at any time during the scan. */ static void print_checkpoint_scan_warning(pool_scan_stat_t *ps, pool_checkpoint_stat_t *pcs) { if (ps == NULL || pcs == NULL) return; if (pcs->pcs_state == CS_NONE || pcs->pcs_state == CS_CHECKPOINT_DISCARDING) return; assert(pcs->pcs_state == CS_CHECKPOINT_EXISTS); if (ps->pss_state == DSS_NONE) return; if ((ps->pss_state == DSS_FINISHED || ps->pss_state == DSS_CANCELED) && ps->pss_end_time < pcs->pcs_start_time) return; if (ps->pss_state == DSS_FINISHED || ps->pss_state == DSS_CANCELED) { (void) printf(gettext(" scan warning: skipped blocks " "that are only referenced by the checkpoint.\n")); } else { assert(ps->pss_state == DSS_SCANNING); (void) printf(gettext(" scan warning: skipping blocks " "that are only referenced by the checkpoint.\n")); } } /* * Print out detailed removal status. */ static void print_removal_status(zpool_handle_t *zhp, pool_removal_stat_t *prs) { char copied_buf[7], examined_buf[7], total_buf[7], rate_buf[7]; time_t start, end; nvlist_t *config, *nvroot; nvlist_t **child; uint_t children; char *vdev_name; if (prs == NULL || prs->prs_state == DSS_NONE) return; /* * Determine name of vdev. */ config = zpool_get_config(zhp, NULL); nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE); verify(nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, &child, &children) == 0); assert(prs->prs_removing_vdev < children); vdev_name = zpool_vdev_name(g_zfs, zhp, child[prs->prs_removing_vdev], B_TRUE); (void) printf(gettext("remove: ")); start = prs->prs_start_time; end = prs->prs_end_time; zfs_nicenum(prs->prs_copied, copied_buf, sizeof (copied_buf)); /* * Removal is finished or canceled. */ if (prs->prs_state == DSS_FINISHED) { uint64_t minutes_taken = (end - start) / 60; (void) printf(gettext("Removal of vdev %llu copied %s " "in %lluh%um, completed on %s"), (longlong_t)prs->prs_removing_vdev, copied_buf, (u_longlong_t)(minutes_taken / 60), (uint_t)(minutes_taken % 60), ctime((time_t *)&end)); } else if (prs->prs_state == DSS_CANCELED) { (void) printf(gettext("Removal of %s canceled on %s"), vdev_name, ctime(&end)); } else { uint64_t copied, total, elapsed, mins_left, hours_left; double fraction_done; uint_t rate; assert(prs->prs_state == DSS_SCANNING); /* * Removal is in progress. */ (void) printf(gettext( "Evacuation of %s in progress since %s"), vdev_name, ctime(&start)); copied = prs->prs_copied > 0 ? prs->prs_copied : 1; total = prs->prs_to_copy; fraction_done = (double)copied / total; /* elapsed time for this pass */ elapsed = time(NULL) - prs->prs_start_time; elapsed = elapsed > 0 ? elapsed : 1; rate = copied / elapsed; rate = rate > 0 ? rate : 1; mins_left = ((total - copied) / rate) / 60; hours_left = mins_left / 60; zfs_nicenum(copied, examined_buf, sizeof (examined_buf)); zfs_nicenum(total, total_buf, sizeof (total_buf)); zfs_nicenum(rate, rate_buf, sizeof (rate_buf)); /* * do not print estimated time if hours_left is more than * 30 days */ (void) printf(gettext(" %s copied out of %s at %s/s, " "%.2f%% done"), examined_buf, total_buf, rate_buf, 100 * fraction_done); if (hours_left < (30 * 24)) { (void) printf(gettext(", %lluh%um to go\n"), (u_longlong_t)hours_left, (uint_t)(mins_left % 60)); } else { (void) printf(gettext( ", (copy is slow, no estimated time)\n")); } } if (prs->prs_mapping_memory > 0) { char mem_buf[7]; zfs_nicenum(prs->prs_mapping_memory, mem_buf, sizeof (mem_buf)); (void) printf(gettext(" %s memory used for " "removed device mappings\n"), mem_buf); } } static void print_checkpoint_status(pool_checkpoint_stat_t *pcs) { time_t start; char space_buf[7]; if (pcs == NULL || pcs->pcs_state == CS_NONE) return; (void) printf(gettext("checkpoint: ")); start = pcs->pcs_start_time; zfs_nicenum(pcs->pcs_space, space_buf, sizeof (space_buf)); if (pcs->pcs_state == CS_CHECKPOINT_EXISTS) { char *date = ctime(&start); /* * ctime() adds a newline at the end of the generated * string, thus the weird format specifier and the * strlen() call used to chop it off from the output. */ (void) printf(gettext("created %.*s, consumes %s\n"), strlen(date) - 1, date, space_buf); return; } assert(pcs->pcs_state == CS_CHECKPOINT_DISCARDING); (void) printf(gettext("discarding, %s remaining.\n"), space_buf); } static void print_error_log(zpool_handle_t *zhp) { nvlist_t *nverrlist = NULL; nvpair_t *elem; char *pathname; size_t len = MAXPATHLEN * 2; if (zpool_get_errlog(zhp, &nverrlist) != 0) { (void) printf("errors: List of errors unavailable " "(insufficient privileges)\n"); return; } (void) printf("errors: Permanent errors have been " "detected in the following files:\n\n"); pathname = safe_malloc(len); elem = NULL; while ((elem = nvlist_next_nvpair(nverrlist, elem)) != NULL) { nvlist_t *nv; uint64_t dsobj, obj; verify(nvpair_value_nvlist(elem, &nv) == 0); verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_DATASET, &dsobj) == 0); verify(nvlist_lookup_uint64(nv, ZPOOL_ERR_OBJECT, &obj) == 0); zpool_obj_to_path(zhp, dsobj, obj, pathname, len); (void) printf("%7s %s\n", "", pathname); } free(pathname); nvlist_free(nverrlist); } static void print_spares(zpool_handle_t *zhp, nvlist_t **spares, uint_t nspares, int namewidth) { uint_t i; char *name; if (nspares == 0) return; (void) printf(gettext("\tspares\n")); for (i = 0; i < nspares; i++) { name = zpool_vdev_name(g_zfs, zhp, spares[i], B_FALSE); print_status_config(zhp, name, spares[i], namewidth, 2, B_TRUE); free(name); } } static void print_l2cache(zpool_handle_t *zhp, nvlist_t **l2cache, uint_t nl2cache, int namewidth) { uint_t i; char *name; if (nl2cache == 0) return; (void) printf(gettext("\tcache\n")); for (i = 0; i < nl2cache; i++) { name = zpool_vdev_name(g_zfs, zhp, l2cache[i], B_FALSE); print_status_config(zhp, name, l2cache[i], namewidth, 2, B_FALSE); free(name); } } static void print_dedup_stats(nvlist_t *config) { ddt_histogram_t *ddh; ddt_stat_t *dds; ddt_object_t *ddo; uint_t c; /* * If the pool was faulted then we may not have been able to * obtain the config. Otherwise, if we have anything in the dedup * table continue processing the stats. */ if (nvlist_lookup_uint64_array(config, ZPOOL_CONFIG_DDT_OBJ_STATS, (uint64_t **)&ddo, &c) != 0) return; (void) printf("\n"); (void) printf(gettext(" dedup: ")); if (ddo->ddo_count == 0) { (void) printf(gettext("no DDT entries\n")); return; } (void) printf("DDT entries %llu, size %llu on disk, %llu in core\n", (u_longlong_t)ddo->ddo_count, (u_longlong_t)ddo->ddo_dspace, (u_longlong_t)ddo->ddo_mspace); verify(nvlist_lookup_uint64_array(config, ZPOOL_CONFIG_DDT_STATS, (uint64_t **)&dds, &c) == 0); verify(nvlist_lookup_uint64_array(config, ZPOOL_CONFIG_DDT_HISTOGRAM, (uint64_t **)&ddh, &c) == 0); zpool_dump_ddt(dds, ddh); } /* * Display a summary of pool status. Displays a summary such as: * * pool: tank * status: DEGRADED * reason: One or more devices ... * see: http://illumos.org/msg/ZFS-xxxx-01 * config: * mirror DEGRADED * c1t0d0 OK * c2t0d0 UNAVAIL * * When given the '-v' option, we print out the complete config. If the '-e' * option is specified, then we print out error rate information as well. */ int status_callback(zpool_handle_t *zhp, void *data) { status_cbdata_t *cbp = data; nvlist_t *config, *nvroot; char *msgid; int reason; const char *health; uint_t c; vdev_stat_t *vs; config = zpool_get_config(zhp, NULL); reason = zpool_get_status(zhp, &msgid); cbp->cb_count++; /* * If we were given 'zpool status -x', only report those pools with * problems. */ if (cbp->cb_explain && (reason == ZPOOL_STATUS_OK || reason == ZPOOL_STATUS_VERSION_OLDER || reason == ZPOOL_STATUS_NON_NATIVE_ASHIFT || reason == ZPOOL_STATUS_FEAT_DISABLED)) { if (!cbp->cb_allpools) { (void) printf(gettext("pool '%s' is healthy\n"), zpool_get_name(zhp)); if (cbp->cb_first) cbp->cb_first = B_FALSE; } return (0); } if (cbp->cb_first) cbp->cb_first = B_FALSE; else (void) printf("\n"); nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE); verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &c) == 0); health = zpool_state_to_name(vs->vs_state, vs->vs_aux); (void) printf(gettext(" pool: %s\n"), zpool_get_name(zhp)); (void) printf(gettext(" state: %s\n"), health); switch (reason) { case ZPOOL_STATUS_MISSING_DEV_R: (void) printf(gettext("status: One or more devices could not " "be opened. Sufficient replicas exist for\n\tthe pool to " "continue functioning in a degraded state.\n")); (void) printf(gettext("action: Attach the missing device and " "online it using 'zpool online'.\n")); break; case ZPOOL_STATUS_MISSING_DEV_NR: (void) printf(gettext("status: One or more devices could not " "be opened. There are insufficient\n\treplicas for the " "pool to continue functioning.\n")); (void) printf(gettext("action: Attach the missing device and " "online it using 'zpool online'.\n")); break; case ZPOOL_STATUS_CORRUPT_LABEL_R: (void) printf(gettext("status: One or more devices could not " "be used because the label is missing or\n\tinvalid. " "Sufficient replicas exist for the pool to continue\n\t" "functioning in a degraded state.\n")); (void) printf(gettext("action: Replace the device using " "'zpool replace'.\n")); break; case ZPOOL_STATUS_CORRUPT_LABEL_NR: (void) printf(gettext("status: One or more devices could not " "be used because the label is missing \n\tor invalid. " "There are insufficient replicas for the pool to " "continue\n\tfunctioning.\n")); zpool_explain_recover(zpool_get_handle(zhp), zpool_get_name(zhp), reason, config); break; case ZPOOL_STATUS_FAILING_DEV: (void) printf(gettext("status: One or more devices has " "experienced an unrecoverable error. An\n\tattempt was " "made to correct the error. Applications are " "unaffected.\n")); (void) printf(gettext("action: Determine if the device needs " "to be replaced, and clear the errors\n\tusing " "'zpool clear' or replace the device with 'zpool " "replace'.\n")); break; case ZPOOL_STATUS_OFFLINE_DEV: (void) printf(gettext("status: One or more devices has " "been taken offline by the administrator.\n\tSufficient " "replicas exist for the pool to continue functioning in " "a\n\tdegraded state.\n")); (void) printf(gettext("action: Online the device using " "'zpool online' or replace the device with\n\t'zpool " "replace'.\n")); break; case ZPOOL_STATUS_REMOVED_DEV: (void) printf(gettext("status: One or more devices has " "been removed by the administrator.\n\tSufficient " "replicas exist for the pool to continue functioning in " "a\n\tdegraded state.\n")); (void) printf(gettext("action: Online the device using " "'zpool online' or replace the device with\n\t'zpool " "replace'.\n")); break; case ZPOOL_STATUS_RESILVERING: (void) printf(gettext("status: One or more devices is " "currently being resilvered. The pool will\n\tcontinue " "to function, possibly in a degraded state.\n")); (void) printf(gettext("action: Wait for the resilver to " "complete.\n")); break; case ZPOOL_STATUS_CORRUPT_DATA: (void) printf(gettext("status: One or more devices has " "experienced an error resulting in data\n\tcorruption. " "Applications may be affected.\n")); (void) printf(gettext("action: Restore the file in question " "if possible. Otherwise restore the\n\tentire pool from " "backup.\n")); break; case ZPOOL_STATUS_CORRUPT_POOL: (void) printf(gettext("status: The pool metadata is corrupted " "and the pool cannot be opened.\n")); zpool_explain_recover(zpool_get_handle(zhp), zpool_get_name(zhp), reason, config); break; case ZPOOL_STATUS_VERSION_OLDER: (void) printf(gettext("status: The pool is formatted using a " "legacy on-disk format. The pool can\n\tstill be used, " "but some features are unavailable.\n")); (void) printf(gettext("action: Upgrade the pool using 'zpool " "upgrade'. Once this is done, the\n\tpool will no longer " "be accessible on software that does not support feature\n" "\tflags.\n")); break; case ZPOOL_STATUS_VERSION_NEWER: (void) printf(gettext("status: The pool has been upgraded to a " "newer, incompatible on-disk version.\n\tThe pool cannot " "be accessed on this system.\n")); (void) printf(gettext("action: Access the pool from a system " "running more recent software, or\n\trestore the pool from " "backup.\n")); break; case ZPOOL_STATUS_FEAT_DISABLED: (void) printf(gettext("status: Some supported features are not " "enabled on the pool. The pool can\n\tstill be used, but " "some features are unavailable.\n")); (void) printf(gettext("action: Enable all features using " "'zpool upgrade'. Once this is done,\n\tthe pool may no " "longer be accessible by software that does not support\n\t" "the features. See zpool-features(7) for details.\n")); break; case ZPOOL_STATUS_UNSUP_FEAT_READ: (void) printf(gettext("status: The pool cannot be accessed on " "this system because it uses the\n\tfollowing feature(s) " "not supported on this system:\n")); zpool_print_unsup_feat(config); (void) printf("\n"); (void) printf(gettext("action: Access the pool from a system " "that supports the required feature(s),\n\tor restore the " "pool from backup.\n")); break; case ZPOOL_STATUS_UNSUP_FEAT_WRITE: (void) printf(gettext("status: The pool can only be accessed " "in read-only mode on this system. It\n\tcannot be " "accessed in read-write mode because it uses the " "following\n\tfeature(s) not supported on this system:\n")); zpool_print_unsup_feat(config); (void) printf("\n"); (void) printf(gettext("action: The pool cannot be accessed in " "read-write mode. Import the pool with\n" "\t\"-o readonly=on\", access the pool from a system that " "supports the\n\trequired feature(s), or restore the " "pool from backup.\n")); break; case ZPOOL_STATUS_FAULTED_DEV_R: (void) printf(gettext("status: One or more devices are " "faulted in response to persistent errors.\n\tSufficient " "replicas exist for the pool to continue functioning " "in a\n\tdegraded state.\n")); (void) printf(gettext("action: Replace the faulted device, " "or use 'zpool clear' to mark the device\n\trepaired.\n")); break; case ZPOOL_STATUS_FAULTED_DEV_NR: (void) printf(gettext("status: One or more devices are " "faulted in response to persistent errors. There are " "insufficient replicas for the pool to\n\tcontinue " "functioning.\n")); (void) printf(gettext("action: Destroy and re-create the pool " "from a backup source. Manually marking the device\n" "\trepaired using 'zpool clear' may allow some data " "to be recovered.\n")); break; case ZPOOL_STATUS_IO_FAILURE_WAIT: case ZPOOL_STATUS_IO_FAILURE_CONTINUE: (void) printf(gettext("status: One or more devices are " "faulted in response to IO failures.\n")); (void) printf(gettext("action: Make sure the affected devices " "are connected, then run 'zpool clear'.\n")); break; case ZPOOL_STATUS_BAD_LOG: (void) printf(gettext("status: An intent log record " "could not be read.\n" "\tWaiting for adminstrator intervention to fix the " "faulted pool.\n")); (void) printf(gettext("action: Either restore the affected " "device(s) and run 'zpool online',\n" "\tor ignore the intent log records by running " "'zpool clear'.\n")); break; case ZPOOL_STATUS_NON_NATIVE_ASHIFT: (void) printf(gettext("status: One or more devices are " "configured to use a non-native block size.\n" "\tExpect reduced performance.\n")); (void) printf(gettext("action: Replace affected devices with " "devices that support the\n\tconfigured block size, or " "migrate data to a properly configured\n\tpool.\n")); break; default: /* * The remaining errors can't actually be generated, yet. */ assert(reason == ZPOOL_STATUS_OK); } if (msgid != NULL) (void) printf(gettext(" see: http://illumos.org/msg/%s\n"), msgid); if (config != NULL) { int namewidth; uint64_t nerr; nvlist_t **spares, **l2cache; uint_t nspares, nl2cache; pool_checkpoint_stat_t *pcs = NULL; pool_scan_stat_t *ps = NULL; pool_removal_stat_t *prs = NULL; (void) nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_CHECKPOINT_STATS, (uint64_t **)&pcs, &c); (void) nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_SCAN_STATS, (uint64_t **)&ps, &c); (void) nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_REMOVAL_STATS, (uint64_t **)&prs, &c); print_scan_status(ps); print_checkpoint_scan_warning(ps, pcs); print_removal_status(zhp, prs); print_checkpoint_status(pcs); namewidth = max_width(zhp, nvroot, 0, 0); if (namewidth < 10) namewidth = 10; (void) printf(gettext("config:\n\n")); (void) printf(gettext("\t%-*s %-8s %5s %5s %5s\n"), namewidth, "NAME", "STATE", "READ", "WRITE", "CKSUM"); print_status_config(zhp, zpool_get_name(zhp), nvroot, namewidth, 0, B_FALSE); if (num_logs(nvroot) > 0) print_logs(zhp, nvroot, namewidth, B_TRUE); if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE, &l2cache, &nl2cache) == 0) print_l2cache(zhp, l2cache, nl2cache, namewidth); if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, &spares, &nspares) == 0) print_spares(zhp, spares, nspares, namewidth); if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_ERRCOUNT, &nerr) == 0) { nvlist_t *nverrlist = NULL; /* * If the approximate error count is small, get a * precise count by fetching the entire log and * uniquifying the results. */ if (nerr > 0 && nerr < 100 && !cbp->cb_verbose && zpool_get_errlog(zhp, &nverrlist) == 0) { nvpair_t *elem; elem = NULL; nerr = 0; while ((elem = nvlist_next_nvpair(nverrlist, elem)) != NULL) { nerr++; } } nvlist_free(nverrlist); (void) printf("\n"); if (nerr == 0) (void) printf(gettext("errors: No known data " "errors\n")); else if (!cbp->cb_verbose) (void) printf(gettext("errors: %llu data " "errors, use '-v' for a list\n"), (u_longlong_t)nerr); else print_error_log(zhp); } if (cbp->cb_dedup_stats) print_dedup_stats(config); } else { (void) printf(gettext("config: The configuration cannot be " "determined.\n")); } return (0); } /* * zpool status [-vx] [-T d|u] [pool] ... [interval [count]] * * -v Display complete error logs * -x Display only pools with potential problems * -D Display dedup status (undocumented) * -T Display a timestamp in date(1) or Unix format * * Describes the health status of all pools or some subset. */ int zpool_do_status(int argc, char **argv) { int c; int ret; unsigned long interval = 0, count = 0; status_cbdata_t cb = { 0 }; /* check options */ while ((c = getopt(argc, argv, "vxDT:")) != -1) { switch (c) { case 'v': cb.cb_verbose = B_TRUE; break; case 'x': cb.cb_explain = B_TRUE; break; case 'D': cb.cb_dedup_stats = B_TRUE; break; case 'T': get_timestamp_arg(*optarg); break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; get_interval_count(&argc, argv, &interval, &count); if (argc == 0) cb.cb_allpools = B_TRUE; cb.cb_first = B_TRUE; for (;;) { if (timestamp_fmt != NODATE) print_timestamp(timestamp_fmt); ret = for_each_pool(argc, argv, B_TRUE, NULL, status_callback, &cb); if (argc == 0 && cb.cb_count == 0) (void) printf(gettext("no pools available\n")); else if (cb.cb_explain && cb.cb_first && cb.cb_allpools) (void) printf(gettext("all pools are healthy\n")); if (ret != 0) return (ret); if (interval == 0) break; if (count != 0 && --count == 0) break; (void) sleep(interval); } return (0); } typedef struct upgrade_cbdata { boolean_t cb_first; boolean_t cb_unavail; char cb_poolname[ZFS_MAX_DATASET_NAME_LEN]; int cb_argc; uint64_t cb_version; char **cb_argv; } upgrade_cbdata_t; #ifdef __FreeBSD__ static int is_root_pool(zpool_handle_t *zhp) { static struct statfs sfs; static char *poolname = NULL; static boolean_t stated = B_FALSE; char *slash; if (!stated) { stated = B_TRUE; if (statfs("/", &sfs) == -1) { (void) fprintf(stderr, "Unable to stat root file system: %s.\n", strerror(errno)); return (0); } if (strcmp(sfs.f_fstypename, "zfs") != 0) return (0); poolname = sfs.f_mntfromname; if ((slash = strchr(poolname, '/')) != NULL) *slash = '\0'; } return (poolname != NULL && strcmp(poolname, zpool_get_name(zhp)) == 0); } static void root_pool_upgrade_check(zpool_handle_t *zhp, char *poolname, int size) { if (poolname[0] == '\0' && is_root_pool(zhp)) (void) strlcpy(poolname, zpool_get_name(zhp), size); } #endif /* FreeBSD */ static int upgrade_version(zpool_handle_t *zhp, uint64_t version) { int ret; nvlist_t *config; uint64_t oldversion; config = zpool_get_config(zhp, NULL); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &oldversion) == 0); assert(SPA_VERSION_IS_SUPPORTED(oldversion)); assert(oldversion < version); ret = zpool_upgrade(zhp, version); if (ret != 0) return (ret); if (version >= SPA_VERSION_FEATURES) { (void) printf(gettext("Successfully upgraded " "'%s' from version %llu to feature flags.\n"), zpool_get_name(zhp), oldversion); } else { (void) printf(gettext("Successfully upgraded " "'%s' from version %llu to version %llu.\n"), zpool_get_name(zhp), oldversion, version); } return (0); } static int upgrade_enable_all(zpool_handle_t *zhp, int *countp) { int i, ret, count; boolean_t firstff = B_TRUE; nvlist_t *enabled = zpool_get_features(zhp); count = 0; for (i = 0; i < SPA_FEATURES; i++) { const char *fname = spa_feature_table[i].fi_uname; const char *fguid = spa_feature_table[i].fi_guid; if (!nvlist_exists(enabled, fguid)) { char *propname; verify(-1 != asprintf(&propname, "feature@%s", fname)); ret = zpool_set_prop(zhp, propname, ZFS_FEATURE_ENABLED); if (ret != 0) { free(propname); return (ret); } count++; if (firstff) { (void) printf(gettext("Enabled the " "following features on '%s':\n"), zpool_get_name(zhp)); firstff = B_FALSE; } (void) printf(gettext(" %s\n"), fname); free(propname); } } if (countp != NULL) *countp = count; return (0); } static int upgrade_cb(zpool_handle_t *zhp, void *arg) { upgrade_cbdata_t *cbp = arg; nvlist_t *config; uint64_t version; boolean_t printnl = B_FALSE; int ret; if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { (void) fprintf(stderr, gettext("cannot upgrade '%s': pool is " "currently unavailable.\n\n"), zpool_get_name(zhp)); cbp->cb_unavail = B_TRUE; /* Allow iteration to continue. */ return (0); } config = zpool_get_config(zhp, NULL); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &version) == 0); assert(SPA_VERSION_IS_SUPPORTED(version)); if (version < cbp->cb_version) { cbp->cb_first = B_FALSE; ret = upgrade_version(zhp, cbp->cb_version); if (ret != 0) return (ret); #ifdef __FreeBSD__ root_pool_upgrade_check(zhp, cbp->cb_poolname, sizeof(cbp->cb_poolname)); #endif /* __FreeBSD__ */ printnl = B_TRUE; #ifdef illumos /* * If they did "zpool upgrade -a", then we could * be doing ioctls to different pools. We need * to log this history once to each pool, and bypass * the normal history logging that happens in main(). */ (void) zpool_log_history(g_zfs, history_str); log_history = B_FALSE; #endif } if (cbp->cb_version >= SPA_VERSION_FEATURES) { int count; ret = upgrade_enable_all(zhp, &count); if (ret != 0) return (ret); if (count > 0) { cbp->cb_first = B_FALSE; printnl = B_TRUE; #ifdef __FreeBSD__ root_pool_upgrade_check(zhp, cbp->cb_poolname, sizeof(cbp->cb_poolname)); #endif /* __FreeBSD__ */ /* * If they did "zpool upgrade -a", then we could * be doing ioctls to different pools. We need * to log this history once to each pool, and bypass * the normal history logging that happens in main(). */ (void) zpool_log_history(g_zfs, history_str); log_history = B_FALSE; } } if (printnl) { (void) printf(gettext("\n")); } return (0); } static int upgrade_list_unavail(zpool_handle_t *zhp, void *arg) { upgrade_cbdata_t *cbp = arg; if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { if (cbp->cb_first) { (void) fprintf(stderr, gettext("The following pools " "are unavailable and cannot be upgraded as this " "time.\n\n")); (void) fprintf(stderr, gettext("POOL\n")); (void) fprintf(stderr, gettext("------------\n")); cbp->cb_first = B_FALSE; } (void) printf(gettext("%s\n"), zpool_get_name(zhp)); cbp->cb_unavail = B_TRUE; } return (0); } static int upgrade_list_older_cb(zpool_handle_t *zhp, void *arg) { upgrade_cbdata_t *cbp = arg; nvlist_t *config; uint64_t version; if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { /* * This will have been reported by upgrade_list_unavail so * just allow iteration to continue. */ cbp->cb_unavail = B_TRUE; return (0); } config = zpool_get_config(zhp, NULL); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &version) == 0); assert(SPA_VERSION_IS_SUPPORTED(version)); if (version < SPA_VERSION_FEATURES) { if (cbp->cb_first) { (void) printf(gettext("The following pools are " "formatted with legacy version numbers and can\n" "be upgraded to use feature flags. After " "being upgraded, these pools\nwill no " "longer be accessible by software that does not " "support feature\nflags.\n\n")); (void) printf(gettext("VER POOL\n")); (void) printf(gettext("--- ------------\n")); cbp->cb_first = B_FALSE; } (void) printf("%2llu %s\n", (u_longlong_t)version, zpool_get_name(zhp)); } return (0); } static int upgrade_list_disabled_cb(zpool_handle_t *zhp, void *arg) { upgrade_cbdata_t *cbp = arg; nvlist_t *config; uint64_t version; if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { /* * This will have been reported by upgrade_list_unavail so * just allow iteration to continue. */ cbp->cb_unavail = B_TRUE; return (0); } config = zpool_get_config(zhp, NULL); verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_VERSION, &version) == 0); if (version >= SPA_VERSION_FEATURES) { int i; boolean_t poolfirst = B_TRUE; nvlist_t *enabled = zpool_get_features(zhp); for (i = 0; i < SPA_FEATURES; i++) { const char *fguid = spa_feature_table[i].fi_guid; const char *fname = spa_feature_table[i].fi_uname; if (!nvlist_exists(enabled, fguid)) { if (cbp->cb_first) { (void) printf(gettext("\nSome " "supported features are not " "enabled on the following pools. " "Once a\nfeature is enabled the " "pool may become incompatible with " "software\nthat does not support " "the feature. See " "zpool-features(7) for " "details.\n\n")); (void) printf(gettext("POOL " "FEATURE\n")); (void) printf(gettext("------" "---------\n")); cbp->cb_first = B_FALSE; } if (poolfirst) { (void) printf(gettext("%s\n"), zpool_get_name(zhp)); poolfirst = B_FALSE; } (void) printf(gettext(" %s\n"), fname); } } } return (0); } /* ARGSUSED */ static int upgrade_one(zpool_handle_t *zhp, void *data) { boolean_t printnl = B_FALSE; upgrade_cbdata_t *cbp = data; uint64_t cur_version; int ret; if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) { (void) fprintf(stderr, gettext("cannot upgrade '%s': pool is " "is currently unavailable.\n\n"), zpool_get_name(zhp)); cbp->cb_unavail = B_TRUE; return (1); } if (strcmp("log", zpool_get_name(zhp)) == 0) { (void) printf(gettext("'log' is now a reserved word\n" "Pool 'log' must be renamed using export and import" " to upgrade.\n\n")); return (1); } cur_version = zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL); if (cur_version > cbp->cb_version) { (void) printf(gettext("Pool '%s' is already formatted " "using more current version '%llu'.\n\n"), zpool_get_name(zhp), cur_version); return (0); } if (cbp->cb_version != SPA_VERSION && cur_version == cbp->cb_version) { (void) printf(gettext("Pool '%s' is already formatted " "using version %llu.\n\n"), zpool_get_name(zhp), cbp->cb_version); return (0); } if (cur_version != cbp->cb_version) { printnl = B_TRUE; ret = upgrade_version(zhp, cbp->cb_version); if (ret != 0) return (ret); #ifdef __FreeBSD__ root_pool_upgrade_check(zhp, cbp->cb_poolname, sizeof(cbp->cb_poolname)); #endif /* __FreeBSD__ */ } if (cbp->cb_version >= SPA_VERSION_FEATURES) { int count = 0; ret = upgrade_enable_all(zhp, &count); if (ret != 0) return (ret); if (count != 0) { printnl = B_TRUE; #ifdef __FreeBSD__ root_pool_upgrade_check(zhp, cbp->cb_poolname, sizeof(cbp->cb_poolname)); #endif /* __FreeBSD __*/ } else if (cur_version == SPA_VERSION) { (void) printf(gettext("Pool '%s' already has all " "supported features enabled.\n\n"), zpool_get_name(zhp)); } } if (printnl) { (void) printf(gettext("\n")); } return (0); } /* * zpool upgrade * zpool upgrade -v * zpool upgrade [-V version] <-a | pool ...> * * With no arguments, display downrev'd ZFS pool available for upgrade. * Individual pools can be upgraded by specifying the pool, and '-a' will * upgrade all pools. */ int zpool_do_upgrade(int argc, char **argv) { int c; upgrade_cbdata_t cb = { 0 }; int ret = 0; boolean_t showversions = B_FALSE; boolean_t upgradeall = B_FALSE; char *end; /* check options */ while ((c = getopt(argc, argv, ":avV:")) != -1) { switch (c) { case 'a': upgradeall = B_TRUE; break; case 'v': showversions = B_TRUE; break; case 'V': cb.cb_version = strtoll(optarg, &end, 10); if (*end != '\0' || !SPA_VERSION_IS_SUPPORTED(cb.cb_version)) { (void) fprintf(stderr, gettext("invalid version '%s'\n"), optarg); usage(B_FALSE); } break; case ':': (void) fprintf(stderr, gettext("missing argument for " "'%c' option\n"), optopt); usage(B_FALSE); break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } cb.cb_argc = argc; cb.cb_argv = argv; argc -= optind; argv += optind; if (cb.cb_version == 0) { cb.cb_version = SPA_VERSION; } else if (!upgradeall && argc == 0) { (void) fprintf(stderr, gettext("-V option is " "incompatible with other arguments\n")); usage(B_FALSE); } if (showversions) { if (upgradeall || argc != 0) { (void) fprintf(stderr, gettext("-v option is " "incompatible with other arguments\n")); usage(B_FALSE); } } else if (upgradeall) { if (argc != 0) { (void) fprintf(stderr, gettext("-a option should not " "be used along with a pool name\n")); usage(B_FALSE); } } (void) printf(gettext("This system supports ZFS pool feature " "flags.\n\n")); if (showversions) { int i; (void) printf(gettext("The following features are " "supported:\n\n")); (void) printf(gettext("FEAT DESCRIPTION\n")); (void) printf("----------------------------------------------" "---------------\n"); for (i = 0; i < SPA_FEATURES; i++) { zfeature_info_t *fi = &spa_feature_table[i]; const char *ro = (fi->fi_flags & ZFEATURE_FLAG_READONLY_COMPAT) ? " (read-only compatible)" : ""; (void) printf("%-37s%s\n", fi->fi_uname, ro); (void) printf(" %s\n", fi->fi_desc); } (void) printf("\n"); (void) printf(gettext("The following legacy versions are also " "supported:\n\n")); (void) printf(gettext("VER DESCRIPTION\n")); (void) printf("--- -----------------------------------------" "---------------\n"); (void) printf(gettext(" 1 Initial ZFS version\n")); (void) printf(gettext(" 2 Ditto blocks " "(replicated metadata)\n")); (void) printf(gettext(" 3 Hot spares and double parity " "RAID-Z\n")); (void) printf(gettext(" 4 zpool history\n")); (void) printf(gettext(" 5 Compression using the gzip " "algorithm\n")); (void) printf(gettext(" 6 bootfs pool property\n")); (void) printf(gettext(" 7 Separate intent log devices\n")); (void) printf(gettext(" 8 Delegated administration\n")); (void) printf(gettext(" 9 refquota and refreservation " "properties\n")); (void) printf(gettext(" 10 Cache devices\n")); (void) printf(gettext(" 11 Improved scrub performance\n")); (void) printf(gettext(" 12 Snapshot properties\n")); (void) printf(gettext(" 13 snapused property\n")); (void) printf(gettext(" 14 passthrough-x aclinherit\n")); (void) printf(gettext(" 15 user/group space accounting\n")); (void) printf(gettext(" 16 stmf property support\n")); (void) printf(gettext(" 17 Triple-parity RAID-Z\n")); (void) printf(gettext(" 18 Snapshot user holds\n")); (void) printf(gettext(" 19 Log device removal\n")); (void) printf(gettext(" 20 Compression using zle " "(zero-length encoding)\n")); (void) printf(gettext(" 21 Deduplication\n")); (void) printf(gettext(" 22 Received properties\n")); (void) printf(gettext(" 23 Slim ZIL\n")); (void) printf(gettext(" 24 System attributes\n")); (void) printf(gettext(" 25 Improved scrub stats\n")); (void) printf(gettext(" 26 Improved snapshot deletion " "performance\n")); (void) printf(gettext(" 27 Improved snapshot creation " "performance\n")); (void) printf(gettext(" 28 Multiple vdev replacements\n")); (void) printf(gettext("\nFor more information on a particular " "version, including supported releases,\n")); (void) printf(gettext("see the ZFS Administration Guide.\n\n")); } else if (argc == 0 && upgradeall) { cb.cb_first = B_TRUE; ret = zpool_iter(g_zfs, upgrade_cb, &cb); if (ret == 0 && cb.cb_first) { if (cb.cb_version == SPA_VERSION) { (void) printf(gettext("All %spools are already " "formatted using feature flags.\n\n"), cb.cb_unavail ? gettext("available ") : ""); (void) printf(gettext("Every %sfeature flags " "pool already has all supported features " "enabled.\n"), cb.cb_unavail ? gettext("available ") : ""); } else { (void) printf(gettext("All pools are already " "formatted with version %llu or higher.\n"), cb.cb_version); } } } else if (argc == 0) { cb.cb_first = B_TRUE; ret = zpool_iter(g_zfs, upgrade_list_unavail, &cb); assert(ret == 0); if (!cb.cb_first) { (void) fprintf(stderr, "\n"); } cb.cb_first = B_TRUE; ret = zpool_iter(g_zfs, upgrade_list_older_cb, &cb); assert(ret == 0); if (cb.cb_first) { (void) printf(gettext("All %spools are formatted using " "feature flags.\n\n"), cb.cb_unavail ? gettext("available ") : ""); } else { (void) printf(gettext("\nUse 'zpool upgrade -v' " "for a list of available legacy versions.\n")); } cb.cb_first = B_TRUE; ret = zpool_iter(g_zfs, upgrade_list_disabled_cb, &cb); assert(ret == 0); if (cb.cb_first) { (void) printf(gettext("Every %sfeature flags pool has " "all supported features enabled.\n"), cb.cb_unavail ? gettext("available ") : ""); } else { (void) printf(gettext("\n")); } } else { ret = for_each_pool(argc, argv, B_TRUE, NULL, upgrade_one, &cb); } if (cb.cb_poolname[0] != '\0') { (void) printf( "If you boot from pool '%s', don't forget to update boot code.\n" "Assuming you use GPT partitioning and da0 is your boot disk\n" "the following command will do it:\n" "\n" "\tgpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 da0\n\n", cb.cb_poolname); } return (ret); } typedef struct hist_cbdata { boolean_t first; boolean_t longfmt; boolean_t internal; } hist_cbdata_t; /* * Print out the command history for a specific pool. */ static int get_history_one(zpool_handle_t *zhp, void *data) { nvlist_t *nvhis; nvlist_t **records; uint_t numrecords; int ret, i; hist_cbdata_t *cb = (hist_cbdata_t *)data; cb->first = B_FALSE; (void) printf(gettext("History for '%s':\n"), zpool_get_name(zhp)); if ((ret = zpool_get_history(zhp, &nvhis)) != 0) return (ret); verify(nvlist_lookup_nvlist_array(nvhis, ZPOOL_HIST_RECORD, &records, &numrecords) == 0); for (i = 0; i < numrecords; i++) { nvlist_t *rec = records[i]; char tbuf[30] = ""; if (nvlist_exists(rec, ZPOOL_HIST_TIME)) { time_t tsec; struct tm t; tsec = fnvlist_lookup_uint64(records[i], ZPOOL_HIST_TIME); (void) localtime_r(&tsec, &t); (void) strftime(tbuf, sizeof (tbuf), "%F.%T", &t); } if (nvlist_exists(rec, ZPOOL_HIST_CMD)) { (void) printf("%s %s", tbuf, fnvlist_lookup_string(rec, ZPOOL_HIST_CMD)); } else if (nvlist_exists(rec, ZPOOL_HIST_INT_EVENT)) { int ievent = fnvlist_lookup_uint64(rec, ZPOOL_HIST_INT_EVENT); if (!cb->internal) continue; if (ievent >= ZFS_NUM_LEGACY_HISTORY_EVENTS) { (void) printf("%s unrecognized record:\n", tbuf); dump_nvlist(rec, 4); continue; } (void) printf("%s [internal %s txg:%lld] %s", tbuf, zfs_history_event_names[ievent], fnvlist_lookup_uint64(rec, ZPOOL_HIST_TXG), fnvlist_lookup_string(rec, ZPOOL_HIST_INT_STR)); } else if (nvlist_exists(rec, ZPOOL_HIST_INT_NAME)) { if (!cb->internal) continue; (void) printf("%s [txg:%lld] %s", tbuf, fnvlist_lookup_uint64(rec, ZPOOL_HIST_TXG), fnvlist_lookup_string(rec, ZPOOL_HIST_INT_NAME)); if (nvlist_exists(rec, ZPOOL_HIST_DSNAME)) { (void) printf(" %s (%llu)", fnvlist_lookup_string(rec, ZPOOL_HIST_DSNAME), fnvlist_lookup_uint64(rec, ZPOOL_HIST_DSID)); } (void) printf(" %s", fnvlist_lookup_string(rec, ZPOOL_HIST_INT_STR)); } else if (nvlist_exists(rec, ZPOOL_HIST_IOCTL)) { if (!cb->internal) continue; (void) printf("%s ioctl %s\n", tbuf, fnvlist_lookup_string(rec, ZPOOL_HIST_IOCTL)); if (nvlist_exists(rec, ZPOOL_HIST_INPUT_NVL)) { (void) printf(" input:\n"); dump_nvlist(fnvlist_lookup_nvlist(rec, ZPOOL_HIST_INPUT_NVL), 8); } if (nvlist_exists(rec, ZPOOL_HIST_OUTPUT_NVL)) { (void) printf(" output:\n"); dump_nvlist(fnvlist_lookup_nvlist(rec, ZPOOL_HIST_OUTPUT_NVL), 8); } if (nvlist_exists(rec, ZPOOL_HIST_ERRNO)) { (void) printf(" errno: %lld\n", fnvlist_lookup_int64(rec, ZPOOL_HIST_ERRNO)); } } else { if (!cb->internal) continue; (void) printf("%s unrecognized record:\n", tbuf); dump_nvlist(rec, 4); } if (!cb->longfmt) { (void) printf("\n"); continue; } (void) printf(" ["); if (nvlist_exists(rec, ZPOOL_HIST_WHO)) { uid_t who = fnvlist_lookup_uint64(rec, ZPOOL_HIST_WHO); struct passwd *pwd = getpwuid(who); (void) printf("user %d ", (int)who); if (pwd != NULL) (void) printf("(%s) ", pwd->pw_name); } if (nvlist_exists(rec, ZPOOL_HIST_HOST)) { (void) printf("on %s", fnvlist_lookup_string(rec, ZPOOL_HIST_HOST)); } if (nvlist_exists(rec, ZPOOL_HIST_ZONE)) { (void) printf(":%s", fnvlist_lookup_string(rec, ZPOOL_HIST_ZONE)); } (void) printf("]"); (void) printf("\n"); } (void) printf("\n"); nvlist_free(nvhis); return (ret); } /* * zpool history * * Displays the history of commands that modified pools. */ int zpool_do_history(int argc, char **argv) { hist_cbdata_t cbdata = { 0 }; int ret; int c; cbdata.first = B_TRUE; /* check options */ while ((c = getopt(argc, argv, "li")) != -1) { switch (c) { case 'l': cbdata.longfmt = B_TRUE; break; case 'i': cbdata.internal = B_TRUE; break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; ret = for_each_pool(argc, argv, B_FALSE, NULL, get_history_one, &cbdata); if (argc == 0 && cbdata.first == B_TRUE) { (void) printf(gettext("no pools available\n")); return (0); } return (ret); } static int get_callback(zpool_handle_t *zhp, void *data) { zprop_get_cbdata_t *cbp = (zprop_get_cbdata_t *)data; char value[MAXNAMELEN]; zprop_source_t srctype; zprop_list_t *pl; for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) { /* * Skip the special fake placeholder. This will also skip * over the name property when 'all' is specified. */ if (pl->pl_prop == ZPOOL_PROP_NAME && pl == cbp->cb_proplist) continue; if (pl->pl_prop == ZPROP_INVAL && (zpool_prop_feature(pl->pl_user_prop) || zpool_prop_unsupported(pl->pl_user_prop))) { srctype = ZPROP_SRC_LOCAL; if (zpool_prop_get_feature(zhp, pl->pl_user_prop, value, sizeof (value)) == 0) { zprop_print_one_property(zpool_get_name(zhp), cbp, pl->pl_user_prop, value, srctype, NULL, NULL); } } else { if (zpool_get_prop(zhp, pl->pl_prop, value, sizeof (value), &srctype, cbp->cb_literal) != 0) continue; zprop_print_one_property(zpool_get_name(zhp), cbp, zpool_prop_to_name(pl->pl_prop), value, srctype, NULL, NULL); } } return (0); } /* * zpool get [-Hp] [-o "all" | field[,...]] <"all" | property[,...]> ... * * -H Scripted mode. Don't display headers, and separate properties * by a single tab. * -o List of columns to display. Defaults to * "name,property,value,source". * -p Diplay values in parsable (exact) format. * * Get properties of pools in the system. Output space statistics * for each one as well as other attributes. */ int zpool_do_get(int argc, char **argv) { zprop_get_cbdata_t cb = { 0 }; zprop_list_t fake_name = { 0 }; int ret; int c, i; char *value; cb.cb_first = B_TRUE; /* * Set up default columns and sources. */ cb.cb_sources = ZPROP_SRC_ALL; cb.cb_columns[0] = GET_COL_NAME; cb.cb_columns[1] = GET_COL_PROPERTY; cb.cb_columns[2] = GET_COL_VALUE; cb.cb_columns[3] = GET_COL_SOURCE; cb.cb_type = ZFS_TYPE_POOL; /* check options */ while ((c = getopt(argc, argv, ":Hpo:")) != -1) { switch (c) { case 'p': cb.cb_literal = B_TRUE; break; case 'H': cb.cb_scripted = B_TRUE; break; case 'o': bzero(&cb.cb_columns, sizeof (cb.cb_columns)); i = 0; while (*optarg != '\0') { static char *col_subopts[] = { "name", "property", "value", "source", "all", NULL }; if (i == ZFS_GET_NCOLS) { (void) fprintf(stderr, gettext("too " "many fields given to -o " "option\n")); usage(B_FALSE); } switch (getsubopt(&optarg, col_subopts, &value)) { case 0: cb.cb_columns[i++] = GET_COL_NAME; break; case 1: cb.cb_columns[i++] = GET_COL_PROPERTY; break; case 2: cb.cb_columns[i++] = GET_COL_VALUE; break; case 3: cb.cb_columns[i++] = GET_COL_SOURCE; break; case 4: if (i > 0) { (void) fprintf(stderr, gettext("\"all\" conflicts " "with specific fields " "given to -o option\n")); usage(B_FALSE); } cb.cb_columns[0] = GET_COL_NAME; cb.cb_columns[1] = GET_COL_PROPERTY; cb.cb_columns[2] = GET_COL_VALUE; cb.cb_columns[3] = GET_COL_SOURCE; i = ZFS_GET_NCOLS; break; default: (void) fprintf(stderr, gettext("invalid column name " "'%s'\n"), suboptarg); usage(B_FALSE); } } break; case '?': (void) fprintf(stderr, gettext("invalid option '%c'\n"), optopt); usage(B_FALSE); } } argc -= optind; argv += optind; if (argc < 1) { (void) fprintf(stderr, gettext("missing property " "argument\n")); usage(B_FALSE); } if (zprop_get_list(g_zfs, argv[0], &cb.cb_proplist, ZFS_TYPE_POOL) != 0) usage(B_FALSE); argc--; argv++; if (cb.cb_proplist != NULL) { fake_name.pl_prop = ZPOOL_PROP_NAME; fake_name.pl_width = strlen(gettext("NAME")); fake_name.pl_next = cb.cb_proplist; cb.cb_proplist = &fake_name; } ret = for_each_pool(argc, argv, B_TRUE, &cb.cb_proplist, get_callback, &cb); if (cb.cb_proplist == &fake_name) zprop_free_list(fake_name.pl_next); else zprop_free_list(cb.cb_proplist); return (ret); } typedef struct set_cbdata { char *cb_propname; char *cb_value; boolean_t cb_any_successful; } set_cbdata_t; int set_callback(zpool_handle_t *zhp, void *data) { int error; set_cbdata_t *cb = (set_cbdata_t *)data; error = zpool_set_prop(zhp, cb->cb_propname, cb->cb_value); if (!error) cb->cb_any_successful = B_TRUE; return (error); } int zpool_do_set(int argc, char **argv) { set_cbdata_t cb = { 0 }; int error; if (argc > 1 && argv[1][0] == '-') { (void) fprintf(stderr, gettext("invalid option '%c'\n"), argv[1][1]); usage(B_FALSE); } if (argc < 2) { (void) fprintf(stderr, gettext("missing property=value " "argument\n")); usage(B_FALSE); } if (argc < 3) { (void) fprintf(stderr, gettext("missing pool name\n")); usage(B_FALSE); } if (argc > 3) { (void) fprintf(stderr, gettext("too many pool names\n")); usage(B_FALSE); } cb.cb_propname = argv[1]; cb.cb_value = strchr(cb.cb_propname, '='); if (cb.cb_value == NULL) { (void) fprintf(stderr, gettext("missing value in " "property=value argument\n")); usage(B_FALSE); } *(cb.cb_value) = '\0'; cb.cb_value++; error = for_each_pool(argc - 2, argv + 2, B_TRUE, NULL, set_callback, &cb); return (error); } static int find_command_idx(char *command, int *idx) { int i; for (i = 0; i < NCOMMAND; i++) { if (command_table[i].name == NULL) continue; if (strcmp(command, command_table[i].name) == 0) { *idx = i; return (0); } } return (1); } int main(int argc, char **argv) { int ret = 0; int i; char *cmdname; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); if ((g_zfs = libzfs_init()) == NULL) { (void) fprintf(stderr, gettext("internal error: failed to " "initialize ZFS library\n")); return (1); } libzfs_print_on_error(g_zfs, B_TRUE); opterr = 0; /* * Make sure the user has specified some command. */ if (argc < 2) { (void) fprintf(stderr, gettext("missing command\n")); usage(B_FALSE); } cmdname = argv[1]; /* * Special case '-?' */ if (strcmp(cmdname, "-?") == 0) usage(B_TRUE); zfs_save_arguments(argc, argv, history_str, sizeof (history_str)); /* * Run the appropriate command. */ if (find_command_idx(cmdname, &i) == 0) { current_command = &command_table[i]; ret = command_table[i].func(argc - 1, argv + 1); } else if (strchr(cmdname, '=')) { verify(find_command_idx("set", &i) == 0); current_command = &command_table[i]; ret = command_table[i].func(argc, argv); } else if (strcmp(cmdname, "freeze") == 0 && argc == 3) { /* * 'freeze' is a vile debugging abomination, so we treat * it as such. */ zfs_cmd_t zc = { 0 }; (void) strlcpy(zc.zc_name, argv[2], sizeof (zc.zc_name)); return (!!zfs_ioctl(g_zfs, ZFS_IOC_POOL_FREEZE, &zc)); } else { (void) fprintf(stderr, gettext("unrecognized " "command '%s'\n"), cmdname); usage(B_FALSE); } if (ret == 0 && log_history) (void) zpool_log_history(g_zfs, history_str); libzfs_fini(g_zfs); /* * The 'ZFS_ABORT' environment variable causes us to dump core on exit * for the purposes of running ::findleaks. */ if (getenv("ZFS_ABORT") != NULL) { (void) printf("dumping core by request\n"); abort(); } return (ret); } Index: stable/11/cddl/contrib/opensolaris/cmd/zpool/zpool_util.h =================================================================== --- stable/11/cddl/contrib/opensolaris/cmd/zpool/zpool_util.h (revision 359753) +++ stable/11/cddl/contrib/opensolaris/cmd/zpool/zpool_util.h (revision 359754) @@ -1,73 +1,73 @@ /* * 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) 2005, 2010, Oracle and/or its affiliates. All rights reserved. */ #ifndef ZPOOL_UTIL_H #define ZPOOL_UTIL_H #include #include #ifdef __cplusplus extern "C" { #endif /* * Basic utility functions */ void *safe_malloc(size_t); void zpool_no_memory(void); uint_t num_logs(nvlist_t *nv); /* * Virtual device functions */ nvlist_t *make_root_vdev(zpool_handle_t *zhp, int force, int check_rep, boolean_t replacing, boolean_t dryrun, zpool_boot_label_t boot_type, uint64_t boot_size, int argc, char **argv); nvlist_t *split_mirror_vdev(zpool_handle_t *zhp, char *newname, nvlist_t *props, splitflags_t flags, int argc, char **argv); /* * Pool list functions */ int for_each_pool(int, char **, boolean_t unavail, zprop_list_t **, zpool_iter_f, void *); typedef struct zpool_list zpool_list_t; zpool_list_t *pool_list_get(int, char **, zprop_list_t **, int *); void pool_list_update(zpool_list_t *); int pool_list_iter(zpool_list_t *, int unavail, zpool_iter_f, void *); void pool_list_free(zpool_list_t *); int pool_list_count(zpool_list_t *); void pool_list_remove(zpool_list_t *, zpool_handle_t *); -libzfs_handle_t *g_zfs; +extern libzfs_handle_t *g_zfs; #ifdef __cplusplus } #endif #endif /* ZPOOL_UTIL_H */ Index: stable/11/contrib/bmake/main.c =================================================================== --- stable/11/contrib/bmake/main.c (revision 359753) +++ stable/11/contrib/bmake/main.c (revision 359754) @@ -1,2221 +1,2223 @@ -/* $NetBSD: main.c,v 1.273 2017/10/28 21:54:54 sjg Exp $ */ +/* $NetBSD: main.c,v 1.274 2020/03/30 02:41:06 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: main.c,v 1.273 2017/10/28 21:54:54 sjg Exp $"; +static char rcsid[] = "$NetBSD: main.c,v 1.274 2020/03/30 02:41:06 sjg Exp $"; #else #include #ifndef lint __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993\ The Regents of the University of California. All rights reserved."); #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: main.c,v 1.273 2017/10/28 21:54:54 sjg Exp $"); +__RCSID("$NetBSD: main.c,v 1.274 2020/03/30 02:41:06 sjg Exp $"); #endif #endif /* not lint */ #endif /*- * main.c -- * The main file for this entire program. Exit routines etc * reside here. * * Utility functions defined in this file: * Main_ParseArgLine Takes a line of arguments, breaks them and * treats them as if they were given when first * invoked. Used by the parse module to implement * the .MFLAGS target. * * Error Print a tagged error message. The global * MAKE variable must have been defined. This * takes a format string and optional arguments * for it. * * Fatal Print an error message and exit. Also takes * a format string and arguments for it. * * Punt Aborts all jobs and exits with a message. Also * takes a format string and arguments for it. * * Finish Finish things up by printing the number of * errors which occurred, as passed to it, and * exiting. */ #include #include #include #include #include #if defined(MAKE_NATIVE) && defined(HAVE_SYSCTL) #include #endif #include #include "wait.h" #include #include #include #include #include #include #include #include "make.h" #include "hash.h" #include "dir.h" #include "job.h" #include "pathnames.h" #include "trace.h" #ifdef USE_IOVEC #include #endif #ifndef DEFMAXLOCAL #define DEFMAXLOCAL DEFMAXJOBS #endif /* DEFMAXLOCAL */ #ifndef __arraycount # define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) #endif Lst create; /* Targets to be made */ time_t now; /* Time at start of make */ GNode *DEFAULT; /* .DEFAULT node */ Boolean allPrecious; /* .PRECIOUS given on line by itself */ Boolean deleteOnError; /* .DELETE_ON_ERROR: set */ static Boolean noBuiltins; /* -r flag */ static Lst makefiles; /* ordered list of makefiles to read */ static int printVars; /* -[vV] argument */ #define COMPAT_VARS 1 #define EXPAND_VARS 2 static Lst variables; /* list of variables to print */ int maxJobs; /* -j argument */ static int maxJobTokens; /* -j argument */ Boolean compatMake; /* -B argument */ int debug; /* -d argument */ Boolean debugVflag; /* -dV */ Boolean noExecute; /* -n flag */ Boolean noRecursiveExecute; /* -N flag */ Boolean keepgoing; /* -k flag */ Boolean queryFlag; /* -q flag */ Boolean touchFlag; /* -t flag */ Boolean enterFlag; /* -w flag */ Boolean enterFlagObj; /* -w and objdir != srcdir */ Boolean ignoreErrors; /* -i flag */ Boolean beSilent; /* -s flag */ Boolean oldVars; /* variable substitution style */ Boolean checkEnvFirst; /* -e flag */ Boolean parseWarnFatal; /* -W flag */ Boolean jobServer; /* -J flag */ static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */ Boolean varNoExportEnv; /* -X flag */ Boolean doing_depend; /* Set while reading .depend */ static Boolean jobsRunning; /* TRUE if the jobs might be running */ static const char * tracefile; static void MainParseArgs(int, char **); static int ReadMakefile(const void *, const void *); static void usage(void) MAKE_ATTR_DEAD; static void purge_cached_realpaths(void); static Boolean ignorePWD; /* if we use -C, PWD is meaningless */ static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */ char curdir[MAXPATHLEN + 1]; /* Startup directory */ char *progname; /* the program name */ char *makeDependfile; pid_t myPid; int makelevel; + +FILE *debug_file; Boolean forceJobs = FALSE; /* * On some systems MACHINE is defined as something other than * what we want. */ #ifdef FORCE_MACHINE # undef MACHINE # define MACHINE FORCE_MACHINE #endif extern Lst parseIncPath; /* * For compatibility with the POSIX version of MAKEFLAGS that includes * all the options with out -, convert flags to -f -l -a -g -s. */ static char * explode(const char *flags) { size_t len; char *nf, *st; const char *f; if (flags == NULL) return NULL; for (f = flags; *f; f++) if (!isalpha((unsigned char)*f)) break; if (*f) return bmake_strdup(flags); len = strlen(flags); st = nf = bmake_malloc(len * 3 + 1); while (*flags) { *nf++ = '-'; *nf++ = *flags++; *nf++ = ' '; } *nf = '\0'; return st; } static void parse_debug_options(const char *argvalue) { const char *modules; const char *mode; char *fname; int len; for (modules = argvalue; *modules; ++modules) { switch (*modules) { case 'A': debug = ~0; break; case 'a': debug |= DEBUG_ARCH; break; case 'C': debug |= DEBUG_CWD; break; case 'c': debug |= DEBUG_COND; break; case 'd': debug |= DEBUG_DIR; break; case 'e': debug |= DEBUG_ERROR; break; case 'f': debug |= DEBUG_FOR; break; case 'g': if (modules[1] == '1') { debug |= DEBUG_GRAPH1; ++modules; } else if (modules[1] == '2') { debug |= DEBUG_GRAPH2; ++modules; } else if (modules[1] == '3') { debug |= DEBUG_GRAPH3; ++modules; } break; case 'j': debug |= DEBUG_JOB; break; case 'l': debug |= DEBUG_LOUD; break; case 'M': debug |= DEBUG_META; break; case 'm': debug |= DEBUG_MAKE; break; case 'n': debug |= DEBUG_SCRIPT; break; case 'p': debug |= DEBUG_PARSE; break; case 's': debug |= DEBUG_SUFF; break; case 't': debug |= DEBUG_TARG; break; case 'V': debugVflag = TRUE; break; case 'v': debug |= DEBUG_VAR; break; case 'x': debug |= DEBUG_SHELL; break; case 'F': if (debug_file != stdout && debug_file != stderr) fclose(debug_file); if (*++modules == '+') { modules++; mode = "a"; } else mode = "w"; if (strcmp(modules, "stdout") == 0) { debug_file = stdout; goto debug_setbuf; } if (strcmp(modules, "stderr") == 0) { debug_file = stderr; goto debug_setbuf; } len = strlen(modules); fname = bmake_malloc(len + 20); memcpy(fname, modules, len + 1); /* Let the filename be modified by the pid */ if (strcmp(fname + len - 3, ".%d") == 0) snprintf(fname + len - 2, 20, "%d", getpid()); debug_file = fopen(fname, mode); if (!debug_file) { fprintf(stderr, "Cannot open debug file %s\n", fname); usage(); } free(fname); goto debug_setbuf; default: (void)fprintf(stderr, "%s: illegal argument to d option -- %c\n", progname, *modules); usage(); } } debug_setbuf: /* * Make the debug_file unbuffered, and make * stdout line buffered (unless debugfile == stdout). */ setvbuf(debug_file, NULL, _IONBF, 0); if (debug_file != stdout) { setvbuf(stdout, NULL, _IOLBF, 0); } } /* * does path contain any relative components */ static int is_relpath(const char *path) { const char *cp; if (path[0] != '/') return TRUE; cp = path; do { cp = strstr(cp, "/."); if (!cp) break; cp += 2; if (cp[0] == '/' || cp[0] == '\0') return TRUE; else if (cp[0] == '.') { if (cp[1] == '/' || cp[1] == '\0') return TRUE; } } while (cp); return FALSE; } /*- * MainParseArgs -- * Parse a given argument vector. Called from main() and from * Main_ParseArgLine() when the .MAKEFLAGS target is used. * * XXX: Deal with command line overriding .MAKEFLAGS in makefile * * Results: * None * * Side Effects: * Various global and local flags will be set depending on the flags * given */ static void MainParseArgs(int argc, char **argv) { char *p; int c = '?'; int arginc; char *argvalue; const char *getopt_def; struct stat sa, sb; char *optscan; Boolean inOption, dashDash = FALSE; char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ #define OPTFLAGS "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w" /* Can't actually use getopt(3) because rescanning is not portable */ getopt_def = OPTFLAGS; rearg: inOption = FALSE; optscan = NULL; while(argc > 1) { char *getopt_spec; if(!inOption) optscan = argv[1]; c = *optscan++; arginc = 0; if(inOption) { if(c == '\0') { ++argv; --argc; inOption = FALSE; continue; } } else { if (c != '-' || dashDash) break; inOption = TRUE; c = *optscan++; } /* '-' found at some earlier point */ getopt_spec = strchr(getopt_def, c); if(c != '\0' && getopt_spec != NULL && getopt_spec[1] == ':') { /* - found, and should have an arg */ inOption = FALSE; arginc = 1; argvalue = optscan; if(*argvalue == '\0') { if (argc < 3) goto noarg; argvalue = argv[2]; arginc = 2; } } else { argvalue = NULL; } switch(c) { case '\0': arginc = 1; inOption = FALSE; break; case 'B': compatMake = TRUE; Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL); Var_Set(MAKE_MODE, "compat", VAR_GLOBAL, 0); break; case 'C': if (chdir(argvalue) == -1) { (void)fprintf(stderr, "%s: chdir %s: %s\n", progname, argvalue, strerror(errno)); exit(1); } if (getcwd(curdir, MAXPATHLEN) == NULL) { (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); exit(2); } if (!is_relpath(argvalue) && stat(argvalue, &sa) != -1 && stat(curdir, &sb) != -1 && sa.st_ino == sb.st_ino && sa.st_dev == sb.st_dev) strncpy(curdir, argvalue, MAXPATHLEN); ignorePWD = TRUE; break; case 'D': if (argvalue == NULL || argvalue[0] == 0) goto noarg; Var_Set(argvalue, "1", VAR_GLOBAL, 0); Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'I': if (argvalue == NULL) goto noarg; Parse_AddIncludeDir(argvalue); Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'J': if (argvalue == NULL) goto noarg; if (sscanf(argvalue, "%d,%d", &jp_0, &jp_1) != 2) { (void)fprintf(stderr, "%s: internal error -- J option malformed (%s)\n", progname, argvalue); usage(); } if ((fcntl(jp_0, F_GETFD, 0) < 0) || (fcntl(jp_1, F_GETFD, 0) < 0)) { #if 0 (void)fprintf(stderr, "%s: ###### warning -- J descriptors were closed!\n", progname); exit(2); #endif jp_0 = -1; jp_1 = -1; compatMake = TRUE; } else { Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); jobServer = TRUE; } break; case 'N': noExecute = TRUE; noRecursiveExecute = TRUE; Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL); break; case 'S': keepgoing = FALSE; Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); break; case 'T': if (argvalue == NULL) goto noarg; tracefile = bmake_strdup(argvalue); Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'V': case 'v': if (argvalue == NULL) goto noarg; printVars = c == 'v' ? EXPAND_VARS : COMPAT_VARS; (void)Lst_AtEnd(variables, argvalue); Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'W': parseWarnFatal = TRUE; break; case 'X': varNoExportEnv = TRUE; Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL); break; case 'd': if (argvalue == NULL) goto noarg; /* If '-d-opts' don't pass to children */ if (argvalue[0] == '-') argvalue++; else { Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); } parse_debug_options(argvalue); break; case 'e': checkEnvFirst = TRUE; Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); break; case 'f': if (argvalue == NULL) goto noarg; (void)Lst_AtEnd(makefiles, argvalue); break; case 'i': ignoreErrors = TRUE; Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); break; case 'j': if (argvalue == NULL) goto noarg; forceJobs = TRUE; maxJobs = strtol(argvalue, &p, 0); if (*p != '\0' || maxJobs < 1) { (void)fprintf(stderr, "%s: illegal argument to -j -- must be positive integer!\n", progname); exit(1); } Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); Var_Set(".MAKE.JOBS", argvalue, VAR_GLOBAL, 0); maxJobTokens = maxJobs; break; case 'k': keepgoing = TRUE; Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); break; case 'm': if (argvalue == NULL) goto noarg; /* look for magic parent directory search string */ if (strncmp(".../", argvalue, 4) == 0) { if (!Dir_FindHereOrAbove(curdir, argvalue+4, found_path, sizeof(found_path))) break; /* nothing doing */ (void)Dir_AddDir(sysIncPath, found_path); } else { (void)Dir_AddDir(sysIncPath, argvalue); } Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL); Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); break; case 'n': noExecute = TRUE; Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); break; case 'q': queryFlag = TRUE; /* Kind of nonsensical, wot? */ Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); break; case 'r': noBuiltins = TRUE; Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); break; case 's': beSilent = TRUE; Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); break; case 't': touchFlag = TRUE; Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); break; case 'w': enterFlag = TRUE; Var_Append(MAKEFLAGS, "-w", VAR_GLOBAL); break; case '-': dashDash = TRUE; break; default: case '?': #ifndef MAKE_NATIVE fprintf(stderr, "getopt(%s) -> %d (%c)\n", OPTFLAGS, c, c); #endif usage(); } argv += arginc; argc -= arginc; } oldVars = TRUE; /* * See if the rest of the arguments are variable assignments and * perform them if so. Else take them to be targets and stuff them * on the end of the "create" list. */ for (; argc > 1; ++argv, --argc) if (Parse_IsVar(argv[1])) { Parse_DoVar(argv[1], VAR_CMD); } else { if (!*argv[1]) Punt("illegal (null) argument."); if (*argv[1] == '-' && !dashDash) goto rearg; (void)Lst_AtEnd(create, bmake_strdup(argv[1])); } return; noarg: (void)fprintf(stderr, "%s: option requires an argument -- %c\n", progname, c); usage(); } /*- * Main_ParseArgLine -- * Used by the parse module when a .MFLAGS or .MAKEFLAGS target * is encountered and by main() when reading the .MAKEFLAGS envariable. * Takes a line of arguments and breaks it into its * component words and passes those words and the number of them to the * MainParseArgs function. * The line should have all its leading whitespace removed. * * Input: * line Line to fracture * * Results: * None * * Side Effects: * Only those that come from the various arguments. */ void Main_ParseArgLine(const char *line) { char **argv; /* Manufactured argument vector */ int argc; /* Number of arguments in argv */ char *args; /* Space used by the args */ char *buf, *p1; char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1); size_t len; if (line == NULL) return; for (; *line == ' '; ++line) continue; if (!*line) return; #ifndef POSIX { /* * $MAKE may simply be naming the make(1) binary */ char *cp; if (!(cp = strrchr(line, '/'))) cp = line; if ((cp = strstr(cp, "make")) && strcmp(cp, "make") == 0) return; } #endif buf = bmake_malloc(len = strlen(line) + strlen(argv0) + 2); (void)snprintf(buf, len, "%s %s", argv0, line); free(p1); argv = brk_string(buf, &argc, TRUE, &args); if (argv == NULL) { Error("Unterminated quoted string [%s]", buf); free(buf); return; } free(buf); MainParseArgs(argc, argv); free(args); free(argv); } Boolean Main_SetObjdir(const char *fmt, ...) { struct stat sb; char *path; char buf[MAXPATHLEN + 1]; char buf2[MAXPATHLEN + 1]; Boolean rc = FALSE; va_list ap; va_start(ap, fmt); vsnprintf(path = buf, MAXPATHLEN, fmt, ap); va_end(ap); if (path[0] != '/') { snprintf(buf2, MAXPATHLEN, "%s/%s", curdir, path); path = buf2; } /* look for the directory and try to chdir there */ if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { if (chdir(path)) { (void)fprintf(stderr, "make warning: %s: %s.\n", path, strerror(errno)); } else { strncpy(objdir, path, MAXPATHLEN); Var_Set(".OBJDIR", objdir, VAR_GLOBAL, 0); setenv("PWD", objdir, 1); Dir_InitDot(); purge_cached_realpaths(); rc = TRUE; if (enterFlag && strcmp(objdir, curdir) != 0) enterFlagObj = TRUE; } } return rc; } static Boolean Main_SetVarObjdir(const char *var, const char *suffix) { char *p, *path, *xpath; if ((path = Var_Value(var, VAR_CMD, &p)) == NULL || *path == '\0') return FALSE; /* expand variable substitutions */ if (strchr(path, '$') != 0) xpath = Var_Subst(NULL, path, VAR_GLOBAL, VARF_WANTRES); else xpath = path; (void)Main_SetObjdir("%s%s", xpath, suffix); if (xpath != path) free(xpath); free(p); return TRUE; } /*- * ReadAllMakefiles -- * wrapper around ReadMakefile() to read all. * * Results: * TRUE if ok, FALSE on error */ static int ReadAllMakefiles(const void *p, const void *q) { return (ReadMakefile(p, q) == 0); } int str2Lst_Append(Lst lp, char *str, const char *sep) { char *cp; int n; if (!sep) sep = " \t"; for (n = 0, cp = strtok(str, sep); cp; cp = strtok(NULL, sep)) { (void)Lst_AtEnd(lp, cp); n++; } return (n); } #ifdef SIGINFO /*ARGSUSED*/ static void siginfo(int signo MAKE_ATTR_UNUSED) { char dir[MAXPATHLEN]; char str[2 * MAXPATHLEN]; int len; if (getcwd(dir, sizeof(dir)) == NULL) return; len = snprintf(str, sizeof(str), "%s: Working in: %s\n", progname, dir); if (len > 0) (void)write(STDERR_FILENO, str, (size_t)len); } #endif /* * Allow makefiles some control over the mode we run in. */ void MakeMode(const char *mode) { char *mp = NULL; if (!mode) mode = mp = Var_Subst(NULL, "${" MAKE_MODE ":tl}", VAR_GLOBAL, VARF_WANTRES); if (mode && *mode) { if (strstr(mode, "compat")) { compatMake = TRUE; forceJobs = FALSE; } #if USE_META if (strstr(mode, "meta")) meta_mode_init(mode); #endif } free(mp); } static void doPrintVars(void) { LstNode ln; Boolean expandVars; if (printVars == EXPAND_VARS) expandVars = TRUE; else if (debugVflag) expandVars = FALSE; else expandVars = getBoolean(".MAKE.EXPAND_VARIABLES", FALSE); for (ln = Lst_First(variables); ln != NULL; ln = Lst_Succ(ln)) { char *var = (char *)Lst_Datum(ln); char *value; char *p1; if (strchr(var, '$')) { value = p1 = Var_Subst(NULL, var, VAR_GLOBAL, VARF_WANTRES); } else if (expandVars) { char tmp[128]; int len = snprintf(tmp, sizeof(tmp), "${%s}", var); if (len >= (int)sizeof(tmp)) Fatal("%s: variable name too big: %s", progname, var); value = p1 = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); } else { value = Var_Value(var, VAR_GLOBAL, &p1); } printf("%s\n", value ? value : ""); free(p1); } } static Boolean runTargets(void) { Lst targs; /* target nodes to create -- passed to Make_Init */ Boolean outOfDate; /* FALSE if all targets up to date */ /* * Have now read the entire graph and need to make a list of * targets to create. If none was given on the command line, * we consult the parsing module to find the main target(s) * to create. */ if (Lst_IsEmpty(create)) targs = Parse_MainName(); else targs = Targ_FindList(create, TARG_CREATE); if (!compatMake) { /* * Initialize job module before traversing the graph * now that any .BEGIN and .END targets have been read. * This is done only if the -q flag wasn't given * (to prevent the .BEGIN from being executed should * it exist). */ if (!queryFlag) { Job_Init(); jobsRunning = TRUE; } /* Traverse the graph, checking on all the targets */ outOfDate = Make_Run(targs); } else { /* * Compat_Init will take care of creating all the * targets as well as initializing the module. */ Compat_Run(targs); outOfDate = FALSE; } Lst_Destroy(targs, NULL); return outOfDate; } /*- * main -- * The main function, for obvious reasons. Initializes variables * and a few modules, then parses the arguments give it in the * environment and on the command line. Reads the system makefile * followed by either Makefile, makefile or the file given by the * -f argument. Sets the .MAKEFLAGS PMake variable based on all the * flags it has received by then uses either the Make or the Compat * module to create the initial list of targets. * * Results: * If -q was given, exits -1 if anything was out-of-date. Else it exits * 0. * * Side Effects: * The program exits when done. Targets are created. etc. etc. etc. */ int main(int argc, char **argv) { Boolean outOfDate; /* FALSE if all targets up to date */ struct stat sb, sa; char *p1, *path; char mdpath[MAXPATHLEN]; #ifdef FORCE_MACHINE const char *machine = FORCE_MACHINE; #else const char *machine = getenv("MACHINE"); #endif const char *machine_arch = getenv("MACHINE_ARCH"); char *syspath = getenv("MAKESYSPATH"); Lst sysMkPath; /* Path of sys.mk */ char *cp = NULL, *start; /* avoid faults on read-only strings */ static char defsyspath[] = _PATH_DEFSYSPATH; char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ struct timeval rightnow; /* to initialize random seed */ struct utsname utsname; /* default to writing debug to stderr */ debug_file = stderr; #ifdef SIGINFO (void)bmake_signal(SIGINFO, siginfo); #endif /* * Set the seed to produce a different random sequence * on each program execution. */ gettimeofday(&rightnow, NULL); srandom(rightnow.tv_sec + rightnow.tv_usec); if ((progname = strrchr(argv[0], '/')) != NULL) progname++; else progname = argv[0]; #if defined(MAKE_NATIVE) || (defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)) /* * get rid of resource limit on file descriptors */ { struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && rl.rlim_cur != rl.rlim_max) { rl.rlim_cur = rl.rlim_max; (void)setrlimit(RLIMIT_NOFILE, &rl); } } #endif if (uname(&utsname) == -1) { (void)fprintf(stderr, "%s: uname failed (%s).\n", progname, strerror(errno)); exit(2); } /* * Get the name of this type of MACHINE from utsname * so we can share an executable for similar machines. * (i.e. m68k: amiga hp300, mac68k, sun3, ...) * * Note that both MACHINE and MACHINE_ARCH are decided at * run-time. */ if (!machine) { #ifdef MAKE_NATIVE machine = utsname.machine; #else #ifdef MAKE_MACHINE machine = MAKE_MACHINE; #else machine = "unknown"; #endif #endif } if (!machine_arch) { #if defined(MAKE_NATIVE) && defined(HAVE_SYSCTL) && defined(CTL_HW) && defined(HW_MACHINE_ARCH) static char machine_arch_buf[sizeof(utsname.machine)]; int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; size_t len = sizeof(machine_arch_buf); if (sysctl(mib, __arraycount(mib), machine_arch_buf, &len, NULL, 0) < 0) { (void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname, strerror(errno)); exit(2); } machine_arch = machine_arch_buf; #else #ifndef MACHINE_ARCH #ifdef MAKE_MACHINE_ARCH machine_arch = MAKE_MACHINE_ARCH; #else machine_arch = "unknown"; #endif #else machine_arch = MACHINE_ARCH; #endif #endif } myPid = getpid(); /* remember this for vFork() */ /* * Just in case MAKEOBJDIR wants us to do something tricky. */ Var_Init(); /* Initialize the lists of variables for * parsing arguments */ Var_Set(".MAKE.OS", utsname.sysname, VAR_GLOBAL, 0); Var_Set("MACHINE", machine, VAR_GLOBAL, 0); Var_Set("MACHINE_ARCH", machine_arch, VAR_GLOBAL, 0); #ifdef MAKE_VERSION Var_Set("MAKE_VERSION", MAKE_VERSION, VAR_GLOBAL, 0); #endif Var_Set(".newline", "\n", VAR_GLOBAL, 0); /* handy for :@ loops */ /* * This is the traditional preference for makefiles. */ #ifndef MAKEFILE_PREFERENCE_LIST # define MAKEFILE_PREFERENCE_LIST "makefile Makefile" #endif Var_Set(MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST, VAR_GLOBAL, 0); Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL, 0); create = Lst_Init(FALSE); makefiles = Lst_Init(FALSE); printVars = 0; debugVflag = FALSE; variables = Lst_Init(FALSE); beSilent = FALSE; /* Print commands as executed */ ignoreErrors = FALSE; /* Pay attention to non-zero returns */ noExecute = FALSE; /* Execute all commands */ noRecursiveExecute = FALSE; /* Execute all .MAKE targets */ keepgoing = FALSE; /* Stop on error */ allPrecious = FALSE; /* Remove targets when interrupted */ deleteOnError = FALSE; /* Historical default behavior */ queryFlag = FALSE; /* This is not just a check-run */ noBuiltins = FALSE; /* Read the built-in rules */ touchFlag = FALSE; /* Actually update targets */ debug = 0; /* No debug verbosity, please. */ jobsRunning = FALSE; maxJobs = DEFMAXLOCAL; /* Set default local max concurrency */ maxJobTokens = maxJobs; compatMake = FALSE; /* No compat mode */ ignorePWD = FALSE; /* * Initialize the parsing, directory and variable modules to prepare * for the reading of inclusion paths and variable settings on the * command line */ /* * Initialize various variables. * MAKE also gets this name, for compatibility * .MAKEFLAGS gets set to the empty string just in case. * MFLAGS also gets initialized empty, for compatibility. */ Parse_Init(); if (argv[0][0] == '/' || strchr(argv[0], '/') == NULL) { /* * Leave alone if it is an absolute path, or if it does * not contain a '/' in which case we need to find it in * the path, like execvp(3) and the shells do. */ p1 = argv[0]; } else { /* * A relative path, canonicalize it. */ p1 = cached_realpath(argv[0], mdpath); if (!p1 || *p1 != '/' || stat(p1, &sb) < 0) { p1 = argv[0]; /* realpath failed */ } } Var_Set("MAKE", p1, VAR_GLOBAL, 0); Var_Set(".MAKE", p1, VAR_GLOBAL, 0); Var_Set(MAKEFLAGS, "", VAR_GLOBAL, 0); Var_Set(MAKEOVERRIDES, "", VAR_GLOBAL, 0); Var_Set("MFLAGS", "", VAR_GLOBAL, 0); Var_Set(".ALLTARGETS", "", VAR_GLOBAL, 0); /* some makefiles need to know this */ Var_Set(MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV, VAR_CMD, 0); /* * Set some other useful macros */ { char tmp[64], *ep; makelevel = ((ep = getenv(MAKE_LEVEL_ENV)) && *ep) ? atoi(ep) : 0; if (makelevel < 0) makelevel = 0; snprintf(tmp, sizeof(tmp), "%d", makelevel); Var_Set(MAKE_LEVEL, tmp, VAR_GLOBAL, 0); snprintf(tmp, sizeof(tmp), "%u", myPid); Var_Set(".MAKE.PID", tmp, VAR_GLOBAL, 0); snprintf(tmp, sizeof(tmp), "%u", getppid()); Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL, 0); } if (makelevel > 0) { char pn[1024]; snprintf(pn, sizeof(pn), "%s[%d]", progname, makelevel); progname = bmake_strdup(pn); } #ifdef USE_META meta_init(); #endif Dir_Init(NULL); /* Dir_* safe to call from MainParseArgs */ /* * First snag any flags out of the MAKE environment variable. * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's * in a different format). */ #ifdef POSIX p1 = explode(getenv("MAKEFLAGS")); Main_ParseArgLine(p1); free(p1); #else Main_ParseArgLine(getenv("MAKE")); #endif /* * Find where we are (now). * We take care of PWD for the automounter below... */ if (getcwd(curdir, MAXPATHLEN) == NULL) { (void)fprintf(stderr, "%s: getcwd: %s.\n", progname, strerror(errno)); exit(2); } MainParseArgs(argc, argv); if (enterFlag) printf("%s: Entering directory `%s'\n", progname, curdir); /* * Verify that cwd is sane. */ if (stat(curdir, &sa) == -1) { (void)fprintf(stderr, "%s: %s: %s.\n", progname, curdir, strerror(errno)); exit(2); } /* * All this code is so that we know where we are when we start up * on a different machine with pmake. * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX * since the value of curdir can vary depending on how we got * here. Ie sitting at a shell prompt (shell that provides $PWD) * or via subdir.mk in which case its likely a shell which does * not provide it. * So, to stop it breaking this case only, we ignore PWD if * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a transform. */ #ifndef NO_PWD_OVERRIDE if (!ignorePWD) { char *pwd, *ptmp1 = NULL, *ptmp2 = NULL; if ((pwd = getenv("PWD")) != NULL && Var_Value("MAKEOBJDIRPREFIX", VAR_CMD, &ptmp1) == NULL) { const char *makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMD, &ptmp2); if (makeobjdir == NULL || !strchr(makeobjdir, '$')) { if (stat(pwd, &sb) == 0 && sa.st_ino == sb.st_ino && sa.st_dev == sb.st_dev) (void)strncpy(curdir, pwd, MAXPATHLEN); } } free(ptmp1); free(ptmp2); } #endif Var_Set(".CURDIR", curdir, VAR_GLOBAL, 0); /* * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, * MAKEOBJDIR is set in the environment, try only that value * and fall back to .CURDIR if it does not exist. * * Otherwise, try _PATH_OBJDIR.MACHINE-MACHINE_ARCH, _PATH_OBJDIR.MACHINE, * and * finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none * of these paths exist, just use .CURDIR. */ Dir_Init(curdir); (void)Main_SetObjdir("%s", curdir); if (!Main_SetVarObjdir("MAKEOBJDIRPREFIX", curdir) && !Main_SetVarObjdir("MAKEOBJDIR", "") && !Main_SetObjdir("%s.%s-%s", _PATH_OBJDIR, machine, machine_arch) && !Main_SetObjdir("%s.%s", _PATH_OBJDIR, machine) && !Main_SetObjdir("%s", _PATH_OBJDIR)) (void)Main_SetObjdir("%s%s", _PATH_OBJDIRPREFIX, curdir); /* * Initialize archive, target and suffix modules in preparation for * parsing the makefile(s) */ Arch_Init(); Targ_Init(); Suff_Init(); Trace_Init(tracefile); DEFAULT = NULL; (void)time(&now); Trace_Log(MAKESTART, NULL); /* * Set up the .TARGETS variable to contain the list of targets to be * created. If none specified, make the variable empty -- the parser * will fill the thing in with the default or .MAIN target. */ if (!Lst_IsEmpty(create)) { LstNode ln; for (ln = Lst_First(create); ln != NULL; ln = Lst_Succ(ln)) { char *name = (char *)Lst_Datum(ln); Var_Append(".TARGETS", name, VAR_GLOBAL); } } else Var_Set(".TARGETS", "", VAR_GLOBAL, 0); /* * If no user-supplied system path was given (through the -m option) * add the directories from the DEFSYSPATH (more than one may be given * as dir1:...:dirn) to the system include path. */ if (syspath == NULL || *syspath == '\0') syspath = defsyspath; else syspath = bmake_strdup(syspath); for (start = syspath; *start != '\0'; start = cp) { for (cp = start; *cp != '\0' && *cp != ':'; cp++) continue; if (*cp == ':') { *cp++ = '\0'; } /* look for magic parent directory search string */ if (strncmp(".../", start, 4) != 0) { (void)Dir_AddDir(defIncPath, start); } else { if (Dir_FindHereOrAbove(curdir, start+4, found_path, sizeof(found_path))) { (void)Dir_AddDir(defIncPath, found_path); } } } if (syspath != defsyspath) free(syspath); /* * Read in the built-in rules first, followed by the specified * makefile, if it was (makefile != NULL), or the default * makefile and Makefile, in that order, if it wasn't. */ if (!noBuiltins) { LstNode ln; sysMkPath = Lst_Init(FALSE); Dir_Expand(_PATH_DEFSYSMK, Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath, sysMkPath); if (Lst_IsEmpty(sysMkPath)) Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK); ln = Lst_Find(sysMkPath, NULL, ReadMakefile); if (ln == NULL) Fatal("%s: cannot open %s.", progname, (char *)Lst_Datum(ln)); } if (!Lst_IsEmpty(makefiles)) { LstNode ln; ln = Lst_Find(makefiles, NULL, ReadAllMakefiles); if (ln != NULL) Fatal("%s: cannot open %s.", progname, (char *)Lst_Datum(ln)); } else { p1 = Var_Subst(NULL, "${" MAKEFILE_PREFERENCE "}", VAR_CMD, VARF_WANTRES); if (p1) { (void)str2Lst_Append(makefiles, p1, NULL); (void)Lst_Find(makefiles, NULL, ReadMakefile); free(p1); } } /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ if (!noBuiltins || !printVars) { makeDependfile = Var_Subst(NULL, "${.MAKE.DEPENDFILE:T}", VAR_CMD, VARF_WANTRES); doing_depend = TRUE; (void)ReadMakefile(makeDependfile, NULL); doing_depend = FALSE; } if (enterFlagObj) printf("%s: Entering directory `%s'\n", progname, objdir); MakeMode(NULL); Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL); free(p1); if (!forceJobs && !compatMake && Var_Exists(".MAKE.JOBS", VAR_GLOBAL)) { char *value; int n; value = Var_Subst(NULL, "${.MAKE.JOBS}", VAR_GLOBAL, VARF_WANTRES); n = strtol(value, NULL, 0); if (n < 1) { (void)fprintf(stderr, "%s: illegal value for .MAKE.JOBS -- must be positive integer!\n", progname); exit(1); } if (n != maxJobs) { Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); Var_Append(MAKEFLAGS, value, VAR_GLOBAL); } maxJobs = n; maxJobTokens = maxJobs; forceJobs = TRUE; free(value); } /* * Be compatible if user did not specify -j and did not explicitly * turned compatibility on */ if (!compatMake && !forceJobs) { compatMake = TRUE; } if (!compatMake) Job_ServerStart(maxJobTokens, jp_0, jp_1); if (DEBUG(JOB)) fprintf(debug_file, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", jp_0, jp_1, maxJobs, maxJobTokens, compatMake); if (!printVars) Main_ExportMAKEFLAGS(TRUE); /* initial export */ /* * For compatibility, look at the directories in the VPATH variable * and add them to the search path, if the variable is defined. The * variable's value is in the same format as the PATH envariable, i.e. * ::... */ if (Var_Exists("VPATH", VAR_CMD)) { char *vpath, savec; /* * GCC stores string constants in read-only memory, but * Var_Subst will want to write this thing, so store it * in an array */ static char VPATH[] = "${VPATH}"; vpath = Var_Subst(NULL, VPATH, VAR_CMD, VARF_WANTRES); path = vpath; do { /* skip to end of directory */ for (cp = path; *cp != ':' && *cp != '\0'; cp++) continue; /* Save terminator character so know when to stop */ savec = *cp; *cp = '\0'; /* Add directory to search path */ (void)Dir_AddDir(dirSearchPath, path); *cp = savec; path = cp + 1; } while (savec == ':'); free(vpath); } /* * Now that all search paths have been read for suffixes et al, it's * time to add the default search path to their lists... */ Suff_DoPaths(); /* * Propagate attributes through :: dependency lists. */ Targ_Propagate(); /* print the initial graph, if the user requested it */ if (DEBUG(GRAPH1)) Targ_PrintGraph(1); /* print the values of any variables requested by the user */ if (printVars) { doPrintVars(); outOfDate = FALSE; } else { outOfDate = runTargets(); } #ifdef CLEANUP Lst_Destroy(variables, NULL); Lst_Destroy(makefiles, NULL); Lst_Destroy(create, (FreeProc *)free); #endif /* print the graph now it's been processed if the user requested it */ if (DEBUG(GRAPH2)) Targ_PrintGraph(2); Trace_Log(MAKEEND, 0); if (enterFlagObj) printf("%s: Leaving directory `%s'\n", progname, objdir); if (enterFlag) printf("%s: Leaving directory `%s'\n", progname, curdir); #ifdef USE_META meta_finish(); #endif Suff_End(); Targ_End(); Arch_End(); Var_End(); Parse_End(); Dir_End(); Job_End(); Trace_End(); return outOfDate ? 1 : 0; } /*- * ReadMakefile -- * Open and parse the given makefile. * * Results: * 0 if ok. -1 if couldn't open file. * * Side Effects: * lots */ static int ReadMakefile(const void *p, const void *q MAKE_ATTR_UNUSED) { const char *fname = p; /* makefile to read */ int fd; size_t len = MAXPATHLEN; char *name, *path = bmake_malloc(len); if (!strcmp(fname, "-")) { Parse_File(NULL /*stdin*/, -1); Var_Set("MAKEFILE", "", VAR_INTERNAL, 0); } else { /* if we've chdir'd, rebuild the path name */ if (strcmp(curdir, objdir) && *fname != '/') { size_t plen = strlen(curdir) + strlen(fname) + 2; if (len < plen) path = bmake_realloc(path, len = 2 * plen); (void)snprintf(path, len, "%s/%s", curdir, fname); fd = open(path, O_RDONLY); if (fd != -1) { fname = path; goto found; } /* If curdir failed, try objdir (ala .depend) */ plen = strlen(objdir) + strlen(fname) + 2; if (len < plen) path = bmake_realloc(path, len = 2 * plen); (void)snprintf(path, len, "%s/%s", objdir, fname); fd = open(path, O_RDONLY); if (fd != -1) { fname = path; goto found; } } else { fd = open(fname, O_RDONLY); if (fd != -1) goto found; } /* look in -I and system include directories. */ name = Dir_FindFile(fname, parseIncPath); if (!name) name = Dir_FindFile(fname, Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath); if (!name || (fd = open(name, O_RDONLY)) == -1) { free(name); free(path); return(-1); } fname = name; /* * set the MAKEFILE variable desired by System V fans -- the * placement of the setting here means it gets set to the last * makefile specified, as it is set by SysV make. */ found: if (!doing_depend) Var_Set("MAKEFILE", fname, VAR_INTERNAL, 0); Parse_File(fname, fd); } free(path); return(0); } /*- * Cmd_Exec -- * Execute the command in cmd, and return the output of that command * in a string. * * Results: * A string containing the output of the command, or the empty string * If errnum is not NULL, it contains the reason for the command failure * * Side Effects: * The string must be freed by the caller. */ char * Cmd_Exec(const char *cmd, const char **errnum) { const char *args[4]; /* Args for invoking the shell */ int fds[2]; /* Pipe streams */ int cpid; /* Child PID */ int pid; /* PID from wait() */ char *res; /* result */ WAIT_T status; /* command exit status */ Buffer buf; /* buffer to store the result */ char *cp; int cc; /* bytes read, or -1 */ int savederr; /* saved errno */ *errnum = NULL; if (!shellName) Shell_Init(); /* * Set up arguments for shell */ args[0] = shellName; args[1] = "-c"; args[2] = cmd; args[3] = NULL; /* * Open a pipe for fetching its output */ if (pipe(fds) == -1) { *errnum = "Couldn't create pipe for \"%s\""; goto bad; } /* * Fork */ switch (cpid = vFork()) { case 0: /* * Close input side of pipe */ (void)close(fds[0]); /* * Duplicate the output stream to the shell's output, then * shut the extra thing down. Note we don't fetch the error * stream...why not? Why? */ (void)dup2(fds[1], 1); (void)close(fds[1]); Var_ExportVars(); (void)execv(shellPath, UNCONST(args)); _exit(1); /*NOTREACHED*/ case -1: *errnum = "Couldn't exec \"%s\""; goto bad; default: /* * No need for the writing half */ (void)close(fds[1]); savederr = 0; Buf_Init(&buf, 0); do { char result[BUFSIZ]; cc = read(fds[0], result, sizeof(result)); if (cc > 0) Buf_AddBytes(&buf, cc, result); } while (cc > 0 || (cc == -1 && errno == EINTR)); if (cc == -1) savederr = errno; /* * Close the input side of the pipe. */ (void)close(fds[0]); /* * Wait for the process to exit. */ while(((pid = waitpid(cpid, &status, 0)) != cpid) && (pid >= 0)) { JobReapChild(pid, status, FALSE); continue; } cc = Buf_Size(&buf); res = Buf_Destroy(&buf, FALSE); if (savederr != 0) *errnum = "Couldn't read shell's output for \"%s\""; if (WIFSIGNALED(status)) *errnum = "\"%s\" exited on a signal"; else if (WEXITSTATUS(status) != 0) *errnum = "\"%s\" returned non-zero status"; /* * Null-terminate the result, convert newlines to spaces and * install it in the variable. */ res[cc] = '\0'; cp = &res[cc]; if (cc > 0 && *--cp == '\n') { /* * A final newline is just stripped */ *cp-- = '\0'; } while (cp >= res) { if (*cp == '\n') { *cp = ' '; } cp--; } break; } return res; bad: res = bmake_malloc(1); *res = '\0'; return res; } /*- * Error -- * Print an error message given its format. * * Results: * None. * * Side Effects: * The message is printed. */ /* VARARGS */ void Error(const char *fmt, ...) { va_list ap; FILE *err_file; err_file = debug_file; if (err_file == stdout) err_file = stderr; (void)fflush(stdout); for (;;) { va_start(ap, fmt); fprintf(err_file, "%s: ", progname); (void)vfprintf(err_file, fmt, ap); va_end(ap); (void)fprintf(err_file, "\n"); (void)fflush(err_file); if (err_file == stderr) break; err_file = stderr; } } /*- * Fatal -- * Produce a Fatal error message. If jobs are running, waits for them * to finish. * * Results: * None * * Side Effects: * The program exits */ /* VARARGS */ void Fatal(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (jobsRunning) Job_Wait(); (void)fflush(stdout); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); (void)fflush(stderr); PrintOnError(NULL, NULL); if (DEBUG(GRAPH2) || DEBUG(GRAPH3)) Targ_PrintGraph(2); Trace_Log(MAKEERROR, 0); exit(2); /* Not 1 so -q can distinguish error */ } /* * Punt -- * Major exception once jobs are being created. Kills all jobs, prints * a message and exits. * * Results: * None * * Side Effects: * All children are killed indiscriminately and the program Lib_Exits */ /* VARARGS */ void Punt(const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)fflush(stdout); (void)fprintf(stderr, "%s: ", progname); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); (void)fflush(stderr); PrintOnError(NULL, NULL); DieHorribly(); } /*- * DieHorribly -- * Exit without giving a message. * * Results: * None * * Side Effects: * A big one... */ void DieHorribly(void) { if (jobsRunning) Job_AbortAll(); if (DEBUG(GRAPH2)) Targ_PrintGraph(2); Trace_Log(MAKEERROR, 0); exit(2); /* Not 1, so -q can distinguish error */ } /* * Finish -- * Called when aborting due to errors in child shell to signal * abnormal exit. * * Results: * None * * Side Effects: * The program exits */ void Finish(int errors) /* number of errors encountered in Make_Make */ { Fatal("%d error%s", errors, errors == 1 ? "" : "s"); } /* * eunlink -- * Remove a file carefully, avoiding directories. */ int eunlink(const char *file) { struct stat st; if (lstat(file, &st) == -1) return -1; if (S_ISDIR(st.st_mode)) { errno = EISDIR; return -1; } return unlink(file); } /* * execError -- * Print why exec failed, avoiding stdio. */ void execError(const char *af, const char *av) { #ifdef USE_IOVEC int i = 0; struct iovec iov[8]; #define IOADD(s) \ (void)(iov[i].iov_base = UNCONST(s), \ iov[i].iov_len = strlen(iov[i].iov_base), \ i++) #else #define IOADD(s) (void)write(2, s, strlen(s)) #endif IOADD(progname); IOADD(": "); IOADD(af); IOADD("("); IOADD(av); IOADD(") failed ("); IOADD(strerror(errno)); IOADD(")\n"); #ifdef USE_IOVEC while (writev(2, iov, 8) == -1 && errno == EAGAIN) continue; #endif } /* * usage -- * exit with usage message */ static void usage(void) { char *p; if ((p = strchr(progname, '[')) != NULL) *p = '\0'; (void)fprintf(stderr, "usage: %s [-BeikNnqrstWwX] \n\ [-C directory] [-D variable] [-d flags] [-f makefile]\n\ [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n\ [-V variable] [-v variable] [variable=value] [target ...]\n", progname); exit(2); } /* * realpath(3) can get expensive, cache results... */ static GNode *cached_realpaths = NULL; static GNode * get_cached_realpaths(void) { if (!cached_realpaths) { cached_realpaths = Targ_NewGN("Realpath"); #ifndef DEBUG_REALPATH_CACHE cached_realpaths->flags = INTERNAL; #endif } return cached_realpaths; } /* purge any relative paths */ static void purge_cached_realpaths(void) { GNode *cache = get_cached_realpaths(); Hash_Entry *he, *nhe; Hash_Search hs; he = Hash_EnumFirst(&cache->context, &hs); while (he) { nhe = Hash_EnumNext(&hs); if (he->name[0] != '/') { if (DEBUG(DIR)) fprintf(stderr, "cached_realpath: purging %s\n", he->name); Hash_DeleteEntry(&cache->context, he); } he = nhe; } } char * cached_realpath(const char *pathname, char *resolved) { GNode *cache; char *rp, *cp; if (!pathname || !pathname[0]) return NULL; cache = get_cached_realpaths(); if ((rp = Var_Value(pathname, cache, &cp)) != NULL) { /* a hit */ strlcpy(resolved, rp, MAXPATHLEN); } else if ((rp = realpath(pathname, resolved)) != NULL) { Var_Set(pathname, rp, cache, 0); } free(cp); return rp ? resolved : NULL; } int PrintAddr(void *a, void *b) { printf("%lx ", (unsigned long) a); return b ? 0 : 0; } static int addErrorCMD(void *cmdp, void *gnp MAKE_ATTR_UNUSED) { if (cmdp == NULL) return 1; /* stop */ Var_Append(".ERROR_CMD", cmdp, VAR_GLOBAL); return 0; } void PrintOnError(GNode *gn, const char *s) { static GNode *en = NULL; char tmp[64]; char *cp; if (s) printf("%s", s); printf("\n%s: stopped in %s\n", progname, curdir); if (en) return; /* we've been here! */ if (gn) { /* * We can print this even if there is no .ERROR target. */ Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL, 0); Var_Delete(".ERROR_CMD", VAR_GLOBAL); Lst_ForEach(gn->commands, addErrorCMD, gn); } strncpy(tmp, "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", sizeof(tmp) - 1); cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); if (cp) { if (*cp) printf("%s", cp); free(cp); } fflush(stdout); /* * Finally, see if there is a .ERROR target, and run it if so. */ en = Targ_FindNode(".ERROR", TARG_NOCREATE); if (en) { en->type |= OP_SPECIAL; Compat_Make(en, en); } } void Main_ExportMAKEFLAGS(Boolean first) { static int once = 1; char tmp[64]; char *s; if (once != first) return; once = 0; strncpy(tmp, "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}", sizeof(tmp)); s = Var_Subst(NULL, tmp, VAR_CMD, VARF_WANTRES); if (s && *s) { #ifdef POSIX setenv("MAKEFLAGS", s, 1); #else setenv("MAKE", s, 1); #endif } } char * getTmpdir(void) { static char *tmpdir = NULL; if (!tmpdir) { struct stat st; /* * Honor $TMPDIR but only if it is valid. * Ensure it ends with /. */ tmpdir = Var_Subst(NULL, "${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL, VARF_WANTRES); if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { free(tmpdir); tmpdir = bmake_strdup(_PATH_TMP); } } return tmpdir; } /* * Create and open a temp file using "pattern". * If "fnamep" is provided set it to a copy of the filename created. * Otherwise unlink the file once open. */ int mkTempFile(const char *pattern, char **fnamep) { static char *tmpdir = NULL; char tfile[MAXPATHLEN]; int fd; if (!pattern) pattern = TMPPAT; if (!tmpdir) tmpdir = getTmpdir(); if (pattern[0] == '/') { snprintf(tfile, sizeof(tfile), "%s", pattern); } else { snprintf(tfile, sizeof(tfile), "%s%s", tmpdir, pattern); } if ((fd = mkstemp(tfile)) < 0) Punt("Could not create temporary file %s: %s", tfile, strerror(errno)); if (fnamep) { *fnamep = bmake_strdup(tfile); } else { unlink(tfile); /* we just want the descriptor */ } return fd; } /* * Convert a string representation of a boolean. * Anything that looks like "No", "False", "Off", "0" etc, * is FALSE, otherwise TRUE. */ Boolean s2Boolean(const char *s, Boolean bf) { if (s) { switch(*s) { case '\0': /* not set - the default wins */ break; case '0': case 'F': case 'f': case 'N': case 'n': bf = FALSE; break; case 'O': case 'o': switch (s[1]) { case 'F': case 'f': bf = FALSE; break; default: bf = TRUE; break; } break; default: bf = TRUE; break; } } return (bf); } /* * Return a Boolean based on setting of a knob. * * If the knob is not set, the supplied default is the return value. * If set, anything that looks or smells like "No", "False", "Off", "0" etc, * is FALSE, otherwise TRUE. */ Boolean getBoolean(const char *name, Boolean bf) { char tmp[64]; char *cp; if (snprintf(tmp, sizeof(tmp), "${%s:U:tl}", name) < (int)(sizeof(tmp))) { cp = Var_Subst(NULL, tmp, VAR_GLOBAL, VARF_WANTRES); if (cp) { bf = s2Boolean(cp, bf); free(cp); } } return (bf); } Index: stable/11/contrib/bmake/make.h =================================================================== --- stable/11/contrib/bmake/make.h (revision 359753) +++ stable/11/contrib/bmake/make.h (revision 359754) @@ -1,553 +1,553 @@ -/* $NetBSD: make.h,v 1.104 2018/02/12 21:38:09 sjg Exp $ */ +/* $NetBSD: make.h,v 1.105 2020/03/30 02:41:06 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)make.h 8.3 (Berkeley) 6/13/95 */ /* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * 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. * * from: @(#)make.h 8.3 (Berkeley) 6/13/95 */ /*- * make.h -- * The global definitions for pmake */ #ifndef _MAKE_H_ #define _MAKE_H_ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #ifdef HAVE_STRING_H #include #else #include #endif #include #include #ifndef FD_CLOEXEC #define FD_CLOEXEC 1 #endif #if defined(__GNUC__) #define MAKE_GNUC_PREREQ(x, y) \ ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \ (__GNUC__ > (x))) #else /* defined(__GNUC__) */ #define MAKE_GNUC_PREREQ(x, y) 0 #endif /* defined(__GNUC__) */ #if MAKE_GNUC_PREREQ(2, 7) #define MAKE_ATTR_UNUSED __attribute__((__unused__)) #else #define MAKE_ATTR_UNUSED /* delete */ #endif #if MAKE_GNUC_PREREQ(2, 5) #define MAKE_ATTR_DEAD __attribute__((__noreturn__)) #elif defined(__GNUC__) #define MAKE_ATTR_DEAD __volatile #else #define MAKE_ATTR_DEAD /* delete */ #endif #if MAKE_GNUC_PREREQ(2, 7) #define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) \ __attribute__((__format__ (__printf__, fmtarg, firstvararg))) #else #define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */ #endif #include "sprite.h" #include "lst.h" #include "hash.h" #include "make-conf.h" #include "buf.h" #include "make_malloc.h" /* * some vendors don't have this --sjg */ #if defined(S_IFDIR) && !defined(S_ISDIR) # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif #if defined(sun) && (defined(__svr4__) || defined(__SVR4)) #define POSIX_SIGNALS #endif /*- * The structure for an individual graph node. Each node has several * pieces of data associated with it. * 1) the name of the target it describes * 2) the location of the target file in the file system. * 3) the type of operator used to define its sources (qv. parse.c) * 4) whether it is involved in this invocation of make * 5) whether the target has been remade * 6) whether any of its children has been remade * 7) the number of its children that are, as yet, unmade * 8) its modification time * 9) the modification time of its youngest child (qv. make.c) * 10) a list of nodes for which this is a source (parents) * 11) a list of nodes on which this depends (children) * 12) a list of nodes that depend on this, as gleaned from the * transformation rules (iParents) * 13) a list of ancestor nodes, which includes parents, iParents, * and recursive parents of parents * 14) a list of nodes of the same name created by the :: operator * 15) a list of nodes that must be made (if they're made) before * this node can be, but that do not enter into the datedness of * this node. * 16) a list of nodes that must be made (if they're made) before * this node or any child of this node can be, but that do not * enter into the datedness of this node. * 17) a list of nodes that must be made (if they're made) after * this node is, but that do not depend on this node, in the * normal sense. * 18) a Lst of ``local'' variables that are specific to this target * and this target only (qv. var.c [$@ $< $?, etc.]) * 19) a Lst of strings that are commands to be given to a shell * to create this target. */ typedef struct GNode { char *name; /* The target's name */ char *uname; /* The unexpanded name of a .USE node */ char *path; /* The full pathname of the file */ int type; /* Its type (see the OP flags, below) */ int flags; #define REMAKE 0x1 /* this target needs to be (re)made */ #define CHILDMADE 0x2 /* children of this target were made */ #define FORCE 0x4 /* children don't exist, and we pretend made */ #define DONE_WAIT 0x8 /* Set by Make_ProcessWait() */ #define DONE_ORDER 0x10 /* Build requested by .ORDER processing */ #define FROM_DEPEND 0x20 /* Node created from .depend */ #define DONE_ALLSRC 0x40 /* We do it once only */ #define CYCLE 0x1000 /* Used by MakePrintStatus */ #define DONECYCLE 0x2000 /* Used by MakePrintStatus */ #define INTERNAL 0x4000 /* Internal use only */ enum enum_made { UNMADE, DEFERRED, REQUESTED, BEINGMADE, MADE, UPTODATE, ERROR, ABORTED } made; /* Set to reflect the state of processing * on this node: * UNMADE - Not examined yet * DEFERRED - Examined once (building child) * REQUESTED - on toBeMade list * BEINGMADE - Target is already being made. * Indicates a cycle in the graph. * MADE - Was out-of-date and has been made * UPTODATE - Was already up-to-date * ERROR - An error occurred while it was being * made (used only in compat mode) * ABORTED - The target was aborted due to * an error making an inferior (compat). */ int unmade; /* The number of unmade children */ time_t mtime; /* Its modification time */ struct GNode *cmgn; /* The youngest child */ Lst iParents; /* Links to parents for which this is an * implied source, if any */ Lst cohorts; /* Other nodes for the :: operator */ Lst parents; /* Nodes that depend on this one */ Lst children; /* Nodes on which this one depends */ Lst order_pred; /* .ORDER nodes we need made */ Lst order_succ; /* .ORDER nodes who need us */ char cohort_num[8]; /* #n for this cohort */ int unmade_cohorts;/* # of unmade instances on the cohorts list */ struct GNode *centurion; /* Pointer to the first instance of a :: node; only set when on a cohorts list */ unsigned int checked; /* Last time we tried to makle this node */ Hash_Table context; /* The local variables */ Lst commands; /* Creation commands */ struct _Suff *suffix; /* Suffix for the node (determined by * Suff_FindDeps and opaque to everyone * but the Suff module) */ const char *fname; /* filename where the GNode got defined */ int lineno; /* line number where the GNode got defined */ } GNode; /* * The OP_ constants are used when parsing a dependency line as a way of * communicating to other parts of the program the way in which a target * should be made. These constants are bitwise-OR'ed together and * placed in the 'type' field of each node. Any node that has * a 'type' field which satisfies the OP_NOP function was never never on * the lefthand side of an operator, though it may have been on the * righthand side... */ #define OP_DEPENDS 0x00000001 /* Execution of commands depends on * kids (:) */ #define OP_FORCE 0x00000002 /* Always execute commands (!) */ #define OP_DOUBLEDEP 0x00000004 /* Execution of commands depends on kids * per line (::) */ #define OP_OPMASK (OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP) #define OP_OPTIONAL 0x00000008 /* Don't care if the target doesn't * exist and can't be created */ #define OP_USE 0x00000010 /* Use associated commands for parents */ #define OP_EXEC 0x00000020 /* Target is never out of date, but always * execute commands anyway. Its time * doesn't matter, so it has none...sort * of */ #define OP_IGNORE 0x00000040 /* Ignore errors when creating the node */ #define OP_PRECIOUS 0x00000080 /* Don't remove the target when * interrupted */ #define OP_SILENT 0x00000100 /* Don't echo commands when executed */ #define OP_MAKE 0x00000200 /* Target is a recursive make so its * commands should always be executed when * it is out of date, regardless of the * state of the -n or -t flags */ #define OP_JOIN 0x00000400 /* Target is out-of-date only if any of its * children was out-of-date */ #define OP_MADE 0x00000800 /* Assume the children of the node have * been already made */ #define OP_SPECIAL 0x00001000 /* Special .BEGIN, .END, .INTERRUPT */ #define OP_USEBEFORE 0x00002000 /* Like .USE, only prepend commands */ #define OP_INVISIBLE 0x00004000 /* The node is invisible to its parents. * I.e. it doesn't show up in the parents's * local variables. */ #define OP_NOTMAIN 0x00008000 /* The node is exempt from normal 'main * target' processing in parse.c */ #define OP_PHONY 0x00010000 /* Not a file target; run always */ #define OP_NOPATH 0x00020000 /* Don't search for file in the path */ #define OP_WAIT 0x00040000 /* .WAIT phony node */ #define OP_NOMETA 0x00080000 /* .NOMETA do not create a .meta file */ #define OP_META 0x00100000 /* .META we _do_ want a .meta file */ #define OP_NOMETA_CMP 0x00200000 /* Do not compare commands in .meta file */ #define OP_SUBMAKE 0x00400000 /* Possibly a submake node */ /* Attributes applied by PMake */ #define OP_TRANSFORM 0x80000000 /* The node is a transformation rule */ #define OP_MEMBER 0x40000000 /* Target is a member of an archive */ #define OP_LIB 0x20000000 /* Target is a library */ #define OP_ARCHV 0x10000000 /* Target is an archive construct */ #define OP_HAS_COMMANDS 0x08000000 /* Target has all the commands it should. * Used when parsing to catch multiple * commands for a target */ #define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */ #define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */ #define OP_MARK 0x01000000 /* Node found while expanding .ALLSRC */ #define NoExecute(gn) ((gn->type & OP_MAKE) ? noRecursiveExecute : noExecute) /* * OP_NOP will return TRUE if the node with the given type was not the * object of a dependency operator */ #define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000) #define OP_NOTARGET (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM) /* * The TARG_ constants are used when calling the Targ_FindNode and * Targ_FindList functions in targ.c. They simply tell the functions what to * do if the desired node(s) is (are) not found. If the TARG_CREATE constant * is given, a new, empty node will be created for the target, placed in the * table of all targets and its address returned. If TARG_NOCREATE is given, * a NULL pointer will be returned. */ #define TARG_NOCREATE 0x00 /* don't create it */ #define TARG_CREATE 0x01 /* create node if not found */ #define TARG_NOHASH 0x02 /* don't look in/add to hash table */ /* * These constants are all used by the Str_Concat function to decide how the * final string should look. If STR_ADDSPACE is given, a space will be * placed between the two strings. If STR_ADDSLASH is given, a '/' will * be used instead of a space. If neither is given, no intervening characters * will be placed between the two strings in the final output. If the * STR_DOFREE bit is set, the two input strings will be freed before * Str_Concat returns. */ #define STR_ADDSPACE 0x01 /* add a space when Str_Concat'ing */ #define STR_ADDSLASH 0x02 /* add a slash when Str_Concat'ing */ /* * Error levels for parsing. PARSE_FATAL means the process cannot continue * once the makefile has been parsed. PARSE_WARNING means it can. Passed * as the first argument to Parse_Error. */ #define PARSE_INFO 3 #define PARSE_WARNING 2 #define PARSE_FATAL 1 /* * Values returned by Cond_Eval. */ #define COND_PARSE 0 /* Parse the next lines */ #define COND_SKIP 1 /* Skip the next lines */ #define COND_INVALID 2 /* Not a conditional statement */ /* * Definitions for the "local" variables. Used only for clarity. */ #define TARGET "@" /* Target of dependency */ #define OODATE "?" /* All out-of-date sources */ #define ALLSRC ">" /* All sources */ #define IMPSRC "<" /* Source implied by transformation */ #define PREFIX "*" /* Common prefix */ #define ARCHIVE "!" /* Archive in "archive(member)" syntax */ #define MEMBER "%" /* Member in "archive(member)" syntax */ #define FTARGET "@F" /* file part of TARGET */ #define DTARGET "@D" /* directory part of TARGET */ #define FIMPSRC " b) ? a : b) #endif /* At least GNU/Hurd systems lack hardcoded MAXPATHLEN/PATH_MAX */ #ifdef HAVE_LIMITS_H #include #endif #ifndef MAXPATHLEN #define MAXPATHLEN BMAKE_PATH_MAX #endif #ifndef PATH_MAX #define PATH_MAX MAXPATHLEN #endif #if defined(SYSV) #define KILLPG(pid, sig) kill(-(pid), (sig)) #else #define KILLPG(pid, sig) killpg((pid), (sig)) #endif #endif /* _MAKE_H_ */ Index: stable/11/contrib/ipfilter/ipf.h =================================================================== --- stable/11/contrib/ipfilter/ipf.h (revision 359753) +++ stable/11/contrib/ipfilter/ipf.h (revision 359754) @@ -1,385 +1,385 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * @(#)ipf.h 1.12 6/5/96 * $Id$ */ #ifndef __IPF_H__ #define __IPF_H__ #include #include #include /* * This is a workaround for troubles on FreeBSD, HPUX, OpenBSD. * Needed here because on some systems gets included by things * like */ #ifndef _KERNEL # define ADD_KERNEL # define _KERNEL # define KERNEL #endif #include #ifdef ADD_KERNEL # undef _KERNEL # undef KERNEL #endif #include #include #include #include #include #include #include # include #include #include #include #include #include #include #include #include #if !defined(__SVR4) && !defined(__svr4__) && defined(sun) # include #endif #include #include #include "netinet/ip_compat.h" #include "netinet/ip_fil.h" #include "netinet/ip_nat.h" #include "netinet/ip_frag.h" #include "netinet/ip_state.h" #include "netinet/ip_proxy.h" #include "netinet/ip_auth.h" #include "netinet/ip_lookup.h" #include "netinet/ip_pool.h" #include "netinet/ip_scan.h" #include "netinet/ip_htable.h" #include "netinet/ip_sync.h" #include "netinet/ip_dstlist.h" #include "opts.h" #ifndef __P # ifdef __STDC__ # define __P(x) x # else # define __P(x) () # endif #endif #ifndef __STDC__ # undef const # define const #endif #ifndef U_32_T # define U_32_T 1 # if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || \ defined(__sgi) typedef u_int32_t u_32_t; # else # if defined(__alpha__) || defined(__alpha) || defined(_LP64) typedef unsigned int u_32_t; # else # if SOLARIS2 >= 6 typedef uint32_t u_32_t; # else typedef unsigned int u_32_t; # endif # endif # endif /* __NetBSD__ || __OpenBSD__ || __FreeBSD__ || __sgi */ #endif /* U_32_T */ #ifndef MAXHOSTNAMELEN # define MAXHOSTNAMELEN 256 #endif #define MAX_ICMPCODE 16 #define MAX_ICMPTYPE 19 #define PRINTF (void)printf #define FPRINTF (void)fprintf struct ipopt_names { int on_value; int on_bit; int on_siz; char *on_name; }; typedef struct alist_s { struct alist_s *al_next; int al_not; int al_family; i6addr_t al_i6addr; i6addr_t al_i6mask; } alist_t; #define al_addr al_i6addr.in4_addr #define al_mask al_i6mask.in4_addr #define al_1 al_addr #define al_2 al_mask typedef struct plist_s { struct plist_s *pl_next; int pl_compare; u_short pl_port1; u_short pl_port2; } plist_t; typedef struct { u_short fb_c; u_char fb_t; u_char fb_f; u_32_t fb_k; } fakebpf_t; typedef struct { char *it_name; int it_v4; int it_v6; } icmptype_t; typedef struct wordtab { char *w_word; int w_value; } wordtab_t; typedef struct namelist { struct namelist *na_next; char *na_name; int na_value; } namelist_t; typedef struct proxyrule { struct proxyrule *pr_next; char *pr_proxy; char *pr_conf; namelist_t *pr_names; int pr_proto; } proxyrule_t; #if defined(__NetBSD__) || defined(__FreeBSD_version) || \ SOLARIS # include typedef int (* ioctlfunc_t) __P((int, ioctlcmd_t, ...)); #else typedef int (* ioctlfunc_t) __P((dev_t, ioctlcmd_t, void *)); #endif typedef int (* addfunc_t) __P((int, ioctlfunc_t, void *)); typedef int (* copyfunc_t) __P((void *, void *, size_t)); -extern char thishost[]; +extern char thishost[MAXHOSTNAMELEN]; extern char flagset[]; extern u_char flags[]; extern struct ipopt_names ionames[]; extern struct ipopt_names secclass[]; extern char *icmpcodes[MAX_ICMPCODE + 1]; extern char *icmptypes[MAX_ICMPTYPE + 1]; extern int use_inet6; extern int lineNum; extern int debuglevel; extern struct ipopt_names v6ionames[]; extern icmptype_t icmptypelist[]; extern wordtab_t statefields[]; extern wordtab_t natfields[]; extern wordtab_t poolfields[]; extern int addicmp __P((char ***, struct frentry *, int)); extern int addipopt __P((char *, struct ipopt_names *, int, char *)); extern int addkeep __P((char ***, struct frentry *, int)); extern alist_t *alist_new __P((int, char *)); extern void alist_free __P((alist_t *)); extern void assigndefined __P((char *)); extern void binprint __P((void *, size_t)); extern u_32_t buildopts __P((char *, char *, int)); extern int checkrev __P((char *)); extern int connecttcp __P((char *, int)); extern int count6bits __P((u_32_t *)); extern int count4bits __P((u_32_t)); extern char *fac_toname __P((int)); extern int fac_findname __P((char *)); extern const char *familyname __P((const int)); extern void fill6bits __P((int, u_int *)); extern wordtab_t *findword __P((wordtab_t *, char *)); extern int ftov __P((int)); extern char *ipf_geterror __P((int, ioctlfunc_t *)); extern int genmask __P((int, char *, i6addr_t *)); extern int gethost __P((int, char *, i6addr_t *)); extern int geticmptype __P((int, char *)); extern int getport __P((struct frentry *, char *, u_short *, char *)); extern int getportproto __P((char *, int)); extern int getproto __P((char *)); extern char *getnattype __P((struct nat *)); extern char *getsumd __P((u_32_t)); extern u_32_t getoptbyname __P((char *)); extern u_32_t getoptbyvalue __P((int)); extern u_32_t getv6optbyname __P((char *)); extern u_32_t getv6optbyvalue __P((int)); extern char *icmptypename __P((int, int)); extern void initparse __P((void)); extern void ipf_dotuning __P((int, char *, ioctlfunc_t)); extern int ipf_addrule __P((int, ioctlfunc_t, void *)); extern void ipf_mutex_clean __P((void)); extern int ipf_parsefile __P((int, addfunc_t, ioctlfunc_t *, char *)); extern int ipf_parsesome __P((int, addfunc_t, ioctlfunc_t *, FILE *)); extern void ipf_perror __P((int, char *)); extern int ipf_perror_fd __P(( int, ioctlfunc_t, char *)); extern void ipf_rwlock_clean __P((void)); extern char *ipf_strerror __P((int)); extern void ipferror __P((int, char *)); extern int ipmon_parsefile __P((char *)); extern int ipmon_parsesome __P((FILE *)); extern int ipnat_addrule __P((int, ioctlfunc_t, void *)); extern int ipnat_parsefile __P((int, addfunc_t, ioctlfunc_t, char *)); extern int ipnat_parsesome __P((int, addfunc_t, ioctlfunc_t, FILE *)); extern int ippool_parsefile __P((int, char *, ioctlfunc_t)); extern int ippool_parsesome __P((int, FILE *, ioctlfunc_t)); extern int kmemcpywrap __P((void *, void *, size_t)); extern char *kvatoname __P((ipfunc_t, ioctlfunc_t)); extern int load_dstlist __P((struct ippool_dst *, ioctlfunc_t, ipf_dstnode_t *)); extern int load_dstlistnode __P((int, char *, struct ipf_dstnode *, ioctlfunc_t)); extern alist_t *load_file __P((char *)); extern int load_hash __P((struct iphtable_s *, struct iphtent_s *, ioctlfunc_t)); extern int load_hashnode __P((int, char *, struct iphtent_s *, int, ioctlfunc_t)); extern alist_t *load_http __P((char *)); extern int load_pool __P((struct ip_pool_s *list, ioctlfunc_t)); extern int load_poolnode __P((int, char *, ip_pool_node_t *, int, ioctlfunc_t)); extern alist_t *load_url __P((char *)); extern alist_t *make_range __P((int, struct in_addr, struct in_addr)); extern void mb_hexdump __P((mb_t *, FILE *)); extern ipfunc_t nametokva __P((char *, ioctlfunc_t)); extern void nat_setgroupmap __P((struct ipnat *)); extern int ntomask __P((int, int, u_32_t *)); extern u_32_t optname __P((char ***, u_short *, int)); extern wordtab_t *parsefields __P((wordtab_t *, char *)); extern int *parseipfexpr __P((char *, char **)); extern int parsewhoisline __P((char *, addrfamily_t *, addrfamily_t *)); extern void pool_close __P((void)); extern int pool_fd __P((void)); extern int pool_ioctl __P((ioctlfunc_t, ioctlcmd_t, void *)); extern int pool_open __P((void)); extern char *portname __P((int, int)); extern int pri_findname __P((char *)); extern char *pri_toname __P((int)); extern void print_toif __P((int, char *, char *, struct frdest *)); extern void printaps __P((ap_session_t *, int, int)); extern void printaddr __P((int, int, char *, int, u_32_t *, u_32_t *)); extern void printbuf __P((char *, int, int)); extern void printfieldhdr __P((wordtab_t *, wordtab_t *)); extern void printfr __P((struct frentry *, ioctlfunc_t)); extern struct iphtable_s *printhash __P((struct iphtable_s *, copyfunc_t, char *, int, wordtab_t *)); extern struct iphtable_s *printhash_live __P((iphtable_t *, int, char *, int, wordtab_t *)); extern ippool_dst_t *printdstl_live __P((ippool_dst_t *, int, char *, int, wordtab_t *)); extern void printhashdata __P((iphtable_t *, int)); extern struct iphtent_s *printhashnode __P((struct iphtable_s *, struct iphtent_s *, copyfunc_t, int, wordtab_t *)); extern void printhost __P((int, u_32_t *)); extern void printhostmask __P((int, u_32_t *, u_32_t *)); extern void printip __P((int, u_32_t *)); extern void printlog __P((struct frentry *)); extern void printlookup __P((char *, i6addr_t *addr, i6addr_t *mask)); extern void printmask __P((int, u_32_t *)); extern void printnataddr __P((int, char *, nat_addr_t *, int)); extern void printnatfield __P((nat_t *, int)); extern void printnatside __P((char *, nat_stat_side_t *)); extern void printpacket __P((int, mb_t *)); extern void printpacket6 __P((int, mb_t *)); extern struct ippool_dst *printdstlist __P((struct ippool_dst *, copyfunc_t, char *, int, ipf_dstnode_t *, wordtab_t *)); extern void printdstlistdata __P((ippool_dst_t *, int)); extern ipf_dstnode_t *printdstlistnode __P((ipf_dstnode_t *, copyfunc_t, int, wordtab_t *)); extern void printdstlistpolicy __P((ippool_policy_t)); extern struct ip_pool_s *printpool __P((struct ip_pool_s *, copyfunc_t, char *, int, wordtab_t *)); extern struct ip_pool_s *printpool_live __P((struct ip_pool_s *, int, char *, int, wordtab_t *)); extern void printpooldata __P((ip_pool_t *, int)); extern void printpoolfield __P((void *, int, int)); extern struct ip_pool_node *printpoolnode __P((struct ip_pool_node *, int, wordtab_t *)); extern void printproto __P((struct protoent *, int, struct ipnat *)); extern void printportcmp __P((int, struct frpcmp *)); extern void printstatefield __P((ipstate_t *, int)); extern void printtqtable __P((ipftq_t *)); extern void printtunable __P((ipftune_t *)); extern void printunit __P((int)); extern void optprint __P((u_short *, u_long, u_long)); #ifdef USE_INET6 extern void optprintv6 __P((u_short *, u_long, u_long)); #endif extern int remove_hash __P((struct iphtable_s *, ioctlfunc_t)); extern int remove_hashnode __P((int, char *, struct iphtent_s *, ioctlfunc_t)); extern int remove_pool __P((ip_pool_t *, ioctlfunc_t)); extern int remove_poolnode __P((int, char *, ip_pool_node_t *, ioctlfunc_t)); extern u_char tcpflags __P((char *)); extern void printc __P((struct frentry *)); extern void printC __P((int)); extern void emit __P((int, int, void *, struct frentry *)); extern u_char secbit __P((int)); extern u_char seclevel __P((char *)); extern void printfraginfo __P((char *, struct ipfr *)); extern void printifname __P((char *, char *, void *)); extern char *hostname __P((int, void *)); extern struct ipstate *printstate __P((struct ipstate *, int, u_long)); extern void printsbuf __P((char *)); extern void printnat __P((struct ipnat *, int)); extern void printactiveaddress __P((int, char *, i6addr_t *, char *)); extern void printactivenat __P((struct nat *, int, u_long)); extern void printhostmap __P((struct hostmap *, u_int)); extern void printtcpflags __P((u_32_t, u_32_t)); extern void printipfexpr __P((int *)); extern void printstatefield __P((ipstate_t *, int)); extern void printstatefieldhdr __P((int)); extern int sendtrap_v1_0 __P((int, char *, char *, int, time_t)); extern int sendtrap_v2_0 __P((int, char *, char *, int)); extern int vtof __P((int)); extern void set_variable __P((char *, char *)); extern char *get_variable __P((char *, char **, int)); extern void resetlexer __P((void)); extern void debug __P((int, char *, ...)); extern void verbose __P((int, char *, ...)); extern void ipfkdebug __P((char *, ...)); extern void ipfkverbose __P((char *, ...)); #if SOLARIS extern int gethostname __P((char *, int )); extern void sync __P((void)); #endif #endif /* __IPF_H__ */ Index: stable/11/contrib/ipfilter/tools/ipnat.c =================================================================== --- stable/11/contrib/ipfilter/tools/ipnat.c (revision 359753) +++ stable/11/contrib/ipfilter/tools/ipnat.c (revision 359754) @@ -1,843 +1,842 @@ /* $FreeBSD$ */ /* * Copyright (C) 2012 by Darren Reed. * * See the IPFILTER.LICENCE file for details on licencing. * * Added redirect stuff and a variety of bug fixes. (mcn@EnGarde.com) */ #include #include #include #include #include #if !defined(__SVR4) #include #else #include #endif #include #include #include #include #include #include #define _KERNEL #include #undef _KERNEL #include #include #if defined(sun) && defined(__SVR4) # include # include #endif #include #include #include #include #include #include #include #include #include #include # include #include "ipf.h" #include "netinet/ipl.h" #include "kmem.h" # define STRERROR(x) strerror(x) #if !defined(lint) static const char sccsid[] ="@(#)ipnat.c 1.9 6/5/96 (C) 1993 Darren Reed"; static const char rcsid[] = "@(#)$Id$"; #endif #if SOLARIS #define bzero(a,b) memset(a,0,b) #endif int use_inet6 = 0; -char thishost[MAXHOSTNAMELEN]; extern char *optarg; void dostats __P((int, natstat_t *, int, int, int *)); void dotable __P((natstat_t *, int, int, int, char *)); void flushtable __P((int, int, int *)); void usage __P((char *)); int main __P((int, char*[])); void showhostmap __P((natstat_t *nsp)); void natstat_dead __P((natstat_t *, char *)); void dostats_live __P((int, natstat_t *, int, int *)); void showhostmap_dead __P((natstat_t *)); void showhostmap_live __P((int, natstat_t *)); void dostats_dead __P((natstat_t *, int, int *)); int nat_matcharray __P((nat_t *, int *)); int opts; int nohdrfields = 0; wordtab_t *nat_fields = NULL; void usage(name) char *name; { fprintf(stderr, "Usage: %s [-CFhlnrRsv] [-f filename]\n", name); exit(1); } int main(argc, argv) int argc; char *argv[]; { int fd, c, mode, *natfilter; char *file, *core, *kernel; natstat_t ns, *nsp; ipfobj_t obj; fd = -1; opts = 0; nsp = &ns; file = NULL; core = NULL; kernel = NULL; mode = O_RDWR; natfilter = NULL; assigndefined(getenv("IPNAT_PREDEFINED")); while ((c = getopt(argc, argv, "CdFf:hlm:M:N:nO:prRsv")) != -1) switch (c) { case 'C' : opts |= OPT_CLEAR; break; case 'd' : opts |= OPT_DEBUG; break; case 'f' : file = optarg; break; case 'F' : opts |= OPT_FLUSH; break; case 'h' : opts |=OPT_HITS; break; case 'l' : opts |= OPT_LIST; mode = O_RDONLY; break; case 'm' : natfilter = parseipfexpr(optarg, NULL); break; case 'M' : core = optarg; break; case 'N' : kernel = optarg; break; case 'n' : opts |= OPT_DONOTHING|OPT_DONTOPEN; mode = O_RDONLY; break; case 'O' : nat_fields = parsefields(natfields, optarg); break; case 'p' : opts |= OPT_PURGE; break; case 'R' : opts |= OPT_NORESOLVE; break; case 'r' : opts |= OPT_REMOVE; break; case 's' : opts |= OPT_STAT; mode = O_RDONLY; break; case 'v' : opts |= OPT_VERBOSE; break; default : usage(argv[0]); } if (((opts & OPT_PURGE) != 0) && ((opts & OPT_REMOVE) == 0)) { (void) fprintf(stderr, "%s: -p must be used with -r\n", argv[0]); exit(1); } initparse(); if ((kernel != NULL) || (core != NULL)) { (void) setgid(getgid()); (void) setuid(getuid()); } if (!(opts & OPT_DONOTHING)) { if (((fd = open(IPNAT_NAME, mode)) == -1) && ((fd = open(IPNAT_NAME, O_RDONLY)) == -1)) { (void) fprintf(stderr, "%s: open: %s\n", IPNAT_NAME, STRERROR(errno)); exit(1); } } bzero((char *)&ns, sizeof(ns)); if ((opts & OPT_DONOTHING) == 0) { if (checkrev(IPL_NAME) == -1) { fprintf(stderr, "User/kernel version check failed\n"); exit(1); } } if (!(opts & OPT_DONOTHING) && (kernel == NULL) && (core == NULL)) { bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_NATSTAT; obj.ipfo_size = sizeof(*nsp); obj.ipfo_ptr = (void *)nsp; if (ioctl(fd, SIOCGNATS, &obj) == -1) { ipferror(fd, "ioctl(SIOCGNATS)"); exit(1); } (void) setgid(getgid()); (void) setuid(getuid()); } else if ((kernel != NULL) || (core != NULL)) { if (openkmem(kernel, core) == -1) exit(1); natstat_dead(nsp, kernel); if (opts & (OPT_LIST|OPT_STAT)) dostats(fd, nsp, opts, 0, natfilter); exit(0); } if (opts & (OPT_FLUSH|OPT_CLEAR)) flushtable(fd, opts, natfilter); if (file) { return ipnat_parsefile(fd, ipnat_addrule, ioctl, file); } if (opts & (OPT_LIST|OPT_STAT)) dostats(fd, nsp, opts, 1, natfilter); return 0; } /* * Read NAT statistic information in using a symbol table and memory file * rather than doing ioctl's. */ void natstat_dead(nsp, kernel) natstat_t *nsp; char *kernel; { struct nlist nat_nlist[10] = { { "nat_table" }, /* 0 */ { "nat_list" }, { "maptable" }, { "ipf_nattable_sz" }, { "ipf_natrules_sz" }, { "ipf_rdrrules_sz" }, /* 5 */ { "ipf_hostmap_sz" }, { "nat_instances" }, { NULL } }; void *tables[2]; if (nlist(kernel, nat_nlist) == -1) { fprintf(stderr, "nlist error\n"); return; } /* * Normally the ioctl copies all of these values into the structure * for us, before returning it to userland, so here we must copy each * one in individually. */ kmemcpy((char *)&tables, nat_nlist[0].n_value, sizeof(tables)); nsp->ns_side[0].ns_table = tables[0]; nsp->ns_side[1].ns_table = tables[1]; kmemcpy((char *)&nsp->ns_list, nat_nlist[1].n_value, sizeof(nsp->ns_list)); kmemcpy((char *)&nsp->ns_maptable, nat_nlist[2].n_value, sizeof(nsp->ns_maptable)); kmemcpy((char *)&nsp->ns_nattab_sz, nat_nlist[3].n_value, sizeof(nsp->ns_nattab_sz)); kmemcpy((char *)&nsp->ns_rultab_sz, nat_nlist[4].n_value, sizeof(nsp->ns_rultab_sz)); kmemcpy((char *)&nsp->ns_rdrtab_sz, nat_nlist[5].n_value, sizeof(nsp->ns_rdrtab_sz)); kmemcpy((char *)&nsp->ns_hostmap_sz, nat_nlist[6].n_value, sizeof(nsp->ns_hostmap_sz)); kmemcpy((char *)&nsp->ns_instances, nat_nlist[7].n_value, sizeof(nsp->ns_instances)); } /* * Issue an ioctl to flush either the NAT rules table or the active mapping * table or both. */ void flushtable(fd, opts, match) int fd, opts, *match; { int n = 0; if (opts & OPT_FLUSH) { n = 0; if (!(opts & OPT_DONOTHING)) { if (match != NULL) { ipfobj_t obj; obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_size = match[0] * sizeof(int); obj.ipfo_type = IPFOBJ_IPFEXPR; obj.ipfo_ptr = match; if (ioctl(fd, SIOCMATCHFLUSH, &obj) == -1) { ipferror(fd, "ioctl(SIOCMATCHFLUSH)"); n = -1; } else { n = obj.ipfo_retval; } } else if (ioctl(fd, SIOCIPFFL, &n) == -1) { ipferror(fd, "ioctl(SIOCIPFFL)"); n = -1; } } if (n >= 0) printf("%d entries flushed from NAT table\n", n); } if (opts & OPT_CLEAR) { n = 1; if (!(opts & OPT_DONOTHING) && ioctl(fd, SIOCIPFFL, &n) == -1) ipferror(fd, "ioctl(SIOCCNATL)"); else printf("%d entries flushed from NAT list\n", n); } } /* * Display NAT statistics. */ void dostats_dead(nsp, opts, filter) natstat_t *nsp; int opts, *filter; { nat_t *np, nat; ipnat_t ipn; int i; if (nat_fields == NULL) { printf("List of active MAP/Redirect filters:\n"); while (nsp->ns_list) { if (kmemcpy((char *)&ipn, (long)nsp->ns_list, sizeof(ipn))) { perror("kmemcpy"); break; } if (opts & OPT_HITS) printf("%lu ", ipn.in_hits); printnat(&ipn, opts & (OPT_DEBUG|OPT_VERBOSE)); nsp->ns_list = ipn.in_next; } } if (nat_fields == NULL) { printf("\nList of active sessions:\n"); } else if (nohdrfields == 0) { for (i = 0; nat_fields[i].w_value != 0; i++) { printfieldhdr(natfields, nat_fields + i); if (nat_fields[i + 1].w_value != 0) printf("\t"); } printf("\n"); } for (np = nsp->ns_instances; np; np = nat.nat_next) { if (kmemcpy((char *)&nat, (long)np, sizeof(nat))) break; if ((filter != NULL) && (nat_matcharray(&nat, filter) == 0)) continue; if (nat_fields != NULL) { for (i = 0; nat_fields[i].w_value != 0; i++) { printnatfield(&nat, nat_fields[i].w_value); if (nat_fields[i + 1].w_value != 0) printf("\t"); } printf("\n"); } else { printactivenat(&nat, opts, nsp->ns_ticks); if (nat.nat_aps) { int proto; if (nat.nat_dir & NAT_OUTBOUND) proto = nat.nat_pr[1]; else proto = nat.nat_pr[0]; printaps(nat.nat_aps, opts, proto); } } } if (opts & OPT_VERBOSE) showhostmap_dead(nsp); } void dotable(nsp, fd, alive, which, side) natstat_t *nsp; int fd, alive, which; char *side; { int sz, i, used, maxlen, minlen, totallen; ipftable_t table; u_int *buckets; ipfobj_t obj; sz = sizeof(*buckets) * nsp->ns_nattab_sz; buckets = (u_int *)malloc(sz); if (buckets == NULL) { fprintf(stderr, "cannot allocate memory (%d) for buckets\n", sz); return; } obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_GTABLE; obj.ipfo_size = sizeof(table); obj.ipfo_ptr = &table; if (which == 0) { table.ita_type = IPFTABLE_BUCKETS_NATIN; } else if (which == 1) { table.ita_type = IPFTABLE_BUCKETS_NATOUT; } table.ita_table = buckets; if (alive) { if (ioctl(fd, SIOCGTABL, &obj) != 0) { ipferror(fd, "SIOCFTABL"); free(buckets); return; } } else { if (kmemcpy((char *)buckets, (u_long)nsp->ns_nattab_sz, sz)) { free(buckets); return; } } minlen = nsp->ns_side[which].ns_inuse; totallen = 0; maxlen = 0; used = 0; for (i = 0; i < nsp->ns_nattab_sz; i++) { if (buckets[i] > maxlen) maxlen = buckets[i]; if (buckets[i] < minlen) minlen = buckets[i]; if (buckets[i] != 0) used++; totallen += buckets[i]; } printf("%d%%\thash efficiency %s\n", totallen ? used * 100 / totallen : 0, side); printf("%2.2f%%\tbucket usage %s\n", ((float)used / nsp->ns_nattab_sz) * 100.0, side); printf("%d\tminimal length %s\n", minlen, side); printf("%d\tmaximal length %s\n", maxlen, side); printf("%.3f\taverage length %s\n", used ? ((float)totallen / used) : 0.0, side); free(buckets); } void dostats(fd, nsp, opts, alive, filter) natstat_t *nsp; int fd, opts, alive, *filter; { /* * Show statistics ? */ if (opts & OPT_STAT) { printnatside("in", &nsp->ns_side[0]); dotable(nsp, fd, alive, 0, "in"); printnatside("out", &nsp->ns_side[1]); dotable(nsp, fd, alive, 1, "out"); printf("%lu\tlog successes\n", nsp->ns_side[0].ns_log); printf("%lu\tlog failures\n", nsp->ns_side[1].ns_log); printf("%lu\tadded in\n%lu\tadded out\n", nsp->ns_side[0].ns_added, nsp->ns_side[1].ns_added); printf("%u\tactive\n", nsp->ns_active); printf("%lu\ttransparent adds\n", nsp->ns_addtrpnt); printf("%lu\tdivert build\n", nsp->ns_divert_build); printf("%lu\texpired\n", nsp->ns_expire); printf("%lu\tflush all\n", nsp->ns_flush_all); printf("%lu\tflush closing\n", nsp->ns_flush_closing); printf("%lu\tflush queue\n", nsp->ns_flush_queue); printf("%lu\tflush state\n", nsp->ns_flush_state); printf("%lu\tflush timeout\n", nsp->ns_flush_timeout); printf("%lu\thostmap new\n", nsp->ns_hm_new); printf("%lu\thostmap fails\n", nsp->ns_hm_newfail); printf("%lu\thostmap add\n", nsp->ns_hm_addref); printf("%lu\thostmap NULL rule\n", nsp->ns_hm_nullnp); printf("%lu\tlog ok\n", nsp->ns_log_ok); printf("%lu\tlog fail\n", nsp->ns_log_fail); printf("%u\torphan count\n", nsp->ns_orphans); printf("%u\trule count\n", nsp->ns_rules); printf("%u\tmap rules\n", nsp->ns_rules_map); printf("%u\trdr rules\n", nsp->ns_rules_rdr); printf("%u\twilds\n", nsp->ns_wilds); if (opts & OPT_VERBOSE) printf("list %p\n", nsp->ns_list); } if (opts & OPT_LIST) { if (alive) dostats_live(fd, nsp, opts, filter); else dostats_dead(nsp, opts, filter); } } /* * Display NAT statistics. */ void dostats_live(fd, nsp, opts, filter) natstat_t *nsp; int fd, opts, *filter; { ipfgeniter_t iter; char buffer[2000]; ipfobj_t obj; ipnat_t *ipn; nat_t nat; int i; bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_GENITER; obj.ipfo_size = sizeof(iter); obj.ipfo_ptr = &iter; iter.igi_type = IPFGENITER_IPNAT; iter.igi_nitems = 1; iter.igi_data = buffer; ipn = (ipnat_t *)buffer; /* * Show list of NAT rules and NAT sessions ? */ if (nat_fields == NULL) { printf("List of active MAP/Redirect filters:\n"); while (nsp->ns_list) { if (ioctl(fd, SIOCGENITER, &obj) == -1) break; if (opts & OPT_HITS) printf("%lu ", ipn->in_hits); printnat(ipn, opts & (OPT_DEBUG|OPT_VERBOSE)); nsp->ns_list = ipn->in_next; } } if (nat_fields == NULL) { printf("\nList of active sessions:\n"); } else if (nohdrfields == 0) { for (i = 0; nat_fields[i].w_value != 0; i++) { printfieldhdr(natfields, nat_fields + i); if (nat_fields[i + 1].w_value != 0) printf("\t"); } printf("\n"); } i = IPFGENITER_IPNAT; (void) ioctl(fd,SIOCIPFDELTOK, &i); iter.igi_type = IPFGENITER_NAT; iter.igi_nitems = 1; iter.igi_data = &nat; while (nsp->ns_instances != NULL) { if (ioctl(fd, SIOCGENITER, &obj) == -1) break; if ((filter != NULL) && (nat_matcharray(&nat, filter) == 0)) continue; if (nat_fields != NULL) { for (i = 0; nat_fields[i].w_value != 0; i++) { printnatfield(&nat, nat_fields[i].w_value); if (nat_fields[i + 1].w_value != 0) printf("\t"); } printf("\n"); } else { printactivenat(&nat, opts, nsp->ns_ticks); if (nat.nat_aps) { int proto; if (nat.nat_dir & NAT_OUTBOUND) proto = nat.nat_pr[1]; else proto = nat.nat_pr[0]; printaps(nat.nat_aps, opts, proto); } } nsp->ns_instances = nat.nat_next; } if (opts & OPT_VERBOSE) showhostmap_live(fd, nsp); i = IPFGENITER_NAT; (void) ioctl(fd,SIOCIPFDELTOK, &i); } /* * Display the active host mapping table. */ void showhostmap_dead(nsp) natstat_t *nsp; { hostmap_t hm, *hmp, **maptable; u_int hv; printf("\nList of active host mappings:\n"); maptable = (hostmap_t **)malloc(sizeof(hostmap_t *) * nsp->ns_hostmap_sz); if (kmemcpy((char *)maptable, (u_long)nsp->ns_maptable, sizeof(hostmap_t *) * nsp->ns_hostmap_sz)) { perror("kmemcpy (maptable)"); return; } for (hv = 0; hv < nsp->ns_hostmap_sz; hv++) { hmp = maptable[hv]; while (hmp) { if (kmemcpy((char *)&hm, (u_long)hmp, sizeof(hm))) { perror("kmemcpy (hostmap)"); return; } printhostmap(&hm, hv); hmp = hm.hm_next; } } free(maptable); } /* * Display the active host mapping table. */ void showhostmap_live(fd, nsp) int fd; natstat_t *nsp; { ipfgeniter_t iter; hostmap_t hm; ipfobj_t obj; int i; bzero((char *)&obj, sizeof(obj)); obj.ipfo_rev = IPFILTER_VERSION; obj.ipfo_type = IPFOBJ_GENITER; obj.ipfo_size = sizeof(iter); obj.ipfo_ptr = &iter; iter.igi_type = IPFGENITER_HOSTMAP; iter.igi_nitems = 1; iter.igi_data = &hm; printf("\nList of active host mappings:\n"); while (nsp->ns_maplist != NULL) { if (ioctl(fd, SIOCGENITER, &obj) == -1) break; printhostmap(&hm, hm.hm_hv); nsp->ns_maplist = hm.hm_next; } i = IPFGENITER_HOSTMAP; (void) ioctl(fd,SIOCIPFDELTOK, &i); } int nat_matcharray(nat, array) nat_t *nat; int *array; { int i, n, *x, rv, p; ipfexp_t *e; rv = 0; n = array[0]; x = array + 1; for (; n > 0; x += 3 + x[3], rv = 0) { e = (ipfexp_t *)x; if (e->ipfe_cmd == IPF_EXP_END) break; n -= e->ipfe_size; p = e->ipfe_cmd >> 16; if ((p != 0) && (p != nat->nat_pr[1])) break; switch (e->ipfe_cmd) { case IPF_EXP_IP_PR : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (nat->nat_pr[1] == e->ipfe_arg0[i]); } break; case IPF_EXP_IP_SRCADDR : if (nat->nat_v[0] != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((nat->nat_osrcaddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]) || ((nat->nat_nsrcaddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_DSTADDR : if (nat->nat_v[0] != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((nat->nat_odstaddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]) || ((nat->nat_ndstaddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; case IPF_EXP_IP_ADDR : if (nat->nat_v[0] != 4) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= ((nat->nat_osrcaddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]) || ((nat->nat_nsrcaddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]) || ((nat->nat_odstaddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]) || ((nat->nat_ndstaddr & e->ipfe_arg0[i * 2 + 1]) == e->ipfe_arg0[i * 2]); } break; #ifdef USE_INET6 case IPF_EXP_IP6_SRCADDR : if (nat->nat_v[0] != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&nat->nat_osrc6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]) || IP6_MASKEQ(&nat->nat_nsrc6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_DSTADDR : if (nat->nat_v[0] != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&nat->nat_odst6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]) || IP6_MASKEQ(&nat->nat_ndst6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; case IPF_EXP_IP6_ADDR : if (nat->nat_v[0] != 6) break; for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= IP6_MASKEQ(&nat->nat_osrc6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]) || IP6_MASKEQ(&nat->nat_nsrc6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]) || IP6_MASKEQ(&nat->nat_odst6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]) || IP6_MASKEQ(&nat->nat_ndst6, &e->ipfe_arg0[i * 8 + 4], &e->ipfe_arg0[i * 8]); } break; #endif case IPF_EXP_UDP_PORT : case IPF_EXP_TCP_PORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (nat->nat_osport == e->ipfe_arg0[i]) || (nat->nat_nsport == e->ipfe_arg0[i]) || (nat->nat_odport == e->ipfe_arg0[i]) || (nat->nat_ndport == e->ipfe_arg0[i]); } break; case IPF_EXP_UDP_SPORT : case IPF_EXP_TCP_SPORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (nat->nat_osport == e->ipfe_arg0[i]) || (nat->nat_nsport == e->ipfe_arg0[i]); } break; case IPF_EXP_UDP_DPORT : case IPF_EXP_TCP_DPORT : for (i = 0; !rv && i < e->ipfe_narg; i++) { rv |= (nat->nat_odport == e->ipfe_arg0[i]) || (nat->nat_ndport == e->ipfe_arg0[i]); } break; } rv ^= e->ipfe_not; if (rv == 0) break; } return rv; } Index: stable/11/contrib/ntp/include/ntp_config.h =================================================================== --- stable/11/contrib/ntp/include/ntp_config.h (revision 359753) +++ stable/11/contrib/ntp/include/ntp_config.h (revision 359754) @@ -1,359 +1,359 @@ #ifndef NTP_CONFIG_H #define NTP_CONFIG_H #ifdef HAVE_SYS_RESOURCE_H # include #endif /* HAVE_SYS_RESOURCE_H */ #include "ntp_machine.h" #include "ntp_psl.h" #include "ntpsim.h" /* * Configuration file name */ #ifndef CONFIG_FILE # ifndef SYS_WINNT # define CONFIG_FILE "/etc/ntp.conf" # else /* SYS_WINNT */ # define CONFIG_FILE "%windir%\\system32\\drivers\\etc\\ntp.conf" # define ALT_CONFIG_FILE "%windir%\\ntp.conf" # define NTP_KEYSDIR "%windir%\\system32\\drivers\\etc" # endif /* SYS_WINNT */ #endif /* not CONFIG_FILE */ /* * We keep config trees around for possible saveconfig use. When * built with configure --disable-saveconfig, and when built with * debugging enabled, include the free_config_*() routines. In the * DEBUG case, they are used in an atexit() cleanup routine to make * postmortem leak check reports more interesting. */ #if !defined(FREE_CFG_T) && (!defined(SAVECONFIG) || defined(DEBUG)) #define FREE_CFG_T #endif /* Limits */ #define MAXLINE 1024 /* Configuration sources */ #define CONF_SOURCE_FILE 0 #define CONF_SOURCE_NTPQ 1 /* list of servers from command line for config_peers() */ extern int cmdline_server_count; extern char ** cmdline_servers; /* set to zero if we're not locking memory */ extern int cur_memlock; typedef struct int_range_tag { int first; int last; } int_range; /* generic list node */ typedef struct any_node_tag any_node; struct any_node_tag { any_node * link; }; typedef DECL_FIFO_ANCHOR(any_node) any_node_fifo; /* Structure for storing an attribute-value pair */ typedef struct attr_val_tag attr_val; struct attr_val_tag { attr_val * link; int attr; int type; /* T_String, T_Integer, ... */ int flag; /* auxiliary flags */ union val { double d; /* T_Double */ int i; /* T_Integer */ int_range r; /* T_Intrange */ char * s; /* T_String */ u_int u; /* T_U_int */ } value; }; typedef DECL_FIFO_ANCHOR(attr_val) attr_val_fifo; /* Structure for nodes on the syntax tree */ typedef struct address_node_tag address_node; struct address_node_tag { address_node * link; char * address; u_short type; /* family, AF_UNSPEC (0), AF_INET[6] */ }; typedef DECL_FIFO_ANCHOR(address_node) address_fifo; typedef struct int_node_tag int_node; struct int_node_tag { int_node * link; int i; }; typedef DECL_FIFO_ANCHOR(int_node) int_fifo; typedef struct string_node_tag string_node; struct string_node_tag { string_node * link; char * s; }; typedef DECL_FIFO_ANCHOR(string_node) string_fifo; typedef struct restrict_node_tag restrict_node; struct restrict_node_tag { restrict_node * link; address_node * addr; address_node * mask; attr_val_fifo * flag_tok_fifo; int line_no; short ippeerlimit; short srvfuzrft; }; typedef DECL_FIFO_ANCHOR(restrict_node) restrict_fifo; typedef struct peer_node_tag peer_node; struct peer_node_tag { peer_node * link; int host_mode; address_node * addr; attr_val_fifo * peerflags; u_char minpoll; u_char maxpoll; u_int32 ttl; u_char peerversion; keyid_t peerkey; char * group; }; typedef DECL_FIFO_ANCHOR(peer_node) peer_fifo; typedef struct unpeer_node_tag unpeer_node; struct unpeer_node_tag { unpeer_node * link; associd_t assocID; address_node * addr; }; typedef DECL_FIFO_ANCHOR(unpeer_node) unpeer_fifo; typedef struct auth_node_tag auth_node; struct auth_node_tag { int control_key; int cryptosw; attr_val_fifo * crypto_cmd_list; char * keys; char * keysdir; int request_key; int revoke; attr_val_fifo * trusted_key_list; char * ntp_signd_socket; }; typedef struct filegen_node_tag filegen_node; struct filegen_node_tag { filegen_node * link; int filegen_token; attr_val_fifo * options; }; typedef DECL_FIFO_ANCHOR(filegen_node) filegen_fifo; typedef struct setvar_node_tag setvar_node; struct setvar_node_tag { setvar_node * link; char * var; char * val; int isdefault; }; typedef DECL_FIFO_ANCHOR(setvar_node) setvar_fifo; typedef struct nic_rule_node_tag nic_rule_node; struct nic_rule_node_tag { nic_rule_node * link; int match_class; char * if_name; /* or numeric address */ int action; }; typedef DECL_FIFO_ANCHOR(nic_rule_node) nic_rule_fifo; typedef struct addr_opts_node_tag addr_opts_node; struct addr_opts_node_tag { addr_opts_node *link; address_node * addr; attr_val_fifo * options; }; typedef DECL_FIFO_ANCHOR(addr_opts_node) addr_opts_fifo; typedef struct sim_node_tag sim_node; struct sim_node_tag { sim_node * link; attr_val_fifo * init_opts; server_info_fifo * servers; }; typedef DECL_FIFO_ANCHOR(sim_node) sim_fifo; /* The syntax tree */ typedef struct config_tree_tag config_tree; struct config_tree_tag { config_tree * link; attr_val source; time_t timestamp; peer_fifo * peers; unpeer_fifo * unpeers; /* Other Modes */ int broadcastclient; address_fifo * manycastserver; address_fifo * multicastclient; attr_val_fifo * orphan_cmds; /* s/b renamed tos_options */ /* Monitoring Configuration */ int_fifo * stats_list; char * stats_dir; filegen_fifo * filegen_opts; /* Access Control Configuration */ attr_val_fifo * discard_opts; attr_val_fifo * mru_opts; restrict_fifo * restrict_opts; addr_opts_fifo *fudge; attr_val_fifo * rlimit; attr_val_fifo * tinker; attr_val_fifo * enable_opts; attr_val_fifo * disable_opts; auth_node auth; attr_val_fifo * logconfig; string_fifo * phone; setvar_fifo * setvar; int_fifo * ttl; addr_opts_fifo *trap; attr_val_fifo * vars; nic_rule_fifo * nic_rules; int_fifo * reset_counters; attr_val_fifo * pollskewlist; sim_fifo * sim_details; int mdnstries; }; /* Structure for holding a remote configuration command */ struct REMOTE_CONFIG_INFO { char buffer[MAXLINE]; char err_msg[MAXLINE]; int pos; int err_pos; int no_errors; }; /* * context for trap_name_resolved() to call ctlsettrap() once the * name->address resolution completes. */ typedef struct settrap_parms_tag { sockaddr_u ifaddr; int ifaddr_nonnull; } settrap_parms; /* * Poll Skew List */ -psl_item psl[17-3+1]; /* values for polls 3-17 */ +extern psl_item psl[17-3+1]; /* values for polls 3-17 */ /* To simplify the runtime code we */ /* don't want to have to special-case */ /* dealing with a default */ /* ** Data Minimization Items */ /* Serverresponse fuzz reftime: stored in 'restrict' fifos */ /* get text from T_ tokens */ const char * token_name(int token); /* generic fifo routines for structs linked by 1st member */ typedef void (*fifo_deleter)(void*); void * destroy_gen_fifo(void *fifo, fifo_deleter func); void * append_gen_fifo(void *fifo, void *entry); void * concat_gen_fifos(void *first, void *second); #define DESTROY_G_FIFO(pf, func) \ ((pf) = destroy_gen_fifo((pf), (fifo_deleter)(func))) #define APPEND_G_FIFO(pf, pe) \ ((pf) = append_gen_fifo((pf), (pe))) #define CONCAT_G_FIFOS(first, second) \ ((first) = concat_gen_fifos((first), (second))) #define HEAD_PFIFO(pf) \ (((pf) != NULL) \ ? HEAD_FIFO(*(pf)) \ : NULL) peer_node *create_peer_node(int hmode, address_node *addr, attr_val_fifo *options); unpeer_node *create_unpeer_node(address_node *addr); address_node *create_address_node(char *addr, int type); void destroy_address_node(address_node *my_node); attr_val *create_attr_dval(int attr, double value); attr_val *create_attr_ival(int attr, int value); attr_val *create_attr_rval(int attr, int first, int last); attr_val *create_attr_sval(int attr, const char *s); attr_val *create_attr_uval(int attr, u_int value); void destroy_attr_val(attr_val *node); filegen_node *create_filegen_node(int filegen_token, attr_val_fifo *options); string_node *create_string_node(char *str); restrict_node *create_restrict_node(address_node *addr, address_node *mask, short ippeerlimit, attr_val_fifo *flags, int line_no); int_node *create_int_node(int val); addr_opts_node *create_addr_opts_node(address_node *addr, attr_val_fifo *options); sim_node *create_sim_node(attr_val_fifo *init_opts, server_info_fifo *servers); setvar_node *create_setvar_node(char *var, char *val, int isdefault); nic_rule_node *create_nic_rule_node(int match_class, char *if_name, int action); script_info *create_sim_script_info(double duration, attr_val_fifo *script_queue); server_info *create_sim_server(address_node *addr, double server_offset, script_info_fifo *script); extern struct REMOTE_CONFIG_INFO remote_config; void config_remotely(sockaddr_u *); #ifdef SAVECONFIG int dump_config_tree(config_tree *ptree, FILE *df, int comment); int dump_all_config_trees(FILE *df, int comment); #endif #if defined(HAVE_SETRLIMIT) void ntp_rlimit(int, rlim_t, int, const char *); #endif #endif /* !defined(NTP_CONFIG_H) */ Index: stable/11/contrib/ntp/ntpd/ntp_config.c =================================================================== --- stable/11/contrib/ntp/ntpd/ntp_config.c (revision 359753) +++ stable/11/contrib/ntp/ntpd/ntp_config.c (revision 359754) @@ -1,5739 +1,5741 @@ /* ntp_config.c * * This file contains the ntpd configuration code. * * Written By: Sachin Kamboj * University of Delaware * Newark, DE 19711 * Some parts borrowed from the older ntp_config.c * Copyright (c) 2006 */ #ifdef HAVE_CONFIG_H # include #endif #ifdef HAVE_NETINFO # include #endif #include #include #ifdef HAVE_SYS_PARAM_H # include #endif #include #ifndef SIGCHLD # define SIGCHLD SIGCLD #endif #ifdef HAVE_SYS_WAIT_H # include #endif #include #include #include #include "ntp.h" #include "ntpd.h" #include "ntp_io.h" #include "ntp_unixtime.h" #include "ntp_refclock.h" #include "ntp_filegen.h" #include "ntp_stdlib.h" #include "lib_strbuf.h" #include "ntp_assert.h" #include "ntp_random.h" /* * [Bug 467]: Some linux headers collide with CONFIG_PHONE and CONFIG_KEYS * so #include these later. */ #include "ntp_config.h" #include "ntp_cmdargs.h" #include "ntp_scanner.h" #include "ntp_parser.h" #include "ntpd-opts.h" #ifndef IGNORE_DNS_ERRORS # define DNSFLAGS 0 #else # define DNSFLAGS GAIR_F_IGNDNSERR #endif extern int yyparse(void); /* Bug 2817 */ #if defined(HAVE_SYS_MMAN_H) # include #endif /* list of servers from command line for config_peers() */ int cmdline_server_count; char ** cmdline_servers; /* Current state of memory locking: * -1: default * 0: memory locking disabled * 1: Memory locking enabled */ int cur_memlock = -1; /* * "logconfig" building blocks */ struct masks { const char * const name; const u_int32 mask; }; static struct masks logcfg_class[] = { { "clock", NLOG_OCLOCK }, { "peer", NLOG_OPEER }, { "sync", NLOG_OSYNC }, { "sys", NLOG_OSYS }, { NULL, 0 } }; /* logcfg_noclass_items[] masks are complete and must not be shifted */ static struct masks logcfg_noclass_items[] = { { "allall", NLOG_SYSMASK | NLOG_PEERMASK | NLOG_CLOCKMASK | NLOG_SYNCMASK }, { "allinfo", NLOG_SYSINFO | NLOG_PEERINFO | NLOG_CLOCKINFO | NLOG_SYNCINFO }, { "allevents", NLOG_SYSEVENT | NLOG_PEEREVENT | NLOG_CLOCKEVENT | NLOG_SYNCEVENT }, { "allstatus", NLOG_SYSSTATUS | NLOG_PEERSTATUS | NLOG_CLOCKSTATUS | NLOG_SYNCSTATUS }, { "allstatistics", NLOG_SYSSTATIST | NLOG_PEERSTATIST | NLOG_CLOCKSTATIST | NLOG_SYNCSTATIST }, /* the remainder are misspellings of clockall, peerall, sysall, and syncall. */ { "allclock", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OCLOCK }, { "allpeer", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OPEER }, { "allsys", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OSYS }, { "allsync", (NLOG_INFO | NLOG_STATIST | NLOG_EVENT | NLOG_STATUS) << NLOG_OSYNC }, { NULL, 0 } }; /* logcfg_class_items[] masks are shiftable by NLOG_O* counts */ static struct masks logcfg_class_items[] = { { "all", NLOG_INFO | NLOG_EVENT | NLOG_STATUS | NLOG_STATIST }, { "info", NLOG_INFO }, { "events", NLOG_EVENT }, { "status", NLOG_STATUS }, { "statistics", NLOG_STATIST }, { NULL, 0 } }; typedef struct peer_resolved_ctx_tag { int flags; int host_mode; /* T_* token identifier */ u_short family; keyid_t keyid; u_char hmode; /* MODE_* */ u_char version; u_char minpoll; u_char maxpoll; u_int32 ttl; const char * group; } peer_resolved_ctx; /* Limits */ #define MAXPHONE 10 /* maximum number of phone strings */ #define MAXPPS 20 /* maximum length of PPS device string */ /* * Miscellaneous macros */ #define ISEOL(c) ((c) == '#' || (c) == '\n' || (c) == '\0') #define ISSPACE(c) ((c) == ' ' || (c) == '\t') #define _UC(str) ((char *)(intptr_t)(str)) /* * Definitions of things either imported from or exported to outside */ extern int yydebug; /* ntp_parser.c (.y) */ config_tree cfgt; /* Parser output stored here */ config_tree *cfg_tree_history; /* History of configs */ char * sys_phone[MAXPHONE] = {NULL}; /* ACTS phone numbers */ char default_keysdir[] = NTP_KEYSDIR; char * keysdir = default_keysdir; /* crypto keys directory */ char * saveconfigdir; #if defined(HAVE_SCHED_SETSCHEDULER) int config_priority_override = 0; int config_priority; #endif const char *config_file; static char default_ntp_signd_socket[] = #ifdef NTP_SIGND_PATH NTP_SIGND_PATH; #else ""; #endif char *ntp_signd_socket = default_ntp_signd_socket; #ifdef HAVE_NETINFO struct netinfo_config_state *config_netinfo = NULL; int check_netinfo = 1; #endif /* HAVE_NETINFO */ #ifdef SYS_WINNT char *alt_config_file; LPTSTR temp; char config_file_storage[MAX_PATH]; char alt_config_file_storage[MAX_PATH]; #endif /* SYS_WINNT */ #ifdef HAVE_NETINFO /* * NetInfo configuration state */ struct netinfo_config_state { void *domain; /* domain with config */ ni_id config_dir; /* ID config dir */ int prop_index; /* current property */ int val_index; /* current value */ char **val_list; /* value list */ }; #endif struct REMOTE_CONFIG_INFO remote_config; /* Remote configuration buffer and pointer info */ int old_config_style = 1; /* A boolean flag, which when set, * indicates that the old configuration * format with a newline at the end of * every command is being used */ int cryptosw; /* crypto command called */ extern char *stats_drift_file; /* name of the driftfile */ +psl_item psl[17-3+1]; + #ifdef BC_LIST_FRAMEWORK_NOT_YET_USED /* * backwards compatibility flags */ bc_entry bc_list[] = { { T_Bc_bugXXXX, 1 } /* default enabled */ }; /* * declare an int pointer for each flag for quick testing without * walking bc_list. If the pointer is consumed by libntp rather * than ntpd, declare it in a libntp source file pointing to storage * initialized with the appropriate value for other libntp clients, and * redirect it to point into bc_list during ntpd startup. */ int *p_bcXXXX_enabled = &bc_list[0].enabled; #endif /* FUNCTION PROTOTYPES */ static void init_syntax_tree(config_tree *); static void apply_enable_disable(attr_val_fifo *q, int enable); #ifdef FREE_CFG_T static void free_auth_node(config_tree *); static void free_all_config_trees(void); static void free_config_access(config_tree *); static void free_config_auth(config_tree *); static void free_config_fudge(config_tree *); static void free_config_logconfig(config_tree *); static void free_config_monitor(config_tree *); static void free_config_nic_rules(config_tree *); static void free_config_other_modes(config_tree *); static void free_config_peers(config_tree *); static void free_config_phone(config_tree *); static void free_config_reset_counters(config_tree *); static void free_config_rlimit(config_tree *); static void free_config_setvar(config_tree *); static void free_config_system_opts(config_tree *); static void free_config_tinker(config_tree *); static void free_config_tos(config_tree *); static void free_config_trap(config_tree *); static void free_config_ttl(config_tree *); static void free_config_unpeers(config_tree *); static void free_config_vars(config_tree *); #ifdef SIM static void free_config_sim(config_tree *); #endif static void destroy_address_fifo(address_fifo *); #define FREE_ADDRESS_FIFO(pf) \ do { \ destroy_address_fifo(pf); \ (pf) = NULL; \ } while (0) void free_all_config_trees(void); /* atexit() */ static void free_config_tree(config_tree *ptree); #endif /* FREE_CFG_T */ static void destroy_restrict_node(restrict_node *my_node); static int is_sane_resolved_address(sockaddr_u *peeraddr, int hmode); static void save_and_apply_config_tree(int/*BOOL*/ from_file); static void destroy_int_fifo(int_fifo *); #define FREE_INT_FIFO(pf) \ do { \ destroy_int_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_string_fifo(string_fifo *); #define FREE_STRING_FIFO(pf) \ do { \ destroy_string_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_attr_val_fifo(attr_val_fifo *); #define FREE_ATTR_VAL_FIFO(pf) \ do { \ destroy_attr_val_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_filegen_fifo(filegen_fifo *); #define FREE_FILEGEN_FIFO(pf) \ do { \ destroy_filegen_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_restrict_fifo(restrict_fifo *); #define FREE_RESTRICT_FIFO(pf) \ do { \ destroy_restrict_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_setvar_fifo(setvar_fifo *); #define FREE_SETVAR_FIFO(pf) \ do { \ destroy_setvar_fifo(pf); \ (pf) = NULL; \ } while (0) static void destroy_addr_opts_fifo(addr_opts_fifo *); #define FREE_ADDR_OPTS_FIFO(pf) \ do { \ destroy_addr_opts_fifo(pf); \ (pf) = NULL; \ } while (0) static void config_logconfig(config_tree *); static void config_monitor(config_tree *); static void config_rlimit(config_tree *); static void config_system_opts(config_tree *); static void config_tinker(config_tree *); static int config_tos_clock(config_tree *); static void config_tos(config_tree *); static void config_vars(config_tree *); #ifdef SIM static sockaddr_u *get_next_address(address_node *addr); static void config_sim(config_tree *); static void config_ntpdsim(config_tree *); #else /* !SIM follows */ static void config_ntpd(config_tree *, int/*BOOL*/ input_from_file); static void config_other_modes(config_tree *); static void config_auth(config_tree *); static void attrtopsl(int poll, attr_val *avp); static void config_access(config_tree *); static void config_mdnstries(config_tree *); static void config_phone(config_tree *); static void config_setvar(config_tree *); static void config_ttl(config_tree *); static void config_trap(config_tree *); static void config_fudge(config_tree *); static void config_peers(config_tree *); static void config_unpeers(config_tree *); static void config_nic_rules(config_tree *, int/*BOOL*/ input_from_file); static void config_reset_counters(config_tree *); static u_char get_correct_host_mode(int token); static int peerflag_bits(peer_node *); #endif /* !SIM */ #ifdef WORKER static void peer_name_resolved(int, int, void *, const char *, const char *, const struct addrinfo *, const struct addrinfo *); static void unpeer_name_resolved(int, int, void *, const char *, const char *, const struct addrinfo *, const struct addrinfo *); static void trap_name_resolved(int, int, void *, const char *, const char *, const struct addrinfo *, const struct addrinfo *); #endif enum gnn_type { t_UNK, /* Unknown */ t_REF, /* Refclock */ t_MSK /* Network Mask */ }; static void ntpd_set_tod_using(const char *); static char * normal_dtoa(double); static u_int32 get_pfxmatch(const char **, struct masks *); static u_int32 get_match(const char *, struct masks *); static u_int32 get_logmask(const char *); static int/*BOOL*/ is_refclk_addr(const address_node * addr); static void appendstr(char *, size_t, const char *); #ifndef SIM static int getnetnum(const char *num, sockaddr_u *addr, int complain, enum gnn_type a_type); #endif #if defined(__GNUC__) /* this covers CLANG, too */ static void __attribute__((noreturn,format(printf,1,2))) fatal_error(const char *fmt, ...) #elif defined(_MSC_VER) static void __declspec(noreturn) fatal_error(const char *fmt, ...) #else static void fatal_error(const char *fmt, ...) #endif { va_list va; va_start(va, fmt); mvsyslog(LOG_EMERG, fmt, va); va_end(va); _exit(1); } /* FUNCTIONS FOR INITIALIZATION * ---------------------------- */ #ifdef FREE_CFG_T static void free_auth_node( config_tree *ptree ) { if (ptree->auth.keys) { free(ptree->auth.keys); ptree->auth.keys = NULL; } if (ptree->auth.keysdir) { free(ptree->auth.keysdir); ptree->auth.keysdir = NULL; } if (ptree->auth.ntp_signd_socket) { free(ptree->auth.ntp_signd_socket); ptree->auth.ntp_signd_socket = NULL; } } #endif /* DEBUG */ static void init_syntax_tree( config_tree *ptree ) { ZERO(*ptree); ptree->mdnstries = 5; } #ifdef FREE_CFG_T static void free_all_config_trees(void) { config_tree *ptree; config_tree *pnext; ptree = cfg_tree_history; while (ptree != NULL) { pnext = ptree->link; free_config_tree(ptree); ptree = pnext; } } static void free_config_tree( config_tree *ptree ) { #if defined(_MSC_VER) && defined (_DEBUG) _CrtCheckMemory(); #endif if (ptree->source.value.s != NULL) free(ptree->source.value.s); free_config_other_modes(ptree); free_config_auth(ptree); free_config_tos(ptree); free_config_monitor(ptree); free_config_access(ptree); free_config_tinker(ptree); free_config_rlimit(ptree); free_config_system_opts(ptree); free_config_logconfig(ptree); free_config_phone(ptree); free_config_setvar(ptree); free_config_ttl(ptree); free_config_trap(ptree); free_config_fudge(ptree); free_config_vars(ptree); free_config_peers(ptree); free_config_unpeers(ptree); free_config_nic_rules(ptree); free_config_reset_counters(ptree); #ifdef SIM free_config_sim(ptree); #endif free_auth_node(ptree); free(ptree); #if defined(_MSC_VER) && defined (_DEBUG) _CrtCheckMemory(); #endif } #endif /* FREE_CFG_T */ #ifdef SAVECONFIG /* Dump all trees */ int dump_all_config_trees( FILE *df, int comment ) { config_tree * cfg_ptr; int return_value; time_t now = time(NULL); struct tm tm = *localtime(&now); fprintf(df, "#NTF:D %04d%02d%02d@%02d:%02d:%02d\n", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); fprintf(df, "#NTF:V %s\n", Version); return_value = 0; for (cfg_ptr = cfg_tree_history; cfg_ptr != NULL; cfg_ptr = cfg_ptr->link) return_value |= dump_config_tree(cfg_ptr, df, comment); return return_value; } /* The config dumper */ int dump_config_tree( config_tree *ptree, FILE *df, int comment ) { peer_node *peern; unpeer_node *unpeern; attr_val *atrv; address_node *addr; address_node *peer_addr; address_node *fudge_addr; filegen_node *fgen_node; restrict_node *rest_node; addr_opts_node *addr_opts; setvar_node *setv_node; nic_rule_node *rule_node; int_node *i_n; int_node *counter_set; string_node *str_node; const char *s = NULL; char *s1; char *s2; char timestamp[80]; int enable; DPRINTF(1, ("dump_config_tree(%p)\n", ptree)); if (comment) { if (!strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", localtime(&ptree->timestamp))) timestamp[0] = '\0'; fprintf(df, "# %s %s %s\n", timestamp, (CONF_SOURCE_NTPQ == ptree->source.attr) ? "ntpq remote config from" : "startup configuration file", ptree->source.value.s); } /* * For options without documentation we just output the name * and its data value */ atrv = HEAD_PFIFO(ptree->vars); for ( ; atrv != NULL; atrv = atrv->link) { switch (atrv->type) { #ifdef DEBUG default: fprintf(df, "\n# dump error:\n" "# unknown vars type %d (%s) for %s\n", atrv->type, token_name(atrv->type), token_name(atrv->attr)); break; #endif case T_Double: fprintf(df, "%s %s\n", keyword(atrv->attr), normal_dtoa(atrv->value.d)); break; case T_Integer: fprintf(df, "%s %d\n", keyword(atrv->attr), atrv->value.i); break; case T_String: fprintf(df, "%s \"%s\"", keyword(atrv->attr), atrv->value.s); if (T_Driftfile == atrv->attr && atrv->link != NULL && T_WanderThreshold == atrv->link->attr) { atrv = atrv->link; fprintf(df, " %s\n", normal_dtoa(atrv->value.d)); } else if (T_Leapfile == atrv->attr) { fputs((atrv->flag ? " checkhash\n" : " ignorehash\n"), df); } else { fprintf(df, "\n"); } break; } } atrv = HEAD_PFIFO(ptree->logconfig); if (atrv != NULL) { fprintf(df, "logconfig"); for ( ; atrv != NULL; atrv = atrv->link) fprintf(df, " %c%s", atrv->attr, atrv->value.s); fprintf(df, "\n"); } if (ptree->stats_dir) fprintf(df, "statsdir \"%s\"\n", ptree->stats_dir); i_n = HEAD_PFIFO(ptree->stats_list); if (i_n != NULL) { fprintf(df, "statistics"); for ( ; i_n != NULL; i_n = i_n->link) fprintf(df, " %s", keyword(i_n->i)); fprintf(df, "\n"); } fgen_node = HEAD_PFIFO(ptree->filegen_opts); for ( ; fgen_node != NULL; fgen_node = fgen_node->link) { atrv = HEAD_PFIFO(fgen_node->options); if (atrv != NULL) { fprintf(df, "filegen %s", keyword(fgen_node->filegen_token)); for ( ; atrv != NULL; atrv = atrv->link) { switch (atrv->attr) { #ifdef DEBUG default: fprintf(df, "\n# dump error:\n" "# unknown filegen option token %s\n" "filegen %s", token_name(atrv->attr), keyword(fgen_node->filegen_token)); break; #endif case T_File: fprintf(df, " file %s", atrv->value.s); break; case T_Type: fprintf(df, " type %s", keyword(atrv->value.i)); break; case T_Flag: fprintf(df, " %s", keyword(atrv->value.i)); break; } } fprintf(df, "\n"); } } atrv = HEAD_PFIFO(ptree->auth.crypto_cmd_list); if (atrv != NULL) { fprintf(df, "crypto"); for ( ; atrv != NULL; atrv = atrv->link) { fprintf(df, " %s %s", keyword(atrv->attr), atrv->value.s); } fprintf(df, "\n"); } if (ptree->auth.revoke != 0) fprintf(df, "revoke %d\n", ptree->auth.revoke); if (ptree->auth.keysdir != NULL) fprintf(df, "keysdir \"%s\"\n", ptree->auth.keysdir); if (ptree->auth.keys != NULL) fprintf(df, "keys \"%s\"\n", ptree->auth.keys); atrv = HEAD_PFIFO(ptree->auth.trusted_key_list); if (atrv != NULL) { fprintf(df, "trustedkey"); for ( ; atrv != NULL; atrv = atrv->link) { if (T_Integer == atrv->type) fprintf(df, " %d", atrv->value.i); else if (T_Intrange == atrv->type) fprintf(df, " (%d ... %d)", atrv->value.r.first, atrv->value.r.last); #ifdef DEBUG else fprintf(df, "\n# dump error:\n" "# unknown trustedkey attr type %d\n" "trustedkey", atrv->type); #endif } fprintf(df, "\n"); } if (ptree->auth.control_key) fprintf(df, "controlkey %d\n", ptree->auth.control_key); if (ptree->auth.request_key) fprintf(df, "requestkey %d\n", ptree->auth.request_key); /* dump enable list, then disable list */ for (enable = 1; enable >= 0; enable--) { atrv = (enable) ? HEAD_PFIFO(ptree->enable_opts) : HEAD_PFIFO(ptree->disable_opts); if (atrv != NULL) { fprintf(df, "%s", (enable) ? "enable" : "disable"); for ( ; atrv != NULL; atrv = atrv->link) fprintf(df, " %s", keyword(atrv->value.i)); fprintf(df, "\n"); } } atrv = HEAD_PFIFO(ptree->orphan_cmds); if (atrv != NULL) { fprintf(df, "tos"); for ( ; atrv != NULL; atrv = atrv->link) { switch (atrv->type) { #ifdef DEBUG default: fprintf(df, "\n# dump error:\n" "# unknown tos attr type %d %s\n" "tos", atrv->type, token_name(atrv->type)); break; #endif case T_Integer: if (atrv->attr == T_Basedate) { struct calendar jd; ntpcal_rd_to_date(&jd, atrv->value.i + DAY_NTP_STARTS); fprintf(df, " %s \"%04hu-%02hu-%02hu\"", keyword(atrv->attr), jd.year, (u_short)jd.month, (u_short)jd.monthday); } else { fprintf(df, " %s %d", keyword(atrv->attr), atrv->value.i); } break; case T_Double: fprintf(df, " %s %s", keyword(atrv->attr), normal_dtoa(atrv->value.d)); break; } } fprintf(df, "\n"); } atrv = HEAD_PFIFO(ptree->rlimit); if (atrv != NULL) { fprintf(df, "rlimit"); for ( ; atrv != NULL; atrv = atrv->link) { INSIST(T_Integer == atrv->type); fprintf(df, " %s %d", keyword(atrv->attr), atrv->value.i); } fprintf(df, "\n"); } atrv = HEAD_PFIFO(ptree->tinker); if (atrv != NULL) { fprintf(df, "tinker"); for ( ; atrv != NULL; atrv = atrv->link) { INSIST(T_Double == atrv->type); fprintf(df, " %s %s", keyword(atrv->attr), normal_dtoa(atrv->value.d)); } fprintf(df, "\n"); } if (ptree->broadcastclient) fprintf(df, "broadcastclient\n"); peern = HEAD_PFIFO(ptree->peers); for ( ; peern != NULL; peern = peern->link) { addr = peern->addr; fprintf(df, "%s", keyword(peern->host_mode)); switch (addr->type) { #ifdef DEBUG default: fprintf(df, "# dump error:\n" "# unknown peer family %d for:\n" "%s", addr->type, keyword(peern->host_mode)); break; #endif case AF_UNSPEC: break; case AF_INET: fprintf(df, " -4"); break; case AF_INET6: fprintf(df, " -6"); break; } fprintf(df, " %s", addr->address); if (peern->minpoll != 0) fprintf(df, " minpoll %u", peern->minpoll); if (peern->maxpoll != 0) fprintf(df, " maxpoll %u", peern->maxpoll); if (peern->ttl != 0) { if (strlen(addr->address) > 8 && !memcmp(addr->address, "127.127.", 8)) fprintf(df, " mode %u", peern->ttl); else fprintf(df, " ttl %u", peern->ttl); } if (peern->peerversion != NTP_VERSION) fprintf(df, " version %u", peern->peerversion); if (peern->peerkey != 0) fprintf(df, " key %u", peern->peerkey); if (peern->group != NULL) fprintf(df, " ident \"%s\"", peern->group); atrv = HEAD_PFIFO(peern->peerflags); for ( ; atrv != NULL; atrv = atrv->link) { INSIST(T_Flag == atrv->attr); INSIST(T_Integer == atrv->type); fprintf(df, " %s", keyword(atrv->value.i)); } fprintf(df, "\n"); addr_opts = HEAD_PFIFO(ptree->fudge); for ( ; addr_opts != NULL; addr_opts = addr_opts->link) { peer_addr = peern->addr; fudge_addr = addr_opts->addr; s1 = peer_addr->address; s2 = fudge_addr->address; if (strcmp(s1, s2)) continue; fprintf(df, "fudge %s", s1); for (atrv = HEAD_PFIFO(addr_opts->options); atrv != NULL; atrv = atrv->link) { switch (atrv->type) { #ifdef DEBUG default: fprintf(df, "\n# dump error:\n" "# unknown fudge atrv->type %d\n" "fudge %s", atrv->type, s1); break; #endif case T_Double: fprintf(df, " %s %s", keyword(atrv->attr), normal_dtoa(atrv->value.d)); break; case T_Integer: fprintf(df, " %s %d", keyword(atrv->attr), atrv->value.i); break; case T_String: fprintf(df, " %s %s", keyword(atrv->attr), atrv->value.s); break; } } fprintf(df, "\n"); } } addr = HEAD_PFIFO(ptree->manycastserver); if (addr != NULL) { fprintf(df, "manycastserver"); for ( ; addr != NULL; addr = addr->link) fprintf(df, " %s", addr->address); fprintf(df, "\n"); } addr = HEAD_PFIFO(ptree->multicastclient); if (addr != NULL) { fprintf(df, "multicastclient"); for ( ; addr != NULL; addr = addr->link) fprintf(df, " %s", addr->address); fprintf(df, "\n"); } for (unpeern = HEAD_PFIFO(ptree->unpeers); unpeern != NULL; unpeern = unpeern->link) fprintf(df, "unpeer %s\n", unpeern->addr->address); atrv = HEAD_PFIFO(ptree->mru_opts); if (atrv != NULL) { fprintf(df, "mru"); for ( ; atrv != NULL; atrv = atrv->link) fprintf(df, " %s %d", keyword(atrv->attr), atrv->value.i); fprintf(df, "\n"); } atrv = HEAD_PFIFO(ptree->discard_opts); if (atrv != NULL) { fprintf(df, "discard"); for ( ; atrv != NULL; atrv = atrv->link) fprintf(df, " %s %d", keyword(atrv->attr), atrv->value.i); fprintf(df, "\n"); } atrv = HEAD_PFIFO(ptree->pollskewlist); if (atrv != NULL) { fprintf(df, "pollskewlist"); for ( ; atrv != NULL; atrv = atrv->link) { if (-1 == atrv->attr) { fprintf(df, " default"); } else { fprintf(df, " %d", atrv->attr); } fprintf(df, " %d|%d", atrv->value.r.first, atrv->value.r.last); } fprintf(df, "\n"); } for (rest_node = HEAD_PFIFO(ptree->restrict_opts); rest_node != NULL; rest_node = rest_node->link) { int is_default = 0; if (NULL == rest_node->addr) { s = "default"; /* Don't need to set is_default=1 here */ atrv = HEAD_PFIFO(rest_node->flag_tok_fifo); for ( ; atrv != NULL; atrv = atrv->link) { if ( T_Integer == atrv->type && T_Source == atrv->attr) { s = "source"; break; } } } else { const char *ap = rest_node->addr->address; const char *mp = ""; if (rest_node->mask) mp = rest_node->mask->address; if ( rest_node->addr->type == AF_INET && !strcmp(ap, "0.0.0.0") && !strcmp(mp, "0.0.0.0")) { is_default = 1; s = "-4 default"; } else if ( rest_node->mask && rest_node->mask->type == AF_INET6 && !strcmp(ap, "::") && !strcmp(mp, "::")) { is_default = 1; s = "-6 default"; } else { s = ap; } } fprintf(df, "restrict %s", s); if (rest_node->mask != NULL && !is_default) fprintf(df, " mask %s", rest_node->mask->address); fprintf(df, " ippeerlimit %d", rest_node->ippeerlimit); atrv = HEAD_PFIFO(rest_node->flag_tok_fifo); for ( ; atrv != NULL; atrv = atrv->link) { if ( T_Integer == atrv->type && T_Source != atrv->attr) { fprintf(df, " %s", keyword(atrv->attr)); } } fprintf(df, "\n"); /**/ #if 0 msyslog(LOG_INFO, "Dumping flag_tok_fifo:"); atrv = HEAD_PFIFO(rest_node->flag_tok_fifo); for ( ; atrv != NULL; atrv = atrv->link) { msyslog(LOG_INFO, "- flag_tok_fifo: flags: %08x", atrv->flag); switch(atrv->type) { case T_Integer: msyslog(LOG_INFO, "- T_Integer: attr <%s>/%d, value %d", keyword(atrv->attr), atrv->attr, atrv->value.i); break; default: msyslog(LOG_INFO, "- Other: attr <%s>/%d, value ???", keyword(atrv->attr), atrv->attr); break; } } #endif /**/ } rule_node = HEAD_PFIFO(ptree->nic_rules); for ( ; rule_node != NULL; rule_node = rule_node->link) { fprintf(df, "interface %s %s\n", keyword(rule_node->action), (rule_node->match_class) ? keyword(rule_node->match_class) : rule_node->if_name); } str_node = HEAD_PFIFO(ptree->phone); if (str_node != NULL) { fprintf(df, "phone"); for ( ; str_node != NULL; str_node = str_node->link) fprintf(df, " \"%s\"", str_node->s); fprintf(df, "\n"); } setv_node = HEAD_PFIFO(ptree->setvar); for ( ; setv_node != NULL; setv_node = setv_node->link) { s1 = quote_if_needed(setv_node->var); s2 = quote_if_needed(setv_node->val); fprintf(df, "setvar %s = %s", s1, s2); free(s1); free(s2); if (setv_node->isdefault) fprintf(df, " default"); fprintf(df, "\n"); } i_n = HEAD_PFIFO(ptree->ttl); if (i_n != NULL) { fprintf(df, "ttl"); for( ; i_n != NULL; i_n = i_n->link) fprintf(df, " %d", i_n->i); fprintf(df, "\n"); } addr_opts = HEAD_PFIFO(ptree->trap); for ( ; addr_opts != NULL; addr_opts = addr_opts->link) { addr = addr_opts->addr; fprintf(df, "trap %s", addr->address); atrv = HEAD_PFIFO(addr_opts->options); for ( ; atrv != NULL; atrv = atrv->link) { switch (atrv->attr) { #ifdef DEBUG default: fprintf(df, "\n# dump error:\n" "# unknown trap token %d\n" "trap %s", atrv->attr, addr->address); break; #endif case T_Port: fprintf(df, " port %d", atrv->value.i); break; case T_Interface: fprintf(df, " interface %s", atrv->value.s); break; } } fprintf(df, "\n"); } counter_set = HEAD_PFIFO(ptree->reset_counters); if (counter_set != NULL) { fprintf(df, "reset"); for ( ; counter_set != NULL; counter_set = counter_set->link) fprintf(df, " %s", keyword(counter_set->i)); fprintf(df, "\n"); } return 0; } #endif /* SAVECONFIG */ /* generic fifo routines for structs linked by 1st member */ void * append_gen_fifo( void *fifo, void *entry ) { gen_fifo *pf; gen_node *pe; pf = fifo; pe = entry; if (NULL == pf) pf = emalloc_zero(sizeof(*pf)); else CHECK_FIFO_CONSISTENCY(*pf); if (pe != NULL) LINK_FIFO(*pf, pe, link); CHECK_FIFO_CONSISTENCY(*pf); return pf; } void * concat_gen_fifos( void *first, void *second ) { gen_fifo *pf1; gen_fifo *pf2; pf1 = first; pf2 = second; if (NULL == pf1) return pf2; if (NULL == pf2) return pf1; CONCAT_FIFO(*pf1, *pf2, link); free(pf2); return pf1; } void* destroy_gen_fifo( void *fifo, fifo_deleter func ) { any_node * np = NULL; any_node_fifo * pf1 = fifo; if (pf1 != NULL) { if (!func) func = free; for (;;) { UNLINK_FIFO(np, *pf1, link); if (np == NULL) break; (*func)(np); } free(pf1); } return NULL; } /* FUNCTIONS FOR CREATING NODES ON THE SYNTAX TREE * ----------------------------------------------- */ void destroy_attr_val( attr_val * av ) { if (av) { if (T_String == av->type) free(av->value.s); free(av); } } attr_val * create_attr_dval( int attr, double value ) { attr_val *my_val; my_val = emalloc_zero(sizeof(*my_val)); my_val->attr = attr; my_val->value.d = value; my_val->type = T_Double; return my_val; } attr_val * create_attr_ival( int attr, int value ) { attr_val *my_val; my_val = emalloc_zero(sizeof(*my_val)); my_val->attr = attr; my_val->value.i = value; my_val->type = T_Integer; return my_val; } attr_val * create_attr_uval( int attr, u_int value ) { attr_val *my_val; my_val = emalloc_zero(sizeof(*my_val)); my_val->attr = attr; my_val->value.u = value; my_val->type = T_U_int; return my_val; } attr_val * create_attr_rval( int attr, int first, int last ) { attr_val *my_val; my_val = emalloc_zero(sizeof(*my_val)); my_val->attr = attr; my_val->value.r.first = first; my_val->value.r.last = last; my_val->type = T_Intrange; return my_val; } attr_val * create_attr_sval( int attr, const char *s ) { attr_val *my_val; my_val = emalloc_zero(sizeof(*my_val)); my_val->attr = attr; if (NULL == s) /* free() hates NULL */ s = estrdup(""); my_val->value.s = _UC(s); my_val->type = T_String; return my_val; } int_node * create_int_node( int val ) { int_node *i_n; i_n = emalloc_zero(sizeof(*i_n)); i_n->i = val; return i_n; } string_node * create_string_node( char *str ) { string_node *sn; sn = emalloc_zero(sizeof(*sn)); sn->s = str; return sn; } address_node * create_address_node( char * addr, int type ) { address_node *my_node; REQUIRE(NULL != addr); REQUIRE(AF_INET == type || AF_INET6 == type || AF_UNSPEC == type); my_node = emalloc_zero(sizeof(*my_node)); my_node->address = addr; my_node->type = (u_short)type; return my_node; } void destroy_address_node( address_node *my_node ) { if (NULL == my_node) return; REQUIRE(NULL != my_node->address); free(my_node->address); free(my_node); } peer_node * create_peer_node( int hmode, address_node * addr, attr_val_fifo * options ) { peer_node *my_node; attr_val *option; int freenode; int errflag = 0; my_node = emalloc_zero(sizeof(*my_node)); /* Initialize node values to default */ my_node->peerversion = NTP_VERSION; /* Now set the node to the read values */ my_node->host_mode = hmode; my_node->addr = addr; /* * the options FIFO mixes items that will be saved in the * peer_node as explicit members, such as minpoll, and * those that are moved intact to the peer_node's peerflags * FIFO. The options FIFO is consumed and reclaimed here. */ if (options != NULL) CHECK_FIFO_CONSISTENCY(*options); while (options != NULL) { UNLINK_FIFO(option, *options, link); if (NULL == option) { free(options); break; } freenode = 1; /* Check the kind of option being set */ switch (option->attr) { case T_Flag: APPEND_G_FIFO(my_node->peerflags, option); freenode = 0; break; case T_Minpoll: if (option->value.i < NTP_MINPOLL || option->value.i > UCHAR_MAX) { msyslog(LOG_INFO, "minpoll: provided value (%d) is out of range [%d-%d])", option->value.i, NTP_MINPOLL, UCHAR_MAX); my_node->minpoll = NTP_MINPOLL; } else { my_node->minpoll = (u_char)option->value.u; } break; case T_Maxpoll: if (option->value.i < 0 || option->value.i > NTP_MAXPOLL) { msyslog(LOG_INFO, "maxpoll: provided value (%d) is out of range [0-%d])", option->value.i, NTP_MAXPOLL); my_node->maxpoll = NTP_MAXPOLL; } else { my_node->maxpoll = (u_char)option->value.u; } break; case T_Ttl: if (is_refclk_addr(addr)) { msyslog(LOG_ERR, "'ttl' does not apply for refclocks"); errflag = 1; } else if (option->value.u >= MAX_TTL) { msyslog(LOG_ERR, "ttl: invalid argument"); errflag = 1; } else { my_node->ttl = (u_char)option->value.u; } break; case T_Mode: if (is_refclk_addr(addr)) { my_node->ttl = option->value.u; } else { msyslog(LOG_ERR, "'mode' does not apply for network peers"); errflag = 1; } break; case T_Key: if (option->value.u >= KEYID_T_MAX) { msyslog(LOG_ERR, "key: invalid argument"); errflag = 1; } else { my_node->peerkey = (keyid_t)option->value.u; } break; case T_Version: if (option->value.u >= UCHAR_MAX) { msyslog(LOG_ERR, "version: invalid argument"); errflag = 1; } else { my_node->peerversion = (u_char)option->value.u; } break; case T_Ident: my_node->group = option->value.s; break; default: msyslog(LOG_ERR, "Unknown peer/server option token %s", token_name(option->attr)); errflag = 1; } if (freenode) free(option); } /* Check if errors were reported. If yes, ignore the node */ if (errflag) { free(my_node); my_node = NULL; } return my_node; } unpeer_node * create_unpeer_node( address_node *addr ) { unpeer_node * my_node; u_long u; const u_char * pch; my_node = emalloc_zero(sizeof(*my_node)); /* * From the parser's perspective an association ID fits into * its generic T_String definition of a name/address "address". * We treat all valid 16-bit numbers as association IDs. */ for (u = 0, pch = (u_char*)addr->address; isdigit(*pch); ++pch) { /* accumulate with overflow retention */ u = (10 * u + *pch - '0') | (u & 0xFF000000u); } if (!*pch && u <= ASSOCID_MAX) { my_node->assocID = (associd_t)u; my_node->addr = NULL; destroy_address_node(addr); } else { my_node->assocID = 0; my_node->addr = addr; } return my_node; } filegen_node * create_filegen_node( int filegen_token, attr_val_fifo * options ) { filegen_node *my_node; my_node = emalloc_zero(sizeof(*my_node)); my_node->filegen_token = filegen_token; my_node->options = options; return my_node; } restrict_node * create_restrict_node( address_node * addr, address_node * mask, short ippeerlimit, attr_val_fifo * flag_tok_fifo, int nline ) { restrict_node *my_node; my_node = emalloc_zero(sizeof(*my_node)); my_node->addr = addr; my_node->mask = mask; my_node->ippeerlimit = ippeerlimit; my_node->flag_tok_fifo = flag_tok_fifo; my_node->line_no = nline; return my_node; } static void destroy_restrict_node( restrict_node *my_node ) { /* With great care, free all the memory occupied by * the restrict node */ destroy_address_node(my_node->addr); destroy_address_node(my_node->mask); destroy_attr_val_fifo(my_node->flag_tok_fifo); free(my_node); } static void destroy_int_fifo( int_fifo * fifo ) { int_node * i_n; if (fifo != NULL) { for (;;) { UNLINK_FIFO(i_n, *fifo, link); if (i_n == NULL) break; free(i_n); } free(fifo); } } static void destroy_string_fifo( string_fifo * fifo ) { string_node * sn; if (fifo != NULL) { for (;;) { UNLINK_FIFO(sn, *fifo, link); if (sn == NULL) break; free(sn->s); free(sn); } free(fifo); } } static void destroy_attr_val_fifo( attr_val_fifo * av_fifo ) { attr_val * av; if (av_fifo != NULL) { for (;;) { UNLINK_FIFO(av, *av_fifo, link); if (av == NULL) break; destroy_attr_val(av); } free(av_fifo); } } static void destroy_filegen_fifo( filegen_fifo * fifo ) { filegen_node * fg; if (fifo != NULL) { for (;;) { UNLINK_FIFO(fg, *fifo, link); if (fg == NULL) break; destroy_attr_val_fifo(fg->options); free(fg); } free(fifo); } } static void destroy_restrict_fifo( restrict_fifo * fifo ) { restrict_node * rn; if (fifo != NULL) { for (;;) { UNLINK_FIFO(rn, *fifo, link); if (rn == NULL) break; destroy_restrict_node(rn); } free(fifo); } } static void destroy_setvar_fifo( setvar_fifo * fifo ) { setvar_node * sv; if (fifo != NULL) { for (;;) { UNLINK_FIFO(sv, *fifo, link); if (sv == NULL) break; free(sv->var); free(sv->val); free(sv); } free(fifo); } } static void destroy_addr_opts_fifo( addr_opts_fifo * fifo ) { addr_opts_node * aon; if (fifo != NULL) { for (;;) { UNLINK_FIFO(aon, *fifo, link); if (aon == NULL) break; destroy_address_node(aon->addr); destroy_attr_val_fifo(aon->options); free(aon); } free(fifo); } } setvar_node * create_setvar_node( char * var, char * val, int isdefault ) { setvar_node * my_node; char * pch; /* do not allow = in the variable name */ pch = strchr(var, '='); if (NULL != pch) *pch = '\0'; /* Now store the string into a setvar_node */ my_node = emalloc_zero(sizeof(*my_node)); my_node->var = var; my_node->val = val; my_node->isdefault = isdefault; return my_node; } nic_rule_node * create_nic_rule_node( int match_class, char *if_name, /* interface name or numeric address */ int action ) { nic_rule_node *my_node; REQUIRE(match_class != 0 || if_name != NULL); my_node = emalloc_zero(sizeof(*my_node)); my_node->match_class = match_class; my_node->if_name = if_name; my_node->action = action; return my_node; } addr_opts_node * create_addr_opts_node( address_node * addr, attr_val_fifo * options ) { addr_opts_node *my_node; my_node = emalloc_zero(sizeof(*my_node)); my_node->addr = addr; my_node->options = options; return my_node; } #ifdef SIM script_info * create_sim_script_info( double duration, attr_val_fifo * script_queue ) { script_info *my_info; attr_val *my_attr_val; my_info = emalloc_zero(sizeof(*my_info)); /* Initialize Script Info with default values*/ my_info->duration = duration; my_info->prop_delay = NET_DLY; my_info->proc_delay = PROC_DLY; /* Traverse the script_queue and fill out non-default values */ for (my_attr_val = HEAD_PFIFO(script_queue); my_attr_val != NULL; my_attr_val = my_attr_val->link) { /* Set the desired value */ switch (my_attr_val->attr) { case T_Freq_Offset: my_info->freq_offset = my_attr_val->value.d; break; case T_Wander: my_info->wander = my_attr_val->value.d; break; case T_Jitter: my_info->jitter = my_attr_val->value.d; break; case T_Prop_Delay: my_info->prop_delay = my_attr_val->value.d; break; case T_Proc_Delay: my_info->proc_delay = my_attr_val->value.d; break; default: msyslog(LOG_ERR, "Unknown script token %d", my_attr_val->attr); } } return my_info; } #endif /* SIM */ #ifdef SIM static sockaddr_u * get_next_address( address_node *addr ) { const char addr_prefix[] = "192.168.0."; static int curr_addr_num = 1; #define ADDR_LENGTH 16 + 1 /* room for 192.168.1.255 */ char addr_string[ADDR_LENGTH]; sockaddr_u *final_addr; struct addrinfo *ptr; int gai_err; final_addr = emalloc(sizeof(*final_addr)); if (addr->type == T_String) { snprintf(addr_string, sizeof(addr_string), "%s%d", addr_prefix, curr_addr_num++); printf("Selecting ip address %s for hostname %s\n", addr_string, addr->address); gai_err = getaddrinfo(addr_string, "ntp", NULL, &ptr); } else { gai_err = getaddrinfo(addr->address, "ntp", NULL, &ptr); } if (gai_err) { fprintf(stderr, "ERROR!! Could not get a new address\n"); exit(1); } memcpy(final_addr, ptr->ai_addr, ptr->ai_addrlen); fprintf(stderr, "Successful in setting ip address of simulated server to: %s\n", stoa(final_addr)); freeaddrinfo(ptr); return final_addr; } #endif /* SIM */ #ifdef SIM server_info * create_sim_server( address_node * addr, double server_offset, script_info_fifo * script ) { server_info *my_info; my_info = emalloc_zero(sizeof(*my_info)); my_info->server_time = server_offset; my_info->addr = get_next_address(addr); my_info->script = script; UNLINK_FIFO(my_info->curr_script, *my_info->script, link); return my_info; } #endif /* SIM */ sim_node * create_sim_node( attr_val_fifo * init_opts, server_info_fifo * servers ) { sim_node *my_node; my_node = emalloc(sizeof(*my_node)); my_node->init_opts = init_opts; my_node->servers = servers; return my_node; } /* FUNCTIONS FOR PERFORMING THE CONFIGURATION * ------------------------------------------ */ #ifndef SIM static void config_other_modes( config_tree * ptree ) { sockaddr_u addr_sock; address_node * addr_node; if (ptree->broadcastclient) proto_config(PROTO_BROADCLIENT, ptree->broadcastclient, 0., NULL); addr_node = HEAD_PFIFO(ptree->manycastserver); while (addr_node != NULL) { ZERO_SOCK(&addr_sock); AF(&addr_sock) = addr_node->type; if (1 == getnetnum(addr_node->address, &addr_sock, 1, t_UNK)) { proto_config(PROTO_MULTICAST_ADD, 0, 0., &addr_sock); sys_manycastserver = 1; } addr_node = addr_node->link; } /* Configure the multicast clients */ addr_node = HEAD_PFIFO(ptree->multicastclient); if (addr_node != NULL) { do { ZERO_SOCK(&addr_sock); AF(&addr_sock) = addr_node->type; if (1 == getnetnum(addr_node->address, &addr_sock, 1, t_UNK)) { proto_config(PROTO_MULTICAST_ADD, 0, 0., &addr_sock); } addr_node = addr_node->link; } while (addr_node != NULL); proto_config(PROTO_MULTICAST_ADD, 1, 0., NULL); } } #endif /* !SIM */ #ifdef FREE_CFG_T static void destroy_address_fifo( address_fifo * pfifo ) { address_node * addr_node; if (pfifo != NULL) { for (;;) { UNLINK_FIFO(addr_node, *pfifo, link); if (addr_node == NULL) break; destroy_address_node(addr_node); } free(pfifo); } } static void free_config_other_modes( config_tree *ptree ) { FREE_ADDRESS_FIFO(ptree->manycastserver); FREE_ADDRESS_FIFO(ptree->multicastclient); } #endif /* FREE_CFG_T */ #ifndef SIM static void config_auth( config_tree *ptree ) { attr_val * my_val; int first; int last; int i; int count; #ifdef AUTOKEY int item; #endif /* Crypto Command */ #ifdef AUTOKEY my_val = HEAD_PFIFO(ptree->auth.crypto_cmd_list); for (; my_val != NULL; my_val = my_val->link) { switch (my_val->attr) { default: fatal_error("config_auth: attr-token=%d", my_val->attr); case T_Host: item = CRYPTO_CONF_PRIV; break; case T_Ident: item = CRYPTO_CONF_IDENT; break; case T_Pw: item = CRYPTO_CONF_PW; break; case T_Randfile: item = CRYPTO_CONF_RAND; break; case T_Digest: item = CRYPTO_CONF_NID; break; } crypto_config(item, my_val->value.s); } #endif /* AUTOKEY */ /* Keysdir Command */ if (ptree->auth.keysdir) { if (keysdir != default_keysdir) free(keysdir); keysdir = estrdup(ptree->auth.keysdir); } /* ntp_signd_socket Command */ if (ptree->auth.ntp_signd_socket) { if (ntp_signd_socket != default_ntp_signd_socket) free(ntp_signd_socket); ntp_signd_socket = estrdup(ptree->auth.ntp_signd_socket); } #ifdef AUTOKEY if (ptree->auth.cryptosw && !cryptosw) { crypto_setup(); cryptosw = 1; } #endif /* AUTOKEY */ /* * Count the number of trusted keys to preallocate storage and * size the hash table. */ count = 0; my_val = HEAD_PFIFO(ptree->auth.trusted_key_list); for (; my_val != NULL; my_val = my_val->link) { if (T_Integer == my_val->type) { first = my_val->value.i; if (first > 1 && first <= NTP_MAXKEY) count++; } else { REQUIRE(T_Intrange == my_val->type); first = my_val->value.r.first; last = my_val->value.r.last; if (!(first > last || first < 1 || last > NTP_MAXKEY)) { count += 1 + last - first; } } } auth_prealloc_symkeys(count); /* Keys Command */ if (ptree->auth.keys) getauthkeys(ptree->auth.keys); /* Control Key Command */ if (ptree->auth.control_key) ctl_auth_keyid = (keyid_t)ptree->auth.control_key; /* Requested Key Command */ if (ptree->auth.request_key) { DPRINTF(4, ("set info_auth_keyid to %08lx\n", (u_long) ptree->auth.request_key)); info_auth_keyid = (keyid_t)ptree->auth.request_key; } /* Trusted Key Command */ my_val = HEAD_PFIFO(ptree->auth.trusted_key_list); for (; my_val != NULL; my_val = my_val->link) { if (T_Integer == my_val->type) { first = my_val->value.i; if (first >= 1 && first <= NTP_MAXKEY) { authtrust(first, TRUE); } else { msyslog(LOG_NOTICE, "Ignoring invalid trustedkey %d, min 1 max %d.", first, NTP_MAXKEY); } } else { first = my_val->value.r.first; last = my_val->value.r.last; if (first > last || first < 1 || last > NTP_MAXKEY) { msyslog(LOG_NOTICE, "Ignoring invalid trustedkey range %d ... %d, min 1 max %d.", first, last, NTP_MAXKEY); } else { for (i = first; i <= last; i++) { authtrust(i, TRUE); } } } } #ifdef AUTOKEY /* crypto revoke command */ if (ptree->auth.revoke > 2 && ptree->auth.revoke < 32) sys_revoke = (u_char)ptree->auth.revoke; else if (ptree->auth.revoke) msyslog(LOG_ERR, "'revoke' value %d ignored", ptree->auth.revoke); #endif /* AUTOKEY */ } #endif /* !SIM */ #ifdef FREE_CFG_T static void free_config_auth( config_tree *ptree ) { destroy_attr_val_fifo(ptree->auth.crypto_cmd_list); ptree->auth.crypto_cmd_list = NULL; destroy_attr_val_fifo(ptree->auth.trusted_key_list); ptree->auth.trusted_key_list = NULL; } #endif /* FREE_CFG_T */ /* Configure low-level clock-related parameters. Return TRUE if the * clock might need adjustment like era-checking after the call, FALSE * otherwise. */ static int/*BOOL*/ config_tos_clock( config_tree *ptree ) { int ret; attr_val * tos; ret = FALSE; tos = HEAD_PFIFO(ptree->orphan_cmds); for (; tos != NULL; tos = tos->link) { switch(tos->attr) { default: break; case T_Basedate: basedate_set_day(tos->value.i); ret = TRUE; break; } } if (basedate_get_day() <= NTP_TO_UNIX_DAYS) basedate_set_day(basedate_eval_buildstamp() - 11); return ret; } static void config_tos( config_tree *ptree ) { attr_val * tos; int item; double val; /* [Bug 2896] For the daemon to work properly it is essential * that minsane < minclock <= maxclock. * * If either constraint is violated, the daemon will be or might * become dysfunctional. Fixing the values is too fragile here, * since three variables with interdependecies are involved. We * just log an error but do not stop: This might be caused by * remote config, and it might be fixed by remote config, too. */ int l_maxclock = sys_maxclock; int l_minclock = sys_minclock; int l_minsane = sys_minsane; /* -*- phase one: inspect / sanitize the values */ tos = HEAD_PFIFO(ptree->orphan_cmds); for (; tos != NULL; tos = tos->link) { /* not all attributes are doubles (any more), so loading * 'val' in all cases is not a good idea: It should be * done as needed in every case processed here. */ switch(tos->attr) { default: break; case T_Bcpollbstep: val = tos->value.d; if (val > 4) { msyslog(LOG_WARNING, "Using maximum bcpollbstep ceiling %d, %d requested", 4, (int)val); tos->value.d = 4; } else if (val < 0) { msyslog(LOG_WARNING, "Using minimum bcpollbstep floor %d, %d requested", 0, (int)val); tos->value.d = 0; } break; case T_Ceiling: val = tos->value.d; if (val > STRATUM_UNSPEC - 1) { msyslog(LOG_WARNING, "Using maximum tos ceiling %d, %d requested", STRATUM_UNSPEC - 1, (int)val); tos->value.d = STRATUM_UNSPEC - 1; } else if (val < 1) { msyslog(LOG_WARNING, "Using minimum tos floor %d, %d requested", 1, (int)val); tos->value.d = 1; } break; case T_Minclock: val = tos->value.d; if ((int)tos->value.d < 1) tos->value.d = 1; l_minclock = (int)tos->value.d; break; case T_Maxclock: val = tos->value.d; if ((int)tos->value.d < 1) tos->value.d = 1; l_maxclock = (int)tos->value.d; break; case T_Minsane: val = tos->value.d; if ((int)tos->value.d < 0) tos->value.d = 0; l_minsane = (int)tos->value.d; break; } } if ( ! (l_minsane < l_minclock && l_minclock <= l_maxclock)) { msyslog(LOG_ERR, "tos error: must have minsane (%d) < minclock (%d) <= maxclock (%d)" " - daemon will not operate properly!", l_minsane, l_minclock, l_maxclock); } /* -*- phase two: forward the values to the protocol machinery */ tos = HEAD_PFIFO(ptree->orphan_cmds); for (; tos != NULL; tos = tos->link) { switch(tos->attr) { default: fatal_error("config-tos: attr-token=%d", tos->attr); case T_Bcpollbstep: item = PROTO_BCPOLLBSTEP; break; case T_Ceiling: item = PROTO_CEILING; break; case T_Floor: item = PROTO_FLOOR; break; case T_Cohort: item = PROTO_COHORT; break; case T_Orphan: item = PROTO_ORPHAN; break; case T_Orphanwait: item = PROTO_ORPHWAIT; break; case T_Mindist: item = PROTO_MINDISP; break; case T_Maxdist: item = PROTO_MAXDIST; break; case T_Minclock: item = PROTO_MINCLOCK; break; case T_Maxclock: item = PROTO_MAXCLOCK; break; case T_Minsane: item = PROTO_MINSANE; break; case T_Beacon: item = PROTO_BEACON; break; case T_Basedate: continue; /* SKIP proto-config for this! */ } proto_config(item, 0, tos->value.d, NULL); } } #ifdef FREE_CFG_T static void free_config_tos( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->orphan_cmds); } #endif /* FREE_CFG_T */ static void config_monitor( config_tree *ptree ) { int_node *pfilegen_token; const char *filegen_string; const char *filegen_file; FILEGEN *filegen; filegen_node *my_node; attr_val *my_opts; int filegen_type; int filegen_flag; /* Set the statistics directory */ if (ptree->stats_dir) stats_config(STATS_STATSDIR, ptree->stats_dir, 0); /* NOTE: * Calling filegen_get is brain dead. Doing a string * comparison to find the relavant filegen structure is * expensive. * * Through the parser, we already know which filegen is * being specified. Hence, we should either store a * pointer to the specified structure in the syntax tree * or an index into a filegen array. * * Need to change the filegen code to reflect the above. */ /* Turn on the specified statistics */ pfilegen_token = HEAD_PFIFO(ptree->stats_list); for (; pfilegen_token != NULL; pfilegen_token = pfilegen_token->link) { filegen_string = keyword(pfilegen_token->i); filegen = filegen_get(filegen_string); if (NULL == filegen) { msyslog(LOG_ERR, "stats %s unrecognized", filegen_string); continue; } DPRINTF(4, ("enabling filegen for %s statistics '%s%s'\n", filegen_string, filegen->dir, filegen->fname)); filegen_flag = filegen->flag; filegen_flag |= FGEN_FLAG_ENABLED; filegen_config(filegen, statsdir, filegen_string, filegen->type, filegen_flag); } /* Configure the statistics with the options */ my_node = HEAD_PFIFO(ptree->filegen_opts); for (; my_node != NULL; my_node = my_node->link) { filegen_string = keyword(my_node->filegen_token); filegen = filegen_get(filegen_string); if (NULL == filegen) { msyslog(LOG_ERR, "filegen category '%s' unrecognized", filegen_string); continue; } filegen_file = filegen_string; /* Initialize the filegen variables to their pre-configuration states */ filegen_flag = filegen->flag; filegen_type = filegen->type; /* "filegen ... enabled" is the default (when filegen is used) */ filegen_flag |= FGEN_FLAG_ENABLED; my_opts = HEAD_PFIFO(my_node->options); for (; my_opts != NULL; my_opts = my_opts->link) { switch (my_opts->attr) { case T_File: filegen_file = my_opts->value.s; break; case T_Type: switch (my_opts->value.i) { default: fatal_error("config-monitor: type-token=%d", my_opts->value.i); case T_None: filegen_type = FILEGEN_NONE; break; case T_Pid: filegen_type = FILEGEN_PID; break; case T_Day: filegen_type = FILEGEN_DAY; break; case T_Week: filegen_type = FILEGEN_WEEK; break; case T_Month: filegen_type = FILEGEN_MONTH; break; case T_Year: filegen_type = FILEGEN_YEAR; break; case T_Age: filegen_type = FILEGEN_AGE; break; } break; case T_Flag: switch (my_opts->value.i) { case T_Link: filegen_flag |= FGEN_FLAG_LINK; break; case T_Nolink: filegen_flag &= ~FGEN_FLAG_LINK; break; case T_Enable: filegen_flag |= FGEN_FLAG_ENABLED; break; case T_Disable: filegen_flag &= ~FGEN_FLAG_ENABLED; break; default: msyslog(LOG_ERR, "Unknown filegen flag token %d", my_opts->value.i); exit(1); } break; default: msyslog(LOG_ERR, "Unknown filegen option token %d", my_opts->attr); exit(1); } } filegen_config(filegen, statsdir, filegen_file, filegen_type, filegen_flag); } } #ifdef FREE_CFG_T static void free_config_monitor( config_tree *ptree ) { if (ptree->stats_dir) { free(ptree->stats_dir); ptree->stats_dir = NULL; } FREE_INT_FIFO(ptree->stats_list); FREE_FILEGEN_FIFO(ptree->filegen_opts); } #endif /* FREE_CFG_T */ #ifndef SIM static void config_access( config_tree *ptree ) { static int warned_signd; attr_val * my_opt; restrict_node * my_node; sockaddr_u addr; sockaddr_u mask; struct addrinfo hints; struct addrinfo * ai_list; struct addrinfo * pai; int rc; int restrict_default; u_short rflags; u_short mflags; short ippeerlimit; int range_err; psl_item my_psl_item; attr_val * atrv; attr_val * dflt_psl_atr; const char * signd_warning = #ifdef HAVE_NTP_SIGND "MS-SNTP signd operations currently block ntpd degrading service to all clients."; #else "mssntp restrict bit ignored, this ntpd was configured without --enable-ntp-signd."; #endif /* Configure the mru options */ my_opt = HEAD_PFIFO(ptree->mru_opts); for (; my_opt != NULL; my_opt = my_opt->link) { range_err = FALSE; switch (my_opt->attr) { case T_Incalloc: if (0 <= my_opt->value.i) mru_incalloc = my_opt->value.u; else range_err = TRUE; break; case T_Incmem: if (0 <= my_opt->value.i) mru_incalloc = (my_opt->value.u * 1024U) / sizeof(mon_entry); else range_err = TRUE; break; case T_Initalloc: if (0 <= my_opt->value.i) mru_initalloc = my_opt->value.u; else range_err = TRUE; break; case T_Initmem: if (0 <= my_opt->value.i) mru_initalloc = (my_opt->value.u * 1024U) / sizeof(mon_entry); else range_err = TRUE; break; case T_Mindepth: if (0 <= my_opt->value.i) mru_mindepth = my_opt->value.u; else range_err = TRUE; break; case T_Maxage: mru_maxage = my_opt->value.i; break; case T_Maxdepth: if (0 <= my_opt->value.i) mru_maxdepth = my_opt->value.u; else mru_maxdepth = UINT_MAX; break; case T_Maxmem: if (0 <= my_opt->value.i) mru_maxdepth = (my_opt->value.u * 1024U) / sizeof(mon_entry); else mru_maxdepth = UINT_MAX; break; default: msyslog(LOG_ERR, "Unknown mru option %s (%d)", keyword(my_opt->attr), my_opt->attr); exit(1); } if (range_err) msyslog(LOG_ERR, "mru %s %d out of range, ignored.", keyword(my_opt->attr), my_opt->value.i); } /* Configure the discard options */ my_opt = HEAD_PFIFO(ptree->discard_opts); for (; my_opt != NULL; my_opt = my_opt->link) { switch (my_opt->attr) { case T_Average: if (0 <= my_opt->value.i && my_opt->value.i <= UCHAR_MAX) ntp_minpoll = (u_char)my_opt->value.u; else msyslog(LOG_ERR, "discard average %d out of range, ignored.", my_opt->value.i); break; case T_Minimum: ntp_minpkt = my_opt->value.i; break; case T_Monitor: mon_age = my_opt->value.i; break; default: msyslog(LOG_ERR, "Unknown discard option %s (%d)", keyword(my_opt->attr), my_opt->attr); exit(1); } } /* Configure each line of restrict options */ my_node = HEAD_PFIFO(ptree->restrict_opts); for (; my_node != NULL; my_node = my_node->link) { /* Grab the ippeerlmit */ ippeerlimit = my_node->ippeerlimit; /* Parse the flags */ rflags = 0; mflags = 0; my_opt = HEAD_PFIFO(my_node->flag_tok_fifo); for (; my_opt != NULL; my_opt = my_opt->link) { switch (my_opt->attr) { default: fatal_error("config_access: Unknown flag-type-token=%s/%d", keyword(my_opt->attr), my_opt->attr); case T_Ntpport: mflags |= RESM_NTPONLY; break; case T_Source: mflags |= RESM_SOURCE; break; case T_Flake: rflags |= RES_FLAKE; break; case T_Ignore: rflags |= RES_IGNORE; break; case T_Kod: rflags |= RES_KOD; break; case T_Limited: rflags |= RES_LIMITED; break; case T_Lowpriotrap: rflags |= RES_LPTRAP; break; case T_Mssntp: rflags |= RES_MSSNTP; break; case T_Nomodify: rflags |= RES_NOMODIFY; break; case T_Nomrulist: rflags |= RES_NOMRULIST; break; case T_Noepeer: rflags |= RES_NOEPEER; break; case T_Nopeer: rflags |= RES_NOPEER; break; case T_Noquery: rflags |= RES_NOQUERY; break; case T_Noserve: rflags |= RES_DONTSERVE; break; case T_Notrap: rflags |= RES_NOTRAP; break; case T_Notrust: rflags |= RES_DONTTRUST; break; case T_ServerresponseFuzz: rflags |= RES_SRVRSPFUZ; break; case T_Version: rflags |= RES_VERSION; break; } } if ((RES_MSSNTP & rflags) && !warned_signd) { warned_signd = 1; fprintf(stderr, "%s\n", signd_warning); msyslog(LOG_WARNING, "%s", signd_warning); } /* It would be swell if we could identify the line number */ if ((RES_KOD & rflags) && !(RES_LIMITED & rflags)) { const char *kod_where = (my_node->addr) ? my_node->addr->address : (mflags & RESM_SOURCE) ? "source" : "default"; const char *kod_warn = "KOD does nothing without LIMITED."; fprintf(stderr, "restrict %s: %s\n", kod_where, kod_warn); msyslog(LOG_WARNING, "restrict %s: %s", kod_where, kod_warn); } ZERO_SOCK(&addr); ai_list = NULL; pai = NULL; restrict_default = 0; if (NULL == my_node->addr) { ZERO_SOCK(&mask); if (!(RESM_SOURCE & mflags)) { /* * The user specified a default rule * without a -4 / -6 qualifier, add to * both lists */ restrict_default = 1; } else { /* apply "restrict source ..." */ DPRINTF(1, ("restrict source template ippeerlimit %d mflags %x rflags %x\n", ippeerlimit, mflags, rflags)); hack_restrict(RESTRICT_FLAGS, NULL, NULL, ippeerlimit, mflags, rflags, 0); continue; } } else { /* Resolve the specified address */ AF(&addr) = (u_short)my_node->addr->type; if (getnetnum(my_node->addr->address, &addr, 1, t_UNK) != 1) { /* * Attempt a blocking lookup. This * is in violation of the nonblocking * design of ntpd's mainline code. The * alternative of running without the * restriction until the name resolved * seems worse. * Ideally some scheme could be used for * restrict directives in the startup * ntp.conf to delay starting up the * protocol machinery until after all * restrict hosts have been resolved. */ ai_list = NULL; ZERO(hints); hints.ai_protocol = IPPROTO_UDP; hints.ai_socktype = SOCK_DGRAM; hints.ai_family = my_node->addr->type; rc = getaddrinfo(my_node->addr->address, "ntp", &hints, &ai_list); if (rc) { msyslog(LOG_ERR, "restrict: ignoring line %d, address/host '%s' unusable.", my_node->line_no, my_node->addr->address); continue; } INSIST(ai_list != NULL); pai = ai_list; INSIST(pai->ai_addr != NULL); INSIST(sizeof(addr) >= pai->ai_addrlen); memcpy(&addr, pai->ai_addr, pai->ai_addrlen); INSIST(AF_INET == AF(&addr) || AF_INET6 == AF(&addr)); } SET_HOSTMASK(&mask, AF(&addr)); /* Resolve the mask */ if (my_node->mask) { ZERO_SOCK(&mask); AF(&mask) = my_node->mask->type; if (getnetnum(my_node->mask->address, &mask, 1, t_MSK) != 1) { msyslog(LOG_ERR, "restrict: ignoring line %d, mask '%s' unusable.", my_node->line_no, my_node->mask->address); continue; } } } /* Set the flags */ if (restrict_default) { AF(&addr) = AF_INET; AF(&mask) = AF_INET; hack_restrict(RESTRICT_FLAGS, &addr, &mask, ippeerlimit, mflags, rflags, 0); AF(&addr) = AF_INET6; AF(&mask) = AF_INET6; } do { hack_restrict(RESTRICT_FLAGS, &addr, &mask, ippeerlimit, mflags, rflags, 0); if (pai != NULL && NULL != (pai = pai->ai_next)) { INSIST(pai->ai_addr != NULL); INSIST(sizeof(addr) >= pai->ai_addrlen); ZERO_SOCK(&addr); memcpy(&addr, pai->ai_addr, pai->ai_addrlen); INSIST(AF_INET == AF(&addr) || AF_INET6 == AF(&addr)); SET_HOSTMASK(&mask, AF(&addr)); } } while (pai != NULL); if (ai_list != NULL) freeaddrinfo(ai_list); } /* Deal with the Poll Skew List */ ZERO(psl); ZERO(my_psl_item); /* * First, find the last default pollskewlist item. * There should only be one of these with the current grammar, * but better safe than sorry. */ dflt_psl_atr = NULL; atrv = HEAD_PFIFO(ptree->pollskewlist); for ( ; atrv != NULL; atrv = atrv->link) { switch (atrv->attr) { case -1: /* default */ dflt_psl_atr = atrv; break; case 3: /* Fall through */ case 4: /* Fall through */ case 5: /* Fall through */ case 6: /* Fall through */ case 7: /* Fall through */ case 8: /* Fall through */ case 9: /* Fall through */ case 10: /* Fall through */ case 11: /* Fall through */ case 12: /* Fall through */ case 13: /* Fall through */ case 14: /* Fall through */ case 15: /* Fall through */ case 16: /* Fall through */ case 17: /* ignore */ break; default: msyslog(LOG_ERR, "config_access: default PSL scan: ignoring unexpected poll value %d", atrv->attr); break; } } /* If we have a nonzero default, initialize the PSL */ if ( dflt_psl_atr && ( 0 != dflt_psl_atr->value.r.first || 0 != dflt_psl_atr->value.r.last)) { int i; for (i = 3; i <= 17; ++i) { attrtopsl(i, dflt_psl_atr); } } /* Finally, update the PSL with any explicit entries */ atrv = HEAD_PFIFO(ptree->pollskewlist); for ( ; atrv != NULL; atrv = atrv->link) { switch (atrv->attr) { case -1: /* default */ /* Ignore */ break; case 3: /* Fall through */ case 4: /* Fall through */ case 5: /* Fall through */ case 6: /* Fall through */ case 7: /* Fall through */ case 8: /* Fall through */ case 9: /* Fall through */ case 10: /* Fall through */ case 11: /* Fall through */ case 12: /* Fall through */ case 13: /* Fall through */ case 14: /* Fall through */ case 15: /* Fall through */ case 16: /* Fall through */ case 17: attrtopsl(atrv->attr, atrv); break; default: break; /* Ignore - we reported this above */ } } #if 0 int p; msyslog(LOG_INFO, "Dumping PSL:"); for (p = 3; p <= 17; ++p) { psl_item psi; if (0 == get_pollskew(p, &psi)) { msyslog(LOG_INFO, "poll %d: sub %d, qty %d, msk %d", p, psi.sub, psi.qty, psi.msk); } else { msyslog(LOG_ERR, "Dumping PSL: get_pollskew(%d) failed!", p); } } #endif } void attrtopsl(int poll, attr_val *avp) { DEBUG_INSIST((poll - 3) < sizeof psl); if (poll < 3 || poll > 17) { msyslog(LOG_ERR, "attrtopsl(%d, ...): Poll value is out of range - ignoring", poll); } else { int pao = poll - 3; /* poll array offset */ int lower = avp->value.r.first; /* a positive number */ int upper = avp->value.r.last; int psmax = 1 << (poll - 1); int qmsk; if (lower > psmax) { msyslog(LOG_WARNING, "attrtopsl: default: poll %d lower bound reduced from %d to %d", poll, lower, psmax); lower = psmax; } if (upper > psmax) { msyslog(LOG_WARNING, "attrtopsl: default: poll %d upper bound reduced from %d to %d", poll, upper, psmax); upper = psmax; } psl[pao].sub = lower; psl[pao].qty = lower + upper; qmsk = 1; while (qmsk < (lower + upper)) { qmsk <<= 1; qmsk |= 1; }; psl[pao].msk = qmsk; } return; } #endif /* !SIM */ int get_pollskew( int p, psl_item *rv ) { DEBUG_INSIST(3 <= p && 17 >= p); if (3 <= p && 17 >= p) { *rv = psl[p - 3]; return 0; } else { msyslog(LOG_ERR, "get_pollskew(%d): poll is not between 3 and 17!", p); return -1; } /* NOTREACHED */ } #ifdef FREE_CFG_T static void free_config_access( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->mru_opts); FREE_ATTR_VAL_FIFO(ptree->discard_opts); FREE_RESTRICT_FIFO(ptree->restrict_opts); } #endif /* FREE_CFG_T */ static void config_rlimit( config_tree *ptree ) { attr_val * rlimit_av; rlimit_av = HEAD_PFIFO(ptree->rlimit); for (; rlimit_av != NULL; rlimit_av = rlimit_av->link) { switch (rlimit_av->attr) { default: fatal_error("config-rlimit: value-token=%d", rlimit_av->attr); case T_Memlock: /* What if we HAVE_OPT(SAVECONFIGQUIT) ? */ if (HAVE_OPT( SAVECONFIGQUIT )) { break; } if (rlimit_av->value.i == -1) { # if defined(HAVE_MLOCKALL) if (cur_memlock != 0) { if (-1 == munlockall()) { msyslog(LOG_ERR, "munlockall() failed: %m"); } } cur_memlock = 0; # endif /* HAVE_MLOCKALL */ } else if (rlimit_av->value.i >= 0) { #if defined(RLIMIT_MEMLOCK) # if defined(HAVE_MLOCKALL) if (cur_memlock != 1) { if (-1 == mlockall(MCL_CURRENT|MCL_FUTURE)) { msyslog(LOG_ERR, "mlockall() failed: %m"); } } # endif /* HAVE_MLOCKALL */ ntp_rlimit(RLIMIT_MEMLOCK, (rlim_t)(rlimit_av->value.i * 1024 * 1024), 1024 * 1024, "MB"); cur_memlock = 1; #else /* STDERR as well would be fine... */ msyslog(LOG_WARNING, "'rlimit memlock' specified but is not available on this system."); #endif /* RLIMIT_MEMLOCK */ } else { msyslog(LOG_WARNING, "'rlimit memlock' value of %d is unexpected!", rlimit_av->value.i); } break; case T_Stacksize: #if defined(RLIMIT_STACK) ntp_rlimit(RLIMIT_STACK, (rlim_t)(rlimit_av->value.i * 4096), 4096, "4k"); #else /* STDERR as well would be fine... */ msyslog(LOG_WARNING, "'rlimit stacksize' specified but is not available on this system."); #endif /* RLIMIT_STACK */ break; case T_Filenum: #if defined(RLIMIT_NOFILE) ntp_rlimit(RLIMIT_NOFILE, (rlim_t)(rlimit_av->value.i), 1, ""); #else /* STDERR as well would be fine... */ msyslog(LOG_WARNING, "'rlimit filenum' specified but is not available on this system."); #endif /* RLIMIT_NOFILE */ break; } } } static void config_tinker( config_tree *ptree ) { attr_val * tinker; int item; tinker = HEAD_PFIFO(ptree->tinker); for (; tinker != NULL; tinker = tinker->link) { switch (tinker->attr) { default: fatal_error("config_tinker: attr-token=%d", tinker->attr); case T_Allan: item = LOOP_ALLAN; break; case T_Dispersion: item = LOOP_PHI; break; case T_Freq: item = LOOP_FREQ; break; case T_Huffpuff: item = LOOP_HUFFPUFF; break; case T_Panic: item = LOOP_PANIC; break; case T_Step: item = LOOP_MAX; break; case T_Stepback: item = LOOP_MAX_BACK; break; case T_Stepfwd: item = LOOP_MAX_FWD; break; case T_Stepout: item = LOOP_MINSTEP; break; case T_Tick: item = LOOP_TICK; break; } loop_config(item, tinker->value.d); } } #ifdef FREE_CFG_T static void free_config_rlimit( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->rlimit); } static void free_config_tinker( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->tinker); } #endif /* FREE_CFG_T */ /* * config_nic_rules - apply interface listen/ignore/drop items */ #ifndef SIM static void config_nic_rules( config_tree *ptree, int/*BOOL*/ input_from_file ) { nic_rule_node * curr_node; sockaddr_u addr; nic_rule_match match_type; nic_rule_action action; char * if_name; char * pchSlash; int prefixlen; int addrbits; curr_node = HEAD_PFIFO(ptree->nic_rules); if (curr_node != NULL && (HAVE_OPT( NOVIRTUALIPS ) || HAVE_OPT( INTERFACE ))) { msyslog(LOG_ERR, "interface/nic rules are not allowed with --interface (-I) or --novirtualips (-L)%s", (input_from_file) ? ", exiting" : ""); if (input_from_file) exit(1); else return; } for (; curr_node != NULL; curr_node = curr_node->link) { prefixlen = -1; if_name = curr_node->if_name; if (if_name != NULL) if_name = estrdup(if_name); switch (curr_node->match_class) { default: fatal_error("config_nic_rules: match-class-token=%d", curr_node->match_class); case 0: /* * 0 is out of range for valid token T_... * and in a nic_rules_node indicates the * interface descriptor is either a name or * address, stored in if_name in either case. */ INSIST(if_name != NULL); pchSlash = strchr(if_name, '/'); if (pchSlash != NULL) *pchSlash = '\0'; if (is_ip_address(if_name, AF_UNSPEC, &addr)) { match_type = MATCH_IFADDR; if (pchSlash != NULL && 1 == sscanf(pchSlash + 1, "%d", &prefixlen)) { addrbits = 8 * SIZEOF_INADDR(AF(&addr)); prefixlen = max(-1, prefixlen); prefixlen = min(prefixlen, addrbits); } } else { match_type = MATCH_IFNAME; if (pchSlash != NULL) *pchSlash = '/'; } break; case T_All: match_type = MATCH_ALL; break; case T_Ipv4: match_type = MATCH_IPV4; break; case T_Ipv6: match_type = MATCH_IPV6; break; case T_Wildcard: match_type = MATCH_WILDCARD; break; } switch (curr_node->action) { default: fatal_error("config_nic_rules: action-token=%d", curr_node->action); case T_Listen: action = ACTION_LISTEN; break; case T_Ignore: action = ACTION_IGNORE; break; case T_Drop: action = ACTION_DROP; break; } add_nic_rule(match_type, if_name, prefixlen, action); timer_interfacetimeout(current_time + 2); if (if_name != NULL) free(if_name); } } #endif /* !SIM */ #ifdef FREE_CFG_T static void free_config_nic_rules( config_tree *ptree ) { nic_rule_node *curr_node; if (ptree->nic_rules != NULL) { for (;;) { UNLINK_FIFO(curr_node, *ptree->nic_rules, link); if (NULL == curr_node) break; free(curr_node->if_name); free(curr_node); } free(ptree->nic_rules); ptree->nic_rules = NULL; } } #endif /* FREE_CFG_T */ static void apply_enable_disable( attr_val_fifo * fifo, int enable ) { attr_val *curr_tok_fifo; int option; #ifdef BC_LIST_FRAMEWORK_NOT_YET_USED bc_entry *pentry; #endif for (curr_tok_fifo = HEAD_PFIFO(fifo); curr_tok_fifo != NULL; curr_tok_fifo = curr_tok_fifo->link) { option = curr_tok_fifo->value.i; switch (option) { default: msyslog(LOG_ERR, "can not apply enable/disable token %d, unknown", option); break; case T_Auth: proto_config(PROTO_AUTHENTICATE, enable, 0., NULL); break; case T_Bclient: proto_config(PROTO_BROADCLIENT, enable, 0., NULL); break; case T_Calibrate: proto_config(PROTO_CAL, enable, 0., NULL); break; case T_Kernel: proto_config(PROTO_KERNEL, enable, 0., NULL); break; case T_Monitor: proto_config(PROTO_MONITOR, enable, 0., NULL); break; case T_Mode7: proto_config(PROTO_MODE7, enable, 0., NULL); break; case T_Ntp: proto_config(PROTO_NTP, enable, 0., NULL); break; case T_PCEdigest: proto_config(PROTO_PCEDIGEST, enable, 0., NULL); break; case T_Stats: proto_config(PROTO_FILEGEN, enable, 0., NULL); break; case T_UEcrypto: proto_config(PROTO_UECRYPTO, enable, 0., NULL); break; case T_UEcryptonak: proto_config(PROTO_UECRYPTONAK, enable, 0., NULL); break; case T_UEdigest: proto_config(PROTO_UEDIGEST, enable, 0., NULL); break; #ifdef BC_LIST_FRAMEWORK_NOT_YET_USED case T_Bc_bugXXXX: pentry = bc_list; while (pentry->token) { if (pentry->token == option) break; pentry++; } if (!pentry->token) { msyslog(LOG_ERR, "compat token %d not in bc_list[]", option); continue; } pentry->enabled = enable; break; #endif } } } static void config_system_opts( config_tree *ptree ) { apply_enable_disable(ptree->enable_opts, 1); apply_enable_disable(ptree->disable_opts, 0); } #ifdef FREE_CFG_T static void free_config_system_opts( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->enable_opts); FREE_ATTR_VAL_FIFO(ptree->disable_opts); } #endif /* FREE_CFG_T */ static void config_logconfig( config_tree *ptree ) { attr_val * my_lc; my_lc = HEAD_PFIFO(ptree->logconfig); for (; my_lc != NULL; my_lc = my_lc->link) { switch (my_lc->attr) { case '+': ntp_syslogmask |= get_logmask(my_lc->value.s); break; case '-': ntp_syslogmask &= ~get_logmask(my_lc->value.s); break; case '=': ntp_syslogmask = get_logmask(my_lc->value.s); break; default: fatal_error("config-logconfig: modifier='%c'", my_lc->attr); } } } #ifdef FREE_CFG_T static void free_config_logconfig( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->logconfig); } #endif /* FREE_CFG_T */ #ifndef SIM static void config_phone( config_tree *ptree ) { size_t i; string_node * sn; i = 0; sn = HEAD_PFIFO(ptree->phone); for (; sn != NULL; sn = sn->link) { /* need to leave array entry for NULL terminator */ if (i < COUNTOF(sys_phone) - 1) { sys_phone[i++] = estrdup(sn->s); sys_phone[i] = NULL; } else { msyslog(LOG_INFO, "phone: Number of phone entries exceeds %zu. Ignoring phone %s...", (COUNTOF(sys_phone) - 1), sn->s); } } } #endif /* !SIM */ static void config_mdnstries( config_tree *ptree ) { #ifdef HAVE_DNSREGISTRATION extern int mdnstries; mdnstries = ptree->mdnstries; #endif /* HAVE_DNSREGISTRATION */ } #ifdef FREE_CFG_T static void free_config_phone( config_tree *ptree ) { FREE_STRING_FIFO(ptree->phone); } #endif /* FREE_CFG_T */ #ifndef SIM static void config_setvar( config_tree *ptree ) { setvar_node *my_node; size_t varlen, vallen, octets; char * str; str = NULL; my_node = HEAD_PFIFO(ptree->setvar); for (; my_node != NULL; my_node = my_node->link) { varlen = strlen(my_node->var); vallen = strlen(my_node->val); octets = varlen + vallen + 1 + 1; str = erealloc(str, octets); snprintf(str, octets, "%s=%s", my_node->var, my_node->val); set_sys_var(str, octets, (my_node->isdefault) ? DEF : 0); } if (str != NULL) free(str); } #endif /* !SIM */ #ifdef FREE_CFG_T static void free_config_setvar( config_tree *ptree ) { FREE_SETVAR_FIFO(ptree->setvar); } #endif /* FREE_CFG_T */ #ifndef SIM static void config_ttl( config_tree *ptree ) { size_t i = 0; int_node *curr_ttl; /* [Bug 3465] There is a built-in default for the TTLs. We must * overwrite 'sys_ttlmax' if we change that preset, and leave it * alone otherwise! */ curr_ttl = HEAD_PFIFO(ptree->ttl); for (; curr_ttl != NULL; curr_ttl = curr_ttl->link) { if (i < COUNTOF(sys_ttl)) sys_ttl[i++] = (u_char)curr_ttl->i; else msyslog(LOG_INFO, "ttl: Number of TTL entries exceeds %zu. Ignoring TTL %d...", COUNTOF(sys_ttl), curr_ttl->i); } if (0 != i) /* anything written back at all? */ sys_ttlmax = i - 1; } #endif /* !SIM */ #ifdef FREE_CFG_T static void free_config_ttl( config_tree *ptree ) { FREE_INT_FIFO(ptree->ttl); } #endif /* FREE_CFG_T */ #ifndef SIM static void config_trap( config_tree *ptree ) { addr_opts_node *curr_trap; attr_val *curr_opt; sockaddr_u addr_sock; sockaddr_u peeraddr; struct interface *localaddr; struct addrinfo hints; char port_text[8]; settrap_parms *pstp; u_short port; int err_flag; int rc; /* silence warning about addr_sock potentially uninitialized */ AF(&addr_sock) = AF_UNSPEC; curr_trap = HEAD_PFIFO(ptree->trap); for (; curr_trap != NULL; curr_trap = curr_trap->link) { err_flag = 0; port = 0; localaddr = NULL; curr_opt = HEAD_PFIFO(curr_trap->options); for (; curr_opt != NULL; curr_opt = curr_opt->link) { if (T_Port == curr_opt->attr) { if (curr_opt->value.i < 1 || curr_opt->value.i > USHRT_MAX) { msyslog(LOG_ERR, "invalid port number " "%d, trap ignored", curr_opt->value.i); err_flag = 1; } port = (u_short)curr_opt->value.i; } else if (T_Interface == curr_opt->attr) { /* Resolve the interface address */ ZERO_SOCK(&addr_sock); if (getnetnum(curr_opt->value.s, &addr_sock, 1, t_UNK) != 1) { err_flag = 1; break; } localaddr = findinterface(&addr_sock); if (NULL == localaddr) { msyslog(LOG_ERR, "can't find interface with address %s", stoa(&addr_sock)); err_flag = 1; } } } /* Now process the trap for the specified interface * and port number */ if (!err_flag) { if (!port) port = TRAPPORT; ZERO_SOCK(&peeraddr); rc = getnetnum(curr_trap->addr->address, &peeraddr, 1, t_UNK); if (1 != rc) { #ifndef WORKER msyslog(LOG_ERR, "trap: unable to use IP address %s.", curr_trap->addr->address); #else /* WORKER follows */ /* * save context and hand it off * for name resolution. */ ZERO(hints); hints.ai_protocol = IPPROTO_UDP; hints.ai_socktype = SOCK_DGRAM; snprintf(port_text, sizeof(port_text), "%u", port); hints.ai_flags = Z_AI_NUMERICSERV; pstp = emalloc_zero(sizeof(*pstp)); if (localaddr != NULL) { hints.ai_family = localaddr->family; pstp->ifaddr_nonnull = 1; memcpy(&pstp->ifaddr, &localaddr->sin, sizeof(pstp->ifaddr)); } rc = getaddrinfo_sometime( curr_trap->addr->address, port_text, &hints, INITIAL_DNS_RETRY, &trap_name_resolved, pstp); if (!rc) msyslog(LOG_ERR, "config_trap: getaddrinfo_sometime(%s,%s): %m", curr_trap->addr->address, port_text); #endif /* WORKER */ continue; } /* port is at same location for v4 and v6 */ SET_PORT(&peeraddr, port); if (NULL == localaddr) localaddr = ANY_INTERFACE_CHOOSE(&peeraddr); else AF(&peeraddr) = AF(&addr_sock); if (!ctlsettrap(&peeraddr, localaddr, 0, NTP_VERSION)) msyslog(LOG_ERR, "set trap %s -> %s failed.", latoa(localaddr), stoa(&peeraddr)); } } } /* * trap_name_resolved() * * Callback invoked when config_trap()'s DNS lookup completes. */ # ifdef WORKER static void trap_name_resolved( int rescode, int gai_errno, void * context, const char * name, const char * service, const struct addrinfo * hints, const struct addrinfo * res ) { settrap_parms *pstp; struct interface *localaddr; sockaddr_u peeraddr; (void)gai_errno; (void)service; (void)hints; pstp = context; if (rescode) { msyslog(LOG_ERR, "giving up resolving trap host %s: %s (%d)", name, gai_strerror(rescode), rescode); free(pstp); return; } INSIST(sizeof(peeraddr) >= res->ai_addrlen); ZERO(peeraddr); memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); localaddr = NULL; if (pstp->ifaddr_nonnull) localaddr = findinterface(&pstp->ifaddr); if (NULL == localaddr) localaddr = ANY_INTERFACE_CHOOSE(&peeraddr); if (!ctlsettrap(&peeraddr, localaddr, 0, NTP_VERSION)) msyslog(LOG_ERR, "set trap %s -> %s failed.", latoa(localaddr), stoa(&peeraddr)); free(pstp); } # endif /* WORKER */ #endif /* !SIM */ #ifdef FREE_CFG_T static void free_config_trap( config_tree *ptree ) { FREE_ADDR_OPTS_FIFO(ptree->trap); } #endif /* FREE_CFG_T */ #ifndef SIM static void config_fudge( config_tree *ptree ) { addr_opts_node *curr_fudge; attr_val *curr_opt; sockaddr_u addr_sock; address_node *addr_node; struct refclockstat clock_stat; int err_flag; curr_fudge = HEAD_PFIFO(ptree->fudge); for (; curr_fudge != NULL; curr_fudge = curr_fudge->link) { err_flag = 0; /* Get the reference clock address and * ensure that it is sane */ addr_node = curr_fudge->addr; ZERO_SOCK(&addr_sock); if (getnetnum(addr_node->address, &addr_sock, 1, t_REF) != 1) { err_flag = 1; msyslog(LOG_ERR, "unrecognized fudge reference clock address %s, line ignored", addr_node->address); } else if (!ISREFCLOCKADR(&addr_sock)) { err_flag = 1; msyslog(LOG_ERR, "inappropriate address %s for the fudge command, line ignored", stoa(&addr_sock)); } /* Parse all the options to the fudge command */ ZERO(clock_stat); /* some things are not necessarily cleared by ZERO...*/ clock_stat.fudgeminjitter = 0.0; clock_stat.fudgetime1 = 0.0; clock_stat.fudgetime2 = 0.0; clock_stat.p_lastcode = NULL; clock_stat.clockdesc = NULL; clock_stat.kv_list = NULL; curr_opt = HEAD_PFIFO(curr_fudge->options); for (; curr_opt != NULL; curr_opt = curr_opt->link) { switch (curr_opt->attr) { case T_Time1: clock_stat.haveflags |= CLK_HAVETIME1; clock_stat.fudgetime1 = curr_opt->value.d; break; case T_Time2: clock_stat.haveflags |= CLK_HAVETIME2; clock_stat.fudgetime2 = curr_opt->value.d; break; case T_Stratum: clock_stat.haveflags |= CLK_HAVEVAL1; clock_stat.fudgeval1 = curr_opt->value.i; break; case T_Refid: clock_stat.haveflags |= CLK_HAVEVAL2; /* strncpy() does exactly what we want here: */ strncpy((char*)&clock_stat.fudgeval2, curr_opt->value.s, 4); break; case T_Flag1: clock_stat.haveflags |= CLK_HAVEFLAG1; if (curr_opt->value.i) clock_stat.flags |= CLK_FLAG1; else clock_stat.flags &= ~CLK_FLAG1; break; case T_Flag2: clock_stat.haveflags |= CLK_HAVEFLAG2; if (curr_opt->value.i) clock_stat.flags |= CLK_FLAG2; else clock_stat.flags &= ~CLK_FLAG2; break; case T_Flag3: clock_stat.haveflags |= CLK_HAVEFLAG3; if (curr_opt->value.i) clock_stat.flags |= CLK_FLAG3; else clock_stat.flags &= ~CLK_FLAG3; break; case T_Flag4: clock_stat.haveflags |= CLK_HAVEFLAG4; if (curr_opt->value.i) clock_stat.flags |= CLK_FLAG4; else clock_stat.flags &= ~CLK_FLAG4; break; case T_Minjitter: clock_stat.haveflags |= CLK_HAVEMINJIT; clock_stat.fudgeminjitter = curr_opt->value.d; break; default: msyslog(LOG_ERR, "Unexpected fudge flag %s (%d) for %s", token_name(curr_opt->attr), curr_opt->attr, addr_node->address); exit(curr_opt->attr ? curr_opt->attr : 1); } } # ifdef REFCLOCK if (!err_flag) refclock_control(&addr_sock, &clock_stat, NULL); # endif } } #endif /* !SIM */ #ifdef FREE_CFG_T static void free_config_fudge( config_tree *ptree ) { FREE_ADDR_OPTS_FIFO(ptree->fudge); } #endif /* FREE_CFG_T */ static void config_vars( config_tree *ptree ) { attr_val *curr_var; int len; curr_var = HEAD_PFIFO(ptree->vars); for (; curr_var != NULL; curr_var = curr_var->link) { /* Determine which variable to set and set it */ switch (curr_var->attr) { case T_Broadcastdelay: proto_config(PROTO_BROADDELAY, 0, curr_var->value.d, NULL); break; case T_Tick: loop_config(LOOP_TICK, curr_var->value.d); break; case T_Driftfile: if ('\0' == curr_var->value.s[0]) { stats_drift_file = 0; msyslog(LOG_INFO, "config: driftfile disabled"); } else stats_config(STATS_FREQ_FILE, curr_var->value.s, 0); break; case T_Dscp: /* DSCP is in the upper 6 bits of the IP TOS/DS field */ qos = curr_var->value.i << 2; break; case T_Ident: sys_ident = curr_var->value.s; break; case T_WanderThreshold: /* FALLTHROUGH */ case T_Nonvolatile: wander_threshold = curr_var->value.d; break; case T_Leapfile: stats_config(STATS_LEAP_FILE, curr_var->value.s, curr_var->flag); break; #ifdef LEAP_SMEAR case T_Leapsmearinterval: leap_smear_intv = curr_var->value.i; msyslog(LOG_INFO, "config: leap smear interval %i s", leap_smear_intv); break; #endif case T_Pidfile: stats_config(STATS_PID_FILE, curr_var->value.s, 0); break; case T_Logfile: if (-1 == change_logfile(curr_var->value.s, TRUE)) msyslog(LOG_ERR, "Cannot open logfile %s: %m", curr_var->value.s); break; case T_Saveconfigdir: if (saveconfigdir != NULL) free(saveconfigdir); len = strlen(curr_var->value.s); if (0 == len) { saveconfigdir = NULL; } else if (DIR_SEP != curr_var->value.s[len - 1] #ifdef SYS_WINNT /* slash is also a dir. sep. on Windows */ && '/' != curr_var->value.s[len - 1] #endif ) { len++; saveconfigdir = emalloc(len + 1); snprintf(saveconfigdir, len + 1, "%s%c", curr_var->value.s, DIR_SEP); } else { saveconfigdir = estrdup( curr_var->value.s); } break; case T_Automax: #ifdef AUTOKEY if (curr_var->value.i > 2 && curr_var->value.i < 32) sys_automax = (u_char)curr_var->value.i; else msyslog(LOG_ERR, "'automax' value %d ignored", curr_var->value.i); #endif break; default: msyslog(LOG_ERR, "config_vars(): unexpected token %d", curr_var->attr); } } } #ifdef FREE_CFG_T static void free_config_vars( config_tree *ptree ) { FREE_ATTR_VAL_FIFO(ptree->vars); } #endif /* FREE_CFG_T */ /* Define a function to check if a resolved address is sane. * If yes, return 1, else return 0; */ static int is_sane_resolved_address( sockaddr_u * peeraddr, int hmode ) { if (!ISREFCLOCKADR(peeraddr) && ISBADADR(peeraddr)) { msyslog(LOG_ERR, "attempt to configure invalid address %s", stoa(peeraddr)); return 0; } /* * Shouldn't be able to specify: * - multicast address for server/peer! * - unicast address for manycastclient! */ if ((T_Server == hmode || T_Peer == hmode || T_Pool == hmode) && IS_MCAST(peeraddr)) { msyslog(LOG_ERR, "attempt to configure invalid address %s", stoa(peeraddr)); return 0; } if (T_Manycastclient == hmode && !IS_MCAST(peeraddr)) { msyslog(LOG_ERR, "attempt to configure invalid address %s", stoa(peeraddr)); return 0; } if (IS_IPV6(peeraddr) && !ipv6_works) return 0; /* Ok, all tests succeeded, now we can return 1 */ return 1; } #ifndef SIM static u_char get_correct_host_mode( int token ) { switch (token) { case T_Server: case T_Pool: case T_Manycastclient: return MODE_CLIENT; case T_Peer: return MODE_ACTIVE; case T_Broadcast: return MODE_BROADCAST; default: return 0; } } /* * peerflag_bits() get config_peers() peerflags value from a * peer_node's queue of flag attr_val entries. */ static int peerflag_bits( peer_node *pn ) { int peerflags; attr_val *option; int hmode; DEBUG_INSIST(pn); /* translate peerflags options to bits */ peerflags = 0; hmode = pn->host_mode; option = HEAD_PFIFO(pn->peerflags); for (; option != NULL; option = option->link) { switch (option->value.i) { default: fatal_error("peerflag_bits: option-token=%d", option->value.i); case T_Autokey: peerflags |= FLAG_SKEY; break; case T_Burst: peerflags |= FLAG_BURST; break; case T_Iburst: peerflags |= FLAG_IBURST; break; case T_Noselect: peerflags |= FLAG_NOSELECT; break; case T_Preempt: peerflags |= FLAG_PREEMPT; break; case T_Prefer: peerflags |= FLAG_PREFER; break; case T_True: peerflags |= FLAG_TRUE; break; case T_Xleave: peerflags |= FLAG_XLEAVE; break; case T_Xmtnonce: if ( MODE_CLIENT == hmode ) { peerflags |= FLAG_LOOPNONCE; } break; } } return peerflags; } static void config_peers( config_tree *ptree ) { sockaddr_u peeraddr; struct addrinfo hints; peer_node * curr_peer; peer_resolved_ctx * ctx; u_char hmode; /* add servers named on the command line with iburst implied */ for (; cmdline_server_count > 0; cmdline_server_count--, cmdline_servers++) { ZERO_SOCK(&peeraddr); /* * If we have a numeric address, we can safely * proceed in the mainline with it. Otherwise, hand * the hostname off to the blocking child. * * Note that if we're told to add the peer here, we * do that regardless of ippeerlimit. */ if (is_ip_address(*cmdline_servers, AF_UNSPEC, &peeraddr)) { SET_PORT(&peeraddr, NTP_PORT); if (is_sane_resolved_address(&peeraddr, T_Server)) peer_config( &peeraddr, NULL, NULL, -1, MODE_CLIENT, NTP_VERSION, 0, 0, FLAG_IBURST, 0, 0, NULL); } else { /* we have a hostname to resolve */ # ifdef WORKER ctx = emalloc_zero(sizeof(*ctx)); ctx->family = AF_UNSPEC; ctx->host_mode = T_Server; ctx->hmode = MODE_CLIENT; ctx->version = NTP_VERSION; ctx->flags = FLAG_IBURST; ZERO(hints); hints.ai_family = (u_short)ctx->family; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; getaddrinfo_sometime_ex(*cmdline_servers, "ntp", &hints, INITIAL_DNS_RETRY, &peer_name_resolved, (void *)ctx, DNSFLAGS); # else /* !WORKER follows */ msyslog(LOG_ERR, "hostname %s can not be used, please use IP address instead.", curr_peer->addr->address); # endif } } /* add associations from the configuration file */ curr_peer = HEAD_PFIFO(ptree->peers); for (; curr_peer != NULL; curr_peer = curr_peer->link) { ZERO_SOCK(&peeraddr); /* Find the correct host-mode */ hmode = get_correct_host_mode(curr_peer->host_mode); INSIST(hmode != 0); if (T_Pool == curr_peer->host_mode) { AF(&peeraddr) = curr_peer->addr->type; peer_config( &peeraddr, curr_peer->addr->address, NULL, -1, hmode, curr_peer->peerversion, curr_peer->minpoll, curr_peer->maxpoll, peerflag_bits(curr_peer), curr_peer->ttl, curr_peer->peerkey, curr_peer->group); /* * If we have a numeric address, we can safely * proceed in the mainline with it. Otherwise, hand * the hostname off to the blocking child. */ } else if (is_ip_address(curr_peer->addr->address, curr_peer->addr->type, &peeraddr)) { SET_PORT(&peeraddr, NTP_PORT); if (is_sane_resolved_address(&peeraddr, curr_peer->host_mode)) peer_config( &peeraddr, NULL, NULL, -1, hmode, curr_peer->peerversion, curr_peer->minpoll, curr_peer->maxpoll, peerflag_bits(curr_peer), curr_peer->ttl, curr_peer->peerkey, curr_peer->group); } else { /* we have a hostname to resolve */ # ifdef WORKER ctx = emalloc_zero(sizeof(*ctx)); ctx->family = curr_peer->addr->type; ctx->host_mode = curr_peer->host_mode; ctx->hmode = hmode; ctx->version = curr_peer->peerversion; ctx->minpoll = curr_peer->minpoll; ctx->maxpoll = curr_peer->maxpoll; ctx->flags = peerflag_bits(curr_peer); ctx->ttl = curr_peer->ttl; ctx->keyid = curr_peer->peerkey; ctx->group = curr_peer->group; ZERO(hints); hints.ai_family = ctx->family; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; getaddrinfo_sometime_ex(curr_peer->addr->address, "ntp", &hints, INITIAL_DNS_RETRY, &peer_name_resolved, ctx, DNSFLAGS); # else /* !WORKER follows */ msyslog(LOG_ERR, "hostname %s can not be used, please use IP address instead.", curr_peer->addr->address); # endif } } } #endif /* !SIM */ /* * peer_name_resolved() * * Callback invoked when config_peers()'s DNS lookup completes. */ #ifdef WORKER static void peer_name_resolved( int rescode, int gai_errno, void * context, const char * name, const char * service, const struct addrinfo * hints, const struct addrinfo * res ) { sockaddr_u peeraddr; peer_resolved_ctx * ctx; u_short af; const char * fam_spec; (void)gai_errno; (void)service; (void)hints; ctx = context; DPRINTF(1, ("peer_name_resolved(%s) rescode %d\n", name, rescode)); if (rescode) { free(ctx); msyslog(LOG_ERR, "giving up resolving host %s: %s (%d)", name, gai_strerror(rescode), rescode); return; } /* Loop to configure a single association */ for (; res != NULL; res = res->ai_next) { memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); if (is_sane_resolved_address(&peeraddr, ctx->host_mode)) { NLOG(NLOG_SYSINFO) { af = ctx->family; fam_spec = (AF_INET6 == af) ? "(AAAA) " : (AF_INET == af) ? "(A) " : ""; msyslog(LOG_INFO, "DNS %s %s-> %s", name, fam_spec, stoa(&peeraddr)); } peer_config( &peeraddr, NULL, NULL, -1, ctx->hmode, ctx->version, ctx->minpoll, ctx->maxpoll, ctx->flags, ctx->ttl, ctx->keyid, ctx->group); break; } } free(ctx); } #endif /* WORKER */ #ifdef FREE_CFG_T static void free_config_peers( config_tree *ptree ) { peer_node *curr_peer; if (ptree->peers != NULL) { for (;;) { UNLINK_FIFO(curr_peer, *ptree->peers, link); if (NULL == curr_peer) break; destroy_address_node(curr_peer->addr); destroy_attr_val_fifo(curr_peer->peerflags); free(curr_peer); } free(ptree->peers); ptree->peers = NULL; } } #endif /* FREE_CFG_T */ #ifndef SIM static void config_unpeers( config_tree *ptree ) { sockaddr_u peeraddr; struct addrinfo hints; unpeer_node * curr_unpeer; struct peer * p; const char * name; int rc; curr_unpeer = HEAD_PFIFO(ptree->unpeers); for (; curr_unpeer != NULL; curr_unpeer = curr_unpeer->link) { /* * If we have no address attached, assume we have to * unpeer by AssocID. */ if (!curr_unpeer->addr) { p = findpeerbyassoc(curr_unpeer->assocID); if (p != NULL) { msyslog(LOG_NOTICE, "unpeered %s", stoa(&p->srcadr)); peer_clear(p, "GONE"); unpeer(p); } continue; } ZERO(peeraddr); AF(&peeraddr) = curr_unpeer->addr->type; name = curr_unpeer->addr->address; rc = getnetnum(name, &peeraddr, 0, t_UNK); /* Do we have a numeric address? */ if (rc > 0) { DPRINTF(1, ("unpeer: searching for %s\n", stoa(&peeraddr))); p = findexistingpeer(&peeraddr, NULL, NULL, -1, 0, NULL); if (p != NULL) { msyslog(LOG_NOTICE, "unpeered %s", stoa(&peeraddr)); peer_clear(p, "GONE"); unpeer(p); } continue; } /* * It's not a numeric IP address, it's a hostname. * Check for associations with a matching hostname. */ for (p = peer_list; p != NULL; p = p->p_link) if (p->hostname != NULL) if (!strcasecmp(p->hostname, name)) break; if (p != NULL) { msyslog(LOG_NOTICE, "unpeered %s", name); peer_clear(p, "GONE"); unpeer(p); } /* Resolve the hostname to address(es). */ # ifdef WORKER ZERO(hints); hints.ai_family = curr_unpeer->addr->type; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; getaddrinfo_sometime(name, "ntp", &hints, INITIAL_DNS_RETRY, &unpeer_name_resolved, NULL); # else /* !WORKER follows */ msyslog(LOG_ERR, "hostname %s can not be used, please use IP address instead.", name); # endif } } #endif /* !SIM */ /* * unpeer_name_resolved() * * Callback invoked when config_unpeers()'s DNS lookup completes. */ #ifdef WORKER static void unpeer_name_resolved( int rescode, int gai_errno, void * context, const char * name, const char * service, const struct addrinfo * hints, const struct addrinfo * res ) { sockaddr_u peeraddr; struct peer * peer; u_short af; const char * fam_spec; (void)context; (void)hints; DPRINTF(1, ("unpeer_name_resolved(%s) rescode %d\n", name, rescode)); if (rescode) { msyslog(LOG_ERR, "giving up resolving unpeer %s: %s (%d)", name, gai_strerror(rescode), rescode); return; } /* * Loop through the addresses found */ for (; res != NULL; res = res->ai_next) { INSIST(res->ai_addrlen <= sizeof(peeraddr)); memcpy(&peeraddr, res->ai_addr, res->ai_addrlen); DPRINTF(1, ("unpeer: searching for peer %s\n", stoa(&peeraddr))); peer = findexistingpeer(&peeraddr, NULL, NULL, -1, 0, NULL); if (peer != NULL) { af = AF(&peeraddr); fam_spec = (AF_INET6 == af) ? "(AAAA) " : (AF_INET == af) ? "(A) " : ""; msyslog(LOG_NOTICE, "unpeered %s %s-> %s", name, fam_spec, stoa(&peeraddr)); peer_clear(peer, "GONE"); unpeer(peer); } } } #endif /* WORKER */ #ifdef FREE_CFG_T static void free_config_unpeers( config_tree *ptree ) { unpeer_node *curr_unpeer; if (ptree->unpeers != NULL) { for (;;) { UNLINK_FIFO(curr_unpeer, *ptree->unpeers, link); if (NULL == curr_unpeer) break; destroy_address_node(curr_unpeer->addr); free(curr_unpeer); } free(ptree->unpeers); } } #endif /* FREE_CFG_T */ #ifndef SIM static void config_reset_counters( config_tree *ptree ) { int_node *counter_set; for (counter_set = HEAD_PFIFO(ptree->reset_counters); counter_set != NULL; counter_set = counter_set->link) { switch (counter_set->i) { default: DPRINTF(1, ("config_reset_counters %s (%d) invalid\n", keyword(counter_set->i), counter_set->i)); break; case T_Allpeers: peer_all_reset(); break; case T_Auth: reset_auth_stats(); break; case T_Ctl: ctl_clr_stats(); break; case T_Io: io_clr_stats(); break; case T_Mem: peer_clr_stats(); break; case T_Sys: proto_clr_stats(); break; case T_Timer: timer_clr_stats(); break; } } } #endif /* !SIM */ #ifdef FREE_CFG_T static void free_config_reset_counters( config_tree *ptree ) { FREE_INT_FIFO(ptree->reset_counters); } #endif /* FREE_CFG_T */ #ifdef SIM static void config_sim( config_tree *ptree ) { int i; server_info *serv_info; attr_val *init_stmt; sim_node *sim_n; /* Check if a simulate block was found in the configuration code. * If not, return an error and exit */ sim_n = HEAD_PFIFO(ptree->sim_details); if (NULL == sim_n) { fprintf(stderr, "ERROR!! I couldn't find a \"simulate\" block for configuring the simulator.\n"); fprintf(stderr, "\tCheck your configuration file.\n"); exit(1); } /* Process the initialization statements * ------------------------------------- */ init_stmt = HEAD_PFIFO(sim_n->init_opts); for (; init_stmt != NULL; init_stmt = init_stmt->link) { switch(init_stmt->attr) { case T_Beep_Delay: simulation.beep_delay = init_stmt->value.d; break; case T_Sim_Duration: simulation.end_time = init_stmt->value.d; break; default: fprintf(stderr, "Unknown simulator init token %d\n", init_stmt->attr); exit(1); } } /* Process the server list * ----------------------- */ simulation.num_of_servers = 0; serv_info = HEAD_PFIFO(sim_n->servers); for (; serv_info != NULL; serv_info = serv_info->link) simulation.num_of_servers++; simulation.servers = eallocarray(simulation.num_of_servers, sizeof(simulation.servers[0])); i = 0; serv_info = HEAD_PFIFO(sim_n->servers); for (; serv_info != NULL; serv_info = serv_info->link) { if (NULL == serv_info) { fprintf(stderr, "Simulator server list is corrupt\n"); exit(1); } else { simulation.servers[i] = *serv_info; simulation.servers[i].link = NULL; i++; } } printf("Creating server associations\n"); create_server_associations(); fprintf(stderr,"\tServer associations successfully created!!\n"); } #ifdef FREE_CFG_T static void free_config_sim( config_tree *ptree ) { sim_node *sim_n; server_info *serv_n; script_info *script_n; if (NULL == ptree->sim_details) return; sim_n = HEAD_PFIFO(ptree->sim_details); free(ptree->sim_details); ptree->sim_details = NULL; if (NULL == sim_n) return; FREE_ATTR_VAL_FIFO(sim_n->init_opts); for (;;) { UNLINK_FIFO(serv_n, *sim_n->servers, link); if (NULL == serv_n) break; free(serv_n->curr_script); if (serv_n->script != NULL) { for (;;) { UNLINK_FIFO(script_n, *serv_n->script, link); if (script_n == NULL) break; free(script_n); } free(serv_n->script); } free(serv_n); } free(sim_n); } #endif /* FREE_CFG_T */ #endif /* SIM */ /* Define two different config functions. One for the daemon and the other for * the simulator. The simulator ignores a lot of the standard ntpd configuration * options */ #ifndef SIM static void config_ntpd( config_tree *ptree, int/*BOOL*/ input_from_files ) { /* [Bug 3435] check and esure clock sanity if configured from * file and clock sanity parameters (-> basedate) are given. Do * this ASAP, so we don't disturb the closed loop controller. */ if (input_from_files) { if (config_tos_clock(ptree)) clamp_systime(); } config_nic_rules(ptree, input_from_files); config_monitor(ptree); config_auth(ptree); config_tos(ptree); config_access(ptree); config_tinker(ptree); config_rlimit(ptree); config_system_opts(ptree); config_logconfig(ptree); config_phone(ptree); config_mdnstries(ptree); config_setvar(ptree); config_ttl(ptree); config_vars(ptree); io_open_sockets(); /* [bug 2837] dep. on config_vars() */ config_trap(ptree); /* [bug 2923] dep. on io_open_sockets() */ config_other_modes(ptree); config_peers(ptree); config_unpeers(ptree); config_fudge(ptree); config_reset_counters(ptree); #ifdef DEBUG if (debug > 1) { dump_restricts(); } #endif #ifdef TEST_BLOCKING_WORKER { struct addrinfo hints; ZERO(hints); hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; getaddrinfo_sometime("www.cnn.com", "ntp", &hints, INITIAL_DNS_RETRY, gai_test_callback, (void *)1); hints.ai_family = AF_INET6; getaddrinfo_sometime("ipv6.google.com", "ntp", &hints, INITIAL_DNS_RETRY, gai_test_callback, (void *)0x600); } #endif } #endif /* !SIM */ #ifdef SIM static void config_ntpdsim( config_tree *ptree ) { printf("Configuring Simulator...\n"); printf("Some ntpd-specific commands in the configuration file will be ignored.\n"); config_tos(ptree); config_monitor(ptree); config_tinker(ptree); if (0) config_rlimit(ptree); /* not needed for the simulator */ config_system_opts(ptree); config_logconfig(ptree); config_vars(ptree); config_sim(ptree); } #endif /* SIM */ /* * config_remotely() - implements ntpd side of ntpq :config */ void config_remotely( sockaddr_u * remote_addr ) { char origin[128]; snprintf(origin, sizeof(origin), "remote config from %s", stoa(remote_addr)); lex_init_stack(origin, NULL); /* no checking needed... */ init_syntax_tree(&cfgt); yyparse(); lex_drop_stack(); cfgt.source.attr = CONF_SOURCE_NTPQ; cfgt.timestamp = time(NULL); cfgt.source.value.s = estrdup(stoa(remote_addr)); DPRINTF(1, ("Finished Parsing!!\n")); save_and_apply_config_tree(FALSE); } /* * getconfig() - process startup configuration file e.g /etc/ntp.conf */ void getconfig( int argc, char ** argv ) { char line[256]; #ifdef DEBUG atexit(free_all_config_trees); #endif #ifndef SYS_WINNT config_file = CONFIG_FILE; #else temp = CONFIG_FILE; if (!ExpandEnvironmentStringsA(temp, config_file_storage, sizeof(config_file_storage))) { msyslog(LOG_ERR, "ExpandEnvironmentStrings CONFIG_FILE failed: %m"); exit(1); } config_file = config_file_storage; temp = ALT_CONFIG_FILE; if (!ExpandEnvironmentStringsA(temp, alt_config_file_storage, sizeof(alt_config_file_storage))) { msyslog(LOG_ERR, "ExpandEnvironmentStrings ALT_CONFIG_FILE failed: %m"); exit(1); } alt_config_file = alt_config_file_storage; #endif /* SYS_WINNT */ /* * install a non default variable with this daemon version */ snprintf(line, sizeof(line), "daemon_version=\"%s\"", Version); set_sys_var(line, strlen(line) + 1, RO); /* * Set up for the first time step to install a variable showing * which syscall is being used to step. */ set_tod_using = &ntpd_set_tod_using; getCmdOpts(argc, argv); init_syntax_tree(&cfgt); if ( !lex_init_stack(FindConfig(config_file), "r") #ifdef HAVE_NETINFO /* If there is no config_file, try NetInfo. */ && check_netinfo && !(config_netinfo = get_netinfo_config()) #endif /* HAVE_NETINFO */ ) { msyslog(LOG_INFO, "getconfig: Couldn't open <%s>: %m", FindConfig(config_file)); #ifndef SYS_WINNT io_open_sockets(); return; #else /* Under WinNT try alternate_config_file name, first NTP.CONF, then NTP.INI */ if (!lex_init_stack(FindConfig(alt_config_file), "r")) { /* * Broadcast clients can sometimes run without * a configuration file. */ msyslog(LOG_INFO, "getconfig: Couldn't open <%s>: %m", FindConfig(alt_config_file)); io_open_sockets(); return; } cfgt.source.value.s = estrdup(alt_config_file); #endif /* SYS_WINNT */ } else cfgt.source.value.s = estrdup(config_file); /*** BULK OF THE PARSER ***/ #ifdef DEBUG yydebug = !!(debug >= 5); #endif yyparse(); lex_drop_stack(); DPRINTF(1, ("Finished Parsing!!\n")); cfgt.source.attr = CONF_SOURCE_FILE; cfgt.timestamp = time(NULL); save_and_apply_config_tree(TRUE); #ifdef HAVE_NETINFO if (config_netinfo) free_netinfo_config(config_netinfo); #endif /* HAVE_NETINFO */ } void save_and_apply_config_tree(int/*BOOL*/ input_from_file) { config_tree *ptree; #ifndef SAVECONFIG config_tree *punlinked; #endif /* * Keep all the configuration trees applied since startup in * a list that can be used to dump the configuration back to * a text file. */ ptree = emalloc(sizeof(*ptree)); memcpy(ptree, &cfgt, sizeof(*ptree)); ZERO(cfgt); LINK_TAIL_SLIST(cfg_tree_history, ptree, link, config_tree); #ifdef SAVECONFIG if (HAVE_OPT( SAVECONFIGQUIT )) { FILE *dumpfile; int err; int dumpfailed; dumpfile = fopen(OPT_ARG( SAVECONFIGQUIT ), "w"); if (NULL == dumpfile) { err = errno; mfprintf(stderr, "can not create save file %s, error %d %m\n", OPT_ARG(SAVECONFIGQUIT), err); exit(err); } dumpfailed = dump_all_config_trees(dumpfile, 0); if (dumpfailed) fprintf(stderr, "--saveconfigquit %s error %d\n", OPT_ARG( SAVECONFIGQUIT ), dumpfailed); else fprintf(stderr, "configuration saved to %s\n", OPT_ARG( SAVECONFIGQUIT )); exit(dumpfailed); } #endif /* SAVECONFIG */ /* The actual configuration done depends on whether we are configuring the * simulator or the daemon. Perform a check and call the appropriate * function as needed. */ #ifndef SIM config_ntpd(ptree, input_from_file); #else config_ntpdsim(ptree); #endif /* * With configure --disable-saveconfig, there's no use keeping * the config tree around after application, so free it. */ #ifndef SAVECONFIG UNLINK_SLIST(punlinked, cfg_tree_history, ptree, link, config_tree); INSIST(punlinked == ptree); free_config_tree(ptree); #endif } /* Hack to disambiguate 'server' statements for refclocks and network peers. * Please note the qualification 'hack'. It's just that. */ static int/*BOOL*/ is_refclk_addr( const address_node * addr ) { return addr && addr->address && !strncmp(addr->address, "127.127.", 8); } static void ntpd_set_tod_using( const char *which ) { char line[128]; snprintf(line, sizeof(line), "settimeofday=\"%s\"", which); set_sys_var(line, strlen(line) + 1, RO); } static char * normal_dtoa( double d ) { char * buf; char * pch_e; char * pch_nz; LIB_GETBUF(buf); snprintf(buf, LIB_BUFLENGTH, "%g", d); /* use lowercase 'e', strip any leading zeroes in exponent */ pch_e = strchr(buf, 'e'); if (NULL == pch_e) { pch_e = strchr(buf, 'E'); if (NULL == pch_e) return buf; *pch_e = 'e'; } pch_e++; if ('-' == *pch_e) pch_e++; pch_nz = pch_e; while ('0' == *pch_nz) pch_nz++; if (pch_nz == pch_e) return buf; strlcpy(pch_e, pch_nz, LIB_BUFLENGTH - (pch_e - buf)); return buf; } /* FUNCTIONS COPIED FROM THE OLDER ntp_config.c * -------------------------------------------- */ /* * get_pfxmatch - find value for prefixmatch * and update char * accordingly */ static u_int32 get_pfxmatch( const char ** pstr, struct masks * m ) { while (m->name != NULL) { if (strncmp(*pstr, m->name, strlen(m->name)) == 0) { *pstr += strlen(m->name); return m->mask; } else { m++; } } return 0; } /* * get_match - find logmask value */ static u_int32 get_match( const char * str, struct masks * m ) { while (m->name != NULL) { if (strcmp(str, m->name) == 0) return m->mask; else m++; } return 0; } /* * get_logmask - build bitmask for ntp_syslogmask */ static u_int32 get_logmask( const char * str ) { const char * t; u_int32 offset; u_int32 mask; mask = get_match(str, logcfg_noclass_items); if (mask != 0) return mask; t = str; offset = get_pfxmatch(&t, logcfg_class); mask = get_match(t, logcfg_class_items); if (mask) return mask << offset; else msyslog(LOG_ERR, "logconfig: '%s' not recognized - ignored", str); return 0; } #ifdef HAVE_NETINFO /* * get_netinfo_config - find the nearest NetInfo domain with an ntp * configuration and initialize the configuration state. */ static struct netinfo_config_state * get_netinfo_config(void) { ni_status status; void *domain; ni_id config_dir; struct netinfo_config_state *config; if (ni_open(NULL, ".", &domain) != NI_OK) return NULL; while ((status = ni_pathsearch(domain, &config_dir, NETINFO_CONFIG_DIR)) == NI_NODIR) { void *next_domain; if (ni_open(domain, "..", &next_domain) != NI_OK) { ni_free(next_domain); break; } ni_free(domain); domain = next_domain; } if (status != NI_OK) { ni_free(domain); return NULL; } config = emalloc(sizeof(*config)); config->domain = domain; config->config_dir = config_dir; config->prop_index = 0; config->val_index = 0; config->val_list = NULL; return config; } /* * free_netinfo_config - release NetInfo configuration state */ static void free_netinfo_config( struct netinfo_config_state *config ) { ni_free(config->domain); free(config); } /* * gettokens_netinfo - return tokens from NetInfo */ static int gettokens_netinfo ( struct netinfo_config_state *config, char **tokenlist, int *ntokens ) { int prop_index = config->prop_index; int val_index = config->val_index; char **val_list = config->val_list; /* * Iterate through each keyword and look for a property that matches it. */ again: if (!val_list) { for (; prop_index < COUNTOF(keywords); prop_index++) { ni_namelist namelist; struct keyword current_prop = keywords[prop_index]; ni_index index; /* * For each value associated in the property, we're going to return * a separate line. We squirrel away the values in the config state * so the next time through, we don't need to do this lookup. */ NI_INIT(&namelist); if (NI_OK == ni_lookupprop(config->domain, &config->config_dir, current_prop.text, &namelist)) { /* Found the property, but it has no values */ if (namelist.ni_namelist_len == 0) continue; config->val_list = eallocarray( (namelist.ni_namelist_len + 1), sizeof(char*)); val_list = config->val_list; for (index = 0; index < namelist.ni_namelist_len; index++) { char *value; value = namelist.ni_namelist_val[index]; val_list[index] = estrdup(value); } val_list[index] = NULL; break; } ni_namelist_free(&namelist); } config->prop_index = prop_index; } /* No list; we're done here. */ if (!val_list) return CONFIG_UNKNOWN; /* * We have a list of values for the current property. * Iterate through them and return each in order. */ if (val_list[val_index]) { int ntok = 1; int quoted = 0; char *tokens = val_list[val_index]; msyslog(LOG_INFO, "%s %s", keywords[prop_index].text, val_list[val_index]); (const char*)tokenlist[0] = keywords[prop_index].text; for (ntok = 1; ntok < MAXTOKENS; ntok++) { tokenlist[ntok] = tokens; while (!ISEOL(*tokens) && (!ISSPACE(*tokens) || quoted)) quoted ^= (*tokens++ == '"'); if (ISEOL(*tokens)) { *tokens = '\0'; break; } else { /* must be space */ *tokens++ = '\0'; while (ISSPACE(*tokens)) tokens++; if (ISEOL(*tokens)) break; } } if (ntok == MAXTOKENS) { /* HMS: chomp it to lose the EOL? */ msyslog(LOG_ERR, "gettokens_netinfo: too many tokens. Ignoring: %s", tokens); } else { *ntokens = ntok + 1; } config->val_index++; /* HMS: Should this be in the 'else'? */ return keywords[prop_index].keytype; } /* We're done with the current property. */ prop_index = ++config->prop_index; /* Free val_list and reset counters. */ for (val_index = 0; val_list[val_index]; val_index++) free(val_list[val_index]); free(val_list); val_list = config->val_list = NULL; val_index = config->val_index = 0; goto again; } #endif /* HAVE_NETINFO */ /* * getnetnum - return a net number (this is crude, but careful) * * returns 1 for success, and mysteriously, 0 for most failures, and * -1 if the address found is IPv6 and we believe IPv6 isn't working. */ #ifndef SIM static int getnetnum( const char *num, sockaddr_u *addr, int complain, enum gnn_type a_type /* ignored */ ) { REQUIRE(AF_UNSPEC == AF(addr) || AF_INET == AF(addr) || AF_INET6 == AF(addr)); if (!is_ip_address(num, AF(addr), addr)) return 0; if (IS_IPV6(addr) && !ipv6_works) return -1; # ifdef ISC_PLATFORM_HAVESALEN addr->sa.sa_len = SIZEOF_SOCKADDR(AF(addr)); # endif SET_PORT(addr, NTP_PORT); DPRINTF(2, ("getnetnum given %s, got %s\n", num, stoa(addr))); return 1; } #endif /* !SIM */ #if defined(HAVE_SETRLIMIT) void ntp_rlimit( int rl_what, rlim_t rl_value, int rl_scale, const char * rl_sstr ) { struct rlimit rl; switch (rl_what) { # ifdef RLIMIT_MEMLOCK case RLIMIT_MEMLOCK: if (HAVE_OPT( SAVECONFIGQUIT )) { break; } /* * The default RLIMIT_MEMLOCK is very low on Linux systems. * Unless we increase this limit malloc calls are likely to * fail if we drop root privilege. To be useful the value * has to be larger than the largest ntpd resident set size. */ DPRINTF(2, ("ntp_rlimit: MEMLOCK: %d %s\n", (int)(rl_value / rl_scale), rl_sstr)); rl.rlim_cur = rl.rlim_max = rl_value; if (setrlimit(RLIMIT_MEMLOCK, &rl) == -1) msyslog(LOG_ERR, "Cannot set RLIMIT_MEMLOCK: %m"); break; # endif /* RLIMIT_MEMLOCK */ # ifdef RLIMIT_NOFILE case RLIMIT_NOFILE: /* * For large systems the default file descriptor limit may * not be enough. */ DPRINTF(2, ("ntp_rlimit: NOFILE: %d %s\n", (int)(rl_value / rl_scale), rl_sstr)); rl.rlim_cur = rl.rlim_max = rl_value; if (setrlimit(RLIMIT_NOFILE, &rl) == -1) msyslog(LOG_ERR, "Cannot set RLIMIT_NOFILE: %m"); break; # endif /* RLIMIT_NOFILE */ # ifdef RLIMIT_STACK case RLIMIT_STACK: /* * Provide a way to set the stack limit to something * smaller, so that we don't lock a lot of unused * stack memory. */ DPRINTF(2, ("ntp_rlimit: STACK: %d %s pages\n", (int)(rl_value / rl_scale), rl_sstr)); if (-1 == getrlimit(RLIMIT_STACK, &rl)) { msyslog(LOG_ERR, "getrlimit(RLIMIT_STACK) failed: %m"); } else { if (rl_value > rl.rlim_max) { msyslog(LOG_WARNING, "ntp_rlimit: using maximum allowed stack limit %lu instead of %lu.", (u_long)rl.rlim_max, (u_long)rl_value); rl_value = rl.rlim_max; } rl.rlim_cur = rl_value; if (-1 == setrlimit(RLIMIT_STACK, &rl)) { msyslog(LOG_ERR, "ntp_rlimit: Cannot set RLIMIT_STACK: %m"); } } break; # endif /* RLIMIT_STACK */ default: fatal_error("ntp_rlimit: unexpected RLIMIT case: %d", rl_what); } } #endif /* HAVE_SETRLIMIT */ char * build_iflags( u_int32 iflags ) { static char ifs[1024]; ifs[0] = '\0'; if (iflags & INT_UP) { iflags &= ~INT_UP; appendstr(ifs, sizeof ifs, "up"); } if (iflags & INT_PPP) { iflags &= ~INT_PPP; appendstr(ifs, sizeof ifs, "ppp"); } if (iflags & INT_LOOPBACK) { iflags &= ~INT_LOOPBACK; appendstr(ifs, sizeof ifs, "loopback"); } if (iflags & INT_BROADCAST) { iflags &= ~INT_BROADCAST; appendstr(ifs, sizeof ifs, "broadcast"); } if (iflags & INT_MULTICAST) { iflags &= ~INT_MULTICAST; appendstr(ifs, sizeof ifs, "multicast"); } if (iflags & INT_BCASTOPEN) { iflags &= ~INT_BCASTOPEN; appendstr(ifs, sizeof ifs, "bcastopen"); } if (iflags & INT_MCASTOPEN) { iflags &= ~INT_MCASTOPEN; appendstr(ifs, sizeof ifs, "mcastopen"); } if (iflags & INT_WILDCARD) { iflags &= ~INT_WILDCARD; appendstr(ifs, sizeof ifs, "wildcard"); } if (iflags & INT_MCASTIF) { iflags &= ~INT_MCASTIF; appendstr(ifs, sizeof ifs, "MCASTif"); } if (iflags & INT_PRIVACY) { iflags &= ~INT_PRIVACY; appendstr(ifs, sizeof ifs, "IPv6privacy"); } if (iflags & INT_BCASTXMIT) { iflags &= ~INT_BCASTXMIT; appendstr(ifs, sizeof ifs, "bcastxmit"); } if (iflags) { char string[10]; snprintf(string, sizeof string, "%0x", iflags); appendstr(ifs, sizeof ifs, string); } return ifs; } char * build_mflags( u_short mflags ) { static char mfs[1024]; mfs[0] = '\0'; if (mflags & RESM_NTPONLY) { mflags &= ~RESM_NTPONLY; appendstr(mfs, sizeof mfs, "ntponly"); } if (mflags & RESM_SOURCE) { mflags &= ~RESM_SOURCE; appendstr(mfs, sizeof mfs, "source"); } if (mflags) { char string[10]; snprintf(string, sizeof string, "%0x", mflags); appendstr(mfs, sizeof mfs, string); } return mfs; } char * build_rflags( u_short rflags ) { static char rfs[1024]; rfs[0] = '\0'; if (rflags & RES_FLAKE) { rflags &= ~RES_FLAKE; appendstr(rfs, sizeof rfs, "flake"); } if (rflags & RES_IGNORE) { rflags &= ~RES_IGNORE; appendstr(rfs, sizeof rfs, "ignore"); } if (rflags & RES_KOD) { rflags &= ~RES_KOD; appendstr(rfs, sizeof rfs, "kod"); } if (rflags & RES_MSSNTP) { rflags &= ~RES_MSSNTP; appendstr(rfs, sizeof rfs, "mssntp"); } if (rflags & RES_LIMITED) { rflags &= ~RES_LIMITED; appendstr(rfs, sizeof rfs, "limited"); } if (rflags & RES_LPTRAP) { rflags &= ~RES_LPTRAP; appendstr(rfs, sizeof rfs, "lptrap"); } if (rflags & RES_NOMODIFY) { rflags &= ~RES_NOMODIFY; appendstr(rfs, sizeof rfs, "nomodify"); } if (rflags & RES_NOMRULIST) { rflags &= ~RES_NOMRULIST; appendstr(rfs, sizeof rfs, "nomrulist"); } if (rflags & RES_NOEPEER) { rflags &= ~RES_NOEPEER; appendstr(rfs, sizeof rfs, "noepeer"); } if (rflags & RES_NOPEER) { rflags &= ~RES_NOPEER; appendstr(rfs, sizeof rfs, "nopeer"); } if (rflags & RES_NOQUERY) { rflags &= ~RES_NOQUERY; appendstr(rfs, sizeof rfs, "noquery"); } if (rflags & RES_DONTSERVE) { rflags &= ~RES_DONTSERVE; appendstr(rfs, sizeof rfs, "dontserve"); } if (rflags & RES_NOTRAP) { rflags &= ~RES_NOTRAP; appendstr(rfs, sizeof rfs, "notrap"); } if (rflags & RES_DONTTRUST) { rflags &= ~RES_DONTTRUST; appendstr(rfs, sizeof rfs, "notrust"); } if (rflags & RES_SRVRSPFUZ) { rflags &= ~RES_SRVRSPFUZ; appendstr(rfs, sizeof rfs, "srvrspfuz"); } if (rflags & RES_VERSION) { rflags &= ~RES_VERSION; appendstr(rfs, sizeof rfs, "version"); } if (rflags) { char string[10]; snprintf(string, sizeof string, "%0x", rflags); appendstr(rfs, sizeof rfs, string); } if ('\0' == rfs[0]) { appendstr(rfs, sizeof rfs, "(none)"); } return rfs; } static void appendstr( char *string, size_t s, const char *new ) { if (*string != '\0') { (void)strlcat(string, ",", s); } (void)strlcat(string, new, s); return; } Index: stable/11/contrib/tcsh/tc.sig.c =================================================================== --- stable/11/contrib/tcsh/tc.sig.c (revision 359753) +++ stable/11/contrib/tcsh/tc.sig.c (revision 359754) @@ -1,147 +1,146 @@ /* * tc.sig.c: Signal routine emulations */ /*- * Copyright (c) 1980, 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "sh.h" #include "tc.wait.h" void sigset_interrupting(int sig, void (*fn) (int)) { struct sigaction act; act.sa_handler = fn; sigemptyset(&act.sa_mask); act.sa_flags = 0; if (sigaction(sig, &act, NULL) == 0) { sigset_t set; sigemptyset(&set); sigaddset(&set, sig); sigprocmask(SIG_UNBLOCK, &set, NULL); } } static volatile sig_atomic_t alrmcatch_pending; /* = 0; */ static volatile sig_atomic_t pchild_pending; /* = 0; */ static volatile sig_atomic_t phup_pending; /* = 0; */ static volatile sig_atomic_t pintr_pending; /* = 0; */ int alrmcatch_disabled; /* = 0; */ int phup_disabled; /* = 0; */ int pchild_disabled; /* = 0; */ int pintr_disabled; /* = 0; */ -int handle_interrupt; /* = 0; */ int handle_pending_signals(void) { int rv = 0; if (!phup_disabled && phup_pending) { phup_pending = 0; handle_interrupt++; phup(); handle_interrupt--; } if (!pintr_disabled && pintr_pending) { pintr_pending = 0; handle_interrupt++; pintr(); handle_interrupt--; rv = 1; } if (!pchild_disabled && pchild_pending) { pchild_pending = 0; handle_interrupt++; pchild(); handle_interrupt--; } if (!alrmcatch_disabled && alrmcatch_pending) { alrmcatch_pending = 0; handle_interrupt++; alrmcatch(); handle_interrupt--; } return rv; } void queue_alrmcatch(int sig) { USE(sig); alrmcatch_pending = 1; } void queue_pchild(int sig) { USE(sig); pchild_pending = 1; } void queue_phup(int sig) { USE(sig); phup_pending = 1; } void queue_pintr(int sig) { USE(sig); pintr_pending = 1; } void disabled_cleanup(void *xdisabled) { int *disabled; disabled = xdisabled; if (--*disabled == 0) handle_pending_signals(); } void pintr_disabled_restore(void *xold) { int *old; old = xold; pintr_disabled = *old; } void pintr_push_enable(int *saved) { *saved = pintr_disabled; pintr_disabled = 0; cleanup_push(saved, pintr_disabled_restore); handle_pending_signals(); } Index: stable/11/contrib/telnet/telnetd/ext.h =================================================================== --- stable/11/contrib/telnet/telnetd/ext.h (revision 359753) +++ stable/11/contrib/telnet/telnetd/ext.h (revision 359754) @@ -1,214 +1,218 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ext.h 8.2 (Berkeley) 12/15/93 * $FreeBSD$ */ +#ifndef EXTERN +#define EXTERN extern +#endif + /* * Telnet server variable declarations */ -extern char options[256]; -extern char do_dont_resp[256]; -extern char will_wont_resp[256]; -extern int linemode; /* linemode on/off */ +EXTERN char options[256]; +EXTERN char do_dont_resp[256]; +EXTERN char will_wont_resp[256]; +EXTERN int linemode; /* linemode on/off */ #ifdef LINEMODE -extern int uselinemode; /* what linemode to use (on/off) */ -extern int editmode; /* edit modes in use */ -extern int useeditmode; /* edit modes to use */ -extern int alwayslinemode; /* command line option */ -extern int lmodetype; /* Client support for linemode */ +EXTERN int uselinemode; /* what linemode to use (on/off) */ +EXTERN int editmode; /* edit modes in use */ +EXTERN int useeditmode; /* edit modes to use */ +EXTERN int alwayslinemode; /* command line option */ +EXTERN int lmodetype; /* Client support for linemode */ #endif /* LINEMODE */ -extern int flowmode; /* current flow control state */ -extern int restartany; /* restart output on any character state */ +EXTERN int flowmode; /* current flow control state */ +EXTERN int restartany; /* restart output on any character state */ #ifdef DIAGNOSTICS -extern int diagnostic; /* telnet diagnostic capabilities */ +EXTERN int diagnostic; /* telnet diagnostic capabilities */ #endif /* DIAGNOSTICS */ #ifdef BFTPDAEMON -extern int bftpd; /* behave as bftp daemon */ +EXTERN int bftpd; /* behave as bftp daemon */ #endif /* BFTPDAEMON */ #ifdef AUTHENTICATION -extern int auth_level; +EXTERN int auth_level; #endif -extern slcfun slctab[NSLC + 1]; /* slc mapping table */ +EXTERN slcfun slctab[NSLC + 1]; /* slc mapping table */ -char *terminaltype; +EXTERN char *terminaltype; /* * I/O data buffers, pointers, and counters. */ -extern char ptyobuf[BUFSIZ+NETSLOP], *pfrontp, *pbackp; +EXTERN char ptyobuf[BUFSIZ+NETSLOP], *pfrontp, *pbackp; -extern char netibuf[BUFSIZ], *netip; +EXTERN char netibuf[BUFSIZ], *netip; -extern char netobuf[BUFSIZ], *nfrontp, *nbackp; -extern char *neturg; /* one past last bye of urgent data */ +EXTERN char netobuf[BUFSIZ], *nfrontp, *nbackp; +EXTERN char *neturg; /* one past last bye of urgent data */ -extern int pcc, ncc; +EXTERN int pcc, ncc; -extern int pty, net; -extern char line[32]; -extern int SYNCHing; /* we are in TELNET SYNCH mode */ +EXTERN int pty, net; +EXTERN char line[32]; +EXTERN int SYNCHing; /* we are in TELNET SYNCH mode */ -extern void +EXTERN void _termstat(void), add_slc(char, char, cc_t), check_slc(void), change_slc(char, char, cc_t), cleanup(int), clientstat(int, int, int), copy_termbuf(char *, size_t), deferslc(void), defer_terminit(void), do_opt_slc(unsigned char *, int), doeof(void), dooption(int), dontoption(int), edithost(char *, char *), fatal(int, const char *), fatalperror(int, const char *), get_slc_defaults(void), init_env(void), init_termbuf(void), interrupt(void), localstat(void), flowstat(void), netclear(void), netflush(void), #ifdef DIAGNOSTICS printoption(const char *, int), printdata(const char *, char *, int), printsub(char, unsigned char *, int), #endif process_slc(unsigned char, unsigned char, cc_t), ptyflush(void), putchr(int), putf(char *, char *), recv_ayt(void), send_do(int, int), send_dont(int, int), send_slc(void), send_status(void), send_will(int, int), send_wont(int, int), sendbrk(void), sendsusp(void), set_termbuf(void), start_login(char *, int, char *), start_slc(int), #ifdef AUTHENTICATION start_slave(char *), #else start_slave(char *, int, char *), #endif suboption(void), telrcv(void), ttloop(void), tty_binaryin(int), tty_binaryout(int); -extern int +EXTERN int end_slc(unsigned char **), getnpty(void), #ifndef convex getpty(int *), #endif login_tty(int), spcset(int, cc_t *, cc_t **), stilloob(int), terminit(void), termstat(void), tty_flowmode(void), tty_restartany(void), tty_isbinaryin(void), tty_isbinaryout(void), tty_iscrnl(void), tty_isecho(void), tty_isediting(void), tty_islitecho(void), tty_isnewmap(void), tty_israw(void), tty_issofttab(void), tty_istrapsig(void), tty_linemode(void); -extern void +EXTERN void tty_rspeed(int), tty_setecho(int), tty_setedit(int), tty_setlinemode(int), tty_setlitecho(int), tty_setsig(int), tty_setsofttab(int), tty_tspeed(int), willoption(int), wontoption(int); int output_data(const char *, ...) __printflike(1, 2); void output_datalen(const char *, int); void startslave(char *, int, char *); #ifdef ENCRYPTION extern void (*encrypt_output)(unsigned char *, int); extern int (*decrypt_input)(int); -extern char *nclearto; +EXTERN char *nclearto; #endif /* ENCRYPTION */ /* * The following are some clocks used to decide how to interpret * the relationship between various variables. */ -extern struct { +EXTERN struct { int system, /* what the current time is */ echotoggle, /* last time user entered echo character */ modenegotiated, /* last time operating mode negotiated */ didnetreceive, /* last time we read data from network */ ttypesubopt, /* ttype subopt is received */ tspeedsubopt, /* tspeed subopt is received */ environsubopt, /* environ subopt is received */ oenvironsubopt, /* old environ subopt is received */ xdisplocsubopt, /* xdisploc subopt is received */ baseline, /* time started to do timed action */ gotDM; /* when did we last see a data mark */ } clocks; #ifndef DEFAULT_IM # ifdef ultrix # define DEFAULT_IM "\r\n\r\nULTRIX (%h) (%t)\r\n\r\r\n\r" # else # ifdef __FreeBSD__ # define DEFAULT_IM "\r\n\r\nFreeBSD (%h) (%t)\r\n\r\r\n\r" # else # define DEFAULT_IM "\r\n\r\n4.4 BSD UNIX (%h) (%t)\r\n\r\r\n\r" # endif # endif #endif Index: stable/11/contrib/telnet/telnetd/global.c =================================================================== --- stable/11/contrib/telnet/telnetd/global.c (revision 359753) +++ stable/11/contrib/telnet/telnetd/global.c (revision 359754) @@ -1,48 +1,48 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static const char sccsid[] = "@(#)global.c 8.1 (Berkeley) 6/4/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); /* * Allocate global variables. We do this * by including the header file that defines * them all as externs, but first we define * the keyword "extern" to be nothing, so that * we will actually allocate the space. */ #include "defs.h" -#define extern +#define EXTERN #include "ext.h" Index: stable/11/contrib/telnet/telnetd/sys_term.c =================================================================== --- stable/11/contrib/telnet/telnetd/sys_term.c (revision 359753) +++ stable/11/contrib/telnet/telnetd/sys_term.c (revision 359754) @@ -1,1254 +1,1252 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static const char sccsid[] = "@(#)sys_term.c 8.4+1 (Berkeley) 5/30/95"; #endif #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include "telnetd.h" #include "pathnames.h" #include "types.h" #include "baud.h" #ifdef AUTHENTICATION #include #endif int cleanopen(char *); void scrub_env(void); char *envinit[3]; extern char **environ; #define SCPYN(a, b) (void) strncpy(a, b, sizeof(a)) #define SCMPN(a, b) strncmp(a, b, sizeof(a)) #ifdef t_erase #undef t_erase #undef t_kill #undef t_intrc #undef t_quitc #undef t_startc #undef t_stopc #undef t_eofc #undef t_brkc #undef t_suspc #undef t_dsuspc #undef t_rprntc #undef t_flushc #undef t_werasc #undef t_lnextc #endif #ifndef USE_TERMIO struct termbuf { struct sgttyb sg; struct tchars tc; struct ltchars ltc; int state; int lflags; } termbuf, termbuf2; # define cfsetospeed(tp, val) (tp)->sg.sg_ospeed = (val) # define cfsetispeed(tp, val) (tp)->sg.sg_ispeed = (val) # define cfgetospeed(tp) (tp)->sg.sg_ospeed # define cfgetispeed(tp) (tp)->sg.sg_ispeed #else /* USE_TERMIO */ # ifndef TCSANOW # ifdef TCSETS # define TCSANOW TCSETS # define TCSADRAIN TCSETSW # define tcgetattr(f, t) ioctl(f, TCGETS, (char *)t) # else # ifdef TCSETA # define TCSANOW TCSETA # define TCSADRAIN TCSETAW # define tcgetattr(f, t) ioctl(f, TCGETA, (char *)t) # else # define TCSANOW TIOCSETA # define TCSADRAIN TIOCSETAW # define tcgetattr(f, t) ioctl(f, TIOCGETA, (char *)t) # endif # endif # define tcsetattr(f, a, t) ioctl(f, a, t) # define cfsetospeed(tp, val) (tp)->c_cflag &= ~CBAUD; \ (tp)->c_cflag |= (val) # define cfgetospeed(tp) ((tp)->c_cflag & CBAUD) # ifdef CIBAUD # define cfsetispeed(tp, val) (tp)->c_cflag &= ~CIBAUD; \ (tp)->c_cflag |= ((val)<c_cflag & CIBAUD)>>IBSHIFT) # else # define cfsetispeed(tp, val) (tp)->c_cflag &= ~CBAUD; \ (tp)->c_cflag |= (val) # define cfgetispeed(tp) ((tp)->c_cflag & CBAUD) # endif # endif /* TCSANOW */ struct termios termbuf, termbuf2; /* pty control structure */ #endif /* USE_TERMIO */ #include #include int cleanopen(char *); void scrub_env(void); static char **addarg(char **, const char *); /* * init_termbuf() * copy_termbuf(cp) * set_termbuf() * * These three routines are used to get and set the "termbuf" structure * to and from the kernel. init_termbuf() gets the current settings. * copy_termbuf() hands in a new "termbuf" to write to the kernel, and * set_termbuf() writes the structure into the kernel. */ void init_termbuf(void) { #ifndef USE_TERMIO (void) ioctl(pty, TIOCGETP, (char *)&termbuf.sg); (void) ioctl(pty, TIOCGETC, (char *)&termbuf.tc); (void) ioctl(pty, TIOCGLTC, (char *)&termbuf.ltc); # ifdef TIOCGSTATE (void) ioctl(pty, TIOCGSTATE, (char *)&termbuf.state); # endif #else (void) tcgetattr(pty, &termbuf); #endif termbuf2 = termbuf; } #if defined(LINEMODE) && defined(TIOCPKT_IOCTL) void copy_termbuf(char *cp, size_t len) { if (len > sizeof(termbuf)) len = sizeof(termbuf); memmove((char *)&termbuf, cp, len); termbuf2 = termbuf; } #endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */ void set_termbuf(void) { /* * Only make the necessary changes. */ #ifndef USE_TERMIO if (memcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg))) (void) ioctl(pty, TIOCSETN, (char *)&termbuf.sg); if (memcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc))) (void) ioctl(pty, TIOCSETC, (char *)&termbuf.tc); if (memcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc, sizeof(termbuf.ltc))) (void) ioctl(pty, TIOCSLTC, (char *)&termbuf.ltc); if (termbuf.lflags != termbuf2.lflags) (void) ioctl(pty, TIOCLSET, (char *)&termbuf.lflags); #else /* USE_TERMIO */ if (memcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) (void) tcsetattr(pty, TCSANOW, &termbuf); #endif /* USE_TERMIO */ } /* * spcset(func, valp, valpp) * * This function takes various special characters (func), and * sets *valp to the current value of that character, and * *valpp to point to where in the "termbuf" structure that * value is kept. * * It returns the SLC_ level of support for this function. */ #ifndef USE_TERMIO int spcset(int func, cc_t *valp, cc_t **valpp) { switch(func) { case SLC_EOF: *valp = termbuf.tc.t_eofc; *valpp = (cc_t *)&termbuf.tc.t_eofc; return(SLC_VARIABLE); case SLC_EC: *valp = termbuf.sg.sg_erase; *valpp = (cc_t *)&termbuf.sg.sg_erase; return(SLC_VARIABLE); case SLC_EL: *valp = termbuf.sg.sg_kill; *valpp = (cc_t *)&termbuf.sg.sg_kill; return(SLC_VARIABLE); case SLC_IP: *valp = termbuf.tc.t_intrc; *valpp = (cc_t *)&termbuf.tc.t_intrc; return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); case SLC_ABORT: *valp = termbuf.tc.t_quitc; *valpp = (cc_t *)&termbuf.tc.t_quitc; return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); case SLC_XON: *valp = termbuf.tc.t_startc; *valpp = (cc_t *)&termbuf.tc.t_startc; return(SLC_VARIABLE); case SLC_XOFF: *valp = termbuf.tc.t_stopc; *valpp = (cc_t *)&termbuf.tc.t_stopc; return(SLC_VARIABLE); case SLC_AO: *valp = termbuf.ltc.t_flushc; *valpp = (cc_t *)&termbuf.ltc.t_flushc; return(SLC_VARIABLE); case SLC_SUSP: *valp = termbuf.ltc.t_suspc; *valpp = (cc_t *)&termbuf.ltc.t_suspc; return(SLC_VARIABLE); case SLC_EW: *valp = termbuf.ltc.t_werasc; *valpp = (cc_t *)&termbuf.ltc.t_werasc; return(SLC_VARIABLE); case SLC_RP: *valp = termbuf.ltc.t_rprntc; *valpp = (cc_t *)&termbuf.ltc.t_rprntc; return(SLC_VARIABLE); case SLC_LNEXT: *valp = termbuf.ltc.t_lnextc; *valpp = (cc_t *)&termbuf.ltc.t_lnextc; return(SLC_VARIABLE); case SLC_FORW1: *valp = termbuf.tc.t_brkc; *valpp = (cc_t *)&termbuf.ltc.t_lnextc; return(SLC_VARIABLE); case SLC_BRK: case SLC_SYNCH: case SLC_AYT: case SLC_EOR: *valp = (cc_t)0; *valpp = (cc_t *)0; return(SLC_DEFAULT); default: *valp = (cc_t)0; *valpp = (cc_t *)0; return(SLC_NOSUPPORT); } } #else /* USE_TERMIO */ #define setval(a, b) *valp = termbuf.c_cc[a]; \ *valpp = &termbuf.c_cc[a]; \ return(b); #define defval(a) *valp = ((cc_t)a); *valpp = (cc_t *)0; return(SLC_DEFAULT); int spcset(int func, cc_t *valp, cc_t **valpp) { switch(func) { case SLC_EOF: setval(VEOF, SLC_VARIABLE); case SLC_EC: setval(VERASE, SLC_VARIABLE); case SLC_EL: setval(VKILL, SLC_VARIABLE); case SLC_IP: setval(VINTR, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); case SLC_ABORT: setval(VQUIT, SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); case SLC_XON: #ifdef VSTART setval(VSTART, SLC_VARIABLE); #else defval(0x13); #endif case SLC_XOFF: #ifdef VSTOP setval(VSTOP, SLC_VARIABLE); #else defval(0x11); #endif case SLC_EW: #ifdef VWERASE setval(VWERASE, SLC_VARIABLE); #else defval(0); #endif case SLC_RP: #ifdef VREPRINT setval(VREPRINT, SLC_VARIABLE); #else defval(0); #endif case SLC_LNEXT: #ifdef VLNEXT setval(VLNEXT, SLC_VARIABLE); #else defval(0); #endif case SLC_AO: #if !defined(VDISCARD) && defined(VFLUSHO) # define VDISCARD VFLUSHO #endif #ifdef VDISCARD setval(VDISCARD, SLC_VARIABLE|SLC_FLUSHOUT); #else defval(0); #endif case SLC_SUSP: #ifdef VSUSP setval(VSUSP, SLC_VARIABLE|SLC_FLUSHIN); #else defval(0); #endif #ifdef VEOL case SLC_FORW1: setval(VEOL, SLC_VARIABLE); #endif #ifdef VEOL2 case SLC_FORW2: setval(VEOL2, SLC_VARIABLE); #endif case SLC_AYT: #ifdef VSTATUS setval(VSTATUS, SLC_VARIABLE); #else defval(0); #endif case SLC_BRK: case SLC_SYNCH: case SLC_EOR: defval(0); default: *valp = 0; *valpp = 0; return(SLC_NOSUPPORT); } } #endif /* USE_TERMIO */ /* * getpty() * * Allocate a pty. As a side effect, the external character * array "line" contains the name of the slave side. * * Returns the file descriptor of the opened pty. */ -char line[32]; - int getpty(int *ptynum __unused) { int p; const char *pn; p = posix_openpt(O_RDWR|O_NOCTTY); if (p < 0) return (-1); if (grantpt(p) == -1) return (-1); if (unlockpt(p) == -1) return (-1); pn = ptsname(p); if (pn == NULL) return (-1); if (strlcpy(line, pn, sizeof line) >= sizeof line) return (-1); return (p); } #ifdef LINEMODE /* * tty_flowmode() Find out if flow control is enabled or disabled. * tty_linemode() Find out if linemode (external processing) is enabled. * tty_setlinemod(on) Turn on/off linemode. * tty_isecho() Find out if echoing is turned on. * tty_setecho(on) Enable/disable character echoing. * tty_israw() Find out if terminal is in RAW mode. * tty_binaryin(on) Turn on/off BINARY on input. * tty_binaryout(on) Turn on/off BINARY on output. * tty_isediting() Find out if line editing is enabled. * tty_istrapsig() Find out if signal trapping is enabled. * tty_setedit(on) Turn on/off line editing. * tty_setsig(on) Turn on/off signal trapping. * tty_issofttab() Find out if tab expansion is enabled. * tty_setsofttab(on) Turn on/off soft tab expansion. * tty_islitecho() Find out if typed control chars are echoed literally * tty_setlitecho() Turn on/off literal echo of control chars * tty_tspeed(val) Set transmit speed to val. * tty_rspeed(val) Set receive speed to val. */ int tty_linemode(void) { #ifndef USE_TERMIO return(termbuf.state & TS_EXTPROC); #else return(termbuf.c_lflag & EXTPROC); #endif } void tty_setlinemode(int on) { #ifdef TIOCEXT set_termbuf(); (void) ioctl(pty, TIOCEXT, (char *)&on); init_termbuf(); #else /* !TIOCEXT */ # ifdef EXTPROC if (on) termbuf.c_lflag |= EXTPROC; else termbuf.c_lflag &= ~EXTPROC; # endif #endif /* TIOCEXT */ } #endif /* LINEMODE */ int tty_isecho(void) { #ifndef USE_TERMIO return (termbuf.sg.sg_flags & ECHO); #else return (termbuf.c_lflag & ECHO); #endif } int tty_flowmode(void) { #ifndef USE_TERMIO return(((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0) ? 1 : 0); #else return((termbuf.c_iflag & IXON) ? 1 : 0); #endif } int tty_restartany(void) { #ifndef USE_TERMIO # ifdef DECCTQ return((termbuf.lflags & DECCTQ) ? 0 : 1); # else return(-1); # endif #else return((termbuf.c_iflag & IXANY) ? 1 : 0); #endif } void tty_setecho(int on) { #ifndef USE_TERMIO if (on) termbuf.sg.sg_flags |= ECHO|CRMOD; else termbuf.sg.sg_flags &= ~(ECHO|CRMOD); #else if (on) termbuf.c_lflag |= ECHO; else termbuf.c_lflag &= ~ECHO; #endif } int tty_israw(void) { #ifndef USE_TERMIO return(termbuf.sg.sg_flags & RAW); #else return(!(termbuf.c_lflag & ICANON)); #endif } #ifdef AUTHENTICATION #if defined(NO_LOGIN_F) && defined(LOGIN_R) int tty_setraw(int on) { # ifndef USE_TERMIO if (on) termbuf.sg.sg_flags |= RAW; else termbuf.sg.sg_flags &= ~RAW; # else if (on) termbuf.c_lflag &= ~ICANON; else termbuf.c_lflag |= ICANON; # endif } #endif #endif /* AUTHENTICATION */ void tty_binaryin(int on) { #ifndef USE_TERMIO if (on) termbuf.lflags |= LPASS8; else termbuf.lflags &= ~LPASS8; #else if (on) { termbuf.c_iflag &= ~ISTRIP; } else { termbuf.c_iflag |= ISTRIP; } #endif } void tty_binaryout(int on) { #ifndef USE_TERMIO if (on) termbuf.lflags |= LLITOUT; else termbuf.lflags &= ~LLITOUT; #else if (on) { termbuf.c_cflag &= ~(CSIZE|PARENB); termbuf.c_cflag |= CS8; termbuf.c_oflag &= ~OPOST; } else { termbuf.c_cflag &= ~CSIZE; termbuf.c_cflag |= CS7|PARENB; termbuf.c_oflag |= OPOST; } #endif } int tty_isbinaryin(void) { #ifndef USE_TERMIO return(termbuf.lflags & LPASS8); #else return(!(termbuf.c_iflag & ISTRIP)); #endif } int tty_isbinaryout(void) { #ifndef USE_TERMIO return(termbuf.lflags & LLITOUT); #else return(!(termbuf.c_oflag&OPOST)); #endif } #ifdef LINEMODE int tty_isediting(void) { #ifndef USE_TERMIO return(!(termbuf.sg.sg_flags & (CBREAK|RAW))); #else return(termbuf.c_lflag & ICANON); #endif } int tty_istrapsig(void) { #ifndef USE_TERMIO return(!(termbuf.sg.sg_flags&RAW)); #else return(termbuf.c_lflag & ISIG); #endif } void tty_setedit(int on) { #ifndef USE_TERMIO if (on) termbuf.sg.sg_flags &= ~CBREAK; else termbuf.sg.sg_flags |= CBREAK; #else if (on) termbuf.c_lflag |= ICANON; else termbuf.c_lflag &= ~ICANON; #endif } void tty_setsig(int on) { #ifndef USE_TERMIO if (on) ; #else if (on) termbuf.c_lflag |= ISIG; else termbuf.c_lflag &= ~ISIG; #endif } #endif /* LINEMODE */ int tty_issofttab(void) { #ifndef USE_TERMIO return (termbuf.sg.sg_flags & XTABS); #else # ifdef OXTABS return (termbuf.c_oflag & OXTABS); # endif # ifdef TABDLY return ((termbuf.c_oflag & TABDLY) == TAB3); # endif #endif } void tty_setsofttab(int on) { #ifndef USE_TERMIO if (on) termbuf.sg.sg_flags |= XTABS; else termbuf.sg.sg_flags &= ~XTABS; #else if (on) { # ifdef OXTABS termbuf.c_oflag |= OXTABS; # endif # ifdef TABDLY termbuf.c_oflag &= ~TABDLY; termbuf.c_oflag |= TAB3; # endif } else { # ifdef OXTABS termbuf.c_oflag &= ~OXTABS; # endif # ifdef TABDLY termbuf.c_oflag &= ~TABDLY; termbuf.c_oflag |= TAB0; # endif } #endif } int tty_islitecho(void) { #ifndef USE_TERMIO return (!(termbuf.lflags & LCTLECH)); #else # ifdef ECHOCTL return (!(termbuf.c_lflag & ECHOCTL)); # endif # ifdef TCTLECH return (!(termbuf.c_lflag & TCTLECH)); # endif # if !defined(ECHOCTL) && !defined(TCTLECH) return (0); /* assumes ctl chars are echoed '^x' */ # endif #endif } void tty_setlitecho(int on) { #ifndef USE_TERMIO if (on) termbuf.lflags &= ~LCTLECH; else termbuf.lflags |= LCTLECH; #else # ifdef ECHOCTL if (on) termbuf.c_lflag &= ~ECHOCTL; else termbuf.c_lflag |= ECHOCTL; # endif # ifdef TCTLECH if (on) termbuf.c_lflag &= ~TCTLECH; else termbuf.c_lflag |= TCTLECH; # endif #endif } int tty_iscrnl(void) { #ifndef USE_TERMIO return (termbuf.sg.sg_flags & CRMOD); #else return (termbuf.c_iflag & ICRNL); #endif } void tty_tspeed(int val) { #ifdef DECODE_BAUD struct termspeeds *tp; for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) ; if (tp->speed == -1) /* back up to last valid value */ --tp; cfsetospeed(&termbuf, tp->value); #else /* DECODE_BAUD */ cfsetospeed(&termbuf, val); #endif /* DECODE_BAUD */ } void tty_rspeed(int val) { #ifdef DECODE_BAUD struct termspeeds *tp; for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) ; if (tp->speed == -1) /* back up to last valid value */ --tp; cfsetispeed(&termbuf, tp->value); #else /* DECODE_BAUD */ cfsetispeed(&termbuf, val); #endif /* DECODE_BAUD */ } /* * getptyslave() * * Open the slave side of the pty, and do any initialization * that is necessary. */ static void getptyslave(void) { int t = -1; char erase; # ifdef LINEMODE int waslm; # endif # ifdef TIOCGWINSZ struct winsize ws; extern int def_row, def_col; # endif extern int def_tspeed, def_rspeed; /* * Opening the slave side may cause initilization of the * kernel tty structure. We need remember the state of * if linemode was turned on * terminal window size * terminal speed * erase character * so that we can re-set them if we need to. */ # ifdef LINEMODE waslm = tty_linemode(); # endif erase = termbuf.c_cc[VERASE]; /* * Make sure that we don't have a controlling tty, and * that we are the session (process group) leader. */ # ifdef TIOCNOTTY t = open(_PATH_TTY, O_RDWR); if (t >= 0) { (void) ioctl(t, TIOCNOTTY, (char *)0); (void) close(t); } # endif t = cleanopen(line); if (t < 0) fatalperror(net, line); /* * set up the tty modes as we like them to be. */ init_termbuf(); # ifdef TIOCGWINSZ if (def_row || def_col) { memset((char *)&ws, 0, sizeof(ws)); ws.ws_col = def_col; ws.ws_row = def_row; (void)ioctl(t, TIOCSWINSZ, (char *)&ws); } # endif /* * Settings for sgtty based systems */ # ifndef USE_TERMIO termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO|XTABS; # endif /* USE_TERMIO */ /* * Settings for all other termios/termio based * systems, other than 4.4BSD. In 4.4BSD the * kernel does the initial terminal setup. */ tty_rspeed((def_rspeed > 0) ? def_rspeed : 9600); tty_tspeed((def_tspeed > 0) ? def_tspeed : 9600); if (erase) termbuf.c_cc[VERASE] = erase; # ifdef LINEMODE if (waslm) tty_setlinemode(1); # endif /* LINEMODE */ /* * Set the tty modes, and make this our controlling tty. */ set_termbuf(); if (login_tty(t) == -1) fatalperror(net, "login_tty"); if (net > 2) (void) close(net); #ifdef AUTHENTICATION #if defined(NO_LOGIN_F) && defined(LOGIN_R) /* * Leave the pty open so that we can write out the rlogin * protocol for /bin/login, if the authentication works. */ #else if (pty > 2) { (void) close(pty); pty = -1; } #endif #endif /* AUTHENTICATION */ } #ifndef O_NOCTTY #define O_NOCTTY 0 #endif /* * Open the specified slave side of the pty, * making sure that we have a clean tty. */ int cleanopen(char *li) { int t; /* * Make sure that other people can't open the * slave side of the connection. */ (void) chown(li, 0, 0); (void) chmod(li, 0600); (void) revoke(li); t = open(line, O_RDWR|O_NOCTTY); if (t < 0) return(-1); return(t); } /* * startslave(host) * * Given a hostname, do whatever * is necessary to startup the login process on the slave side of the pty. */ /* ARGSUSED */ void startslave(char *host, int autologin, char *autoname) { int i; #ifdef AUTHENTICATION if (!autoname || !autoname[0]) autologin = 0; if (autologin < auth_level) { fatal(net, "Authorization failed"); exit(1); } #endif if ((i = fork()) < 0) fatalperror(net, "fork"); if (i) { } else { getptyslave(); start_login(host, autologin, autoname); /*NOTREACHED*/ } } void init_env(void) { char **envp; envp = envinit; if ((*envp = getenv("TZ"))) *envp++ -= 3; *envp = 0; environ = envinit; } /* * start_login(host) * * Assuming that we are now running as a child processes, this * function will turn us into the login process. */ #ifndef AUTHENTICATION #define undef1 __unused #else #define undef1 #endif void start_login(char *host undef1, int autologin undef1, char *name undef1) { char **argv; char *user; user = getenv("USER"); user = (user != NULL) ? strdup(user) : NULL; scrub_env(); /* * -h : pass on name of host. * WARNING: -h is accepted by login if and only if * getuid() == 0. * -p : don't clobber the environment (so terminal type stays set). * * -f : force this login, he has already been authenticated */ argv = addarg(0, "login"); #if !defined(NO_LOGIN_H) #ifdef AUTHENTICATION # if defined(NO_LOGIN_F) && defined(LOGIN_R) /* * Don't add the "-h host" option if we are going * to be adding the "-r host" option down below... */ if ((auth_level < 0) || (autologin != AUTH_VALID)) # endif #endif /* AUTHENTICATION */ { argv = addarg(argv, "-h"); argv = addarg(argv, host); } #endif #if !defined(NO_LOGIN_P) argv = addarg(argv, "-p"); #endif #ifdef LINEMODE /* * Set the environment variable "LINEMODE" to either * "real" or "kludge" if we are operating in either * real or kludge linemode. */ if (lmodetype == REAL_LINEMODE) setenv("LINEMODE", "real", 1); # ifdef KLUDGELINEMODE else if (lmodetype == KLUDGE_LINEMODE || lmodetype == KLUDGE_OK) setenv("LINEMODE", "kludge", 1); # endif #endif #ifdef BFTPDAEMON /* * Are we working as the bftp daemon? If so, then ask login * to start bftp instead of shell. */ if (bftpd) { argv = addarg(argv, "-e"); argv = addarg(argv, BFTPPATH); } else #endif #ifdef AUTHENTICATION if (auth_level >= 0 && autologin == AUTH_VALID) { # if !defined(NO_LOGIN_F) argv = addarg(argv, "-f"); argv = addarg(argv, "--"); argv = addarg(argv, name); # else # if defined(LOGIN_R) /* * We don't have support for "login -f", but we * can fool /bin/login into thinking that we are * rlogind, and allow us to log in without a * password. The rlogin protocol expects * local-user\0remote-user\0term/speed\0 */ if (pty > 2) { char *cp; char speed[128]; int isecho, israw, xpty, len; extern int def_rspeed; # ifndef LOGIN_HOST /* * Tell login that we are coming from "localhost". * If we passed in the real host name, then the * user would have to allow .rhost access from * every machine that they want authenticated * access to work from, which sort of defeats * the purpose of an authenticated login... * So, we tell login that the session is coming * from "localhost", and the user will only have * to have "localhost" in their .rhost file. */ # define LOGIN_HOST "localhost" # endif argv = addarg(argv, "-r"); argv = addarg(argv, LOGIN_HOST); xpty = pty; pty = 0; init_termbuf(); isecho = tty_isecho(); israw = tty_israw(); if (isecho || !israw) { tty_setecho(0); /* Turn off echo */ tty_setraw(1); /* Turn on raw */ set_termbuf(); } len = strlen(name)+1; write(xpty, name, len); write(xpty, name, len); snprintf(speed, sizeof(speed), "%s/%d", (cp = getenv("TERM")) ? cp : "", (def_rspeed > 0) ? def_rspeed : 9600); len = strlen(speed)+1; write(xpty, speed, len); if (isecho || !israw) { init_termbuf(); tty_setecho(isecho); tty_setraw(israw); set_termbuf(); if (!israw) { /* * Write a newline to ensure * that login will be able to * read the line... */ write(xpty, "\n", 1); } } pty = xpty; } # else argv = addarg(argv, "--"); argv = addarg(argv, name); # endif # endif } else #endif if (user != NULL) { argv = addarg(argv, "--"); argv = addarg(argv, user); #if defined(LOGIN_ARGS) && defined(NO_LOGIN_P) { char **cpp; for (cpp = environ; *cpp; cpp++) argv = addarg(argv, *cpp); } #endif } #ifdef AUTHENTICATION #if defined(NO_LOGIN_F) && defined(LOGIN_R) if (pty > 2) close(pty); #endif #endif /* AUTHENTICATION */ closelog(); if (user != NULL) free(user); if (altlogin == NULL) { altlogin = _PATH_LOGIN; } execv(altlogin, argv); syslog(LOG_ERR, "%s: %m", altlogin); fatalperror(net, altlogin); /*NOTREACHED*/ } static char ** addarg(char **argv, const char *val) { char **cpp; if (argv == NULL) { /* * 10 entries, a leading length, and a null */ argv = (char **)malloc(sizeof(*argv) * 12); if (argv == NULL) fatal(net, "failure allocating argument space"); *argv++ = (char *)10; *argv = (char *)0; } for (cpp = argv; *cpp; cpp++) ; if (cpp == &argv[(long)argv[-1]]) { --argv; *argv = (char *)((long)(*argv) + 10); argv = (char **)realloc(argv, sizeof(*argv)*((long)(*argv) + 2)); if (argv == NULL) fatal(net, "failure allocating argument space"); argv++; cpp = &argv[(long)argv[-1] - 10]; } if ((*cpp++ = strdup(val)) == NULL) fatal(net, "failure allocating argument space"); *cpp = 0; return(argv); } /* * scrub_env() * * We only accept the environment variables listed below. */ void scrub_env(void) { static const char *rej[] = { "TERMCAP=/", NULL }; static const char *acc[] = { "XAUTH=", "XAUTHORITY=", "DISPLAY=", "TERM=", "EDITOR=", "PAGER=", "LOGNAME=", "POSIXLY_CORRECT=", "PRINTER=", NULL }; char **cpp, **cpp2; const char **p; char ** new_environ; size_t count; /* Allocate space for scrubbed environment. */ for (count = 1, cpp = environ; *cpp; count++, cpp++) continue; if ((new_environ = malloc(count * sizeof(char *))) == NULL) { environ = NULL; return; } for (cpp2 = new_environ, cpp = environ; *cpp; cpp++) { int reject_it = 0; for(p = rej; *p; p++) if(strncmp(*cpp, *p, strlen(*p)) == 0) { reject_it = 1; break; } if (reject_it) continue; for(p = acc; *p; p++) if(strncmp(*cpp, *p, strlen(*p)) == 0) break; if(*p != NULL) { if ((*cpp2++ = strdup(*cpp)) == NULL) { environ = new_environ; return; } } } *cpp2 = NULL; environ = new_environ; } /* * cleanup() * * This is the routine to call when we are all through, to * clean up anything that needs to be cleaned up. */ /* ARGSUSED */ void cleanup(int sig __unused) { (void) shutdown(net, SHUT_RDWR); _exit(1); } Index: stable/11/contrib/telnet/telnetd/telnetd.c =================================================================== --- stable/11/contrib/telnet/telnetd/telnetd.c (revision 359753) +++ stable/11/contrib/telnet/telnetd/telnetd.c (revision 359754) @@ -1,1257 +1,1256 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static const char sccsid[] = "@(#)telnetd.c 8.4 (Berkeley) 5/30/95"; #endif #endif #include __FBSDID("$FreeBSD$"); #include "telnetd.h" #include "pathnames.h" #include #include #include #include #include #include #ifdef AUTHENTICATION #include -int auth_level = 0; #endif #ifdef ENCRYPTION #include #endif #include char remote_hostname[MAXHOSTNAMELEN]; size_t utmp_len = sizeof(remote_hostname) - 1; int registerd_host_only = 0; /* * I/O data buffers, * pointers, and counters. */ char ptyibuf[BUFSIZ], *ptyip = ptyibuf; char ptyibuf2[BUFSIZ]; int readstream(int, char *, int); void doit(struct sockaddr *); int terminaltypeok(char *); int hostinfo = 1; /* do we print login banner? */ static int debug = 0; int keepalive = 1; const char *altlogin; void doit(struct sockaddr *); int terminaltypeok(char *); void startslave(char *, int, char *); extern void usage(void); static void _gettermname(void); /* * The string to pass to getopt(). We do it this way so * that only the actual options that we support will be * passed off to getopt(). */ char valid_opts[] = { 'd', ':', 'h', 'k', 'n', 'p', ':', 'S', ':', 'u', ':', 'U', '4', '6', #ifdef AUTHENTICATION 'a', ':', 'X', ':', #endif #ifdef BFTPDAEMON 'B', #endif #ifdef DIAGNOSTICS 'D', ':', #endif #ifdef ENCRYPTION 'e', ':', #endif #ifdef LINEMODE 'l', #endif '\0' }; int family = AF_INET; #ifndef MAXHOSTNAMELEN #define MAXHOSTNAMELEN 256 #endif /* MAXHOSTNAMELEN */ char *hostname; char host_name[MAXHOSTNAMELEN]; extern void telnet(int, int, char *); int level; char user_name[256]; int main(int argc, char *argv[]) { u_long ultmp; struct sockaddr_storage from; int on = 1, fromlen; int ch; #if defined(IPPROTO_IP) && defined(IP_TOS) int tos = -1; #endif char *ep; pfrontp = pbackp = ptyobuf; netip = netibuf; nfrontp = nbackp = netobuf; #ifdef ENCRYPTION nclearto = 0; #endif /* ENCRYPTION */ /* * This initialization causes linemode to default to a configuration * that works on all telnet clients, including the FreeBSD client. * This is not quite the same as the telnet client issuing a "mode * character" command, but has most of the same benefits, and is * preferable since some clients (like usofts) don't have the * mode character command anyway and linemode breaks things. * The most notable symptom of fix is that csh "set filec" operations * like (filename completion) and ^D (choices) keys now work * in telnet sessions and can be used more than once on the same line. * CR/LF handling is also corrected in some termio modes. This * change resolves problem reports bin/771 and bin/1037. */ linemode=1; /*Default to mode that works on bulk of clients*/ while ((ch = getopt(argc, argv, valid_opts)) != -1) { switch(ch) { #ifdef AUTHENTICATION case 'a': /* * Check for required authentication level */ if (strcmp(optarg, "debug") == 0) { extern int auth_debug_mode; auth_debug_mode = 1; } else if (strcasecmp(optarg, "none") == 0) { auth_level = 0; } else if (strcasecmp(optarg, "other") == 0) { auth_level = AUTH_OTHER; } else if (strcasecmp(optarg, "user") == 0) { auth_level = AUTH_USER; } else if (strcasecmp(optarg, "valid") == 0) { auth_level = AUTH_VALID; } else if (strcasecmp(optarg, "off") == 0) { /* * This hack turns off authentication */ auth_level = -1; } else { warnx("unknown authorization level for -a"); } break; #endif /* AUTHENTICATION */ #ifdef BFTPDAEMON case 'B': bftpd++; break; #endif /* BFTPDAEMON */ case 'd': if (strcmp(optarg, "ebug") == 0) { debug++; break; } usage(); /* NOTREACHED */ break; #ifdef DIAGNOSTICS case 'D': /* * Check for desired diagnostics capabilities. */ if (!strcmp(optarg, "report")) { diagnostic |= TD_REPORT|TD_OPTIONS; } else if (!strcmp(optarg, "exercise")) { diagnostic |= TD_EXERCISE; } else if (!strcmp(optarg, "netdata")) { diagnostic |= TD_NETDATA; } else if (!strcmp(optarg, "ptydata")) { diagnostic |= TD_PTYDATA; } else if (!strcmp(optarg, "options")) { diagnostic |= TD_OPTIONS; } else { usage(); /* NOT REACHED */ } break; #endif /* DIAGNOSTICS */ #ifdef ENCRYPTION case 'e': if (strcmp(optarg, "debug") == 0) { extern int encrypt_debug_mode; encrypt_debug_mode = 1; break; } usage(); /* NOTREACHED */ break; #endif /* ENCRYPTION */ case 'h': hostinfo = 0; break; #ifdef LINEMODE case 'l': alwayslinemode = 1; break; #endif /* LINEMODE */ case 'k': #if defined(LINEMODE) && defined(KLUDGELINEMODE) lmodetype = NO_AUTOKLUDGE; #else /* ignore -k option if built without kludge linemode */ #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ break; case 'n': keepalive = 0; break; case 'p': altlogin = optarg; break; case 'S': #ifdef HAS_GETTOS if ((tos = parsetos(optarg, "tcp")) < 0) warnx("%s%s%s", "bad TOS argument '", optarg, "'; will try to use default TOS"); #else #define MAXTOS 255 ultmp = strtoul(optarg, &ep, 0); if (*ep || ep == optarg || ultmp > MAXTOS) warnx("%s%s%s", "bad TOS argument '", optarg, "'; will try to use default TOS"); else tos = ultmp; #endif break; case 'u': utmp_len = (size_t)atoi(optarg); if (utmp_len >= sizeof(remote_hostname)) utmp_len = sizeof(remote_hostname) - 1; break; case 'U': registerd_host_only = 1; break; #ifdef AUTHENTICATION case 'X': /* * Check for invalid authentication types */ auth_disable_name(optarg); break; #endif /* AUTHENTICATION */ case '4': family = AF_INET; break; #ifdef INET6 case '6': family = AF_INET6; break; #endif default: warnx("%c: unknown option", ch); /* FALLTHROUGH */ case '?': usage(); /* NOTREACHED */ } } argc -= optind; argv += optind; if (debug) { int s, ns, foo, error; const char *service = "telnet"; struct addrinfo hints, *res; if (argc > 1) { usage(); /* NOT REACHED */ } else if (argc == 1) service = *argv; memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; error = getaddrinfo(NULL, service, &hints, &res); if (error) { errx(1, "tcp/%s: %s\n", service, gai_strerror(error)); if (error == EAI_SYSTEM) errx(1, "tcp/%s: %s\n", service, strerror(errno)); usage(); } s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s < 0) err(1, "socket"); (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); if (debug > 1) (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)); if (bind(s, res->ai_addr, res->ai_addrlen) < 0) err(1, "bind"); if (listen(s, 1) < 0) err(1, "listen"); foo = res->ai_addrlen; ns = accept(s, res->ai_addr, &foo); if (ns < 0) err(1, "accept"); (void) setsockopt(ns, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof(on)); (void) dup2(ns, 0); (void) close(ns); (void) close(s); #ifdef convex } else if (argc == 1) { ; /* VOID*/ /* Just ignore the host/port name */ #endif } else if (argc > 0) { usage(); /* NOT REACHED */ } openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); fromlen = sizeof (from); if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { warn("getpeername"); _exit(1); } if (keepalive && setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof (on)) < 0) { syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); } #if defined(IPPROTO_IP) && defined(IP_TOS) if (from.ss_family == AF_INET) { # if defined(HAS_GETTOS) struct tosent *tp; if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) tos = tp->t_tos; # endif if (tos < 0) tos = 020; /* Low Delay bit */ if (tos && (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(tos)) < 0) && (errno != ENOPROTOOPT) ) syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); } #endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ net = 0; doit((struct sockaddr *)&from); /* NOTREACHED */ return(0); } /* end of main */ void usage() { fprintf(stderr, "usage: telnetd"); #ifdef AUTHENTICATION fprintf(stderr, " [-4] [-6] [-a (debug|other|user|valid|off|none)]\n\t"); #endif #ifdef BFTPDAEMON fprintf(stderr, " [-B]"); #endif fprintf(stderr, " [-debug]"); #ifdef DIAGNOSTICS fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t"); #endif #ifdef AUTHENTICATION fprintf(stderr, " [-edebug]"); #endif fprintf(stderr, " [-h]"); #if defined(LINEMODE) && defined(KLUDGELINEMODE) fprintf(stderr, " [-k]"); #endif #ifdef LINEMODE fprintf(stderr, " [-l]"); #endif fprintf(stderr, " [-n]"); fprintf(stderr, "\n\t"); #ifdef HAS_GETTOS fprintf(stderr, " [-S tos]"); #endif #ifdef AUTHENTICATION fprintf(stderr, " [-X auth-type]"); #endif fprintf(stderr, " [-u utmp_hostname_length] [-U]"); fprintf(stderr, " [port]\n"); exit(1); } /* * getterminaltype * * Ask the other end to send along its terminal type and speed. * Output is the variable terminaltype filled in. */ static unsigned char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; #ifndef AUTHENTICATION #define undef2 __unused #else #define undef2 #endif static int getterminaltype(char *name undef2) { int retval = -1; settimer(baseline); #ifdef AUTHENTICATION /* * Handle the Authentication option before we do anything else. */ if (auth_level >= 0) { send_do(TELOPT_AUTHENTICATION, 1); while (his_will_wont_is_changing(TELOPT_AUTHENTICATION)) ttloop(); if (his_state_is_will(TELOPT_AUTHENTICATION)) { retval = auth_wait(name); } } #endif #ifdef ENCRYPTION send_will(TELOPT_ENCRYPT, 1); #endif /* ENCRYPTION */ send_do(TELOPT_TTYPE, 1); send_do(TELOPT_TSPEED, 1); send_do(TELOPT_XDISPLOC, 1); send_do(TELOPT_NEW_ENVIRON, 1); send_do(TELOPT_OLD_ENVIRON, 1); while ( #ifdef ENCRYPTION his_do_dont_is_changing(TELOPT_ENCRYPT) || #endif /* ENCRYPTION */ his_will_wont_is_changing(TELOPT_TTYPE) || his_will_wont_is_changing(TELOPT_TSPEED) || his_will_wont_is_changing(TELOPT_XDISPLOC) || his_will_wont_is_changing(TELOPT_NEW_ENVIRON) || his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) { ttloop(); } #ifdef ENCRYPTION /* * Wait for the negotiation of what type of encryption we can * send with. If autoencrypt is not set, this will just return. */ if (his_state_is_will(TELOPT_ENCRYPT)) { encrypt_wait(); } #endif /* ENCRYPTION */ if (his_state_is_will(TELOPT_TSPEED)) { static unsigned char sb[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; output_datalen(sb, sizeof sb); DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_XDISPLOC)) { static unsigned char sb[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; output_datalen(sb, sizeof sb); DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_NEW_ENVIRON)) { static unsigned char sb[] = { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE }; output_datalen(sb, sizeof sb); DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } else if (his_state_is_will(TELOPT_OLD_ENVIRON)) { static unsigned char sb[] = { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE }; output_datalen(sb, sizeof sb); DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); } if (his_state_is_will(TELOPT_TTYPE)) { output_datalen(ttytype_sbbuf, sizeof ttytype_sbbuf); DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, sizeof ttytype_sbbuf - 2);); } if (his_state_is_will(TELOPT_TSPEED)) { while (sequenceIs(tspeedsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_XDISPLOC)) { while (sequenceIs(xdisplocsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_NEW_ENVIRON)) { while (sequenceIs(environsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_OLD_ENVIRON)) { while (sequenceIs(oenvironsubopt, baseline)) ttloop(); } if (his_state_is_will(TELOPT_TTYPE)) { char first[256], last[256]; while (sequenceIs(ttypesubopt, baseline)) ttloop(); /* * If the other side has already disabled the option, then * we have to just go with what we (might) have already gotten. */ if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { (void) strncpy(first, terminaltype, sizeof(first)-1); first[sizeof(first)-1] = '\0'; for(;;) { /* * Save the unknown name, and request the next name. */ (void) strncpy(last, terminaltype, sizeof(last)-1); last[sizeof(last)-1] = '\0'; _gettermname(); if (terminaltypeok(terminaltype)) break; if ((strncmp(last, terminaltype, sizeof(last)) == 0) || his_state_is_wont(TELOPT_TTYPE)) { /* * We've hit the end. If this is the same as * the first name, just go with it. */ if (strncmp(first, terminaltype, sizeof(first)) == 0) break; /* * Get the terminal name one more time, so that * RFC1091 compliant telnets will cycle back to * the start of the list. */ _gettermname(); if (strncmp(first, terminaltype, sizeof(first)) != 0) { (void) strncpy(terminaltype, first, sizeof(terminaltype)-1); terminaltype[sizeof(terminaltype)-1] = '\0'; } break; } } } } return(retval); } /* end of getterminaltype */ static void _gettermname(void) { /* * If the client turned off the option, * we can't send another request, so we * just return. */ if (his_state_is_wont(TELOPT_TTYPE)) return; settimer(baseline); output_datalen(ttytype_sbbuf, sizeof ttytype_sbbuf); DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, sizeof ttytype_sbbuf - 2);); while (sequenceIs(ttypesubopt, baseline)) ttloop(); } int terminaltypeok(char *s) { char buf[1024]; if (terminaltype == NULL) return(1); /* * tgetent() will return 1 if the type is known, and * 0 if it is not known. If it returns -1, it couldn't * open the database. But if we can't open the database, * it won't help to say we failed, because we won't be * able to verify anything else. So, we treat -1 like 1. */ if (tgetent(buf, s) == 0) return(0); return(1); } /* * Get a pty, scan input lines. */ void doit(struct sockaddr *who) { int err_; /* XXX */ int ptynum; /* * Find an available pty to use. */ #ifndef convex pty = getpty(&ptynum); if (pty < 0) fatal(net, "All network ports in use"); #else for (;;) { char *lp; if ((lp = getpty()) == NULL) fatal(net, "Out of ptys"); if ((pty = open(lp, 2)) >= 0) { strlcpy(line,lp,sizeof(line)); line[5] = 't'; break; } } #endif /* get name of connected client */ if (realhostname_sa(remote_hostname, sizeof(remote_hostname) - 1, who, who->sa_len) == HOSTNAME_INVALIDADDR && registerd_host_only) fatal(net, "Couldn't resolve your address into a host name.\r\n\ Please contact your net administrator"); remote_hostname[sizeof(remote_hostname) - 1] = '\0'; if (!isdigit(remote_hostname[0]) && strlen(remote_hostname) > utmp_len) err_ = getnameinfo(who, who->sa_len, remote_hostname, sizeof(remote_hostname), NULL, 0, NI_NUMERICHOST); /* XXX: do 'err_' check */ (void) gethostname(host_name, sizeof(host_name) - 1); host_name[sizeof(host_name) - 1] = '\0'; hostname = host_name; #ifdef AUTHENTICATION #ifdef ENCRYPTION /* The above #ifdefs should actually be "or"'ed, not "and"'ed. * This is a byproduct of needing "#ifdef" and not "#if defined()" * for unifdef. XXX MarkM */ auth_encrypt_init(hostname, remote_hostname, "TELNETD", 1); #endif #endif init_env(); /* * get terminal type. */ *user_name = 0; level = getterminaltype(user_name); setenv("TERM", terminaltype ? terminaltype : "network", 1); telnet(net, pty, remote_hostname); /* begin server process */ /*NOTREACHED*/ } /* end of doit */ /* * Main loop. Select from pty and network, and * hand data to telnet receiver finite state machine. */ void telnet(int f, int p, char *host) { int on = 1; #define TABBUFSIZ 512 char defent[TABBUFSIZ]; char defstrs[TABBUFSIZ]; #undef TABBUFSIZ char *HE; char *HN; char *IM; char *IF; char *if_buf; int if_fd = -1; struct stat statbuf; int nfd; /* * Initialize the slc mapping table. */ get_slc_defaults(); /* * Do some tests where it is desireable to wait for a response. * Rather than doing them slowly, one at a time, do them all * at once. */ if (my_state_is_wont(TELOPT_SGA)) send_will(TELOPT_SGA, 1); /* * Is the client side a 4.2 (NOT 4.3) system? We need to know this * because 4.2 clients are unable to deal with TCP urgent data. * * To find out, we send out a "DO ECHO". If the remote system * answers "WILL ECHO" it is probably a 4.2 client, and we note * that fact ("WILL ECHO" ==> that the client will echo what * WE, the server, sends it; it does NOT mean that the client will * echo the terminal input). */ send_do(TELOPT_ECHO, 1); #ifdef LINEMODE if (his_state_is_wont(TELOPT_LINEMODE)) { /* Query the peer for linemode support by trying to negotiate * the linemode option. */ linemode = 0; editmode = 0; send_do(TELOPT_LINEMODE, 1); /* send do linemode */ } #endif /* LINEMODE */ /* * Send along a couple of other options that we wish to negotiate. */ send_do(TELOPT_NAWS, 1); send_will(TELOPT_STATUS, 1); flowmode = 1; /* default flow control state */ restartany = -1; /* uninitialized... */ send_do(TELOPT_LFLOW, 1); /* * Spin, waiting for a response from the DO ECHO. However, * some REALLY DUMB telnets out there might not respond * to the DO ECHO. So, we spin looking for NAWS, (most dumb * telnets so far seem to respond with WONT for a DO that * they don't understand...) because by the time we get the * response, it will already have processed the DO ECHO. * Kludge upon kludge. */ while (his_will_wont_is_changing(TELOPT_NAWS)) ttloop(); /* * But... * The client might have sent a WILL NAWS as part of its * startup code; if so, we'll be here before we get the * response to the DO ECHO. We'll make the assumption * that any implementation that understands about NAWS * is a modern enough implementation that it will respond * to our DO ECHO request; hence we'll do another spin * waiting for the ECHO option to settle down, which is * what we wanted to do in the first place... */ if (his_want_state_is_will(TELOPT_ECHO) && his_state_is_will(TELOPT_NAWS)) { while (his_will_wont_is_changing(TELOPT_ECHO)) ttloop(); } /* * On the off chance that the telnet client is broken and does not * respond to the DO ECHO we sent, (after all, we did send the * DO NAWS negotiation after the DO ECHO, and we won't get here * until a response to the DO NAWS comes back) simulate the * receipt of a will echo. This will also send a WONT ECHO * to the client, since we assume that the client failed to * respond because it believes that it is already in DO ECHO * mode, which we do not want. */ if (his_want_state_is_will(TELOPT_ECHO)) { DIAG(TD_OPTIONS, output_data("td: simulating recv\r\n")); willoption(TELOPT_ECHO); } /* * Finally, to clean things up, we turn on our echo. This * will break stupid 4.2 telnets out of local terminal echo. */ if (my_state_is_wont(TELOPT_ECHO)) send_will(TELOPT_ECHO, 1); /* * Turn on packet mode */ (void) ioctl(p, TIOCPKT, (char *)&on); #if defined(LINEMODE) && defined(KLUDGELINEMODE) /* * Continuing line mode support. If client does not support * real linemode, attempt to negotiate kludge linemode by sending * the do timing mark sequence. */ if (lmodetype < REAL_LINEMODE) send_do(TELOPT_TM, 1); #endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ /* * Call telrcv() once to pick up anything received during * terminal type negotiation, 4.2/4.3 determination, and * linemode negotiation. */ telrcv(); (void) ioctl(f, FIONBIO, (char *)&on); (void) ioctl(p, FIONBIO, (char *)&on); #if defined(SO_OOBINLINE) (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof on); #endif /* defined(SO_OOBINLINE) */ #ifdef SIGTSTP (void) signal(SIGTSTP, SIG_IGN); #endif #ifdef SIGTTOU /* * Ignoring SIGTTOU keeps the kernel from blocking us * in ttioct() in /sys/tty.c. */ (void) signal(SIGTTOU, SIG_IGN); #endif (void) signal(SIGCHLD, cleanup); #ifdef TIOCNOTTY { int t; t = open(_PATH_TTY, O_RDWR); if (t >= 0) { (void) ioctl(t, TIOCNOTTY, (char *)0); (void) close(t); } } #endif /* * Show banner that getty never gave. * * We put the banner in the pty input buffer. This way, it * gets carriage return null processing, etc., just like all * other pty --> client data. */ if (getent(defent, "default") == 1) { char *cp=defstrs; HE = Getstr("he", &cp); HN = Getstr("hn", &cp); IM = Getstr("im", &cp); IF = Getstr("if", &cp); if (HN && *HN) (void) strlcpy(host_name, HN, sizeof(host_name)); if (IF) { if_fd = open(IF, O_RDONLY, 000); IM = 0; } if (IM == 0) IM = strdup(""); } else { IM = strdup(DEFAULT_IM); HE = 0; } edithost(HE, host_name); if (hostinfo && *IM) putf(IM, ptyibuf2); if (if_fd != -1) { if (fstat(if_fd, &statbuf) != -1 && statbuf.st_size > 0) { if_buf = (char *) mmap (0, statbuf.st_size, PROT_READ, 0, if_fd, 0); if (if_buf != MAP_FAILED) { putf(if_buf, ptyibuf2); munmap(if_buf, statbuf.st_size); } } close (if_fd); } if (pcc) (void) strncat(ptyibuf2, ptyip, pcc+1); ptyip = ptyibuf2; pcc = strlen(ptyip); #ifdef LINEMODE /* * Last check to make sure all our states are correct. */ init_termbuf(); localstat(); #endif /* LINEMODE */ DIAG(TD_REPORT, output_data("td: Entering processing loop\r\n")); /* * Startup the login process on the slave side of the terminal * now. We delay this until here to insure option negotiation * is complete. */ startslave(host, level, user_name); nfd = ((f > p) ? f : p) + 1; for (;;) { fd_set ibits, obits, xbits; int c; if (ncc < 0 && pcc < 0) break; FD_ZERO(&ibits); FD_ZERO(&obits); FD_ZERO(&xbits); /* * Never look for input if there's still * stuff in the corresponding output buffer */ if (nfrontp - nbackp || pcc > 0) { FD_SET(f, &obits); } else { FD_SET(p, &ibits); } if (pfrontp - pbackp || ncc > 0) { FD_SET(p, &obits); } else { FD_SET(f, &ibits); } if (!SYNCHing) { FD_SET(f, &xbits); } if ((c = select(nfd, &ibits, &obits, &xbits, (struct timeval *)0)) < 1) { if (c == -1) { if (errno == EINTR) { continue; } } sleep(5); continue; } /* * Any urgent data? */ if (FD_ISSET(net, &xbits)) { SYNCHing = 1; } /* * Something to read from the network... */ if (FD_ISSET(net, &ibits)) { #if !defined(SO_OOBINLINE) /* * In 4.2 (and 4.3 beta) systems, the * OOB indication and data handling in the kernel * is such that if two separate TCP Urgent requests * come in, one byte of TCP data will be overlaid. * This is fatal for Telnet, but we try to live * with it. * * In addition, in 4.2 (and...), a special protocol * is needed to pick up the TCP Urgent data in * the correct sequence. * * What we do is: if we think we are in urgent * mode, we look to see if we are "at the mark". * If we are, we do an OOB receive. If we run * this twice, we will do the OOB receive twice, * but the second will fail, since the second * time we were "at the mark", but there wasn't * any data there (the kernel doesn't reset * "at the mark" until we do a normal read). * Once we've read the OOB data, we go ahead * and do normal reads. * * There is also another problem, which is that * since the OOB byte we read doesn't put us * out of OOB state, and since that byte is most * likely the TELNET DM (data mark), we would * stay in the TELNET SYNCH (SYNCHing) state. * So, clocks to the rescue. If we've "just" * received a DM, then we test for the * presence of OOB data when the receive OOB * fails (and AFTER we did the normal mode read * to clear "at the mark"). */ if (SYNCHing) { int atmark; (void) ioctl(net, SIOCATMARK, (char *)&atmark); if (atmark) { ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); if ((ncc == -1) && (errno == EINVAL)) { ncc = read(net, netibuf, sizeof (netibuf)); if (sequenceIs(didnetreceive, gotDM)) { SYNCHing = stilloob(net); } } } else { ncc = read(net, netibuf, sizeof (netibuf)); } } else { ncc = read(net, netibuf, sizeof (netibuf)); } settimer(didnetreceive); #else /* !defined(SO_OOBINLINE)) */ ncc = read(net, netibuf, sizeof (netibuf)); #endif /* !defined(SO_OOBINLINE)) */ if (ncc < 0 && errno == EWOULDBLOCK) ncc = 0; else { if (ncc <= 0) { break; } netip = netibuf; } DIAG((TD_REPORT | TD_NETDATA), output_data("td: netread %d chars\r\n", ncc)); DIAG(TD_NETDATA, printdata("nd", netip, ncc)); } /* * Something to read from the pty... */ if (FD_ISSET(p, &ibits)) { pcc = read(p, ptyibuf, BUFSIZ); /* * On some systems, if we try to read something * off the master side before the slave side is * opened, we get EIO. */ if (pcc < 0 && (errno == EWOULDBLOCK || #ifdef EAGAIN errno == EAGAIN || #endif errno == EIO)) { pcc = 0; } else { if (pcc <= 0) break; #ifdef LINEMODE /* * If ioctl from pty, pass it through net */ if (ptyibuf[0] & TIOCPKT_IOCTL) { copy_termbuf(ptyibuf+1, pcc-1); localstat(); pcc = 1; } #endif /* LINEMODE */ if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { netclear(); /* clear buffer back */ #ifndef NO_URGENT /* * There are client telnets on some * operating systems get screwed up * royally if we send them urgent * mode data. */ output_data("%c%c", IAC, DM); neturg = nfrontp-1; /* off by one XXX */ DIAG(TD_OPTIONS, printoption("td: send IAC", DM)); #endif } if (his_state_is_will(TELOPT_LFLOW) && (ptyibuf[0] & (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { int newflow = ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0; if (newflow != flowmode) { flowmode = newflow; output_data("%c%c%c%c%c%c", IAC, SB, TELOPT_LFLOW, flowmode ? LFLOW_ON : LFLOW_OFF, IAC, SE); DIAG(TD_OPTIONS, printsub('>', (unsigned char *)nfrontp-4, 4);); } } pcc--; ptyip = ptyibuf+1; } } while (pcc > 0) { if ((&netobuf[BUFSIZ] - nfrontp) < 2) break; c = *ptyip++ & 0377, pcc--; if (c == IAC) output_data("%c", c); output_data("%c", c); if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { if (pcc > 0 && ((*ptyip & 0377) == '\n')) { output_data("%c", *ptyip++ & 0377); pcc--; } else output_data("%c", '\0'); } } if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) netflush(); if (ncc > 0) telrcv(); if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) ptyflush(); } cleanup(0); } /* end of telnet */ #ifndef TCSIG # ifdef TIOCSIG # define TCSIG TIOCSIG # endif #endif /* * Send interrupt to process on other side of pty. * If it is in raw mode, just write NULL; * otherwise, write intr char. */ void interrupt(void) { ptyflush(); /* half-hearted */ #ifdef TCSIG (void) ioctl(pty, TCSIG, SIGINT); #else /* TCSIG */ init_termbuf(); *pfrontp++ = slctab[SLC_IP].sptr ? (unsigned char)*slctab[SLC_IP].sptr : '\177'; #endif /* TCSIG */ } /* * Send quit to process on other side of pty. * If it is in raw mode, just write NULL; * otherwise, write quit char. */ void sendbrk(void) { ptyflush(); /* half-hearted */ #ifdef TCSIG (void) ioctl(pty, TCSIG, SIGQUIT); #else /* TCSIG */ init_termbuf(); *pfrontp++ = slctab[SLC_ABORT].sptr ? (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; #endif /* TCSIG */ } void sendsusp(void) { #ifdef SIGTSTP ptyflush(); /* half-hearted */ # ifdef TCSIG (void) ioctl(pty, TCSIG, SIGTSTP); # else /* TCSIG */ *pfrontp++ = slctab[SLC_SUSP].sptr ? (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; # endif /* TCSIG */ #endif /* SIGTSTP */ } /* * When we get an AYT, if ^T is enabled, use that. Otherwise, * just send back "[Yes]". */ void recv_ayt(void) { #if defined(SIGINFO) && defined(TCSIG) if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) { (void) ioctl(pty, TCSIG, SIGINFO); return; } #endif output_data("\r\n[Yes]\r\n"); } void doeof(void) { init_termbuf(); #if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN) if (!tty_isediting()) { extern char oldeofc; *pfrontp++ = oldeofc; return; } #endif *pfrontp++ = slctab[SLC_EOF].sptr ? (unsigned char)*slctab[SLC_EOF].sptr : '\004'; } Index: stable/11/gnu/usr.bin/gdb/Makefile.inc =================================================================== --- stable/11/gnu/usr.bin/gdb/Makefile.inc (revision 359753) +++ stable/11/gnu/usr.bin/gdb/Makefile.inc (revision 359754) @@ -1,63 +1,68 @@ # $FreeBSD$ VERSION= "6.1.1 [FreeBSD]" VENDOR= marcel PACKAGE= gdb BMAKE_GDB= ${.CURDIR:H} BMAKE_ROOT= ${BMAKE_GDB:H} BMAKE_BU= ${BMAKE_ROOT}/binutils CNTRB_BU= ${SRCTOP}/contrib/binutils CNTRB_GDB= ${SRCTOP}/contrib/gdb CNTRB_RL= ${SRCTOP}/contrib/libreadline OBJ_BU= ${OBJTOP}/gnu/usr.bin/binutils OBJ_GDB= ${OBJTOP}/gnu/usr.bin/gdb OBJ_RL= ${OBJTOP}/gnu/lib/libreadline/readline # These assignments duplicate much of the functionality of # MACHINE_CPUARCH, but there's no easy way to export make functions... .if defined(TARGET_ARCH) TARGET_CPUARCH=${TARGET_ARCH:C/mips(n32|64)?(el)?/mips/:C/arm(v6)?(eb)?/arm/:C/powerpc64/powerpc/} .else TARGET_CPUARCH=${MACHINE_CPUARCH} .endif TARGET_ARCH?= ${MACHINE_ARCH} TARGET_SUBDIR= ${BMAKE_GDB}/arch/${TARGET_CPUARCH} .if ${TARGET_ARCH} != ${MACHINE_ARCH} GDB_CROSS_DEBUGGER= .endif .PATH: ${CNTRB_GDB}/gdb ${CNTRB_GDB}/gdb/cli ${CNTRB_GDB}/gdb/mi \ ${CNTRB_GDB}/gdb/signals ${CNTRB_GDB}/gdb/tui ${TARGET_SUBDIR} CFLAGS+= -DHAVE_CONFIG_H -DRL_NO_COMPAT -DMI_OUT=1 -DTUI=1 CFLAGS+= -DDEBUGDIR=\"${DEBUGDIR}\" CFLAGS+= -I. CFLAGS+= -I${TARGET_SUBDIR} CFLAGS+= -I${BMAKE_BU}/libbfd -I${BMAKE_BU}/libbfd/${TARGET_CPUARCH} CFLAGS+= -I${CNTRB_GDB}/gdb CFLAGS+= -I${CNTRB_GDB}/gdb/config CFLAGS+= -I${CNTRB_BU}/include CFLAGS+= -I${CNTRB_GDB}/include CFLAGS+= -I${CNTRB_BU}/bfd CFLAGS+= -I${OBJ_RL:H} +# Some bits here currently rely on some of the linker-merging magic that happens +# with -fcommon. While this is the default right now, explicitly set -fcommon +# so that it continues to build when the default flips. +CFLAGS+= -fcommon + GENSRCS+= nm.h tm.h .if defined(GDB_CROSS_DEBUGGER) CFLAGS+= -DCROSS_DEBUGGER -I${BMAKE_ROOT:H:H} GDB_SUFFIX= -${TARGET_ARCH} MAN= .endif .include "${TARGET_SUBDIR}/Makefile" SRCS+= ${GENSRCS} CLEANFILES+= ${GENSRCS} .include "../Makefile.inc" Index: stable/11/libexec/ypxfr/ypxfr_main.c =================================================================== --- stable/11/libexec/ypxfr/ypxfr_main.c (revision 359753) +++ stable/11/libexec/ypxfr/ypxfr_main.c (revision 359754) @@ -1,577 +1,577 @@ /* * Copyright (c) 1995 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ypxfr_extern.h" +int debug = 1; + char *progname = "ypxfr"; char *yp_dir = _PATH_YP; int _rpcpmstart = 0; static int ypxfr_use_yplib = 0; /* Assume the worst. */ static int ypxfr_clear = 1; static int ypxfr_prognum = 0; static struct sockaddr_in ypxfr_callback_addr; static struct yppushresp_xfr ypxfr_resp; static DB *dbp; static void ypxfr_exit(ypxfrstat retval, char *temp) { CLIENT *clnt; int sock = RPC_ANYSOCK; struct timeval timeout; /* Clean up no matter what happened previously. */ if (temp != NULL) { if (dbp != NULL) (void)(dbp->close)(dbp); if (unlink(temp) == -1) { yp_error("failed to unlink %s",strerror(errno)); } } if (ypxfr_prognum) { timeout.tv_sec = 20; timeout.tv_usec = 0; if ((clnt = clntudp_create(&ypxfr_callback_addr, ypxfr_prognum, 1, timeout, &sock)) == NULL) { yp_error("%s", clnt_spcreateerror("failed to " "establish callback handle")); exit(1); } ypxfr_resp.status = (yppush_status)retval; if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) { yp_error("%s", clnt_sperror(clnt, "callback failed")); clnt_destroy(clnt); exit(1); } clnt_destroy(clnt); } else { yp_error("Exiting: %s", ypxfrerr_string(retval)); } exit(0); } static void usage(void) { if (_rpcpmstart) { ypxfr_exit(YPXFR_BADARGS,NULL); } else { fprintf(stderr, "%s\n%s\n%s\n", "usage: ypxfr [-f] [-c] [-d target domain] [-h source host]", " [-s source domain] [-p path]", " [-C taskid program-number ipaddr port] mapname"); exit(1); } } int ypxfr_foreach(int status, char *key, int keylen, char *val, int vallen, char *data) { DBT dbkey, dbval; if (status != YP_TRUE) return (status); /* * XXX Do not attempt to write zero-length keys or * data into a Berkeley DB hash database. It causes a * strange failure mode where sequential searches get * caught in an infinite loop. */ if (keylen) { dbkey.data = key; dbkey.size = keylen; } else { dbkey.data = ""; dbkey.size = 1; } if (vallen) { dbval.data = val; dbval.size = vallen; } else { dbval.data = ""; dbval.size = 1; } if (yp_put_record(dbp, &dbkey, &dbval, 0) != YP_TRUE) return(yp_errno); return (0); } int main(int argc, char *argv[]) { int ch; int ypxfr_force = 0; char *ypxfr_dest_domain = NULL; char *ypxfr_source_host = NULL; char *ypxfr_source_domain = NULL; char *ypxfr_local_domain = NULL; char *ypxfr_master = NULL; unsigned long ypxfr_order = -1, ypxfr_skew_check = -1; char *ypxfr_mapname = NULL; int ypxfr_args = 0; char ypxfr_temp_map[MAXPATHLEN + 2]; char tempmap[MAXPATHLEN + 2]; char buf[MAXPATHLEN + 2]; DBT key, data; int remoteport; int interdom = 0; int secure = 0; - - debug = 1; if (!isatty(fileno(stderr))) { openlog("ypxfr", LOG_PID, LOG_DAEMON); _rpcpmstart = 1; } if (argc < 2) usage(); while ((ch = getopt(argc, argv, "fcd:h:s:p:C:")) != -1) { int my_optind; switch (ch) { case 'f': ypxfr_force++; ypxfr_args++; break; case 'c': ypxfr_clear = 0; ypxfr_args++; break; case 'd': ypxfr_dest_domain = optarg; ypxfr_args += 2; break; case 'h': ypxfr_source_host = optarg; ypxfr_args += 2; break; case 's': ypxfr_source_domain = optarg; ypxfr_args += 2; break; case 'p': yp_dir = optarg; ypxfr_args += 2; break; case 'C': /* * Whoever decided that the -C flag should take * four arguments is a twit. */ my_optind = optind - 1; if (argv[my_optind] == NULL || !strlen(argv[my_optind])) { yp_error("transaction ID not specified"); usage(); } ypxfr_resp.transid = atol(argv[my_optind]); my_optind++; if (argv[my_optind] == NULL || !strlen(argv[my_optind])) { yp_error("RPC program number not specified"); usage(); } ypxfr_prognum = atol(argv[my_optind]); my_optind++; if (argv[my_optind] == NULL || !strlen(argv[my_optind])) { yp_error("address not specified"); usage(); } if (!inet_aton(argv[my_optind], &ypxfr_callback_addr.sin_addr)) { yp_error("failed to convert '%s' to IP addr", argv[my_optind]); exit(1); } my_optind++; if (argv[my_optind] == NULL || !strlen(argv[my_optind])) { yp_error("port not specified"); usage(); } ypxfr_callback_addr.sin_port = htons((u_short)atoi(argv[my_optind])); ypxfr_args += 5; break; default: usage(); break; } } ypxfr_mapname = argv[ypxfr_args + 1]; if (ypxfr_mapname == NULL) { yp_error("no map name specified"); usage(); } /* Always the case. */ ypxfr_callback_addr.sin_family = AF_INET; /* Determine if local NIS client facilities are turned on. */ if (!yp_get_default_domain(&ypxfr_local_domain) && _yp_check(&ypxfr_local_domain)) ypxfr_use_yplib = 1; /* * If no destination domain is specified, assume that the * local default domain is to be used and try to obtain it. * Fails if NIS client facilities are turned off. */ if (ypxfr_dest_domain == NULL) { if (ypxfr_use_yplib) { yp_get_default_domain(&ypxfr_dest_domain); } else { yp_error("no destination domain specified and \ the local domain name isn't set"); ypxfr_exit(YPXFR_BADARGS,NULL); } } /* * If a source domain is not specified, assume it to * be the same as the destination domain. */ if (ypxfr_source_domain == NULL) { ypxfr_source_domain = ypxfr_dest_domain; } /* * If the source host is not specified, assume it to be the * master for the specified map. If local NIS client facilities * are turned on, we can figure this out using yp_master(). * If not, we have to see if a local copy of the map exists * and extract its YP_MASTER_NAME record. If _that_ fails, * we are stuck and must ask the user for more information. */ if (ypxfr_source_host == NULL) { if (!ypxfr_use_yplib) { /* * Double whammy: NIS isn't turned on and the user * didn't specify a source host. */ char *dptr; key.data = "YP_MASTER_NAME"; key.size = sizeof("YP_MASTER_NAME") - 1; if (yp_get_record(ypxfr_dest_domain, ypxfr_mapname, &key, &data, 1) != YP_TRUE) { yp_error("no source host specified"); ypxfr_exit(YPXFR_BADARGS,NULL); } dptr = data.data; dptr[data.size] = '\0'; ypxfr_master = ypxfr_source_host = strdup(dptr); } } else { if (ypxfr_use_yplib) ypxfr_use_yplib = 0; } if (ypxfr_master == NULL) { if ((ypxfr_master = ypxfr_get_master(ypxfr_source_domain, ypxfr_mapname, ypxfr_source_host, ypxfr_use_yplib)) == NULL) { yp_error("failed to find master of %s in domain %s: %s", ypxfr_mapname, ypxfr_source_domain, ypxfrerr_string((ypxfrstat)yp_errno)); ypxfr_exit(YPXFR_MADDR,NULL); } } /* * If we got here and ypxfr_source_host is still undefined, * it means we had to resort to using yp_master() to find the * master server for the map. The source host and master should * be identical. */ if (ypxfr_source_host == NULL) ypxfr_source_host = ypxfr_master; /* * Don't talk to ypservs on unprivileged ports. */ remoteport = getrpcport(ypxfr_source_host, YPPROG, YPVERS, IPPROTO_UDP); if (remoteport >= IPPORT_RESERVED) { yp_error("ypserv on %s not running on reserved port", ypxfr_source_host); ypxfr_exit(YPXFR_REFUSED, NULL); } if ((ypxfr_order = ypxfr_get_order(ypxfr_source_domain, ypxfr_mapname, ypxfr_master, 0)) == 0) { yp_error("failed to get order number of %s: %s", ypxfr_mapname, yp_errno == YP_TRUE ? "map has order 0" : ypxfrerr_string((ypxfrstat)yp_errno)); ypxfr_exit(YPXFR_YPERR,NULL); } if (ypxfr_match(ypxfr_master, ypxfr_source_domain, ypxfr_mapname, "YP_INTERDOMAIN", sizeof("YP_INTERDOMAIN") - 1)) interdom++; if (ypxfr_match(ypxfr_master, ypxfr_source_domain, ypxfr_mapname, "YP_SECURE", sizeof("YP_SECURE") - 1)) secure++; key.data = "YP_LAST_MODIFIED"; key.size = sizeof("YP_LAST_MODIFIED") - 1; /* The order number is immaterial when the 'force' flag is set. */ if (!ypxfr_force) { int ignore = 0; if (yp_get_record(ypxfr_dest_domain,ypxfr_mapname,&key,&data,1) != YP_TRUE) { switch (yp_errno) { case YP_NOKEY: ypxfr_exit(YPXFR_FORCE,NULL); break; case YP_NOMAP: /* * If the map doesn't exist, we're * creating it. Ignore the error. */ ignore++; break; case YP_BADDB: default: ypxfr_exit(YPXFR_DBM,NULL); break; } } if (!ignore && ypxfr_order <= atoi(data.data)) ypxfr_exit(YPXFR_AGE, NULL); } /* Construct a temporary map file name */ snprintf(tempmap, sizeof(tempmap), "%s.%d",ypxfr_mapname, getpid()); snprintf(ypxfr_temp_map, sizeof(ypxfr_temp_map), "%s/%s/%s", yp_dir, ypxfr_dest_domain, tempmap); if ((remoteport = getrpcport(ypxfr_source_host, YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, IPPROTO_TCP))) { /* Don't talk to rpc.ypxfrds on unprovileged ports. */ if (remoteport >= IPPORT_RESERVED) { yp_error("rpc.ypxfrd on %s not using privileged port", ypxfr_source_host); ypxfr_exit(YPXFR_REFUSED, NULL); } /* Try to send using ypxfrd. If it fails, use old method. */ if (!ypxfrd_get_map(ypxfr_source_host, ypxfr_mapname, ypxfr_source_domain, ypxfr_temp_map)) goto leave; } /* Open the temporary map read/write. */ if ((dbp = yp_open_db_rw(ypxfr_dest_domain, tempmap, 0)) == NULL) { yp_error("failed to open temporary map file"); ypxfr_exit(YPXFR_DBM,NULL); } /* * Fill in the keys we already know, such as the order number, * master name, input file name (we actually make up a bogus * name for that) and output file name. */ snprintf(buf, sizeof(buf), "%lu", ypxfr_order); data.data = buf; data.size = strlen(buf); if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) { yp_error("failed to write order number to database"); ypxfr_exit(YPXFR_DBM,ypxfr_temp_map); } key.data = "YP_MASTER_NAME"; key.size = sizeof("YP_MASTER_NAME") - 1; data.data = ypxfr_master; data.size = strlen(ypxfr_master); if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) { yp_error("failed to write master name to database"); ypxfr_exit(YPXFR_DBM,ypxfr_temp_map); } key.data = "YP_DOMAIN_NAME"; key.size = sizeof("YP_DOMAIN_NAME") - 1; data.data = ypxfr_dest_domain; data.size = strlen(ypxfr_dest_domain); if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) { yp_error("failed to write domain name to database"); ypxfr_exit(YPXFR_DBM,ypxfr_temp_map); } snprintf (buf, sizeof(buf), "%s:%s", ypxfr_source_host, ypxfr_mapname); key.data = "YP_INPUT_NAME"; key.size = sizeof("YP_INPUT_NAME") - 1; data.data = &buf; data.size = strlen(buf); if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) { yp_error("failed to write input name to database"); ypxfr_exit(YPXFR_DBM,ypxfr_temp_map); } snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, ypxfr_dest_domain, ypxfr_mapname); key.data = "YP_OUTPUT_NAME"; key.size = sizeof("YP_OUTPUT_NAME") - 1; data.data = &buf; data.size = strlen(buf); if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) { yp_error("failed to write output name to database"); ypxfr_exit(YPXFR_DBM,ypxfr_temp_map); } if (interdom) { key.data = "YP_INTERDOMAIN"; key.size = sizeof("YP_INTERDOMAIN") - 1; data.data = ""; data.size = 0; if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) { yp_error("failed to add interdomain flag to database"); ypxfr_exit(YPXFR_DBM,ypxfr_temp_map); } } if (secure) { key.data = "YP_SECURE"; key.size = sizeof("YP_SECURE") - 1; data.data = ""; data.size = 0; if (yp_put_record(dbp, &key, &data, 0) != YP_TRUE) { yp_error("failed to add secure flag to database"); ypxfr_exit(YPXFR_DBM,ypxfr_temp_map); } } /* Now suck over the contents of the map from the master. */ if (ypxfr_get_map(ypxfr_mapname,ypxfr_source_domain, ypxfr_source_host, ypxfr_foreach)){ yp_error("failed to retrieve map from source host"); ypxfr_exit(YPXFR_YPERR,ypxfr_temp_map); } (void)(dbp->close)(dbp); dbp = NULL; /* <- yes, it seems this is necessary. */ leave: snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, ypxfr_dest_domain, ypxfr_mapname); /* Peek at the order number again and check for skew. */ if ((ypxfr_skew_check = ypxfr_get_order(ypxfr_source_domain, ypxfr_mapname, ypxfr_master, 0)) == 0) { yp_error("failed to get order number of %s: %s", ypxfr_mapname, yp_errno == YP_TRUE ? "map has order 0" : ypxfrerr_string((ypxfrstat)yp_errno)); ypxfr_exit(YPXFR_YPERR,ypxfr_temp_map); } if (ypxfr_order != ypxfr_skew_check) ypxfr_exit(YPXFR_SKEW,ypxfr_temp_map); /* * Send a YPPROC_CLEAR to the local ypserv. */ if (ypxfr_clear) { char in = 0; char *out = NULL; int stat; if ((stat = callrpc("localhost",YPPROG,YPVERS,YPPROC_CLEAR, (xdrproc_t)xdr_void, (void *)&in, (xdrproc_t)xdr_void, (void *)out)) != RPC_SUCCESS) { yp_error("failed to send 'clear' to local ypserv: %s", clnt_sperrno((enum clnt_stat) stat)); ypxfr_exit(YPXFR_CLEAR, ypxfr_temp_map); } } /* * Put the new map in place immediately. I'm not sure if the * kernel does an unlink() and rename() atomically in the event * that we move a new copy of a map over the top of an existing * one, but there's less chance of a race condition happening * than if we were to do the unlink() ourselves. */ if (rename(ypxfr_temp_map, buf) == -1) { yp_error("rename(%s,%s) failed: %s", ypxfr_temp_map, buf, strerror(errno)); ypxfr_exit(YPXFR_FILE,NULL); } ypxfr_exit(YPXFR_SUCC,NULL); return(1); } Index: stable/11/sbin/fsck_ffs/fsck.h =================================================================== --- stable/11/sbin/fsck_ffs/fsck.h (revision 359753) +++ stable/11/sbin/fsck_ffs/fsck.h (revision 359754) @@ -1,480 +1,480 @@ /* * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Marshall * Kirk McKusick and Network Associates Laboratories, the Security * Research Division of Network Associates, Inc. under DARPA/SPAWAR * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS * research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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. * * @(#)fsck.h 8.4 (Berkeley) 5/9/95 * $FreeBSD$ */ #ifndef _FSCK_H_ #define _FSCK_H_ #include #include #include #include #define MAXDUP 10 /* limit on dup blks (per inode) */ #define MAXBAD 10 /* limit on bad blks (per inode) */ #define MINBUFS 10 /* minimum number of buffers required */ #define MAXBUFS 40 /* maximum space to allocate to buffers */ #define INOBUFSIZE 64*1024 /* size of buffer to read inodes in pass1 */ #define ZEROBUFSIZE (dev_bsize * 128) /* size of zero buffer used by -Z */ union dinode { struct ufs1_dinode dp1; struct ufs2_dinode dp2; }; #define DIP(dp, field) \ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ (dp)->dp1.field : (dp)->dp2.field) #define DIP_SET(dp, field, val) do { \ if (sblock.fs_magic == FS_UFS1_MAGIC) \ (dp)->dp1.field = (val); \ else \ (dp)->dp2.field = (val); \ } while (0) /* * Each inode on the file system is described by the following structure. * The linkcnt is initially set to the value in the inode. Each time it * is found during the descent in passes 2, 3, and 4 the count is * decremented. Any inodes whose count is non-zero after pass 4 needs to * have its link count adjusted by the value remaining in ino_linkcnt. */ struct inostat { char ino_state; /* state of inode, see below */ char ino_type; /* type of inode */ short ino_linkcnt; /* number of links not found */ }; /* * Inode states. */ #define USTATE 0x1 /* inode not allocated */ #define FSTATE 0x2 /* inode is file */ #define FZLINK 0x3 /* inode is file with a link count of zero */ #define DSTATE 0x4 /* inode is directory */ #define DZLINK 0x5 /* inode is directory with a zero link count */ #define DFOUND 0x6 /* directory found during descent */ /* 0x7 UNUSED - see S_IS_DVALID() definition */ #define DCLEAR 0x8 /* directory is to be cleared */ #define FCLEAR 0x9 /* file is to be cleared */ /* DUNFOUND === (state == DSTATE || state == DZLINK) */ #define S_IS_DUNFOUND(state) (((state) & ~0x1) == DSTATE) /* DVALID === (state == DSTATE || state == DZLINK || state == DFOUND) */ #define S_IS_DVALID(state) (((state) & ~0x3) == DSTATE) #define INO_IS_DUNFOUND(ino) S_IS_DUNFOUND(inoinfo(ino)->ino_state) #define INO_IS_DVALID(ino) S_IS_DVALID(inoinfo(ino)->ino_state) /* * Inode state information is contained on per cylinder group lists * which are described by the following structure. */ -struct inostatlist { +extern struct inostatlist { long il_numalloced; /* number of inodes allocated in this cg */ struct inostat *il_stat;/* inostat info for this cylinder group */ } *inostathead; /* * buffer cache structure. */ struct bufarea { TAILQ_ENTRY(bufarea) b_list; /* buffer list */ ufs2_daddr_t b_bno; int b_size; int b_errs; int b_flags; int b_type; union { char *b_buf; /* buffer space */ ufs1_daddr_t *b_indir1; /* UFS1 indirect block */ ufs2_daddr_t *b_indir2; /* UFS2 indirect block */ struct fs *b_fs; /* super block */ struct cg *b_cg; /* cylinder group */ struct ufs1_dinode *b_dinode1; /* UFS1 inode block */ struct ufs2_dinode *b_dinode2; /* UFS2 inode block */ } b_un; char b_dirty; }; #define IBLK(bp, i) \ ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ (bp)->b_un.b_indir1[i] : (bp)->b_un.b_indir2[i]) #define IBLK_SET(bp, i, val) do { \ if (sblock.fs_magic == FS_UFS1_MAGIC) \ (bp)->b_un.b_indir1[i] = (val); \ else \ (bp)->b_un.b_indir2[i] = (val); \ } while (0) /* * Buffer flags */ #define B_INUSE 0x00000001 /* Buffer is in use */ /* * Type of data in buffer */ #define BT_UNKNOWN 0 /* Buffer holds a superblock */ #define BT_SUPERBLK 1 /* Buffer holds a superblock */ #define BT_CYLGRP 2 /* Buffer holds a cylinder group map */ #define BT_LEVEL1 3 /* Buffer holds single level indirect */ #define BT_LEVEL2 4 /* Buffer holds double level indirect */ #define BT_LEVEL3 5 /* Buffer holds triple level indirect */ #define BT_EXTATTR 6 /* Buffer holds external attribute data */ #define BT_INODES 7 /* Buffer holds external attribute data */ #define BT_DIRDATA 8 /* Buffer holds directory data */ #define BT_DATA 9 /* Buffer holds user data */ #define BT_NUMBUFTYPES 10 #define BT_NAMES { \ "unknown", \ "Superblock", \ "Cylinder Group", \ "Single Level Indirect", \ "Double Level Indirect", \ "Triple Level Indirect", \ "External Attribute", \ "Inode Block", \ "Directory Contents", \ "User Data" } extern long readcnt[BT_NUMBUFTYPES]; extern long totalreadcnt[BT_NUMBUFTYPES]; extern struct timespec readtime[BT_NUMBUFTYPES]; extern struct timespec totalreadtime[BT_NUMBUFTYPES]; extern struct timespec startprog; extern struct bufarea sblk; /* file system superblock */ extern struct bufarea *pdirbp; /* current directory contents */ extern struct bufarea *pbp; /* current inode block */ #define dirty(bp) do { \ if (fswritefd < 0) \ pfatal("SETTING DIRTY FLAG IN READ_ONLY MODE\n"); \ else \ (bp)->b_dirty = 1; \ } while (0) #define initbarea(bp, type) do { \ (bp)->b_dirty = 0; \ (bp)->b_bno = (ufs2_daddr_t)-1; \ (bp)->b_flags = 0; \ (bp)->b_type = type; \ } while (0) #define sbdirty() dirty(&sblk) #define sblock (*sblk.b_un.b_fs) enum fixstate {DONTKNOW, NOFIX, FIX, IGNORE}; extern ino_t cursnapshot; struct inodesc { enum fixstate id_fix; /* policy on fixing errors */ int (*id_func)(struct inodesc *); /* function to be applied to blocks of inode */ ino_t id_number; /* inode number described */ ino_t id_parent; /* for DATA nodes, their parent */ ufs_lbn_t id_lbn; /* logical block number of current block */ ufs2_daddr_t id_blkno; /* current block number being examined */ int id_level; /* level of indirection of this block */ int id_numfrags; /* number of frags contained in block */ ufs_lbn_t id_lballoc; /* pass1: last LBN that is allocated */ off_t id_filesize; /* for DATA nodes, the size of the directory */ ufs2_daddr_t id_entryno;/* for DATA nodes, current entry number */ int id_loc; /* for DATA nodes, current location in dir */ struct direct *id_dirp; /* for DATA nodes, ptr to current entry */ char *id_name; /* for DATA nodes, name to find or enter */ char id_type; /* type of descriptor, DATA or ADDR */ }; /* file types */ #define DATA 1 /* a directory */ #define SNAP 2 /* a snapshot */ #define ADDR 3 /* anything but a directory or a snapshot */ /* * Linked list of duplicate blocks. * * The list is composed of two parts. The first part of the * list (from duplist through the node pointed to by muldup) * contains a single copy of each duplicate block that has been * found. The second part of the list (from muldup to the end) * contains duplicate blocks that have been found more than once. * To check if a block has been found as a duplicate it is only * necessary to search from duplist through muldup. To find the * total number of times that a block has been found as a duplicate * the entire list must be searched for occurrences of the block * in question. The following diagram shows a sample list where * w (found twice), x (found once), y (found three times), and z * (found once) are duplicate block numbers: * * w -> y -> x -> z -> y -> w -> y * ^ ^ * | | * duplist muldup */ struct dups { struct dups *next; ufs2_daddr_t dup; }; -struct dups *duplist; /* head of dup list */ -struct dups *muldup; /* end of unique duplicate dup block numbers */ +extern struct dups *duplist; /* head of dup list */ +extern struct dups *muldup; /* end of unique duplicate dup block numbers */ /* * Inode cache data structures. */ -struct inoinfo { +extern struct inoinfo { struct inoinfo *i_nexthash; /* next entry in hash chain */ ino_t i_number; /* inode number of this entry */ ino_t i_parent; /* inode number of parent */ ino_t i_dotdot; /* inode number of `..' */ size_t i_isize; /* size of inode */ u_int i_numblks; /* size of block array in bytes */ ufs2_daddr_t i_blks[1]; /* actually longer */ } **inphead, **inpsort; extern long dirhash, inplast; extern unsigned long numdirs, listmax; extern long countdirs; /* number of directories we actually found */ #define MIBSIZE 3 /* size of fsck sysctl MIBs */ extern int adjrefcnt[MIBSIZE]; /* MIB command to adjust inode reference cnt */ extern int adjblkcnt[MIBSIZE]; /* MIB command to adjust inode block count */ extern int setsize[MIBSIZE]; /* MIB command to set inode size */ extern int adjndir[MIBSIZE]; /* MIB command to adjust number of directories */ extern int adjnbfree[MIBSIZE]; /* MIB command to adjust number of free blocks */ extern int adjnifree[MIBSIZE]; /* MIB command to adjust number of free inodes */ extern int adjnffree[MIBSIZE]; /* MIB command to adjust number of free frags */ extern int adjnumclusters[MIBSIZE]; /* MIB command to adjust number of free clusters */ extern int freefiles[MIBSIZE]; /* MIB command to free a set of files */ extern int freedirs[MIBSIZE]; /* MIB command to free a set of directories */ extern int freeblks[MIBSIZE]; /* MIB command to free a set of data blocks */ extern struct fsck_cmd cmd; /* sysctl file system update commands */ extern char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */ extern char *cdevname; /* name of device being checked */ extern long dev_bsize; /* computed value of DEV_BSIZE */ extern long secsize; /* actual disk sector size */ extern u_int real_dev_bsize; /* actual disk sector size, not overridden */ extern char nflag; /* assume a no response */ extern char yflag; /* assume a yes response */ extern int bkgrdflag; /* use a snapshot to run on an active system */ extern ufs2_daddr_t bflag; /* location of alternate super block */ extern int debug; /* output debugging info */ extern int Eflag; /* delete empty data blocks */ extern int Zflag; /* zero empty data blocks */ extern int zflag; /* zero unused directory space */ extern int inoopt; /* trim out unused inodes */ extern char ckclean; /* only do work if not cleanly unmounted */ extern int cvtlevel; /* convert to newer file system format */ extern int bkgrdcheck; /* determine if background check is possible */ extern int bkgrdsumadj; /* whether the kernel have ability to adjust superblock summary */ extern char usedsoftdep; /* just fix soft dependency inconsistencies */ extern char preen; /* just fix normal inconsistencies */ extern char rerun; /* rerun fsck. Only used in non-preen mode */ extern int returntosingle; /* 1 => return to single user mode on exit */ extern char resolved; /* cleared if unresolved changes => not clean */ extern char havesb; /* superblock has been read */ extern char skipclean; /* skip clean file systems if preening */ extern int fsmodified; /* 1 => write done to file system */ extern int fsreadfd; /* file descriptor for reading file system */ extern int fswritefd; /* file descriptor for writing file system */ extern int surrender; /* Give up if reads fail */ extern int wantrestart; /* Restart fsck on early termination */ extern ufs2_daddr_t maxfsblock; /* number of blocks in the file system */ extern char *blockmap; /* ptr to primary blk allocation map */ extern ino_t maxino; /* number of inodes in file system */ extern ino_t lfdir; /* lost & found directory inode number */ extern const char *lfname; /* lost & found directory name */ extern int lfmode; /* lost & found directory creation mode */ extern ufs2_daddr_t n_blks; /* number of blocks in use */ extern ino_t n_files; /* number of files in use */ extern volatile sig_atomic_t got_siginfo; /* received a SIGINFO */ extern volatile sig_atomic_t got_sigalarm; /* received a SIGALRM */ #define clearinode(dp) \ if (sblock.fs_magic == FS_UFS1_MAGIC) { \ (dp)->dp1 = ufs1_zino; \ } else { \ (dp)->dp2 = ufs2_zino; \ } extern struct ufs1_dinode ufs1_zino; extern struct ufs2_dinode ufs2_zino; #define setbmap(blkno) setbit(blockmap, blkno) #define testbmap(blkno) isset(blockmap, blkno) #define clrbmap(blkno) clrbit(blockmap, blkno) #define STOP 0x01 #define SKIP 0x02 #define KEEPON 0x04 #define ALTERED 0x08 #define FOUND 0x10 #define EEXIT 8 /* Standard error exit. */ #define ERERUN 16 /* fsck needs to be re-run. */ #define ERESTART -1 int flushentry(void); /* * Wrapper for malloc() that flushes the cylinder group cache to try * to get space. */ static inline void* Malloc(size_t size) { void *retval; while ((retval = malloc(size)) == NULL) if (flushentry() == 0) break; return (retval); } /* * Wrapper for calloc() that flushes the cylinder group cache to try * to get space. */ static inline void* Calloc(size_t cnt, size_t size) { void *retval; while ((retval = calloc(cnt, size)) == NULL) if (flushentry() == 0) break; return (retval); } struct fstab; void adjust(struct inodesc *, int lcnt); ufs2_daddr_t allocblk(long frags); ino_t allocdir(ino_t parent, ino_t request, int mode); ino_t allocino(ino_t request, int type); void blkerror(ino_t ino, const char *type, ufs2_daddr_t blk); char *blockcheck(char *name); int blread(int fd, char *buf, ufs2_daddr_t blk, long size); void bufinit(void); void blwrite(int fd, char *buf, ufs2_daddr_t blk, ssize_t size); void blerase(int fd, ufs2_daddr_t blk, long size); void blzero(int fd, ufs2_daddr_t blk, long size); void cacheino(union dinode *dp, ino_t inumber); void catch(int); void catchquit(int); int changeino(ino_t dir, const char *name, ino_t newnum); int check_cgmagic(int cg, struct bufarea *cgbp); int chkrange(ufs2_daddr_t blk, int cnt); void ckfini(int markclean); int ckinode(union dinode *dp, struct inodesc *); void clri(struct inodesc *, const char *type, int flag); int clearentry(struct inodesc *); void direrror(ino_t ino, const char *errmesg); int dirscan(struct inodesc *); int dofix(struct inodesc *, const char *msg); int eascan(struct inodesc *, struct ufs2_dinode *dp); void fileerror(ino_t cwd, ino_t ino, const char *errmesg); void finalIOstats(void); int findino(struct inodesc *); int findname(struct inodesc *); void flush(int fd, struct bufarea *bp); void freeblk(ufs2_daddr_t blkno, long frags); void freeino(ino_t ino); void freeinodebuf(void); void fsutilinit(void); int ftypeok(union dinode *dp); void getblk(struct bufarea *bp, ufs2_daddr_t blk, long size); struct bufarea *cgget(int cg); struct bufarea *getdatablk(ufs2_daddr_t blkno, long size, int type); struct inoinfo *getinoinfo(ino_t inumber); union dinode *getnextinode(ino_t inumber, int rebuildcg); void getpathname(char *namebuf, ino_t curdir, ino_t ino); union dinode *ginode(ino_t inumber); void infohandler(int sig); void alarmhandler(int sig); void inocleanup(void); void inodirty(union dinode *); struct inostat *inoinfo(ino_t inum); void IOstats(char *what); int linkup(ino_t orphan, ino_t parentdir, char *name); int makeentry(ino_t parent, ino_t ino, const char *name); void panic(const char *fmt, ...) __printflike(1, 2); void pass1(void); void pass1b(void); int pass1check(struct inodesc *); void pass2(void); void pass3(void); void pass4(void); int pass4check(struct inodesc *); void pass5(void); void pfatal(const char *fmt, ...) __printflike(1, 2); void pinode(ino_t ino); void propagate(void); void pwarn(const char *fmt, ...) __printflike(1, 2); int readsb(int listerr); int reply(const char *question); void rwerror(const char *mesg, ufs2_daddr_t blk); void sblock_init(void); void setinodebuf(ino_t); int setup(char *dev); void gjournal_check(const char *filesys); int suj_check(const char *filesys); void update_maps(struct cg *, struct cg*, int); void fsckinit(void); #endif /* !_FSCK_H_ */ Index: stable/11/sbin/fsck_ffs/gjournal.c =================================================================== --- stable/11/sbin/fsck_ffs/gjournal.c (revision 359753) +++ stable/11/sbin/fsck_ffs/gjournal.c (revision 359754) @@ -1,510 +1,509 @@ /*- * Copyright (c) 2006 Pawel Jakub Dawidek * 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 AUTHORS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fsck.h" struct cgchain { union { struct cg cgcu_cg; char cgcu_buf[MAXBSIZE]; } cgc_union; int cgc_busy; int cgc_dirty; LIST_ENTRY(cgchain) cgc_next; }; #define cgc_cg cgc_union.cgcu_cg #define MAX_CACHED_CGS 1024 static unsigned ncgs = 0; static LIST_HEAD(, cgchain) cglist = LIST_HEAD_INITIALIZER(cglist); static const char *devnam; static struct uufsd *disk = NULL; static struct fs *fs = NULL; -struct ufs2_dinode ufs2_zino; static void putcgs(void); /* * Return cylinder group from the cache or load it if it is not in the * cache yet. * Don't cache more than MAX_CACHED_CGS cylinder groups. */ static struct cgchain * getcg(int cg) { struct cgchain *cgc; assert(disk != NULL && fs != NULL); LIST_FOREACH(cgc, &cglist, cgc_next) { if (cgc->cgc_cg.cg_cgx == cg) { //printf("%s: Found cg=%d\n", __func__, cg); return (cgc); } } /* * Our cache is full? Let's clean it up. */ if (ncgs >= MAX_CACHED_CGS) { //printf("%s: Flushing CGs.\n", __func__); putcgs(); } cgc = malloc(sizeof(*cgc)); if (cgc == NULL) { /* * Cannot allocate memory? * Let's put all currently loaded and not busy cylinder groups * on disk and try again. */ //printf("%s: No memory, flushing CGs.\n", __func__); putcgs(); cgc = malloc(sizeof(*cgc)); if (cgc == NULL) err(1, "malloc(%zu)", sizeof(*cgc)); } if (cgread1(disk, cg) == -1) err(1, "cgread1(%d)", cg); bcopy(&disk->d_cg, &cgc->cgc_cg, sizeof(cgc->cgc_union)); cgc->cgc_busy = 0; cgc->cgc_dirty = 0; LIST_INSERT_HEAD(&cglist, cgc, cgc_next); ncgs++; //printf("%s: Read cg=%d\n", __func__, cg); return (cgc); } /* * Mark cylinder group as dirty - it will be written back on putcgs(). */ static void dirtycg(struct cgchain *cgc) { cgc->cgc_dirty = 1; } /* * Mark cylinder group as busy - it will not be freed on putcgs(). */ static void busycg(struct cgchain *cgc) { cgc->cgc_busy = 1; } /* * Unmark the given cylinder group as busy. */ static void unbusycg(struct cgchain *cgc) { cgc->cgc_busy = 0; } /* * Write back all dirty cylinder groups. * Free all non-busy cylinder groups. */ static void putcgs(void) { struct cgchain *cgc, *cgc2; assert(disk != NULL && fs != NULL); LIST_FOREACH_SAFE(cgc, &cglist, cgc_next, cgc2) { if (cgc->cgc_busy) continue; LIST_REMOVE(cgc, cgc_next); ncgs--; if (cgc->cgc_dirty) { bcopy(&cgc->cgc_cg, &disk->d_cg, sizeof(cgc->cgc_union)); if (cgwrite1(disk, cgc->cgc_cg.cg_cgx) == -1) err(1, "cgwrite1(%d)", cgc->cgc_cg.cg_cgx); //printf("%s: Wrote cg=%d\n", __func__, // cgc->cgc_cg.cg_cgx); } free(cgc); } } #if 0 /* * Free all non-busy cylinder groups without storing the dirty ones. */ static void cancelcgs(void) { struct cgchain *cgc; assert(disk != NULL && fs != NULL); while ((cgc = LIST_FIRST(&cglist)) != NULL) { if (cgc->cgc_busy) continue; LIST_REMOVE(cgc, cgc_next); //printf("%s: Canceled cg=%d\n", __func__, cgc->cgc_cg.cg_cgx); free(cgc); } } #endif /* * Open the given provider, load superblock. */ static void opendisk(void) { if (disk != NULL) return; disk = malloc(sizeof(*disk)); if (disk == NULL) err(1, "malloc(%zu)", sizeof(*disk)); if (ufs_disk_fillout(disk, devnam) == -1) { err(1, "ufs_disk_fillout(%s) failed: %s", devnam, disk->d_error); } fs = &disk->d_fs; } /* * Mark file system as clean, write the super-block back, close the disk. */ static void closedisk(void) { fs->fs_clean = 1; if (sbwrite(disk, 0) == -1) err(1, "sbwrite(%s)", devnam); if (ufs_disk_close(disk) == -1) err(1, "ufs_disk_close(%s)", devnam); free(disk); disk = NULL; fs = NULL; } static void blkfree(ufs2_daddr_t bno, long size) { struct cgchain *cgc; struct cg *cgp; ufs1_daddr_t fragno, cgbno; int i, cg, blk, frags, bbase; u_int8_t *blksfree; cg = dtog(fs, bno); cgc = getcg(cg); dirtycg(cgc); cgp = &cgc->cgc_cg; cgbno = dtogd(fs, bno); blksfree = cg_blksfree(cgp); if (size == fs->fs_bsize) { fragno = fragstoblks(fs, cgbno); if (!ffs_isfreeblock(fs, blksfree, fragno)) assert(!"blkfree: freeing free block"); ffs_setblock(fs, blksfree, fragno); ffs_clusteracct(fs, cgp, fragno, 1); cgp->cg_cs.cs_nbfree++; fs->fs_cstotal.cs_nbfree++; fs->fs_cs(fs, cg).cs_nbfree++; } else { bbase = cgbno - fragnum(fs, cgbno); /* * decrement the counts associated with the old frags */ blk = blkmap(fs, blksfree, bbase); ffs_fragacct(fs, blk, cgp->cg_frsum, -1); /* * deallocate the fragment */ frags = numfrags(fs, size); for (i = 0; i < frags; i++) { if (isset(blksfree, cgbno + i)) assert(!"blkfree: freeing free frag"); setbit(blksfree, cgbno + i); } cgp->cg_cs.cs_nffree += i; fs->fs_cstotal.cs_nffree += i; fs->fs_cs(fs, cg).cs_nffree += i; /* * add back in counts associated with the new frags */ blk = blkmap(fs, blksfree, bbase); ffs_fragacct(fs, blk, cgp->cg_frsum, 1); /* * if a complete block has been reassembled, account for it */ fragno = fragstoblks(fs, bbase); if (ffs_isblock(fs, blksfree, fragno)) { cgp->cg_cs.cs_nffree -= fs->fs_frag; fs->fs_cstotal.cs_nffree -= fs->fs_frag; fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag; ffs_clusteracct(fs, cgp, fragno, 1); cgp->cg_cs.cs_nbfree++; fs->fs_cstotal.cs_nbfree++; fs->fs_cs(fs, cg).cs_nbfree++; } } } /* * Recursively free all indirect blocks. */ static void freeindir(ufs2_daddr_t blk, int level) { char sblks[MAXBSIZE]; ufs2_daddr_t *blks; int i; if (bread(disk, fsbtodb(fs, blk), (void *)&sblks, (size_t)fs->fs_bsize) == -1) err(1, "bread: %s", disk->d_error); blks = (ufs2_daddr_t *)&sblks; for (i = 0; i < NINDIR(fs); i++) { if (blks[i] == 0) break; if (level == 0) blkfree(blks[i], fs->fs_bsize); else freeindir(blks[i], level - 1); } blkfree(blk, fs->fs_bsize); } #define dblksize(fs, dino, lbn) \ ((dino)->di_size >= smalllblktosize(fs, (lbn) + 1) \ ? (fs)->fs_bsize \ : fragroundup(fs, blkoff(fs, (dino)->di_size))) /* * Free all blocks associated with the given inode. */ static void clear_inode(struct ufs2_dinode *dino) { ufs2_daddr_t bn; int extblocks, i, level; off_t osize; long bsize; extblocks = 0; if (fs->fs_magic == FS_UFS2_MAGIC && dino->di_extsize > 0) extblocks = btodb(fragroundup(fs, dino->di_extsize)); /* deallocate external attributes blocks */ if (extblocks > 0) { osize = dino->di_extsize; dino->di_blocks -= extblocks; dino->di_extsize = 0; for (i = 0; i < NXADDR; i++) { if (dino->di_extb[i] == 0) continue; blkfree(dino->di_extb[i], sblksize(fs, osize, i)); } } #define SINGLE 0 /* index of single indirect block */ #define DOUBLE 1 /* index of double indirect block */ #define TRIPLE 2 /* index of triple indirect block */ /* deallocate indirect blocks */ for (level = SINGLE; level <= TRIPLE; level++) { if (dino->di_ib[level] == 0) break; freeindir(dino->di_ib[level], level); } /* deallocate direct blocks and fragments */ for (i = 0; i < NDADDR; i++) { bn = dino->di_db[i]; if (bn == 0) continue; bsize = dblksize(fs, dino, i); blkfree(bn, bsize); } } void gjournal_check(const char *filesys) { struct ufs2_dinode *dino; void *p; struct cgchain *cgc; struct cg *cgp; uint8_t *inosused; ino_t cino, ino; int cg, mode; devnam = filesys; opendisk(); /* Are there any unreferenced inodes in this file system? */ if (fs->fs_unrefs == 0) { //printf("No unreferenced inodes.\n"); closedisk(); return; } for (cg = 0; cg < fs->fs_ncg; cg++) { /* Show progress if requested. */ if (got_siginfo) { printf("%s: phase j: cyl group %d of %d (%d%%)\n", cdevname, cg, fs->fs_ncg, cg * 100 / fs->fs_ncg); got_siginfo = 0; } if (got_sigalarm) { setproctitle("%s pj %d%%", cdevname, cg * 100 / fs->fs_ncg); got_sigalarm = 0; } cgc = getcg(cg); cgp = &cgc->cgc_cg; /* Are there any unreferenced inodes in this cylinder group? */ if (cgp->cg_unrefs == 0) continue; //printf("Analizing cylinder group %d (count=%d)\n", cg, cgp->cg_unrefs); /* * We are going to modify this cylinder group, so we want it to * be written back. */ dirtycg(cgc); /* We don't want it to be freed in the meantime. */ busycg(cgc); inosused = cg_inosused(cgp); /* * Now go through the list of all inodes in this cylinder group * to find unreferenced ones. */ for (cino = 0; cino < fs->fs_ipg; cino++) { ino = fs->fs_ipg * cg + cino; /* Unallocated? Skip it. */ if (isclr(inosused, cino)) continue; if (getino(disk, &p, ino, &mode) == -1) err(1, "getino(cg=%d ino=%ju)", cg, (uintmax_t)ino); dino = p; /* Not a regular file nor directory? Skip it. */ if (!S_ISREG(dino->di_mode) && !S_ISDIR(dino->di_mode)) continue; /* Has reference(s)? Skip it. */ if (dino->di_nlink > 0) continue; //printf("Clearing inode=%d (size=%jd)\n", ino, (intmax_t)dino->di_size); /* Free inode's blocks. */ clear_inode(dino); /* Deallocate it. */ clrbit(inosused, cino); /* Update position of last used inode. */ if (ino < cgp->cg_irotor) cgp->cg_irotor = ino; /* Update statistics. */ cgp->cg_cs.cs_nifree++; fs->fs_cs(fs, cg).cs_nifree++; fs->fs_cstotal.cs_nifree++; cgp->cg_unrefs--; fs->fs_unrefs--; /* If this is directory, update related statistics. */ if (S_ISDIR(dino->di_mode)) { cgp->cg_cs.cs_ndir--; fs->fs_cs(fs, cg).cs_ndir--; fs->fs_cstotal.cs_ndir--; } /* Zero-fill the inode. */ *dino = ufs2_zino; /* Write the inode back. */ if (putino(disk) == -1) err(1, "putino(cg=%d ino=%ju)", cg, (uintmax_t)ino); if (cgp->cg_unrefs == 0) { //printf("No more unreferenced inodes in cg=%d.\n", cg); break; } } /* * We don't need this cylinder group anymore, so feel free to * free it if needed. */ unbusycg(cgc); /* * If there are no more unreferenced inodes, there is no need to * check other cylinder groups. */ if (fs->fs_unrefs == 0) { //printf("No more unreferenced inodes (cg=%d/%d).\n", cg, // fs->fs_ncg); break; } } /* Write back modified cylinder groups. */ putcgs(); /* Write back updated statistics and super-block. */ closedisk(); } Index: stable/11/sbin/fsck_ffs/globs.c =================================================================== --- stable/11/sbin/fsck_ffs/globs.c (revision 359753) +++ stable/11/sbin/fsck_ffs/globs.c (revision 359754) @@ -1,169 +1,173 @@ /* * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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. */ #if 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1986, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/14/95"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include "fsck.h" long readcnt[BT_NUMBUFTYPES]; long totalreadcnt[BT_NUMBUFTYPES]; struct timespec readtime[BT_NUMBUFTYPES]; struct timespec totalreadtime[BT_NUMBUFTYPES]; struct timespec startprog; struct bufarea sblk; /* file system superblock */ struct bufarea *pdirbp; /* current directory contents */ struct bufarea *pbp; /* current inode block */ ino_t cursnapshot; long dirhash, inplast; unsigned long numdirs, listmax; long countdirs; /* number of directories we actually found */ int adjrefcnt[MIBSIZE]; /* MIB command to adjust inode reference cnt */ int adjblkcnt[MIBSIZE]; /* MIB command to adjust inode block count */ int setsize[MIBSIZE]; /* MIB command to set inode size */ int adjndir[MIBSIZE]; /* MIB command to adjust number of directories */ int adjnbfree[MIBSIZE]; /* MIB command to adjust number of free blocks */ int adjnifree[MIBSIZE]; /* MIB command to adjust number of free inodes */ int adjnffree[MIBSIZE]; /* MIB command to adjust number of free frags */ int adjnumclusters[MIBSIZE]; /* MIB command to adjust number of free clusters */ int freefiles[MIBSIZE]; /* MIB command to free a set of files */ int freedirs[MIBSIZE]; /* MIB command to free a set of directories */ int freeblks[MIBSIZE]; /* MIB command to free a set of data blocks */ struct fsck_cmd cmd; /* sysctl file system update commands */ char snapname[BUFSIZ]; /* when doing snapshots, the name of the file */ char *cdevname; /* name of device being checked */ long dev_bsize; /* computed value of DEV_BSIZE */ long secsize; /* actual disk sector size */ u_int real_dev_bsize; /* actual disk sector size, not overridden */ char nflag; /* assume a no response */ char yflag; /* assume a yes response */ int bkgrdflag; /* use a snapshot to run on an active system */ ufs2_daddr_t bflag; /* location of alternate super block */ int debug; /* output debugging info */ int Eflag; /* delete empty data blocks */ int Zflag; /* zero empty data blocks */ int zflag; /* zero unused directory space */ int inoopt; /* trim out unused inodes */ char ckclean; /* only do work if not cleanly unmounted */ int cvtlevel; /* convert to newer file system format */ int bkgrdcheck; /* determine if background check is possible */ int bkgrdsumadj; /* whether the kernel have ability to adjust superblock summary */ char usedsoftdep; /* just fix soft dependency inconsistencies */ char preen; /* just fix normal inconsistencies */ char rerun; /* rerun fsck. Only used in non-preen mode */ int returntosingle; /* 1 => return to single user mode on exit */ char resolved; /* cleared if unresolved changes => not clean */ char havesb; /* superblock has been read */ char skipclean; /* skip clean file systems if preening */ int fsmodified; /* 1 => write done to file system */ int fsreadfd; /* file descriptor for reading file system */ int fswritefd; /* file descriptor for writing file system */ int surrender; /* Give up if reads fail */ int wantrestart; /* Restart fsck on early termination */ ufs2_daddr_t maxfsblock; /* number of blocks in the file system */ char *blockmap; /* ptr to primary blk allocation map */ ino_t maxino; /* number of inodes in file system */ ino_t lfdir; /* lost & found directory inode number */ const char *lfname; /* lost & found directory name */ int lfmode; /* lost & found directory creation mode */ ufs2_daddr_t n_blks; /* number of blocks in use */ ino_t n_files; /* number of files in use */ volatile sig_atomic_t got_siginfo; /* received a SIGINFO */ volatile sig_atomic_t got_sigalarm; /* received a SIGALRM */ struct ufs1_dinode ufs1_zino; struct ufs2_dinode ufs2_zino; +struct dups *duplist; +struct dups *muldup; +struct inostatlist *inostathead; + void fsckinit(void) { bzero(readcnt, sizeof(long) * BT_NUMBUFTYPES); bzero(totalreadcnt, sizeof(long) * BT_NUMBUFTYPES); bzero(readtime, sizeof(struct timespec) * BT_NUMBUFTYPES); bzero(totalreadtime, sizeof(struct timespec) * BT_NUMBUFTYPES); bzero(&startprog, sizeof(struct timespec)); bzero(&sblk, sizeof(struct bufarea)); pdirbp = NULL; pbp = NULL; cursnapshot = 0; listmax = numdirs = dirhash = inplast = 0; countdirs = 0; bzero(adjrefcnt, sizeof(int) * MIBSIZE); bzero(adjblkcnt, sizeof(int) * MIBSIZE); bzero(setsize, sizeof(int) * MIBSIZE); bzero(adjndir, sizeof(int) * MIBSIZE); bzero(adjnbfree, sizeof(int) * MIBSIZE); bzero(adjnifree, sizeof(int) * MIBSIZE); bzero(adjnffree, sizeof(int) * MIBSIZE); bzero(adjnumclusters, sizeof(int) * MIBSIZE); bzero(freefiles, sizeof(int) * MIBSIZE); bzero(freedirs, sizeof(int) * MIBSIZE); bzero(freeblks, sizeof(int) * MIBSIZE); bzero(&cmd, sizeof(struct fsck_cmd)); bzero(snapname, sizeof(char) * BUFSIZ); cdevname = NULL; dev_bsize = 0; secsize = 0; real_dev_bsize = 0; bkgrdsumadj = 0; usedsoftdep = 0; rerun = 0; returntosingle = 0; resolved = 0; havesb = 0; fsmodified = 0; fsreadfd = 0; fswritefd = 0; maxfsblock = 0; blockmap = NULL; maxino = 0; lfdir = 0; lfname = "lost+found"; lfmode = 0700; n_blks = 0; n_files = 0; got_siginfo = 0; got_sigalarm = 0; bzero(&ufs1_zino, sizeof(struct ufs1_dinode)); bzero(&ufs2_zino, sizeof(struct ufs2_dinode)); } Index: stable/11/sbin/fsck_ffs/setup.c =================================================================== --- stable/11/sbin/fsck_ffs/setup.c (revision 359753) +++ stable/11/sbin/fsck_ffs/setup.c (revision 359754) @@ -1,568 +1,570 @@ /* * Copyright (c) 1980, 1986, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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. */ #if 0 #ifndef lint static const char sccsid[] = "@(#)setup.c 8.10 (Berkeley) 5/9/95"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #define FSTYPENAMES #include #include #include #include #include #include #include #include #include #include #include #include "fsck.h" +struct inoinfo **inphead, **inpsort; + struct bufarea asblk; #define altsblock (*asblk.b_un.b_fs) #define POWEROF2(num) (((num) & ((num) - 1)) == 0) static int calcsb(char *dev, int devfd, struct fs *fs); static void saverecovery(int readfd, int writefd); static int chkrecovery(int devfd); /* * Read in a superblock finding an alternate if necessary. * Return 1 if successful, 0 if unsuccessful, -1 if file system * is already clean (ckclean and preen mode only). */ int setup(char *dev) { long cg, asked, i, j; long bmapsize; struct stat statb; struct fs proto; size_t size; havesb = 0; fswritefd = -1; cursnapshot = 0; if (stat(dev, &statb) < 0) { printf("Can't stat %s: %s\n", dev, strerror(errno)); if (bkgrdflag) { unlink(snapname); bkgrdflag = 0; } return (0); } if ((statb.st_mode & S_IFMT) != S_IFCHR && (statb.st_mode & S_IFMT) != S_IFBLK) { if (bkgrdflag != 0 && (statb.st_flags & SF_SNAPSHOT) == 0) { unlink(snapname); printf("background fsck lacks a snapshot\n"); exit(EEXIT); } if ((statb.st_flags & SF_SNAPSHOT) != 0 && cvtlevel == 0) { cursnapshot = statb.st_ino; } else { if (cvtlevel == 0 || (statb.st_flags & SF_SNAPSHOT) == 0) { if (preen && bkgrdflag) { unlink(snapname); bkgrdflag = 0; } pfatal("%s is not a disk device", dev); if (reply("CONTINUE") == 0) { if (bkgrdflag) { unlink(snapname); bkgrdflag = 0; } return (0); } } else { if (bkgrdflag) { unlink(snapname); bkgrdflag = 0; } pfatal("cannot convert a snapshot"); exit(EEXIT); } } } if ((fsreadfd = open(dev, O_RDONLY)) < 0) { if (bkgrdflag) { unlink(snapname); bkgrdflag = 0; } printf("Can't open %s: %s\n", dev, strerror(errno)); return (0); } if (bkgrdflag) { unlink(snapname); size = MIBSIZE; if (sysctlnametomib("vfs.ffs.adjrefcnt", adjrefcnt, &size) < 0|| sysctlnametomib("vfs.ffs.adjblkcnt", adjblkcnt, &size) < 0|| sysctlnametomib("vfs.ffs.setsize", setsize, &size) < 0 || sysctlnametomib("vfs.ffs.freefiles", freefiles, &size) < 0|| sysctlnametomib("vfs.ffs.freedirs", freedirs, &size) < 0 || sysctlnametomib("vfs.ffs.freeblks", freeblks, &size) < 0) { pfatal("kernel lacks background fsck support\n"); exit(EEXIT); } /* * When kernel is lack of runtime bgfsck superblock summary * adjustment functionality, it does not mean we can not * continue, as old kernels will recompute the summary at * mount time. However, it will be an unexpected softupdates * inconsistency if it turns out that the summary is still * incorrect. Set a flag so subsequent operation can know * this. */ bkgrdsumadj = 1; if (sysctlnametomib("vfs.ffs.adjndir", adjndir, &size) < 0 || sysctlnametomib("vfs.ffs.adjnbfree", adjnbfree, &size) < 0 || sysctlnametomib("vfs.ffs.adjnifree", adjnifree, &size) < 0 || sysctlnametomib("vfs.ffs.adjnffree", adjnffree, &size) < 0 || sysctlnametomib("vfs.ffs.adjnumclusters", adjnumclusters, &size) < 0) { bkgrdsumadj = 0; pwarn("kernel lacks runtime superblock summary adjustment support"); } cmd.version = FFS_CMD_VERSION; cmd.handle = fsreadfd; fswritefd = -1; } if (preen == 0) printf("** %s", dev); if (bkgrdflag == 0 && (nflag || (fswritefd = open(dev, O_WRONLY)) < 0)) { fswritefd = -1; if (preen) pfatal("NO WRITE ACCESS"); printf(" (NO WRITE)"); } if (preen == 0) printf("\n"); /* * Read in the superblock, looking for alternates if necessary */ if (readsb(1) == 0) { skipclean = 0; if (bflag || preen || calcsb(dev, fsreadfd, &proto) == 0) return(0); if (reply("LOOK FOR ALTERNATE SUPERBLOCKS") == 0) return (0); for (cg = 0; cg < proto.fs_ncg; cg++) { bflag = fsbtodb(&proto, cgsblock(&proto, cg)); if (readsb(0) != 0) break; } if (cg >= proto.fs_ncg) { printf("%s %s\n%s %s\n%s %s\n", "SEARCH FOR ALTERNATE SUPER-BLOCK", "FAILED. YOU MUST USE THE", "-b OPTION TO FSCK TO SPECIFY THE", "LOCATION OF AN ALTERNATE", "SUPER-BLOCK TO SUPPLY NEEDED", "INFORMATION; SEE fsck_ffs(8)."); bflag = 0; return(0); } pwarn("USING ALTERNATE SUPERBLOCK AT %jd\n", bflag); bflag = 0; } if (skipclean && ckclean && sblock.fs_clean) { pwarn("FILE SYSTEM CLEAN; SKIPPING CHECKS\n"); return (-1); } maxfsblock = sblock.fs_size; maxino = sblock.fs_ncg * sblock.fs_ipg; /* * Check and potentially fix certain fields in the super block. */ if (sblock.fs_optim != FS_OPTTIME && sblock.fs_optim != FS_OPTSPACE) { pfatal("UNDEFINED OPTIMIZATION IN SUPERBLOCK"); if (reply("SET TO DEFAULT") == 1) { sblock.fs_optim = FS_OPTTIME; sbdirty(); } } if ((sblock.fs_minfree < 0 || sblock.fs_minfree > 99)) { pfatal("IMPOSSIBLE MINFREE=%d IN SUPERBLOCK", sblock.fs_minfree); if (reply("SET TO DEFAULT") == 1) { sblock.fs_minfree = 10; sbdirty(); } } if (sblock.fs_magic == FS_UFS1_MAGIC && sblock.fs_old_inodefmt < FS_44INODEFMT) { pwarn("Format of file system is too old.\n"); pwarn("Must update to modern format using a version of fsck\n"); pfatal("from before 2002 with the command ``fsck -c 2''\n"); exit(EEXIT); } if (asblk.b_dirty && !bflag) { memmove(&altsblock, &sblock, (size_t)sblock.fs_sbsize); flush(fswritefd, &asblk); } if (preen == 0 && yflag == 0 && sblock.fs_magic == FS_UFS2_MAGIC && fswritefd != -1 && chkrecovery(fsreadfd) == 0 && reply("SAVE DATA TO FIND ALTERNATE SUPERBLOCKS") != 0) saverecovery(fsreadfd, fswritefd); /* * read in the summary info. */ asked = 0; sblock.fs_csp = Calloc(1, sblock.fs_cssize); if (sblock.fs_csp == NULL) { printf("cannot alloc %u bytes for cg summary info\n", (unsigned)sblock.fs_cssize); goto badsb; } for (i = 0, j = 0; i < sblock.fs_cssize; i += sblock.fs_bsize, j++) { size = MIN(sblock.fs_cssize - i, sblock.fs_bsize); readcnt[sblk.b_type]++; if (blread(fsreadfd, (char *)sblock.fs_csp + i, fsbtodb(&sblock, sblock.fs_csaddr + j * sblock.fs_frag), size) != 0 && !asked) { pfatal("BAD SUMMARY INFORMATION"); if (reply("CONTINUE") == 0) { ckfini(0); exit(EEXIT); } asked++; } } /* * allocate and initialize the necessary maps */ bmapsize = roundup(howmany(maxfsblock, CHAR_BIT), sizeof(short)); blockmap = Calloc((unsigned)bmapsize, sizeof (char)); if (blockmap == NULL) { printf("cannot alloc %u bytes for blockmap\n", (unsigned)bmapsize); goto badsb; } inostathead = Calloc(sblock.fs_ncg, sizeof(struct inostatlist)); if (inostathead == NULL) { printf("cannot alloc %u bytes for inostathead\n", (unsigned)(sizeof(struct inostatlist) * (sblock.fs_ncg))); goto badsb; } numdirs = MAX(sblock.fs_cstotal.cs_ndir, 128); dirhash = numdirs; inplast = 0; listmax = numdirs + 10; inpsort = (struct inoinfo **)Calloc(listmax, sizeof(struct inoinfo *)); inphead = (struct inoinfo **)Calloc(numdirs, sizeof(struct inoinfo *)); if (inpsort == NULL || inphead == NULL) { printf("cannot alloc %ju bytes for inphead\n", (uintmax_t)numdirs * sizeof(struct inoinfo *)); goto badsb; } bufinit(); if (sblock.fs_flags & FS_DOSOFTDEP) usedsoftdep = 1; else usedsoftdep = 0; return (1); badsb: ckfini(0); return (0); } /* * Possible superblock locations ordered from most to least likely. */ static int sblock_try[] = SBLOCKSEARCH; #define BAD_MAGIC_MSG \ "The previous newfs operation on this volume did not complete.\n" \ "You must complete newfs before mounting this volume.\n" /* * Read in the super block and its summary info. */ int readsb(int listerr) { ufs2_daddr_t super; int i, bad; if (bflag) { super = bflag; readcnt[sblk.b_type]++; if ((blread(fsreadfd, (char *)&sblock, super, (long)SBLOCKSIZE))) return (0); if (sblock.fs_magic == FS_BAD_MAGIC) { fprintf(stderr, BAD_MAGIC_MSG); exit(11); } if (sblock.fs_magic != FS_UFS1_MAGIC && sblock.fs_magic != FS_UFS2_MAGIC) { fprintf(stderr, "%jd is not a file system superblock\n", bflag); return (0); } } else { for (i = 0; sblock_try[i] != -1; i++) { super = sblock_try[i] / dev_bsize; readcnt[sblk.b_type]++; if ((blread(fsreadfd, (char *)&sblock, super, (long)SBLOCKSIZE))) return (0); if (sblock.fs_magic == FS_BAD_MAGIC) { fprintf(stderr, BAD_MAGIC_MSG); exit(11); } if ((sblock.fs_magic == FS_UFS1_MAGIC || (sblock.fs_magic == FS_UFS2_MAGIC && sblock.fs_sblockloc == sblock_try[i])) && sblock.fs_ncg >= 1 && sblock.fs_bsize >= MINBSIZE && sblock.fs_sbsize >= roundup(sizeof(struct fs), dev_bsize)) break; } if (sblock_try[i] == -1) { fprintf(stderr, "Cannot find file system superblock\n"); return (0); } } /* * Compute block size that the file system is based on, * according to fsbtodb, and adjust superblock block number * so we can tell if this is an alternate later. */ super *= dev_bsize; dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); sblk.b_bno = super / dev_bsize; sblk.b_size = SBLOCKSIZE; /* * Compare all fields that should not differ in alternate super block. * When an alternate super-block is specified this check is skipped. */ if (bflag) goto out; getblk(&asblk, cgsblock(&sblock, sblock.fs_ncg - 1), sblock.fs_sbsize); if (asblk.b_errs) return (0); bad = 0; #define CHK(x, y) \ if (altsblock.x != sblock.x) { \ bad++; \ if (listerr && debug) \ printf("SUPER BLOCK VS ALTERNATE MISMATCH %s: " y " vs " y "\n", \ #x, (intmax_t)sblock.x, (intmax_t)altsblock.x); \ } CHK(fs_sblkno, "%jd"); CHK(fs_cblkno, "%jd"); CHK(fs_iblkno, "%jd"); CHK(fs_dblkno, "%jd"); CHK(fs_ncg, "%jd"); CHK(fs_bsize, "%jd"); CHK(fs_fsize, "%jd"); CHK(fs_frag, "%jd"); CHK(fs_bmask, "%#jx"); CHK(fs_fmask, "%#jx"); CHK(fs_bshift, "%jd"); CHK(fs_fshift, "%jd"); CHK(fs_fragshift, "%jd"); CHK(fs_fsbtodb, "%jd"); CHK(fs_sbsize, "%jd"); CHK(fs_nindir, "%jd"); CHK(fs_inopb, "%jd"); CHK(fs_cssize, "%jd"); CHK(fs_ipg, "%jd"); CHK(fs_fpg, "%jd"); CHK(fs_magic, "%#jx"); #undef CHK if (bad) { if (listerr == 0) return (0); if (preen) printf("%s: ", cdevname); printf( "VALUES IN SUPER BLOCK LSB=%jd DISAGREE WITH THOSE IN\n" "LAST ALTERNATE LSB=%jd\n", sblk.b_bno, asblk.b_bno); if (reply("IGNORE ALTERNATE SUPER BLOCK") == 0) return (0); } out: /* * If not yet done, update UFS1 superblock with new wider fields. */ if (sblock.fs_magic == FS_UFS1_MAGIC && sblock.fs_maxbsize != sblock.fs_bsize) { sblock.fs_maxbsize = sblock.fs_bsize; sblock.fs_time = sblock.fs_old_time; sblock.fs_size = sblock.fs_old_size; sblock.fs_dsize = sblock.fs_old_dsize; sblock.fs_csaddr = sblock.fs_old_csaddr; sblock.fs_cstotal.cs_ndir = sblock.fs_old_cstotal.cs_ndir; sblock.fs_cstotal.cs_nbfree = sblock.fs_old_cstotal.cs_nbfree; sblock.fs_cstotal.cs_nifree = sblock.fs_old_cstotal.cs_nifree; sblock.fs_cstotal.cs_nffree = sblock.fs_old_cstotal.cs_nffree; } havesb = 1; return (1); } void sblock_init(void) { fswritefd = -1; fsmodified = 0; lfdir = 0; initbarea(&sblk, BT_SUPERBLK); initbarea(&asblk, BT_SUPERBLK); sblk.b_un.b_buf = Malloc(SBLOCKSIZE); asblk.b_un.b_buf = Malloc(SBLOCKSIZE); if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL) errx(EEXIT, "cannot allocate space for superblock"); dev_bsize = secsize = DEV_BSIZE; } /* * Calculate a prototype superblock based on information in the boot area. * When done the cgsblock macro can be calculated and the fs_ncg field * can be used. Do NOT attempt to use other macros without verifying that * their needed information is available! */ static int calcsb(char *dev, int devfd, struct fs *fs) { struct fsrecovery *fsr; char *fsrbuf; u_int secsize; /* * We need fragments-per-group and the partition-size. * * Newfs stores these details at the end of the boot block area * at the start of the filesystem partition. If they have been * overwritten by a boot block, we fail. But usually they are * there and we can use them. */ if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1) return (0); fsrbuf = Malloc(secsize); if (fsrbuf == NULL) errx(EEXIT, "calcsb: cannot allocate recovery buffer"); if (blread(devfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize, secsize) != 0) return (0); fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr]; if (fsr->fsr_magic != FS_UFS2_MAGIC) return (0); memset(fs, 0, sizeof(struct fs)); fs->fs_fpg = fsr->fsr_fpg; fs->fs_fsbtodb = fsr->fsr_fsbtodb; fs->fs_sblkno = fsr->fsr_sblkno; fs->fs_magic = fsr->fsr_magic; fs->fs_ncg = fsr->fsr_ncg; free(fsrbuf); return (1); } /* * Check to see if recovery information exists. * Return 1 if it exists or cannot be created. * Return 0 if it does not exist and can be created. */ static int chkrecovery(int devfd) { struct fsrecovery *fsr; char *fsrbuf; u_int secsize; /* * Could not determine if backup material exists, so do not * offer to create it. */ if (ioctl(devfd, DIOCGSECTORSIZE, &secsize) == -1 || (fsrbuf = Malloc(secsize)) == NULL || blread(devfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize, secsize) != 0) return (1); /* * Recovery material has already been created, so do not * need to create it again. */ fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr]; if (fsr->fsr_magic == FS_UFS2_MAGIC) { free(fsrbuf); return (1); } /* * Recovery material has not been created and can be if desired. */ free(fsrbuf); return (0); } /* * Read the last sector of the boot block, replace the last * 20 bytes with the recovery information, then write it back. * The recovery information only works for UFS2 filesystems. */ static void saverecovery(int readfd, int writefd) { struct fsrecovery *fsr; char *fsrbuf; u_int secsize; if (sblock.fs_magic != FS_UFS2_MAGIC || ioctl(readfd, DIOCGSECTORSIZE, &secsize) == -1 || (fsrbuf = Malloc(secsize)) == NULL || blread(readfd, fsrbuf, (SBLOCK_UFS2 - secsize) / dev_bsize, secsize) != 0) { printf("RECOVERY DATA COULD NOT BE CREATED\n"); return; } fsr = (struct fsrecovery *)&fsrbuf[secsize - sizeof *fsr]; fsr->fsr_magic = sblock.fs_magic; fsr->fsr_fpg = sblock.fs_fpg; fsr->fsr_fsbtodb = sblock.fs_fsbtodb; fsr->fsr_sblkno = sblock.fs_sblkno; fsr->fsr_ncg = sblock.fs_ncg; blwrite(writefd, fsrbuf, (SBLOCK_UFS2 - secsize) / secsize, secsize); free(fsrbuf); } Index: stable/11/sbin/fsdb/fsdb.c =================================================================== --- stable/11/sbin/fsdb/fsdb.c (revision 359753) +++ stable/11/sbin/fsdb/fsdb.c (revision 359754) @@ -1,1239 +1,1236 @@ /* $NetBSD: fsdb.c,v 1.2 1995/10/08 23:18:10 thorpej Exp $ */ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1995 John T. Kohl * 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. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fsdb.h" #include "fsck.h" static void usage(void) __dead2; int cmdloop(void); static int compare_blk32(uint32_t *wantedblk, uint32_t curblk); static int compare_blk64(uint64_t *wantedblk, uint64_t curblk); static int founddatablk(uint64_t blk); static int find_blks32(uint32_t *buf, int size, uint32_t *blknum); static int find_blks64(uint64_t *buf, int size, uint64_t *blknum); static int find_indirblks32(uint32_t blk, int ind_level, uint32_t *blknum); static int find_indirblks64(uint64_t blk, int ind_level, uint64_t *blknum); static void usage(void) { fprintf(stderr, "usage: fsdb [-d] [-f] [-r] fsname\n"); exit(1); } -int returntosingle; -char nflag; - /* * We suck in lots of fsck code, and just pick & choose the stuff we want. * * fsreadfd is set up to read from the file system, fswritefd to write to * the file system. */ int main(int argc, char *argv[]) { int ch, rval; char *fsys = NULL; while (-1 != (ch = getopt(argc, argv, "fdr"))) { switch (ch) { case 'f': /* The -f option is left for historical * reasons and has no meaning. */ break; case 'd': debug++; break; case 'r': nflag++; /* "no" in fsck, readonly for us */ break; default: usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); else fsys = argv[0]; sblock_init(); if (!setup(fsys)) errx(1, "cannot set up file system `%s'", fsys); printf("%s file system `%s'\nLast Mounted on %s\n", nflag? "Examining": "Editing", fsys, sblock.fs_fsmnt); rval = cmdloop(); if (!nflag) { sblock.fs_clean = 0; /* mark it dirty */ sbdirty(); ckfini(0); printf("*** FILE SYSTEM MARKED DIRTY\n"); printf("*** BE SURE TO RUN FSCK TO CLEAN UP ANY DAMAGE\n"); printf("*** IF IT WAS MOUNTED, RE-MOUNT WITH -u -o reload\n"); } exit(rval); } #define CMDFUNC(func) int func(int argc, char *argv[]) #define CMDFUNCSTART(func) int func(int argc, char *argv[]) CMDFUNC(helpfn); CMDFUNC(focus); /* focus on inode */ CMDFUNC(active); /* print active inode */ CMDFUNC(blocks); /* print blocks for active inode */ CMDFUNC(focusname); /* focus by name */ CMDFUNC(zapi); /* clear inode */ CMDFUNC(uplink); /* incr link */ CMDFUNC(downlink); /* decr link */ CMDFUNC(linkcount); /* set link count */ CMDFUNC(quit); /* quit */ CMDFUNC(findblk); /* find block */ CMDFUNC(ls); /* list directory */ CMDFUNC(rm); /* remove name */ CMDFUNC(ln); /* add name */ CMDFUNC(newtype); /* change type */ CMDFUNC(chmode); /* change mode */ CMDFUNC(chlen); /* change length */ CMDFUNC(chaflags); /* change flags */ CMDFUNC(chgen); /* change generation */ CMDFUNC(chowner); /* change owner */ CMDFUNC(chgroup); /* Change group */ CMDFUNC(back); /* pop back to last ino */ CMDFUNC(chbtime); /* Change btime */ CMDFUNC(chmtime); /* Change mtime */ CMDFUNC(chctime); /* Change ctime */ CMDFUNC(chatime); /* Change atime */ CMDFUNC(chinum); /* Change inode # of dirent */ CMDFUNC(chname); /* Change dirname of dirent */ CMDFUNC(chsize); /* Change size */ struct cmdtable cmds[] = { { "help", "Print out help", 1, 1, FL_RO, helpfn }, { "?", "Print out help", 1, 1, FL_RO, helpfn }, { "inode", "Set active inode to INUM", 2, 2, FL_RO, focus }, { "clri", "Clear inode INUM", 2, 2, FL_WR, zapi }, { "lookup", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname }, { "cd", "Set active inode by looking up NAME", 2, 2, FL_RO | FL_ST, focusname }, { "back", "Go to previous active inode", 1, 1, FL_RO, back }, { "active", "Print active inode", 1, 1, FL_RO, active }, { "print", "Print active inode", 1, 1, FL_RO, active }, { "blocks", "Print block numbers of active inode", 1, 1, FL_RO, blocks }, { "uplink", "Increment link count", 1, 1, FL_WR, uplink }, { "downlink", "Decrement link count", 1, 1, FL_WR, downlink }, { "linkcount", "Set link count to COUNT", 2, 2, FL_WR, linkcount }, { "findblk", "Find inode owning disk block(s)", 2, 33, FL_RO, findblk}, { "ls", "List current inode as directory", 1, 1, FL_RO, ls }, { "rm", "Remove NAME from current inode directory", 2, 2, FL_WR | FL_ST, rm }, { "del", "Remove NAME from current inode directory", 2, 2, FL_WR | FL_ST, rm }, { "ln", "Hardlink INO into current inode directory as NAME", 3, 3, FL_WR | FL_ST, ln }, { "chinum", "Change dir entry number INDEX to INUM", 3, 3, FL_WR, chinum }, { "chname", "Change dir entry number INDEX to NAME", 3, 3, FL_WR | FL_ST, chname }, { "chtype", "Change type of current inode to TYPE", 2, 2, FL_WR, newtype }, { "chmod", "Change mode of current inode to MODE", 2, 2, FL_WR, chmode }, { "chlen", "Change length of current inode to LENGTH", 2, 2, FL_WR, chlen }, { "chown", "Change owner of current inode to OWNER", 2, 2, FL_WR, chowner }, { "chgrp", "Change group of current inode to GROUP", 2, 2, FL_WR, chgroup }, { "chflags", "Change flags of current inode to FLAGS", 2, 2, FL_WR, chaflags }, { "chgen", "Change generation number of current inode to GEN", 2, 2, FL_WR, chgen }, { "chsize", "Change size of current inode to SIZE", 2, 2, FL_WR, chsize }, { "btime", "Change btime of current inode to BTIME", 2, 2, FL_WR, chbtime }, { "mtime", "Change mtime of current inode to MTIME", 2, 2, FL_WR, chmtime }, { "ctime", "Change ctime of current inode to CTIME", 2, 2, FL_WR, chctime }, { "atime", "Change atime of current inode to ATIME", 2, 2, FL_WR, chatime }, { "quit", "Exit", 1, 1, FL_RO, quit }, { "q", "Exit", 1, 1, FL_RO, quit }, { "exit", "Exit", 1, 1, FL_RO, quit }, { NULL, 0, 0, 0, 0, NULL }, }; int helpfn(int argc, char *argv[]) { struct cmdtable *cmdtp; printf("Commands are:\n%-10s %5s %5s %s\n", "command", "min args", "max args", "what"); for (cmdtp = cmds; cmdtp->cmd; cmdtp++) printf("%-10s %5u %5u %s\n", cmdtp->cmd, cmdtp->minargc-1, cmdtp->maxargc-1, cmdtp->helptxt); return 0; } char * prompt(EditLine *el) { static char pstring[64]; snprintf(pstring, sizeof(pstring), "fsdb (inum: %ju)> ", (uintmax_t)curinum); return pstring; } int cmdloop(void) { char *line; const char *elline; int cmd_argc, rval = 0, known; #define scratch known char **cmd_argv; struct cmdtable *cmdp; History *hist; EditLine *elptr; HistEvent he; curinode = ginode(ROOTINO); curinum = ROOTINO; printactive(0); hist = history_init(); history(hist, &he, H_SETSIZE, 100); /* 100 elt history buffer */ elptr = el_init("fsdb", stdin, stdout, stderr); el_set(elptr, EL_EDITOR, "emacs"); el_set(elptr, EL_PROMPT, prompt); el_set(elptr, EL_HIST, history, hist); el_source(elptr, NULL); while ((elline = el_gets(elptr, &scratch)) != NULL && scratch != 0) { if (debug) printf("command `%s'\n", elline); history(hist, &he, H_ENTER, elline); line = strdup(elline); cmd_argv = crack(line, &cmd_argc); /* * el_parse returns -1 to signal that it's not been handled * internally. */ if (el_parse(elptr, cmd_argc, (const char **)cmd_argv) != -1) continue; if (cmd_argc) { known = 0; for (cmdp = cmds; cmdp->cmd; cmdp++) { if (!strcmp(cmdp->cmd, cmd_argv[0])) { if ((cmdp->flags & FL_WR) == FL_WR && nflag) warnx("`%s' requires write access", cmd_argv[0]), rval = 1; else if (cmd_argc >= cmdp->minargc && cmd_argc <= cmdp->maxargc) rval = (*cmdp->handler)(cmd_argc, cmd_argv); else if (cmd_argc >= cmdp->minargc && (cmdp->flags & FL_ST) == FL_ST) { strcpy(line, elline); cmd_argv = recrack(line, &cmd_argc, cmdp->maxargc); rval = (*cmdp->handler)(cmd_argc, cmd_argv); } else rval = argcount(cmdp, cmd_argc, cmd_argv); known = 1; break; } } if (!known) warnx("unknown command `%s'", cmd_argv[0]), rval = 1; } else rval = 0; free(line); if (rval < 0) /* user typed "quit" */ return 0; if (rval) warnx("rval was %d", rval); } el_end(elptr); history_end(hist); return rval; } union dinode *curinode; ino_t curinum, ocurrent; #define GETINUM(ac,inum) inum = strtoul(argv[ac], &cp, 0); \ if (inum < ROOTINO || inum > maxino || cp == argv[ac] || *cp != '\0' ) { \ printf("inode %ju out of range; range is [%ju,%ju]\n", \ (uintmax_t)inum, (uintmax_t)ROOTINO, (uintmax_t)maxino); \ return 1; \ } /* * Focus on given inode number */ CMDFUNCSTART(focus) { ino_t inum; char *cp; GETINUM(1,inum); curinode = ginode(inum); ocurrent = curinum; curinum = inum; printactive(0); return 0; } CMDFUNCSTART(back) { curinum = ocurrent; curinode = ginode(curinum); printactive(0); return 0; } CMDFUNCSTART(zapi) { ino_t inum; union dinode *dp; char *cp; GETINUM(1,inum); dp = ginode(inum); clearinode(dp); inodirty(dp); if (curinode) /* re-set after potential change */ curinode = ginode(curinum); return 0; } CMDFUNCSTART(active) { printactive(0); return 0; } CMDFUNCSTART(blocks) { printactive(1); return 0; } CMDFUNCSTART(quit) { return -1; } CMDFUNCSTART(uplink) { if (!checkactive()) return 1; DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) + 1); printf("inode %ju link count now %d\n", (uintmax_t)curinum, DIP(curinode, di_nlink)); inodirty(curinode); return 0; } CMDFUNCSTART(downlink) { if (!checkactive()) return 1; DIP_SET(curinode, di_nlink, DIP(curinode, di_nlink) - 1); printf("inode %ju link count now %d\n", (uintmax_t)curinum, DIP(curinode, di_nlink)); inodirty(curinode); return 0; } const char *typename[] = { "unknown", "fifo", "char special", "unregistered #3", "directory", "unregistered #5", "blk special", "unregistered #7", "regular", "unregistered #9", "symlink", "unregistered #11", "socket", "unregistered #13", "whiteout", }; int diroff; int slot; int scannames(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; printf("slot %d off %d ino %d reclen %d: %s, `%.*s'\n", slot++, diroff, dirp->d_ino, dirp->d_reclen, typename[dirp->d_type], dirp->d_namlen, dirp->d_name); diroff += dirp->d_reclen; return (KEEPON); } CMDFUNCSTART(ls) { struct inodesc idesc; checkactivedir(); /* let it go on anyway */ slot = 0; diroff = 0; idesc.id_number = curinum; idesc.id_func = scannames; idesc.id_type = DATA; idesc.id_fix = IGNORE; ckinode(curinode, &idesc); curinode = ginode(curinum); return 0; } static int findblk_numtofind; static int wantedblksize; CMDFUNCSTART(findblk) { ino_t inum, inosused; uint32_t *wantedblk32; uint64_t *wantedblk64; struct bufarea *cgbp; struct cg *cgp; int c, i, is_ufs2; wantedblksize = (argc - 1); is_ufs2 = sblock.fs_magic == FS_UFS2_MAGIC; ocurrent = curinum; if (is_ufs2) { wantedblk64 = calloc(wantedblksize, sizeof(uint64_t)); if (wantedblk64 == NULL) err(1, "malloc"); for (i = 1; i < argc; i++) wantedblk64[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0)); } else { wantedblk32 = calloc(wantedblksize, sizeof(uint32_t)); if (wantedblk32 == NULL) err(1, "malloc"); for (i = 1; i < argc; i++) wantedblk32[i - 1] = dbtofsb(&sblock, strtoull(argv[i], NULL, 0)); } findblk_numtofind = wantedblksize; /* * sblock.fs_ncg holds a number of cylinder groups. * Iterate over all cylinder groups. */ for (c = 0; c < sblock.fs_ncg; c++) { /* * sblock.fs_ipg holds a number of inodes per cylinder group. * Calculate a highest inode number for a given cylinder group. */ inum = c * sblock.fs_ipg; /* Read cylinder group. */ cgbp = cgget(c); cgp = cgbp->b_un.b_cg; /* * Get a highest used inode number for a given cylinder group. * For UFS1 all inodes initialized at the newfs stage. */ if (is_ufs2) inosused = cgp->cg_initediblk; else inosused = sblock.fs_ipg; for (; inosused > 0; inum++, inosused--) { /* Skip magic inodes: 0, WINO, ROOTINO. */ if (inum < ROOTINO) continue; /* * Check if the block we are looking for is just an inode block. * * ino_to_fsba() - get block containing inode from its number. * INOPB() - get a number of inodes in one disk block. */ if (is_ufs2 ? compare_blk64(wantedblk64, ino_to_fsba(&sblock, inum)) : compare_blk32(wantedblk32, ino_to_fsba(&sblock, inum))) { printf("block %llu: inode block (%ju-%ju)\n", (unsigned long long)fsbtodb(&sblock, ino_to_fsba(&sblock, inum)), (uintmax_t)(inum / INOPB(&sblock)) * INOPB(&sblock), (uintmax_t)(inum / INOPB(&sblock) + 1) * INOPB(&sblock)); findblk_numtofind--; if (findblk_numtofind == 0) goto end; } /* Get on-disk inode aka dinode. */ curinum = inum; curinode = ginode(inum); /* Find IFLNK dinode with allocated data blocks. */ switch (DIP(curinode, di_mode) & IFMT) { case IFDIR: case IFREG: if (DIP(curinode, di_blocks) == 0) continue; break; case IFLNK: { uint64_t size = DIP(curinode, di_size); if (size > 0 && size < sblock.fs_maxsymlinklen && DIP(curinode, di_blocks) == 0) continue; else break; } default: continue; } /* Look through direct data blocks. */ if (is_ufs2 ? find_blks64(curinode->dp2.di_db, NDADDR, wantedblk64) : find_blks32(curinode->dp1.di_db, NDADDR, wantedblk32)) goto end; for (i = 0; i < NIADDR; i++) { /* * Does the block we are looking for belongs to the * indirect blocks? */ if (is_ufs2 ? compare_blk64(wantedblk64, curinode->dp2.di_ib[i]) : compare_blk32(wantedblk32, curinode->dp1.di_ib[i])) if (founddatablk(is_ufs2 ? curinode->dp2.di_ib[i] : curinode->dp1.di_ib[i])) goto end; /* * Search through indirect, double and triple indirect * data blocks. */ if (is_ufs2 ? (curinode->dp2.di_ib[i] != 0) : (curinode->dp1.di_ib[i] != 0)) if (is_ufs2 ? find_indirblks64(curinode->dp2.di_ib[i], i, wantedblk64) : find_indirblks32(curinode->dp1.di_ib[i], i, wantedblk32)) goto end; } } } end: curinum = ocurrent; curinode = ginode(curinum); return 0; } static int compare_blk32(uint32_t *wantedblk, uint32_t curblk) { int i; for (i = 0; i < wantedblksize; i++) { if (wantedblk[i] != 0 && wantedblk[i] == curblk) { wantedblk[i] = 0; return 1; } } return 0; } static int compare_blk64(uint64_t *wantedblk, uint64_t curblk) { int i; for (i = 0; i < wantedblksize; i++) { if (wantedblk[i] != 0 && wantedblk[i] == curblk) { wantedblk[i] = 0; return 1; } } return 0; } static int founddatablk(uint64_t blk) { printf("%llu: data block of inode %ju\n", (unsigned long long)fsbtodb(&sblock, blk), (uintmax_t)curinum); findblk_numtofind--; if (findblk_numtofind == 0) return 1; return 0; } static int find_blks32(uint32_t *buf, int size, uint32_t *wantedblk) { int blk; for (blk = 0; blk < size; blk++) { if (buf[blk] == 0) continue; if (compare_blk32(wantedblk, buf[blk])) { if (founddatablk(buf[blk])) return 1; } } return 0; } static int find_indirblks32(uint32_t blk, int ind_level, uint32_t *wantedblk) { #define MAXNINDIR (MAXBSIZE / sizeof(uint32_t)) uint32_t idblk[MAXNINDIR]; int i; blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize); if (ind_level <= 0) { if (find_blks32(idblk, sblock.fs_bsize / sizeof(uint32_t), wantedblk)) return 1; } else { ind_level--; for (i = 0; i < sblock.fs_bsize / sizeof(uint32_t); i++) { if (compare_blk32(wantedblk, idblk[i])) { if (founddatablk(idblk[i])) return 1; } if (idblk[i] != 0) if (find_indirblks32(idblk[i], ind_level, wantedblk)) return 1; } } #undef MAXNINDIR return 0; } static int find_blks64(uint64_t *buf, int size, uint64_t *wantedblk) { int blk; for (blk = 0; blk < size; blk++) { if (buf[blk] == 0) continue; if (compare_blk64(wantedblk, buf[blk])) { if (founddatablk(buf[blk])) return 1; } } return 0; } static int find_indirblks64(uint64_t blk, int ind_level, uint64_t *wantedblk) { #define MAXNINDIR (MAXBSIZE / sizeof(uint64_t)) uint64_t idblk[MAXNINDIR]; int i; blread(fsreadfd, (char *)idblk, fsbtodb(&sblock, blk), (int)sblock.fs_bsize); if (ind_level <= 0) { if (find_blks64(idblk, sblock.fs_bsize / sizeof(uint64_t), wantedblk)) return 1; } else { ind_level--; for (i = 0; i < sblock.fs_bsize / sizeof(uint64_t); i++) { if (compare_blk64(wantedblk, idblk[i])) { if (founddatablk(idblk[i])) return 1; } if (idblk[i] != 0) if (find_indirblks64(idblk[i], ind_level, wantedblk)) return 1; } } #undef MAXNINDIR return 0; } int findino(struct inodesc *idesc); /* from fsck */ static int dolookup(char *name); static int dolookup(char *name) { struct inodesc idesc; if (!checkactivedir()) return 0; idesc.id_number = curinum; idesc.id_func = findino; idesc.id_name = name; idesc.id_type = DATA; idesc.id_fix = IGNORE; if (ckinode(curinode, &idesc) & FOUND) { curinum = idesc.id_parent; curinode = ginode(curinum); printactive(0); return 1; } else { warnx("name `%s' not found in current inode directory", name); return 0; } } CMDFUNCSTART(focusname) { char *p, *val; if (!checkactive()) return 1; ocurrent = curinum; if (argv[1][0] == '/') { curinum = ROOTINO; curinode = ginode(ROOTINO); } else { if (!checkactivedir()) return 1; } for (p = argv[1]; p != NULL;) { while ((val = strsep(&p, "/")) != NULL && *val == '\0'); if (val) { printf("component `%s': ", val); fflush(stdout); if (!dolookup(val)) { curinode = ginode(curinum); return(1); } } } return 0; } CMDFUNCSTART(ln) { ino_t inum; int rval; char *cp; GETINUM(1,inum); if (!checkactivedir()) return 1; rval = makeentry(curinum, inum, argv[2]); if (rval) printf("Ino %ju entered as `%s'\n", (uintmax_t)inum, argv[2]); else printf("could not enter name? weird.\n"); curinode = ginode(curinum); return rval; } CMDFUNCSTART(rm) { int rval; if (!checkactivedir()) return 1; rval = changeino(curinum, argv[1], 0); if (rval & ALTERED) { printf("Name `%s' removed\n", argv[1]); return 0; } else { printf("could not remove name ('%s')? weird.\n", argv[1]); return 1; } } long slotcount, desired; int chinumfunc(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; if (slotcount++ == desired) { dirp->d_ino = idesc->id_parent; return STOP|ALTERED|FOUND; } return KEEPON; } CMDFUNCSTART(chinum) { char *cp; ino_t inum; struct inodesc idesc; slotcount = 0; if (!checkactivedir()) return 1; GETINUM(2,inum); desired = strtol(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' || desired < 0) { printf("invalid slot number `%s'\n", argv[1]); return 1; } idesc.id_number = curinum; idesc.id_func = chinumfunc; idesc.id_fix = IGNORE; idesc.id_type = DATA; idesc.id_parent = inum; /* XXX convenient hiding place */ if (ckinode(curinode, &idesc) & FOUND) return 0; else { warnx("no %sth slot in current directory", argv[1]); return 1; } } int chnamefunc(struct inodesc *idesc) { struct direct *dirp = idesc->id_dirp; struct direct testdir; if (slotcount++ == desired) { /* will name fit? */ testdir.d_namlen = strlen(idesc->id_name); if (DIRSIZ(NEWDIRFMT, &testdir) <= dirp->d_reclen) { dirp->d_namlen = testdir.d_namlen; strcpy(dirp->d_name, idesc->id_name); return STOP|ALTERED|FOUND; } else return STOP|FOUND; /* won't fit, so give up */ } return KEEPON; } CMDFUNCSTART(chname) { int rval; char *cp; struct inodesc idesc; slotcount = 0; if (!checkactivedir()) return 1; desired = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0') { printf("invalid slot number `%s'\n", argv[1]); return 1; } idesc.id_number = curinum; idesc.id_func = chnamefunc; idesc.id_fix = IGNORE; idesc.id_type = DATA; idesc.id_name = argv[2]; rval = ckinode(curinode, &idesc); if ((rval & (FOUND|ALTERED)) == (FOUND|ALTERED)) return 0; else if (rval & FOUND) { warnx("new name `%s' does not fit in slot %s\n", argv[2], argv[1]); return 1; } else { warnx("no %sth slot in current directory", argv[1]); return 1; } } struct typemap { const char *typename; int typebits; } typenamemap[] = { {"file", IFREG}, {"dir", IFDIR}, {"socket", IFSOCK}, {"fifo", IFIFO}, }; CMDFUNCSTART(newtype) { int type; struct typemap *tp; if (!checkactive()) return 1; type = DIP(curinode, di_mode) & IFMT; for (tp = typenamemap; tp < &typenamemap[nitems(typenamemap)]; tp++) { if (!strcmp(argv[1], tp->typename)) { printf("setting type to %s\n", tp->typename); type = tp->typebits; break; } } if (tp == &typenamemap[nitems(typenamemap)]) { warnx("type `%s' not known", argv[1]); warnx("try one of `file', `dir', `socket', `fifo'"); return 1; } DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~IFMT); DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | type); inodirty(curinode); printactive(0); return 0; } CMDFUNCSTART(chlen) { int rval = 1; long len; char *cp; if (!checkactive()) return 1; len = strtol(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' || len < 0) { warnx("bad length `%s'", argv[1]); return 1; } DIP_SET(curinode, di_size, len); inodirty(curinode); printactive(0); return rval; } CMDFUNCSTART(chmode) { int rval = 1; long modebits; char *cp; if (!checkactive()) return 1; modebits = strtol(argv[1], &cp, 8); if (cp == argv[1] || *cp != '\0' || (modebits & ~07777)) { warnx("bad modebits `%s'", argv[1]); return 1; } DIP_SET(curinode, di_mode, DIP(curinode, di_mode) & ~07777); DIP_SET(curinode, di_mode, DIP(curinode, di_mode) | modebits); inodirty(curinode); printactive(0); return rval; } CMDFUNCSTART(chaflags) { int rval = 1; u_long flags; char *cp; if (!checkactive()) return 1; flags = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { warnx("bad flags `%s'", argv[1]); return 1; } if (flags > UINT_MAX) { warnx("flags set beyond 32-bit range of field (%lx)\n", flags); return(1); } DIP_SET(curinode, di_flags, flags); inodirty(curinode); printactive(0); return rval; } CMDFUNCSTART(chgen) { int rval = 1; long gen; char *cp; if (!checkactive()) return 1; gen = strtol(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { warnx("bad gen `%s'", argv[1]); return 1; } if (gen > INT_MAX || gen < INT_MIN) { warnx("gen set beyond 32-bit range of field (%lx)\n", gen); return(1); } DIP_SET(curinode, di_gen, gen); inodirty(curinode); printactive(0); return rval; } CMDFUNCSTART(chsize) { int rval = 1; off_t size; char *cp; if (!checkactive()) return 1; size = strtoll(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0') { warnx("bad size `%s'", argv[1]); return 1; } if (size < 0) { warnx("size set to negative (%jd)\n", (intmax_t)size); return(1); } DIP_SET(curinode, di_size, size); inodirty(curinode); printactive(0); return rval; } CMDFUNCSTART(linkcount) { int rval = 1; int lcnt; char *cp; if (!checkactive()) return 1; lcnt = strtol(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { warnx("bad link count `%s'", argv[1]); return 1; } if (lcnt > USHRT_MAX || lcnt < 0) { warnx("max link count is %d\n", USHRT_MAX); return 1; } DIP_SET(curinode, di_nlink, lcnt); inodirty(curinode); printactive(0); return rval; } CMDFUNCSTART(chowner) { int rval = 1; unsigned long uid; char *cp; struct passwd *pwd; if (!checkactive()) return 1; uid = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { /* try looking up name */ if ((pwd = getpwnam(argv[1]))) { uid = pwd->pw_uid; } else { warnx("bad uid `%s'", argv[1]); return 1; } } DIP_SET(curinode, di_uid, uid); inodirty(curinode); printactive(0); return rval; } CMDFUNCSTART(chgroup) { int rval = 1; unsigned long gid; char *cp; struct group *grp; if (!checkactive()) return 1; gid = strtoul(argv[1], &cp, 0); if (cp == argv[1] || *cp != '\0' ) { if ((grp = getgrnam(argv[1]))) { gid = grp->gr_gid; } else { warnx("bad gid `%s'", argv[1]); return 1; } } DIP_SET(curinode, di_gid, gid); inodirty(curinode); printactive(0); return rval; } int dotime(char *name, time_t *secp, int32_t *nsecp) { char *p, *val; struct tm t; int32_t nsec; p = strchr(name, '.'); if (p) { *p = '\0'; nsec = strtoul(++p, &val, 0); if (val == p || *val != '\0' || nsec >= 1000000000 || nsec < 0) { warnx("invalid nanoseconds"); goto badformat; } } else nsec = 0; if (strlen(name) != 14) { badformat: warnx("date format: YYYYMMDDHHMMSS[.nsec]"); return 1; } *nsecp = nsec; for (p = name; *p; p++) if (*p < '0' || *p > '9') goto badformat; p = name; #define VAL() ((*p++) - '0') t.tm_year = VAL(); t.tm_year = VAL() + t.tm_year * 10; t.tm_year = VAL() + t.tm_year * 10; t.tm_year = VAL() + t.tm_year * 10 - 1900; t.tm_mon = VAL(); t.tm_mon = VAL() + t.tm_mon * 10 - 1; t.tm_mday = VAL(); t.tm_mday = VAL() + t.tm_mday * 10; t.tm_hour = VAL(); t.tm_hour = VAL() + t.tm_hour * 10; t.tm_min = VAL(); t.tm_min = VAL() + t.tm_min * 10; t.tm_sec = VAL(); t.tm_sec = VAL() + t.tm_sec * 10; t.tm_isdst = -1; *secp = mktime(&t); if (*secp == -1) { warnx("date/time out of range"); return 1; } return 0; } CMDFUNCSTART(chbtime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) return 1; curinode->dp2.di_birthtime = _time_to_time64(secs); curinode->dp2.di_birthnsec = nsecs; inodirty(curinode); printactive(0); return 0; } CMDFUNCSTART(chmtime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) curinode->dp1.di_mtime = _time_to_time32(secs); else curinode->dp2.di_mtime = _time_to_time64(secs); DIP_SET(curinode, di_mtimensec, nsecs); inodirty(curinode); printactive(0); return 0; } CMDFUNCSTART(chatime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) curinode->dp1.di_atime = _time_to_time32(secs); else curinode->dp2.di_atime = _time_to_time64(secs); DIP_SET(curinode, di_atimensec, nsecs); inodirty(curinode); printactive(0); return 0; } CMDFUNCSTART(chctime) { time_t secs; int32_t nsecs; if (dotime(argv[1], &secs, &nsecs)) return 1; if (sblock.fs_magic == FS_UFS1_MAGIC) curinode->dp1.di_ctime = _time_to_time32(secs); else curinode->dp2.di_ctime = _time_to_time64(secs); DIP_SET(curinode, di_ctimensec, nsecs); inodirty(curinode); printactive(0); return 0; } Index: stable/11/sbin/iscontrol/iscontrol.c =================================================================== --- stable/11/sbin/iscontrol/iscontrol.c (revision 359753) +++ stable/11/sbin/iscontrol/iscontrol.c (revision 359754) @@ -1,261 +1,264 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005-2010 Daniel Braniss * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* | $Id: iscontrol.c,v 2.2 2006/12/01 09:11:56 danny Exp danny $ */ /* | the user level initiator (client) */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iscontrol.h" static char version[] = "2.3.1"; // keep in sync with iscsi_initiator #define USAGE "[-v] [-d] [-c config] [-n name] [-t target] [-p pidfile]" #define OPTIONS "vdc:t:n:p:" token_t AuthMethods[] = { {"None", NONE}, {"KRB5", KRB5}, {"SPKM1", SPKM1}, {"SPKM2", SPKM2}, {"SRP", SRP}, {"CHAP", CHAP}, {0, 0} }; token_t DigestMethods[] = { {"None", 0}, {"CRC32", 1}, {"CRC32C", 1}, {0, 0} }; +int vflag; +char *iscsidev; + u_char isid[6 + 6]; /* | Default values */ isc_opt_t opvals = { .port = 3260, .sockbufsize = 128, .iqn = "iqn.2005-01.il.ac.huji.cs:", .sessionType = "Normal", .targetAddress = 0, .targetName = 0, .initiatorName = 0, .authMethod = "None", .headerDigest = "None,CRC32C", .dataDigest = "None,CRC32C", .maxConnections = 1, .maxRecvDataSegmentLength = 64 * 1024, .maxXmitDataSegmentLength = 8 * 1024, // 64 * 1024, .maxBurstLength = 128 * 1024, .firstBurstLength = 64 * 1024, // must be less than maxBurstLength .defaultTime2Wait = 0, .defaultTime2Retain = 0, .maxOutstandingR2T = 1, .errorRecoveryLevel = 0, .dataPDUInOrder = TRUE, .dataSequenceInOrder = TRUE, .initialR2T = TRUE, .immediateData = TRUE, }; static void usage(const char *pname) { fprintf(stderr, "usage: %s " USAGE "\n", pname); exit(1); } int lookup(token_t *tbl, char *m) { token_t *tp; for(tp = tbl; tp->name != NULL; tp++) if(strcasecmp(tp->name, m) == 0) return tp->val; return 0; } int main(int cc, char **vv) { int ch, disco; char *pname, *pidfile, *p, *q, *ta, *kw, *v; isc_opt_t *op; FILE *fd; size_t n; op = &opvals; iscsidev = "/dev/"ISCSIDEV; fd = NULL; pname = vv[0]; if ((pname = basename(pname)) == NULL) err(1, "basename"); kw = ta = 0; disco = 0; pidfile = NULL; /* | check for driver & controller version match */ n = 0; #define VERSION_OID_S "net.iscsi_initiator.driver_version" if (sysctlbyname(VERSION_OID_S, 0, &n, 0, 0) != 0) { if (errno == ENOENT) errx(1, "sysctlbyname(\"" VERSION_OID_S "\") " "failed; is the iscsi driver loaded?"); err(1, "sysctlbyname(\"" VERSION_OID_S "\")"); } v = malloc(n+1); if (v == NULL) err(1, "malloc"); if (sysctlbyname(VERSION_OID_S, v, &n, 0, 0) != 0) err(1, "sysctlbyname"); if (strncmp(version, v, 3) != 0) errx(1, "versions mismatch"); while((ch = getopt(cc, vv, OPTIONS)) != -1) { switch(ch) { case 'v': vflag++; break; case 'c': fd = fopen(optarg, "r"); if (fd == NULL) err(1, "fopen(\"%s\")", optarg); break; case 'd': disco = 1; break; case 't': ta = optarg; break; case 'n': kw = optarg; break; case 'p': pidfile = optarg; break; default: usage(pname); } } if(fd == NULL) fd = fopen("/etc/iscsi.conf", "r"); if(fd != NULL) { parseConfig(fd, kw, op); fclose(fd); } cc -= optind; vv += optind; if(cc > 0) { if(vflag) printf("adding '%s'\n", *vv); parseArgs(cc, vv, op); } if(ta) op->targetAddress = ta; if(op->targetAddress == NULL) { warnx("no target specified!"); usage(pname); } q = op->targetAddress; if(*q == '[' && (q = strchr(q, ']')) != NULL) { *q++ = '\0'; op->targetAddress++; } else q = op->targetAddress; if((p = strchr(q, ':')) != NULL) { *p++ = 0; op->port = atoi(p); p = strchr(p, ','); } if(p || ((p = strchr(q, ',')) != NULL)) { *p++ = 0; op->targetPortalGroupTag = atoi(p); } if(op->initiatorName == 0) { char hostname[MAXHOSTNAMELEN]; if(op->iqn) { if(gethostname(hostname, sizeof(hostname)) == 0) asprintf(&op->initiatorName, "%s:%s", op->iqn, hostname); else asprintf(&op->initiatorName, "%s:%d", op->iqn, (int)time(0) & 0xff); // XXX: } else { if(gethostname(hostname, sizeof(hostname)) == 0) asprintf(&op->initiatorName, "%s", hostname); else asprintf(&op->initiatorName, "%d", (int)time(0) & 0xff); // XXX: } } if(disco) { op->sessionType = "Discovery"; op->targetName = 0; } op->pidfile = pidfile; fsm(op); exit(0); } Index: stable/11/sbin/iscontrol/iscontrol.h =================================================================== --- stable/11/sbin/iscontrol/iscontrol.h (revision 359753) +++ stable/11/sbin/iscontrol/iscontrol.h (revision 359754) @@ -1,167 +1,167 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005-2010 Daniel Braniss * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* | $Id: iscontrol.h,v 2.3 2007/04/27 08:36:49 danny Exp danny $ */ #ifdef DEBUG int vflag; # define debug(level, fmt, args...) do {if (level <= vflag) printf("%s: " fmt "\n", __func__ , ##args);} while(0) # define debug_called(level) do {if (level <= vflag) printf("%s: called\n", __func__);} while(0) #else # define debug(level, fmt, args...) # define debug_called(level) #endif // DEBUG #define xdebug(fmt, args...) printf("%s: " fmt "\n", __func__ , ##args) #define BIT(n) (1 <<(n)) #define MAXREDIRECTS 2 typedef int auth_t(void *sess); typedef struct { char *address; int port; int pgt; } target_t; typedef struct isess { int flags; #define SESS_CONNECTED BIT(0) #define SESS_DISCONNECT BIT(1) #define SESS_LOGGEDIN BIT(2) #define SESS_RECONNECT BIT(3) #define SESS_REDIRECT BIT(4) #define SESS_NEGODONE BIT(10) // XXX: kludge #define SESS_FULLFEATURE BIT(29) #define SESS_INITIALLOGIN1 BIT(30) #define SESS_INITIALLOGIN BIT(31) isc_opt_t *op; // operational values target_t target; // the Original target address int fd; // the session fd int soc; // the socket iscsi_cam_t cam; struct cam_device *camdev; time_t open_time; int redirect_cnt; time_t redirect_time; int reconnect_cnt; int reconnect_cnt1; time_t reconnect_time; char isid[6+1]; int csg; // current stage int nsg; // next stage // Phases/Stages #define SN_PHASE 0 // Security Negotiation #define LON_PHASE 1 // Login Operational Negotiation #define FF_PHASE 3 // FuLL-Feature uint tsih; sn_t sn; } isess_t; typedef struct token { char *name; int val; } token_t; typedef enum { NONE = 0, KRB5, SPKM1, SPKM2, SRP, CHAP } authm_t; extern token_t AuthMethods[]; extern token_t DigestMethods[]; typedef enum { SET, GET } oper_t; typedef enum { U_PR, // private U_IO, // Initialize Only -- during login U_LO, // Leading Only -- when TSIH is zero U_FFPO, // Full Feature Phase Only U_ALL // in any phase } usage_t; typedef enum { S_PR, S_CO, // Connect only S_SW // Session Wide } scope_t; typedef void keyfun_t(isess_t *, oper_t); typedef struct { usage_t usage; scope_t scope; char *name; int tokenID; } textkey_t; typedef int handler_t(isess_t *sess, pdu_t *pp); int authenticateLogin(isess_t *sess); int fsm(isc_opt_t *op); int sendPDU(isess_t *sess, pdu_t *pp, handler_t *hdlr); int addText(pdu_t *pp, char *fmt, ...); void freePDU(pdu_t *pp); int xmitpdu(isess_t *sess, pdu_t *pp); int recvpdu(isess_t *sess, pdu_t *pp); int lookup(token_t *tbl, char *m); -int vflag; -char *iscsidev; +extern int vflag; +extern char *iscsidev; void parseArgs(int nargs, char **args, isc_opt_t *op); void parseConfig(FILE *fd, char *key, isc_opt_t *op); char *chapDigest(char *ap, char id, char *cp, char *chapSecret); char *genChapChallenge(char *encoding, uint len); int str2bin(char *str, char **rsp); char *bin2str(char *fmt, unsigned char *md, int blen); int negotiateOPV(isess_t *sess); int setOptions(isess_t *sess, int flag); int loginPhase(isess_t *sess); Index: stable/11/stand/userboot/userboot/libuserboot.h =================================================================== --- stable/11/stand/userboot/userboot/libuserboot.h (revision 359753) +++ stable/11/stand/userboot/userboot/libuserboot.h (revision 359754) @@ -1,68 +1,68 @@ /*- * Copyright (c) 2011 Google, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "userboot.h" extern struct loader_callbacks *callbacks; extern void *callbacks_arg; #define CALLBACK(fn, args...) (callbacks->fn(callbacks_arg , ##args)) #define MAXDEV 31 /* maximum number of distinct devices */ typedef unsigned long physaddr_t; /* exported devices */ extern struct devsw userboot_disk; extern int userboot_disk_maxunit; extern struct devsw host_dev; /* access to host filesystem */ -struct fs_ops host_fsops; +extern struct fs_ops host_fsops; struct bootinfo; struct preloaded_file; extern int bi_load(struct bootinfo *, struct preloaded_file *); extern void delay(int); extern int userboot_autoload(void); extern ssize_t userboot_copyin(const void *, vm_offset_t, size_t); extern ssize_t userboot_copyout(vm_offset_t, void *, size_t); extern ssize_t userboot_readin(int, vm_offset_t, size_t); extern int userboot_getdev(void **, const char *, const char **); char *userboot_fmtdev(void *vdev); int userboot_setcurrdev(struct env_var *ev, int flags, const void *value); int bi_getboothowto(char *kargs); void bi_setboothowto(int howto); vm_offset_t bi_copyenv(vm_offset_t addr); int bi_load32(char *args, int *howtop, int *bootdevp, vm_offset_t *bip, vm_offset_t *modulep, vm_offset_t *kernend); int bi_load64(char *args, vm_offset_t *modulep, vm_offset_t *kernend); void bios_addsmapdata(struct preloaded_file *kfp); Index: stable/11/tests/sys/kqueue/libkqueue/common.h =================================================================== --- stable/11/tests/sys/kqueue/libkqueue/common.h (revision 359753) +++ stable/11/tests/sys/kqueue/libkqueue/common.h (revision 359754) @@ -1,79 +1,80 @@ /* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #ifndef _COMMON_H #define _COMMON_H #include "config.h" /* Needed for HAVE_* defines */ #if HAVE_ERR_H # include #else # define err(rc,msg,...) do { perror(msg); exit(rc); } while (0) # define errx(rc,msg,...) do { puts(msg); exit(rc); } while (0) #endif #include #include #include #include #include #include #include #include #include #include #include extern char *cur_test_id; -int vnode_fd; +extern int vnode_fd; +extern int kqfd; extern char * kevent_to_str(struct kevent *); struct kevent * kevent_get(int); struct kevent * kevent_get_timeout(int, int); void kevent_cmp(struct kevent *, struct kevent *); void kevent_add(int kqfd, struct kevent *kev, uintptr_t ident, short filter, u_short flags, u_int fflags, intptr_t data, void *udata); /* DEPRECATED: */ #define KEV_CMP(kev,_ident,_filter,_flags) do { \ if (kev.ident != (_ident) || \ kev.filter != (_filter) || \ kev.flags != (_flags)) \ err(1, "kevent mismatch: got [%d,%d,%d] but expecting [%d,%d,%d]", \ (int)_ident, (int)_filter, (int)_flags,\ (int)kev.ident, kev.filter, kev.flags);\ } while (0); /* Checks if any events are pending, which is an error. */ extern void test_no_kevents(void); extern void test_no_kevents_quietly(void); extern void test_begin(const char *); extern void success(void); #endif /* _COMMON_H */ Index: stable/11/tests/sys/kqueue/libkqueue/proc.c =================================================================== --- stable/11/tests/sys/kqueue/libkqueue/proc.c (revision 359753) +++ stable/11/tests/sys/kqueue/libkqueue/proc.c (revision 359754) @@ -1,426 +1,425 @@ /* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include #include #include "config.h" #include "common.h" static int sigusr1_caught = 0; -int kqfd; static void sig_handler(int signum) { sigusr1_caught = 1; } static void add_and_delete(void) { struct kevent kev; pid_t pid; /* Create a child that waits to be killed and then exits */ pid = fork(); if (pid == 0) { struct stat s; if (fstat(kqfd, &s) != -1) errx(1, "kqueue inherited across fork! (%s() at %s:%d)", __func__, __FILE__, __LINE__); pause(); exit(2); } printf(" -- child created (pid %d)\n", (int) pid); test_begin("kevent(EVFILT_PROC, EV_ADD)"); test_no_kevents(); kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL); test_no_kevents(); success(); test_begin("kevent(EVFILT_PROC, EV_DELETE)"); sleep(1); test_no_kevents(); kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL); if (kill(pid, SIGKILL) < 0) err(1, "kill"); sleep(1); test_no_kevents(); success(); } static void proc_track(int sleep_time) { char test_id[64]; struct kevent kev; pid_t pid; int pipe_fd[2]; ssize_t result; snprintf(test_id, sizeof(test_id), "kevent(EVFILT_PROC, NOTE_TRACK); sleep %d", sleep_time); test_begin(test_id); test_no_kevents(); if (pipe(pipe_fd)) { err(1, "pipe (parent) failed! (%s() at %s:%d)", __func__, __FILE__, __LINE__); } /* Create a child to track. */ pid = fork(); if (pid == 0) { /* Child */ pid_t grandchild = -1; /* * Give the parent a chance to start tracking us. */ result = read(pipe_fd[1], test_id, 1); if (result != 1) { err(1, "read from pipe in child failed! (ret %zd) (%s() at %s:%d)", result, __func__, __FILE__, __LINE__); } /* * Spawn a grandchild that will immediately exit. If the kernel has bug * 180385, the parent will see a kevent with both NOTE_CHILD and * NOTE_EXIT. If that bug is fixed, it will see two separate kevents * for those notes. Note that this triggers the conditions for * detecting the bug quite reliably on a 1 CPU system (or if the test * process is restricted to a single CPU), but may not trigger it on a * multi-CPU system. */ grandchild = fork(); if (grandchild == 0) { /* Grandchild */ if (sleep_time) sleep(sleep_time); exit(1); } else if (grandchild == -1) { /* Error */ err(1, "fork (grandchild) failed! (%s() at %s:%d)", __func__, __FILE__, __LINE__); } else { /* Child (Grandchild Parent) */ printf(" -- grandchild created (pid %d)\n", (int) grandchild); } if (sleep_time) sleep(sleep_time); exit(0); } else if (pid == -1) { /* Error */ err(1, "fork (child) failed! (%s() at %s:%d)", __func__, __FILE__, __LINE__); } printf(" -- child created (pid %d)\n", (int) pid); kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD | EV_ENABLE, NOTE_TRACK | NOTE_EXEC | NOTE_EXIT | NOTE_FORK, 0, NULL); printf(" -- tracking child (pid %d)\n", (int) pid); /* Now that we're tracking the child, tell it to proceed. */ result = write(pipe_fd[0], test_id, 1); if (result != 1) { err(1, "write to pipe in parent failed! (ret %zd) (%s() at %s:%d)", result, __func__, __FILE__, __LINE__); } /* * Several events should be received: * - NOTE_FORK (from child) * - NOTE_CHILD (from grandchild) * - NOTE_EXIT (from grandchild) * - NOTE_EXIT (from child) * * The NOTE_FORK and NOTE_EXIT from the child could be combined into a * single event, but the NOTE_CHILD and NOTE_EXIT from the grandchild must * not be combined. * * The loop continues until no events are received within a 5 second * period, at which point it is assumed that no more will be coming. The * loop is deliberately designed to attempt to get events even after all * the expected ones are received in case some spurious events are * generated as well as the expected ones. */ { int child_exit = 0; int child_fork = 0; int gchild_exit = 0; int gchild_note = 0; pid_t gchild_pid = -1; int done = 0; char *kev_str; while (!done) { int handled = 0; struct kevent *kevp; kevp = kevent_get_timeout(kqfd, 5); if (kevp == NULL) { done = 1; } else { kev_str = kevent_to_str(kevp); printf(" -- Received kevent: %s\n", kev_str); free(kev_str); if ((kevp->fflags & NOTE_CHILD) && (kevp->fflags & NOTE_EXIT)) { errx(1, "NOTE_CHILD and NOTE_EXIT in same kevent: %s", kevent_to_str(kevp)); } if (kevp->fflags & NOTE_CHILD) { if (kevp->data == pid) { if (!gchild_note) { ++gchild_note; gchild_pid = kevp->ident; ++handled; } else { errx(1, "Spurious NOTE_CHILD: %s", kevent_to_str(kevp)); } } } if (kevp->fflags & NOTE_EXIT) { if ((kevp->ident == pid) && (!child_exit)) { ++child_exit; ++handled; } else if ((kevp->ident == gchild_pid) && (!gchild_exit)) { ++gchild_exit; ++handled; } else { errx(1, "Spurious NOTE_EXIT: %s", kevent_to_str(kevp)); } } if (kevp->fflags & NOTE_FORK) { if ((kevp->ident == pid) && (!child_fork)) { ++child_fork; ++handled; } else { errx(1, "Spurious NOTE_FORK: %s", kevent_to_str(kevp)); } } if (!handled) { errx(1, "Spurious kevent: %s", kevent_to_str(kevp)); } free(kevp); } } /* Make sure all expected events were received. */ if (child_exit && child_fork && gchild_exit && gchild_note) { printf(" -- Received all expected events.\n"); } else { errx(1, "Did not receive all expected events."); } } success(); } #ifdef TODO static void event_trigger(void) { struct kevent kev; pid_t pid; test_begin("kevent(EVFILT_PROC, wait)"); /* Create a child that waits to be killed and then exits */ pid = fork(); if (pid == 0) { pause(); printf(" -- child caught signal, exiting\n"); exit(2); } printf(" -- child created (pid %d)\n", (int) pid); test_no_kevents(); kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL); /* Cause the child to exit, then retrieve the event */ printf(" -- killing process %d\n", (int) pid); if (kill(pid, SIGUSR1) < 0) err(1, "kill"); kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(); success(); } void test_kevent_signal_disable(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) err(1, "sigprocmask"); if (kill(getpid(), SIGKILL) < 0) err(1, "kill"); test_no_kevents(); success(); } void test_kevent_signal_enable(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) err(1, "sigprocmask"); if (kill(getpid(), SIGUSR1) < 0) err(1, "kill"); kev.flags = EV_ADD | EV_CLEAR; #if LIBKQUEUE kev.data = 1; /* WORKAROUND */ #else kev.data = 2; // one extra time from test_kevent_signal_disable() #endif kevent_cmp(&kev, kevent_get(kqfd)); /* Delete the watch */ kev.flags = EV_DELETE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } void test_kevent_signal_del(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)"; struct kevent kev; test_begin(test_id); /* Delete the kevent */ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) err(1, "sigprocmask"); if (kill(getpid(), SIGUSR1) < 0) err(1, "kill"); test_no_kevents(); success(); } void test_kevent_signal_oneshot(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) err(1, "sigprocmask"); if (kill(getpid(), SIGUSR1) < 0) err(1, "kill"); kev.flags |= EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Send another one and make sure we get no events */ if (kill(getpid(), SIGUSR1) < 0) err(1, "kill"); test_no_kevents(); success(); } #endif void test_evfilt_proc() { kqfd = kqueue(); signal(SIGUSR1, sig_handler); add_and_delete(); proc_track(0); /* Run without sleeping before children exit. */ proc_track(1); /* Sleep a bit in the children before exiting. */ #if TODO event_trigger(); #endif signal(SIGUSR1, SIG_DFL); #if TODO test_kevent_signal_add(); test_kevent_signal_del(); test_kevent_signal_get(); test_kevent_signal_disable(); test_kevent_signal_enable(); test_kevent_signal_oneshot(); #endif close(kqfd); } Index: stable/11/tests/sys/kqueue/libkqueue/read.c =================================================================== --- stable/11/tests/sys/kqueue/libkqueue/read.c (revision 359753) +++ stable/11/tests/sys/kqueue/libkqueue/read.c (revision 359754) @@ -1,326 +1,325 @@ /* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "common.h" -int kqfd; int sockfd[2]; static void kevent_socket_drain(void) { char buf[1]; /* Drain the read buffer, then make sure there are no more events. */ puts("draining the read buffer"); if (read(sockfd[0], &buf[0], 1) < 1) err(1, "read(2)"); } static void kevent_socket_fill(void) { puts("filling the read buffer"); if (write(sockfd[1], ".", 1) < 1) err(1, "write(2)"); } void test_kevent_socket_add(void) { const char *test_id = "kevent(EVFILT_READ, EV_ADD)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } void test_kevent_socket_get(void) { const char *test_id = "kevent(EVFILT_READ) wait"; struct kevent kev; test_begin(test_id); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); kevent_socket_fill(); kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); kevent_socket_drain(); test_no_kevents(); kev.flags = EV_DELETE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } void test_kevent_socket_clear(void) { const char *test_id = "kevent(EVFILT_READ, EV_CLEAR)"; struct kevent kev; test_begin(test_id); test_no_kevents(); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); kevent_socket_fill(); kevent_socket_fill(); kev.data = 2; kevent_cmp(&kev, kevent_get(kqfd)); /* We filled twice, but drain once. Edge-triggered would not generate additional events. */ kevent_socket_drain(); test_no_kevents(); kevent_socket_drain(); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } void test_kevent_socket_disable_and_enable(void) { const char *test_id = "kevent(EVFILT_READ, EV_DISABLE)"; struct kevent kev; test_begin(test_id); /* * Write to the socket before adding the event. This way we can verify that * enabling a triggered kevent causes the event to be returned immediately. */ kevent_socket_fill(); /* Add a disabled event. */ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_DISABLE, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); test_no_kevents(); /* Re-enable the knote, then see if an event is generated */ kev.flags = EV_ENABLE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); kev.flags = EV_ADD; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); kevent_socket_drain(); kev.flags = EV_DELETE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } void test_kevent_socket_del(void) { const char *test_id = "kevent(EVFILT_READ, EV_DELETE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); kevent_socket_fill(); test_no_kevents(); kevent_socket_drain(); success(); } void test_kevent_socket_oneshot(void) { const char *test_id = "kevent(EVFILT_READ, EV_ONESHOT)"; struct kevent kev; test_begin(test_id); /* Re-add the watch and make sure no events are pending */ puts("-- re-adding knote"); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); test_no_kevents(); puts("-- getting one event"); kevent_socket_fill(); kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); puts("-- checking knote disabled"); test_no_kevents(); /* Try to delete the knote, it should already be deleted */ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) == 0) err(1, "%s", test_id); kevent_socket_drain(); success(); } #if HAVE_EV_DISPATCH void test_kevent_socket_dispatch(void) { const char *test_id = "kevent(EVFILT_READ, EV_DISPATCH)"; test_begin(test_id); struct kevent kev; /* Re-add the watch and make sure no events are pending */ puts("-- re-adding knote"); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_DISPATCH, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); test_no_kevents(); /* The event will occur only once, even though EV_CLEAR is not specified. */ kevent_socket_fill(); kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(); /* Since the knote is disabled, the EV_DELETE operation succeeds. */ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); kevent_socket_drain(); success(); } #endif /* HAVE_EV_DISPATCH */ #if BROKEN void test_kevent_socket_lowat(void) { const char *test_id = "kevent(EVFILT_READ, NOTE_LOWAT)"; struct kevent kev; test_begin(test_id); /* Re-add the watch and make sure no events are pending */ puts("-- re-adding knote, setting low watermark to 2 bytes"); EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD | EV_ONESHOT, NOTE_LOWAT, 2, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); test_no_kevents(); puts("-- checking that one byte does not trigger an event.."); kevent_socket_fill(); test_no_kevents(); puts("-- checking that two bytes triggers an event.."); kevent_socket_fill(); if (kevent(kqfd, NULL, 0, &kev, 1, NULL) != 1) err(1, "%s", test_id); KEV_CMP(kev, sockfd[0], EVFILT_READ, 0); test_no_kevents(); kevent_socket_drain(); kevent_socket_drain(); success(); } #endif void test_kevent_socket_eof(void) { const char *test_id = "kevent(EVFILT_READ, EV_EOF)"; struct kevent kev; test_begin(test_id); /* Re-add the watch and make sure no events are pending */ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); test_no_kevents(); if (close(sockfd[1]) < 0) err(1, "close(2)"); kev.flags |= EV_EOF; kevent_cmp(&kev, kevent_get(kqfd)); /* Delete the watch */ EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } void test_evfilt_read() { /* Create a connected pair of full-duplex sockets for testing socket events */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0) abort(); kqfd = kqueue(); test_kevent_socket_add(); test_kevent_socket_del(); test_kevent_socket_get(); test_kevent_socket_disable_and_enable(); test_kevent_socket_oneshot(); test_kevent_socket_clear(); #if HAVE_EV_DISPATCH test_kevent_socket_dispatch(); #endif test_kevent_socket_eof(); close(kqfd); } Index: stable/11/tests/sys/kqueue/libkqueue/signal.c =================================================================== --- stable/11/tests/sys/kqueue/libkqueue/signal.c (revision 359753) +++ stable/11/tests/sys/kqueue/libkqueue/signal.c (revision 359754) @@ -1,199 +1,198 @@ /* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "common.h" -int kqfd; void test_kevent_signal_add(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_ADD)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } void test_kevent_signal_get(void) { const char *test_id = "kevent(EVFILT_SIGNAL, wait)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) err(1, "sigprocmask"); if (kill(getpid(), SIGUSR1) < 0) err(1, "kill"); kev.flags |= EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); success(); } void test_kevent_signal_disable(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) err(1, "sigprocmask"); if (kill(getpid(), SIGUSR1) < 0) err(1, "kill"); test_no_kevents(); success(); } void test_kevent_signal_enable(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) err(1, "sigprocmask"); if (kill(getpid(), SIGUSR1) < 0) err(1, "kill"); kev.flags = EV_ADD | EV_CLEAR; #if LIBKQUEUE kev.data = 1; /* WORKAROUND */ #else kev.data = 2; // one extra time from test_kevent_signal_disable() #endif kevent_cmp(&kev, kevent_get(kqfd)); /* Delete the watch */ kev.flags = EV_DELETE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } void test_kevent_signal_del(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)"; struct kevent kev; test_begin(test_id); /* Delete the kevent */ EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) err(1, "sigprocmask"); if (kill(getpid(), SIGUSR1) < 0) err(1, "kill"); test_no_kevents(); success(); } void test_kevent_signal_oneshot(void) { const char *test_id = "kevent(EVFILT_SIGNAL, EV_ONESHOT)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD | EV_ONESHOT, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Block SIGUSR1, then send it to ourselves */ sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGUSR1); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) err(1, "sigprocmask"); if (kill(getpid(), SIGUSR1) < 0) err(1, "kill"); kev.flags |= EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Send another one and make sure we get no events */ if (kill(getpid(), SIGUSR1) < 0) err(1, "kill"); test_no_kevents(); success(); } void test_evfilt_signal() { kqfd = kqueue(); test_kevent_signal_add(); test_kevent_signal_del(); test_kevent_signal_get(); test_kevent_signal_disable(); test_kevent_signal_enable(); test_kevent_signal_oneshot(); close(kqfd); } Index: stable/11/tests/sys/kqueue/libkqueue/timer.c =================================================================== --- stable/11/tests/sys/kqueue/libkqueue/timer.c (revision 359753) +++ stable/11/tests/sys/kqueue/libkqueue/timer.c (revision 359754) @@ -1,493 +1,492 @@ /* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "common.h" #include #define MILLION 1000000 #define THOUSAND 1000 #define SEC_TO_MS(t) ((t) * THOUSAND) /* Convert seconds to milliseconds. */ #define SEC_TO_US(t) ((t) * MILLION) /* Convert seconds to microseconds. */ #define MS_TO_US(t) ((t) * THOUSAND) /* Convert milliseconds to microseconds. */ #define US_TO_NS(t) ((t) * THOUSAND) /* Convert microseconds to nanoseconds. */ -int kqfd; /* Get the current time with microsecond precision. Used for * sub-second timing to make some timer tests run faster. */ static long now(void) { struct timeval tv; gettimeofday(&tv, NULL); return SEC_TO_US(tv.tv_sec) + tv.tv_usec; } /* Sleep for a given number of milliseconds. The timeout is assumed to * be less than 1 second. */ void mssleep(int t) { struct timespec stime = { .tv_sec = 0, .tv_nsec = US_TO_NS(MS_TO_US(t)), }; nanosleep(&stime, NULL); } /* Sleep for a given number of microseconds. The timeout is assumed to * be less than 1 second. */ void ussleep(int t) { struct timespec stime = { .tv_sec = 0, .tv_nsec = US_TO_NS(t), }; nanosleep(&stime, NULL); } void test_kevent_timer_add(void) { const char *test_id = "kevent(EVFILT_TIMER, EV_ADD)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } void test_kevent_timer_del(void) { const char *test_id = "kevent(EVFILT_TIMER, EV_DELETE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); test_no_kevents(); success(); } void test_kevent_timer_get(void) { const char *test_id = "kevent(EVFILT_TIMER, wait)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); kev.flags |= EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } static void test_oneshot(void) { const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT)"; struct kevent kev; test_begin(test_id); test_no_kevents(); EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 500,NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Retrieve the event */ kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Check if the event occurs again */ sleep(3); test_no_kevents(); success(); } static void test_periodic(void) { const char *test_id = "kevent(EVFILT_TIMER, periodic)"; struct kevent kev; test_begin(test_id); test_no_kevents(); EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, 1000,NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Retrieve the event */ kev.flags = EV_ADD | EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Check if the event occurs again */ sleep(1); kevent_cmp(&kev, kevent_get(kqfd)); /* Delete the event */ kev.flags = EV_DELETE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } static void disable_and_enable(void) { const char *test_id = "kevent(EVFILT_TIMER, EV_DISABLE and EV_ENABLE)"; struct kevent kev; test_begin(test_id); test_no_kevents(); /* Add the watch and immediately disable it */ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 2000,NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); kev.flags = EV_DISABLE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); test_no_kevents(); /* Re-enable and check again */ kev.flags = EV_ENABLE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); success(); } static void test_update(void) { const char *test_id = "kevent(EVFILT_TIMER (UPDATE), EV_ADD | EV_ONESHOT)"; struct kevent kev; long elapsed; long start; test_begin(test_id); test_no_kevents(); /* First set the timer to 1 second */ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, NOTE_USECONDS, SEC_TO_US(1), (void *)1); start = now(); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Now reduce the timer to 1 ms */ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, NOTE_USECONDS, MS_TO_US(1), (void *)2); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Wait for the event */ kev.flags |= EV_CLEAR; kev.fflags &= ~NOTE_USECONDS; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); elapsed = now() - start; /* Check that the timer expired after at least 1 ms, but less than * 1 second. This check is to make sure that the original 1 second * timeout was not used. */ printf("timer expired after %ld us\n", elapsed); if (elapsed < MS_TO_US(1)) errx(1, "early timer expiration: %ld us", elapsed); if (elapsed > SEC_TO_US(1)) errx(1, "late timer expiration: %ld us", elapsed); success(); } static void test_update_equal(void) { const char *test_id = "kevent(EVFILT_TIMER (UPDATE=), EV_ADD | EV_ONESHOT)"; struct kevent kev; long elapsed; long start; test_begin(test_id); test_no_kevents(); /* First set the timer to 1 ms */ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, NOTE_USECONDS, MS_TO_US(1), NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Sleep for a significant fraction of the timeout. */ ussleep(600); /* Now re-add the timer with the same parameters */ start = now(); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Wait for the event */ kev.flags |= EV_CLEAR; kev.fflags &= ~NOTE_USECONDS; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); elapsed = now() - start; /* Check that the timer expired after at least 1 ms. This check is * to make sure that the timer re-started and that the event is * not from the original add of the timer. */ printf("timer expired after %ld us\n", elapsed); if (elapsed < MS_TO_US(1)) errx(1, "early timer expiration: %ld us", elapsed); success(); } static void test_update_expired(void) { const char *test_id = "kevent(EVFILT_TIMER (UPDATE EXP), EV_ADD | EV_ONESHOT)"; struct kevent kev; long elapsed; long start; test_begin(test_id); test_no_kevents(); /* Set the timer to 1ms */ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, NOTE_USECONDS, MS_TO_US(1), NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Wait for 2 ms to give the timer plenty of time to expire. */ mssleep(2); /* Now re-add the timer */ start = now(); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Wait for the event */ kev.flags |= EV_CLEAR; kev.fflags &= ~NOTE_USECONDS; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); elapsed = now() - start; /* Check that the timer expired after at least 1 ms. This check * is to make sure that the timer re-started and that the event is * not from the original add (and expiration) of the timer. */ printf("timer expired after %ld us\n", elapsed); if (elapsed < MS_TO_US(1)) errx(1, "early timer expiration: %ld us", elapsed); /* Make sure the re-added timer does not fire. In other words, * test that the event received above was the only event from the * add and re-add of the timer. */ mssleep(2); test_no_kevents(); success(); } static void test_update_periodic(void) { const char *test_id = "kevent(EVFILT_TIMER (UPDATE), periodic)"; struct kevent kev; long elapsed; long start; long stop; test_begin(test_id); test_no_kevents(); EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(1), NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Retrieve the event */ kev.flags = EV_ADD | EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); /* Check if the event occurs again */ sleep(1); kevent_cmp(&kev, kevent_get(kqfd)); /* Re-add with new timeout. */ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(2), NULL); start = now(); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Retrieve the event */ kev.flags = EV_ADD | EV_CLEAR; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); stop = now(); elapsed = stop - start; /* Check that the timer expired after at least 2 ms. */ printf("timer expired after %ld us\n", elapsed); if (elapsed < MS_TO_US(2)) errx(1, "early timer expiration: %ld us", elapsed); /* Delete the event */ kev.flags = EV_DELETE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } static void test_update_timing(void) { #define MIN_SLEEP 500 #define MAX_SLEEP 1500 const char *test_id = "kevent(EVFILT_TIMER (UPDATE TIMING), EV_ADD | EV_ONESHOT)"; struct kevent kev; int iteration; int sleeptime; long elapsed; long start; long stop; test_begin(test_id); test_no_kevents(); /* Re-try the update tests with a variety of delays between the * original timer activation and the update of the timer. The goal * is to show that in all cases the only timer event that is * received is from the update and not the original timer add. */ for (sleeptime = MIN_SLEEP, iteration = 1; sleeptime < MAX_SLEEP; ++sleeptime, ++iteration) { /* First set the timer to 1 ms */ EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, NOTE_USECONDS, MS_TO_US(1), NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Delay; the delay ranges from less than to greater than the * timer period. */ ussleep(sleeptime); /* Now re-add the timer with the same parameters */ start = now(); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Wait for the event */ kev.flags |= EV_CLEAR; kev.fflags &= ~NOTE_USECONDS; kev.data = 1; kevent_cmp(&kev, kevent_get(kqfd)); stop = now(); elapsed = stop - start; /* Check that the timer expired after at least 1 ms. This * check is to make sure that the timer re-started and that * the event is not from the original add of the timer. */ if (elapsed < MS_TO_US(1)) errx(1, "early timer expiration: %ld us", elapsed); /* Make sure the re-added timer does not fire. In other words, * test that the event received above was the only event from * the add and re-add of the timer. */ mssleep(2); test_no_kevents_quietly(); } success(); } void test_evfilt_timer() { kqfd = kqueue(); test_kevent_timer_add(); test_kevent_timer_del(); test_kevent_timer_get(); test_oneshot(); test_periodic(); test_update(); test_update_equal(); test_update_expired(); test_update_timing(); test_update_periodic(); disable_and_enable(); close(kqfd); } Index: stable/11/tests/sys/kqueue/libkqueue/user.c =================================================================== --- stable/11/tests/sys/kqueue/libkqueue/user.c (revision 359753) +++ stable/11/tests/sys/kqueue/libkqueue/user.c (revision 359754) @@ -1,129 +1,128 @@ /* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "common.h" -int kqfd; static void add_and_delete(void) { const char *test_id = "kevent(EVFILT_USER, EV_ADD and EV_DELETE)"; struct kevent kev; test_begin(test_id); kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL); test_no_kevents(); kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DELETE, 0, 0, NULL); test_no_kevents(); success(); } static void event_wait(void) { const char *test_id = "kevent(EVFILT_USER, wait)"; struct kevent kev; test_begin(test_id); test_no_kevents(); /* Add the event, and then trigger it */ kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, NULL); kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); kev.fflags &= ~NOTE_FFCTRLMASK; kev.fflags &= ~NOTE_TRIGGER; kev.flags = EV_CLEAR; kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(); success(); } static void disable_and_enable(void) { const char *test_id = "kevent(EVFILT_USER, EV_DISABLE and EV_ENABLE)"; struct kevent kev; test_begin(test_id); test_no_kevents(); kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL); kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DISABLE, 0, 0, NULL); /* Trigger the event, but since it is disabled, nothing will happen. */ kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); test_no_kevents(); kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ENABLE, 0, 0, NULL); kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); kev.flags = EV_CLEAR; kev.fflags &= ~NOTE_FFCTRLMASK; kev.fflags &= ~NOTE_TRIGGER; kevent_cmp(&kev, kevent_get(kqfd)); success(); } static void oneshot(void) { const char *test_id = "kevent(EVFILT_USER, EV_ONESHOT)"; struct kevent kev; test_begin(test_id); test_no_kevents(); kevent_add(kqfd, &kev, 2, EVFILT_USER, EV_ADD | EV_ONESHOT, 0, 0, NULL); puts(" -- event 1"); kevent_add(kqfd, &kev, 2, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL); kev.flags = EV_ONESHOT; kev.fflags &= ~NOTE_FFCTRLMASK; kev.fflags &= ~NOTE_TRIGGER; kevent_cmp(&kev, kevent_get(kqfd)); test_no_kevents(); success(); } void test_evfilt_user() { kqfd = kqueue(); add_and_delete(); event_wait(); disable_and_enable(); oneshot(); /* TODO: try different fflags operations */ close(kqfd); } Index: stable/11/tests/sys/kqueue/libkqueue/vnode.c =================================================================== --- stable/11/tests/sys/kqueue/libkqueue/vnode.c (revision 359753) +++ stable/11/tests/sys/kqueue/libkqueue/vnode.c (revision 359754) @@ -1,266 +1,265 @@ /* * Copyright (c) 2009 Mark Heily * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * $FreeBSD$ */ #include "common.h" -int kqfd; int vnode_fd; void test_kevent_vnode_add(void) { const char *test_id = "kevent(EVFILT_VNODE, EV_ADD)"; const char *testfile = "./kqueue-test.tmp"; struct kevent kev; test_begin(test_id); system("touch ./kqueue-test.tmp"); vnode_fd = open(testfile, O_RDONLY); if (vnode_fd < 0) err(1, "open of %s", testfile); else printf("vnode_fd = %d\n", vnode_fd); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD, NOTE_WRITE | NOTE_ATTRIB | NOTE_RENAME | NOTE_DELETE, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } void test_kevent_vnode_note_delete(void) { const char *test_id = "kevent(EVFILT_VNODE, NOTE_DELETE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_DELETE, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (unlink("./kqueue-test.tmp") < 0) err(1, "unlink"); kevent_cmp(&kev, kevent_get(kqfd)); success(); } void test_kevent_vnode_note_write(void) { const char *test_id = "kevent(EVFILT_VNODE, NOTE_WRITE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_WRITE, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (system("echo hello >> ./kqueue-test.tmp") < 0) err(1, "system"); /* BSD kqueue adds NOTE_EXTEND even though it was not requested */ /* BSD kqueue removes EV_ENABLE */ kev.flags &= ~EV_ENABLE; // XXX-FIXME compatibility issue kev.fflags |= NOTE_EXTEND; // XXX-FIXME compatibility issue kevent_cmp(&kev, kevent_get(kqfd)); success(); } void test_kevent_vnode_note_attrib(void) { const char *test_id = "kevent(EVFILT_VNODE, NOTE_ATTRIB)"; struct kevent kev; int nfds; test_begin(test_id); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (system("touch ./kqueue-test.tmp") < 0) err(1, "system"); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) err(1, "%s", test_id); if (kev.ident != vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_ATTRIB) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); success(); } void test_kevent_vnode_note_rename(void) { const char *test_id = "kevent(EVFILT_VNODE, NOTE_RENAME)"; struct kevent kev; int nfds; test_begin(test_id); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_RENAME, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (system("mv ./kqueue-test.tmp ./kqueue-test2.tmp") < 0) err(1, "system"); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) err(1, "%s", test_id); if (kev.ident != vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_RENAME) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); if (system("mv ./kqueue-test2.tmp ./kqueue-test.tmp") < 0) err(1, "system"); success(); } void test_kevent_vnode_del(void) { const char *test_id = "kevent(EVFILT_VNODE, EV_DELETE)"; struct kevent kev; test_begin(test_id); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_DELETE, 0, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); success(); } void test_kevent_vnode_disable_and_enable(void) { const char *test_id = "kevent(EVFILT_VNODE, EV_DISABLE and EV_ENABLE)"; struct kevent kev; int nfds; test_begin(test_id); test_no_kevents(); /* Add the watch and immediately disable it */ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, NOTE_ATTRIB, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); kev.flags = EV_DISABLE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); /* Confirm that the watch is disabled */ if (system("touch ./kqueue-test.tmp") < 0) err(1, "system"); test_no_kevents(); /* Re-enable and check again */ kev.flags = EV_ENABLE; if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (system("touch ./kqueue-test.tmp") < 0) err(1, "system"); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) err(1, "%s", test_id); if (kev.ident != vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_ATTRIB) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); success(); } #if HAVE_EV_DISPATCH void test_kevent_vnode_dispatch(void) { const char *test_id = "kevent(EVFILT_VNODE, EV_DISPATCH)"; struct kevent kev; int nfds; test_begin(test_id); test_no_kevents(); EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD | EV_DISPATCH, NOTE_ATTRIB, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "%s", test_id); if (system("touch ./kqueue-test.tmp") < 0) err(1, "system"); nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL); if (nfds < 1) err(1, "%s", test_id); if (kev.ident != vnode_fd || kev.filter != EVFILT_VNODE || kev.fflags != NOTE_ATTRIB) err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)", test_id, (unsigned int)kev.ident, kev.filter, kev.flags); /* Confirm that the watch is disabled automatically */ puts("-- checking that watch is disabled"); if (system("touch ./kqueue-test.tmp") < 0) err(1, "system"); test_no_kevents(); /* Delete the watch */ EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_DELETE, NOTE_ATTRIB, 0, NULL); if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) err(1, "remove watch failed: %s", test_id); success(); } #endif /* HAVE_EV_DISPATCH */ void test_evfilt_vnode() { kqfd = kqueue(); test_kevent_vnode_add(); test_kevent_vnode_del(); test_kevent_vnode_disable_and_enable(); #if HAVE_EV_DISPATCH test_kevent_vnode_dispatch(); #endif test_kevent_vnode_note_write(); test_kevent_vnode_note_attrib(); test_kevent_vnode_note_rename(); test_kevent_vnode_note_delete(); close(kqfd); } Index: stable/11/usr.bin/locate/locate/locate.c =================================================================== --- stable/11/usr.bin/locate/locate/locate.c (revision 359753) +++ stable/11/usr.bin/locate/locate/locate.c (revision 359754) @@ -1,372 +1,373 @@ /* * Copyright (c) 1995 Wolfram Schneider . Berlin. * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * James A. Woods. * * 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1995-1996 Wolfram Schneider, Berlin.\n\ @(#) Copyright (c) 1989, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)locate.c 8.1 (Berkeley) 6/6/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ /* * Ref: Usenix ;login:, Vol 8, No 1, February/March, 1983, p. 8. * * Locate scans a file list for the full pathname of a file given only part * of the name. The list has been processed with with "front-compression" * and bigram coding. Front compression reduces space by a factor of 4-5, * bigram coding by a further 20-25%. * * The codes are: * * 0-28 likeliest differential counts + offset to make nonnegative * 30 switch code for out-of-range count to follow in next word * 31 an 8 bit char followed * 128-255 bigram codes (128 most common, as determined by 'updatedb') * 32-127 single character (printable) ascii residue (ie, literal) * * A novel two-tiered string search technique is employed: * * First, a metacharacter-free subpattern and partial pathname is matched * BACKWARDS to avoid full expansion of the pathname list. The time savings * is 40-50% over forward matching, which cannot efficiently handle * overlapped search patterns and compressed path residue. * * Then, the actual shell glob-style regular expression (if in this form) is * matched against the candidate pathnames using the slower routines provided * in the standard 'find'. */ #include #include #include #include #include #include #include #include #include #ifdef MMAP # include # include # include # include #endif #include "locate.h" #include "pathnames.h" #ifdef DEBUG # include # include # include #endif int f_mmap; /* use mmap */ int f_icase; /* ignore case */ int f_stdin; /* read database from stdin */ int f_statistic; /* print statistic */ int f_silent; /* suppress output, show only count of matches */ int f_limit; /* limit number of output lines, 0 == infinite */ u_int counter; /* counter for matches [-c] */ char separator='\n'; /* line separator */ +u_char myctype[UCHAR_MAX + 1]; void usage(void); void statistic(FILE *, char *); void fastfind(FILE *, char *, char *); void fastfind_icase(FILE *, char *, char *); void fastfind_mmap(char *, caddr_t, int, char *); void fastfind_mmap_icase(char *, caddr_t, int, char *); void search_mmap(char *, char **); void search_fopen(char *, char **); unsigned long cputime(void); extern char **colon(char **, char*, char*); extern void print_matches(u_int); extern int getwm(caddr_t); extern int getwf(FILE *); extern u_char *tolower_word(u_char *); extern int check_bigram_char(int); extern char *patprep(char *); int main(int argc, char **argv) { register int ch; char **dbv = NULL; char *path_fcodes; /* locate database */ #ifdef MMAP f_mmap = 1; /* mmap is default */ #endif (void) setlocale(LC_ALL, ""); while ((ch = getopt(argc, argv, "0Scd:il:ms")) != -1) switch(ch) { case '0': /* 'find -print0' style */ separator = '\0'; break; case 'S': /* statistic lines */ f_statistic = 1; break; case 'l': /* limit number of output lines, 0 == infinite */ f_limit = atoi(optarg); break; case 'd': /* database */ dbv = colon(dbv, optarg, _PATH_FCODES); break; case 'i': /* ignore case */ f_icase = 1; break; case 'm': /* mmap */ #ifdef MMAP f_mmap = 1; #else warnx("mmap(2) not implemented"); #endif break; case 's': /* stdio lib */ f_mmap = 0; break; case 'c': /* suppress output, show only count of matches */ f_silent = 1; break; default: usage(); } argv += optind; argc -= optind; /* to few arguments */ if (argc < 1 && !(f_statistic)) usage(); /* no (valid) database as argument */ if (dbv == NULL || *dbv == NULL) { /* try to read database from environment */ if ((path_fcodes = getenv("LOCATE_PATH")) == NULL || *path_fcodes == '\0') /* use default database */ dbv = colon(dbv, _PATH_FCODES, _PATH_FCODES); else /* $LOCATE_PATH */ dbv = colon(dbv, path_fcodes, _PATH_FCODES); } if (f_icase && UCHAR_MAX < 4096) /* init tolower lookup table */ for (ch = 0; ch < UCHAR_MAX + 1; ch++) myctype[ch] = tolower(ch); /* foreach database ... */ while((path_fcodes = *dbv) != NULL) { dbv++; if (!strcmp(path_fcodes, "-")) f_stdin = 1; else f_stdin = 0; #ifndef MMAP f_mmap = 0; /* be paranoid */ #endif if (!f_mmap || f_stdin || f_statistic) search_fopen(path_fcodes, argv); else search_mmap(path_fcodes, argv); } if (f_silent) print_matches(counter); exit(0); } /* * Arguments: * db database * s search strings */ void search_fopen(char *db, char **s) { FILE *fp; #ifdef DEBUG long t0; #endif /* can only read stdin once */ if (f_stdin) { fp = stdin; if (*(s+1) != NULL) { warnx("read database from stdin, use only `%s' as pattern", *s); *(s+1) = NULL; } } else if ((fp = fopen(db, "r")) == NULL) err(1, "`%s'", db); /* count only chars or lines */ if (f_statistic) { statistic(fp, db); (void)fclose(fp); return; } /* foreach search string ... */ while(*s != NULL) { #ifdef DEBUG t0 = cputime(); #endif if (!f_stdin && fseek(fp, (long)0, SEEK_SET) == -1) err(1, "fseek to begin of ``%s''\n", db); if (f_icase) fastfind_icase(fp, *s, db); else fastfind(fp, *s, db); #ifdef DEBUG warnx("fastfind %ld ms", cputime () - t0); #endif s++; } (void)fclose(fp); } #ifdef MMAP /* * Arguments: * db database * s search strings */ void search_mmap(char *db, char **s) { struct stat sb; int fd; caddr_t p; off_t len; #ifdef DEBUG long t0; #endif if ((fd = open(db, O_RDONLY)) == -1 || fstat(fd, &sb) == -1) err(1, "`%s'", db); len = sb.st_size; if (len < (2*NBG)) errx(1, "database too small: %s\nRun /usr/libexec/locate.updatedb", db); if ((p = mmap((caddr_t)0, (size_t)len, PROT_READ, MAP_SHARED, fd, (off_t)0)) == MAP_FAILED) err(1, "mmap ``%s''", db); /* foreach search string ... */ while (*s != NULL) { #ifdef DEBUG t0 = cputime(); #endif if (f_icase) fastfind_mmap_icase(*s, p, (int)len, db); else fastfind_mmap(*s, p, (int)len, db); #ifdef DEBUG warnx("fastfind %ld ms", cputime () - t0); #endif s++; } if (munmap(p, (size_t)len) == -1) warn("munmap %s\n", db); (void)close(fd); } #endif /* MMAP */ #ifdef DEBUG unsigned long cputime () { struct rusage rus; getrusage(RUSAGE_SELF, &rus); return(rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000); } #endif /* DEBUG */ void usage () { (void)fprintf(stderr, "usage: locate [-0Scims] [-l limit] [-d database] pattern ...\n\n"); (void)fprintf(stderr, "default database: `%s' or $LOCATE_PATH\n", _PATH_FCODES); exit(1); } /* load fastfind functions */ /* statistic */ /* fastfind_mmap, fastfind_mmap_icase */ #ifdef MMAP #undef FF_MMAP #undef FF_ICASE #define FF_MMAP #include "fastfind.c" #define FF_ICASE #include "fastfind.c" #endif /* MMAP */ /* fopen */ /* fastfind, fastfind_icase */ #undef FF_MMAP #undef FF_ICASE #include "fastfind.c" #define FF_ICASE #include "fastfind.c" Index: stable/11/usr.bin/locate/locate/locate.h =================================================================== --- stable/11/usr.bin/locate/locate/locate.h (revision 359753) +++ stable/11/usr.bin/locate/locate/locate.h (revision 359754) @@ -1,72 +1,72 @@ /* * Copyright (c) 1995 Wolfram Schneider . Berlin. * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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. * * @(#)locate.h 8.1 (Berkeley) 6/6/93 * $FreeBSD$ */ /* Symbolic constants shared by locate.c and code.c */ #define NBG 128 /* number of bigrams considered */ #define OFFSET 14 /* abs value of max likely diff */ #define PARITY 0200 /* parity bit */ #define SWITCH 30 /* switch code */ #define UMLAUT 31 /* an 8 bit char followed */ /* 0-28 likeliest differential counts + offset to make nonnegative */ #define LDC_MIN 0 #define LDC_MAX 28 /* 128-255 bigram codes (128 most common, as determined by 'updatedb') */ #define BIGRAM_MIN (UCHAR_MAX - SCHAR_MAX) #define BIGRAM_MAX UCHAR_MAX /* 32-127 single character (printable) ascii residue (ie, literal) */ #define ASCII_MIN 32 #define ASCII_MAX SCHAR_MAX /* #define TO7BIT(x) (x = ( ((u_char)x) & SCHAR_MAX )) */ #define TO7BIT(x) (x = x & SCHAR_MAX ) #if UCHAR_MAX >= 4096 define TOLOWER(ch) tolower(ch) #else -u_char myctype[UCHAR_MAX + 1]; +extern u_char myctype[UCHAR_MAX + 1]; #define TOLOWER(ch) (myctype[ch]) #endif #define INTSIZE (sizeof(int)) #define LOCATE_REG "*?[]\\" /* fnmatch(3) meta characters */ Index: stable/11/usr.bin/systat/swap.c =================================================================== --- stable/11/usr.bin/systat/swap.c (revision 359753) +++ stable/11/usr.bin/systat/swap.c (revision 359754) @@ -1,218 +1,216 @@ /*- * Copyright (c) 1980, 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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. */ #include __FBSDID("$FreeBSD$"); #ifdef lint static const char sccsid[] = "@(#)swap.c 8.3 (Berkeley) 4/29/95"; #endif /* * swapinfo - based on a program of the same name by Kevin Lahey */ #include #include #include #include #include #include #include #include #include #include #include "systat.h" #include "extern.h" -kvm_t *kd; - static char *header; static long blocksize; static int dlen, odlen; static int hlen; static int ulen, oulen; static int pagesize; WINDOW * openswap(void) { return (subwin(stdscr, LINES-3-1, 0, MAINWIN_ROW, 0)); } void closeswap(WINDOW *w) { if (w == NULL) return; wclear(w); wrefresh(w); delwin(w); } /* * The meat of all the swap stuff is stolen from pstat(8)'s * swapmode(), which is based on a program called swapinfo written by * Kevin Lahey . */ #define NSWAP 16 static struct kvm_swap kvmsw[NSWAP]; static int kvnsw, okvnsw; static void calclens(void); #define CONVERT(v) ((int)((int64_t)(v) * pagesize / blocksize)) static void calclens(void) { int i, n; int len; dlen = sizeof("Disk"); for (i = 0; i < kvnsw; ++i) { len = strlen(kvmsw[i].ksw_devname); if (dlen < len) dlen = len; } ulen = sizeof("Used"); for (n = CONVERT(kvmsw[kvnsw].ksw_used), len = 2; n /= 10; ++len); if (ulen < len) ulen = len; } int initswap(void) { static int once = 0; if (once) return (1); header = getbsize(&hlen, &blocksize); pagesize = getpagesize(); if ((kvnsw = kvm_getswapinfo(kd, kvmsw, NSWAP, 0)) < 0) { error("systat: kvm_getswapinfo failed"); return (0); } okvnsw = kvnsw; calclens(); odlen = dlen; oulen = ulen; once = 1; return (1); } void fetchswap(void) { okvnsw = kvnsw; if ((kvnsw = kvm_getswapinfo(kd, kvmsw, NSWAP, 0)) < 0) { error("systat: kvm_getswapinfo failed"); return; } odlen = dlen; oulen = ulen; calclens(); } void labelswap(void) { const char *name; int i; fetchswap(); werase(wnd); mvwprintw(wnd, 0, 0, "%*s%*s%*s %s", -dlen, "Disk", hlen, header, ulen, "Used", "/0% /10 /20 /30 /40 /50 /60 /70 /80 /90 /100"); for (i = 0; i <= kvnsw; ++i) { if (i == kvnsw) { if (kvnsw == 1) break; name = "Total"; } else name = kvmsw[i].ksw_devname; mvwprintw(wnd, i + 1, 0, "%*s", -dlen, name); } } void showswap(void) { int count; int i; if (kvnsw != okvnsw || dlen != odlen || ulen != oulen) labelswap(); for (i = 0; i <= kvnsw; ++i) { if (i == kvnsw) { if (kvnsw == 1) break; } if (kvmsw[i].ksw_total == 0) { mvwprintw( wnd, i + 1, dlen + hlen + ulen + 1, "(swap not configured)" ); continue; } wmove(wnd, i + 1, dlen); wprintw(wnd, "%*d", hlen, CONVERT(kvmsw[i].ksw_total)); wprintw(wnd, "%*d", ulen, CONVERT(kvmsw[i].ksw_used)); count = 50.0 * kvmsw[i].ksw_used / kvmsw[i].ksw_total + 1; waddch(wnd, ' '); while (count--) waddch(wnd, 'X'); wclrtoeol(wnd); } } Index: stable/11/usr.sbin/config/config.h =================================================================== --- stable/11/usr.sbin/config/config.h (revision 359753) +++ stable/11/usr.sbin/config/config.h (revision 359754) @@ -1,219 +1,219 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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. * * @(#)config.h 8.1 (Berkeley) 6/6/93 * $FreeBSD$ */ /* * Config. */ #include #include #include #include #include struct cfgfile { STAILQ_ENTRY(cfgfile) cfg_next; char *cfg_path; }; -STAILQ_HEAD(, cfgfile) cfgfiles; +extern STAILQ_HEAD(cfgfile_head, cfgfile) cfgfiles; struct file_list { STAILQ_ENTRY(file_list) f_next; char *f_fn; /* the name */ int f_type; /* type */ u_char f_flags; /* see below */ char *f_compilewith; /* special make rule if present */ char *f_depends; /* additional dependencies */ char *f_clean; /* File list to add to clean rule */ char *f_warn; /* warning message */ const char *f_objprefix; /* prefix string for object name */ const char *f_srcprefix; /* source prefix such as $S/ */ }; struct files_name { char *f_name; STAILQ_ENTRY(files_name) f_next; }; /* * Types. */ #define NORMAL 1 #define PROFILING 3 #define NODEPEND 4 #define LOCAL 5 #define DEVDONE 0x80000000 #define TYPEMASK 0x7fffffff /* * Attributes (flags). */ #define NO_IMPLCT_RULE 1 #define NO_OBJ 2 #define BEFORE_DEPEND 4 #define NOWERROR 16 struct device { int d_done; /* processed */ char *d_name; /* name of device (e.g. rk11) */ #define UNKNOWN -2 /* -2 means not set yet */ STAILQ_ENTRY(device) d_next; /* Next one in list */ }; struct config { char *s_sysname; }; /* * Config has a global notion of which machine type is * being used. It uses the name of the machine in choosing * files and directories. Thus if the name of the machine is ``i386'', * it will build from ``Makefile.i386'' and use ``../i386/inline'' * in the makerules, etc. machinearch is the global notion of the * MACHINE_ARCH for this MACHINE. */ -char *machinename; -char *machinearch; +extern char *machinename; +extern char *machinearch; /* * For each machine, a set of CPU's may be specified as supported. * These and the options (below) are put in the C flags in the makefile. */ struct cputype { char *cpu_name; SLIST_ENTRY(cputype) cpu_next; }; -SLIST_HEAD(, cputype) cputype; +extern SLIST_HEAD(cputype_head, cputype) cputype; /* * A set of options may also be specified which are like CPU types, * but which may also specify values for the options. * A separate set of options may be defined for make-style options. */ struct opt { char *op_name; char *op_value; int op_ownfile; /* true = own file, false = makefile */ SLIST_ENTRY(opt) op_next; SLIST_ENTRY(opt) op_append; }; -SLIST_HEAD(opt_head, opt) opt, mkopt, rmopts; +extern SLIST_HEAD(opt_head, opt) opt, mkopt, rmopts; struct opt_list { char *o_name; char *o_file; int o_flags; #define OL_ALIAS 1 SLIST_ENTRY(opt_list) o_next; }; -SLIST_HEAD(, opt_list) otab; +extern SLIST_HEAD(opt_list_head, opt_list) otab; struct envvar { char *env_str; bool env_is_file; STAILQ_ENTRY(envvar) envvar_next; }; -STAILQ_HEAD(envvar_head, envvar) envvars; +extern STAILQ_HEAD(envvar_head, envvar) envvars; struct hint { char *hint_name; STAILQ_ENTRY(hint) hint_next; }; -STAILQ_HEAD(hint_head, hint) hints; +extern STAILQ_HEAD(hint_head, hint) hints; struct includepath { char *path; SLIST_ENTRY(includepath) path_next; }; -SLIST_HEAD(, includepath) includepath; +extern SLIST_HEAD(includepath_head, includepath) includepath; /* * Tag present in the kernconf.tmpl template file. It's mandatory for those * two strings to be the same. Otherwise you'll get into trouble. */ #define KERNCONFTAG "%%KERNCONFFILE%%" /* * Faked option to note, that the configuration file has been taken from the * kernel file and inclusion of DEFAULTS etc.. isn't nessesery, because we * already have a list of all required devices. */ #define OPT_AUTOGEN "CONFIG_AUTOGENERATED" extern char *ident; extern char kernconfstr[]; extern int do_trace; extern int incignore; char *get_word(FILE *); char *get_quoted_word(FILE *); char *path(const char *); char *raisestr(char *); void remember(const char *); void moveifchanged(const char *, const char *); int yylex(void); void options(void); void makefile(void); void makeenv(void); void makehints(void); void headers(void); void cfgfile_add(const char *); void cfgfile_removeall(void); FILE *open_makefile_template(void); extern STAILQ_HEAD(device_head, device) dtab; extern char errbuf[80]; extern int yyline; extern const char *yyfile; extern STAILQ_HEAD(file_list_head, file_list) ftab; extern STAILQ_HEAD(files_name_head, files_name) fntab; extern int profiling; extern int debugging; extern int found_defaults; extern int maxusers; extern int versreq; extern char *PREFIX; /* Config file name - for error messages */ extern char srcdir[]; /* root of the kernel source tree */ #define eq(a,b) (!strcmp(a,b)) #define ns(s) strdup(s) Index: stable/11/usr.sbin/config/main.c =================================================================== --- stable/11/usr.sbin/config/main.c (revision 359753) +++ stable/11/usr.sbin/config/main.c (revision 359754) @@ -1,780 +1,791 @@ /* * Copyright (c) 1980, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1980, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "y.tab.h" #include "config.h" #include "configvers.h" #ifndef TRUE #define TRUE (1) #endif #ifndef FALSE #define FALSE (0) #endif #define CDIR "../compile/" +char *machinename; +char *machinearch; + +struct cfgfile_head cfgfiles; +struct cputype_head cputype; +struct opt_head opt, mkopt, rmopts; +struct opt_list_head otab; +struct envvar_head envvars; +struct hint_head hints; +struct includepath_head includepath; + char * PREFIX; char destdir[MAXPATHLEN]; char srcdir[MAXPATHLEN]; int debugging; int profiling; int found_defaults; int incignore; /* * Preserve old behaviour in INCLUDE_CONFIG_FILE handling (files are included * literally). */ int filebased = 0; int versreq; static void configfile(void); static void get_srcdir(void); static void usage(void); static void cleanheaders(char *); static void kernconfdump(const char *); static void badversion(void); static void checkversion(void); extern int yyparse(void); struct hdr_list { char *h_name; struct hdr_list *h_next; } *htab; /* * Config builds a set of files for building a UNIX * system given a description of the desired system. */ int main(int argc, char **argv) { struct stat buf; int ch, len; char *p; char *kernfile; struct includepath* ipath; int printmachine; printmachine = 0; kernfile = NULL; SLIST_INIT(&includepath); while ((ch = getopt(argc, argv, "CI:d:gmps:Vx:")) != -1) switch (ch) { case 'C': filebased = 1; break; case 'I': ipath = (struct includepath *) \ calloc(1, sizeof (struct includepath)); if (ipath == NULL) err(EXIT_FAILURE, "calloc"); ipath->path = optarg; SLIST_INSERT_HEAD(&includepath, ipath, path_next); break; case 'm': printmachine = 1; break; case 'd': if (*destdir == '\0') strlcpy(destdir, optarg, sizeof(destdir)); else errx(EXIT_FAILURE, "directory already set"); break; case 'g': debugging++; break; case 'p': profiling++; break; case 's': if (*srcdir == '\0') strlcpy(srcdir, optarg, sizeof(srcdir)); else errx(EXIT_FAILURE, "src directory already set"); break; case 'V': printf("%d\n", CONFIGVERS); exit(0); case 'x': kernfile = optarg; break; case '?': default: usage(); } argc -= optind; argv += optind; if (kernfile != NULL) { kernconfdump(kernfile); exit(EXIT_SUCCESS); } if (argc != 1) usage(); PREFIX = *argv; if (stat(PREFIX, &buf) != 0 || !S_ISREG(buf.st_mode)) err(2, "%s", PREFIX); if (freopen("DEFAULTS", "r", stdin) != NULL) { found_defaults = 1; yyfile = "DEFAULTS"; } else { if (freopen(PREFIX, "r", stdin) == NULL) err(2, "%s", PREFIX); yyfile = PREFIX; } if (*destdir != '\0') { len = strlen(destdir); while (len > 1 && destdir[len - 1] == '/') destdir[--len] = '\0'; if (*srcdir == '\0') get_srcdir(); } else { strlcpy(destdir, CDIR, sizeof(destdir)); strlcat(destdir, PREFIX, sizeof(destdir)); } SLIST_INIT(&cputype); SLIST_INIT(&mkopt); SLIST_INIT(&opt); SLIST_INIT(&rmopts); STAILQ_INIT(&cfgfiles); STAILQ_INIT(&dtab); STAILQ_INIT(&fntab); STAILQ_INIT(&ftab); STAILQ_INIT(&hints); STAILQ_INIT(&envvars); if (yyparse()) exit(3); /* * Ensure that required elements (machine, cpu, ident) are present. */ if (machinename == NULL) { printf("Specify machine type, e.g. ``machine i386''\n"); exit(1); } if (ident == NULL) { printf("no ident line specified\n"); exit(1); } if (SLIST_EMPTY(&cputype)) { printf("cpu type must be specified\n"); exit(1); } checkversion(); if (printmachine) { printf("%s\t%s\n",machinename,machinearch); exit(0); } /* Make compile directory */ p = path((char *)NULL); if (stat(p, &buf)) { if (mkdir(p, 0777)) err(2, "%s", p); } else if (!S_ISDIR(buf.st_mode)) errx(EXIT_FAILURE, "%s isn't a directory", p); configfile(); /* put config file into kernel*/ options(); /* make options .h files */ makefile(); /* build Makefile */ makeenv(); /* build env.c */ makehints(); /* build hints.c */ headers(); /* make a lot of .h files */ cleanheaders(p); printf("Kernel build directory is %s\n", p); printf("Don't forget to do ``make cleandepend && make depend''\n"); exit(0); } /* * get_srcdir * determine the root of the kernel source tree * and save that in srcdir. */ static void get_srcdir(void) { struct stat lg, phy; char *p, *pwd; int i; if (realpath("../..", srcdir) == NULL) err(EXIT_FAILURE, "Unable to find root of source tree"); if ((pwd = getenv("PWD")) != NULL && *pwd == '/' && (pwd = strdup(pwd)) != NULL) { /* Remove the last two path components. */ for (i = 0; i < 2; i++) { if ((p = strrchr(pwd, '/')) == NULL) { free(pwd); return; } *p = '\0'; } if (stat(pwd, &lg) != -1 && stat(srcdir, &phy) != -1 && lg.st_dev == phy.st_dev && lg.st_ino == phy.st_ino) strlcpy(srcdir, pwd, MAXPATHLEN); free(pwd); } } static void usage(void) { fprintf(stderr, "usage: config [-CgmpV] [-d destdir] [-s srcdir] sysname\n"); fprintf(stderr, " config -x kernel\n"); exit(EX_USAGE); } /* * get_word * returns EOF on end of file * NULL on end of line * pointer to the word otherwise */ char * get_word(FILE *fp) { static char line[80]; int ch; char *cp; int escaped_nl = 0; begin: while ((ch = getc(fp)) != EOF) if (ch != ' ' && ch != '\t') break; if (ch == EOF) return ((char *)EOF); if (ch == '\\'){ escaped_nl = 1; goto begin; } if (ch == '\n') { if (escaped_nl){ escaped_nl = 0; goto begin; } else return (NULL); } cp = line; *cp++ = ch; /* Negation operator is a word by itself. */ if (ch == '!') { *cp = 0; return (line); } while ((ch = getc(fp)) != EOF) { if (isspace(ch)) break; *cp++ = ch; } *cp = 0; if (ch == EOF) return ((char *)EOF); (void) ungetc(ch, fp); return (line); } /* * get_quoted_word * like get_word but will accept something in double or single quotes * (to allow embedded spaces). */ char * get_quoted_word(FILE *fp) { static char line[256]; int ch; char *cp; int escaped_nl = 0; begin: while ((ch = getc(fp)) != EOF) if (ch != ' ' && ch != '\t') break; if (ch == EOF) return ((char *)EOF); if (ch == '\\'){ escaped_nl = 1; goto begin; } if (ch == '\n') { if (escaped_nl){ escaped_nl = 0; goto begin; } else return (NULL); } cp = line; if (ch == '"' || ch == '\'') { int quote = ch; escaped_nl = 0; while ((ch = getc(fp)) != EOF) { if (ch == quote && !escaped_nl) break; if (ch == '\n' && !escaped_nl) { *cp = 0; printf("config: missing quote reading `%s'\n", line); exit(2); } if (ch == '\\' && !escaped_nl) { escaped_nl = 1; continue; } if (ch != quote && escaped_nl) *cp++ = '\\'; *cp++ = ch; escaped_nl = 0; } } else { *cp++ = ch; while ((ch = getc(fp)) != EOF) { if (isspace(ch)) break; *cp++ = ch; } if (ch != EOF) (void) ungetc(ch, fp); } *cp = 0; if (ch == EOF) return ((char *)EOF); return (line); } /* * prepend the path to a filename */ char * path(const char *file) { char *cp = NULL; if (file) asprintf(&cp, "%s/%s", destdir, file); else cp = strdup(destdir); return (cp); } /* * Generate configuration file based on actual settings. With this mode, user * will be able to obtain and build conifguration file with one command. */ static void configfile_dynamic(struct sbuf *sb) { struct cputype *cput; struct device *d; struct opt *ol; char *lend; unsigned int i; asprintf(&lend, "\\n\\\n"); assert(lend != NULL); sbuf_printf(sb, "options\t%s%s", OPT_AUTOGEN, lend); sbuf_printf(sb, "ident\t%s%s", ident, lend); sbuf_printf(sb, "machine\t%s%s", machinename, lend); SLIST_FOREACH(cput, &cputype, cpu_next) sbuf_printf(sb, "cpu\t%s%s", cput->cpu_name, lend); SLIST_FOREACH(ol, &mkopt, op_next) sbuf_printf(sb, "makeoptions\t%s=%s%s", ol->op_name, ol->op_value, lend); SLIST_FOREACH(ol, &opt, op_next) { if (strncmp(ol->op_name, "DEV_", 4) == 0) continue; sbuf_printf(sb, "options\t%s", ol->op_name); if (ol->op_value != NULL) { sbuf_putc(sb, '='); for (i = 0; i < strlen(ol->op_value); i++) { if (ol->op_value[i] == '"') sbuf_printf(sb, "\\%c", ol->op_value[i]); else sbuf_printf(sb, "%c", ol->op_value[i]); } sbuf_printf(sb, "%s", lend); } else { sbuf_printf(sb, "%s", lend); } } /* * Mark this file as containing everything we need. */ STAILQ_FOREACH(d, &dtab, d_next) sbuf_printf(sb, "device\t%s%s", d->d_name, lend); free(lend); } /* * Generate file from the configuration files. */ static void configfile_filebased(struct sbuf *sb) { FILE *cff; struct cfgfile *cf; int i; /* * Try to read all configuration files. Since those will be present as * C string in the macro, we have to slash their ends then the line * wraps. */ STAILQ_FOREACH(cf, &cfgfiles, cfg_next) { cff = fopen(cf->cfg_path, "r"); if (cff == NULL) { warn("Couldn't open file %s", cf->cfg_path); continue; } while ((i = getc(cff)) != EOF) { if (i == '\n') sbuf_printf(sb, "\\n\\\n"); else if (i == '"' || i == '\'') sbuf_printf(sb, "\\%c", i); else sbuf_putc(sb, i); } fclose(cff); } } static void configfile(void) { FILE *fo; struct sbuf *sb; char *p; /* Add main configuration file to the list of files to be included */ cfgfile_add(PREFIX); p = path("config.c.new"); fo = fopen(p, "w"); if (!fo) err(2, "%s", p); sb = sbuf_new(NULL, NULL, 2048, SBUF_AUTOEXTEND); assert(sb != NULL); sbuf_clear(sb); if (filebased) { /* Is needed, can be used for backward compatibility. */ configfile_filebased(sb); } else { configfile_dynamic(sb); } sbuf_finish(sb); /* * We print first part of the template, replace our tag with * configuration files content and later continue writing our * template. */ p = strstr(kernconfstr, KERNCONFTAG); if (p == NULL) errx(EXIT_FAILURE, "Something went terribly wrong!"); *p = '\0'; fprintf(fo, "%s", kernconfstr); fprintf(fo, "%s", sbuf_data(sb)); p += strlen(KERNCONFTAG); fprintf(fo, "%s", p); sbuf_delete(sb); fclose(fo); moveifchanged(path("config.c.new"), path("config.c")); cfgfile_removeall(); } /* * moveifchanged -- * compare two files; rename if changed. */ void moveifchanged(const char *from_name, const char *to_name) { char *p, *q; int changed; size_t tsize; struct stat from_sb, to_sb; int from_fd, to_fd; changed = 0; if ((from_fd = open(from_name, O_RDONLY)) < 0) err(EX_OSERR, "moveifchanged open(%s)", from_name); if ((to_fd = open(to_name, O_RDONLY)) < 0) changed++; if (!changed && fstat(from_fd, &from_sb) < 0) err(EX_OSERR, "moveifchanged fstat(%s)", from_name); if (!changed && fstat(to_fd, &to_sb) < 0) err(EX_OSERR, "moveifchanged fstat(%s)", to_name); if (!changed && from_sb.st_size != to_sb.st_size) changed++; tsize = (size_t)from_sb.st_size; if (!changed) { p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0); if (p == MAP_FAILED) err(EX_OSERR, "mmap %s", from_name); q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0); if (q == MAP_FAILED) err(EX_OSERR, "mmap %s", to_name); changed = memcmp(p, q, tsize); munmap(p, tsize); munmap(q, tsize); } if (changed) { if (rename(from_name, to_name) < 0) err(EX_OSERR, "rename(%s, %s)", from_name, to_name); } else { if (unlink(from_name) < 0) err(EX_OSERR, "unlink(%s)", from_name); } } static void cleanheaders(char *p) { DIR *dirp; struct dirent *dp; struct file_list *fl; struct hdr_list *hl; size_t len; remember("y.tab.h"); remember("setdefs.h"); STAILQ_FOREACH(fl, &ftab, f_next) remember(fl->f_fn); /* * Scan the build directory and clean out stuff that looks like * it might have been a leftover NFOO header, etc. */ if ((dirp = opendir(p)) == NULL) err(EX_OSERR, "opendir %s", p); while ((dp = readdir(dirp)) != NULL) { len = strlen(dp->d_name); /* Skip non-headers */ if (len < 2 || dp->d_name[len - 2] != '.' || dp->d_name[len - 1] != 'h') continue; /* Skip special stuff, eg: bus_if.h, but check opt_*.h */ if (strchr(dp->d_name, '_') && strncmp(dp->d_name, "opt_", 4) != 0) continue; /* Check if it is a target file */ for (hl = htab; hl != NULL; hl = hl->h_next) { if (eq(dp->d_name, hl->h_name)) { break; } } if (hl) continue; printf("Removing stale header: %s\n", dp->d_name); if (unlink(path(dp->d_name)) == -1) warn("unlink %s", dp->d_name); } (void)closedir(dirp); } void remember(const char *file) { char *s; struct hdr_list *hl; if ((s = strrchr(file, '/')) != NULL) s = ns(s + 1); else s = ns(file); if (strchr(s, '_') && strncmp(s, "opt_", 4) != 0) { free(s); return; } for (hl = htab; hl != NULL; hl = hl->h_next) { if (eq(s, hl->h_name)) { free(s); return; } } hl = calloc(1, sizeof(*hl)); if (hl == NULL) err(EXIT_FAILURE, "calloc"); hl->h_name = s; hl->h_next = htab; htab = hl; } /* * This one is quick hack. Will be probably moved to elf(3) interface. * It takes kernel configuration file name, passes it as an argument to * elfdump -a, which output is parsed by some UNIX tools... */ static void kernconfdump(const char *file) { struct stat st; FILE *fp, *pp; int error, osz, r; unsigned int i, off, size, t1, t2, align; char *cmd, *o; r = open(file, O_RDONLY); if (r == -1) err(EXIT_FAILURE, "Couldn't open file '%s'", file); error = fstat(r, &st); if (error == -1) err(EXIT_FAILURE, "fstat() failed"); if (S_ISDIR(st.st_mode)) errx(EXIT_FAILURE, "'%s' is a directory", file); fp = fdopen(r, "r"); if (fp == NULL) err(EXIT_FAILURE, "fdopen() failed"); osz = 1024; o = calloc(1, osz); if (o == NULL) err(EXIT_FAILURE, "Couldn't allocate memory"); /* ELF note section header. */ asprintf(&cmd, "/usr/bin/elfdump -c %s | grep -A 8 kern_conf" "| tail -5 | cut -d ' ' -f 2 | paste - - - - -", file); if (cmd == NULL) errx(EXIT_FAILURE, "asprintf() failed"); pp = popen(cmd, "r"); if (pp == NULL) errx(EXIT_FAILURE, "popen() failed"); free(cmd); (void)fread(o, osz, 1, pp); pclose(pp); r = sscanf(o, "%d%d%d%d%d", &off, &size, &t1, &t2, &align); free(o); if (r != 5) errx(EXIT_FAILURE, "File %s doesn't contain configuration " "file. Either unsupported, or not compiled with " "INCLUDE_CONFIG_FILE", file); r = fseek(fp, off, SEEK_CUR); if (r != 0) err(EXIT_FAILURE, "fseek() failed"); for (i = 0; i < size; i++) { r = fgetc(fp); if (r == EOF) break; if (r == '\0') { assert(i == size - 1 && ("\\0 found in the middle of a file")); break; } fputc(r, stdout); } fclose(fp); } static void badversion(void) { fprintf(stderr, "ERROR: version of config(8) does not match kernel!\n"); fprintf(stderr, "config version = %d, ", CONFIGVERS); fprintf(stderr, "version required = %d\n\n", versreq); fprintf(stderr, "Make sure that /usr/src/usr.sbin/config is in sync\n"); fprintf(stderr, "with your /usr/src/sys and install a new config binary\n"); fprintf(stderr, "before trying this again.\n\n"); fprintf(stderr, "If running the new config fails check your config\n"); fprintf(stderr, "file against the GENERIC or LINT config files for\n"); fprintf(stderr, "changes in config syntax, or option/device naming\n"); fprintf(stderr, "conventions\n\n"); exit(1); } static void checkversion(void) { FILE *ifp; char line[BUFSIZ]; ifp = open_makefile_template(); while (fgets(line, BUFSIZ, ifp) != 0) { if (*line != '%') continue; if (strncmp(line, "%VERSREQ=", 9) != 0) continue; versreq = atoi(line + 9); if (MAJOR_VERS(versreq) == MAJOR_VERS(CONFIGVERS) && versreq <= CONFIGVERS) continue; badversion(); } fclose(ifp); } Index: stable/11/usr.sbin/rpc.yppasswdd/yppasswdd_main.c =================================================================== --- stable/11/usr.sbin/rpc.yppasswdd/yppasswdd_main.c (revision 359753) +++ stable/11/usr.sbin/rpc.yppasswdd/yppasswdd_main.c (revision 359754) @@ -1,352 +1,351 @@ /* * Copyright (c) 1995, 1996 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* getenv, exit */ #include /* strcmp */ #include #include #include #include #include /* for pmap_unset */ #include #include #include "yppasswd.h" #include "yppasswdd_extern.h" #include "yppasswd_private.h" #include "ypxfr_extern.h" #include "yp_extern.h" #ifndef SIG_PF #define SIG_PF void(*)(int) #endif #ifdef DEBUG #define RPC_SVC_FG #endif #define _RPCSVC_CLOSEDOWN 120 int _rpcpmstart = 0; /* Started by a port monitor ? */ static int _rpcfdtype; /* Whether Stream or Datagram ? */ /* States a server can be in wrt request */ #define _IDLE 0 #define _SERVED 1 #define _SERVING 2 +int debug; static char _localhost[] = "localhost"; static char _passwd_byname[] = "passwd.byname"; extern int _rpcsvcstate; /* Set when a request is serviced */ static char _progname[] = "rpc.yppasswdd"; char *progname = _progname; static char _yp_dir[] = _PATH_YP; char *yp_dir = _yp_dir; static char _passfile_default[] = _PATH_YP "master.passwd"; char *passfile_default = _passfile_default; char *passfile; char *yppasswd_domain = NULL; int no_chsh = 0; int no_chfn = 0; int allow_additions = 0; int multidomain = 0; int verbose = 0; int resvport = 1; int inplace = 0; char sockname[] = YP_SOCKNAME; static void terminate(int sig __unused) { rpcb_unset(YPPASSWDPROG, YPPASSWDVERS, NULL); rpcb_unset(MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, NULL); unlink(sockname); exit(0); } static void reload(int sig __unused) { load_securenets(); } static void closedown(int sig __unused) { if (_rpcsvcstate == _IDLE) { extern fd_set svc_fdset; static int size; int i, openfd; if (_rpcfdtype == SOCK_DGRAM) { unlink(sockname); exit(0); } if (size == 0) { size = getdtablesize(); } for (i = 0, openfd = 0; i < size && openfd < 2; i++) if (FD_ISSET(i, &svc_fdset)) openfd++; if (openfd <= 1) { unlink(sockname); exit(0); } } if (_rpcsvcstate == _SERVED) _rpcsvcstate = _IDLE; (void) signal(SIGALRM, (SIG_PF) closedown); (void) alarm(_RPCSVC_CLOSEDOWN/2); } static void usage(void) { fprintf(stderr, "%s\n%s\n", "usage: rpc.yppasswdd [-t master.passwd file] [-d domain] [-p path] [-s]", " [-f] [-m] [-i] [-a] [-v] [-u] [-h]"); exit(1); } int main(int argc, char *argv[]) { struct rlimit rlim; SVCXPRT *transp = NULL; struct sockaddr_in saddr; socklen_t asize = sizeof (saddr); struct netconfig *nconf; struct sigaction sa; void *localhandle; int ch; char *mastername; char myname[MAXHOSTNAMELEN + 2]; int maxrec = RPC_MAXDATASIZE; - - extern int debug; debug = 1; while ((ch = getopt(argc, argv, "t:d:p:sfamuivh")) != -1) { switch (ch) { case 't': passfile_default = optarg; break; case 'd': yppasswd_domain = optarg; break; case 's': no_chsh++; break; case 'f': no_chfn++; break; case 'p': yp_dir = optarg; break; case 'a': allow_additions++; break; case 'm': multidomain++; break; case 'i': inplace++; break; case 'v': verbose++; break; case 'u': resvport = 0; break; default: case 'h': usage(); break; } } if (yppasswd_domain == NULL) { if (yp_get_default_domain(&yppasswd_domain)) { yp_error("no domain specified and system domain \ name isn't set -- aborting"); usage(); } } load_securenets(); if (getrpcport(_localhost, YPPROG, YPVERS, IPPROTO_UDP) <= 0) { yp_error("no ypserv processes registered with local portmap"); yp_error("this host is not an NIS server -- aborting"); exit(1); } if ((mastername = ypxfr_get_master(yppasswd_domain, _passwd_byname, _localhost, 0)) == NULL) { yp_error("can't get name of NIS master server for domain %s", yppasswd_domain); exit(1); } if (gethostname((char *)&myname, sizeof(myname)) == -1) { yp_error("can't get local hostname: %s", strerror(errno)); exit(1); } if (strncasecmp(mastername, (char *)&myname, sizeof(myname))) { yp_error("master of %s is %s, but we are %s", "passwd.byname", mastername, myname); yp_error("this host is not the NIS master server for \ the %s domain -- aborting", yppasswd_domain); exit(1); } debug = 0; if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) { socklen_t ssize = sizeof (int); if (saddr.sin_family != AF_INET) exit(1); if (getsockopt(0, SOL_SOCKET, SO_TYPE, (char *)&_rpcfdtype, &ssize) == -1) exit(1); _rpcpmstart = 1; } if (!debug && _rpcpmstart == 0) { if (daemon(0,0)) { err(1,"cannot fork"); } } openlog("rpc.yppasswdd", LOG_PID, LOG_DAEMON); memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_NOCLDWAIT; sigaction(SIGCHLD, &sa, NULL); rpcb_unset(YPPASSWDPROG, YPPASSWDVERS, NULL); rpcb_unset(MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, NULL); rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); if (svc_create(yppasswdprog_1, YPPASSWDPROG, YPPASSWDVERS, "netpath") == 0) { yp_error("cannot create yppasswd service."); exit(1); } if (svc_create(master_yppasswdprog_1, MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, "netpath") == 0) { yp_error("cannot create master_yppasswd service."); exit(1); } nconf = NULL; localhandle = setnetconfig(); while ((nconf = getnetconfig(localhandle)) != NULL) { if (nconf->nc_protofmly != NULL && strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) break; } if (nconf == NULL) { yp_error("getnetconfigent unix: %s", nc_sperror()); exit(1); } unlink(sockname); transp = svcunix_create(RPC_ANYSOCK, 0, 0, sockname); if (transp == NULL) { yp_error("cannot create AF_LOCAL service."); exit(1); } if (!svc_reg(transp, MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, master_yppasswdprog_1, nconf)) { yp_error("unable to register (MASTER_YPPASSWDPROG, \ MASTER_YPPASSWDVERS, unix)."); exit(1); } endnetconfig(localhandle); /* Only root may connect() to the AF_UNIX link. */ if (chmod(sockname, 0)) err(1, "chmod of %s failed", sockname); if (transp == (SVCXPRT *)NULL) { yp_error("could not create a handle"); exit(1); } if (_rpcpmstart) { (void) signal(SIGALRM, (SIG_PF) closedown); (void) alarm(_RPCSVC_CLOSEDOWN/2); } /* Unlimited resource limits. */ rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; (void)setrlimit(RLIMIT_CPU, &rlim); (void)setrlimit(RLIMIT_FSIZE, &rlim); (void)setrlimit(RLIMIT_STACK, &rlim); (void)setrlimit(RLIMIT_DATA, &rlim); (void)setrlimit(RLIMIT_RSS, &rlim); /* Don't drop core (not really necessary, but GP's). */ rlim.rlim_cur = rlim.rlim_max = 0; (void)setrlimit(RLIMIT_CORE, &rlim); /* Turn off signals. */ (void)signal(SIGALRM, SIG_IGN); (void)signal(SIGHUP, (SIG_PF) reload); (void)signal(SIGINT, SIG_IGN); (void)signal(SIGPIPE, SIG_IGN); (void)signal(SIGQUIT, SIG_IGN); (void)signal(SIGTERM, (SIG_PF) terminate); svc_run(); yp_error("svc_run returned"); exit(1); /* NOTREACHED */ } Index: stable/11/usr.sbin/rpc.ypupdated/ypupdated_main.c =================================================================== --- stable/11/usr.sbin/rpc.ypupdated/ypupdated_main.c (revision 359753) +++ stable/11/usr.sbin/rpc.ypupdated/ypupdated_main.c (revision 359754) @@ -1,285 +1,287 @@ /* * Copyright (c) 1995, 1996 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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. */ #include __FBSDID("$FreeBSD$"); #include "ypupdate_prot.h" #include #include /* getenv, exit */ #include /* for pmap_unset */ #include #include /* strcmp */ #include #ifdef __cplusplus #include /* getdtablesize, open */ #endif /* __cplusplus */ #include #include #include #include #include #include #include #include #include "ypupdated_extern.h" #include "yp_extern.h" #ifndef SIG_PF #define SIG_PF void(*)(int) #endif #ifdef DEBUG #define RPC_SVC_FG #endif #define _RPCSVC_CLOSEDOWN 120 int _rpcpmstart; /* Started by a port monitor ? */ static int _rpcfdtype; /* Whether Stream or Datagram ? */ /* States a server can be in wrt request */ #define _IDLE 0 #define _SERVED 1 #define _SERVING 2 extern int _rpcsvcstate; /* Set when a request is serviced */ +int debug; + char *progname = "rpc.ypupdated"; char *yp_dir = "/var/yp/"; static void _msgout(char* msg) { #ifdef RPC_SVC_FG if (_rpcpmstart) syslog(LOG_ERR, "%s", msg); else warnx("%s", msg); #else syslog(LOG_ERR, "%s", msg); #endif } static void closedown(int sig) { if (_rpcsvcstate == _IDLE) { extern fd_set svc_fdset; static int size; int i, openfd; if (_rpcfdtype == SOCK_DGRAM) exit(0); if (size == 0) { size = getdtablesize(); } for (i = 0, openfd = 0; i < size && openfd < 2; i++) if (FD_ISSET(i, &svc_fdset)) openfd++; if (openfd <= 1) exit(0); } if (_rpcsvcstate == _SERVED) _rpcsvcstate = _IDLE; (void) signal(SIGALRM, (SIG_PF) closedown); (void) alarm(_RPCSVC_CLOSEDOWN/2); } static void ypupdated_svc_run(void) { #ifdef FD_SETSIZE fd_set readfds; #else int readfds; #endif /* def FD_SETSIZE */ extern int forked; int pid; int fd_setsize = _rpc_dtablesize(); /* Establish the identity of the parent ypupdated process. */ pid = getpid(); for (;;) { #ifdef FD_SETSIZE readfds = svc_fdset; #else readfds = svc_fds; #endif /* def FD_SETSIZE */ switch (select(fd_setsize, &readfds, NULL, NULL, (struct timeval *)0)) { case -1: if (errno == EINTR) { continue; } warn("svc_run: - select failed"); return; case 0: continue; default: svc_getreqset(&readfds); if (forked && pid != getpid()) exit(0); } } } static void reaper(int sig) { int status; if (sig == SIGHUP) { #ifdef foo load_securenets(); #endif return; } if (sig == SIGCHLD) { while (wait3(&status, WNOHANG, NULL) > 0) children--; } else { (void) pmap_unset(YPU_PROG, YPU_VERS); exit(0); } } void usage(void) { fprintf(stderr, "rpc.ypupdatedd [-p path]\n"); exit(0); } int main(int argc, char *argv[]) { register SVCXPRT *transp = NULL; int sock; int proto = 0; struct sockaddr_in saddr; int asize = sizeof (saddr); int ch; while ((ch = getopt(argc, argv, "p:h")) != -1) { switch (ch) { case 'p': yp_dir = optarg; break; default: usage(); break; } } #ifdef foo load_securenets(); #endif if (svc_auth_reg(AUTH_DES, _svcauth_des) == -1) { yp_error("failed to register AUTH_DES flavor"); exit(1); } if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) { int ssize = sizeof (int); if (saddr.sin_family != AF_INET) exit(1); if (getsockopt(0, SOL_SOCKET, SO_TYPE, (char *)&_rpcfdtype, &ssize) == -1) exit(1); sock = 0; _rpcpmstart = 1; proto = 0; openlog("rpc.ypupdatedd", LOG_PID, LOG_DAEMON); } else { #ifndef RPC_SVC_FG if (daemon(0,0)) { err(1, "cannot fork"); } openlog("rpc.ypupdated", LOG_PID, LOG_DAEMON); #endif sock = RPC_ANYSOCK; (void) pmap_unset(YPU_PROG, YPU_VERS); } if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) { transp = svcudp_create(sock); if (transp == NULL) { _msgout("cannot create udp service."); exit(1); } if (!_rpcpmstart) proto = IPPROTO_UDP; if (!svc_register(transp, YPU_PROG, YPU_VERS, ypu_prog_1, proto)) { _msgout("unable to register (YPU_PROG, YPU_VERS, udp)."); exit(1); } } if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) { transp = svctcp_create(sock, 0, 0); if (transp == NULL) { _msgout("cannot create tcp service."); exit(1); } if (!_rpcpmstart) proto = IPPROTO_TCP; if (!svc_register(transp, YPU_PROG, YPU_VERS, ypu_prog_1, proto)) { _msgout("unable to register (YPU_PROG, YPU_VERS, tcp)."); exit(1); } } if (transp == (SVCXPRT *)NULL) { _msgout("could not create a handle"); exit(1); } if (_rpcpmstart) { (void) signal(SIGALRM, (SIG_PF) closedown); (void) alarm(_RPCSVC_CLOSEDOWN/2); } (void) signal(SIGPIPE, SIG_IGN); (void) signal(SIGCHLD, (SIG_PF) reaper); (void) signal(SIGTERM, (SIG_PF) reaper); (void) signal(SIGINT, (SIG_PF) reaper); (void) signal(SIGHUP, (SIG_PF) reaper); ypupdated_svc_run(); _msgout("svc_run returned"); exit(1); /* NOTREACHED */ } Index: stable/11/usr.sbin/rpc.ypxfrd/ypxfrd_main.c =================================================================== --- stable/11/usr.sbin/rpc.ypxfrd/ypxfrd_main.c (revision 359753) +++ stable/11/usr.sbin/rpc.ypxfrd/ypxfrd_main.c (revision 359754) @@ -1,302 +1,304 @@ /* * Copyright (c) 1995, 1996 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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. */ #include __FBSDID("$FreeBSD$"); #include "ypxfrd.h" #include #include #include #include #include /* getenv, exit */ #include #include /* for pmap_unset */ #include #include /* strcmp */ #include #include /* TIOCNOTTY */ #ifdef __cplusplus #include /* getdtablesize, open */ #endif /* __cplusplus */ #include #include #include #include #include "ypxfrd_extern.h" #include #include #ifndef SIG_PF #define SIG_PF void(*)(int) #endif #ifdef DEBUG #define RPC_SVC_FG #endif #define _RPCSVC_CLOSEDOWN 120 int _rpcpmstart; /* Started by a port monitor ? */ static int _rpcfdtype; /* Whether Stream or Datagram ? */ /* States a server can be in wrt request */ #define _IDLE 0 #define _SERVED 1 #define _SERVING 2 extern int _rpcsvcstate; /* Set when a request is serviced */ +int debug; + char *progname = "rpc.ypxfrd"; char *yp_dir = "/var/yp/"; static void _msgout(char *msg) { #ifdef RPC_SVC_FG if (_rpcpmstart) syslog(LOG_ERR, "%s", msg); else warnx("%s", msg); #else syslog(LOG_ERR, "%s", msg); #endif } static void closedown(int sig) { if (_rpcsvcstate == _IDLE) { extern fd_set svc_fdset; static int size; int i, openfd; if (_rpcfdtype == SOCK_DGRAM) exit(0); if (size == 0) { size = getdtablesize(); } for (i = 0, openfd = 0; i < size && openfd < 2; i++) if (FD_ISSET(i, &svc_fdset)) openfd++; if (openfd <= 1) exit(0); } if (_rpcsvcstate == _SERVED) _rpcsvcstate = _IDLE; (void) signal(SIGALRM, (SIG_PF) closedown); (void) alarm(_RPCSVC_CLOSEDOWN/2); } static void ypxfrd_svc_run(void) { #ifdef FD_SETSIZE fd_set readfds; #else int readfds; #endif /* def FD_SETSIZE */ extern int forked; int pid; int fd_setsize = _rpc_dtablesize(); /* Establish the identity of the parent ypserv process. */ pid = getpid(); for (;;) { #ifdef FD_SETSIZE readfds = svc_fdset; #else readfds = svc_fds; #endif /* def FD_SETSIZE */ switch (select(fd_setsize, &readfds, NULL, NULL, (struct timeval *)0)) { case -1: if (errno == EINTR) { continue; } warn("svc_run: - select failed"); return; case 0: continue; default: svc_getreqset(&readfds); if (forked && pid != getpid()) exit(0); } } } static void reaper(int sig) { int status; int saved_errno; saved_errno = errno; if (sig == SIGHUP) { load_securenets(); errno = saved_errno; return; } if (sig == SIGCHLD) { while (wait3(&status, WNOHANG, NULL) > 0) children--; } else { (void) pmap_unset(YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS); exit(0); } errno = saved_errno; return; } void usage(void) { fprintf(stderr, "usage: rpc.ypxfrd [-p path]\n"); exit(0); } int main(int argc, char *argv[]) { register SVCXPRT *transp = NULL; int sock; int proto = 0; struct sockaddr_in saddr; int asize = sizeof (saddr); int ch; while ((ch = getopt(argc, argv, "p:h")) != -1) { switch (ch) { case 'p': yp_dir = optarg; break; default: usage(); break; } } load_securenets(); if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) { int ssize = sizeof (int); if (saddr.sin_family != AF_INET) exit(1); if (getsockopt(0, SOL_SOCKET, SO_TYPE, (char *)&_rpcfdtype, &ssize) == -1) exit(1); sock = 0; _rpcpmstart = 1; proto = 0; openlog("rpc.ypxfrd", LOG_PID, LOG_DAEMON); } else { #ifndef RPC_SVC_FG int size; int pid, i; pid = fork(); if (pid < 0) err(1, "fork"); if (pid) exit(0); size = getdtablesize(); for (i = 0; i < size; i++) (void) close(i); i = open(_PATH_CONSOLE, 2); (void) dup2(i, 1); (void) dup2(i, 2); i = open(_PATH_TTY, 2); if (i >= 0) { (void) ioctl(i, TIOCNOTTY, (char *)NULL); (void) close(i); } openlog("rpc.ypxfrd", LOG_PID, LOG_DAEMON); #endif sock = RPC_ANYSOCK; (void) pmap_unset(YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS); } if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) { transp = svcudp_create(sock); if (transp == NULL) { _msgout("cannot create udp service."); exit(1); } if (!_rpcpmstart) proto = IPPROTO_UDP; if (!svc_register(transp, YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, ypxfrd_freebsd_prog_1, proto)) { _msgout("unable to register (YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, udp)."); exit(1); } } if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) { transp = svctcp_create(sock, 0, 0); if (transp == NULL) { _msgout("cannot create tcp service."); exit(1); } if (!_rpcpmstart) proto = IPPROTO_TCP; if (!svc_register(transp, YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, ypxfrd_freebsd_prog_1, proto)) { _msgout("unable to register (YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, tcp)."); exit(1); } } if (transp == (SVCXPRT *)NULL) { _msgout("could not create a handle"); exit(1); } if (_rpcpmstart) { (void) signal(SIGALRM, (SIG_PF) closedown); (void) alarm(_RPCSVC_CLOSEDOWN/2); } (void) signal(SIGPIPE, SIG_IGN); (void) signal(SIGCHLD, (SIG_PF) reaper); (void) signal(SIGTERM, (SIG_PF) reaper); (void) signal(SIGINT, (SIG_PF) reaper); (void) signal(SIGHUP, (SIG_PF) reaper); ypxfrd_svc_run(); _msgout("svc_run returned"); exit(1); /* NOTREACHED */ } Index: stable/11/usr.sbin/ypldap/ypldap.c =================================================================== --- stable/11/usr.sbin/ypldap/ypldap.c (revision 359753) +++ stable/11/usr.sbin/ypldap/ypldap.c (revision 359754) @@ -1,650 +1,652 @@ /* $OpenBSD: ypldap.c,v 1.16 2015/11/02 10:06:06 jmatthew Exp $ */ /* $FreeBSD */ /* * Copyright (c) 2008 Pierre-Yves Ritschard * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ypldap.h" +enum ypldap_process_type ypldap_process; + __dead2 void usage(void); int check_child(pid_t, const char *); void main_sig_handler(int, short, void *); void main_shutdown(void); void main_dispatch_client(int, short, void *); void main_configure_client(struct env *); void main_init_timer(int, short, void *); void main_start_update(struct env *); void main_trash_update(struct env *); void main_end_update(struct env *); int main_create_user_groups(struct env *); void purge_config(struct env *); void reconfigure(struct env *); int pipe_main2client[2]; pid_t client_pid = 0; char *conffile = YPLDAP_CONF_FILE; int opts = 0; void usage(void) { extern const char *__progname; fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n", __progname); exit(1); } int check_child(pid_t pid, const char *pname) { int status; if (waitpid(pid, &status, WNOHANG) > 0) { if (WIFEXITED(status)) { log_warnx("check_child: lost child %s exited", pname); return (1); } if (WIFSIGNALED(status)) { log_warnx("check_child: lost child %s terminated; " "signal %d", pname, WTERMSIG(status)); return (1); } } return (0); } /* ARGUSED */ void main_sig_handler(int sig, short event, void *p) { int die = 0; switch (sig) { case SIGTERM: case SIGINT: die = 1; /* FALLTHROUGH */ case SIGCHLD: if (check_child(client_pid, "ldap client")) { client_pid = 0; die = 1; } if (die) main_shutdown(); break; case SIGHUP: /* reconfigure */ break; default: fatalx("unexpected signal"); } } void main_shutdown(void) { _exit(0); } void main_start_update(struct env *env) { env->update_trashed = 0; log_debug("starting directory update"); env->sc_user_line_len = 0; env->sc_group_line_len = 0; if ((env->sc_user_names_t = calloc(1, sizeof(*env->sc_user_names_t))) == NULL || (env->sc_group_names_t = calloc(1, sizeof(*env->sc_group_names_t))) == NULL) fatal(NULL); RB_INIT(env->sc_user_names_t); RB_INIT(env->sc_group_names_t); } /* * XXX: Currently this function should only be called when updating is * finished. A notification should be send to ldapclient that it should stop * sending new pwd/grp entries before it can be called from different places. */ void main_trash_update(struct env *env) { struct userent *ue; struct groupent *ge; env->update_trashed = 1; while ((ue = RB_ROOT(env->sc_user_names_t)) != NULL) { RB_REMOVE(user_name_tree, env->sc_user_names_t, ue); free(ue->ue_line); free(ue->ue_netid_line); free(ue); } free(env->sc_user_names_t); env->sc_user_names_t = NULL; while ((ge = RB_ROOT(env->sc_group_names_t)) != NULL) { RB_REMOVE(group_name_tree, env->sc_group_names_t, ge); free(ge->ge_line); free(ge); } free(env->sc_group_names_t); env->sc_group_names_t = NULL; } int main_create_user_groups(struct env *env) { struct userent *ue; struct userent ukey; struct groupent *ge; gid_t pw_gid; char *bp, *cp; char *p; const char *errstr = NULL; size_t len; RB_FOREACH(ue, user_name_tree, env->sc_user_names_t) { bp = cp = ue->ue_line; /* name */ bp += strlen(bp) + 1; /* password */ bp += strcspn(bp, ":") + 1; /* uid */ bp += strcspn(bp, ":") + 1; /* gid */ bp[strcspn(bp, ":")] = '\0'; pw_gid = (gid_t)strtonum(bp, 0, GID_MAX, &errstr); if (errstr) { log_warnx("main: failed to parse gid for uid: %d\n", ue->ue_uid); return (-1); } /* bring gid column back to its proper state */ bp[strlen(bp)] = ':'; if ((ue->ue_netid_line = calloc(1, LINE_WIDTH)) == NULL) { return (-1); } if (snprintf(ue->ue_netid_line, LINE_WIDTH-1, "%d:%d", ue->ue_uid, pw_gid) >= LINE_WIDTH) { return (-1); } ue->ue_gid = pw_gid; } RB_FOREACH(ge, group_name_tree, env->sc_group_names_t) { bp = cp = ge->ge_line; /* name */ bp += strlen(bp) + 1; /* password */ bp += strcspn(bp, ":") + 1; /* gid */ bp += strcspn(bp, ":") + 1; cp = bp; if (*bp == '\0') continue; bp = cp; for (;;) { if (!(cp = strsep(&bp, ","))) break; ukey.ue_line = cp; if ((ue = RB_FIND(user_name_tree, env->sc_user_names_t, &ukey)) == NULL) { /* User not found */ log_warnx("main: unknown user %s in group %s\n", ukey.ue_line, ge->ge_line); if (bp != NULL) *(bp-1) = ','; continue; } if (bp != NULL) *(bp-1) = ','; /* Make sure the new group doesn't equal to the main gid */ if (ge->ge_gid == ue->ue_gid) continue; len = strlen(ue->ue_netid_line); p = ue->ue_netid_line + len; if ((snprintf(p, LINE_WIDTH-len-1, ",%d", ge->ge_gid)) >= (int)(LINE_WIDTH-len)) { return (-1); } } } return (0); } void main_end_update(struct env *env) { struct userent *ue; struct groupent *ge; if (env->update_trashed) return; log_debug("updates are over, cleaning up trees now"); if (main_create_user_groups(env) == -1) { main_trash_update(env); return; } if (env->sc_user_names == NULL) { env->sc_user_names = env->sc_user_names_t; env->sc_user_lines = NULL; env->sc_user_names_t = NULL; env->sc_group_names = env->sc_group_names_t; env->sc_group_lines = NULL; env->sc_group_names_t = NULL; flatten_entries(env); goto make_uids; } /* * clean previous tree. */ while ((ue = RB_ROOT(env->sc_user_names)) != NULL) { RB_REMOVE(user_name_tree, env->sc_user_names, ue); free(ue->ue_netid_line); free(ue); } free(env->sc_user_names); free(env->sc_user_lines); env->sc_user_names = env->sc_user_names_t; env->sc_user_lines = NULL; env->sc_user_names_t = NULL; while ((ge = RB_ROOT(env->sc_group_names)) != NULL) { RB_REMOVE(group_name_tree, env->sc_group_names, ge); free(ge); } free(env->sc_group_names); free(env->sc_group_lines); env->sc_group_names = env->sc_group_names_t; env->sc_group_lines = NULL; env->sc_group_names_t = NULL; flatten_entries(env); /* * trees are flat now. build up uid, gid and netid trees. */ make_uids: RB_INIT(&env->sc_user_uids); RB_INIT(&env->sc_group_gids); RB_FOREACH(ue, user_name_tree, env->sc_user_names) RB_INSERT(user_uid_tree, &env->sc_user_uids, ue); RB_FOREACH(ge, group_name_tree, env->sc_group_names) RB_INSERT(group_gid_tree, &env->sc_group_gids, ge); } void main_dispatch_client(int fd, short events, void *p) { int n; int shut = 0; struct env *env = p; struct imsgev *iev = env->sc_iev; struct imsgbuf *ibuf = &iev->ibuf; struct idm_req ir; struct imsg imsg; if ((events & (EV_READ | EV_WRITE)) == 0) fatalx("unknown event"); if (events & EV_READ) { if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) fatal("imsg_read error"); if (n == 0) shut = 1; } if (events & EV_WRITE) { if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN) fatal("msgbuf_write"); if (n == 0) shut = 1; goto done; } for (;;) { if ((n = imsg_get(ibuf, &imsg)) == -1) fatal("main_dispatch_client: imsg_get error"); if (n == 0) break; switch (imsg.hdr.type) { case IMSG_START_UPDATE: main_start_update(env); break; case IMSG_PW_ENTRY: { struct userent *ue; size_t len; if (env->update_trashed) break; (void)memcpy(&ir, imsg.data, sizeof(ir)); if ((ue = calloc(1, sizeof(*ue))) == NULL || (ue->ue_line = strdup(ir.ir_line)) == NULL) { /* * should cancel tree update instead. */ fatal("out of memory"); } ue->ue_uid = ir.ir_key.ik_uid; len = strlen(ue->ue_line) + 1; ue->ue_line[strcspn(ue->ue_line, ":")] = '\0'; if (RB_INSERT(user_name_tree, env->sc_user_names_t, ue) != NULL) { /* dup */ free(ue->ue_line); free(ue); } else env->sc_user_line_len += len; break; } case IMSG_GRP_ENTRY: { struct groupent *ge; size_t len; if (env->update_trashed) break; (void)memcpy(&ir, imsg.data, sizeof(ir)); if ((ge = calloc(1, sizeof(*ge))) == NULL || (ge->ge_line = strdup(ir.ir_line)) == NULL) { /* * should cancel tree update instead. */ fatal("out of memory"); } ge->ge_gid = ir.ir_key.ik_gid; len = strlen(ge->ge_line) + 1; ge->ge_line[strcspn(ge->ge_line, ":")] = '\0'; if (RB_INSERT(group_name_tree, env->sc_group_names_t, ge) != NULL) { /* dup */ free(ge->ge_line); free(ge); } else env->sc_group_line_len += len; break; } case IMSG_TRASH_UPDATE: main_trash_update(env); break; case IMSG_END_UPDATE: { main_end_update(env); break; } default: log_debug("main_dispatch_client: unexpected imsg %d", imsg.hdr.type); break; } imsg_free(&imsg); } done: if (!shut) imsg_event_add(iev); else { log_debug("king bula sez: ran into dead pipe"); event_del(&iev->ev); event_loopexit(NULL); } } void main_configure_client(struct env *env) { struct idm *idm; struct imsgev *iev = env->sc_iev; imsg_compose_event(iev, IMSG_CONF_START, 0, 0, -1, env, sizeof(*env)); TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) { imsg_compose_event(iev, IMSG_CONF_IDM, 0, 0, -1, idm, sizeof(*idm)); } imsg_compose_event(iev, IMSG_CONF_END, 0, 0, -1, NULL, 0); } void main_init_timer(int fd, short event, void *p) { struct env *env = p; main_configure_client(env); } void purge_config(struct env *env) { struct idm *idm; while ((idm = TAILQ_FIRST(&env->sc_idms)) != NULL) { TAILQ_REMOVE(&env->sc_idms, idm, idm_entry); free(idm); } } int main(int argc, char *argv[]) { int c; int debug; struct passwd *pw; struct env env; struct event ev_sigint; struct event ev_sigterm; struct event ev_sigchld; struct event ev_sighup; struct event ev_timer; struct timeval tv; debug = 0; ypldap_process = PROC_MAIN; log_init(1); while ((c = getopt(argc, argv, "dD:nf:v")) != -1) { switch (c) { case 'd': debug = 2; break; case 'D': if (cmdline_symset(optarg) < 0) log_warnx("could not parse macro definition %s", optarg); break; case 'n': debug = 2; opts |= YPLDAP_OPT_NOACTION; break; case 'f': conffile = optarg; break; case 'v': opts |= YPLDAP_OPT_VERBOSE; break; default: usage(); } } argc -= optind; argv += optind; if (argc) usage(); RB_INIT(&env.sc_user_uids); RB_INIT(&env.sc_group_gids); if (parse_config(&env, conffile, opts)) exit(1); if (opts & YPLDAP_OPT_NOACTION) { fprintf(stderr, "configuration OK\n"); exit(0); } if (geteuid()) errx(1, "need root privileges"); log_init(debug); if (!debug) { if (daemon(1, 0) == -1) err(1, "failed to daemonize"); } log_info("startup%s", (debug > 1)?" [debug mode]":""); if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC, pipe_main2client) == -1) fatal("socketpair"); client_pid = ldapclient(pipe_main2client); setproctitle("parent"); event_init(); signal_set(&ev_sigint, SIGINT, main_sig_handler, &env); signal_set(&ev_sigterm, SIGTERM, main_sig_handler, &env); signal_set(&ev_sighup, SIGHUP, main_sig_handler, &env); signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, &env); signal_add(&ev_sigint, NULL); signal_add(&ev_sigterm, NULL); signal_add(&ev_sighup, NULL); signal_add(&ev_sigchld, NULL); close(pipe_main2client[1]); if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL) fatal(NULL); imsg_init(&env.sc_iev->ibuf, pipe_main2client[0]); env.sc_iev->handler = main_dispatch_client; env.sc_iev->events = EV_READ; env.sc_iev->data = &env; event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events, env.sc_iev->handler, &env); event_add(&env.sc_iev->ev, NULL); yp_init(&env); if ((pw = getpwnam(YPLDAP_USER)) == NULL) fatal("getpwnam"); #ifndef DEBUG if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) fatal("cannot drop privileges"); #else #warning disabling privilege revocation in debug mode #endif memset(&tv, 0, sizeof(tv)); evtimer_set(&ev_timer, main_init_timer, &env); evtimer_add(&ev_timer, &tv); yp_enable_events(); event_dispatch(); main_shutdown(); return (0); } void imsg_event_add(struct imsgev *iev) { if (iev->handler == NULL) { imsg_flush(&iev->ibuf); return; } iev->events = EV_READ; if (iev->ibuf.w.queued) iev->events |= EV_WRITE; event_del(&iev->ev); event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); event_add(&iev->ev, NULL); } int imsg_compose_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid, pid_t pid, int fd, void *data, u_int16_t datalen) { int ret; if ((ret = imsg_compose(&iev->ibuf, type, peerid, pid, fd, data, datalen)) != -1) imsg_event_add(iev); return (ret); } Index: stable/11/usr.sbin/ypldap/ypldap.h =================================================================== --- stable/11/usr.sbin/ypldap/ypldap.h (revision 359753) +++ stable/11/usr.sbin/ypldap/ypldap.h (revision 359754) @@ -1,223 +1,224 @@ /* $OpenBSD: ypldap.h,v 1.16 2015/01/16 06:40:22 deraadt Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #define YPLDAP_USER "_ypldap" #define YPLDAP_CONF_FILE "/etc/ypldap.conf" #define DEFAULT_INTERVAL 600 #define LINE_WIDTH 1024 #define FILTER_WIDTH 128 #define ATTR_WIDTH 32 #define MAX_SERVERS_DNS 8 enum imsg_type { IMSG_NONE, IMSG_CONF_START, IMSG_CONF_IDM, IMSG_CONF_END, IMSG_START_UPDATE, IMSG_END_UPDATE, IMSG_TRASH_UPDATE, IMSG_PW_ENTRY, IMSG_GRP_ENTRY, IMSG_HOST_DNS }; struct ypldap_addr { TAILQ_ENTRY(ypldap_addr) next; struct sockaddr_storage ss; }; TAILQ_HEAD(ypldap_addr_list, ypldap_addr); -enum { +enum ypldap_process_type { PROC_MAIN, PROC_CLIENT -} ypldap_process; +}; +extern enum ypldap_process_type ypldap_process; struct userent { RB_ENTRY(userent) ue_name_node; RB_ENTRY(userent) ue_uid_node; uid_t ue_uid; char *ue_line; char *ue_netid_line; gid_t ue_gid; }; struct groupent { RB_ENTRY(groupent) ge_name_node; RB_ENTRY(groupent) ge_gid_node; gid_t ge_gid; char *ge_line; }; enum client_state { STATE_NONE, STATE_DNS_INPROGRESS, STATE_DNS_TEMPFAIL, STATE_DNS_DONE, STATE_LDAP_FAIL, STATE_LDAP_DONE }; /* * beck, djm, dlg: pay attention to the struct name */ struct idm { TAILQ_ENTRY(idm) idm_entry; u_int32_t idm_id; char idm_name[MAXHOSTNAMELEN]; #define F_SSL 0x00100000 #define F_CONFIGURING 0x00200000 #define F_NEEDAUTH 0x00400000 #define F_FIXED_ATTR(n) (1<. 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "yp_extern.h" #ifdef TCP_WRAPPER #include "tcpd.h" #endif -extern int debug; - static const char *yp_procs[] = { /* NIS v1 */ "ypoldproc_null", "ypoldproc_domain", "ypoldproc_domain_nonack", "ypoldproc_match", "ypoldproc_first", "ypoldproc_next", "ypoldproc_poll", "ypoldproc_push", "ypoldproc_get", "badproc1", /* placeholder */ "badproc2", /* placeholder */ "badproc3", /* placeholder */ /* NIS v2 */ "ypproc_null", "ypproc_domain", "ypproc_domain_nonack", "ypproc_match", "ypproc_first", "ypproc_next", "ypproc_xfr", "ypproc_clear", "ypproc_all", "ypproc_master", "ypproc_order", "ypproc_maplist" }; struct securenet { struct in_addr net; struct in_addr mask; struct securenet *next; }; static struct securenet *securenets; #define LINEBUFSZ 1024 #ifdef TCP_WRAPPER int hosts_ctl(char *, char *, char *, char *); #endif /* * Read /var/yp/securenets file and initialize the securenets * list. If the file doesn't exist, we set up a dummy entry that * allows all hosts to connect. */ void load_securenets(void) { FILE *fp; char path[MAXPATHLEN + 2]; char linebuf[1024 + 2]; struct securenet *tmp; /* * If securenets is not NULL, we are being called to reload * the list; free the existing list before re-reading the * securenets file. */ while (securenets) { tmp = securenets->next; free(securenets); securenets = tmp; } snprintf(path, MAXPATHLEN, "%s/securenets", yp_dir); if ((fp = fopen(path, "r")) == NULL) { if (errno == ENOENT) { securenets = malloc(sizeof(struct securenet)); securenets->net.s_addr = INADDR_ANY; securenets->mask.s_addr = INADDR_ANY; securenets->next = NULL; return; } else { yp_error("fopen(%s) failed: %s", path, strerror(errno)); exit(1); } } securenets = NULL; while (fgets(linebuf, LINEBUFSZ, fp)) { char addr1[20], addr2[20]; if ((linebuf[0] == '#') || (strspn(linebuf, " \t\r\n") == strlen(linebuf))) continue; if (sscanf(linebuf, "%s %s", addr1, addr2) < 2) { yp_error("badly formatted securenets entry: %s", linebuf); continue; } tmp = malloc(sizeof(struct securenet)); if (!inet_aton((char *)&addr1, (struct in_addr *)&tmp->net)) { yp_error("badly formatted securenets entry: %s", addr1); free(tmp); continue; } if (!inet_aton((char *)&addr2, (struct in_addr *)&tmp->mask)) { yp_error("badly formatted securenets entry: %s", addr2); free(tmp); continue; } tmp->next = securenets; securenets = tmp; } fclose(fp); } /* * Access control functions. * * yp_access() checks the mapname and client host address and watches for * the following things: * * - If the client is referencing one of the master.passwd.* or shadow.* maps, * it must be using a privileged port to make its RPC to us. If it is, then * we can assume that the caller is root and allow the RPC to succeed. If it * isn't access is denied. * * - The client's IP address is checked against the securenets rules. * There are two kinds of securenets support: the built-in support, * which is very simple and depends on the presence of a * /var/yp/securenets file, and tcp-wrapper support, which requires * Wietse Venema's libwrap.a and tcpd.h. (Since the tcp-wrapper * package does not ship with FreeBSD, we use the built-in support * by default. Users can recompile the server with the tcp-wrapper library * if they already have it installed and want to use hosts.allow and * hosts.deny to control access instead of having a separate securenets * file.) * * If no /var/yp/securenets file is present, the host access checks * are bypassed and all hosts are allowed to connect. * * The yp_validdomain() function checks the domain specified by the caller * to make sure it's actually served by this server. This is more a sanity * check than an a security check, but this seems to be the best place for * it. */ #ifdef DB_CACHE int yp_access(const char *map, const char *domain, const struct svc_req *rqstp) #else int yp_access(const char *map, const struct svc_req *rqstp) #endif { struct sockaddr_in *rqhost; int status_securenets = 0; #ifdef TCP_WRAPPER int status_tcpwrap; #endif static unsigned long oldaddr = 0; struct securenet *tmp; const char *yp_procedure = NULL; char procbuf[50]; if (rqstp->rq_prog != YPPASSWDPROG && rqstp->rq_prog != YPPROG) { snprintf(procbuf, sizeof(procbuf), "#%lu/#%lu", (unsigned long)rqstp->rq_prog, (unsigned long)rqstp->rq_proc); yp_procedure = (char *)&procbuf; } else { yp_procedure = rqstp->rq_prog == YPPASSWDPROG ? "yppasswdprog_update" : yp_procs[rqstp->rq_proc + (12 * (rqstp->rq_vers - 1))]; } rqhost = svc_getcaller(rqstp->rq_xprt); if (debug) { yp_error("procedure %s called from %s:%d", yp_procedure, inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port)); if (map != NULL) yp_error("client is referencing map \"%s\".", map); } /* Check the map name if one was supplied. */ if (map != NULL) { if (strchr(map, '/')) { yp_error("embedded slash in map name \"%s\" -- \ possible spoof attempt from %s:%d", map, inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port)); return(1); } #ifdef DB_CACHE if ((yp_testflag((char *)map, (char *)domain, YP_SECURE) || #else if ((strstr(map, "master.passwd.") || strstr(map, "shadow.") || #endif (rqstp->rq_prog == YPPROG && rqstp->rq_proc == YPPROC_XFR) || (rqstp->rq_prog == YPXFRD_FREEBSD_PROG && rqstp->rq_proc == YPXFRD_GETMAP)) && ntohs(rqhost->sin_port) >= IPPORT_RESERVED) { yp_error("access to %s denied -- client %s:%d \ not privileged", map, inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port)); return(1); } } #ifdef TCP_WRAPPER status_tcpwrap = hosts_ctl("ypserv", STRING_UNKNOWN, inet_ntoa(rqhost->sin_addr), ""); #endif tmp = securenets; while (tmp) { if (((rqhost->sin_addr.s_addr & ~tmp->mask.s_addr) | tmp->net.s_addr) == rqhost->sin_addr.s_addr) { status_securenets = 1; break; } tmp = tmp->next; } #ifdef TCP_WRAPPER if (status_securenets == 0 || status_tcpwrap == 0) { #else if (status_securenets == 0) { #endif /* * One of the following two events occurred: * * (1) The /var/yp/securenets exists and the remote host does not * match any of the networks specified in it. * (2) The hosts.allow file has denied access and TCP_WRAPPER is * defined. * * In either case deny access. */ if (rqhost->sin_addr.s_addr != oldaddr) { yp_error("connect from %s:%d to procedure %s refused", inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port), yp_procedure); oldaddr = rqhost->sin_addr.s_addr; } return(1); } return(0); } int yp_validdomain(const char *domain) { struct stat statbuf; char dompath[MAXPATHLEN + 2]; if (domain == NULL || strstr(domain, "binding") || !strcmp(domain, ".") || !strcmp(domain, "..") || strchr(domain, '/') || strlen(domain) > YPMAXDOMAIN) return(1); snprintf(dompath, sizeof(dompath), "%s/%s", yp_dir, domain); if (stat(dompath, &statbuf) < 0 || !S_ISDIR(statbuf.st_mode)) return(1); return(0); } Index: stable/11/usr.sbin/ypserv/yp_error.c =================================================================== --- stable/11/usr.sbin/ypserv/yp_error.c (revision 359753) +++ stable/11/usr.sbin/ypserv/yp_error.c (revision 359754) @@ -1,73 +1,71 @@ /* * Copyright (c) 1995 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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. * */ #include __FBSDID("$FreeBSD$"); /* * error logging/reporting facilities * stolen from /usr/libexec/mail.local via ypserv */ #include #include #include #include #include "yp_extern.h" -int debug; - extern int _rpcpmstart; extern char *progname; static void __verr(const char *fmt, va_list ap) __printflike(1, 0); static void __verr(const char *fmt, va_list ap) { if (debug && !_rpcpmstart) { fprintf(stderr,"%s: ",progname); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); } else { vsyslog(LOG_NOTICE, fmt, ap); } } void yp_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); __verr(fmt,ap); va_end(ap); } Index: stable/11/usr.sbin/ypserv/yp_main.c =================================================================== --- stable/11/usr.sbin/ypserv/yp_main.c (revision 359753) +++ stable/11/usr.sbin/ypserv/yp_main.c (revision 359754) @@ -1,579 +1,579 @@ /* * Copyright (c) 1995 * Bill Paul . 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 Bill Paul. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY Bill Paul 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 Bill Paul 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. */ #include __FBSDID("$FreeBSD$"); /* * ypserv startup function. * We need out own main() since we have to do some additional work * that rpcgen won't do for us. Most of this file was generated using * rpcgen.new, and later modified. */ #include #include #include #include #include #include "yp.h" #include #include #include #include #include #include #include /* getenv, exit */ #include /* strcmp */ #include #include #ifdef __cplusplus #include /* getdtablesize, open */ #endif /* __cplusplus */ #include #include #include "yp_extern.h" #include #include #include #ifndef SIG_PF #define SIG_PF void(*)(int) #endif #define _RPCSVC_CLOSEDOWN 120 int _rpcpmstart; /* Started by a port monitor ? */ static int _rpcfdtype; /* Whether Stream or Datagram? */ static int _rpcaf; static int _rpcfd; /* States a server can be in wrt request */ #define _IDLE 0 #define _SERVED 1 #define _SERVING 2 extern void ypprog_1(struct svc_req *, SVCXPRT *); extern void ypprog_2(struct svc_req *, SVCXPRT *); extern int _rpc_dtablesize(void); extern int _rpcsvcstate; /* Set when a request is serviced */ char *progname = "ypserv"; char *yp_dir = _PATH_YP; -/*int debug = 0;*/ +int debug; int do_dns = 0; int resfd; struct socklistent { int sle_sock; struct sockaddr_storage sle_ss; SLIST_ENTRY(socklistent) sle_next; }; static SLIST_HEAD(, socklistent) sle_head = SLIST_HEAD_INITIALIZER(sle_head); struct bindaddrlistent { const char *ble_hostname; SLIST_ENTRY(bindaddrlistent) ble_next; }; static SLIST_HEAD(, bindaddrlistent) ble_head = SLIST_HEAD_INITIALIZER(ble_head); static char *servname = "0"; static void _msgout(char* msg, ...) { va_list ap; va_start(ap, msg); if (debug) { if (_rpcpmstart) vsyslog(LOG_ERR, msg, ap); else vwarnx(msg, ap); } else vsyslog(LOG_ERR, msg, ap); va_end(ap); } pid_t yp_pid; static void yp_svc_run(void) { #ifdef FD_SETSIZE fd_set readfds; #else int readfds; #endif /* def FD_SETSIZE */ int fd_setsize = _rpc_dtablesize(); struct timeval timeout; /* Establish the identity of the parent ypserv process. */ yp_pid = getpid(); for (;;) { #ifdef FD_SETSIZE readfds = svc_fdset; #else readfds = svc_fds; #endif /* def FD_SETSIZE */ FD_SET(resfd, &readfds); timeout.tv_sec = RESOLVER_TIMEOUT; timeout.tv_usec = 0; switch (select(fd_setsize, &readfds, NULL, NULL, &timeout)) { case -1: if (errno == EINTR) { continue; } warn("svc_run: - select failed"); return; case 0: if (getpid() == yp_pid) yp_prune_dnsq(); break; default: if (getpid() == yp_pid) { if (FD_ISSET(resfd, &readfds)) { yp_run_dnsq(); FD_CLR(resfd, &readfds); } svc_getreqset(&readfds); } } if (yp_pid != getpid()) _exit(0); } } static void unregister(void) { (void)svc_unreg(YPPROG, YPVERS); (void)svc_unreg(YPPROG, YPOLDVERS); } static void reaper(int sig) { int status; int saved_errno; saved_errno = errno; if (sig == SIGHUP) { load_securenets(); #ifdef DB_CACHE yp_flush_all(); #endif errno = saved_errno; return; } if (sig == SIGCHLD) { while (wait3(&status, WNOHANG, NULL) > 0) children--; } else { unregister(); exit(0); } errno = saved_errno; return; } static void usage(void) { fprintf(stderr, "usage: ypserv [-h addr] [-d] [-n] [-p path] [-P port]\n"); exit(1); } static void closedown(int sig) { if (_rpcsvcstate == _IDLE) { extern fd_set svc_fdset; static int size; int i, openfd; if (_rpcfdtype == SOCK_DGRAM) { unregister(); exit(0); } if (size == 0) { size = getdtablesize(); } for (i = 0, openfd = 0; i < size && openfd < 2; i++) if (FD_ISSET(i, &svc_fdset)) openfd++; if (openfd <= 1) { unregister(); exit(0); } } if (_rpcsvcstate == _SERVED) _rpcsvcstate = _IDLE; (void) signal(SIGALRM, (SIG_PF) closedown); (void) alarm(_RPCSVC_CLOSEDOWN/2); } static int create_service(const int sock, const struct netconfig *nconf, const struct __rpc_sockinfo *si) { int error; SVCXPRT *transp; struct addrinfo hints, *res, *res0; struct socklistent *slep; struct bindaddrlistent *blep; struct netbuf svcaddr; SLIST_INIT(&sle_head); memset(&hints, 0, sizeof(hints)); memset(&svcaddr, 0, sizeof(svcaddr)); hints.ai_family = si->si_af; hints.ai_socktype = si->si_socktype; hints.ai_protocol = si->si_proto; /* * Build socketlist from bindaddrlist. */ if (sock == RPC_ANYFD) { SLIST_FOREACH(blep, &ble_head, ble_next) { if (blep->ble_hostname == NULL) hints.ai_flags = AI_PASSIVE; else hints.ai_flags = 0; error = getaddrinfo(blep->ble_hostname, servname, &hints, &res0); if (error) { _msgout("getaddrinfo(): %s", gai_strerror(error)); return -1; } for (res = res0; res; res = res->ai_next) { int s; s = __rpc_nconf2fd(nconf); if (s < 0) { if (errno == EAFNOSUPPORT) _msgout("unsupported" " transport: %s", nconf->nc_netid); else _msgout("cannot create" " %s socket: %s", nconf->nc_netid, strerror(errno)); freeaddrinfo(res0); return -1; } if (bindresvport_sa(s, res->ai_addr) == -1) { if ((errno != EPERM) || (bind(s, res->ai_addr, res->ai_addrlen) == -1)) { _msgout("cannot bind " "%s socket: %s", nconf->nc_netid, strerror(errno)); freeaddrinfo(res0); close(sock); return -1; } } if (nconf->nc_semantics != NC_TPI_CLTS) listen(s, SOMAXCONN); slep = malloc(sizeof(*slep)); if (slep == NULL) { _msgout("malloc failed: %s", strerror(errno)); freeaddrinfo(res0); close(s); return -1; } memset(slep, 0, sizeof(*slep)); memcpy(&slep->sle_ss, res->ai_addr, res->ai_addrlen); slep->sle_sock = s; SLIST_INSERT_HEAD(&sle_head, slep, sle_next); /* * If servname == "0", redefine it by using * the bound socket. */ if (strncmp("0", servname, 1) == 0) { struct sockaddr *sap; socklen_t slen; char *sname; sname = malloc(NI_MAXSERV); if (sname == NULL) { _msgout("malloc(): %s", strerror(errno)); freeaddrinfo(res0); close(s); return -1; } memset(sname, 0, NI_MAXSERV); sap = (struct sockaddr *)&slep->sle_ss; slen = sizeof(*sap); error = getsockname(s, sap, &slen); if (error) { _msgout("getsockname(): %s", strerror(errno)); freeaddrinfo(res0); close(s); free(sname); return -1; } error = getnameinfo(sap, slen, NULL, 0, sname, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV); if (error) { _msgout("getnameinfo(): %s", strerror(errno)); freeaddrinfo(res0); close(s); free(sname); return -1; } servname = sname; } } freeaddrinfo(res0); } } else { slep = malloc(sizeof(*slep)); if (slep == NULL) { _msgout("malloc failed: %s", strerror(errno)); return -1; } memset(slep, 0, sizeof(*slep)); slep->sle_sock = sock; SLIST_INSERT_HEAD(&sle_head, slep, sle_next); } /* * Traverse socketlist and create rpc service handles for each socket. */ SLIST_FOREACH(slep, &sle_head, sle_next) { if (nconf->nc_semantics == NC_TPI_CLTS) transp = svc_dg_create(slep->sle_sock, 0, 0); else transp = svc_vc_create(slep->sle_sock, RPC_MAXDATASIZE, RPC_MAXDATASIZE); if (transp == NULL) { _msgout("unable to create service: %s", nconf->nc_netid); continue; } if (!svc_reg(transp, YPPROG, YPOLDVERS, ypprog_1, NULL)) { svc_destroy(transp); close(slep->sle_sock); _msgout("unable to register (YPPROG, YPOLDVERS, %s):" " %s", nconf->nc_netid, strerror(errno)); continue; } if (!svc_reg(transp, YPPROG, YPVERS, ypprog_2, NULL)) { svc_destroy(transp); close(slep->sle_sock); _msgout("unable to register (YPPROG, YPVERS, %s): %s", nconf->nc_netid, strerror(errno)); continue; } } while(!(SLIST_EMPTY(&sle_head))) SLIST_REMOVE_HEAD(&sle_head, sle_next); /* * Register RPC service to rpcbind by using AI_PASSIVE address. */ hints.ai_flags = AI_PASSIVE; error = getaddrinfo(NULL, servname, &hints, &res0); if (error) { _msgout("getaddrinfo(): %s", gai_strerror(error)); return -1; } svcaddr.buf = res0->ai_addr; svcaddr.len = res0->ai_addrlen; if (si->si_af == AF_INET) { /* XXX: ignore error intentionally */ rpcb_set(YPPROG, YPOLDVERS, nconf, &svcaddr); } /* XXX: ignore error intentionally */ rpcb_set(YPPROG, YPVERS, nconf, &svcaddr); freeaddrinfo(res0); return 0; } int main(int argc, char *argv[]) { int ch; int error; int ntrans; void *nc_handle; struct netconfig *nconf; struct __rpc_sockinfo si; struct bindaddrlistent *blep; memset(&si, 0, sizeof(si)); SLIST_INIT(&ble_head); while ((ch = getopt(argc, argv, "dh:np:P:")) != -1) { switch (ch) { case 'd': debug = ypdb_debug = 1; break; case 'h': blep = malloc(sizeof(*blep)); if (blep == NULL) err(1, "malloc() failed: -h %s", optarg); blep->ble_hostname = optarg; SLIST_INSERT_HEAD(&ble_head, blep, ble_next); break; case 'n': do_dns = 1; break; case 'p': yp_dir = optarg; break; case 'P': servname = optarg; break; default: usage(); } } /* * Add "anyaddr" entry if no -h is specified. */ if (SLIST_EMPTY(&ble_head)) { blep = malloc(sizeof(*blep)); if (blep == NULL) err(1, "malloc() failed"); memset(blep, 0, sizeof(*blep)); SLIST_INSERT_HEAD(&ble_head, blep, ble_next); } load_securenets(); yp_init_resolver(); #ifdef DB_CACHE yp_init_dbs(); #endif nc_handle = setnetconfig(); if (nc_handle == NULL) err(1, "cannot read %s", NETCONFIG); if (__rpc_fd2sockinfo(0, &si) != 0) { /* invoked from inetd */ _rpcpmstart = 1; _rpcfdtype = si.si_socktype; _rpcaf = si.si_af; _rpcfd = 0; openlog("ypserv", LOG_PID, LOG_DAEMON); } else { /* standalone mode */ if (!debug) { if (daemon(0,0)) { err(1,"cannot fork"); } openlog("ypserv", LOG_PID, LOG_DAEMON); } _rpcpmstart = 0; _rpcaf = AF_INET; _rpcfd = RPC_ANYFD; unregister(); } if (madvise(NULL, 0, MADV_PROTECT) != 0) _msgout("madvise(): %s", strerror(errno)); /* * Create RPC service for each transport. */ ntrans = 0; while((nconf = getnetconfig(nc_handle))) { if ((nconf->nc_flag & NC_VISIBLE)) { if (__rpc_nconf2sockinfo(nconf, &si) == 0) { _msgout("cannot get information for %s. " "Ignored.", nconf->nc_netid); continue; } if (_rpcpmstart) { if (si.si_socktype != _rpcfdtype || si.si_af != _rpcaf) continue; } else if (si.si_af != _rpcaf) continue; error = create_service(_rpcfd, nconf, &si); if (error) { endnetconfig(nc_handle); exit(1); } ntrans++; } } endnetconfig(nc_handle); while(!(SLIST_EMPTY(&ble_head))) SLIST_REMOVE_HEAD(&ble_head, ble_next); if (ntrans == 0) { _msgout("no transport is available. Aborted."); exit(1); } if (_rpcpmstart) { (void) signal(SIGALRM, (SIG_PF) closedown); (void) alarm(_RPCSVC_CLOSEDOWN/2); } /* * Make sure SIGPIPE doesn't blow us away while servicing TCP * connections. */ (void) signal(SIGPIPE, SIG_IGN); (void) signal(SIGCHLD, (SIG_PF) reaper); (void) signal(SIGTERM, (SIG_PF) reaper); (void) signal(SIGINT, (SIG_PF) reaper); (void) signal(SIGHUP, (SIG_PF) reaper); yp_svc_run(); _msgout("svc_run returned"); exit(1); /* NOTREACHED */ } Index: stable/11 =================================================================== --- stable/11 (revision 359753) +++ stable/11 (revision 359754) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r359389,359394,359397-359399,359403-359404,359406,359413-359416,359425,359427,359432-359433,359443,359675-359677