Index: head/bin/mv/pathnames.h =================================================================== --- head/bin/mv/pathnames.h (revision 114762) +++ head/bin/mv/pathnames.h (nonexistent) @@ -1,37 +0,0 @@ -/* - * 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. - * - * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 - * $FreeBSD$ - */ - -#define _PATH_RM "/bin/rm" Property changes on: head/bin/mv/pathnames.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/bin/mv/mv.c =================================================================== --- head/bin/mv/mv.c (revision 114762) +++ head/bin/mv/mv.c (revision 114763) @@ -1,386 +1,384 @@ /* * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Ken Smith of The State University of New York at Buffalo. * * 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. */ #if 0 #ifndef lint static char const copyright[] = "@(#) Copyright (c) 1989, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include "pathnames.h" - int fflg, iflg, nflg, vflg; int copy(char *, char *); int do_move(char *, char *); int fastcopy(char *, char *, struct stat *); void usage(void); int main(int argc, char *argv[]) { size_t baselen, len; int rval; char *p, *endp; struct stat sb; int ch; char path[PATH_MAX]; while ((ch = getopt(argc, argv, "finv")) != -1) switch (ch) { case 'i': iflg = 1; fflg = nflg = 0; break; case 'f': fflg = 1; iflg = nflg = 0; break; case 'n': nflg = 1; fflg = iflg = 0; break; case 'v': vflg = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc < 2) usage(); /* * If the stat on the target fails or the target isn't a directory, * try the move. More than 2 arguments is an error in this case. */ if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { if (argc > 2) usage(); exit(do_move(argv[0], argv[1])); } /* It's a directory, move each file into it. */ if (strlen(argv[argc - 1]) > sizeof(path) - 1) errx(1, "%s: destination pathname too long", *argv); (void)strcpy(path, argv[argc - 1]); baselen = strlen(path); endp = &path[baselen]; if (!baselen || *(endp - 1) != '/') { *endp++ = '/'; ++baselen; } for (rval = 0; --argc; ++argv) { /* * Find the last component of the source pathname. It * may have trailing slashes. */ p = *argv + strlen(*argv); while (p != *argv && p[-1] == '/') --p; while (p != *argv && p[-1] != '/') --p; if ((baselen + (len = strlen(p))) >= PATH_MAX) { warnx("%s: destination pathname too long", *argv); rval = 1; } else { memmove(endp, p, (size_t)len + 1); if (do_move(*argv, path)) rval = 1; } } exit(rval); } int do_move(char *from, char *to) { struct stat sb; int ask, ch, first; char modep[15]; /* * Check access. If interactive and file exists, ask user if it * should be replaced. Otherwise if file exists but isn't writable * make sure the user wants to clobber it. */ if (!fflg && !access(to, F_OK)) { /* prompt only if source exist */ if (lstat(from, &sb) == -1) { warn("%s", from); return (1); } #define YESNO "(y/n [n]) " ask = 0; if (nflg) { if (vflg) printf("%s not overwritten\n", to); return (0); } else if (iflg) { (void)fprintf(stderr, "overwrite %s? %s", to, YESNO); ask = 1; } else if (access(to, W_OK) && !stat(to, &sb)) { strmode(sb.st_mode, modep); (void)fprintf(stderr, "override %s%s%s/%s for %s? %s", modep + 1, modep[9] == ' ' ? "" : " ", user_from_uid((unsigned long)sb.st_uid, 0), group_from_gid((unsigned long)sb.st_gid, 0), to, YESNO); ask = 1; } if (ask) { first = ch = getchar(); while (ch != '\n' && ch != EOF) ch = getchar(); if (first != 'y' && first != 'Y') { (void)fprintf(stderr, "not overwritten\n"); return (0); } } } if (!rename(from, to)) { if (vflg) printf("%s -> %s\n", from, to); return (0); } if (errno == EXDEV) { struct statfs sfs; char path[PATH_MAX]; /* Can't mv(1) a mount point. */ if (realpath(from, path) == NULL) { warnx("cannot resolve %s: %s", from, path); return (1); } if (!statfs(path, &sfs) && !strcmp(path, sfs.f_mntonname)) { warnx("cannot rename a mount point"); return (1); } } else { warn("rename %s to %s", from, to); return (1); } /* * If rename fails because we're trying to cross devices, and * it's a regular file, do the copy internally; otherwise, use * cp and rm. */ if (lstat(from, &sb)) { warn("%s", from); return (1); } return (S_ISREG(sb.st_mode) ? fastcopy(from, to, &sb) : copy(from, to)); } int fastcopy(char *from, char *to, struct stat *sbp) { struct timeval tval[2]; static u_int blen; static char *bp; mode_t oldmode; int nread, from_fd, to_fd; if ((from_fd = open(from, O_RDONLY, 0)) < 0) { warn("%s", from); return (1); } if (blen < sbp->st_blksize) { if (bp != NULL) free(bp); if ((bp = malloc((size_t)sbp->st_blksize)) == NULL) { blen = 0; warnx("malloc failed"); return (1); } blen = sbp->st_blksize; } while ((to_fd = open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) { if (errno == EEXIST && unlink(to) == 0) continue; warn("%s", to); (void)close(from_fd); return (1); } while ((nread = read(from_fd, bp, (size_t)blen)) > 0) if (write(to_fd, bp, (size_t)nread) != nread) { warn("%s", to); goto err; } if (nread < 0) { warn("%s", from); err: if (unlink(to)) warn("%s: remove", to); (void)close(from_fd); (void)close(to_fd); return (1); } (void)close(from_fd); oldmode = sbp->st_mode & ALLPERMS; if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { warn("%s: set owner/group (was: %lu/%lu)", to, (u_long)sbp->st_uid, (u_long)sbp->st_gid); if (oldmode & (S_ISUID | S_ISGID)) { warnx( "%s: owner/group changed; clearing suid/sgid (mode was 0%03o)", to, oldmode); sbp->st_mode &= ~(S_ISUID | S_ISGID); } } if (fchmod(to_fd, sbp->st_mode)) warn("%s: set mode (was: 0%03o)", to, oldmode); /* * XXX * NFS doesn't support chflags; ignore errors unless there's reason * to believe we're losing bits. (Note, this still won't be right * if the server supports flags and we were trying to *remove* flags * on a file that we copied, i.e., that we didn't create.) */ errno = 0; if (fchflags(to_fd, (u_long)sbp->st_flags)) if (errno != EOPNOTSUPP || sbp->st_flags != 0) warn("%s: set flags (was: 0%07o)", to, sbp->st_flags); tval[0].tv_sec = sbp->st_atime; tval[1].tv_sec = sbp->st_mtime; tval[0].tv_usec = tval[1].tv_usec = 0; if (utimes(to, tval)) warn("%s: set times", to); if (close(to_fd)) { warn("%s", to); return (1); } if (unlink(from)) { warn("%s: remove", from); return (1); } if (vflg) printf("%s -> %s\n", from, to); return (0); } int copy(char *from, char *to) { int pid, status; if ((pid = fork()) == 0) { execl(_PATH_CP, "mv", vflg ? "-PRpv" : "-PRp", "--", from, to, (char *)NULL); warn("%s", _PATH_CP); _exit(1); } if (waitpid(pid, &status, 0) == -1) { warn("%s: waitpid", _PATH_CP); return (1); } if (!WIFEXITED(status)) { warn("%s: did not terminate normally", _PATH_CP); return (1); } if (WEXITSTATUS(status)) { warn("%s: terminated with %d (non-zero) status", _PATH_CP, WEXITSTATUS(status)); return (1); } if (!(pid = vfork())) { execl(_PATH_RM, "mv", "-rf", "--", from, (char *)NULL); warn("%s", _PATH_RM); _exit(1); } if (waitpid(pid, &status, 0) == -1) { warn("%s: waitpid", _PATH_RM); return (1); } if (!WIFEXITED(status)) { warn("%s: did not terminate normally", _PATH_RM); return (1); } if (WEXITSTATUS(status)) { warn("%s: terminated with %d (non-zero) status", _PATH_RM, WEXITSTATUS(status)); return (1); } return (0); } void usage(void) { (void)fprintf(stderr, "%s\n%s\n", "usage: mv [-f | -i | -n] [-v] source target", " mv [-f | -i | -n] [-v] source ... directory"); exit(EX_USAGE); } Index: head/bin/sh/var.c =================================================================== --- head/bin/sh/var.c (revision 114762) +++ head/bin/sh/var.c (revision 114763) @@ -1,805 +1,806 @@ /*- * 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. * 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 #if 0 static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include +#include /* * Shell variables. */ #include #include "shell.h" #include "output.h" #include "expand.h" #include "nodes.h" /* for other headers */ #include "eval.h" /* defines cmdenviron */ #include "exec.h" #include "syntax.h" #include "options.h" #include "mail.h" #include "var.h" #include "memalloc.h" #include "error.h" #include "mystring.h" #include "parser.h" #ifndef NO_HISTORY #include "myhistedit.h" #endif #define VTABSIZE 39 struct varinit { struct var *var; int flags; char *text; void (*func)(const char *); }; #ifndef NO_HISTORY struct var vhistsize; #endif struct var vifs; struct var vmail; struct var vmpath; struct var vpath; struct var vppid; struct var vps1; struct var vps2; struct var vvers; struct var voptind; const struct varinit varinit[] = { #ifndef NO_HISTORY { &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=", sethistsize }, #endif { &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n", NULL }, { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", NULL }, { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", NULL }, - { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=/bin:/usr/bin", + { &vpath, VSTRFIXED|VTEXTFIXED, "PATH=" _PATH_DEFPATH, changepath }, { &vppid, VSTRFIXED|VTEXTFIXED|VUNSET, "PPID=", NULL }, /* * vps1 depends on uid */ { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", NULL }, { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset }, { NULL, 0, NULL, NULL } }; struct var *vartab[VTABSIZE]; STATIC struct var **hashvar(char *); STATIC int varequal(char *, char *); STATIC int localevar(char *); /* * Initialize the varable symbol tables and import the environment */ #ifdef mkinit INCLUDE "var.h" INIT { char **envp; extern char **environ; initvar(); for (envp = environ ; *envp ; envp++) { if (strchr(*envp, '=')) { setvareq(*envp, VEXPORT|VTEXTFIXED); } } } #endif /* * This routine initializes the builtin variables. It is called when the * shell is initialized and again when a shell procedure is spawned. */ void initvar(void) { char ppid[20]; const struct varinit *ip; struct var *vp; struct var **vpp; for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { if ((vp->flags & VEXPORT) == 0) { vpp = hashvar(ip->text); vp->next = *vpp; *vpp = vp; vp->text = ip->text; vp->flags = ip->flags; vp->func = ip->func; } } /* * PS1 depends on uid */ if ((vps1.flags & VEXPORT) == 0) { vpp = hashvar("PS1="); vps1.next = *vpp; *vpp = &vps1; vps1.text = geteuid() ? "PS1=$ " : "PS1=# "; vps1.flags = VSTRFIXED|VTEXTFIXED; } if ((vppid.flags & VEXPORT) == 0) { fmtstr(ppid, sizeof(ppid), "%d", (int)getppid()); setvarsafe("PPID", ppid, 0); } } /* * Safe version of setvar, returns 1 on success 0 on failure. */ int setvarsafe(char *name, char *val, int flags) { struct jmploc jmploc; struct jmploc *volatile savehandler = handler; int err = 0; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &err; #endif if (setjmp(jmploc.loc)) err = 1; else { handler = &jmploc; setvar(name, val, flags); } handler = savehandler; return err; } /* * Set the value of a variable. The flags argument is tored with the * flags of the variable. If val is NULL, the variable is unset. */ void setvar(char *name, char *val, int flags) { char *p, *q; int len; int namelen; char *nameeq; int isbad; isbad = 0; p = name; if (! is_name(*p)) isbad = 1; p++; for (;;) { if (! is_in_name(*p)) { if (*p == '\0' || *p == '=') break; isbad = 1; } p++; } namelen = p - name; if (isbad) error("%.*s: bad variable name", namelen, name); len = namelen + 2; /* 2 is space for '=' and '\0' */ if (val == NULL) { flags |= VUNSET; } else { len += strlen(val); } p = nameeq = ckmalloc(len); q = name; while (--namelen >= 0) *p++ = *q++; *p++ = '='; *p = '\0'; if (val) scopy(val, p); setvareq(nameeq, flags); } STATIC int localevar(char *s) { static char *lnames[7] = { "ALL", "COLLATE", "CTYPE", "MONETARY", "NUMERIC", "TIME", NULL }; char **ss; if (*s != 'L') return 0; if (varequal(s + 1, "ANG")) return 1; if (strncmp(s + 1, "C_", 2) != 0) return 0; for (ss = lnames; *ss ; ss++) if (varequal(s + 3, *ss)) return 1; return 0; } /* * Same as setvar except that the variable and value are passed in * the first argument as name=value. Since the first argument will * be actually stored in the table, it should not be a string that * will go away. */ void setvareq(char *s, int flags) { struct var *vp, **vpp; int len; if (aflag) flags |= VEXPORT; vpp = hashvar(s); for (vp = *vpp ; vp ; vp = vp->next) { if (varequal(s, vp->text)) { if (vp->flags & VREADONLY) { len = strchr(s, '=') - s; error("%.*s: is read only", len, s); } INTOFF; if (vp->func && (flags & VNOFUNC) == 0) (*vp->func)(strchr(s, '=') + 1); if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) ckfree(vp->text); vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); vp->flags |= flags; vp->text = s; /* * We could roll this to a function, to handle it as * a regular variable function callback, but why bother? */ if (vp == &vmpath || (vp == &vmail && ! mpathset())) chkmail(1); if ((vp->flags & VEXPORT) && localevar(s)) { putenv(s); (void) setlocale(LC_ALL, ""); } INTON; return; } } /* not found */ vp = ckmalloc(sizeof (*vp)); vp->flags = flags; vp->text = s; vp->next = *vpp; vp->func = NULL; INTOFF; *vpp = vp; if ((vp->flags & VEXPORT) && localevar(s)) { putenv(s); (void) setlocale(LC_ALL, ""); } INTON; } /* * Process a linked list of variable assignments. */ void listsetvar(struct strlist *list) { struct strlist *lp; INTOFF; for (lp = list ; lp ; lp = lp->next) { setvareq(savestr(lp->text), 0); } INTON; } /* * Find the value of a variable. Returns NULL if not set. */ char * lookupvar(char *name) { struct var *v; for (v = *hashvar(name) ; v ; v = v->next) { if (varequal(v->text, name)) { if (v->flags & VUNSET) return NULL; return strchr(v->text, '=') + 1; } } return NULL; } /* * Search the environment of a builtin command. If the second argument * is nonzero, return the value of a variable even if it hasn't been * exported. */ char * bltinlookup(char *name, int doall) { struct strlist *sp; struct var *v; for (sp = cmdenviron ; sp ; sp = sp->next) { if (varequal(sp->text, name)) return strchr(sp->text, '=') + 1; } for (v = *hashvar(name) ; v ; v = v->next) { if (varequal(v->text, name)) { if ((v->flags & VUNSET) || (!doall && (v->flags & VEXPORT) == 0)) return NULL; return strchr(v->text, '=') + 1; } } return NULL; } /* * Generate a list of exported variables. This routine is used to construct * the third argument to execve when executing a program. */ char ** environment(void) { int nenv; struct var **vpp; struct var *vp; char **env, **ep; nenv = 0; for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { for (vp = *vpp ; vp ; vp = vp->next) if (vp->flags & VEXPORT) nenv++; } ep = env = stalloc((nenv + 1) * sizeof *env); for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { for (vp = *vpp ; vp ; vp = vp->next) if (vp->flags & VEXPORT) *ep++ = vp->text; } *ep = NULL; return env; } /* * Called when a shell procedure is invoked to clear out nonexported * variables. It is also necessary to reallocate variables of with * VSTACK set since these are currently allocated on the stack. */ #ifdef mkinit MKINIT void shprocvar(); SHELLPROC { shprocvar(); } #endif void shprocvar(void) { struct var **vpp; struct var *vp, **prev; for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { for (prev = vpp ; (vp = *prev) != NULL ; ) { if ((vp->flags & VEXPORT) == 0) { *prev = vp->next; if ((vp->flags & VTEXTFIXED) == 0) ckfree(vp->text); if ((vp->flags & VSTRFIXED) == 0) ckfree(vp); } else { if (vp->flags & VSTACK) { vp->text = savestr(vp->text); vp->flags &=~ VSTACK; } prev = &vp->next; } } } initvar(); } /* * Command to list all variables which are set. Currently this command * is invoked from the set command when the set command is called without * any variables. */ int showvarscmd(int argc __unused, char **argv __unused) { struct var **vpp; struct var *vp; const char *s; for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { for (vp = *vpp ; vp ; vp = vp->next) { if (vp->flags & VUNSET) continue; for (s = vp->text; *s != '='; s++) out1c(*s); out1c('='); out1qstr(s + 1); out1c('\n'); } } return 0; } /* * The export and readonly commands. */ int exportcmd(int argc, char **argv) { struct var **vpp; struct var *vp; char *name; char *p; char *cmdname; int ch, values; int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; cmdname = argv[0]; optreset = optind = 1; opterr = 0; values = 0; while ((ch = getopt(argc, argv, "p")) != -1) { switch (ch) { case 'p': values = 1; break; case '?': default: error("unknown option: -%c", optopt); } } argc -= optind; argv += optind; listsetvar(cmdenviron); if (argc != 0) { while ((name = *argptr++) != NULL) { if ((p = strchr(name, '=')) != NULL) { p++; } else { vpp = hashvar(name); for (vp = *vpp ; vp ; vp = vp->next) { if (varequal(vp->text, name)) { vp->flags |= flag; if ((vp->flags & VEXPORT) && localevar(vp->text)) { putenv(vp->text); (void) setlocale(LC_ALL, ""); } goto found; } } } setvar(name, p, flag); found:; } } else { for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { for (vp = *vpp ; vp ; vp = vp->next) { if (vp->flags & flag) { if (values) { out1str(cmdname); out1c(' '); } for (p = vp->text ; *p != '=' ; p++) out1c(*p); if (values && !(vp->flags & VUNSET)) { out1c('='); out1qstr(p + 1); } out1c('\n'); } } } } return 0; } /* * The "local" command. */ int localcmd(int argc __unused, char **argv __unused) { char *name; if (! in_function()) error("Not in a function"); while ((name = *argptr++) != NULL) { mklocal(name); } return 0; } /* * Make a variable a local variable. When a variable is made local, it's * value and flags are saved in a localvar structure. The saved values * will be restored when the shell function returns. We handle the name * "-" as a special case. */ void mklocal(char *name) { struct localvar *lvp; struct var **vpp; struct var *vp; INTOFF; lvp = ckmalloc(sizeof (struct localvar)); if (name[0] == '-' && name[1] == '\0') { lvp->text = ckmalloc(sizeof optlist); memcpy(lvp->text, optlist, sizeof optlist); vp = NULL; } else { vpp = hashvar(name); for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next); if (vp == NULL) { if (strchr(name, '=')) setvareq(savestr(name), VSTRFIXED); else setvar(name, NULL, VSTRFIXED); vp = *vpp; /* the new variable */ lvp->text = NULL; lvp->flags = VUNSET; } else { lvp->text = vp->text; lvp->flags = vp->flags; vp->flags |= VSTRFIXED|VTEXTFIXED; if (strchr(name, '=')) setvareq(savestr(name), 0); } } lvp->vp = vp; lvp->next = localvars; localvars = lvp; INTON; } /* * Called after a function returns. */ void poplocalvars(void) { struct localvar *lvp; struct var *vp; while ((lvp = localvars) != NULL) { localvars = lvp->next; vp = lvp->vp; if (vp == NULL) { /* $- saved */ memcpy(optlist, lvp->text, sizeof optlist); ckfree(lvp->text); } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { (void)unsetvar(vp->text); } else { if ((vp->flags & VTEXTFIXED) == 0) ckfree(vp->text); vp->flags = lvp->flags; vp->text = lvp->text; } ckfree(lvp); } } int setvarcmd(int argc, char **argv) { if (argc <= 2) return unsetcmd(argc, argv); else if (argc == 3) setvar(argv[1], argv[2], 0); else error("List assignment not implemented"); return 0; } /* * The unset builtin command. We unset the function before we unset the * variable to allow a function to be unset when there is a readonly variable * with the same name. */ int unsetcmd(int argc __unused, char **argv __unused) { char **ap; int i; int flg_func = 0; int flg_var = 0; int ret = 0; while ((i = nextopt("vf")) != '\0') { if (i == 'f') flg_func = 1; else flg_var = 1; } if (flg_func == 0 && flg_var == 0) flg_var = 1; for (ap = argptr; *ap ; ap++) { if (flg_func) ret |= unsetfunc(*ap); if (flg_var) ret |= unsetvar(*ap); } return ret; } /* * Unset the specified variable. */ int unsetvar(char *s) { struct var **vpp; struct var *vp; vpp = hashvar(s); for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { if (varequal(vp->text, s)) { if (vp->flags & VREADONLY) return (1); INTOFF; if (*(strchr(vp->text, '=') + 1) != '\0') setvar(s, nullstr, 0); if ((vp->flags & VEXPORT) && localevar(vp->text)) { unsetenv(s); setlocale(LC_ALL, ""); } vp->flags &= ~VEXPORT; vp->flags |= VUNSET; if ((vp->flags & VSTRFIXED) == 0) { if ((vp->flags & VTEXTFIXED) == 0) ckfree(vp->text); *vpp = vp->next; ckfree(vp); } INTON; return (0); } } return (1); } /* * Find the appropriate entry in the hash table from the name. */ STATIC struct var ** hashvar(char *p) { unsigned int hashval; hashval = ((unsigned char) *p) << 4; while (*p && *p != '=') hashval += (unsigned char) *p++; return &vartab[hashval % VTABSIZE]; } /* * Returns true if the two strings specify the same varable. The first * variable name is terminated by '='; the second may be terminated by * either '=' or '\0'. */ STATIC int varequal(char *p, char *q) { while (*p == *q++) { if (*p++ == '=') return 1; } if (*p == '=' && *(q - 1) == '\0') return 1; return 0; } Index: head/contrib/isc-dhcp/client/clparse.c =================================================================== --- head/contrib/isc-dhcp/client/clparse.c (revision 114762) +++ head/contrib/isc-dhcp/client/clparse.c (revision 114763) @@ -1,1173 +1,1173 @@ /* clparse.c Parser for dhclient config and lease files... */ /* * Copyright (c) 1996-2002 Internet Software Consortium. * 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 Internet Software Consortium 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 INTERNET SOFTWARE CONSORTIUM 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 INTERNET SOFTWARE CONSORTIUM 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. * * This software has been written for the Internet Software Consortium * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. * To learn more about the Internet Software Consortium, see * ``http://www.isc.org/''. To learn more about Vixie Enterprises, * see ``http://www.vix.com''. To learn more about Nominum, Inc., see * ``http://www.nominum.com''. */ #ifndef lint static char copyright[] = "$Id: clparse.c,v 1.62.2.3 2002/11/17 02:25:43 dhankins Exp $ Copyright (c) 1996-2002 The Internet Software Consortium. All rights reserved.\n" "$FreeBSD$\n"; #endif /* not lint */ #include "dhcpd.h" static TIME parsed_time; struct client_config top_level_config; -char client_script_name [] = "/sbin/dhclient-script"; +char client_script_name [] = _PATH_DHCLIENT_SCRIPT; u_int32_t default_requested_options [] = { DHO_SUBNET_MASK, DHO_BROADCAST_ADDRESS, DHO_TIME_OFFSET, DHO_ROUTERS, DHO_DOMAIN_NAME, DHO_DOMAIN_NAME_SERVERS, DHO_HOST_NAME, 0 }; /* client-conf-file :== client-declarations END_OF_FILE client-declarations :== | client-declaration | client-declarations client-declaration */ isc_result_t read_client_conf () { struct client_config *config; struct client_state *state; struct interface_info *ip; isc_result_t status; /* Set up the initial dhcp option universe. */ initialize_common_option_spaces (); /* Initialize the top level client configuration. */ memset (&top_level_config, 0, sizeof top_level_config); /* Set some defaults... */ top_level_config.timeout = 60; top_level_config.select_interval = 0; top_level_config.reboot_timeout = 10; top_level_config.retry_interval = 300; top_level_config.backoff_cutoff = 15; top_level_config.initial_interval = 3; top_level_config.bootp_policy = P_ACCEPT; top_level_config.script_name = path_dhclient_script; top_level_config.requested_options = default_requested_options; top_level_config.omapi_port = -1; top_level_config.do_forward_update = 1; group_allocate (&top_level_config.on_receipt, MDL); if (!top_level_config.on_receipt) log_fatal ("no memory for top-level on_receipt group"); group_allocate (&top_level_config.on_transmission, MDL); if (!top_level_config.on_transmission) log_fatal ("no memory for top-level on_transmission group"); status = read_client_conf_file (path_dhclient_conf, (struct interface_info *)0, &top_level_config); if (status != ISC_R_SUCCESS) { ; #ifdef LATER /* Set up the standard name service updater routine. */ parse = (struct parse *)0; status = new_parse (&parse, -1, default_client_config, (sizeof default_client_config) - 1, "default client configuration", 0); if (status != ISC_R_SUCCESS) log_fatal ("can't begin default client config!"); do { token = peek_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) break; parse_client_statement (cfile, (struct interface_info *)0, &top_level_config); } while (1); end_parse (&parse); #endif } /* Set up state and config structures for clients that don't have per-interface configuration statements. */ config = (struct client_config *)0; for (ip = interfaces; ip; ip = ip -> next) { if (!ip -> client) { ip -> client = (struct client_state *) dmalloc (sizeof (struct client_state), MDL); if (!ip -> client) log_fatal ("no memory for client state."); memset (ip -> client, 0, sizeof *(ip -> client)); ip -> client -> interface = ip; } if (!ip -> client -> config) { if (!config) { config = (struct client_config *) dmalloc (sizeof (struct client_config), MDL); if (!config) log_fatal ("no memory for client config."); memcpy (config, &top_level_config, sizeof top_level_config); } ip -> client -> config = config; } } return status; } int read_client_conf_file (const char *name, struct interface_info *ip, struct client_config *client) { int file; struct parse *cfile; const char *val; int token; isc_result_t status; if ((file = open (name, O_RDONLY)) < 0) return uerr2isc (errno); cfile = (struct parse *)0; new_parse (&cfile, file, (char *)0, 0, path_dhclient_conf, 0); do { token = peek_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) break; parse_client_statement (cfile, ip, client); } while (1); token = next_token (&val, (unsigned *)0, cfile); status = (cfile -> warnings_occurred ? ISC_R_BADPARSE : ISC_R_SUCCESS); close (file); end_parse (&cfile); return status; } /* lease-file :== client-lease-statements END_OF_FILE client-lease-statements :== | client-lease-statements LEASE client-lease-statement */ void read_client_leases () { int file; struct parse *cfile; const char *val; int token; /* Open the lease file. If we can't open it, just return - we can safely trust the server to remember our state. */ if ((file = open (path_dhclient_db, O_RDONLY)) < 0) return; cfile = (struct parse *)0; new_parse (&cfile, file, (char *)0, 0, path_dhclient_db, 0); do { token = next_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) break; if (token != LEASE) { log_error ("Corrupt lease file - possible data loss!"); skip_to_semi (cfile); break; } else parse_client_lease_statement (cfile, 0); } while (1); close (file); end_parse (&cfile); } /* client-declaration :== SEND option-decl | DEFAULT option-decl | SUPERSEDE option-decl | PREPEND option-decl | APPEND option-decl | hardware-declaration | REQUEST option-list | REQUIRE option-list | TIMEOUT number | RETRY number | REBOOT number | SELECT_TIMEOUT number | SCRIPT string | VENDOR_SPACE string | interface-declaration | LEASE client-lease-statement | ALIAS client-lease-statement | KEY key-definition */ void parse_client_statement (cfile, ip, config) struct parse *cfile; struct interface_info *ip; struct client_config *config; { int token; const char *val; struct option *option; struct executable_statement *stmt, **p; enum statement_op op; int lose; char *name; struct data_string key_id; enum policy policy; int known; int tmp, i; isc_result_t status; switch (peek_token (&val, (unsigned *)0, cfile)) { case INCLUDE: next_token (&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "filename string expected."); skip_to_semi (cfile); } else { status = read_client_conf_file (val, ip, config); if (status != ISC_R_SUCCESS) parse_warn (cfile, "%s: bad parse.", val); parse_semi (cfile); } return; case KEY: next_token (&val, (unsigned *)0, cfile); if (ip) { /* This may seem arbitrary, but there's a reason for doing it: the authentication key database is not scoped. If we allow the user to declare a key other than in the outer scope, the user is very likely to believe that the key will only be used in that scope. If the user only wants the key to be used on one interface, because it's known that the other interface may be connected to an insecure net and the secret key is considered sensitive, we don't want to lull them into believing they've gotten their way. This is a bit contrived, but people tend not to be entirely rational about security. */ parse_warn (cfile, "key definition not allowed here."); skip_to_semi (cfile); break; } parse_key (cfile); return; /* REQUIRE can either start a policy statement or a comma-seperated list of names of required options. */ case REQUIRE: next_token (&val, (unsigned *)0, cfile); token = peek_token (&val, (unsigned *)0, cfile); if (token == AUTHENTICATION) { policy = P_REQUIRE; goto do_policy; } parse_option_list (cfile, &config -> required_options); return; case IGNORE: next_token (&val, (unsigned *)0, cfile); policy = P_IGNORE; goto do_policy; case ACCEPT: next_token (&val, (unsigned *)0, cfile); policy = P_ACCEPT; goto do_policy; case PREFER: next_token (&val, (unsigned *)0, cfile); policy = P_PREFER; goto do_policy; case DONT: next_token (&val, (unsigned *)0, cfile); policy = P_DONT; goto do_policy; do_policy: token = next_token (&val, (unsigned *)0, cfile); if (token == AUTHENTICATION) { if (policy != P_PREFER && policy != P_REQUIRE && policy != P_DONT) { parse_warn (cfile, "invalid authentication policy."); skip_to_semi (cfile); return; } config -> auth_policy = policy; } else if (token != TOKEN_BOOTP) { if (policy != P_PREFER && policy != P_IGNORE && policy != P_ACCEPT) { parse_warn (cfile, "invalid bootp policy."); skip_to_semi (cfile); return; } config -> bootp_policy = policy; } else { parse_warn (cfile, "expecting a policy type."); skip_to_semi (cfile); return; } break; case OPTION: token = next_token (&val, (unsigned *)0, cfile); token = peek_token (&val, (unsigned *)0, cfile); if (token == SPACE) { if (ip) { parse_warn (cfile, "option space definitions %s", " may not be scoped."); skip_to_semi (cfile); break; } parse_option_space_decl (cfile); return; } option = parse_option_name (cfile, 1, &known); if (!option) return; token = next_token (&val, (unsigned *)0, cfile); if (token != CODE) { parse_warn (cfile, "expecting \"code\" keyword."); skip_to_semi (cfile); free_option (option, MDL); return; } if (ip) { parse_warn (cfile, "option definitions may only appear in %s", "the outermost scope."); skip_to_semi (cfile); free_option (option, MDL); return; } if (!parse_option_code_definition (cfile, option)) free_option (option, MDL); return; case MEDIA: token = next_token (&val, (unsigned *)0, cfile); parse_string_list (cfile, &config -> media, 1); return; case HARDWARE: token = next_token (&val, (unsigned *)0, cfile); if (ip) { parse_hardware_param (cfile, &ip -> hw_address); } else { parse_warn (cfile, "hardware address parameter %s", "not allowed here."); skip_to_semi (cfile); } return; case REQUEST: token = next_token (&val, (unsigned *)0, cfile); if (config -> requested_options == default_requested_options) config -> requested_options = (u_int32_t *)0; parse_option_list (cfile, &config -> requested_options); return; case TIMEOUT: token = next_token (&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> timeout); return; case RETRY: token = next_token (&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> retry_interval); return; case SELECT_TIMEOUT: token = next_token (&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> select_interval); return; case OMAPI: token = next_token (&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != PORT) { parse_warn (cfile, "unexpected omapi subtype: %s", val); skip_to_semi (cfile); return; } token = next_token (&val, (unsigned *)0, cfile); if (token != NUMBER) { parse_warn (cfile, "invalid port number: `%s'", val); skip_to_semi (cfile); return; } tmp = atoi (val); if (tmp < 0 || tmp > 65535) parse_warn (cfile, "invalid omapi port %d.", tmp); else if (config != &top_level_config) parse_warn (cfile, "omapi port only works at top level."); else config -> omapi_port = tmp; parse_semi (cfile); return; case DO_FORWARD_UPDATE: token = next_token (&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (!strcasecmp (val, "on") || !strcasecmp (val, "true")) config -> do_forward_update = 1; else if (!strcasecmp (val, "off") || !strcasecmp (val, "false")) config -> do_forward_update = 0; else { parse_warn (cfile, "expecting boolean value."); skip_to_semi (cfile); return; } parse_semi (cfile); return; case REBOOT: token = next_token (&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> reboot_timeout); return; case BACKOFF_CUTOFF: token = next_token (&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> backoff_cutoff); return; case INITIAL_INTERVAL: token = next_token (&val, (unsigned *)0, cfile); parse_lease_time (cfile, &config -> initial_interval); return; case SCRIPT: token = next_token (&val, (unsigned *)0, cfile); parse_string (cfile, &config -> script_name, (unsigned *)0); return; case VENDOR: token = next_token (&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); if (token != OPTION) { parse_warn (cfile, "expecting 'vendor option space'"); skip_to_semi (cfile); return; } token = next_token (&val, (unsigned *)0, cfile); if (token != SPACE) { parse_warn (cfile, "expecting 'vendor option space'"); skip_to_semi (cfile); return; } token = next_token (&val, (unsigned *)0, cfile); if (!is_identifier (token)) { parse_warn (cfile, "expecting an identifier."); skip_to_semi (cfile); return; } config -> vendor_space_name = dmalloc (strlen (val) + 1, MDL); if (!config -> vendor_space_name) log_fatal ("no memory for vendor option space name."); strcpy (config -> vendor_space_name, val); for (i = 0; i < universe_count; i++) if (!strcmp (universes [i] -> name, config -> vendor_space_name)) break; if (i == universe_count) { log_error ("vendor option space %s not found.", config -> vendor_space_name); } parse_semi (cfile); return; case INTERFACE: token = next_token (&val, (unsigned *)0, cfile); if (ip) parse_warn (cfile, "nested interface declaration."); parse_interface_declaration (cfile, config, (char *)0); return; case PSEUDO: token = next_token (&val, (unsigned *)0, cfile); token = next_token (&val, (unsigned *)0, cfile); name = dmalloc (strlen (val) + 1, MDL); if (!name) log_fatal ("no memory for pseudo interface name"); strcpy (name, val); parse_interface_declaration (cfile, config, name); return; case LEASE: token = next_token (&val, (unsigned *)0, cfile); parse_client_lease_statement (cfile, 1); return; case ALIAS: token = next_token (&val, (unsigned *)0, cfile); parse_client_lease_statement (cfile, 2); return; case REJECT: token = next_token (&val, (unsigned *)0, cfile); parse_reject_statement (cfile, config); return; default: lose = 0; stmt = (struct executable_statement *)0; if (!parse_executable_statement (&stmt, cfile, &lose, context_any)) { if (!lose) { parse_warn (cfile, "expecting a statement."); skip_to_semi (cfile); } } else { struct executable_statement **eptr, *sptr; if (stmt && (stmt -> op == send_option_statement || (stmt -> op == on_statement && (stmt -> data.on.evtypes & ON_TRANSMISSION)))) { eptr = &config -> on_transmission -> statements; if (stmt -> op == on_statement) { sptr = (struct executable_statement *)0; executable_statement_reference (&sptr, stmt -> data.on.statements, MDL); executable_statement_dereference (&stmt, MDL); executable_statement_reference (&stmt, sptr, MDL); executable_statement_dereference (&sptr, MDL); } } else eptr = &config -> on_receipt -> statements; if (stmt) { for (; *eptr; eptr = &(*eptr) -> next) ; executable_statement_reference (eptr, stmt, MDL); } return; } break; } parse_semi (cfile); } /* option-list :== option_name | option_list COMMA option_name */ void parse_option_list (cfile, list) struct parse *cfile; u_int32_t **list; { int ix, i; int token; const char *val; pair p = (pair)0, q, r; ix = 0; do { token = next_token (&val, (unsigned *)0, cfile); if (token == SEMI) break; if (!is_identifier (token)) { parse_warn (cfile, "%s: expected option name.", val); skip_to_semi (cfile); return; } for (i = 0; i < 256; i++) { if (!strcasecmp (dhcp_options [i].name, val)) break; } if (i == 256) { parse_warn (cfile, "%s: expected option name.", val); skip_to_semi (cfile); return; } r = new_pair (MDL); if (!r) log_fatal ("can't allocate pair for option code."); r -> car = (caddr_t)(long)i; r -> cdr = (pair)0; if (p) q -> cdr = r; else p = r; q = r; ++ix; token = next_token (&val, (unsigned *)0, cfile); } while (token == COMMA); if (token != SEMI) { parse_warn (cfile, "expecting semicolon."); skip_to_semi (cfile); return; } /* XXX we can't free the list here, because we may have copied XXX it from an outer config state. */ *list = (u_int32_t *)0; if (ix) { *list = dmalloc ((ix + 1) * sizeof **list, MDL); if (!*list) log_error ("no memory for option list."); else { ix = 0; for (q = p; q; q = q -> cdr) (*list) [ix++] = (u_int32_t)(long)q -> car; (*list) [ix] = 0; } while (p) { q = p -> cdr; free_pair (p, MDL); p = q; } } } /* interface-declaration :== INTERFACE string LBRACE client-declarations RBRACE */ void parse_interface_declaration (cfile, outer_config, name) struct parse *cfile; struct client_config *outer_config; char *name; { int token; const char *val; struct client_state *client, **cp; struct interface_info *ip = (struct interface_info *)0; token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "expecting interface name (in quotes)."); skip_to_semi (cfile); return; } if (!interface_or_dummy (&ip, val)) log_fatal ("Can't allocate interface %s.", val); /* If we were given a name, this is a pseudo-interface. */ if (name) { make_client_state (&client); client -> name = name; client -> interface = ip; for (cp = &ip -> client; *cp; cp = &((*cp) -> next)) ; *cp = client; } else { if (!ip -> client) { make_client_state (&ip -> client); ip -> client -> interface = ip; } client = ip -> client; } if (!client -> config) make_client_config (client, outer_config); ip -> flags &= ~INTERFACE_AUTOMATIC; interfaces_requested = 1; token = next_token (&val, (unsigned *)0, cfile); if (token != LBRACE) { parse_warn (cfile, "expecting left brace."); skip_to_semi (cfile); return; } do { token = peek_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) { parse_warn (cfile, "unterminated interface declaration."); return; } if (token == RBRACE) break; parse_client_statement (cfile, ip, client -> config); } while (1); token = next_token (&val, (unsigned *)0, cfile); } int interface_or_dummy (struct interface_info **pi, const char *name) { struct interface_info *i; struct interface_info *ip = (struct interface_info *)0; isc_result_t status; /* Find the interface (if any) that matches the name. */ for (i = interfaces; i; i = i -> next) { if (!strcmp (i -> name, name)) { interface_reference (&ip, i, MDL); break; } } /* If it's not a real interface, see if it's on the dummy list. */ if (!ip) { for (ip = dummy_interfaces; ip; ip = ip -> next) { if (!strcmp (ip -> name, name)) { interface_reference (&ip, i, MDL); break; } } } /* If we didn't find an interface, make a dummy interface as a placeholder. */ if (!ip) { isc_result_t status; status = interface_allocate (&ip, MDL); if (status != ISC_R_SUCCESS) log_fatal ("Can't record interface %s: %s", name, isc_result_totext (status)); strlcpy (ip -> name, name, IFNAMSIZ); if (dummy_interfaces) { interface_reference (&ip -> next, dummy_interfaces, MDL); interface_dereference (&dummy_interfaces, MDL); } interface_reference (&dummy_interfaces, ip, MDL); } if (pi) status = interface_reference (pi, ip, MDL); interface_dereference (&ip, MDL); if (status != ISC_R_SUCCESS) return 0; return 1; } void make_client_state (state) struct client_state **state; { *state = ((struct client_state *)dmalloc (sizeof **state, MDL)); if (!*state) log_fatal ("no memory for client state\n"); memset (*state, 0, sizeof **state); } void make_client_config (client, config) struct client_state *client; struct client_config *config; { client -> config = (((struct client_config *) dmalloc (sizeof (struct client_config), MDL))); if (!client -> config) log_fatal ("no memory for client config\n"); memcpy (client -> config, config, sizeof *config); if (!clone_group (&client -> config -> on_receipt, config -> on_receipt, MDL) || !clone_group (&client -> config -> on_transmission, config -> on_transmission, MDL)) log_fatal ("no memory for client state groups."); } /* client-lease-statement :== RBRACE client-lease-declarations LBRACE client-lease-declarations :== | client-lease-declaration | client-lease-declarations client-lease-declaration */ void parse_client_lease_statement (cfile, is_static) struct parse *cfile; int is_static; { struct client_lease *lease, *lp, *pl; struct interface_info *ip = (struct interface_info *)0; int token; const char *val; struct client_state *client = (struct client_state *)0; token = next_token (&val, (unsigned *)0, cfile); if (token != LBRACE) { parse_warn (cfile, "expecting left brace."); skip_to_semi (cfile); return; } lease = ((struct client_lease *) dmalloc (sizeof (struct client_lease), MDL)); if (!lease) log_fatal ("no memory for lease.\n"); memset (lease, 0, sizeof *lease); lease -> is_static = is_static; if (!option_state_allocate (&lease -> options, MDL)) log_fatal ("no memory for lease options.\n"); do { token = peek_token (&val, (unsigned *)0, cfile); if (token == END_OF_FILE) { parse_warn (cfile, "unterminated lease declaration."); return; } if (token == RBRACE) break; parse_client_lease_declaration (cfile, lease, &ip, &client); } while (1); token = next_token (&val, (unsigned *)0, cfile); /* If the lease declaration didn't include an interface declaration that we recognized, it's of no use to us. */ if (!ip) { destroy_client_lease (lease); return; } /* Make sure there's a client state structure... */ if (!ip -> client) { make_client_state (&ip -> client); ip -> client -> interface = ip; } if (!client) client = ip -> client; /* If this is an alias lease, it doesn't need to be sorted in. */ if (is_static == 2) { ip -> client -> alias = lease; return; } /* The new lease may supersede a lease that's not the active lease but is still on the lease list, so scan the lease list looking for a lease with the same address, and if we find it, toss it. */ pl = (struct client_lease *)0; for (lp = client -> leases; lp; lp = lp -> next) { if (lp -> address.len == lease -> address.len && !memcmp (lp -> address.iabuf, lease -> address.iabuf, lease -> address.len)) { if (pl) pl -> next = lp -> next; else client -> leases = lp -> next; destroy_client_lease (lp); break; } } /* If this is a preloaded lease, just put it on the list of recorded leases - don't make it the active lease. */ if (is_static) { lease -> next = client -> leases; client -> leases = lease; return; } /* The last lease in the lease file on a particular interface is the active lease for that interface. Of course, we don't know what the last lease in the file is until we've parsed the whole file, so at this point, we assume that the lease we just parsed is the active lease for its interface. If there's already an active lease for the interface, and this lease is for the same ip address, then we just toss the old active lease and replace it with this one. If this lease is for a different address, then if the old active lease has expired, we dump it; if not, we put it on the list of leases for this interface which are still valid but no longer active. */ if (client -> active) { if (client -> active -> expiry < cur_time) destroy_client_lease (client -> active); else if (client -> active -> address.len == lease -> address.len && !memcmp (client -> active -> address.iabuf, lease -> address.iabuf, lease -> address.len)) destroy_client_lease (client -> active); else { client -> active -> next = client -> leases; client -> leases = client -> active; } } client -> active = lease; /* phew. */ } /* client-lease-declaration :== BOOTP | INTERFACE string | FIXED_ADDR ip_address | FILENAME string | SERVER_NAME string | OPTION option-decl | RENEW time-decl | REBIND time-decl | EXPIRE time-decl | KEY id */ void parse_client_lease_declaration (cfile, lease, ipp, clientp) struct parse *cfile; struct client_lease *lease; struct interface_info **ipp; struct client_state **clientp; { int token; const char *val; char *t, *n; struct interface_info *ip; struct option_cache *oc; struct client_state *client = (struct client_state *)0; struct data_string key_id; switch (next_token (&val, (unsigned *)0, cfile)) { case KEY: token = next_token (&val, (unsigned *)0, cfile); if (token != STRING && !is_identifier (token)) { parse_warn (cfile, "expecting key name."); skip_to_semi (cfile); break; } if (omapi_auth_key_lookup_name (&lease -> key, val) != ISC_R_SUCCESS) parse_warn (cfile, "unknown key %s", val); parse_semi (cfile); break; case TOKEN_BOOTP: lease -> is_bootp = 1; break; case INTERFACE: token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "expecting interface name (in quotes)."); skip_to_semi (cfile); break; } interface_or_dummy (ipp, val); break; case NAME: token = next_token (&val, (unsigned *)0, cfile); ip = *ipp; if (!ip) { parse_warn (cfile, "state name precedes interface."); break; } for (client = ip -> client; client; client = client -> next) if (client -> name && !strcmp (client -> name, val)) break; if (!client) parse_warn (cfile, "lease specified for unknown pseudo."); *clientp = client; break; case FIXED_ADDR: if (!parse_ip_addr (cfile, &lease -> address)) return; break; case MEDIUM: parse_string_list (cfile, &lease -> medium, 0); return; case FILENAME: parse_string (cfile, &lease -> filename, (unsigned *)0); return; case SERVER_NAME: parse_string (cfile, &lease -> server_name, (unsigned *)0); return; case RENEW: lease -> renewal = parse_date (cfile); return; case REBIND: lease -> rebind = parse_date (cfile); return; case EXPIRE: lease -> expiry = parse_date (cfile); return; case OPTION: oc = (struct option_cache *)0; if (parse_option_decl (&oc, cfile)) { save_option (oc -> option -> universe, lease -> options, oc); option_cache_dereference (&oc, MDL); } return; default: parse_warn (cfile, "expecting lease declaration."); skip_to_semi (cfile); break; } token = next_token (&val, (unsigned *)0, cfile); if (token != SEMI) { parse_warn (cfile, "expecting semicolon."); skip_to_semi (cfile); } } void parse_string_list (cfile, lp, multiple) struct parse *cfile; struct string_list **lp; int multiple; { int token; const char *val; struct string_list *cur, *tmp; /* Find the last medium in the media list. */ if (*lp) { for (cur = *lp; cur -> next; cur = cur -> next) ; } else { cur = (struct string_list *)0; } do { token = next_token (&val, (unsigned *)0, cfile); if (token != STRING) { parse_warn (cfile, "Expecting media options."); skip_to_semi (cfile); return; } tmp = ((struct string_list *) dmalloc (strlen (val) + sizeof (struct string_list), MDL)); if (!tmp) log_fatal ("no memory for string list entry."); strcpy (tmp -> string, val); tmp -> next = (struct string_list *)0; /* Store this medium at the end of the media list. */ if (cur) cur -> next = tmp; else *lp = tmp; cur = tmp; token = next_token (&val, (unsigned *)0, cfile); } while (multiple && token == COMMA); if (token != SEMI) { parse_warn (cfile, "expecting semicolon."); skip_to_semi (cfile); } } void parse_reject_statement (cfile, config) struct parse *cfile; struct client_config *config; { int token; const char *val; struct iaddr addr; struct iaddrlist *list; do { if (!parse_ip_addr (cfile, &addr)) { parse_warn (cfile, "expecting IP address."); skip_to_semi (cfile); return; } list = (struct iaddrlist *)dmalloc (sizeof (struct iaddrlist), MDL); if (!list) log_fatal ("no memory for reject list!"); list -> addr = addr; list -> next = config -> reject_list; config -> reject_list = list; token = next_token (&val, (unsigned *)0, cfile); } while (token == COMMA); if (token != SEMI) { parse_warn (cfile, "expecting semicolon."); skip_to_semi (cfile); } } /* allow-deny-keyword :== BOOTP | BOOTING | DYNAMIC_BOOTP | UNKNOWN_CLIENTS */ int parse_allow_deny (oc, cfile, flag) struct option_cache **oc; struct parse *cfile; int flag; { enum dhcp_token token; const char *val; unsigned char rf = flag; struct expression *data = (struct expression *)0; int status; parse_warn (cfile, "allow/deny/ignore not permitted here."); skip_to_semi (cfile); return 0; } Index: head/contrib/tar/src/buffer.c =================================================================== --- head/contrib/tar/src/buffer.c (revision 114762) +++ head/contrib/tar/src/buffer.c (revision 114763) @@ -1,1613 +1,1619 @@ /* Buffer management for tar. Copyright 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001 Free Software Foundation, Inc. Written by John Gilmore, on 1985-08-25. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $FreeBSD$ */ #include "system.h" #include +#if __FreeBSD__ +# include +#else +# define _PATH_BSHELL "/bin/sh" +#endif + #if MSDOS # include #endif #if XENIX # include #endif #include #include #include #include "common.h" #include "rmt.h" #define PREAD 0 /* read file descriptor from pipe() */ #define PWRITE 1 /* write file descriptor from pipe() */ /* Number of retries before giving up on read. */ #define READ_ERROR_MAX 10 /* Globbing pattern to append to volume label if initial match failed. */ #define VOLUME_LABEL_APPEND " Volume [1-9]*" /* Variables. */ static tarlong prev_written; /* bytes written on previous volumes */ static tarlong bytes_written; /* bytes written on this volume */ /* FIXME: The following variables should ideally be static to this module. However, this cannot be done yet. The cleanup continues! */ union block *record_start; /* start of record of archive */ union block *record_end; /* last+1 block of archive record */ union block *current_block; /* current block of archive */ enum access_mode access_mode; /* how do we handle the archive */ off_t records_read; /* number of records read from this archive */ off_t records_written; /* likewise, for records written */ static struct stat archive_stat; /* stat block for archive file */ static off_t record_start_block; /* block ordinal at record_start */ /* Where we write list messages (not errors, not interactions) to. Stdout unless we're writing a pipe, in which case stderr. */ FILE *stdlis; static void backspace_output PARAMS ((void)); static int new_volume PARAMS ((enum access_mode)); static void archive_write_error PARAMS ((ssize_t)) __attribute__ ((noreturn)); static void archive_read_error PARAMS ((void)); #if !MSDOS /* Obnoxious test to see if dimwit is trying to dump the archive. */ dev_t ar_dev; ino_t ar_ino; #endif /* PID of child program, if compress_option or remote archive access. */ static pid_t child_pid; /* Error recovery stuff */ static int read_error_count; /* Have we hit EOF yet? */ static int hit_eof; /* Checkpointing counter */ static int checkpoint; /* We're reading, but we just read the last block and its time to update. */ /* As least EXTERN like this one as possible. FIXME! */ extern int time_to_start_writing; int file_to_switch_to = -1; /* if remote update, close archive, and use this descriptor to write to */ static int volno = 1; /* which volume of a multi-volume tape we're on */ static int global_volno = 1; /* volume number to print in external messages */ static pid_t grandchild_pid; /* The pointer save_name, which is set in function dump_file() of module create.c, points to the original long filename instead of the new, shorter mangled name that is set in start_header() of module create.c. The pointer save_name is only used in multi-volume mode when the file being processed is non-sparse; if a file is split between volumes, the save_name is used in generating the LF_MULTIVOL record on the second volume. (From Pierce Cantrell, 1991-08-13.) */ char *save_name; /* name of the file we are currently writing */ off_t save_totsize; /* total size of file we are writing, only valid if save_name is nonzero */ off_t save_sizeleft; /* where we are in the file we are writing, only valid if save_name is nonzero */ bool write_archive_to_stdout; /* Used by flush_read and flush_write to store the real info about saved names. */ static char *real_s_name; static off_t real_s_totsize; static off_t real_s_sizeleft; /* Functions. */ void print_total_written (void) { tarlong written = prev_written + bytes_written; char bytes[sizeof (tarlong) * CHAR_BIT]; char abbr[LONGEST_HUMAN_READABLE + 1]; char rate[LONGEST_HUMAN_READABLE + 1]; double seconds; #if HAVE_CLOCK_GETTIME struct timespec now; if (clock_gettime (CLOCK_REALTIME, &now) == 0) seconds = ((now.tv_sec - start_timespec.tv_sec) + (now.tv_nsec - start_timespec.tv_nsec) / 1e9); else #endif seconds = time (0) - start_time; sprintf (bytes, TARLONG_FORMAT, written); /* Amanda 2.4.1p1 looks for "Total bytes written: [0-9][0-9]*". */ fprintf (stderr, _("Total bytes written: %s (%sB, %sB/s)\n"), bytes, human_readable ((uintmax_t) written, abbr, 1, -1024), (0 < seconds && written / seconds < (uintmax_t) -1 ? human_readable ((uintmax_t) (written / seconds), rate, 1, -1024) : "?")); } /* Compute and return the block ordinal at current_block. */ off_t current_block_ordinal (void) { return record_start_block + (current_block - record_start); } /* If the EOF flag is set, reset it, as well as current_block, etc. */ void reset_eof (void) { if (hit_eof) { hit_eof = 0; current_block = record_start; record_end = record_start + blocking_factor; access_mode = ACCESS_WRITE; } } /* Return the location of the next available input or output block. Return zero for EOF. Once we have returned zero, we just keep returning it, to avoid accidentally going on to the next file on the tape. */ union block * find_next_block (void) { if (current_block == record_end) { if (hit_eof) return 0; flush_archive (); if (current_block == record_end) { hit_eof = 1; return 0; } } return current_block; } /* Indicate that we have used all blocks up thru BLOCK. FIXME: should the arg have an off-by-1? */ void set_next_block_after (union block *block) { while (block >= current_block) current_block++; /* Do *not* flush the archive here. If we do, the same argument to set_next_block_after could mean the next block (if the input record is exactly one block long), which is not what is intended. */ if (current_block > record_end) abort (); } /* Return the number of bytes comprising the space between POINTER through the end of the current buffer of blocks. This space is available for filling with data, or taking data from. POINTER is usually (but not always) the result previous find_next_block call. */ size_t available_space_after (union block *pointer) { return record_end->buffer - pointer->buffer; } /* Close file having descriptor FD, and abort if close unsuccessful. */ static void xclose (int fd) { if (close (fd) != 0) close_error (_("(pipe)")); } /* Duplicate file descriptor FROM into becoming INTO. INTO is closed first and has to be the next available slot. */ static void xdup2 (int from, int into) { if (from != into) { int status = close (into); if (status != 0 && errno != EBADF) { int e = errno; FATAL_ERROR ((0, e, _("Cannot close"))); } status = dup (from); if (status != into) { if (status < 0) { int e = errno; FATAL_ERROR ((0, e, _("Cannot dup"))); } abort (); } xclose (from); } } #if MSDOS /* Set ARCHIVE for writing, then compressing an archive. */ static void child_open_for_compress (void) { FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives"))); } /* Set ARCHIVE for uncompressing, then reading an archive. */ static void child_open_for_uncompress (void) { FATAL_ERROR ((0, 0, _("Cannot use compressed or remote archives"))); } #else /* not MSDOS */ /* Return nonzero if NAME is the name of a regular file, or if the file does not exist (so it would be created as a regular file). */ static int is_regular_file (const char *name) { struct stat stbuf; if (stat (name, &stbuf) == 0) return S_ISREG (stbuf.st_mode); else return errno == ENOENT; } static ssize_t write_archive_buffer (void) { ssize_t status; ssize_t written = 0; while (0 <= (status = rmtwrite (archive, record_start->buffer + written, record_size - written))) { written += status; if (written == record_size || _isrmt (archive) || ! (S_ISFIFO (archive_stat.st_mode) || S_ISSOCK (archive_stat.st_mode))) break; } return written ? written : status; } /* Set ARCHIVE for writing, then compressing an archive. */ static void child_open_for_compress (void) { int parent_pipe[2]; int child_pipe[2]; int wait_status; xpipe (parent_pipe); child_pid = xfork (); if (child_pid > 0) { /* The parent tar is still here! Just clean up. */ archive = parent_pipe[PWRITE]; xclose (parent_pipe[PREAD]); return; } /* The new born child tar is here! */ program_name = _("tar (child)"); xdup2 (parent_pipe[PREAD], STDIN_FILENO); xclose (parent_pipe[PWRITE]); /* Check if we need a grandchild tar. This happens only if either: a) we are writing stdout: to force reblocking; b) the file is to be accessed by rmt: compressor doesn't know how; c) the file is not a plain file. */ if (strcmp (archive_name_array[0], "-") != 0 && !_remdev (archive_name_array[0]) && is_regular_file (archive_name_array[0])) { if (backup_option) maybe_backup_file (archive_name_array[0], 1); /* We don't need a grandchild tar. Open the archive and launch the compressor. */ archive = creat (archive_name_array[0], MODE_RW); if (archive < 0) { int saved_errno = errno; if (backup_option) undo_last_backup (); errno = saved_errno; open_fatal (archive_name_array[0]); } xdup2 (archive, STDOUT_FILENO); execlp (use_compress_program_option, use_compress_program_option, (char *) 0); exec_fatal (use_compress_program_option); } /* We do need a grandchild tar. */ xpipe (child_pipe); grandchild_pid = xfork (); if (grandchild_pid == 0) { /* The newborn grandchild tar is here! Launch the compressor. */ program_name = _("tar (grandchild)"); xdup2 (child_pipe[PWRITE], STDOUT_FILENO); xclose (child_pipe[PREAD]); execlp (use_compress_program_option, use_compress_program_option, (char *) 0); exec_fatal (use_compress_program_option); } /* The child tar is still here! */ /* Prepare for reblocking the data from the compressor into the archive. */ xdup2 (child_pipe[PREAD], STDIN_FILENO); xclose (child_pipe[PWRITE]); if (strcmp (archive_name_array[0], "-") == 0) archive = STDOUT_FILENO; else { archive = rmtcreat (archive_name_array[0], MODE_RW, rsh_command_option); if (archive < 0) open_fatal (archive_name_array[0]); } /* Let's read out of the stdin pipe and write an archive. */ while (1) { ssize_t status = 0; char *cursor; size_t length; /* Assemble a record. */ for (length = 0, cursor = record_start->buffer; length < record_size; length += status, cursor += status) { size_t size = record_size - length; if (size < BLOCKSIZE) size = BLOCKSIZE; status = safe_read (STDIN_FILENO, cursor, size); if (status <= 0) break; } if (status < 0) read_fatal (use_compress_program_option); /* Copy the record. */ if (status == 0) { /* We hit the end of the file. Write last record at full length, as the only role of the grandchild is doing proper reblocking. */ if (length > 0) { memset (record_start->buffer + length, 0, record_size - length); status = write_archive_buffer (); if (status != record_size) archive_write_error (status); } /* There is nothing else to read, break out. */ break; } status = write_archive_buffer (); if (status != record_size) archive_write_error (status); } #if 0 close_archive (); #endif /* Propagate any failure of the grandchild back to the parent. */ while (waitpid (grandchild_pid, &wait_status, 0) == -1) if (errno != EINTR) { waitpid_error (use_compress_program_option); break; } if (WIFSIGNALED (wait_status)) { kill (child_pid, WTERMSIG (wait_status)); exit_status = TAREXIT_FAILURE; } else if (WEXITSTATUS (wait_status) != 0) exit_status = WEXITSTATUS (wait_status); exit (exit_status); } static void sig_propagate(int sig) { kill (grandchild_pid, sig); exit (TAREXIT_FAILURE); } /* Set ARCHIVE for uncompressing, then reading an archive. */ static void child_open_for_uncompress (void) { int parent_pipe[2]; int child_pipe[2]; int wait_status; xpipe (parent_pipe); child_pid = xfork (); if (child_pid > 0) { /* The parent tar is still here! Just clean up. */ read_full_records_option = 1; archive = parent_pipe[PREAD]; xclose (parent_pipe[PWRITE]); return; } /* The newborn child tar is here! */ program_name = _("tar (child)"); xdup2 (parent_pipe[PWRITE], STDOUT_FILENO); xclose (parent_pipe[PREAD]); /* Check if we need a grandchild tar. This happens only if either: a) we're reading stdin: to force unblocking; b) the file is to be accessed by rmt: compressor doesn't know how; c) the file is not a plain file. */ if (strcmp (archive_name_array[0], "-") != 0 && !_remdev (archive_name_array[0]) && is_regular_file (archive_name_array[0])) { /* We don't need a grandchild tar. Open the archive and lauch the uncompressor. */ archive = open (archive_name_array[0], O_RDONLY | O_BINARY, MODE_RW); if (archive < 0) open_fatal (archive_name_array[0]); xdup2 (archive, STDIN_FILENO); execlp (use_compress_program_option, use_compress_program_option, "-d", (char *) 0); exec_fatal (use_compress_program_option); } /* We do need a grandchild tar. */ xpipe (child_pipe); grandchild_pid = xfork (); if (grandchild_pid == 0) { /* The newborn grandchild tar is here! Launch the uncompressor. */ program_name = _("tar (grandchild)"); xdup2 (child_pipe[PREAD], STDIN_FILENO); xclose (child_pipe[PWRITE]); execlp (use_compress_program_option, use_compress_program_option, "-d", (char *) 0); exec_fatal (use_compress_program_option); } /* The child tar is still here! */ signal (SIGTERM, sig_propagate); /* Prepare for unblocking the data from the archive into the uncompressor. */ xdup2 (child_pipe[PWRITE], STDOUT_FILENO); xclose (child_pipe[PREAD]); if (strcmp (archive_name_array[0], "-") == 0) archive = STDIN_FILENO; else archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY, MODE_RW, rsh_command_option); if (archive < 0) open_fatal (archive_name_array[0]); /* Let's read the archive and pipe it into stdout. */ while (1) { char *cursor; size_t maximum; size_t count; ssize_t status; read_error_count = 0; error_loop: status = rmtread (archive, record_start->buffer, record_size); if (status < 0) { archive_read_error (); goto error_loop; } if (status == 0) break; cursor = record_start->buffer; maximum = status; while (maximum) { count = maximum < BLOCKSIZE ? maximum : BLOCKSIZE; if (full_write (STDOUT_FILENO, cursor, count) != count) write_error (use_compress_program_option); cursor += count; maximum -= count; } } xclose (STDOUT_FILENO); #if 0 close_archive (); #endif /* Propagate any failure of the grandchild back to the parent. */ while (waitpid (grandchild_pid, &wait_status, 0) == -1) if (errno != EINTR) { waitpid_error (use_compress_program_option); break; } if (WIFSIGNALED (wait_status)) { kill (child_pid, WTERMSIG (wait_status)); exit_status = TAREXIT_FAILURE; } else if (WEXITSTATUS (wait_status) != 0) exit_status = WEXITSTATUS (wait_status); exit (exit_status); } #endif /* not MSDOS */ /* Check the LABEL block against the volume label, seen as a globbing pattern. Return true if the pattern matches. In case of failure, retry matching a volume sequence number before giving up in multi-volume mode. */ static int check_label_pattern (union block *label) { char *string; int result; if (! memchr (label->header.name, '\0', sizeof label->header.name)) return 0; if (fnmatch (volume_label_option, label->header.name, 0) == 0) return 1; if (!multi_volume_option) return 0; string = xmalloc (strlen (volume_label_option) + sizeof VOLUME_LABEL_APPEND + 1); strcpy (string, volume_label_option); strcat (string, VOLUME_LABEL_APPEND); result = fnmatch (string, label->header.name, 0) == 0; free (string); return result; } /* Open an archive file. The argument specifies whether we are reading or writing, or both. */ void open_archive (enum access_mode wanted_access) { int backed_up_flag = 0; stdlis = to_stdout_option ? stderr : stdout; if (record_size == 0) FATAL_ERROR ((0, 0, _("Invalid value for record_size"))); if (archive_names == 0) FATAL_ERROR ((0, 0, _("No archive name given"))); current_file_name = 0; current_link_name = 0; save_name = 0; real_s_name = 0; if (multi_volume_option) { if (verify_option) FATAL_ERROR ((0, 0, _("Cannot verify multi-volume archives"))); record_start = valloc (record_size + (2 * BLOCKSIZE)); if (record_start) record_start += 2; } else record_start = valloc (record_size); if (!record_start) FATAL_ERROR ((0, 0, _("Cannot allocate memory for blocking factor %d"), blocking_factor)); current_block = record_start; record_end = record_start + blocking_factor; /* When updating the archive, we start with reading. */ access_mode = wanted_access == ACCESS_UPDATE ? ACCESS_READ : wanted_access; if (use_compress_program_option) { if (multi_volume_option) FATAL_ERROR ((0, 0, _("Cannot use multi-volume compressed archives"))); if (verify_option) FATAL_ERROR ((0, 0, _("Cannot verify compressed archives"))); switch (wanted_access) { case ACCESS_READ: child_open_for_uncompress (); break; case ACCESS_WRITE: child_open_for_compress (); break; case ACCESS_UPDATE: FATAL_ERROR ((0, 0, _("Cannot update compressed archives"))); break; } if (wanted_access == ACCESS_WRITE && strcmp (archive_name_array[0], "-") == 0) stdlis = stderr; } else if (strcmp (archive_name_array[0], "-") == 0) { read_full_records_option = 1; /* could be a pipe, be safe */ if (verify_option) FATAL_ERROR ((0, 0, _("Cannot verify stdin/stdout archive"))); switch (wanted_access) { case ACCESS_READ: archive = STDIN_FILENO; break; case ACCESS_WRITE: archive = STDOUT_FILENO; stdlis = stderr; break; case ACCESS_UPDATE: archive = STDIN_FILENO; stdlis = stderr; write_archive_to_stdout = 1; break; } } else if (verify_option) archive = rmtopen (archive_name_array[0], O_RDWR | O_CREAT | O_BINARY, MODE_RW, rsh_command_option); else switch (wanted_access) { case ACCESS_READ: archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY, MODE_RW, rsh_command_option); break; case ACCESS_WRITE: if (backup_option) { maybe_backup_file (archive_name_array[0], 1); backed_up_flag = 1; } archive = rmtcreat (archive_name_array[0], MODE_RW, rsh_command_option); break; case ACCESS_UPDATE: archive = rmtopen (archive_name_array[0], O_RDWR | O_CREAT | O_BINARY, MODE_RW, rsh_command_option); break; } if (archive < 0 || (! _isrmt (archive) && fstat (archive, &archive_stat) < 0)) { int saved_errno = errno; if (backed_up_flag) undo_last_backup (); errno = saved_errno; open_fatal (archive_name_array[0]); } #if !MSDOS /* Detect if outputting to "/dev/null". */ { static char const dev_null[] = "/dev/null"; struct stat dev_null_stat; dev_null_output = (strcmp (archive_name_array[0], dev_null) == 0 || (! _isrmt (archive) && S_ISCHR (archive_stat.st_mode) && stat (dev_null, &dev_null_stat) == 0 && archive_stat.st_dev == dev_null_stat.st_dev && archive_stat.st_ino == dev_null_stat.st_ino)); } if (!_isrmt (archive) && S_ISREG (archive_stat.st_mode)) { ar_dev = archive_stat.st_dev; ar_ino = archive_stat.st_ino; } else ar_dev = 0; #endif /* not MSDOS */ #if MSDOS setmode (archive, O_BINARY); #endif switch (wanted_access) { case ACCESS_UPDATE: records_written = 0; case ACCESS_READ: records_read = 0; record_end = record_start; /* set up for 1st record = # 0 */ find_next_block (); /* read it in, check for EOF */ if (volume_label_option) { union block *label = find_next_block (); if (!label) FATAL_ERROR ((0, 0, _("Archive not labeled to match %s"), quote (volume_label_option))); if (!check_label_pattern (label)) FATAL_ERROR ((0, 0, _("Volume %s does not match %s"), quote_n (0, label->header.name), quote_n (1, volume_label_option))); } break; case ACCESS_WRITE: records_written = 0; if (volume_label_option) { memset (record_start, 0, BLOCKSIZE); if (multi_volume_option) sprintf (record_start->header.name, "%s Volume 1", volume_label_option); else strcpy (record_start->header.name, volume_label_option); assign_string (¤t_file_name, record_start->header.name); record_start->header.typeflag = GNUTYPE_VOLHDR; TIME_TO_CHARS (start_time, record_start->header.mtime); finish_header (record_start); #if 0 current_block++; #endif } break; } } /* Perform a write to flush the buffer. */ void flush_write (void) { int copy_back; ssize_t status; if (checkpoint_option && !(++checkpoint % 10)) WARN ((0, 0, _("Write checkpoint %d"), checkpoint)); if (tape_length_option && tape_length_option <= bytes_written) { errno = ENOSPC; status = 0; } else if (dev_null_output) status = record_size; else status = write_archive_buffer (); if (status != record_size && !multi_volume_option) archive_write_error (status); if (status > 0) { records_written++; bytes_written += status; } if (status == record_size) { if (multi_volume_option) { char *cursor; if (!save_name) { assign_string (&real_s_name, 0); real_s_totsize = 0; real_s_sizeleft = 0; return; } cursor = save_name + FILESYSTEM_PREFIX_LEN (save_name); while (ISSLASH (*cursor)) cursor++; assign_string (&real_s_name, cursor); real_s_totsize = save_totsize; real_s_sizeleft = save_sizeleft; } return; } /* We're multivol. Panic if we didn't get the right kind of response. */ /* ENXIO is for the UNIX PC. */ if (status < 0 && errno != ENOSPC && errno != EIO && errno != ENXIO) archive_write_error (status); /* If error indicates a short write, we just move to the next tape. */ if (!new_volume (ACCESS_WRITE)) return; if (totals_option) prev_written += bytes_written; bytes_written = 0; if (volume_label_option && real_s_name) { copy_back = 2; record_start -= 2; } else if (volume_label_option || real_s_name) { copy_back = 1; record_start--; } else copy_back = 0; if (volume_label_option) { memset (record_start, 0, BLOCKSIZE); sprintf (record_start->header.name, "%s Volume %d", volume_label_option, volno); TIME_TO_CHARS (start_time, record_start->header.mtime); record_start->header.typeflag = GNUTYPE_VOLHDR; finish_header (record_start); } if (real_s_name) { int tmp; if (volume_label_option) record_start++; memset (record_start, 0, BLOCKSIZE); /* FIXME: Michael P Urban writes: [a long name file] is being written when a new volume rolls around [...] Looks like the wrong value is being preserved in real_s_name, though. */ strcpy (record_start->header.name, real_s_name); record_start->header.typeflag = GNUTYPE_MULTIVOL; OFF_TO_CHARS (real_s_sizeleft, record_start->header.size); OFF_TO_CHARS (real_s_totsize - real_s_sizeleft, record_start->oldgnu_header.offset); tmp = verbose_option; verbose_option = 0; finish_header (record_start); verbose_option = tmp; if (volume_label_option) record_start--; } status = write_archive_buffer (); if (status != record_size) archive_write_error (status); bytes_written += status; if (copy_back) { record_start += copy_back; memcpy (current_block, record_start + blocking_factor - copy_back, copy_back * BLOCKSIZE); current_block += copy_back; if (real_s_sizeleft >= copy_back * BLOCKSIZE) real_s_sizeleft -= copy_back * BLOCKSIZE; else if ((real_s_sizeleft + BLOCKSIZE - 1) / BLOCKSIZE <= copy_back) assign_string (&real_s_name, 0); else { char *cursor = save_name + FILESYSTEM_PREFIX_LEN (save_name); while (ISSLASH (*cursor)) cursor++; assign_string (&real_s_name, cursor); real_s_sizeleft = save_sizeleft; real_s_totsize = save_totsize; } copy_back = 0; } } /* Handle write errors on the archive. Write errors are always fatal. Hitting the end of a volume does not cause a write error unless the write was the first record of the volume. */ static void archive_write_error (ssize_t status) { /* It might be useful to know how much was written before the error occurred. */ if (totals_option) { int e = errno; print_total_written (); errno = e; } write_fatal_details (*archive_name_cursor, status, record_size); } /* Handle read errors on the archive. If the read should be retried, return to the caller. */ static void archive_read_error (void) { read_error (*archive_name_cursor); if (record_start_block == 0) FATAL_ERROR ((0, 0, _("At beginning of tape, quitting now"))); /* Read error in mid archive. We retry up to READ_ERROR_MAX times and then give up on reading the archive. */ if (read_error_count++ > READ_ERROR_MAX) FATAL_ERROR ((0, 0, _("Too many errors, quitting"))); return; } /* Perform a read to flush the buffer. */ void flush_read (void) { ssize_t status; /* result from system call */ size_t left; /* bytes left */ char *more; /* pointer to next byte to read */ if (checkpoint_option && !(++checkpoint % 10)) WARN ((0, 0, _("Read checkpoint %d"), checkpoint)); /* Clear the count of errors. This only applies to a single call to flush_read. */ read_error_count = 0; /* clear error count */ if (write_archive_to_stdout && record_start_block != 0) { archive = STDOUT_FILENO; status = write_archive_buffer (); archive = STDIN_FILENO; if (status != record_size) archive_write_error (status); } if (multi_volume_option) { if (save_name) { char *cursor = save_name + FILESYSTEM_PREFIX_LEN (save_name); while (ISSLASH (*cursor)) cursor++; assign_string (&real_s_name, cursor); real_s_sizeleft = save_sizeleft; real_s_totsize = save_totsize; } else { assign_string (&real_s_name, 0); real_s_totsize = 0; real_s_sizeleft = 0; } } error_loop: status = rmtread (archive, record_start->buffer, record_size); if (status == record_size) { records_read++; return; } if ((status == 0 || (status < 0 && errno == ENOSPC) || (status > 0 && !read_full_records_option)) && multi_volume_option) { union block *cursor; try_volume: switch (subcommand_option) { case APPEND_SUBCOMMAND: case CAT_SUBCOMMAND: case UPDATE_SUBCOMMAND: if (!new_volume (ACCESS_UPDATE)) return; break; default: if (!new_volume (ACCESS_READ)) return; break; } vol_error: status = rmtread (archive, record_start->buffer, record_size); if (status < 0) { archive_read_error (); goto vol_error; } if (status != record_size) goto short_read; cursor = record_start; if (cursor->header.typeflag == GNUTYPE_VOLHDR) { if (volume_label_option) { if (!check_label_pattern (cursor)) { WARN ((0, 0, _("Volume %s does not match %s"), quote_n (0, cursor->header.name), quote_n (1, volume_label_option))); volno--; global_volno--; goto try_volume; } } if (verbose_option) fprintf (stdlis, _("Reading %s\n"), quote (cursor->header.name)); cursor++; } else if (volume_label_option) WARN ((0, 0, _("WARNING: No volume header"))); if (real_s_name) { uintmax_t s1, s2; if (cursor->header.typeflag != GNUTYPE_MULTIVOL || strcmp (cursor->header.name, real_s_name)) { WARN ((0, 0, _("%s is not continued on this volume"), quote (real_s_name))); volno--; global_volno--; goto try_volume; } s1 = UINTMAX_FROM_HEADER (cursor->header.size); s2 = UINTMAX_FROM_HEADER (cursor->oldgnu_header.offset); if (real_s_totsize != s1 + s2 || s1 + s2 < s2) { char totsizebuf[UINTMAX_STRSIZE_BOUND]; char s1buf[UINTMAX_STRSIZE_BOUND]; char s2buf[UINTMAX_STRSIZE_BOUND]; WARN ((0, 0, _("%s is the wrong size (%s != %s + %s)"), quote (cursor->header.name), STRINGIFY_BIGINT (save_totsize, totsizebuf), STRINGIFY_BIGINT (s1, s1buf), STRINGIFY_BIGINT (s2, s2buf))); volno--; global_volno--; goto try_volume; } if (real_s_totsize - real_s_sizeleft != OFF_FROM_HEADER (cursor->oldgnu_header.offset)) { WARN ((0, 0, _("This volume is out of sequence"))); volno--; global_volno--; goto try_volume; } cursor++; } current_block = cursor; records_read++; return; } else if (status < 0) { archive_read_error (); goto error_loop; /* try again */ } short_read: more = record_start->buffer + status; left = record_size - status; while (left % BLOCKSIZE != 0 || (left && status && read_full_records_option)) { if (status) while ((status = rmtread (archive, more, left)) < 0) archive_read_error (); if (status == 0) break; if (! read_full_records_option) FATAL_ERROR ((0, 0, _("Unaligned block (%lu bytes) in archive"), (unsigned long) (record_size - left))); /* User warned us about this. Fix up. */ left -= status; more += status; } /* FIXME: for size=0, multi-volume support. On the first record, warn about the problem. */ if (!read_full_records_option && verbose_option && record_start_block == 0 && status > 0) WARN ((0, 0, _("Record size = %lu blocks"), (unsigned long) ((record_size - left) / BLOCKSIZE))); record_end = record_start + (record_size - left) / BLOCKSIZE; records_read++; } /* Flush the current buffer to/from the archive. */ void flush_archive (void) { record_start_block += record_end - record_start; current_block = record_start; record_end = record_start + blocking_factor; if (access_mode == ACCESS_READ && time_to_start_writing) { access_mode = ACCESS_WRITE; time_to_start_writing = 0; if (file_to_switch_to >= 0) { if (rmtclose (archive) != 0) close_warn (*archive_name_cursor); archive = file_to_switch_to; } else backspace_output (); } switch (access_mode) { case ACCESS_READ: flush_read (); break; case ACCESS_WRITE: flush_write (); break; case ACCESS_UPDATE: abort (); } } /* Backspace the archive descriptor by one record worth. If it's a tape, MTIOCTOP will work. If it's something else, try to seek on it. If we can't seek, we lose! */ static void backspace_output (void) { #ifdef MTIOCTOP { struct mtop operation; operation.mt_op = MTBSR; operation.mt_count = 1; if (rmtioctl (archive, MTIOCTOP, (char *) &operation) >= 0) return; if (errno == EIO && rmtioctl (archive, MTIOCTOP, (char *) &operation) >= 0) return; } #endif { off_t position = rmtlseek (archive, (off_t) 0, SEEK_CUR); /* Seek back to the beginning of this record and start writing there. */ position -= record_size; if (position < 0) position = 0; if (rmtlseek (archive, position, SEEK_SET) != position) { /* Lseek failed. Try a different method. */ WARN ((0, 0, _("Cannot backspace archive file; it may be unreadable without -i"))); /* Replace the first part of the record with NULs. */ if (record_start->buffer != output_start) memset (record_start->buffer, 0, output_start - record_start->buffer); } } } /* Close the archive file. */ void close_archive (void) { if (time_to_start_writing || access_mode == ACCESS_WRITE) flush_archive (); #if !MSDOS /* Manage to fully drain a pipe we might be reading, so to not break it on the producer after the EOF block. FIXME: one of these days, GNU tar might become clever enough to just stop working, once there is no more work to do, we might have to revise this area in such time. */ if (fast_read_option && namelist_freed && child_pid > 0) kill(child_pid, SIGTERM); if (access_mode == ACCESS_READ && ! _isrmt (archive) && (S_ISFIFO (archive_stat.st_mode) || S_ISSOCK (archive_stat.st_mode))) while (rmtread (archive, record_start->buffer, record_size) > 0) continue; #endif if (verify_option) verify_volume (); if (rmtclose (archive) != 0) close_warn (*archive_name_cursor); #if !MSDOS if (child_pid) { int wait_status; while (waitpid (child_pid, &wait_status, 0) == -1) if (errno != EINTR) { waitpid_error (use_compress_program_option); break; } if (!fast_read_option || !namelist_freed) if (WIFSIGNALED (wait_status)) ERROR ((0, 0, _("Child died with signal %d"), WTERMSIG (wait_status))); else if (WEXITSTATUS (wait_status) != 0) ERROR ((0, 0, _("Child returned status %d"), WEXITSTATUS (wait_status))); } #endif /* !MSDOS */ if (current_file_name) free (current_file_name); if (current_link_name) free (current_link_name); if (save_name) free (save_name); if (real_s_name) free (real_s_name); free (multi_volume_option ? record_start - 2 : record_start); } /* Called to initialize the global volume number. */ void init_volume_number (void) { FILE *file = fopen (volno_file_option, "r"); if (file) { if (fscanf (file, "%d", &global_volno) != 1 || global_volno < 0) FATAL_ERROR ((0, 0, _("%s: contains invalid volume number"), quotearg_colon (volno_file_option))); if (ferror (file)) read_error (volno_file_option); if (fclose (file) != 0) close_error (volno_file_option); } else if (errno != ENOENT) open_error (volno_file_option); } /* Called to write out the closing global volume number. */ void closeout_volume_number (void) { FILE *file = fopen (volno_file_option, "w"); if (file) { fprintf (file, "%d\n", global_volno); if (ferror (file)) write_error (volno_file_option); if (fclose (file) != 0) close_error (volno_file_option); } else open_error (volno_file_option); } /* We've hit the end of the old volume. Close it and open the next one. Return nonzero on success. */ static int new_volume (enum access_mode access) { static FILE *read_file; static int looped; if (!read_file && !info_script_option) /* FIXME: if fopen is used, it will never be closed. */ read_file = archive == STDIN_FILENO ? fopen (TTY_NAME, "r") : stdin; if (now_verifying) return 0; if (verify_option) verify_volume (); if (rmtclose (archive) != 0) close_warn (*archive_name_cursor); global_volno++; if (global_volno < 0) FATAL_ERROR ((0, 0, _("Volume number overflow"))); volno++; archive_name_cursor++; if (archive_name_cursor == archive_name_array + archive_names) { archive_name_cursor = archive_name_array; looped = 1; } tryagain: if (looped) { /* We have to prompt from now on. */ if (info_script_option) { if (volno_file_option) closeout_volume_number (); if (system (info_script_option) != 0) FATAL_ERROR ((0, 0, _("`%s' command failed"), info_script_option)); } else while (1) { char input_buffer[80]; fputc ('\007', stderr); fprintf (stderr, _("Prepare volume #%d for %s and hit return: "), global_volno, quote (*archive_name_cursor)); fflush (stderr); if (fgets (input_buffer, sizeof input_buffer, read_file) == 0) { WARN ((0, 0, _("EOF where user reply was expected"))); if (subcommand_option != EXTRACT_SUBCOMMAND && subcommand_option != LIST_SUBCOMMAND && subcommand_option != DIFF_SUBCOMMAND) WARN ((0, 0, _("WARNING: Archive is incomplete"))); fatal_exit (); } if (input_buffer[0] == '\n' || input_buffer[0] == 'y' || input_buffer[0] == 'Y') break; switch (input_buffer[0]) { case '?': { fprintf (stderr, _("\ n [name] Give a new file name for the next (and subsequent) volume(s)\n\ q Abort tar\n\ ! Spawn a subshell\n\ ? Print this list\n")); } break; case 'q': /* Quit. */ WARN ((0, 0, _("No new volume; exiting.\n"))); if (subcommand_option != EXTRACT_SUBCOMMAND && subcommand_option != LIST_SUBCOMMAND && subcommand_option != DIFF_SUBCOMMAND) WARN ((0, 0, _("WARNING: Archive is incomplete"))); fatal_exit (); case 'n': /* Get new file name. */ { char *name = &input_buffer[1]; char *cursor; while (*name == ' ' || *name == '\t') name++; cursor = name; while (*cursor && *cursor != '\n') cursor++; *cursor = '\0'; /* FIXME: the following allocation is never reclaimed. */ *archive_name_cursor = xstrdup (name); } break; case '!': #if MSDOS spawnl (P_WAIT, getenv ("COMSPEC"), "-", 0); #else /* not MSDOS */ { pid_t child; const char *shell = getenv ("SHELL"); if (! shell) - shell = "/bin/sh"; + shell = _PATH_BSHELL; child = xfork (); if (child == 0) { execlp (shell, "-sh", "-i", (char *) 0); exec_fatal (shell); } else { int wait_status; while (waitpid (child, &wait_status, 0) == -1) if (errno != EINTR) { waitpid_error (shell); break; } } } #endif /* not MSDOS */ break; } } } if (verify_option) archive = rmtopen (*archive_name_cursor, O_RDWR | O_CREAT, MODE_RW, rsh_command_option); else switch (access) { case ACCESS_READ: archive = rmtopen (*archive_name_cursor, O_RDONLY, MODE_RW, rsh_command_option); break; case ACCESS_WRITE: if (backup_option) maybe_backup_file (*archive_name_cursor, 1); archive = rmtcreat (*archive_name_cursor, MODE_RW, rsh_command_option); break; case ACCESS_UPDATE: archive = rmtopen (*archive_name_cursor, O_RDWR | O_CREAT, MODE_RW, rsh_command_option); break; } if (archive < 0) { open_warn (*archive_name_cursor); if (!verify_option && access == ACCESS_WRITE && backup_option) undo_last_backup (); goto tryagain; } #if MSDOS setmode (archive, O_BINARY); #endif return 1; } Index: head/include/paths.h =================================================================== --- head/include/paths.h (revision 114762) +++ head/include/paths.h (revision 114763) @@ -1,93 +1,97 @@ /* * 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. * * @(#)paths.h 8.1 (Berkeley) 6/2/93 * $FreeBSD$ */ #ifndef _PATHS_H_ #define _PATHS_H_ #include /* Default search path. */ #define _PATH_DEFPATH "/usr/bin:/bin" /* All standard utilities path. */ #define _PATH_STDPATH \ "/usr/bin:/bin:/usr/sbin:/sbin:" #define _PATH_AUTHCONF "/etc/auth.conf" #define _PATH_BSHELL "/bin/sh" #define _PATH_CAPABILITY "/etc/capability" #define _PATH_CAPABILITY_DB "/etc/capability.db" #define _PATH_CONSOLE "/dev/console" #define _PATH_CP "/bin/cp" #define _PATH_CSHELL "/bin/csh" #define _PATH_DEFTAPE "/dev/sa0" #define _PATH_DEVDB "/var/run/dev.db" #define _PATH_DEVNULL "/dev/null" #define _PATH_DEVZERO "/dev/zero" #define _PATH_DRUM "/dev/drum" #define _PATH_ETC "/etc" #define _PATH_FTPUSERS "/etc/ftpusers" +#define _PATH_HALT "/sbin/halt" +#define _PATH_IFCONFIG "/sbin/ifconfig" #define _PATH_KMEM "/dev/kmem" #define _PATH_LIBMAP_CONF "/etc/libmap.conf" #define _PATH_LOGIN "/usr/bin/login" #define _PATH_MAILDIR "/var/mail" #define _PATH_MAN "/usr/share/man" #define _PATH_MEM "/dev/mem" #define _PATH_NOLOGIN "/var/run/nologin" #define _PATH_RCP "/bin/rcp" +#define _PATH_REBOOT "/sbin/reboot" #define _PATH_RLOGIN "/usr/bin/rlogin" +#define _PATH_RM "/bin/rm" #define _PATH_RSH "/usr/bin/rsh" #define _PATH_SENDMAIL "/usr/sbin/sendmail" #define _PATH_SHELLS "/etc/shells" #define _PATH_TTY "/dev/tty" #define _PATH_UNIX "don't use _PATH_UNIX" #define _PATH_VI "/usr/bin/vi" #define _PATH_WALL "/usr/bin/wall" /* Provide trailing slash, since mostly used for building pathnames. */ #define _PATH_DEV "/dev/" #define _PATH_TMP "/tmp/" #define _PATH_VARDB "/var/db/" #define _PATH_VARRUN "/var/run/" #define _PATH_VARTMP "/var/tmp/" #define _PATH_YP "/var/yp/" #define _PATH_UUCPLOCK "/var/spool/lock/" /* How to get the correct name of the kernel. */ __BEGIN_DECLS const char *getbootfile(void); __END_DECLS #endif /* !_PATHS_H_ */ Index: head/sbin/shutdown/pathnames.h =================================================================== --- head/sbin/shutdown/pathnames.h (revision 114762) +++ head/sbin/shutdown/pathnames.h (nonexistent) @@ -1,41 +0,0 @@ -/* - * 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. - * - * @(#)pathnames.h 8.1 (Berkeley) 6/5/93 - * $FreeBSD$ - */ - -#include - -#define _PATH_FASTBOOT "/fastboot" -#define _PATH_HALT "/sbin/halt" -#define _PATH_REBOOT "/sbin/reboot" Property changes on: head/sbin/shutdown/pathnames.h ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: head/sbin/shutdown/shutdown.c =================================================================== --- head/sbin/shutdown/shutdown.c (revision 114762) +++ head/sbin/shutdown/shutdown.c (revision 114763) @@ -1,527 +1,526 @@ /* * Copyright (c) 1988, 1990, 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. */ #if 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1988, 1990, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)shutdown.c 8.4 (Berkeley) 4/28/95"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include - -#include "pathnames.h" #ifdef DEBUG #undef _PATH_NOLOGIN #define _PATH_NOLOGIN "./nologin" #endif #define H *60*60 #define M *60 #define S *1 #define NOLOG_TIME 5*60 struct interval { int timeleft, timetowait; } tlist[] = { { 10 H, 5 H }, { 5 H, 3 H }, { 2 H, 1 H }, { 1 H, 30 M }, { 30 M, 10 M }, { 20 M, 10 M }, { 10 M, 5 M }, { 5 M, 3 M }, { 2 M, 1 M }, { 1 M, 30 S }, { 30 S, 30 S }, { 0 , 0 } }; #undef H #undef M #undef S static time_t offset, shuttime; static int dohalt, dopower, doreboot, killflg, mbuflen, oflag; static char mbuf[BUFSIZ]; static const char *nosync, *whom; void badtime(void); void die_you_gravy_sucking_pig_dog(void); void finish(int); void getoffset(char *); void loop(void); void nolog(void); void timeout(int); void timewarn(int); void usage(const char *); int main(argc, argv) int argc; char *argv[]; { char *p, *endp; struct passwd *pw; int arglen, ch, len, readstdin; #ifndef DEBUG if (geteuid()) errx(1, "NOT super-user"); #endif nosync = NULL; readstdin = 0; while ((ch = getopt(argc, argv, "-hknopr")) != -1) switch (ch) { case '-': readstdin = 1; break; case 'h': dohalt = 1; break; case 'k': killflg = 1; break; case 'n': nosync = "-n"; break; case 'o': oflag = 1; break; case 'p': dopower = 1; break; case 'r': doreboot = 1; break; case '?': default: usage((char *)NULL); } argc -= optind; argv += optind; if (argc < 1) usage((char *)NULL); if (killflg + doreboot + dohalt + dopower > 1) usage("incompatible switches -h, -k, -p and -r"); if (oflag && !(dohalt || dopower || doreboot)) usage("-o requires -h, -p or -r"); if (nosync != NULL && !oflag) usage("-n requires -o"); getoffset(*argv++); if (*argv) { for (p = mbuf, len = sizeof(mbuf); *argv; ++argv) { arglen = strlen(*argv); if ((len -= arglen) <= 2) break; if (p != mbuf) *p++ = ' '; memmove(p, *argv, arglen); p += arglen; } *p = '\n'; *++p = '\0'; } if (readstdin) { p = mbuf; endp = mbuf + sizeof(mbuf) - 2; for (;;) { if (!fgets(p, endp - p + 1, stdin)) break; for (; *p && p < endp; ++p); if (p == endp) { *p = '\n'; *++p = '\0'; break; } } } mbuflen = strlen(mbuf); if (offset) (void)printf("Shutdown at %.24s.\n", ctime(&shuttime)); else (void)printf("Shutdown NOW!\n"); if (!(whom = getlogin())) whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; #ifdef DEBUG (void)putc('\n', stdout); #else (void)setpriority(PRIO_PROCESS, 0, PRIO_MIN); { int forkpid; forkpid = fork(); if (forkpid == -1) err(1, "fork"); if (forkpid) errx(0, "[pid %d]", forkpid); } setsid(); #endif openlog("shutdown", LOG_CONS, LOG_AUTH); loop(); return(0); } void loop() { struct interval *tp; u_int sltime; int logged; if (offset <= NOLOG_TIME) { logged = 1; nolog(); } else logged = 0; tp = tlist; if (tp->timeleft < offset) (void)sleep((u_int)(offset - tp->timeleft)); else { while (tp->timeleft && offset < tp->timeleft) ++tp; /* * Warn now, if going to sleep more than a fifth of * the next wait time. */ if ((sltime = offset - tp->timeleft)) { if (sltime > (u_int)(tp->timetowait / 5)) timewarn(offset); (void)sleep(sltime); } } for (;; ++tp) { timewarn(tp->timeleft); if (!logged && tp->timeleft <= NOLOG_TIME) { logged = 1; nolog(); } (void)sleep((u_int)tp->timetowait); if (!tp->timeleft) break; } die_you_gravy_sucking_pig_dog(); } static jmp_buf alarmbuf; static const char *restricted_environ[] = { "PATH=" _PATH_STDPATH, NULL }; void timewarn(timeleft) int timeleft; { static int first; static char hostname[MAXHOSTNAMELEN + 1]; FILE *pf; char wcmd[MAXPATHLEN + 4]; extern const char **environ; if (!first++) (void)gethostname(hostname, sizeof(hostname)); /* undoc -n option to wall suppresses normal wall banner */ (void)snprintf(wcmd, sizeof(wcmd), "%s -n", _PATH_WALL); environ = restricted_environ; if (!(pf = popen(wcmd, "w"))) { syslog(LOG_ERR, "shutdown: can't find %s: %m", _PATH_WALL); return; } (void)fprintf(pf, "\007*** %sSystem shutdown message from %s@%s ***\007\n", timeleft ? "": "FINAL ", whom, hostname); if (timeleft > 10*60) (void)fprintf(pf, "System going down at %5.5s\n\n", ctime(&shuttime) + 11); else if (timeleft > 59) (void)fprintf(pf, "System going down in %d minute%s\n\n", timeleft / 60, (timeleft > 60) ? "s" : ""); else if (timeleft) (void)fprintf(pf, "System going down in 30 seconds\n\n"); else (void)fprintf(pf, "System going down IMMEDIATELY\n\n"); if (mbuflen) (void)fwrite(mbuf, sizeof(*mbuf), mbuflen, pf); /* * play some games, just in case wall doesn't come back * probably unnecessary, given that wall is careful. */ if (!setjmp(alarmbuf)) { (void)signal(SIGALRM, timeout); (void)alarm((u_int)30); (void)pclose(pf); (void)alarm((u_int)0); (void)signal(SIGALRM, SIG_DFL); } } void timeout(signo) int signo __unused; { longjmp(alarmbuf, 1); } void die_you_gravy_sucking_pig_dog() { char *empty_environ[] = { NULL }; syslog(LOG_NOTICE, "%s by %s: %s", doreboot ? "reboot" : dohalt ? "halt" : dopower ? "power-down" : "shutdown", whom, mbuf); (void)sleep(2); (void)printf("\r\nSystem shutdown time has arrived\007\007\r\n"); if (killflg) { (void)printf("\rbut you'll have to do it yourself\r\n"); exit(0); } #ifdef DEBUG if (doreboot) (void)printf("reboot"); else if (dohalt) (void)printf("halt"); else if (dopower) (void)printf("power-down"); if (nosync != NULL) (void)printf(" no sync"); (void)printf("\nkill -HUP 1\n"); #else if (!oflag) { (void)kill(1, doreboot ? SIGINT : /* reboot */ dohalt ? SIGUSR1 : /* halt */ dopower ? SIGUSR2 : /* power-down */ SIGTERM); /* single-user */ } else { if (doreboot) { execle(_PATH_REBOOT, "reboot", "-l", nosync, (char *)NULL, empty_environ); syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_REBOOT); warn(_PATH_REBOOT); } else if (dohalt) { execle(_PATH_HALT, "halt", "-l", nosync, (char *)NULL, empty_environ); syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); warn(_PATH_HALT); } else if (dopower) { execle(_PATH_HALT, "halt", "-l", "-p", nosync, (char *)NULL, empty_environ); syslog(LOG_ERR, "shutdown: can't exec %s: %m.", _PATH_HALT); warn(_PATH_HALT); } (void)kill(1, SIGTERM); /* to single-user */ } #endif finish(0); } #define ATOI2(p) (p[0] - '0') * 10 + (p[1] - '0'); p += 2; void getoffset(timearg) char *timearg; { struct tm *lt; char *p; time_t now; int this_year; (void)time(&now); if (!strcasecmp(timearg, "now")) { /* now */ offset = 0; shuttime = now; return; } if (*timearg == '+') { /* +minutes */ if (!isdigit(*++timearg)) badtime(); if ((offset = atoi(timearg) * 60) < 0) badtime(); shuttime = now + offset; return; } /* handle hh:mm by getting rid of the colon */ for (p = timearg; *p; ++p) if (!isascii(*p) || !isdigit(*p)) { if (*p == ':' && strlen(p) == 3) { p[0] = p[1]; p[1] = p[2]; p[2] = '\0'; } else badtime(); } unsetenv("TZ"); /* OUR timezone */ lt = localtime(&now); /* current time val */ switch(strlen(timearg)) { case 10: this_year = lt->tm_year; lt->tm_year = ATOI2(timearg); /* * check if the specified year is in the next century. * allow for one year of user error as many people will * enter n - 1 at the start of year n. */ if (lt->tm_year < (this_year % 100) - 1) lt->tm_year += 100; /* adjust for the year 2000 and beyond */ lt->tm_year += (this_year - (this_year % 100)); /* FALLTHROUGH */ case 8: lt->tm_mon = ATOI2(timearg); if (--lt->tm_mon < 0 || lt->tm_mon > 11) badtime(); /* FALLTHROUGH */ case 6: lt->tm_mday = ATOI2(timearg); if (lt->tm_mday < 1 || lt->tm_mday > 31) badtime(); /* FALLTHROUGH */ case 4: lt->tm_hour = ATOI2(timearg); if (lt->tm_hour < 0 || lt->tm_hour > 23) badtime(); lt->tm_min = ATOI2(timearg); if (lt->tm_min < 0 || lt->tm_min > 59) badtime(); lt->tm_sec = 0; if ((shuttime = mktime(lt)) == -1) badtime(); if ((offset = shuttime - now) < 0) errx(1, "that time is already past."); break; default: badtime(); } } #define NOMSG "\n\nNO LOGINS: System going down at " void nolog() { int logfd; char *ct; (void)unlink(_PATH_NOLOGIN); /* in case linked to another file */ (void)signal(SIGINT, finish); (void)signal(SIGHUP, finish); (void)signal(SIGQUIT, finish); (void)signal(SIGTERM, finish); if ((logfd = open(_PATH_NOLOGIN, O_WRONLY|O_CREAT|O_TRUNC, 0664)) >= 0) { (void)write(logfd, NOMSG, sizeof(NOMSG) - 1); ct = ctime(&shuttime); (void)write(logfd, ct + 11, 5); (void)write(logfd, "\n\n", 2); (void)write(logfd, mbuf, strlen(mbuf)); (void)close(logfd); } } void finish(signo) int signo __unused; { if (!killflg) (void)unlink(_PATH_NOLOGIN); exit(0); } void badtime() { errx(1, "bad time format"); } void usage(cp) const char *cp; { if (cp != NULL) warnx("%s", cp); (void)fprintf(stderr, "usage: shutdown [-] [-h | -p | -r | -k] [-o [-n]]" " time [warning-message ...]\n"); exit(1); } Index: head/sbin/startslip/startslip.c =================================================================== --- head/sbin/startslip/startslip.c (revision 114762) +++ head/sbin/startslip/startslip.c (revision 114763) @@ -1,601 +1,601 @@ /*- * Copyright (c) 1990, 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. 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) 1990, 1991, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)startslip.c 8.1 (Berkeley) 6/5/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 #define DEFAULT_BAUD B9600 int speed = DEFAULT_BAUD; #define FC_NONE 0 /* flow control: none */ #define FC_HW 1 /* flow control: hardware (RTS/CTS) */ int flowcontrol = FC_NONE; int modem_control = 1; /* !CLOCAL+HUPCL iff we watch carrier. */ int sl_unit = -1; int uucp_lock = 0; /* uucp locking */ char *annex; char *username; int hup; int terminate; int locked = 0; /* uucp lock active */ int logged_in = 0; int wait_time = 60; /* then back off */ int script_timeout = 90; /* connect script default timeout */ time_t conn_time, start_time; int MAXTRIES = 6; /* w/60 sec and doubling, takes an hour */ #define PIDFILE "%sstartslip.%s.pid" #define MAXDIALS 20 char *dials[MAXDIALS]; int diali, dialc; int fd = -1; FILE *pfd; char *dvname, *devicename; char pidfile[80]; #ifdef DEBUG int debug = 1; #undef LOG_ERR #undef LOG_INFO #define syslog fprintf #define LOG_ERR stderr #define LOG_INFO stderr #else int debug = 0; #endif #define printd if (debug) printf int carrier(void); void down(int); int getline(char *, int, int, time_t); static void usage(void); int main(argc, argv) int argc; char **argv; { char *cp, **ap; int ch, disc; void sighup(), sigterm(), sigurg(); FILE *wfd = NULL; char *dialerstring = 0, buf[BUFSIZ]; int unitnum, keepal = 0, outfill = 0; char unitname[32]; char *password; char *upscript = NULL, *downscript = NULL; int first = 1, tries = 0; time_t fintimeout; long lpid; pid_t pid; struct termios t; while ((ch = getopt(argc, argv, "dhlb:s:t:w:A:U:D:W:K:O:S:L")) != -1) switch (ch) { case 'd': debug = 1; break; case 'b': speed = atoi(optarg); break; case 's': if (diali >= MAXDIALS) errx(1, "max dial strings number (%d) exceeded", MAXDIALS); dials[diali++] = strdup(optarg); break; case 't': script_timeout = atoi(optarg); break; case 'w': wait_time = atoi(optarg); break; case 'W': MAXTRIES = atoi(optarg); break; case 'A': annex = strdup(optarg); break; case 'U': upscript = strdup(optarg); break; case 'D': downscript = strdup(optarg); break; case 'L': uucp_lock = 1; break; case 'l': modem_control = 0; break; case 'h': flowcontrol = FC_HW; break; case 'K': keepal = atoi(optarg); break; case 'O': outfill = atoi(optarg); break; case 'S': sl_unit = atoi(optarg); break; case '?': default: usage(); } argc -= optind; argv += optind; if (argc != 3) usage(); /* * Copy these so they exist after we clobber them. */ devicename = strdup(argv[0]); username = strdup(argv[1]); password = strdup(argv[2]); /* * Security hack. Do not want private information such as the * password and possible phone number to be left around. * So we clobber the arguments. */ for (ap = argv - optind + 1; ap < argv + 3; ap++) for (cp = *ap; *cp != 0; cp++) *cp = '\0'; openlog("startslip", LOG_PID|LOG_PERROR, LOG_DAEMON); if (debug) setbuf(stdout, NULL); signal(SIGTERM, sigterm); if ((dvname = strrchr(devicename, '/')) == NULL) dvname = devicename; else dvname++; if (snprintf(pidfile, sizeof(pidfile), PIDFILE, _PATH_VARRUN, dvname) >= sizeof(pidfile)) usage(); if ((pfd = fopen(pidfile, "r")) != NULL) { if (fscanf(pfd, "%ld\n", &lpid) == 1) { pid = lpid; if (pid == lpid && pid > 0) kill(pid, SIGTERM); } fclose(pfd); pfd = NULL; /* not remove pidfile yet */ sleep(5); /* allow down script to be completed */ } else restart: signal(SIGHUP, SIG_IGN); signal(SIGURG, SIG_IGN); hup = 0; if (wfd) { printd("fclose, "); fclose(wfd); conn_time = time(NULL) - start_time; if (uucp_lock) uu_unlock(dvname); locked = 0; wfd = NULL; fd = -1; sleep(5); } else if (fd >= 0) { printd("close, "); close(fd); conn_time = time(NULL) - start_time; if (uucp_lock) uu_unlock(dvname); locked = 0; fd = -1; sleep(5); } if (logged_in) { syslog(LOG_INFO, "%s: connection time elapsed: %ld secs", username, (long)conn_time); sprintf(buf, "LINE=%d %s %s down", diali ? (dialc - 1) % diali : 0, - downscript ? downscript : "/sbin/ifconfig" , unitname); + downscript ? downscript : _PATH_IFCONFIG , unitname); (void) system(buf); logged_in = 0; } if (terminate) down(0); tries++; if (MAXTRIES > 0 && tries > MAXTRIES) { syslog(LOG_ERR, "%s: exiting login after %d tries", username, tries); /* ??? if (first) */ down(3); } if (tries > 1) { syslog(LOG_INFO, "%s: sleeping %d seconds (%d tries)", username, wait_time * (tries - 1), tries); sleep(wait_time * (tries - 1)); if (terminate) goto restart; } if (daemon(1, debug) < 0) { syslog(LOG_ERR, "%s: daemon: %m", username); down(2); } pid = getpid(); printd("restart: pid %ld: ", (long)pid); if ((pfd = fopen(pidfile, "w")) != NULL) { fprintf(pfd, "%ld\n", (long)pid); fclose(pfd); } printd("open"); if (uucp_lock) { int res; if ((res = uu_lock(dvname)) != UU_LOCK_OK) { if (res != UU_LOCK_INUSE) syslog(LOG_ERR, "uu_lock: %s", uu_lockerr(res)); syslog(LOG_ERR, "%s: can't lock %s", username, devicename); goto restart; } locked = 1; } if ((fd = open(devicename, O_RDWR | O_NONBLOCK)) < 0) { syslog(LOG_ERR, "%s: open %s: %m", username, devicename); if (first) down(1); else { if (uucp_lock) uu_unlock(dvname); locked = 0; goto restart; } } printd(" %d", fd); signal(SIGHUP, sighup); if (ioctl(fd, TIOCSCTTY, 0) < 0) { syslog(LOG_ERR, "%s: ioctl (TIOCSCTTY): %m", username); down(2); } if (tcsetpgrp(fd, getpid()) < 0) { syslog(LOG_ERR, "%s: tcsetpgrp failed: %m", username); down(2); } printd(", ioctl\n"); if (tcgetattr(fd, &t) < 0) { syslog(LOG_ERR, "%s: tcgetattr(%s): %m", username, devicename); down(2); } cfmakeraw(&t); switch (flowcontrol) { case FC_HW: t.c_cflag |= (CRTS_IFLOW|CCTS_OFLOW); break; case FC_NONE: t.c_cflag &= ~(CRTS_IFLOW|CCTS_OFLOW); break; } if (modem_control) t.c_cflag |= HUPCL; else t.c_cflag &= ~(HUPCL); t.c_cflag |= CLOCAL; /* until modem commands passes */ cfsetispeed(&t, speed); cfsetospeed(&t, speed); if (tcsetattr(fd, TCSAFLUSH, &t) < 0) { syslog(LOG_ERR, "%s: tcsetattr(%s): %m", username, devicename); down(2); } sleep(2); /* wait for flakey line to settle */ if (hup || terminate) goto restart; wfd = fdopen(fd, "w+"); if (wfd == NULL) { syslog(LOG_ERR, "%s: can't fdopen %s: %m", username, devicename); down(2); } setbuf(wfd, NULL); if (diali > 0) dialerstring = dials[dialc++ % diali]; if (dialerstring) { syslog(LOG_INFO, "%s: dialer string: %s\\r", username, dialerstring); fprintf(wfd, "%s\r", dialerstring); } printd("\n"); fintimeout = time(NULL) + script_timeout; if (modem_control) { printd("waiting for carrier\n"); while (time(NULL) < fintimeout && !carrier()) { sleep(1); if (hup || terminate) goto restart; } if (!carrier()) goto restart; t.c_cflag &= ~(CLOCAL); if (tcsetattr(fd, TCSANOW, &t) < 0) { syslog(LOG_ERR, "%s: tcsetattr(%s): %m", username, devicename); down(2); } /* Only now we able to receive HUP on carrier drop! */ } /* * Log in */ printd("look for login: "); for (;;) { if (getline(buf, BUFSIZ, fd, fintimeout) == 0 || hup || terminate) goto restart; if (annex) { if (bcmp(buf, annex, strlen(annex)) == 0) { fprintf(wfd, "slip\r"); printd("Sent \"slip\"\n"); continue; } if (bcmp(&buf[1], "sername:", 8) == 0) { fprintf(wfd, "%s\r", username); printd("Sent login: %s\n", username); continue; } if (bcmp(&buf[1], "assword:", 8) == 0) { fprintf(wfd, "%s\r", password); printd("Sent password: %s\n", password); break; } } else { if (strstr(&buf[1], "ogin:") != NULL) { fprintf(wfd, "%s\r", username); printd("Sent login: %s\n", username); continue; } if (strstr(&buf[1], "assword:") != NULL) { fprintf(wfd, "%s\r", password); printd("Sent password: %s\n", password); break; } } } sleep(5); /* Wait until login completed */ if (hup || terminate) goto restart; start_time = time(NULL); /* * Attach */ printd("setd"); disc = SLIPDISC; if (ioctl(fd, TIOCSETD, &disc) < 0) { syslog(LOG_ERR, "%s: ioctl (%s, TIOCSETD): %m", username, devicename); down(2); } if (sl_unit >= 0 && ioctl(fd, SLIOCSUNIT, &sl_unit) < 0) { syslog(LOG_ERR, "%s: ioctl(SLIOCSUNIT): %m", username); down(2); } if (ioctl(fd, SLIOCGUNIT, &unitnum) < 0) { syslog(LOG_ERR, "%s: ioctl(SLIOCGUNIT): %m", username); down(2); } sprintf(unitname, "sl%d", unitnum); if (keepal > 0) { signal(SIGURG, sigurg); if (ioctl(fd, SLIOCSKEEPAL, &keepal) < 0) { syslog(LOG_ERR, "%s: ioctl(SLIOCSKEEPAL): %m", username); down(2); } } if (outfill > 0 && ioctl(fd, SLIOCSOUTFILL, &outfill) < 0) { syslog(LOG_ERR, "%s: ioctl(SLIOCSOUTFILL): %m", username); down(2); } sprintf(buf, "LINE=%d %s %s up", diali ? (dialc - 1) % diali : 0, - upscript ? upscript : "/sbin/ifconfig" , unitname); + upscript ? upscript : _PATH_IFCONFIG , unitname); (void) system(buf); printd(", ready\n"); if (!first) syslog(LOG_INFO, "%s: reconnected on %s (%d tries)", username, unitname, tries); else syslog(LOG_INFO, "%s: connected on %s", username, unitname); first = 0; tries = 0; logged_in = 1; while (hup == 0 && terminate == 0) { sigpause(0L); printd("sigpause return\n"); } goto restart; return(0); /* not reached */ } void sighup() { printd("hup\n"); if (hup == 0 && logged_in) syslog(LOG_INFO, "%s: got hangup signal", username); hup = 1; } void sigurg() { printd("urg\n"); if (hup == 0 && logged_in) syslog(LOG_INFO, "%s: got dead line signal", username); hup = 1; } void sigterm() { printd("terminate\n"); if (terminate == 0 && logged_in) syslog(LOG_INFO, "%s: got terminate signal", username); terminate = 1; } int getline(buf, size, fd, fintimeout) char *buf; int size, fd; time_t fintimeout; { int i; int ret; fd_set readfds; struct timeval tv; time_t timeout; size--; for (i = 0; i < size; i++) { if (hup || terminate) return (0); if ((timeout = fintimeout - time(NULL)) <= 0) goto tout; FD_ZERO(&readfds); FD_SET(fd, &readfds); tv.tv_sec = timeout; tv.tv_usec = 0; if ((ret = select(fd + 1, &readfds, NULL, NULL, &tv)) < 0) { if (errno != EINTR) syslog(LOG_ERR, "%s: getline: select: %m", username); } else { if (! ret) { tout: printd("getline: timed out\n"); return (0); } if ((ret = read(fd, &buf[i], 1)) == 1) { buf[i] &= 0177; if (buf[i] == '\r' || buf[i] == '\0') { i--; continue; } if (buf[i] != '\n' && buf[i] != ':') continue; buf[i + 1] = '\0'; printd("Got %d: %s", i + 1, buf); return (i+1); } if (ret <= 0) { if (ret < 0) { syslog(LOG_ERR, "%s: getline: read: %m", username); } else syslog(LOG_ERR, "%s: read returned 0", username); buf[i] = '\0'; printd("returning %d after %d: %s\n", ret, i, buf); return (0); } } } return (0); } int carrier() { int comstate; if (ioctl(fd, TIOCMGET, &comstate) < 0) { syslog(LOG_ERR, "%s: ioctl (%s, TIOCMGET): %m", username, devicename); down(2); } return !!(comstate & TIOCM_CD); } void down(code) { if (fd > -1) close(fd); if (pfd) unlink(pidfile); if (uucp_lock && locked) uu_unlock(dvname); exit(code); } static void usage() { (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", "usage: startslip [-d] [-b speed] [-s string1 [-s string2 [...]]] [-h] [-l]", " [-L] [-A annexname] [-U upscript] [-D downscript]", " [-t script_timeout] [-W maxtries] [-w retry_pause]", " [-K keepalive] [-O outfill] [-S unit] device user passwd"); exit(1); } Index: head/sbin/vinum/commands.c =================================================================== --- head/sbin/vinum/commands.c (revision 114762) +++ head/sbin/vinum/commands.c (revision 114763) @@ -1,2393 +1,2393 @@ /* commands.c: vinum interface program, main commands */ /*- * Copyright (c) 1997, 1998 * Nan Yang Computer Services Limited. All rights reserved. * * Written by Greg Lehey * * This software is distributed under the so-called ``Berkeley * License'': * * 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 Nan Yang Computer * Services Limited. * 4. Neither the name of the Company 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 ``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 company 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: commands.c,v 1.23 2003/05/04 05:23:59 grog Exp grog $ * $FreeBSD$ */ #include "vext.h" #include static void dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen); void vinum_create(int argc, char *argv[], char *arg0[]) { int error; FILE *dfd; /* file descriptor for the config file */ char buffer[BUFSIZE]; /* read config file in here */ char commandline[BUFSIZE]; /* issue command from here */ struct _ioctl_reply *reply; int ioctltype; /* for ioctl call */ char tempfile[PATH_MAX]; /* name of temp file for direct editing */ char *file; /* file to read */ FILE *tf; /* temp file */ if (argc == 0) { /* no args, */ char *editor; /* editor to start */ int status; editor = getenv("EDITOR"); if (editor == NULL) - editor = "/usr/bin/vi"; + editor = _PATH_VI; sprintf(tempfile, "/var/tmp/" VINUMMOD ".create.%d", getpid()); /* create a temp file */ tf = fopen(tempfile, "w"); /* open it */ if (tf == NULL) { fprintf(stderr, "Can't open %s: %s\n", argv[0], strerror(errno)); return; } printconfig(tf, "# "); /* and put the current config it */ fclose(tf); sprintf(commandline, "%s %s", editor, tempfile); /* create an edit command */ status = system(commandline); /* do it */ if (status != 0) { fprintf(stderr, "Can't edit config: status %d\n", status); return; } file = tempfile; } else if (argc == 1) file = argv[0]; else { fprintf(stderr, "Expecting 1 parameter, not %d\n", argc); return; } reply = (struct _ioctl_reply *) &buffer; dfd = fopen(file, "r"); if (dfd == NULL) { /* no go */ fprintf(stderr, "Can't open %s: %s\n", file, strerror(errno)); return; } if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ printf("Can't configure: %s (%d)\n", strerror(errno), errno); return; } file_line = 0; /* start with line 1 */ /* Parse the configuration, and add it to the global configuration */ for (;;) { /* love this style(9) */ char *configline; configline = fgets(buffer, BUFSIZE, dfd); if (History) fprintf(History, "%s", buffer); if (configline == NULL) { if (ferror(dfd)) perror("Can't read config file"); break; } file_line++; /* count the lines */ if (vflag) printf("%4d: %s", file_line, buffer); strcpy(commandline, buffer); /* make a copy */ ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (!vflag) /* print this line anyway */ printf("%4d: %s", file_line, commandline); fprintf(stdout, "** %d %s: %s\n", file_line, reply->msg, strerror(reply->error)); /* * XXX at the moment, we reset the config * lock on error, so try to get it again. * If we fail, don't cry again. */ if (ioctl(superdev, VINUM_STARTCONFIG, &force)) /* can't get config? */ return; } } fclose(dfd); /* done with the config file */ ioctltype = 0; /* saveconfig after update */ error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ if (error != 0) perror("Can't save Vinum config"); if (no_devfs) make_devices(); listconfig(); checkupdates(); /* make sure we're updating */ } /* Read vinum config from a disk */ void vinum_read(int argc, char *argv[], char *arg0[]) { int error; char buffer[BUFSIZE]; /* read config file in here */ struct _ioctl_reply *reply; int i; reply = (struct _ioctl_reply *) &buffer; buffer[0] = '\0'; /* make sure we don't pass anything */ if (argc > 0) { /* args specified, */ for (i = 0; i < argc; i++) { /* each drive name */ strcat(buffer, argv[i]); strcat(buffer, " "); } } if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ fprintf(stderr, "Can't configure: %s (%d)\n", strerror(errno), errno); return; } ioctl(superdev, VINUM_READCONFIG, &buffer); if (reply->error != 0) { /* error in config */ fprintf(stdout, "** %s: %s\n", reply->msg, strerror(reply->error)); error = ioctl(superdev, VINUM_RELEASECONFIG, NULL); /* save the config to disk */ if (error != 0) perror("Can't save Vinum config"); } else { error = ioctl(superdev, VINUM_RELEASECONFIG, NULL); /* save the config to disk */ if (error != 0) perror("Can't save Vinum config"); if (no_devfs) make_devices(); } checkupdates(); /* make sure we're updating */ } void vinum_debug(int argc, char *argv[], char *arg0[]) { struct debuginfo info; if (vinum_conf.flags & VF_HASDEBUG) { if (argc > 0) { info.param = atoi(argv[0]); info.changeit = 1; } else { info.changeit = 0; sleep(2); /* give a chance to leave the window */ } ioctl(superdev, VINUM_DEBUG, (caddr_t) & info); } else /* no debug in kernel module */ fprintf(stderr, "Kernel module does not have debug support\n"); } void vinum_modify(int argc, char *argv[], char *arg0[]) { fprintf(stderr, "Modify command is currently not implemented\n"); checkupdates(); /* make sure we're updating */ } void vinum_set(int argc, char *argv[], char *arg0[]) { fprintf(stderr, "set is not implemented yet\n"); } void vinum_rm(int argc, char *argv[], char *arg0[]) { int object; struct _ioctl_reply reply; struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; if (argc == 0) /* start everything */ fprintf(stderr, "usage: rm object [object...]\n"); else { /* start specified objects */ int index; enum objecttype type; for (index = 0; index < argc; index++) { object = find_object(argv[index], &type); /* look for it */ if (type == invalid_object) fprintf(stderr, "Can't find object: %s\n", argv[index]); else { message->index = object; /* pass object number */ message->type = type; /* and type of object */ message->force = force; /* do we want to force the operation? */ message->recurse = recurse; /* do we want to remove subordinates? */ ioctl(superdev, VINUM_REMOVE, message); if (reply.error != 0) { fprintf(stderr, "Can't remove %s: %s (%d)\n", argv[index], reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); } else if (vflag) fprintf(stderr, "%s removed\n", argv[index]); } } checkupdates(); /* make sure we're updating */ /* Arguably we should be cleverer about this. */ if (no_devfs) make_devices(); } } void vinum_resetconfig(int argc, char *argv[], char *arg0[]) { char reply[32]; int error; if (isatty(STDIN_FILENO)) { printf(" WARNING! This command will completely wipe out your vinum configuration.\n" " All data will be lost. If you really want to do this, enter the text\n\n" " NO FUTURE\n" " Enter text -> "); fgets(reply, sizeof(reply), stdin); if (strcmp(reply, "NO FUTURE\n")) /* changed his mind */ printf("\n No change\n"); else { error = ioctl(superdev, VINUM_RESETCONFIG, NULL); /* trash config on disk */ if (error) { if (errno == EBUSY) fprintf(stderr, "Can't reset configuration: objects are in use\n"); else perror("Can't find vinum config"); } else { if (no_devfs) make_devices(); /* recreate the /dev/vinum hierarchy */ printf("\b Vinum configuration obliterated\n"); start_daemon(); /* then restart the daemon */ } } checkupdates(); /* make sure we're updating */ } else fprintf(stderr, "Please enter this command from a terminal\n"); } /* Initialize subdisks */ void vinum_init(int argc, char *argv[], char *arg0[]) { if (argc > 0) { /* initialize plexes */ int objindex; int objno; enum objecttype type; /* type returned */ if (History) fflush(History); /* don't let all the kids do it. */ for (objindex = 0; objindex < argc; objindex++) { objno = find_object(argv[objindex], &type); /* find the object */ if (objno < 0) printf("Can't find %s\n", argv[objindex]); else { switch (type) { case volume_object: initvol(objno); break; case plex_object: initplex(objno, argv[objindex]); break; case sd_object: initsd(objno, dowait); break; default: printf("Can't initialize %s: wrong object type\n", argv[objindex]); break; } } } } checkupdates(); /* make sure we're updating */ } void initvol(int volno) { printf("Initializing volumes is not implemented yet\n"); } void initplex(int plexno, char *name) { int sdno; int plexfh = NULL; /* file handle for plex */ pid_t pid; char filename[MAXPATHLEN]; /* create a file name here */ /* Variables for use by children */ int failed = 0; /* set if a child dies badly */ sprintf(filename, VINUM_DIR "/plex/%s", name); if ((plexfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* got a plex, open it */ /* * We don't actually write anything to the * plex. We open it to ensure that nobody * else tries to open it while we initialize * its subdisks. */ fprintf(stderr, "can't open plex %s: %s\n", filename, strerror(errno)); return; } if (dowait == 0) { pid = fork(); /* into the background with you */ if (pid != 0) { /* I'm the parent, or we failed */ if (pid < 0) /* failure */ printf("Couldn't fork: %s", strerror(errno)); close(plexfh); /* we don't need this any more */ return; } } /* * If we get here, we're either the first-level * child (if we're not waiting) or we're going * to wait. */ for (sdno = 0; sdno < plex.subdisks; sdno++) { /* initialize each subdisk */ get_plex_sd_info(&sd, plexno, sdno); initsd(sd.sdno, 0); } /* Now wait for them to complete */ while (1) { int status; pid = wait(&status); if (((int) pid == -1) && (errno == ECHILD)) /* all gone */ break; if (WEXITSTATUS(status) != 0) { /* oh, oh */ printf("child %d exited with status 0x%x\n", pid, WEXITSTATUS(status)); failed++; } } if (failed == 0) { syslog(LOG_INFO | LOG_KERN, "plex %s initialized", plex.name); } else syslog(LOG_ERR | LOG_KERN, "couldn't initialize plex %s, %d processes died", plex.name, failed); if (dowait == 0) /* we're the waiting child, */ exit(0); /* we've done our dash */ } /* Initialize a subdisk. */ void initsd(int sdno, int dowait) { pid_t pid; struct _ioctl_reply reply; struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; char filename[MAXPATHLEN]; /* create a file name here */ /* Variables for use by children */ int sdfh; /* and for subdisk */ int initsize; /* actual size to write */ int64_t sdsize; /* size of subdisk */ if (dowait == 0) { pid = fork(); /* into the background with you */ if (pid > 0) /* I'm the parent */ return; else if (pid < 0) { /* failure */ printf("couldn't fork for subdisk %d: %s", sdno, strerror(errno)); return; } } if (SSize != 0) { /* specified a size for init */ if (SSize < 512) SSize <<= DEV_BSHIFT; initsize = min(SSize, MAXPLEXINITSIZE); } else initsize = PLEXINITSIZE; openlog("vinum", LOG_CONS | LOG_PERROR | LOG_PID, LOG_KERN); get_sd_info(&sd, sdno); sdsize = sd.sectors * DEV_BSIZE; /* size of subdisk in bytes */ sprintf(filename, VINUM_DIR "/sd/%s", sd.name); setproctitle("initializing %s", filename); /* show what we're doing */ syslog(LOG_INFO | LOG_KERN, "initializing subdisk %s", filename); if ((sdfh = open(filename, O_RDWR, S_IRWXU)) < 0) { /* no go */ syslog(LOG_ERR | LOG_KERN, "can't open subdisk %s: %s", filename, strerror(errno)); exit(1); } /* Set the subdisk in initializing state */ message->index = sd.sdno; /* pass object number */ message->type = sd_object; /* and type of object */ message->state = object_initializing; message->verify = vflag; /* verify what we write? */ message->force = 1; /* insist */ ioctl(superdev, VINUM_SETSTATE, message); if ((SSize > 0) /* specified a size for init */ &&(SSize < 512)) SSize <<= DEV_BSHIFT; if (reply.error) { fprintf(stderr, "Can't initialize %s: %s (%d)\n", filename, strerror(reply.error), reply.error); exit(1); } else { do { if (interval) /* pause between copies */ usleep(interval * 1000); message->index = sd.sdno; /* pass object number */ message->type = sd_object; /* and type of object */ message->state = object_up; message->verify = vflag; /* verify what we write? */ message->blocksize = SSize; ioctl(superdev, VINUM_SETSTATE, message); } while (reply.error == EAGAIN); /* until we're done */ if (reply.error) { fprintf(stderr, "Can't initialize %s: %s (%d)\n", filename, strerror(reply.error), reply.error); get_sd_info(&sd, sdno); if (sd.state != sd_up) /* Set the subdisk down */ message->index = sd.sdno; /* pass object number */ message->type = sd_object; /* and type of object */ message->state = object_down; message->verify = vflag; /* verify what we write? */ message->force = 1; /* insist */ ioctl(superdev, VINUM_SETSTATE, message); } } printf("subdisk %s initialized\n", filename); if (!dowait) exit(0); } void vinum_start(int argc, char *argv[], char *arg0[]) { int object; struct _ioctl_reply reply; struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; if (argc == 0) /* start everything */ /* XXX how should we do this right? */ vinum_read(0, NULL, NULL); /* that's what vinum_read does now */ else { /* start specified objects */ int index; enum objecttype type; for (index = 0; index < argc; index++) { object = find_object(argv[index], &type); /* look for it */ if (type == invalid_object) fprintf(stderr, "Can't find object: %s\n", argv[index]); else { int doit = 0; /* set to 1 if we pass our tests */ switch (type) { case drive_object: if (drive.state == drive_up) /* already up */ fprintf(stderr, "%s is already up\n", drive.label.name); else doit = 1; break; case sd_object: if (sd.state == sd_up) /* already up */ fprintf(stderr, "%s is already up\n", sd.name); else doit = 1; break; case plex_object: if (plex.state == plex_up) /* already up */ fprintf(stderr, "%s is already up\n", plex.name); else { int sdno; /* * First, see if we can bring it up * just by asking. This might happen * if somebody has used setupstate on * the subdisks. If we don't do this, * we'll return success, but the plex * won't have changed state. Note * that we don't check for errors * here. */ message->index = plex.plexno; /* pass object number */ message->type = plex_object; /* it's a plex */ message->state = object_up; message->force = 0; /* don't force it */ ioctl(superdev, VINUM_SETSTATE, message); for (sdno = 0; sdno < plex.subdisks; sdno++) { get_plex_sd_info(&sd, object, sdno); if ((sd.state >= sd_empty) && (sd.state <= sd_reviving)) { /* candidate for start */ message->index = sd.sdno; /* pass object number */ message->type = sd_object; /* it's a subdisk */ message->state = object_up; message->force = force; /* don't force it, use a larger hammer */ /* * We don't do any checking here. * The kernel module has a better * understanding of these things, * let it do it. */ if (SSize != 0) { /* specified a size for init */ if (SSize < 512) SSize <<= DEV_BSHIFT; message->blocksize = SSize; } else message->blocksize = DEFAULT_REVIVE_BLOCKSIZE; ioctl(superdev, VINUM_SETSTATE, message); if (reply.error != 0) { if (reply.error == EAGAIN) /* we're reviving */ continue_revive(sd.sdno); else fprintf(stderr, "Can't start %s: %s (%d)\n", sd.name, reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); } if (Verbose) vinum_lsi(sd.sdno, 0); } } } break; case volume_object: if (vol.state == volume_up) /* already up */ fprintf(stderr, "%s is already up\n", vol.name); else doit = 1; break; } if (doit) { message->index = object; /* pass object number */ message->type = type; /* and type of object */ message->state = object_up; message->force = force; /* don't force it, use a larger hammer */ /* * We don't do any checking here. * The kernel module has a better * understanding of these things, * let it do it. */ if (SSize != 0) { /* specified a size for init or revive */ if (SSize < 512) SSize <<= DEV_BSHIFT; message->blocksize = SSize; } else message->blocksize = 0; ioctl(superdev, VINUM_SETSTATE, message); if (reply.error != 0) { if ((reply.error == EAGAIN) /* we're reviving */ &&(type == sd_object)) continue_revive(object); else fprintf(stderr, "Can't start %s: %s (%d)\n", argv[index], reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); } if (Verbose) vinum_li(object, type); } } } } checkupdates(); /* make sure we're updating */ } void vinum_stop(int argc, char *argv[], char *arg0[]) { int object; struct _ioctl_reply reply; struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; if (checkupdates() && (!force)) /* not updating? */ return; message->force = force; /* should we force the transition? */ if (argc == 0) { /* stop vinum */ int fileid = 0; /* ID of Vinum kld */ close(superdev); /* we can't stop if we have vinum open */ sleep(1); /* wait for the daemon to let go */ fileid = kldfind(VINUMMOD); if ((fileid < 0) /* no go */ ||(kldunload(fileid) < 0)) perror("Can't unload " VINUMMOD); else { fprintf(stderr, VINUMMOD " unloaded\n"); exit(0); } /* If we got here, the stop failed. Reopen the superdevice. */ superdev = open(VINUM_SUPERDEV_NAME, O_RDWR); /* reopen vinum superdevice */ if (superdev < 0) { perror("Can't reopen Vinum superdevice"); exit(1); } } else { /* stop specified objects */ int i; enum objecttype type; for (i = 0; i < argc; i++) { object = find_object(argv[i], &type); /* look for it */ if (type == invalid_object) fprintf(stderr, "Can't find object: %s\n", argv[i]); else { message->index = object; /* pass object number */ message->type = type; /* and type of object */ message->state = object_down; ioctl(superdev, VINUM_SETSTATE, message); if (reply.error != 0) fprintf(stderr, "Can't stop %s: %s (%d)\n", argv[i], reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); if (Verbose) vinum_li(object, type); } } } } void vinum_label(int argc, char *argv[], char *arg0[]) { int object; struct _ioctl_reply reply; int *message = (int *) &reply; if (argc == 0) /* start everything */ fprintf(stderr, "label: please specify one or more volume names\n"); else { /* start specified objects */ int i; enum objecttype type; for (i = 0; i < argc; i++) { object = find_object(argv[i], &type); /* look for it */ if (type == invalid_object) fprintf(stderr, "Can't find object: %s\n", argv[i]); else if (type != volume_object) /* it exists, but it isn't a volume */ fprintf(stderr, "%s is not a volume\n", argv[i]); else { message[0] = object; /* pass object number */ ioctl(superdev, VINUM_LABEL, message); if (reply.error != 0) fprintf(stderr, "Can't label %s: %s (%d)\n", argv[i], reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); if (Verbose) vinum_li(object, type); } } } checkupdates(); /* not updating? */ } void reset_volume_stats(int volno, int recurse) { struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; msg.index = volno; msg.type = volume_object; /* XXX get these numbers right if we ever * actually return errors */ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { fprintf(stderr, "Can't reset stats for volume %d: %s\n", volno, reply->msg); longjmp(command_fail, -1); } else if (recurse) { struct _volume vol; int plexno; get_volume_info(&vol, volno); for (plexno = 0; plexno < vol.plexes; plexno++) reset_plex_stats(vol.plex[plexno], recurse); } } void reset_plex_stats(int plexno, int recurse) { struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; msg.index = plexno; msg.type = plex_object; /* XXX get these numbers right if we ever * actually return errors */ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { fprintf(stderr, "Can't reset stats for plex %d: %s\n", plexno, reply->msg); longjmp(command_fail, -1); } else if (recurse) { struct _plex plex; struct _sd sd; int sdno; get_plex_info(&plex, plexno); for (sdno = 0; sdno < plex.subdisks; sdno++) { get_plex_sd_info(&sd, plex.plexno, sdno); reset_sd_stats(sd.sdno, recurse); } } } void reset_sd_stats(int sdno, int recurse) { struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; msg.index = sdno; msg.type = sd_object; /* XXX get these numbers right if we ever * actually return errors */ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { fprintf(stderr, "Can't reset stats for subdisk %d: %s\n", sdno, reply->msg); longjmp(command_fail, -1); } else if (recurse) { get_sd_info(&sd, sdno); /* get the info */ reset_drive_stats(sd.driveno); /* and clear the drive */ } } void reset_drive_stats(int driveno) { struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; msg.index = driveno; msg.type = drive_object; /* XXX get these numbers right if we ever * actually return errors */ if (ioctl(superdev, VINUM_RESETSTATS, &msg) < 0) { fprintf(stderr, "Can't reset stats for drive %d: %s\n", driveno, reply->msg); longjmp(command_fail, -1); } } void vinum_resetstats(int argc, char *argv[], char *argv0[]) { int i; int objno; enum objecttype type; if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { perror("Can't get vinum config"); return; } if (argc == 0) { for (objno = 0; objno < vinum_conf.volumes_allocated; objno++) reset_volume_stats(objno, 1); /* clear everything recursively */ } else { for (i = 0; i < argc; i++) { objno = find_object(argv[i], &type); if (objno >= 0) { /* not invalid */ switch (type) { case drive_object: reset_drive_stats(objno); break; case sd_object: reset_sd_stats(objno, recurse); break; case plex_object: reset_plex_stats(objno, recurse); break; case volume_object: reset_volume_stats(objno, recurse); break; case invalid_object: /* can't get this */ break; } } } } } /* Attach a subdisk to a plex, or a plex to a volume. * attach subdisk plex [offset] [rename] * attach plex volume [rename] */ void vinum_attach(int argc, char *argv[], char *argv0[]) { int i; enum objecttype supertype; struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; const char *objname = argv[0]; const char *supername = argv[1]; int sdno = -1; int plexno = -1; char oldname[MAXNAME + 8]; char newname[MAXNAME + 8]; int rename = 0; /* set if we want to rename the object */ if ((argc < 2) || (argc > 4)) { fprintf(stderr, "usage: \tattach [rename] []\n" "\tattach [rename]\n"); return; } if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { perror("Can't get vinum config"); return; } msg.index = find_object(objname, &msg.type); /* find the object to attach */ msg.otherobject = find_object(supername, &supertype); /* and the object to attach to */ msg.force = force; /* did we specify the use of force? */ msg.recurse = recurse; msg.offset = -1; /* and no offset */ for (i = 2; i < argc; i++) { if (!strcmp(argv[i], "rename")) { rename = 1; msg.rename = 1; /* do renaming */ } else if (!isdigit(argv[i][0])) { /* not an offset */ fprintf(stderr, "Unknown attribute: %s\n", supername); return; } else msg.offset = sizespec(argv[i]); } switch (msg.type) { case sd_object: find_object(argv[1], &supertype); if (supertype != plex_object) { /* huh? */ fprintf(stderr, "%s can only be attached to a plex\n", objname); return; } if ((plex.organization != plex_concat) /* not a cat plex, */ &&(!force)) { fprintf(stderr, "Can't attach subdisks to a %s plex\n", plex_org(plex.organization)); return; } sdno = msg.index; /* note the subdisk number for later */ break; case plex_object: find_object(argv[1], &supertype); if (supertype != volume_object) { /* huh? */ fprintf(stderr, "%s can only be attached to a volume\n", objname); return; } break; case volume_object: case drive_object: fprintf(stderr, "Can only attach subdisks and plexes\n"); return; default: fprintf(stderr, "%s is not a Vinum object\n", objname); return; } ioctl(superdev, VINUM_ATTACH, &msg); if (reply->error != 0) { if (reply->error == EAGAIN) /* reviving */ continue_revive(sdno); /* continue the revive */ else fprintf(stderr, "Can't attach %s to %s: %s (%d)\n", objname, supername, reply->msg[0] ? reply->msg : strerror(reply->error), reply->error); } if (rename) { struct sd; struct _plex; struct _volume; /* we've overwritten msg with the * ioctl reply, start again */ msg.index = find_object(objname, &msg.type); /* find the object to rename */ switch (msg.type) { case sd_object: get_sd_info(&sd, msg.index); get_plex_info(&plex, sd.plexno); for (sdno = 0; sdno < plex.subdisks; sdno++) { if (plex.sdnos[sdno] == msg.index) /* found our subdisk */ break; } sprintf(newname, "%s.s%d", plex.name, sdno); sprintf(oldname, "%s", sd.name); vinum_rename_2(oldname, newname); break; case plex_object: get_plex_info(&plex, msg.index); get_volume_info(&vol, plex.volno); for (plexno = 0; plexno < vol.plexes; plexno++) { if (vol.plex[plexno] == msg.index) /* found our subdisk */ break; } sprintf(newname, "%s.p%d", vol.name, plexno); sprintf(oldname, "%s", plex.name); vinum_rename_2(oldname, newname); /* this may recurse */ break; } } checkupdates(); /* make sure we're updating */ } /* Detach a subdisk from a plex, or a plex from a volume. * detach subdisk plex [rename] * detach plex volume [rename] */ void vinum_detach(int argc, char *argv[], char *argv0[]) { struct vinum_ioctl_msg msg; struct _ioctl_reply *reply = (struct _ioctl_reply *) &msg; if ((argc < 1) || (argc > 2)) { fprintf(stderr, "usage: \tdetach [rename]\n" "\tdetach [rename]\n"); return; } if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { perror("Can't get vinum config"); return; } msg.index = find_object(argv[0], &msg.type); /* find the object to detach */ msg.force = force; /* did we specify the use of force? */ msg.rename = 0; /* don't specify new name */ msg.recurse = recurse; /* but recurse if we have to */ /* XXX are we going to keep this? * Don't document it yet, since the * kernel side of things doesn't * implement it */ if (argc == 2) { if (!strcmp(argv[1], "rename")) msg.rename = 1; /* do renaming */ else { fprintf(stderr, "Unknown attribute: %s\n", argv[1]); return; } } if ((msg.type != sd_object) && (msg.type != plex_object)) { fprintf(stderr, "Can only detach subdisks and plexes\n"); return; } ioctl(superdev, VINUM_DETACH, &msg); if (reply->error != 0) fprintf(stderr, "Can't detach %s: %s (%d)\n", argv[0], reply->msg[0] ? reply->msg : strerror(reply->error), reply->error); checkupdates(); /* make sure we're updating */ } static void dorename(struct vinum_rename_msg *msg, const char *oldname, const char *name, int maxlen) { struct _ioctl_reply *reply = (struct _ioctl_reply *) msg; if (strlen(name) > maxlen) { fprintf(stderr, "%s is too long\n", name); return; } strcpy(msg->newname, name); ioctl(superdev, VINUM_RENAME, msg); if (reply->error != 0) fprintf(stderr, "Can't rename %s to %s: %s (%d)\n", oldname, name, reply->msg[0] ? reply->msg : strerror(reply->error), reply->error); } /* Rename an object: * rename "newname" */ void vinum_rename_2(char *oldname, char *newname) { struct vinum_rename_msg msg; int volno; int plexno; msg.index = find_object(oldname, &msg.type); /* find the object to rename */ msg.recurse = recurse; /* Ugh. Determine how long the name may be */ switch (msg.type) { case drive_object: dorename(&msg, oldname, newname, MAXDRIVENAME); break; case sd_object: dorename(&msg, oldname, newname, MAXSDNAME); break; case plex_object: plexno = msg.index; dorename(&msg, oldname, newname, MAXPLEXNAME); if (recurse) { int sdno; get_plex_info(&plex, plexno); /* find out who we are */ msg.type = sd_object; for (sdno = 0; sdno < plex.subdisks; sdno++) { char sdname[MAXPLEXNAME + 8]; get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */ sprintf(sdname, "%s.s%d", newname, sdno); msg.index = sd.sdno; /* number of the subdisk */ dorename(&msg, sd.name, sdname, MAXSDNAME); } } break; case volume_object: volno = msg.index; dorename(&msg, oldname, newname, MAXVOLNAME); if (recurse) { int sdno; int plexno; get_volume_info(&vol, volno); /* find out who we are */ for (plexno = 0; plexno < vol.plexes; plexno++) { char plexname[MAXVOLNAME + 8]; msg.type = plex_object; sprintf(plexname, "%s.p%d", newname, plexno); msg.index = vol.plex[plexno]; /* number of the plex */ dorename(&msg, plex.name, plexname, MAXPLEXNAME); get_plex_info(&plex, vol.plex[plexno]); /* find out who we are */ msg.type = sd_object; for (sdno = 0; sdno < plex.subdisks; sdno++) { char sdname[MAXPLEXNAME + 8]; get_plex_sd_info(&sd, plex.plexno, sdno); /* get info about the subdisk */ sprintf(sdname, "%s.s%d", plexname, sdno); msg.index = sd.sdno; /* number of the subdisk */ dorename(&msg, sd.name, sdname, MAXSDNAME); } } } break; default: fprintf(stderr, "%s is not a Vinum object\n", oldname); return; } } void vinum_rename(int argc, char *argv[], char *argv0[]) { if (argc != 2) { fprintf(stderr, "usage: \trename \n"); return; } if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { perror("Can't get vinum config"); return; } vinum_rename_2(argv[0], argv[1]); checkupdates(); /* make sure we're updating */ } /* * Move objects: * * mv ... */ void vinum_mv(int argc, char *argv[], char *argv0[]) { int i; /* loop index */ int srcobj; int destobj; enum objecttype srct; enum objecttype destt; int sdno; struct _ioctl_reply reply; struct vinum_ioctl_msg *msg = (struct vinum_ioctl_msg *) &reply; if (argc < 2) { fprintf(stderr, "usage: \tmove ...\n"); return; } /* Get current config */ if (ioctl(superdev, VINUM_GETCONFIG, &vinum_conf) < 0) { perror("Cannot get vinum config\n"); return; } /* Get our destination */ destobj = find_object(argv[0], &destt); if (destobj == -1) { fprintf(stderr, "Can't find %s\n", argv[0]); return; } /* Verify that the target is a drive */ if (destt != drive_object) { fprintf(stderr, "%s is not a drive\n", argv[0]); return; } for (i = 1; i < argc; i++) { /* for all the sources */ srcobj = find_object(argv[i], &srct); if (srcobj == -1) { fprintf(stderr, "Can't find %s\n", argv[i]); continue; } msg->index = destobj; switch (srct) { /* Handle the source object */ case drive_object: /* Move all subdisks on the drive to dst. */ get_drive_info(&drive, srcobj); /* get info on drive */ for (sdno = 0; sdno < vinum_conf.subdisks_allocated; ++sdno) { get_sd_info(&sd, sdno); if (sd.driveno == srcobj) { msg->index = destobj; msg->otherobject = sd.sdno; if (ioctl(superdev, VINUM_MOVE, msg) < 0) fprintf(stderr, "Can't move %s (part of %s) to %s: %s (%d)\n", sd.name, drive.label.name, argv[0], strerror(reply.error), reply.error); } } break; case sd_object: msg->otherobject = srcobj; if (ioctl(superdev, VINUM_MOVE, msg) < 0) fprintf(stderr, "Can't move %s to %s: %s (%d)\n", sd.name, argv[0], strerror(reply.error), reply.error); break; case plex_object: get_plex_info(&plex, srcobj); for (sdno = 0; sdno < plex.subdisks; ++sdno) { get_plex_sd_info(&sd, plex.plexno, sdno); msg->index = destobj; msg->otherobject = sd.sdno; if (ioctl(superdev, VINUM_MOVE, msg) < 0) fprintf(stderr, "Can't move %s (part of %s) to %s: %s (%d)\n", sd.name, plex.name, argv[0], strerror(reply.error), reply.error); } break; case volume_object: case invalid_object: default: fprintf(stderr, "Can't move %s (inappropriate object).\n", argv[i]); break; } if (reply.error) fprintf(stderr, "Can't move %s to %s: %s (%d)\n", argv[i], argv[0], strerror(reply.error), reply.error); } checkupdates(); /* make sure we're updating */ } /* * Replace objects. Not implemented, may never be. */ void vinum_replace(int argc, char *argv[], char *argv0[]) { fprintf(stderr, "'replace' not implemented yet. Use 'move' instead\n"); } /* Primitive help function */ void vinum_help(int argc, char *argv[], char *argv0[]) { char commands[] = { "COMMANDS\n" "attach plex volume [rename]\n" "attach subdisk plex [offset] [rename]\n" " Attach a plex to a volume, or a subdisk to a plex.\n" "checkparity plex [-f] [-v]\n" " Check the parity blocks of a RAID-4 or RAID-5 plex.\n" "concat [-f] [-n name] [-v] drives\n" " Create a concatenated volume from the specified drives.\n" "create [-f] description-file\n" " Create a volume as described in description-file.\n" "debug Cause the volume manager to enter the kernel debugger.\n" "debug flags\n" " Set debugging flags.\n" "detach [-f] [plex | subdisk]\n" " Detach a plex or subdisk from the volume or plex to which it is\n" " attached.\n" "dumpconfig [drive ...]\n" " List the configuration information stored on the specified\n" " drives, or all drives in the system if no drive names are speci-\n" " fied.\n" "info [-v] [-V]\n" " List information about volume manager state.\n" "init [-S size] [-w] plex | subdisk\n" " Initialize the contents of a subdisk or all the subdisks of a\n" " plex to all zeros.\n" "label volume\n" " Create a volume label.\n" "l | list [-r] [-s] [-v] [-V] [volume | plex | subdisk]\n" " List information about specified objects.\n" "ld [-r] [-s] [-v] [-V] [volume]\n" " List information about drives.\n" "ls [-r] [-s] [-v] [-V] [subdisk]\n" " List information about subdisks.\n" "lp [-r] [-s] [-v] [-V] [plex]\n" " List information about plexes.\n" "lv [-r] [-s] [-v] [-V] [volume]\n" " List information about volumes.\n" "makedev\n" " Remake the device nodes in /dev/vinum.\n" "mirror [-f] [-n name] [-s] [-v] drives\n" " Create a mirrored volume from the specified drives.\n" "move | mv -f drive object ...\n" " Move the object(s) to the specified drive.\n" "printconfig [file]\n" " Write a copy of the current configuration to file.\n" "quit Exit the vinum program when running in interactive mode. Nor-\n" " mally this would be done by entering the EOF character.\n" "read disk ...\n" " Read the vinum configuration from the specified disks.\n" "rename [-r] [drive | subdisk | plex | volume] newname\n" " Change the name of the specified object.\n" "rebuildparity plex [-f] [-v] [-V]\n" " Rebuild the parity blocks of a RAID-4 or RAID-5 plex.\n" "resetconfig\n" " Reset the complete vinum configuration.\n" "resetstats [-r] [volume | plex | subdisk]\n" " Reset statistics counters for the specified objects, or for all\n" " objects if none are specified.\n" "rm [-f] [-r] volume | plex | subdisk\n" " Remove an object.\n" "saveconfig\n" " Save vinum configuration to disk after configuration failures.\n" "setdaemon [value]\n" " Set daemon configuration.\n" "setstate state [volume | plex | subdisk | drive]\n" " Set state without influencing other objects, for diagnostic pur-\n" " poses only.\n" "start Read configuration from all vinum drives.\n" "start [-i interval] [-S size] [-w] volume | plex | subdisk\n" " Allow the system to access the objects.\n" "stop [-f] [volume | plex | subdisk]\n" " Terminate access to the objects, or stop vinum if no parameters\n" " are specified.\n" "stripe [-f] [-n name] [-v] drives\n" " Create a striped volume from the specified drives.\n" }; puts(commands); } /* Set daemon options. * XXX quick and dirty: use a bitmap, which requires * knowing which bit does what. FIXME */ void vinum_setdaemon(int argc, char *argv[], char *argv0[]) { int options; switch (argc) { case 0: if (ioctl(superdev, VINUM_GETDAEMON, &options) < 0) fprintf(stderr, "Can't get daemon options: %s (%d)\n", strerror(errno), errno); else printf("Options mask: %d\n", options); break; case 1: options = atoi(argv[0]); if (ioctl(superdev, VINUM_SETDAEMON, &options) < 0) fprintf(stderr, "Can't set daemon options: %s (%d)\n", strerror(errno), errno); break; default: fprintf(stderr, "usage: \tsetdaemon []\n"); } checkupdates(); /* make sure we're updating */ } /* Save config info */ void vinum_saveconfig(int argc, char *argv[], char *argv0[]) { int ioctltype; if (argc != 0) { printf("usage: saveconfig\n"); return; } ioctltype = 1; /* user saveconfig */ if (ioctl(superdev, VINUM_SAVECONFIG, &ioctltype) < 0) fprintf(stderr, "Can't save configuration: %s (%d)\n", strerror(errno), errno); checkupdates(); /* make sure we're updating */ } /* * Create a volume name for the quick and dirty * commands. It will be of the form "vinum#", * where # is a small positive number. */ void genvolname() { int v; /* volume number */ static char volumename[MAXVOLNAME]; /* name to create */ enum objecttype type; objectname = volumename; /* point to it */ for (v = 0;; v++) { sprintf(objectname, "vinum%d", v); /* create the name */ if (find_object(objectname, &type) == -1) /* does it exist? */ return; /* no, it's ours */ } } /* * Create a drive for the quick and dirty * commands. The name will be of the form * vinumdrive#, where # is a small positive * number. Return the name of the drive. */ struct _drive * create_drive(char *devicename) { int d; /* volume number */ static char drivename[MAXDRIVENAME]; /* name to create */ enum objecttype type; struct _ioctl_reply *reply; /* * We're never likely to get anything * like 10000 drives. The only reason for * this limit is to stop the thing * looping if we have a bug somewhere. */ for (d = 0; d < 100000; d++) { /* look for a free drive number */ sprintf(drivename, "vinumdrive%d", d); /* create the name */ if (find_object(drivename, &type) == -1) { /* does it exist? */ char command[MAXDRIVENAME * 2]; sprintf(command, "drive %s device %s", drivename, devicename); /* create a create command */ if (vflag) printf("drive %s device %s\n", drivename, devicename); /* create a create command */ ioctl(superdev, VINUM_CREATE, command); reply = (struct _ioctl_reply *) &command; if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create drive %s, device %s: %s\n", drivename, devicename, reply->msg); else fprintf(stderr, "Can't create drive %s, device %s: %s (%d)\n", drivename, devicename, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } find_object(drivename, &type); return &drive; /* return the name of the drive */ } } fprintf(stderr, "Can't generate a drive name\n"); /* NOTREACHED */ return NULL; } /* * Create a volume with a single concatenated plex from * as much space as we can get on the specified drives. * If the drives aren't Vinum drives, make them so. */ void vinum_concat(int argc, char *argv[], char *argv0[]) { int o; /* object number */ char buffer[BUFSIZE]; struct _drive *drive; /* drive we're currently looking at */ struct _ioctl_reply *reply; int ioctltype; int error; enum objecttype type; reply = (struct _ioctl_reply *) &buffer; if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ printf("Can't configure: %s (%d)\n", strerror(errno), errno); return; } if (!objectname) /* we need a name for our object */ genvolname(); sprintf(buffer, "volume %s", objectname); if (vflag) printf("volume %s\n", objectname); ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create volume %s: %s\n", objectname, reply->msg); else fprintf(stderr, "Can't create volume %s: %s (%d)\n", objectname, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } sprintf(buffer, "plex name %s.p0 org concat", objectname); if (vflag) printf(" plex name %s.p0 org concat\n", objectname); ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create plex %s.p0: %s\n", objectname, reply->msg); else fprintf(stderr, "Can't create plex %s.p0: %s (%d)\n", objectname, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } for (o = 0; o < argc; o++) { if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ drive = create_drive(argv[o]); /* create it */ sprintf(buffer, "sd name %s.p0.s%d drive %s size 0", objectname, o, drive->label.name); if (vflag) printf(" sd name %s.p0.s%d drive %s size 0\n", objectname, o, drive->label.name); ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create subdisk %s.p0.s%d: %s\n", objectname, o, reply->msg); else fprintf(stderr, "Can't create subdisk %s.p0.s%d: %s (%d)\n", objectname, o, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } } /* done, save the config */ ioctltype = 0; /* saveconfig after update */ error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ if (error != 0) perror("Can't save Vinum config"); find_object(objectname, &type); /* find the index of the volume */ make_vol_dev(vol.volno, 1); /* and create the devices */ if (vflag) { vflag--; /* XXX don't give too much detail */ find_object(objectname, &type); /* point to the volume */ vinum_lvi(vol.volno, 1); /* and print info about it */ } } /* * Create a volume with a single striped plex from * as much space as we can get on the specified drives. * If the drives aren't Vinum drives, make them so. */ void vinum_stripe(int argc, char *argv[], char *argv0[]) { int o; /* object number */ char buffer[BUFSIZE]; struct _drive *drive; /* drive we're currently looking at */ struct _ioctl_reply *reply; int ioctltype; int error; enum objecttype type; off_t maxsize; int fe; /* freelist entry index */ struct drive_freelist freelist; struct ferq { /* request to pass to ioctl */ int driveno; int fe; } *ferq = (struct ferq *) &freelist; u_int64_t bigchunk; /* biggest chunk in freelist */ maxsize = QUAD_MAX; reply = (struct _ioctl_reply *) &buffer; /* * First, check our drives. */ if (argc < 2) { fprintf(stderr, "You need at least two drives to create a striped plex\n"); return; } if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ printf("Can't configure: %s (%d)\n", strerror(errno), errno); return; } if (!objectname) /* we need a name for our object */ genvolname(); for (o = 0; o < argc; o++) { if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ drive = create_drive(argv[o]); /* create it */ /* Now find the largest chunk available on the drive */ bigchunk = 0; /* ain't found nothin' yet */ for (fe = 0; fe < drive->freelist_entries; fe++) { ferq->driveno = drive->driveno; ferq->fe = fe; if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { fprintf(stderr, "Can't get free list element %d: %s\n", fe, strerror(errno)); longjmp(command_fail, -1); } bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ } maxsize = min(maxsize, bigchunk); /* this is as much as we can do */ } /* Now create the volume */ sprintf(buffer, "volume %s", objectname); if (vflag) printf("volume %s\n", objectname); ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create volume %s: %s\n", objectname, reply->msg); else fprintf(stderr, "Can't create volume %s: %s (%d)\n", objectname, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } sprintf(buffer, "plex name %s.p0 org striped 279k", objectname); if (vflag) printf(" plex name %s.p0 org striped 279k\n", objectname); ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create plex %s.p0: %s\n", objectname, reply->msg); else fprintf(stderr, "Can't create plex %s.p0: %s (%d)\n", objectname, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } for (o = 0; o < argc; o++) { drive = find_drive_by_devname(argv[o]); /* we know it exists... */ sprintf(buffer, "sd name %s.p0.s%d drive %s size %lldb", objectname, o, drive->label.name, (long long) maxsize); if (vflag) printf(" sd name %s.p0.s%d drive %s size %lldb\n", objectname, o, drive->label.name, (long long) maxsize); ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create subdisk %s.p0.s%d: %s\n", objectname, o, reply->msg); else fprintf(stderr, "Can't create subdisk %s.p0.s%d: %s (%d)\n", objectname, o, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } } /* done, save the config */ ioctltype = 0; /* saveconfig after update */ error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ if (error != 0) perror("Can't save Vinum config"); find_object(objectname, &type); /* find the index of the volume */ make_vol_dev(vol.volno, 1); /* and create the devices */ if (vflag) { vflag--; /* XXX don't give too much detail */ find_object(objectname, &type); /* point to the volume */ vinum_lvi(vol.volno, 1); /* and print info about it */ } } /* * Create a volume with a single RAID-4 plex from * as much space as we can get on the specified drives. * If the drives aren't Vinum drives, make them so. */ void vinum_raid4(int argc, char *argv[], char *argv0[]) { int o; /* object number */ char buffer[BUFSIZE]; struct _drive *drive; /* drive we're currently looking at */ struct _ioctl_reply *reply; int ioctltype; int error; enum objecttype type; off_t maxsize; int fe; /* freelist entry index */ struct drive_freelist freelist; struct ferq { /* request to pass to ioctl */ int driveno; int fe; } *ferq = (struct ferq *) &freelist; u_int64_t bigchunk; /* biggest chunk in freelist */ maxsize = QUAD_MAX; reply = (struct _ioctl_reply *) &buffer; /* * First, check our drives. */ if (argc < 3) { fprintf(stderr, "You need at least three drives to create a RAID-4 plex\n"); return; } if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ printf("Can't configure: %s (%d)\n", strerror(errno), errno); return; } if (!objectname) /* we need a name for our object */ genvolname(); for (o = 0; o < argc; o++) { if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ drive = create_drive(argv[o]); /* create it */ /* Now find the largest chunk available on the drive */ bigchunk = 0; /* ain't found nothin' yet */ for (fe = 0; fe < drive->freelist_entries; fe++) { ferq->driveno = drive->driveno; ferq->fe = fe; if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { fprintf(stderr, "Can't get free list element %d: %s\n", fe, strerror(errno)); longjmp(command_fail, -1); } bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ } maxsize = min(maxsize, bigchunk); /* this is as much as we can do */ } /* Now create the volume */ sprintf(buffer, "volume %s", objectname); if (vflag) printf("volume %s\n", objectname); ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create volume %s: %s\n", objectname, reply->msg); else fprintf(stderr, "Can't create volume %s: %s (%d)\n", objectname, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } sprintf(buffer, "plex name %s.p0 org raid4 279k", objectname); if (vflag) printf(" plex name %s.p0 org raid4 279k\n", objectname); ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create plex %s.p0: %s\n", objectname, reply->msg); else fprintf(stderr, "Can't create plex %s.p0: %s (%d)\n", objectname, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } for (o = 0; o < argc; o++) { drive = find_drive_by_devname(argv[o]); /* we know it exists... */ sprintf(buffer, "sd name %s.p0.s%d drive %s size %lldb", objectname, o, drive->label.name, (long long) maxsize); if (vflag) printf(" sd name %s.p0.s%d drive %s size %lldb\n", objectname, o, drive->label.name, (long long) maxsize); ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create subdisk %s.p0.s%d: %s\n", objectname, o, reply->msg); else fprintf(stderr, "Can't create subdisk %s.p0.s%d: %s (%d)\n", objectname, o, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } } /* done, save the config */ ioctltype = 0; /* saveconfig after update */ error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ if (error != 0) perror("Can't save Vinum config"); find_object(objectname, &type); /* find the index of the volume */ make_vol_dev(vol.volno, 1); /* and create the devices */ if (vflag) { vflag--; /* XXX don't give too much detail */ find_object(objectname, &type); /* point to the volume */ vinum_lvi(vol.volno, 1); /* and print info about it */ } } /* * Create a volume with a single RAID-4 plex from * as much space as we can get on the specified drives. * If the drives aren't Vinum drives, make them so. */ void vinum_raid5(int argc, char *argv[], char *argv0[]) { int o; /* object number */ char buffer[BUFSIZE]; struct _drive *drive; /* drive we're currently looking at */ struct _ioctl_reply *reply; int ioctltype; int error; enum objecttype type; off_t maxsize; int fe; /* freelist entry index */ struct drive_freelist freelist; struct ferq { /* request to pass to ioctl */ int driveno; int fe; } *ferq = (struct ferq *) &freelist; u_int64_t bigchunk; /* biggest chunk in freelist */ maxsize = QUAD_MAX; reply = (struct _ioctl_reply *) &buffer; /* * First, check our drives. */ if (argc < 3) { fprintf(stderr, "You need at least three drives to create a RAID-5 plex\n"); return; } if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ printf("Can't configure: %s (%d)\n", strerror(errno), errno); return; } if (!objectname) /* we need a name for our object */ genvolname(); for (o = 0; o < argc; o++) { if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ drive = create_drive(argv[o]); /* create it */ /* Now find the largest chunk available on the drive */ bigchunk = 0; /* ain't found nothin' yet */ for (fe = 0; fe < drive->freelist_entries; fe++) { ferq->driveno = drive->driveno; ferq->fe = fe; if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { fprintf(stderr, "Can't get free list element %d: %s\n", fe, strerror(errno)); longjmp(command_fail, -1); } bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ } maxsize = min(maxsize, bigchunk); /* this is as much as we can do */ } /* Now create the volume */ sprintf(buffer, "volume %s", objectname); if (vflag) printf("volume %s\n", objectname); ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create volume %s: %s\n", objectname, reply->msg); else fprintf(stderr, "Can't create volume %s: %s (%d)\n", objectname, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } sprintf(buffer, "plex name %s.p0 org raid5 279k", objectname); if (vflag) printf(" plex name %s.p0 org raid5 279k\n", objectname); ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create plex %s.p0: %s\n", objectname, reply->msg); else fprintf(stderr, "Can't create plex %s.p0: %s (%d)\n", objectname, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } for (o = 0; o < argc; o++) { drive = find_drive_by_devname(argv[o]); /* we know it exists... */ sprintf(buffer, "sd name %s.p0.s%d drive %s size %lldb", objectname, o, drive->label.name, (long long) maxsize); if (vflag) printf(" sd name %s.p0.s%d drive %s size %lldb\n", objectname, o, drive->label.name, (long long) maxsize); ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create subdisk %s.p0.s%d: %s\n", objectname, o, reply->msg); else fprintf(stderr, "Can't create subdisk %s.p0.s%d: %s (%d)\n", objectname, o, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } } /* done, save the config */ ioctltype = 0; /* saveconfig after update */ error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ if (error != 0) perror("Can't save Vinum config"); find_object(objectname, &type); /* find the index of the volume */ make_vol_dev(vol.volno, 1); /* and create the devices */ if (vflag) { vflag--; /* XXX don't give too much detail */ find_object(objectname, &type); /* point to the volume */ vinum_lvi(vol.volno, 1); /* and print info about it */ } } /* * Create a volume with a two plexes from as much space * as we can get on the specified drives. If the * drives aren't Vinum drives, make them so. * * The number of drives must be even, and at least 4 * for a striped plex. Specify striped plexes with the * -s flag; otherwise they will be concatenated. It's * possible that the two plexes may differ in length. */ void vinum_mirror(int argc, char *argv[], char *argv0[]) { int o; /* object number */ int p; /* plex number */ char buffer[BUFSIZE]; struct _drive *drive; /* drive we're currently looking at */ struct _ioctl_reply *reply; int ioctltype; int error; enum objecttype type; off_t maxsize[2]; /* maximum subdisk size for striped plexes */ int fe; /* freelist entry index */ struct drive_freelist freelist; struct ferq { /* request to pass to ioctl */ int driveno; int fe; } *ferq = (struct ferq *) &freelist; u_int64_t bigchunk; /* biggest chunk in freelist */ if (sflag) /* striped, */ maxsize[0] = maxsize[1] = QUAD_MAX; /* we need to calculate sd size */ else maxsize[0] = maxsize[1] = 0; /* let the kernel routines do it */ reply = (struct _ioctl_reply *) &buffer; /* * First, check our drives. */ if ((argc < 2) || (argc & 1)) { fprintf(stderr, "You need an even number of drives to create a mirrored volume\n"); return; } if (sflag && (argc < 4)) { fprintf(stderr, "You need at least 4 drives to create a mirrored, striped volume\n"); return; } if (ioctl(superdev, VINUM_STARTCONFIG, &force)) { /* can't get config? */ printf("Can't configure: %s (%d)\n", strerror(errno), errno); return; } if (!objectname) /* we need a name for our object */ genvolname(); for (o = 0; o < argc; o++) { if ((drive = find_drive_by_devname(argv[o])) == NULL) /* doesn't exist */ drive = create_drive(argv[o]); /* create it */ if (sflag) { /* striping, */ /* Find the largest chunk available on the drive */ bigchunk = 0; /* ain't found nothin' yet */ for (fe = 0; fe < drive->freelist_entries; fe++) { ferq->driveno = drive->driveno; ferq->fe = fe; if (ioctl(superdev, VINUM_GETFREELIST, &freelist) < 0) { fprintf(stderr, "Can't get free list element %d: %s\n", fe, strerror(errno)); longjmp(command_fail, -1); } bigchunk = bigchunk > freelist.sectors ? bigchunk : freelist.sectors; /* max it */ } maxsize[o & 1] = min(maxsize[o & 1], bigchunk); /* get the maximum size of a subdisk */ } } /* Now create the volume */ sprintf(buffer, "volume %s setupstate", objectname); if (vflag) printf("volume %s setupstate\n", objectname); ioctl(superdev, VINUM_CREATE, buffer); /* create the volume */ if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create volume %s: %s\n", objectname, reply->msg); else fprintf(stderr, "Can't create volume %s: %s (%d)\n", objectname, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } for (p = 0; p < 2; p++) { /* create each plex */ if (sflag) { sprintf(buffer, "plex name %s.p%d org striped 279k", objectname, p); if (vflag) printf(" plex name %s.p%d org striped 279k\n", objectname, p); } else { /* concat */ sprintf(buffer, "plex name %s.p%d org concat", objectname, p); if (vflag) printf(" plex name %s.p%d org concat\n", objectname, p); } ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create plex %s.p%d: %s\n", objectname, p, reply->msg); else fprintf(stderr, "Can't create plex %s.p%d: %s (%d)\n", objectname, p, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } /* Now look at the subdisks */ for (o = p; o < argc; o += 2) { /* every second one */ drive = find_drive_by_devname(argv[o]); /* we know it exists... */ sprintf(buffer, "sd name %s.p%d.s%d drive %s size %lldb", objectname, p, o >> 1, drive->label.name, (long long) maxsize[p]); if (vflag) printf(" sd name %s.p%d.s%d drive %s size %lldb\n", objectname, p, o >> 1, drive->label.name, (long long) maxsize[p]); ioctl(superdev, VINUM_CREATE, buffer); if (reply->error != 0) { /* error in config */ if (reply->msg[0]) fprintf(stderr, "Can't create subdisk %s.p%d.s%d: %s\n", objectname, p, o >> 1, reply->msg); else fprintf(stderr, "Can't create subdisk %s.p%d.s%d: %s (%d)\n", objectname, p, o >> 1, strerror(reply->error), reply->error); longjmp(command_fail, -1); /* give up */ } } } /* done, save the config */ ioctltype = 0; /* saveconfig after update */ error = ioctl(superdev, VINUM_SAVECONFIG, &ioctltype); /* save the config to disk */ if (error != 0) perror("Can't save Vinum config"); find_object(objectname, &type); /* find the index of the volume */ make_vol_dev(vol.volno, 1); /* and create the devices */ if (vflag) { vflag--; /* XXX don't give too much detail */ sflag = 0; /* no stats, please */ find_object(objectname, &type); /* point to the volume */ vinum_lvi(vol.volno, 1); /* and print info about it */ } } void vinum_readpol(int argc, char *argv[], char *argv0[]) { int object; struct _ioctl_reply reply; struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; enum objecttype type; struct _plex plex; struct _volume vol; int plexno; if (argc != 2) { fprintf(stderr, "usage: readpol | round\n"); return; } object = find_object(argv[0], &type); /* look for it */ if (type != volume_object) { fprintf(stderr, "%s is not a volume\n", argv[0]); return; } get_volume_info(&vol, object); if (strcmp(argv[1], "round")) { /* not 'round' */ object = find_object(argv[1], &type); /* look for it */ if (type != plex_object) { fprintf(stderr, "%s is not a plex\n", argv[1]); return; } get_plex_info(&plex, object); plexno = plex.plexno; } else /* round */ plexno = -1; /* Set the value */ message->index = vol.volno; message->otherobject = plexno; ioctl(superdev, VINUM_READPOL, message); if (reply.error) fprintf(stderr, "Can't set read policy: %s (%d)\n", reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); if (vflag) vinum_lpi(plexno, recurse); } /* * Brute force set state function. Don't look at * any dependencies, just do it. */ void vinum_setstate(int argc, char *argv[], char *argv0[]) { int object; struct _ioctl_reply reply; struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; int index; enum objecttype type; int state; for (index = 1; index < argc; index++) { object = find_object(argv[index], &type); /* look for it */ if (type == invalid_object) fprintf(stderr, "Can't find object: %s\n", argv[index]); else { int doit = 0; /* set to 1 if we pass our tests */ switch (type) { case drive_object: state = DriveState(argv[0]); /* get the state */ if (drive.state == state) /* already in that state */ fprintf(stderr, "%s is already %s\n", drive.label.name, argv[0]); else doit = 1; break; case sd_object: state = SdState(argv[0]); /* get the state */ if (sd.state == state) /* already in that state */ fprintf(stderr, "%s is already %s\n", sd.name, argv[0]); else doit = 1; break; case plex_object: state = PlexState(argv[0]); /* get the state */ if (plex.state == state) /* already in that state */ fprintf(stderr, "%s is already %s\n", plex.name, argv[0]); else doit = 1; break; case volume_object: state = VolState(argv[0]); /* get the state */ if (vol.state == state) /* already in that state */ fprintf(stderr, "%s is already %s\n", vol.name, argv[0]); else doit = 1; break; default: state = 0; /* to keep the compiler happy */ } if (state == -1) fprintf(stderr, "Invalid state for object: %s\n", argv[0]); else if (doit) { message->index = object; /* pass object number */ message->type = type; /* and type of object */ message->state = state; message->force = force; /* don't force it, use a larger hammer */ ioctl(superdev, VINUM_SETSTATE_FORCE, message); if (reply.error != 0) fprintf(stderr, "Can't start %s: %s (%d)\n", argv[index], reply.msg[0] ? reply.msg : strerror(reply.error), reply.error); if (Verbose) vinum_li(object, type); } } } } void vinum_checkparity(int argc, char *argv[], char *argv0[]) { Verbose = vflag; /* accept -v for verbose */ if (argc == 0) /* no parameters? */ fprintf(stderr, "usage: checkparity object [object...]\n"); else parityops(argc, argv, checkparity); } void vinum_rebuildparity(int argc, char *argv[], char *argv0[]) { if (argc == 0) /* no parameters? */ fprintf(stderr, "usage: rebuildparity object [object...]\n"); else parityops(argc, argv, vflag ? rebuildandcheckparity : rebuildparity); } /* * Common code for rebuildparity and checkparity. * We bend the meanings of some flags here: * * -v: Report incorrect parity on rebuild. * -V: Show running count of position being checked. * -f: Start from beginning of the plex. */ void parityops(int argc, char *argv[], enum parityop op) { int object; struct _plex plex; struct _ioctl_reply reply; struct vinum_ioctl_msg *message = (struct vinum_ioctl_msg *) &reply; int index; enum objecttype type; char *msg; off_t block; if (op == checkparity) msg = "Checking"; else msg = "Rebuilding"; for (index = 0; index < argc; index++) { object = find_object(argv[index], &type); /* look for it */ if (type != plex_object) fprintf(stderr, "%s is not a plex\n", argv[index]); else { get_plex_info(&plex, object); if (!isparity((&plex))) fprintf(stderr, "%s is not a RAID-4 or RAID-5 plex\n", argv[index]); else { do { message->index = object; /* pass object number */ message->type = type; /* and type of object */ message->op = op; /* what to do */ if (force) message->offset = 0; /* start at the beginning */ else message->offset = plex.checkblock; /* continue where we left off */ force = 0; /* don't reset after the first time */ ioctl(superdev, VINUM_PARITYOP, message); get_plex_info(&plex, object); if (Verbose) { block = (plex.checkblock << DEV_BSHIFT) * (plex.subdisks - 1); if (block != 0) printf("\r%s at %s (%d%%) ", msg, roughlength(block, 1), ((int) (block * 100 / plex.length) >> DEV_BSHIFT)); if ((reply.error == EAGAIN) && (reply.msg[0])) /* got a comment back */ fputs(reply.msg, stderr); /* show it */ fflush(stdout); } } while (reply.error == EAGAIN); if (reply.error != 0) { if (reply.msg[0]) fputs(reply.msg, stderr); else fprintf(stderr, "%s failed: %s\n", msg, strerror(reply.error)); } else if (Verbose) { if (op == checkparity) fprintf(stderr, "%s has correct parity\n", argv[index]); else fprintf(stderr, "Rebuilt parity on %s\n", argv[index]); } } } } } /* Local Variables: */ /* fill-column: 50 */ /* End: */ Index: head/usr.bin/vi/pathnames.h =================================================================== --- head/usr.bin/vi/pathnames.h (revision 114762) +++ head/usr.bin/vi/pathnames.h (revision 114763) @@ -1,45 +1,49 @@ /* @(#)pathnames.h.in 8.4 (Berkeley) 6/26/96 */ +/* $FreeBSD$ */ + +/* Read standard system paths first. */ +#include #ifndef _PATH_BSHELL #define _PATH_BSHELL "/bin/sh" #endif #ifndef _PATH_EXRC #define _PATH_EXRC ".exrc" #endif #ifndef _PATH_MSGCAT #define _PATH_MSGCAT "/usr/share/vi/catalog/" #endif #ifndef _PATH_NEXRC #define _PATH_NEXRC ".nexrc" #endif #ifndef _PATH_PRESERVE #define _PATH_PRESERVE "/var/tmp/vi.recover" #endif #ifndef _PATH_SYSV_PTY #define _PATH_SYSV_PTY "/dev/ptmx" #endif #ifndef _PATH_SENDMAIL #define _PATH_SENDMAIL "/usr/sbin/sendmail" #endif #ifndef _PATH_SYSEXRC #define _PATH_SYSEXRC "/etc/vi.exrc" #endif #ifndef _PATH_TAGS #define _PATH_TAGS "tags" #endif #ifndef _PATH_TMP #define _PATH_TMP "/tmp" #endif #ifndef _PATH_TTY #define _PATH_TTY "/dev/tty" #endif